source: trunk/GSASIIpath.py @ 4515

Last change on this file since 4515 was 4515, checked in by toby, 3 years ago

new capability: general parameter access

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