source: trunk/GSASIIpath.py @ 5047

Last change on this file since 5047 was 5047, checked in by toby, 17 months ago

CIF export fixes; work around for IsModified?() not showing changes

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