source: trunk/GSASIIpath.py @ 4439

Last change on this file since 4439 was 4439, checked in by toby, 19 months ago

bootstrap w/selected binary; update svn switch cmd; save build output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 41.2 KB
Line 
1# -*- coding: utf-8 -*-
2'''
3*GSASIIpath: locations & updates*
4---------------------------------
5
6Routines for dealing with file locations, etc.
7
8Determines the location of the compiled (.pyd or .so) libraries.
9
10Interfaces with subversion (svn):
11Determine the subversion release number by determining the highest version number
12where :func:`SetVersionNumber` is called (best done in every GSASII file).
13Other routines will update GSASII from the subversion server if svn can be
14found.
15
16Accesses configuration options, as defined in config.py
17'''
18
19from __future__ import division, print_function
20import os
21import sys
22import platform
23import glob
24import subprocess
25import numpy as np
26g2home = 'https://subversion.xray.aps.anl.gov/pyGSAS'
27'Define the location of the GSAS-II subversion repository'
28   
29path2GSAS2 = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) # location of this file; save before any changes in pwd
30
31# convert version numbers as '1.2.3' to integers (1002) and back (to 1.2)
32fmtver = lambda v: str(v//1000)+'.'+str(v%1000)
33intver = lambda vs: sum([int(i) for i in vs.split('.')[0:2]]*np.array((1000,1)))
34
35def GetConfigValue(key,default=None):
36    '''Return the configuration file value for key or a default value if not present
37   
38    :param str key: a value to be found in the configuration (config.py) file
39    :param default: a value to be supplied is none is in the config file or
40      the config file is not found. Defaults to None
41    :returns: the value found or the default.
42    '''
43    try:
44        return configDict.get(key,default)
45    except NameError: # this happens when building docs
46        return None
47
48def SetConfigValue(parmdict):
49    '''Set configuration variables from a dictionary where elements are lists
50    First item in list is the default value and second is the value to use.
51    '''
52    global configDict
53    for var in parmdict:
54        if var in configDict:
55            del configDict[var]
56        if isinstance(parmdict[var],tuple):
57            configDict[var] = parmdict[var]
58        else:
59            if parmdict[var][1] is None: continue
60            if parmdict[var][1] == '': continue
61            if parmdict[var][0] == parmdict[var][1]: continue
62            configDict[var] = parmdict[var][1]
63
64def addPrevGPX(fil,configDict):
65    '''Add a GPX file to the list of previous files.
66    Move previous names to start of list. Keep most recent five files
67    '''
68    fil = os.path.abspath(os.path.expanduser(fil))
69    if 'previous_GPX_files' not in configDict: return
70    try:
71        pos = configDict['previous_GPX_files'][1].index(fil) 
72        if pos == 0: return
73        configDict['previous_GPX_files'][1].pop(pos) # if present, remove if not 1st
74    except ValueError:
75        pass
76    except AttributeError:
77        configDict['previous_GPX_files'][1] = []
78    configDict['previous_GPX_files'][1].insert(0,fil)
79    configDict['previous_GPX_files'][1] = configDict['previous_GPX_files'][1][:5]
80
81# routines for looking a version numbers in files
82version = -1
83def SetVersionNumber(RevString):
84    '''Set the subversion version number
85
86    :param str RevString: something like "$Revision: 4439 $"
87      that is set by subversion when the file is retrieved from subversion.
88
89    Place ``GSASIIpath.SetVersionNumber("$Revision: 4439 $")`` in every python
90    file.
91    '''
92    try:
93        RevVersion = int(RevString.split(':')[1].split()[0])
94        global version
95        version = max(version,RevVersion)
96    except:
97        pass
98       
99def GetVersionNumber():
100    '''Return the maximum version number seen in :func:`SetVersionNumber`
101    '''
102    if version > 1000:
103        return version
104    else:
105        return "unknown"
106
107def LoadConfigFile(filename):
108    '''Read a GSAS-II configuration file.
109    Comments (starting with "%") are removed, as are empty lines
110   
111    :param str filename: base file name (such as 'file.dat'). Files with this name
112      are located from the path and the contents of each are concatenated.
113    :returns: a list containing each non-empty (after removal of comments) line
114      found in every matching config file.
115    '''
116    info = []
117    for path in sys.path:
118        fil = os.path.join(path,filename)
119        if not os.path.exists(fil): continue
120        try:
121            i = 0
122            fp = open(fil,'r')
123            for line in fp:
124                expr = line.split('#')[0].strip()
125                if expr:
126                    info.append(expr)
127                    i += 1
128            print(str(i)+' lines read from config file '+fil)
129        finally:
130            fp.close()
131    return info
132
133
134# routines to interface with subversion
135proxycmds = []
136'Used to hold proxy information for subversion, set if needed in whichsvn'
137svnLocCache = None
138'Cached location of svn to avoid multiple searches for it'
139
140def MakeByte2str(arg):
141    '''Convert output from subprocess pipes (bytes) to str (unicode) in Python 3.
142    In Python 2: Leaves output alone (already str).
143    Leaves stuff of other types alone (including unicode in Py2)
144    Works recursively for string-like stuff in nested loops and tuples.
145
146    typical use::
147
148        out = MakeByte2str(out)
149
150    or::
151
152        out,err = MakeByte2str(s.communicate())
153   
154    '''
155    if isinstance(arg,str): return arg
156    if isinstance(arg,bytes): return arg.decode()
157    if isinstance(arg,list):
158        return [MakeByte2str(i) for i in arg]
159    if isinstance(arg,tuple):
160        return tuple([MakeByte2str(i) for i in arg])
161    return arg
162               
163def getsvnProxy():
164    '''Loads a proxy for subversion from the file created by bootstrap.py
165    '''
166    global proxycmds
167    proxycmds = []
168    proxyinfo = os.path.join(os.path.expanduser('~/.G2local/'),"proxyinfo.txt")
169    if not os.path.exists(proxyinfo):
170        proxyinfo = os.path.join(path2GSAS2,"proxyinfo.txt")
171    if not os.path.exists(proxyinfo):
172        return '','',''
173    fp = open(proxyinfo,'r')
174    host = fp.readline().strip()
175    # allow file to begin with comments
176    while host.startswith('#'):
177        host = fp.readline().strip()
178    port = fp.readline().strip()
179    etc = []
180    line = fp.readline()
181    while line:
182        etc.append(line.strip())
183        line = fp.readline()
184    fp.close()
185    setsvnProxy(host,port,etc)
186    return host,port,etc
187
188def setsvnProxy(host,port,etc=[]):
189    '''Sets the svn commands needed to use a proxy
190    '''
191    global proxycmds
192    proxycmds = []
193    host = host.strip()
194    port = port.strip()
195    if host: 
196        proxycmds.append('--config-option')
197        proxycmds.append('servers:global:http-proxy-host='+host)
198        if port:
199            proxycmds.append('--config-option')
200            proxycmds.append('servers:global:http-proxy-port='+port)
201    for item in etc:
202        proxycmds.append(item)
203       
204def whichsvn():
205    '''Returns a path to the subversion exe file, if any is found.
206    Searches the current path after adding likely places where GSAS-II
207    might install svn.
208
209    :returns: None if svn is not found or an absolute path to the subversion
210      executable file.
211    '''
212    # use a previosuly cached svn location
213    global svnLocCache
214    if svnLocCache: return svnLocCache
215    # prepare to find svn
216    is_exe = lambda fpath: os.path.isfile(fpath) and os.access(fpath, os.X_OK)
217    svnprog = 'svn'
218    if sys.platform.startswith('win'): svnprog += '.exe'
219    host,port,etc = getsvnProxy()
220    if GetConfigValue('debug') and host:
221        print('DBG_Using proxy host {} port {}'.format(host,port))
222    # add likely places to find subversion when installed with GSAS-II
223    pathlist = os.environ["PATH"].split(os.pathsep)
224    pathlist.insert(0,os.path.split(sys.executable)[0])
225    pathlist.insert(1,path2GSAS2)
226    for rpt in ('..','bin'),('..','Library','bin'),('svn','bin'),('svn',),('.'):
227        pt = os.path.normpath(os.path.join(path2GSAS2,*rpt))
228        if os.path.exists(pt):
229            pathlist.insert(0,pt)   
230    # search path for svn or svn.exe
231    for path in pathlist:
232        exe_file = os.path.join(path, svnprog)
233        if is_exe(exe_file):
234            try:
235                p = subprocess.Popen([exe_file,'help'],stdout=subprocess.PIPE)
236                res = p.stdout.read()
237                p.communicate()
238                svnLocCache = os.path.abspath(exe_file)
239                return svnLocCache
240            except:
241                pass       
242    svnLocCache = None
243
244def svnVersion(svn=None):
245    '''Get the version number of the current subversion executable
246
247    :returns: a string with a version number such as "1.6.6" or None if
248      subversion is not found.
249
250    '''
251    if not svn: svn = whichsvn()
252    if not svn: return
253
254    cmd = [svn,'--version','--quiet']
255    s = subprocess.Popen(cmd,
256                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
257    out,err = MakeByte2str(s.communicate())
258    if err:
259        print ('subversion error!\nout=%s'%out)
260        print ('err=%s'%err)
261        s = '\nsvn command:  '
262        for i in cmd: s += i + ' '
263        print(s)
264        return None
265    return out.strip()
266
267def svnVersionNumber(svn=None):
268    '''Get the version number of the current subversion executable
269
270    :returns: a fractional version number such as 1.6 or None if
271      subversion is not found.
272
273    '''
274    ver = svnVersion(svn)
275    if not ver: return 
276    M,m = ver.split('.')[:2]
277    return int(M)+int(m)/10.
278
279def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
280    '''Get the revision log information for a specific version of the specified package
281
282    :param str fpath: path to repository dictionary, defaults to directory where
283       the current file is located.
284    :param int version: the version number to be looked up or None (default)
285       for the latest version.
286
287    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
288
289    '''
290    import xml.etree.ElementTree as ET
291    svn = whichsvn()
292    if not svn: return
293    if version is not None:
294        vstr = '-r'+str(version)
295    else:
296        vstr = '-rHEAD'
297
298    cmd = [svn,'log',fpath,'--xml',vstr]
299    if proxycmds: cmd += proxycmds
300    s = subprocess.Popen(cmd,
301                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
302    out,err = MakeByte2str(s.communicate())
303    if err:
304        print ('out=%s'%out)
305        print ('err=%s'%err)
306        s = '\nsvn command:  '
307        for i in cmd: s += i + ' '
308        print(s)
309        return None
310    x = ET.fromstring(out)
311    d = {}
312    for i in x.iter('logentry'):
313        d = {'revision':i.attrib.get('revision','?')}
314        for j in i:
315            d[j.tag] = j.text
316        break # only need the first
317    return d
318
319svnLastError = ''
320def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
321    '''Obtain the version number for the either the last update of the local version
322    or contacts the subversion server to get the latest update version (# of Head).
323
324    :param str fpath: path to repository dictionary, defaults to directory where
325       the current file is located
326    :param bool local: determines the type of version number, where
327       True (default): returns the latest installed update
328       False: returns the version number of Head on the server
329
330    :Returns: the version number as an str or
331       None if there is a subversion error (likely because the path is
332       not a repository or svn is not found). The error message is placed in
333       global variable svnLastError
334    '''
335
336    import xml.etree.ElementTree as ET
337    svn = whichsvn()
338    if not svn: return
339    if local:
340        cmd = [svn,'info',fpath,'--xml']
341    else:
342        cmd = [svn,'info',fpath,'--xml','-rHEAD']
343    if svnVersionNumber() >= 1.6:
344        cmd += ['--non-interactive', '--trust-server-cert']
345    if proxycmds: cmd += proxycmds
346    # if GetConfigValue('debug'):
347    #     s = 'subversion command:\n  '
348    #     for i in cmd: s += i + ' '
349    #     print(s)
350    s = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
351    out,err = MakeByte2str(s.communicate())
352    if err:
353        print ('svn failed\n%s'%out)
354        print ('err=%s'%err)
355        s = '\nsvn command:  '
356        for i in cmd: s += i + ' '
357        print(s)
358        global svnLastError
359        svnLastError = err
360        return None
361    x = ET.fromstring(out)
362    for i in x.iter('entry'):
363        rev = i.attrib.get('revision')
364        if rev: return rev
365
366def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
367    '''Returns a list of files that were changed locally. If no files are changed,
368       the list has length 0
369
370    :param fpath: path to repository dictionary, defaults to directory where
371       the current file is located
372
373    :returns: None if there is a subversion error (likely because the path is
374       not a repository or svn is not found)
375
376    '''
377    import xml.etree.ElementTree as ET
378    svn = whichsvn()
379    if not svn: return
380    cmd = [svn,'status',fpath,'--xml']
381    if proxycmds: cmd += proxycmds
382    s = subprocess.Popen(cmd,
383                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
384    out,err = MakeByte2str(s.communicate())
385    if err: return None
386    x = ET.fromstring(out)
387    changed = []
388    for i in x.iter('entry'):
389        if i.find('wc-status').attrib.get('item') == 'modified': 
390            changed.append(i.attrib.get('path'))
391    return changed
392
393def svnCleanup(fpath=os.path.split(__file__)[0],verbose=True):
394    '''This runs svn cleanup on a selected local directory.
395
396    :param str fpath: path to repository dictionary, defaults to directory where
397       the current file is located
398    '''
399    svn = whichsvn()
400    if not svn: return
401    if verbose: print(u"Performing svn cleanup at "+fpath)
402    cmd = [svn,'cleanup',fpath]
403    if verbose:
404        s = 'subversion command:\n  '
405        for i in cmd: s += i + ' '
406        print(s)
407    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
408    out,err = MakeByte2str(s.communicate())
409    if err:
410        print(60*"=")
411        print("****** An error was noted, see below *********")
412        print(60*"=")
413        print(err)
414        s = '\nsvn command:  '
415        for i in cmd: s += i + ' '
416        print(s)
417        #raise Exception('svn cleanup failed')
418        return False
419    elif verbose:
420        print(out)
421    return True
422       
423def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None,verbose=True):
424    '''This performs an update of the files in a local directory from a server.
425
426    :param str fpath: path to repository dictionary, defaults to directory where
427       the current file is located
428    :param version: the number of the version to be loaded. Used only
429       cast as a string, but should be an integer or something that corresponds to a
430       string representation of an integer value when cast. A value of None (default)
431       causes the latest version on the server to be used.
432    '''
433    svn = whichsvn()
434    if not svn: return
435    if version:
436        verstr = '-r' + str(version)
437    else:
438        verstr = '-rHEAD'
439    if verbose: print(u"Updating files at "+fpath)
440    cmd = [svn,'update',fpath,verstr,
441           '--non-interactive',
442           '--accept','theirs-conflict','--force']
443    if svnVersionNumber() >= 1.6:
444        cmd += ['--trust-server-cert']
445    if proxycmds: cmd += proxycmds
446    #if verbose or GetConfigValue('debug'):
447    if verbose:
448        s = 'subversion command:\n  '
449        for i in cmd: s += i + ' '
450        print(s)
451    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
452    out,err = MakeByte2str(s.communicate())
453    if err:
454        print(60*"=")
455        print("****** An error was noted, see below *********")
456        print(60*"=")
457        print(err)
458        s = '\nsvn command:  '
459        for i in cmd: s += i + ' '
460        print(s)
461        if svnCleanup(fpath):
462            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
463            out,err = MakeByte2str(s.communicate())
464            if err:
465                print(60*"=")
466                print("****** Drat, failed again: *********")
467                print(60*"=")
468                print(err)
469            else:
470                return
471        raise Exception('svn update failed')
472    elif verbose:
473        print(out)
474
475def svnUpgrade(fpath=os.path.split(__file__)[0]):
476    '''This reformats subversion files, which may be needed if an upgrade of subversion is
477    done.
478
479    :param str fpath: path to repository dictionary, defaults to directory where
480       the current file is located
481    '''
482    svn = whichsvn()
483    if not svn: return
484    cmd = [svn,'upgrade',fpath,'--non-interactive']
485    if proxycmds: cmd += proxycmds
486    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
487    out,err = MakeByte2str(s.communicate())
488    if err:
489        print("svn upgrade did not happen (this is probably OK). Messages:")
490        print (err)
491        s = '\nsvn command:  '
492        for i in cmd: s += i + ' '
493        print(s)
494
495def svnUpdateProcess(version=None,projectfile=None,branch=None):
496    '''perform an update of GSAS-II in a separate python process'''
497    if not projectfile:
498        projectfile = ''
499    else:
500        projectfile = os.path.realpath(projectfile)
501        print ('restart using %s'%projectfile)
502    if branch:
503        version = branch
504    elif not version:
505        version = ''
506    else:
507        version = str(version)
508    # start the upgrade in a separate interpreter (avoids loading .pyd files)
509    ex = sys.executable
510    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
511        if os.path.exists(ex+'w'): ex += 'w'
512    proc = subprocess.Popen([ex,__file__,projectfile,version])
513    if sys.platform != "win32":
514        proc.wait()
515    sys.exit()
516
517def svnSwitchDir(rpath,filename,baseURL,loadpath=None,verbose=True):
518    '''This performs a switch command to move files between subversion trees.
519    Note that if the files were previously downloaded,
520    the switch command will update the files to the newest version.
521   
522    :param str rpath: path to locate files, relative to the GSAS-II
523      installation path (defaults to path2GSAS2)
524    :param str URL: the repository URL
525    :param str loadpath: the prefix for the path, if specified. Defaults to path2GSAS2
526    :param bool verbose: if True (default) diagnostics are printed
527    '''
528    svn = whichsvn()
529    if not svn: return
530    URL = baseURL[:]
531    if baseURL[-1] != '/':
532        URL = baseURL + '/' + filename
533    else:
534        URL = baseURL + filename
535    if loadpath:
536        fpath = os.path.join(loadpath,rpath,filename)
537    else:
538        fpath = os.path.join(path2GSAS2,rpath,filename)
539    cmd = [svn,'switch',URL,fpath,
540           '--non-interactive','--trust-server-cert',
541           '--accept','theirs-conflict','--force','-rHEAD']
542    if svnVersionNumber(svn) > 1.6: cmd += ['--ignore-ancestry']
543    if proxycmds: cmd += proxycmds
544    if verbose:
545        print(u"Loading files to "+fpath+u"\n  from "+URL)
546    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
547    out,err = MakeByte2str(s.communicate())
548    if err:
549        print(60*"=")
550        print ("****** An error was noted, see below *********")
551        print(60*"=")
552        print ('out=%s'%out)
553        print ('err=%s'%err)
554        s = '\nsvn command:  '
555        for i in cmd: s += i + ' '
556        print(s)
557        if svnCleanup(fpath):
558            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
559            out,err = MakeByte2str(s.communicate())
560            if err:
561                print(60*"=")
562                print("****** Drat, failed again: *********")
563                print(60*"=")
564                print(err)
565            else:
566                return True
567        return False
568    if verbose:
569        s = '\nsvn command:  '
570        for i in cmd: s += i + ' '
571        print(s)
572        print('\n=== Output from svn switch'+(43*'='))
573        print(out.strip())
574        print((70*'=')+'\n')
575    return True
576
577def svnInstallDir(URL,loadpath):
578    '''Load a subversion tree into a specified directory
579
580    :param str URL: the repository URL
581    :param str loadpath: path to locate files
582
583    '''
584    svn = whichsvn()
585    if not svn: return
586    cmd = [svn,'co',URL,loadpath,'--non-interactive']
587    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
588    print("Loading files from "+URL)
589    if proxycmds: cmd += proxycmds
590    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
591    out,err = MakeByte2str(s.communicate())   #this fails too easily
592    if err:
593        print(60*"=")
594        print ("****** An error was noted, see below *********")
595        print(60*"=")
596        print (err)
597        s = '\nsvn command:  '
598        for i in cmd: s += i + ' '
599        print(s)
600        if svnCleanup(fpath):
601            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
602            out,err = MakeByte2str(s.communicate())
603            if err:
604                print(60*"=")
605                print("****** Drat, failed again: *********")
606                print(60*"=")
607                print(err)
608                return False
609        else:
610            return False
611    print ("Files installed at: "+loadpath)
612    return True
613
614def GetBinaryPrefix():
615    if sys.platform == "win32":
616        prefix = 'win'
617    elif sys.platform == "darwin":
618        prefix = 'mac'
619    elif sys.platform.startswith("linux"):
620        prefix = 'linux'
621    else:
622        print(u'Unknown platform: '+sys.platform)
623        raise Exception('Unknown platform')
624    if platform.architecture()[0] == '64bit':
625        bits = '64'
626    else:
627        bits = '32'
628
629    # format current python version
630    pyver = 'p{}.{}'.format(*sys.version_info[0:2])
631
632    items = [prefix,bits,pyver]
633    return '_'.join(items)
634
635def svnList(URL,verbose=True):
636    '''Get a list of subdirectories from and svn repository
637    '''   
638    svn = whichsvn()
639    if not svn:
640        print('**** unable to load files: svn not found ****')
641        return ''
642    # get binaries matching the required type -- other than for the numpy version
643    cmd = [svn, 'list', URL,'--non-interactive', '--trust-server-cert']
644    if proxycmds: cmd += proxycmds
645    if verbose:
646        s = 'Running svn command:\n  '
647        for i in cmd: s += i + ' '
648        print(s)
649    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
650    res,err = MakeByte2str(p.communicate())
651    return res
652
653def DownloadG2Binaries(g2home,verbose=True):
654    '''Download GSAS-II binaries from appropriate section of the
655    GSAS-II svn repository based on the platform, numpy and Python
656    version
657    '''   
658    bindir = GetBinaryPrefix()
659    #npver = 'n{}.{}'.format(*np.__version__.split('.')[0:2])
660    inpver = intver(np.__version__)
661    svn = whichsvn()
662    if not svn:
663        print('**** unable to load files: svn not found ****')
664        return ''
665    # get binaries matching the required type -- other than for the numpy version
666    cmd = [svn, 'list', g2home + '/Binaries/','--non-interactive', '--trust-server-cert']
667    if proxycmds: cmd += proxycmds
668    if verbose:
669        s = 'Running svn command:\n  '
670        for i in cmd: s += i + ' '
671        print(s)
672    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
673    res,err = MakeByte2str(p.communicate())
674    versions = {}
675    for d in res.split():
676        if d.startswith(bindir):
677            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
678            versions[v] = d
679    intVersionsList = sorted(versions.keys())
680    if not intVersionsList:
681        print('No binaries located matching',bindir)
682        return
683    elif inpver < min(intVersionsList):
684        vsel = min(intVersionsList)
685        print('Warning: The current numpy version, {}, is older than\n\tthe oldest dist version, {}'
686              .format(np.__version__,fmtver(vsel)))
687    elif inpver >= max(intVersionsList):
688        vsel = max(intVersionsList)
689        if verbose: print(
690                'FYI: The current numpy version, {}, is newer than the newest dist version {}'
691                .format(np.__version__,fmtver(vsel)))
692    else:
693        vsel = min(intVersionsList)
694        for v in intVersionsList:
695            if v <= inpver:
696                vsel = v
697            else:
698                if verbose: print(
699                        'FYI: Selecting dist version {} as the current numpy version, {},\n\tis older than the next dist version {}'
700                        .format(fmtver(vsel),np.__version__,fmtver(v)))
701                break
702    distdir = g2home + '/Binaries/' + versions[vsel]
703    # switch reset command: distdir = g2home + '/trunk/bindist'
704    svnSwitchDir('bindist','',distdir,verbose=verbose)
705    return os.path.join(path2GSAS2,'bindist')
706
707# def svnTestBranch(loc=None):
708#     '''Returns the name of the branch directory if the installation has been switched.
709#     Returns none, if not a branch
710#     the test 2frame branch. False otherwise
711#     '''
712#     if loc is None: loc = path2GSAS2
713#     svn = whichsvn()
714#     if not svn:
715#         print('**** unable to load files: svn not found ****')
716#         return ''
717#     cmd = [svn, 'info', loc]
718#     if proxycmds: cmd += proxycmds
719#     p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
720#     res,err = MakeByte2str(p.communicate())
721#     for l in res.split('\n'):
722#         if "Relative URL:" in l: break
723#     if "/branch/" in l:
724#         return l[l.find("/branch/")+8:].strip()
725#     else:
726#         return None
727   
728def svnSwitch2branch(branch=None,loc=None,svnHome=None):
729    '''Switch to a subversion branch if specified. Switches to trunk otherwise.
730    '''
731    if svnHome is None: svnHome = g2home
732    svnURL = svnHome + '/trunk'
733    if branch:
734        if svnHome.endswith('/'):
735            svnURL = svnHome[:-1]
736        else:
737            svnURL = svnHome
738        if branch.startswith('/'):
739            svnURL += branch
740        else:
741            svnURL += '/' + branch
742    svnSwitchDir('','',svnURL,loadpath=loc)
743   
744
745def IPyBreak_base(userMsg=None):
746    '''A routine that invokes an IPython session at the calling location
747    This routine is only used when debug=True is set in config.py
748    '''
749    savehook = sys.excepthook # save the exception hook
750    try: 
751        from IPython.terminal.embed import InteractiveShellEmbed
752    except ImportError:
753        try:
754            # try the IPython 0.12 approach
755            from IPython.frontend.terminal.embed import InteractiveShellEmbed
756        except ImportError:
757            print ('IPython InteractiveShellEmbed not found')
758            return
759    import inspect
760    ipshell = InteractiveShellEmbed()
761
762    frame = inspect.currentframe().f_back
763    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
764    if userMsg: msg += userMsg
765    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
766    sys.excepthook = savehook # reset IPython's change to the exception hook
767
768try:
769    from IPython.core import ultratb
770except:
771    pass
772def exceptHook(*args):
773    '''A routine to be called when an exception occurs. It prints the traceback
774    with fancy formatting and then calls an IPython shell with the environment
775    of the exception location.
776   
777    This routine is only used when debug=True is set in config.py   
778    '''
779    import IPython.core
780    if sys.platform.startswith('win'):
781        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
782    else:
783        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
784
785    try: 
786        from IPython.terminal.embed import InteractiveShellEmbed
787    except ImportError:
788        try:
789            # try the IPython 0.12 approach
790            from IPython.frontend.terminal.embed import InteractiveShellEmbed
791        except ImportError:
792            print ('IPython InteractiveShellEmbed not found')
793            return
794    import inspect
795    frame = inspect.getinnerframes(args[2])[-1][0]
796    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
797    savehook = sys.excepthook # save the exception hook
798    try: # try IPython 5 call 1st
799        class c(object): pass
800        pseudomod = c() # create something that acts like a module
801        pseudomod.__dict__ = frame.f_locals
802        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
803    except:
804        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
805    sys.excepthook = savehook # reset IPython's change to the exception hook
806
807def DoNothing():
808    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
809    except when the debug option is set True in config.py
810    '''
811    pass 
812
813IPyBreak = DoNothing
814pdbBreak = DoNothing
815def InvokeDebugOpts():
816    'Called in GSASII.py to set up debug options'
817    if GetConfigValue('debug'):
818        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
819        import pdb
820        global pdbBreak
821        pdbBreak = pdb.set_trace
822        try:
823            import IPython
824            global IPyBreak
825            IPyBreak = IPyBreak_base
826            if any('SPYDER' in name for name in os.environ):
827                print('Running from Spyder, skipping exception trapping')
828            else:
829                sys.excepthook = exceptHook
830        except:
831            pass
832
833def TestSPG(fpth):
834    '''Test if pyspg.[so,.pyd] can be run from a location in the path
835    '''
836    if not os.path.exists(fpth): return False
837    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
838    savpath = sys.path[:]
839    sys.path = [fpth]
840    # test to see if a shared library can be used
841    try:
842        import pyspg
843        pyspg.sgforpy('P -1')
844    except Exception as err:
845        print(70*'=')
846        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
847        print(70*'=')
848        sys.path = savpath
849        return False
850    sys.path = savpath
851    return True
852   
853# see if a directory for local modifications is defined. If so, stick that in the path
854if os.path.exists(os.path.expanduser('~/.G2local/')):
855    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
856    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
857    files = ""
858    prev = None
859    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
860        f = os.path.split(f)[1]
861        if os.path.splitext(f)[0] == prev: continue
862        prev = os.path.splitext(f)[0]
863        if files: files += ", "
864        files += f
865    if files:
866        print("*"*75)
867        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
868        print("  "+files)
869        print("*"*75)
870
871BinaryPathLoaded = False
872def SetBinaryPath(printInfo=False, loadBinary=True):
873    '''
874    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
875   
876    This routine must be executed after GSASIIpath is imported and before any other
877    GSAS-II imports are done.
878    '''
879    # do this only once no matter how many times it is called
880    global BinaryPathLoaded
881    if BinaryPathLoaded: return
882    try:
883        inpver = intver(np.__version__)
884    except (AttributeError,TypeError): # happens on building docs
885        return
886    binpath = None
887    binprfx = GetBinaryPrefix()
888    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
889        # Look at bin directory (created by a local compile) before looking for standard dist files
890        searchpathlist = [os.path.join(loc,'bin')]
891        # also look for matching binary dist in loc/AllBinaries
892        versions = {}
893        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
894            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
895            versions[v] = d
896        searchpathlist = [os.path.join(loc,'bin')]
897        vmin = None
898        vmax = None
899        for v in sorted(versions.keys()):
900            if v <= inpver:
901                vmin = v
902            elif v > inpver:
903                vmax = v
904                break
905        if vmin in versions:
906            searchpathlist.append(versions[vmin])
907        if vmax in versions:
908            searchpathlist.append(versions[vmax])
909        searchpathlist.append(os.path.join(loc,'bindist'))
910        for fpth in searchpathlist:
911            if TestSPG(fpth):
912                binpath = fpth
913                break       
914        if binpath: break
915    if binpath:                                            # were GSAS-II binaries found
916        sys.path.insert(0,binpath)
917        if printInfo:
918            print('GSAS-II binary directory: {}'.format(binpath))
919        BinaryPathLoaded = True
920    elif not loadBinary:
921        raise Exception
922    else:                                                  # try loading them
923        if printInfo:
924            print('Attempting to download GSAS-II binary files...')
925        try:
926            binpath = DownloadG2Binaries(g2home)
927        except AttributeError:   # this happens when building in Read The Docs
928            if printInfo:
929                print('Problem with download')
930        if binpath and TestSPG(binpath):
931            if printInfo:
932                print('GSAS-II binary directory: {}'.format(binpath))
933            sys.path.insert(0,binpath)
934            BinaryPathLoaded = True
935        # this must be imported before anything that imports any .pyd/.so file for GSASII
936        else:
937            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
938            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
939            # patch: use old location based on the host OS and the python version, 
940            # path is relative to location of the script that is called as well as this file
941            BinaryPathLoaded = True
942            bindir = None
943            if sys.platform == "win32":
944                if platform.architecture()[0] == '64bit':
945                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
946                else:
947                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
948            elif sys.platform == "darwin":
949                if platform.architecture()[0] == '64bit':
950                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
951                else:
952                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
953                #if platform.mac_ver()[0].startswith('10.5.'):
954                #    bindir += '_10.5'
955            elif sys.platform.startswith("linux"):
956                if platform.architecture()[0] == '64bit':
957                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
958                else:
959                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
960            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
961            # Look at bin directory (created by a local compile) before standard dist
962            # that at the top of the path
963                fpth = os.path.join(loc,bindir)
964                binpath = fpth
965                if TestSPG(fpth):
966                    sys.path.insert(0,binpath)
967                    if printInfo:
968                        print('\n'+75*'*')
969                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
970                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
971                        print('  so we can learn what is not working on your installation.')
972                        print('GSAS-II binary directory: {}'.format(binpath))
973                        print(75*'*')
974                    break
975            else:
976            # end patch
977            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
978            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
979                if printInfo:
980                    print(75*'*')
981                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
982                    print(75*'*')
983                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
984
985    # add the data import and export directory to the search path
986    newpath = os.path.join(path2GSAS2,'imports')
987    if newpath not in sys.path: sys.path.append(newpath)
988    newpath = os.path.join(path2GSAS2,'exports')
989    if newpath not in sys.path: sys.path.append(newpath)
990
991    # setup read of config.py, if present
992    global configDict
993    try:
994        import config
995        configDict = config.__dict__
996        import inspect
997        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
998        if printInfo:
999            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
1000    except ImportError:
1001        configDict = {'Clip_on':True}
1002    except Exception as err:
1003        print(60*'*',"\nError reading config.py file")
1004        if printInfo:
1005            import traceback
1006            print(traceback.format_exc())
1007        print(60*'*')
1008        configDict = {'Clip_on':True}
1009
1010def MacStartGSASII(g2script,project=''):
1011    '''Start a new instance of GSAS-II by opening a new terminal window and starting
1012    a new GSAS-II process. Used on Mac OS X only.
1013
1014    :param str g2script: file name for the GSASII.py script
1015    :param str project: GSAS-II project (.gpx) file to be opened, default is blank
1016      which opens a new project
1017    '''
1018    if project and os.path.splitext(project)[1] != '.gpx':
1019        print('file {} cannot be used. Not GSAS-II project (.gpx) file'.format(project))
1020        return
1021    if project and not os.path.exists(project):
1022        print('file {} cannot be found.'.format(project))
1023        return 
1024    elif project:
1025        project = os.path.abspath(project)
1026    g2script = os.path.abspath(g2script)
1027    pythonapp = sys.executable
1028    if os.path.exists(pythonapp+'w'): pythonapp += 'w'
1029    script = '''
1030set python to "{}"
1031set appwithpath to "{}"
1032set filename to "{}"
1033
1034tell application "Terminal"
1035     activate
1036     do script python & " " & appwithpath & " " & filename & "; exit"
1037end tell
1038'''.format(pythonapp,g2script,project)
1039    subprocess.Popen(["osascript","-e",script])
1040
1041def findConda():
1042    '''Determines if GSAS-II has been installed as g2conda or gsas2full
1043    with conda located relative to this file.
1044    We could also look for conda relative to the python (sys.executable)
1045    image, but I don't want to muck around with python that someone else
1046    installed.
1047    '''
1048    parent = os.path.split(path2GSAS2)[0]
1049    if sys.platform != "win32":
1050        activate = os.path.join(parent,'bin','activate')
1051        conda = os.path.join(parent,'bin','conda')
1052    else:
1053        activate = os.path.join(parent,'Scripts','activate.bat')
1054        conda = os.path.join(parent,'condabin','conda.bat')
1055    if os.path.exists(activate) and os.path.exists(conda):
1056        return conda,activate
1057    else:
1058        return None
1059
1060def runScript(cmds=[], wait=False, G2frame=None):
1061    '''run a shell script of commands in an external process
1062   
1063    :param list cmds: a list of str's, each ietm containing a shell (cmd.exe
1064      or bash) command
1065    :param bool wait: if True indicates the commands should be run and then
1066      the script should return. If False, then the currently running Python
1067      will exit. Default is False
1068    :param wx.Frame G2frame: provides the location of the current .gpx file
1069      to be used to restart GSAS-II after running the commands, if wait
1070      is False. Default is None which prevents restarting GSAS-II regardless of
1071      the value of wait.
1072    '''
1073    import tempfile
1074    if not cmds:  #debug
1075        print('nothing to do in runScript')
1076        return
1077    if sys.platform != "win32":
1078        suffix = '.sh'
1079    else:
1080        suffix = '.bat'
1081       
1082    fp = tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False)
1083    shellname = fp.name
1084    for line in cmds:
1085        fp.write(line)
1086        fp.write('\n')
1087
1088    if not wait:
1089        if G2frame:
1090            projectfile = ''
1091            if G2frame.GSASprojectfile:
1092                projectfile = os.path.realpath(G2frame.GSASprojectfile)
1093            main = os.path.join(path2GSAS2,'GSASII.py')
1094            ex = sys.executable
1095            if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1096                if os.path.exists(ex+'w'): ex += 'w'
1097            print ('restart using ',' '.join([ex,main,projectfile]))
1098            fp.write(' '.join([ex,main,projectfile]))
1099            fp.write('\n')
1100    fp.close()
1101
1102    # start the upgrade in a separate interpreter (avoids loading .pyd files)
1103    if sys.platform != "win32":
1104        proc = subprocess.Popen(['bash',shellname])
1105    else:
1106        proc = subprocess.Popen([shellname],shell=True)
1107    if wait:
1108        proc.wait()
1109    else:
1110        if sys.platform != "win32": proc.wait()
1111        sys.exit()
1112   
1113if __name__ == '__main__':
1114    '''What follows is called to update (or downdate) GSAS-II in a separate process.
1115    '''
1116    import time
1117    time.sleep(1) # delay to give the main process a chance to exit
1118    # perform an update and restart GSAS-II
1119    project,version = sys.argv[1:3]
1120    loc = os.path.dirname(__file__)
1121    if version == 'trunk':
1122        svnSwitch2branch('')
1123    elif '/' in version:
1124        svnSwitch2branch(version)
1125    elif version:
1126        print("Regress to version "+str(version))
1127        svnUpdateDir(loc,version=version)
1128    else:
1129        print("Update to current version")
1130        svnUpdateDir(loc)
1131    ex = sys.executable
1132    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1133        if os.path.exists(ex+'w'): ex += 'w'
1134    if project:
1135        print("Restart GSAS-II with project file "+str(project))
1136        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
1137    else:
1138        print("Restart GSAS-II without a project file ")
1139        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
1140    print ('exiting update process')
1141    sys.exit()
Note: See TracBrowser for help on using the repository browser.