source: trunk/GSASIIpath.py @ 4135

Last change on this file since 4135 was 4091, checked in by toby, 6 years ago

delay close process until after update on Linux & Mac

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