source: trunk/GSASIIpath.py

Last change on this file was 4994, checked in by toby, 2 months ago

fix proxy from env var

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 47.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIpath - file location & update routines
3########### SVN repository information ###################
4# $Date: 2021-07-08 23:38:57 +0000 (Thu, 08 Jul 2021) $
5# $Author: vondreele $
6# $Revision: 4994 $
7# $URL: trunk/GSASIIpath.py $
8# $Id: GSASIIpath.py 4994 2021-07-08 23:38:57Z vondreele $
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: 4994 $"
98      that is set by subversion when the file is retrieved from subversion.
99
100    Place ``GSASIIpath.SetVersionNumber("$Revision: 4994 $")`` 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    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
927    sys.excepthook = savehook # reset IPython's change to the exception hook
928
929try:
930    from IPython.core import ultratb
931except:
932    pass
933def exceptHook(*args):
934    '''A routine to be called when an exception occurs. It prints the traceback
935    with fancy formatting and then calls an IPython shell with the environment
936    of the exception location.
937   
938    This routine is only used when debug=True is set in config.py   
939    '''
940    import IPython.core
941    if sys.platform.startswith('win'):
942        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
943    else:
944        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
945
946    try: 
947        from IPython.terminal.embed import InteractiveShellEmbed
948    except ImportError:
949        try:
950            # try the IPython 0.12 approach
951            from IPython.frontend.terminal.embed import InteractiveShellEmbed
952        except ImportError:
953            print ('IPython InteractiveShellEmbed not found')
954            return
955    import inspect
956    frame = inspect.getinnerframes(args[2])[-1][0]
957    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
958    savehook = sys.excepthook # save the exception hook
959    try: # try IPython 5 call 1st
960        class c(object): pass
961        pseudomod = c() # create something that acts like a module
962        pseudomod.__dict__ = frame.f_locals
963        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
964    except:
965        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
966    sys.excepthook = savehook # reset IPython's change to the exception hook
967
968def DoNothing():
969    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
970    except when the debug option is set True in config.py
971    '''
972    pass 
973
974IPyBreak = DoNothing
975pdbBreak = DoNothing
976def InvokeDebugOpts():
977    'Called in GSASII.py to set up debug options'
978    if GetConfigValue('debug'):
979        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
980        import pdb
981        global pdbBreak
982        pdbBreak = pdb.set_trace
983        try:
984            import IPython
985            global IPyBreak
986            IPyBreak = IPyBreak_base
987            if any('SPYDER' in name for name in os.environ):
988                print('Running from Spyder, skipping exception trapping')
989            else:
990                sys.excepthook = exceptHook
991        except:
992            pass
993
994def TestSPG(fpth):
995    '''Test if pyspg.[so,.pyd] can be run from a location in the path
996    '''
997    if not os.path.exists(fpth): return False
998    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
999    savpath = sys.path[:]
1000    sys.path = [fpth]
1001    # test to see if a shared library can be used
1002    try:
1003        import pyspg
1004        pyspg.sgforpy('P -1')
1005    except Exception as err:
1006        print(70*'=')
1007        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
1008        print(70*'=')
1009        sys.path = savpath
1010        return False
1011    sys.path = savpath
1012    return True
1013   
1014# see if a directory for local modifications is defined. If so, stick that in the path
1015if os.path.exists(os.path.expanduser('~/.G2local/')):
1016    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
1017    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
1018    files = ""
1019    prev = None
1020    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
1021        f = os.path.split(f)[1]
1022        if os.path.splitext(f)[0] == prev: continue
1023        prev = os.path.splitext(f)[0]
1024        if files: files += ", "
1025        files += f
1026    if files:
1027        print("*"*75)
1028        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
1029        print("  "+files)
1030        print("*"*75)
1031
1032BinaryPathLoaded = False
1033binaryPath = ''
1034def SetBinaryPath(printInfo=False, loadBinary=True):
1035    '''
1036    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
1037   
1038    This routine must be executed after GSASIIpath is imported and before any other
1039    GSAS-II imports are done.
1040    '''
1041    # do this only once no matter how many times it is called
1042    global BinaryPathLoaded,binaryPath
1043    if BinaryPathLoaded: return
1044    try:
1045        inpver = intver(np.__version__)
1046    except (AttributeError,TypeError): # happens on building docs
1047        return
1048    binpath = None
1049    binprfx = GetBinaryPrefix()
1050    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
1051        # Look at bin directory (created by a local compile) before looking for standard dist files
1052        searchpathlist = [os.path.join(loc,'bin')]
1053        # also look for matching binary dist in loc/AllBinaries
1054        versions = {}
1055        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
1056            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
1057            versions[v] = d
1058        searchpathlist = [os.path.join(loc,'bin')]
1059        vmin = None
1060        vmax = None
1061        for v in sorted(versions.keys()):
1062            if v <= inpver:
1063                vmin = v
1064            elif v > inpver:
1065                vmax = v
1066                break
1067        if vmin in versions:
1068            searchpathlist.append(versions[vmin])
1069        if vmax in versions:
1070            searchpathlist.append(versions[vmax])
1071        searchpathlist.append(os.path.join(loc,'bindist'))
1072        for fpth in searchpathlist:
1073            if TestSPG(fpth):
1074                binpath = fpth
1075                break       
1076        if binpath: break
1077    if binpath:                                            # were GSAS-II binaries found
1078        sys.path.insert(0,binpath)
1079        binaryPath = binpath
1080        if printInfo:
1081            print('GSAS-II binary directory: {}'.format(binpath))
1082        BinaryPathLoaded = True
1083    elif not loadBinary:
1084        raise Exception
1085    else:                                                  # try loading them
1086        if printInfo:
1087            print('Attempting to download GSAS-II binary files...')
1088        try:
1089            binpath = DownloadG2Binaries(g2home)
1090        except AttributeError:   # this happens when building in Read The Docs
1091            if printInfo:
1092                print('Problem with download')
1093        if binpath and TestSPG(binpath):
1094            if printInfo:
1095                print('GSAS-II binary directory: {}'.format(binpath))
1096            sys.path.insert(0,binpath)
1097            binaryPath = binpath
1098            BinaryPathLoaded = True
1099        # this must be imported before anything that imports any .pyd/.so file for GSASII
1100        else:
1101            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1102            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1103            # patch: use old location based on the host OS and the python version, 
1104            # path is relative to location of the script that is called as well as this file
1105            BinaryPathLoaded = True
1106            bindir = None
1107            if sys.platform == "win32":
1108                if platform.architecture()[0] == '64bit':
1109                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
1110                else:
1111                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
1112            elif sys.platform == "darwin":
1113                if platform.architecture()[0] == '64bit':
1114                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
1115                else:
1116                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
1117                #if platform.mac_ver()[0].startswith('10.5.'):
1118                #    bindir += '_10.5'
1119            elif sys.platform.startswith("linux"):
1120                if platform.architecture()[0] == '64bit':
1121                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
1122                else:
1123                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
1124            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
1125            # Look at bin directory (created by a local compile) before standard dist
1126            # that at the top of the path
1127                fpth = os.path.join(loc,bindir)
1128                binpath = fpth
1129                if TestSPG(fpth):
1130                    sys.path.insert(0,binpath)
1131                    binaryPath = binpath
1132                    if printInfo:
1133                        print('\n'+75*'*')
1134                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
1135                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
1136                        print('  so we can learn what is not working on your installation.')
1137                        print('GSAS-II binary directory: {}'.format(binpath))
1138                        print(75*'*')
1139                    break
1140            else:
1141            # end patch
1142            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1143            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1144                if printInfo:
1145                    print(75*'*')
1146                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
1147                    print(75*'*')
1148                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
1149
1150    # add the data import and export directory to the search path
1151    newpath = os.path.join(path2GSAS2,'imports')
1152    if newpath not in sys.path: sys.path.append(newpath)
1153    newpath = os.path.join(path2GSAS2,'exports')
1154    if newpath not in sys.path: sys.path.append(newpath)
1155
1156    # setup read of config.py, if present
1157    global configDict
1158    try:
1159        import config
1160        configDict = config.__dict__
1161        import inspect
1162        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
1163        if printInfo:
1164            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
1165    except ImportError:
1166        configDict = {'Clip_on':True}
1167    except Exception as err:
1168        print(60*'*',"\nError reading config.py file")
1169        if printInfo:
1170            import traceback
1171            print(traceback.format_exc())
1172        print(60*'*')
1173        configDict = {'Clip_on':True}
1174
1175def MacStartGSASII(g2script,project=''):
1176    '''Start a new instance of GSAS-II by opening a new terminal window and starting
1177    a new GSAS-II process. Used on Mac OS X only.
1178
1179    :param str g2script: file name for the GSASII.py script
1180    :param str project: GSAS-II project (.gpx) file to be opened, default is blank
1181      which opens a new project
1182    '''
1183    if project and os.path.splitext(project)[1] != '.gpx':
1184        print('file {} cannot be used. Not GSAS-II project (.gpx) file'.format(project))
1185        return
1186    if project and not os.path.exists(project):
1187        print('file {} cannot be found.'.format(project))
1188        return 
1189    elif project:
1190        project = os.path.abspath(project)
1191    g2script = os.path.abspath(g2script)
1192    pythonapp = sys.executable
1193    if os.path.exists(pythonapp+'w'): pythonapp += 'w'
1194    script = '''
1195set python to "{}"
1196set appwithpath to "{}"
1197set filename to "{}"
1198
1199tell application "Terminal"
1200     activate
1201     do script python & " " & appwithpath & " " & filename & "; exit"
1202end tell
1203'''.format(pythonapp,g2script,project)
1204    subprocess.Popen(["osascript","-e",script])
1205
1206def MacRunScript(script):
1207    '''Start a bash script in a new terminal window.
1208    Used on Mac OS X only.
1209
1210    :param str script: file name for a bash script
1211    '''
1212    script = os.path.abspath(script)
1213    osascript = '''
1214set bash to "/bin/bash"
1215set filename to "{}"
1216
1217tell application "Terminal"
1218     activate
1219     do script bash & " " & filename & "; exit"
1220end tell
1221'''.format(script)
1222    subprocess.Popen(["osascript","-e",osascript])
1223   
1224def findConda():
1225    '''Determines if GSAS-II has been installed as g2conda or gsas2full
1226    with conda located relative to this file.
1227    We could also look for conda relative to the python (sys.executable)
1228    image, but I don't want to muck around with python that someone else
1229    installed.
1230    '''
1231    parent = os.path.split(path2GSAS2)[0]
1232    if sys.platform != "win32":
1233        activate = os.path.join(parent,'bin','activate')
1234        conda = os.path.join(parent,'bin','conda')
1235    else:
1236        activate = os.path.join(parent,'Scripts','activate.bat')
1237        conda = os.path.join(parent,'condabin','conda.bat')
1238    if os.path.exists(activate) and os.path.exists(conda):
1239        return conda,activate
1240    else:
1241        return None
1242
1243def runScript(cmds=[], wait=False, G2frame=None):
1244    '''run a shell script of commands in an external process
1245   
1246    :param list cmds: a list of str's, each ietm containing a shell (cmd.exe
1247      or bash) command
1248    :param bool wait: if True indicates the commands should be run and then
1249      the script should return. If False, then the currently running Python
1250      will exit. Default is False
1251    :param wx.Frame G2frame: provides the location of the current .gpx file
1252      to be used to restart GSAS-II after running the commands, if wait
1253      is False. Default is None which prevents restarting GSAS-II regardless of
1254      the value of wait.
1255    '''
1256    import tempfile
1257    if not cmds:  #debug
1258        print('nothing to do in runScript')
1259        return
1260    if sys.platform != "win32":
1261        suffix = '.sh'
1262    else:
1263        suffix = '.bat'
1264       
1265    fp = tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False)
1266    shellname = fp.name
1267    for line in cmds:
1268        fp.write(line)
1269        fp.write('\n')
1270
1271    if not wait:
1272        if G2frame:
1273            projectfile = ''
1274            if G2frame.GSASprojectfile:
1275                projectfile = os.path.realpath(G2frame.GSASprojectfile)
1276            main = os.path.join(path2GSAS2,'GSASII.py')
1277            ex = sys.executable
1278            if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1279                if os.path.exists(ex+'w'): ex += 'w'
1280            print ('restart using ',' '.join([ex,main,projectfile]))
1281            fp.write(' '.join([ex,main,projectfile]))
1282            fp.write('\n')
1283    fp.close()
1284
1285    # start the upgrade in a separate interpreter (avoids loading .pyd files)
1286    if sys.platform != "win32":
1287        proc = subprocess.Popen(['bash',shellname])
1288    else:
1289        proc = subprocess.Popen([shellname],shell=True)
1290    if wait:
1291        proc.wait()
1292    else:
1293        if sys.platform != "win32": proc.wait()
1294        sys.exit()
1295   
1296if __name__ == '__main__':
1297    '''What follows is called to update (or downdate) GSAS-II in a separate process.
1298    '''
1299    import time
1300    time.sleep(1) # delay to give the main process a chance to exit
1301    # perform an update and restart GSAS-II
1302    try:
1303        project,version = sys.argv[1:3]
1304    except ValueError:
1305        project = None
1306        version = 'trunk'
1307    loc = os.path.dirname(__file__)
1308    if version == 'trunk':
1309        svnSwitch2branch('')
1310    elif '/' in version:
1311        svnSwitch2branch(version)
1312    elif version:
1313        print("Regress to version "+str(version))
1314        svnUpdateDir(loc,version=version)
1315    else:
1316        print("Update to current version")
1317        svnUpdateDir(loc)
1318    ex = sys.executable
1319    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1320        if os.path.exists(ex+'w'): ex += 'w'
1321    if project:
1322        print("Restart GSAS-II with project file "+str(project))
1323        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
1324    else:
1325        print("Restart GSAS-II without a project file ")
1326        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
1327    print ('exiting update process')
1328    sys.exit()
Note: See TracBrowser for help on using the repository browser.