source: trunk/GSASIIpath.py @ 4740

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

more install work

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