source: trunk/GSASIIpath.py @ 4426

Last change on this file since 4426 was 4396, checked in by toby, 5 years ago

improve end-of-life message, add tools to run bash/bat scripts outside GSAS-II

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 41.1 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: 4396 $"
87      that is set by subversion when the file is retrieved from subversion.
88
89    Place ``GSASIIpath.SetVersionNumber("$Revision: 4396 $")`` 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']
542    if svnVersionNumber(svn) > 1.6: cmd += ['--ignore-ancestry']
543    if proxycmds: cmd += proxycmds
544    if verbose: print(u"Loading files to "+fpath+u"\n  from "+URL)
545    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
546    out,err = MakeByte2str(s.communicate())
547    if err:
548        print(60*"=")
549        print ("****** An error was noted, see below *********")
550        print(60*"=")
551        print ('out=%s'%out)
552        print ('err=%s'%err)
553        s = '\nsvn command:  '
554        for i in cmd: s += i + ' '
555        print(s)
556        if svnCleanup(fpath):
557            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
558            out,err = MakeByte2str(s.communicate())
559            if err:
560                print(60*"=")
561                print("****** Drat, failed again: *********")
562                print(60*"=")
563                print(err)
564            else:
565                return True
566        return False
567    if verbose:
568        print('=== Output from svn switch'+(43*'='))
569        print(out.strip())
570        print((70*'=')+'\n')
571    return True
572
573def svnInstallDir(URL,loadpath):
574    '''Load a subversion tree into a specified directory
575
576    :param str URL: the repository URL
577    :param str loadpath: path to locate files
578
579    '''
580    svn = whichsvn()
581    if not svn: return
582    cmd = [svn,'co',URL,loadpath,'--non-interactive']
583    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
584    print("Loading files from "+URL)
585    if proxycmds: cmd += proxycmds
586    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
587    out,err = MakeByte2str(s.communicate())   #this fails too easily
588    if err:
589        print(60*"=")
590        print ("****** An error was noted, see below *********")
591        print(60*"=")
592        print (err)
593        s = '\nsvn command:  '
594        for i in cmd: s += i + ' '
595        print(s)
596        if svnCleanup(fpath):
597            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
598            out,err = MakeByte2str(s.communicate())
599            if err:
600                print(60*"=")
601                print("****** Drat, failed again: *********")
602                print(60*"=")
603                print(err)
604                return False
605        else:
606            return False
607    print ("Files installed at: "+loadpath)
608    return True
609
610def GetBinaryPrefix():
611    if sys.platform == "win32":
612        prefix = 'win'
613    elif sys.platform == "darwin":
614        prefix = 'mac'
615    elif sys.platform.startswith("linux"):
616        prefix = 'linux'
617    else:
618        print(u'Unknown platform: '+sys.platform)
619        raise Exception('Unknown platform')
620    if platform.architecture()[0] == '64bit':
621        bits = '64'
622    else:
623        bits = '32'
624
625    # format current python version
626    pyver = 'p{}.{}'.format(*sys.version_info[0:2])
627
628    items = [prefix,bits,pyver]
629    return '_'.join(items)
630
631def svnList(URL,verbose=True):
632    '''Get a list of subdirectories from and svn repository
633    '''   
634    svn = whichsvn()
635    if not svn:
636        print('**** unable to load files: svn not found ****')
637        return ''
638    # get binaries matching the required type -- other than for the numpy version
639    cmd = [svn, 'list', URL,'--non-interactive', '--trust-server-cert']
640    if proxycmds: cmd += proxycmds
641    if verbose:
642        s = 'Running svn command:\n  '
643        for i in cmd: s += i + ' '
644        print(s)
645    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
646    res,err = MakeByte2str(p.communicate())
647    return res
648
649def DownloadG2Binaries(g2home,verbose=True):
650    '''Download GSAS-II binaries from appropriate section of the
651    GSAS-II svn repository based on the platform, numpy and Python
652    version
653    '''   
654    bindir = GetBinaryPrefix()
655    #npver = 'n{}.{}'.format(*np.__version__.split('.')[0:2])
656    inpver = intver(np.__version__)
657    svn = whichsvn()
658    if not svn:
659        print('**** unable to load files: svn not found ****')
660        return ''
661    # get binaries matching the required type -- other than for the numpy version
662    cmd = [svn, 'list', g2home + '/Binaries/','--non-interactive', '--trust-server-cert']
663    if proxycmds: cmd += proxycmds
664    if verbose:
665        s = 'Running svn command:\n  '
666        for i in cmd: s += i + ' '
667        print(s)
668    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
669    res,err = MakeByte2str(p.communicate())
670    versions = {}
671    for d in res.split():
672        if d.startswith(bindir):
673            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
674            versions[v] = d
675    intVersionsList = sorted(versions.keys())
676    if not intVersionsList:
677        print('No binaries located matching',bindir)
678        return
679    elif inpver < min(intVersionsList):
680        vsel = min(intVersionsList)
681        print('Warning: The current numpy version, {}, is older than\n\tthe oldest dist version, {}'
682              .format(np.__version__,fmtver(vsel)))
683    elif inpver >= max(intVersionsList):
684        vsel = max(intVersionsList)
685        if verbose: print(
686                'FYI: The current numpy version, {}, is newer than the newest dist version {}'
687                .format(np.__version__,fmtver(vsel)))
688    else:
689        vsel = min(intVersionsList)
690        for v in intVersionsList:
691            if v <= inpver:
692                vsel = v
693            else:
694                if verbose: print(
695                        'FYI: Selecting dist version {} as the current numpy version, {},\n\tis older than the next dist version {}'
696                        .format(fmtver(vsel),np.__version__,fmtver(v)))
697                break
698    distdir = g2home + '/Binaries/' + versions[vsel]
699    # switch reset command: distdir = g2home + '/trunk/bindist'
700    svnSwitchDir('bindist','',distdir,verbose=verbose)
701    return os.path.join(path2GSAS2,'bindist')
702
703# def svnTestBranch(loc=None):
704#     '''Returns the name of the branch directory if the installation has been switched.
705#     Returns none, if not a branch
706#     the test 2frame branch. False otherwise
707#     '''
708#     if loc is None: loc = path2GSAS2
709#     svn = whichsvn()
710#     if not svn:
711#         print('**** unable to load files: svn not found ****')
712#         return ''
713#     cmd = [svn, 'info', loc]
714#     if proxycmds: cmd += proxycmds
715#     p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
716#     res,err = MakeByte2str(p.communicate())
717#     for l in res.split('\n'):
718#         if "Relative URL:" in l: break
719#     if "/branch/" in l:
720#         return l[l.find("/branch/")+8:].strip()
721#     else:
722#         return None
723   
724def svnSwitch2branch(branch=None,loc=None,svnHome=None):
725    '''Switch to a subversion branch if specified. Switches to trunk otherwise.
726    '''
727    if svnHome is None: svnHome = g2home
728    svnURL = svnHome + '/trunk'
729    if branch:
730        if svnHome.endswith('/'):
731            svnURL = svnHome[:-1]
732        else:
733            svnURL = svnHome
734        if branch.startswith('/'):
735            svnURL += branch
736        else:
737            svnURL += '/' + branch
738    svnSwitchDir('','',svnURL,loadpath=loc)
739   
740
741def IPyBreak_base(userMsg=None):
742    '''A routine that invokes an IPython session at the calling location
743    This routine is only used when debug=True is set in config.py
744    '''
745    savehook = sys.excepthook # save the exception hook
746    try: 
747        from IPython.terminal.embed import InteractiveShellEmbed
748    except ImportError:
749        try:
750            # try the IPython 0.12 approach
751            from IPython.frontend.terminal.embed import InteractiveShellEmbed
752        except ImportError:
753            print ('IPython InteractiveShellEmbed not found')
754            return
755    import inspect
756    ipshell = InteractiveShellEmbed()
757
758    frame = inspect.currentframe().f_back
759    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
760    if userMsg: msg += userMsg
761    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
762    sys.excepthook = savehook # reset IPython's change to the exception hook
763
764try:
765    from IPython.core import ultratb
766except:
767    pass
768def exceptHook(*args):
769    '''A routine to be called when an exception occurs. It prints the traceback
770    with fancy formatting and then calls an IPython shell with the environment
771    of the exception location.
772   
773    This routine is only used when debug=True is set in config.py   
774    '''
775    import IPython.core
776    if sys.platform.startswith('win'):
777        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
778    else:
779        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
780
781    try: 
782        from IPython.terminal.embed import InteractiveShellEmbed
783    except ImportError:
784        try:
785            # try the IPython 0.12 approach
786            from IPython.frontend.terminal.embed import InteractiveShellEmbed
787        except ImportError:
788            print ('IPython InteractiveShellEmbed not found')
789            return
790    import inspect
791    frame = inspect.getinnerframes(args[2])[-1][0]
792    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
793    savehook = sys.excepthook # save the exception hook
794    try: # try IPython 5 call 1st
795        class c(object): pass
796        pseudomod = c() # create something that acts like a module
797        pseudomod.__dict__ = frame.f_locals
798        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
799    except:
800        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
801    sys.excepthook = savehook # reset IPython's change to the exception hook
802
803def DoNothing():
804    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
805    except when the debug option is set True in config.py
806    '''
807    pass 
808
809IPyBreak = DoNothing
810pdbBreak = DoNothing
811def InvokeDebugOpts():
812    'Called in GSASII.py to set up debug options'
813    if GetConfigValue('debug'):
814        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
815        import pdb
816        global pdbBreak
817        pdbBreak = pdb.set_trace
818        try:
819            import IPython
820            global IPyBreak
821            IPyBreak = IPyBreak_base
822            if any('SPYDER' in name for name in os.environ):
823                print('Running from Spyder, skipping exception trapping')
824            else:
825                sys.excepthook = exceptHook
826        except:
827            pass
828
829def TestSPG(fpth):
830    '''Test if pyspg.[so,.pyd] can be run from a location in the path
831    '''
832    if not os.path.exists(fpth): return False
833    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
834    savpath = sys.path[:]
835    sys.path = [fpth]
836    # test to see if a shared library can be used
837    try:
838        import pyspg
839        pyspg.sgforpy('P -1')
840    except Exception as err:
841        print(70*'=')
842        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
843        print(70*'=')
844        sys.path = savpath
845        return False
846    sys.path = savpath
847    return True
848   
849# see if a directory for local modifications is defined. If so, stick that in the path
850if os.path.exists(os.path.expanduser('~/.G2local/')):
851    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
852    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
853    files = ""
854    prev = None
855    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
856        f = os.path.split(f)[1]
857        if os.path.splitext(f)[0] == prev: continue
858        prev = os.path.splitext(f)[0]
859        if files: files += ", "
860        files += f
861    if files:
862        print("*"*75)
863        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
864        print("  "+files)
865        print("*"*75)
866
867BinaryPathLoaded = False
868def SetBinaryPath(printInfo=False, loadBinary=True):
869    '''
870    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
871   
872    This routine must be executed after GSASIIpath is imported and before any other
873    GSAS-II imports are done.
874    '''
875    # do this only once no matter how many times it is called
876    global BinaryPathLoaded
877    if BinaryPathLoaded: return
878    try:
879        inpver = intver(np.__version__)
880    except (AttributeError,TypeError): # happens on building docs
881        return
882    binpath = None
883    binprfx = GetBinaryPrefix()
884    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
885        # Look at bin directory (created by a local compile) before looking for standard dist files
886        searchpathlist = [os.path.join(loc,'bin')]
887        # also look for matching binary dist in loc/AllBinaries
888        versions = {}
889        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
890            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
891            versions[v] = d
892        searchpathlist = [os.path.join(loc,'bin')]
893        vmin = None
894        vmax = None
895        for v in sorted(versions.keys()):
896            if v <= inpver:
897                vmin = v
898            elif v > inpver:
899                vmax = v
900                break
901        if vmin in versions:
902            searchpathlist.append(versions[vmin])
903        if vmax in versions:
904            searchpathlist.append(versions[vmax])
905        searchpathlist.append(os.path.join(loc,'bindist'))
906        for fpth in searchpathlist:
907            if TestSPG(fpth):
908                binpath = fpth
909                break       
910        if binpath: break
911    if binpath:                                            # were GSAS-II binaries found
912        sys.path.insert(0,binpath)
913        if printInfo:
914            print('GSAS-II binary directory: {}'.format(binpath))
915        BinaryPathLoaded = True
916    elif not loadBinary:
917        raise Exception
918    else:                                                  # try loading them
919        if printInfo:
920            print('Attempting to download GSAS-II binary files...')
921        try:
922            binpath = DownloadG2Binaries(g2home)
923        except AttributeError:   # this happens when building in Read The Docs
924            if printInfo:
925                print('Problem with download')
926        if binpath and TestSPG(binpath):
927            if printInfo:
928                print('GSAS-II binary directory: {}'.format(binpath))
929            sys.path.insert(0,binpath)
930            BinaryPathLoaded = True
931        # this must be imported before anything that imports any .pyd/.so file for GSASII
932        else:
933            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
934            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
935            # patch: use old location based on the host OS and the python version, 
936            # path is relative to location of the script that is called as well as this file
937            BinaryPathLoaded = True
938            bindir = None
939            if sys.platform == "win32":
940                if platform.architecture()[0] == '64bit':
941                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
942                else:
943                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
944            elif sys.platform == "darwin":
945                if platform.architecture()[0] == '64bit':
946                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
947                else:
948                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
949                #if platform.mac_ver()[0].startswith('10.5.'):
950                #    bindir += '_10.5'
951            elif sys.platform.startswith("linux"):
952                if platform.architecture()[0] == '64bit':
953                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
954                else:
955                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
956            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
957            # Look at bin directory (created by a local compile) before standard dist
958            # that at the top of the path
959                fpth = os.path.join(loc,bindir)
960                binpath = fpth
961                if TestSPG(fpth):
962                    sys.path.insert(0,binpath)
963                    if printInfo:
964                        print('\n'+75*'*')
965                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
966                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
967                        print('  so we can learn what is not working on your installation.')
968                        print('GSAS-II binary directory: {}'.format(binpath))
969                        print(75*'*')
970                    break
971            else:
972            # end patch
973            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
974            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
975                if printInfo:
976                    print(75*'*')
977                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
978                    print(75*'*')
979                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
980
981    # add the data import and export directory to the search path
982    newpath = os.path.join(path2GSAS2,'imports')
983    if newpath not in sys.path: sys.path.append(newpath)
984    newpath = os.path.join(path2GSAS2,'exports')
985    if newpath not in sys.path: sys.path.append(newpath)
986
987    # setup read of config.py, if present
988    global configDict
989    try:
990        import config
991        configDict = config.__dict__
992        import inspect
993        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
994        if printInfo:
995            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
996    except ImportError:
997        configDict = {'Clip_on':True}
998    except Exception as err:
999        print(60*'*',"\nError reading config.py file")
1000        if printInfo:
1001            import traceback
1002            print(traceback.format_exc())
1003        print(60*'*')
1004        configDict = {'Clip_on':True}
1005
1006def MacStartGSASII(g2script,project=''):
1007    '''Start a new instance of GSAS-II by opening a new terminal window and starting
1008    a new GSAS-II process. Used on Mac OS X only.
1009
1010    :param str g2script: file name for the GSASII.py script
1011    :param str project: GSAS-II project (.gpx) file to be opened, default is blank
1012      which opens a new project
1013    '''
1014    if project and os.path.splitext(project)[1] != '.gpx':
1015        print('file {} cannot be used. Not GSAS-II project (.gpx) file'.format(project))
1016        return
1017    if project and not os.path.exists(project):
1018        print('file {} cannot be found.'.format(project))
1019        return 
1020    elif project:
1021        project = os.path.abspath(project)
1022    g2script = os.path.abspath(g2script)
1023    pythonapp = sys.executable
1024    if os.path.exists(pythonapp+'w'): pythonapp += 'w'
1025    script = '''
1026set python to "{}"
1027set appwithpath to "{}"
1028set filename to "{}"
1029
1030tell application "Terminal"
1031     activate
1032     do script python & " " & appwithpath & " " & filename & "; exit"
1033end tell
1034'''.format(pythonapp,g2script,project)
1035    subprocess.Popen(["osascript","-e",script])
1036
1037def findConda():
1038    '''Determines if GSAS-II has been installed as g2conda or gsas2full
1039    with conda located relative to this file.
1040    We could also look for conda relative to the python (sys.executable)
1041    image, but I don't want to muck around with python that someone else
1042    installed.
1043    '''
1044    parent = os.path.split(path2GSAS2)[0]
1045    if sys.platform != "win32":
1046        activate = os.path.join(parent,'bin','activate')
1047        conda = os.path.join(parent,'bin','conda')
1048    else:
1049        activate = os.path.join(parent,'Scripts','activate.bat')
1050        conda = os.path.join(parent,'condabin','conda.bat')
1051    if os.path.exists(activate) and os.path.exists(conda):
1052        return conda,activate
1053    else:
1054        return None
1055
1056def runScript(cmds=[], wait=False, G2frame=None):
1057    '''run a shell script of commands in an external process
1058   
1059    :param list cmds: a list of str's, each ietm containing a shell (cmd.exe
1060      or bash) command
1061    :param bool wait: if True indicates the commands should be run and then
1062      the script should return. If False, then the currently running Python
1063      will exit. Default is False
1064    :param wx.Frame G2frame: provides the location of the current .gpx file
1065      to be used to restart GSAS-II after running the commands, if wait
1066      is False. Default is None which prevents restarting GSAS-II regardless of
1067      the value of wait.
1068    '''
1069    import tempfile
1070    if not cmds:  #debug
1071        print('nothing to do in runScript')
1072        return
1073    if sys.platform != "win32":
1074        suffix = '.sh'
1075    else:
1076        suffix = '.bat'
1077       
1078    fp = tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False)
1079    shellname = fp.name
1080    for line in cmds:
1081        fp.write(line)
1082        fp.write('\n')
1083
1084    if not wait:
1085        if G2frame:
1086            projectfile = ''
1087            if G2frame.GSASprojectfile:
1088                projectfile = os.path.realpath(G2frame.GSASprojectfile)
1089            main = os.path.join(path2GSAS2,'GSASII.py')
1090            ex = sys.executable
1091            if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1092                if os.path.exists(ex+'w'): ex += 'w'
1093            print ('restart using ',' '.join([ex,main,projectfile]))
1094            fp.write(' '.join([ex,main,projectfile]))
1095            fp.write('\n')
1096    fp.close()
1097
1098    # start the upgrade in a separate interpreter (avoids loading .pyd files)
1099    if sys.platform != "win32":
1100        proc = subprocess.Popen(['bash',shellname])
1101    else:
1102        proc = subprocess.Popen([shellname],shell=True)
1103    if wait:
1104        proc.wait()
1105    else:
1106        if sys.platform != "win32": proc.wait()
1107        sys.exit()
1108   
1109if __name__ == '__main__':
1110    '''What follows is called to update (or downdate) GSAS-II in a separate process.
1111    '''
1112    import time
1113    time.sleep(1) # delay to give the main process a chance to exit
1114    # perform an update and restart GSAS-II
1115    project,version = sys.argv[1:3]
1116    loc = os.path.dirname(__file__)
1117    if version == 'trunk':
1118        svnSwitch2branch('')
1119    elif '/' in version:
1120        svnSwitch2branch(version)
1121    elif version:
1122        print("Regress to version "+str(version))
1123        svnUpdateDir(loc,version=version)
1124    else:
1125        print("Update to current version")
1126        svnUpdateDir(loc)
1127    ex = sys.executable
1128    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1129        if os.path.exists(ex+'w'): ex += 'w'
1130    if project:
1131        print("Restart GSAS-II with project file "+str(project))
1132        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
1133    else:
1134        print("Restart GSAS-II without a project file ")
1135        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
1136    print ('exiting update process')
1137    sys.exit()
Note: See TracBrowser for help on using the repository browser.