source: trunk/GSASIIpath.py @ 5264

Last change on this file since 5264 was 5264, checked in by toby, 10 months ago

print weight fraction w/esd even if not directly varied

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