source: trunk/GSASIIpath.py @ 4887

Last change on this file since 4887 was 4887, checked in by toby, 2 years ago

deal with 0 on elem sym; save binary path; support ..._exec in config

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 46.9 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIpath - file location & update routines
3########### SVN repository information ###################
4# $Date: 2021-04-20 02:50:52 +0000 (Tue, 20 Apr 2021) $
5# $Author: toby $
6# $Revision: 4887 $
7# $URL: trunk/GSASIIpath.py $
8# $Id: GSASIIpath.py 4887 2021-04-20 02:50:52Z 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: 4887 $"
98      that is set by subversion when the file is retrieved from subversion.
99
100    Place ``GSASIIpath.SetVersionNumber("$Revision: 4887 $")`` 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(pyver=None):
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    if pyver:
770        pyver = 'p'+pyver
771    else:
772        pyver = 'p{}.{}'.format(*sys.version_info[0:2])
773
774    items = [prefix,bits,pyver]
775    return '_'.join(items)
776
777def svnList(URL,verbose=True):
778    '''Get a list of subdirectories from and svn repository
779    '''   
780    svn = whichsvn()
781    if not svn:
782        print('**** unable to load files: svn not found ****')
783        return ''
784    # get binaries matching the required type -- other than for the numpy version
785    cmd = [svn, 'list', URL,'--non-interactive', '--trust-server-cert']
786    if proxycmds: cmd += proxycmds
787    if verbose:
788        s = 'Running svn command:\n  '
789        for i in cmd: s += i + ' '
790        print(s)
791    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
792    res,err = MakeByte2str(p.communicate())
793    return res
794
795def DownloadG2Binaries(g2home,verbose=True):
796    '''Download GSAS-II binaries from appropriate section of the
797    GSAS-II svn repository based on the platform, numpy and Python
798    version
799    '''   
800    bindir = GetBinaryPrefix()
801    #npver = 'n{}.{}'.format(*np.__version__.split('.')[0:2])
802    inpver = intver(np.__version__)
803    svn = whichsvn()
804    if not svn:
805        print('**** unable to load files: svn not found ****')
806        return ''
807    # get binaries matching the required type -- other than for the numpy version
808    cmd = [svn, 'list', g2home + '/Binaries/','--non-interactive', '--trust-server-cert']
809    if proxycmds: cmd += proxycmds
810    if verbose:
811        s = 'Running svn command:\n  '
812        for i in cmd: s += i + ' '
813        print(s)
814    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
815    res,err = MakeByte2str(p.communicate())
816    versions = {}
817    for d in res.split():
818        if d.startswith(bindir):
819            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
820            versions[v] = d
821    intVersionsList = sorted(versions.keys())
822    if not intVersionsList:
823        print('No binaries located matching',bindir)
824        return
825    elif inpver < min(intVersionsList):
826        vsel = min(intVersionsList)
827        print('Warning: The current numpy version, {}, is older than\n\tthe oldest dist version, {}'
828              .format(np.__version__,fmtver(vsel)))
829    elif inpver >= max(intVersionsList):
830        vsel = max(intVersionsList)
831        if verbose: print(
832                'FYI: The current numpy version, {}, is newer than the newest dist version {}'
833                .format(np.__version__,fmtver(vsel)))
834    else:
835        vsel = min(intVersionsList)
836        for v in intVersionsList:
837            if v <= inpver:
838                vsel = v
839            else:
840                if verbose: print(
841                        'FYI: Selecting dist version {} as the current numpy version, {},\n\tis older than the next dist version {}'
842                        .format(fmtver(vsel),np.__version__,fmtver(v)))
843                break
844    distdir = g2home + '/Binaries/' + versions[vsel]
845    # switch reset command: distdir = g2home + '/trunk/bindist'
846    svnSwitchDir('bindist','',distdir,verbose=verbose)
847    return os.path.join(path2GSAS2,'bindist')
848
849# def svnTestBranch(loc=None):
850#     '''Returns the name of the branch directory if the installation has been switched.
851#     Returns none, if not a branch
852#     the test 2frame branch. False otherwise
853#     '''
854#     if loc is None: loc = path2GSAS2
855#     svn = whichsvn()
856#     if not svn:
857#         print('**** unable to load files: svn not found ****')
858#         return ''
859#     cmd = [svn, 'info', loc]
860#     if proxycmds: cmd += proxycmds
861#     p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
862#     res,err = MakeByte2str(p.communicate())
863#     for l in res.split('\n'):
864#         if "Relative URL:" in l: break
865#     if "/branch/" in l:
866#         return l[l.find("/branch/")+8:].strip()
867#     else:
868#         return None
869   
870def svnSwitch2branch(branch=None,loc=None,svnHome=None):
871    '''Switch to a subversion branch if specified. Switches to trunk otherwise.
872    '''
873    if svnHome is None: svnHome = g2home
874    svnURL = svnHome + '/trunk'
875    if branch:
876        if svnHome.endswith('/'):
877            svnURL = svnHome[:-1]
878        else:
879            svnURL = svnHome
880        if branch.startswith('/'):
881            svnURL += branch
882        else:
883            svnURL += '/' + branch
884    svnSwitchDir('','',svnURL,loadpath=loc)
885   
886
887def IPyBreak_base(userMsg=None):
888    '''A routine that invokes an IPython session at the calling location
889    This routine is only used when debug=True is set in config.py
890    '''
891    savehook = sys.excepthook # save the exception hook
892    try: 
893        from IPython.terminal.embed import InteractiveShellEmbed
894    except ImportError:
895        try:
896            # try the IPython 0.12 approach
897            from IPython.frontend.terminal.embed import InteractiveShellEmbed
898        except ImportError:
899            print ('IPython InteractiveShellEmbed not found')
900            return
901    import inspect
902    ipshell = InteractiveShellEmbed()
903
904    frame = inspect.currentframe().f_back
905    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
906    if userMsg: msg += userMsg
907    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
908    sys.excepthook = savehook # reset IPython's change to the exception hook
909
910try:
911    from IPython.core import ultratb
912except:
913    pass
914def exceptHook(*args):
915    '''A routine to be called when an exception occurs. It prints the traceback
916    with fancy formatting and then calls an IPython shell with the environment
917    of the exception location.
918   
919    This routine is only used when debug=True is set in config.py   
920    '''
921    import IPython.core
922    if sys.platform.startswith('win'):
923        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
924    else:
925        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
926
927    try: 
928        from IPython.terminal.embed import InteractiveShellEmbed
929    except ImportError:
930        try:
931            # try the IPython 0.12 approach
932            from IPython.frontend.terminal.embed import InteractiveShellEmbed
933        except ImportError:
934            print ('IPython InteractiveShellEmbed not found')
935            return
936    import inspect
937    frame = inspect.getinnerframes(args[2])[-1][0]
938    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
939    savehook = sys.excepthook # save the exception hook
940    try: # try IPython 5 call 1st
941        class c(object): pass
942        pseudomod = c() # create something that acts like a module
943        pseudomod.__dict__ = frame.f_locals
944        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
945    except:
946        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
947    sys.excepthook = savehook # reset IPython's change to the exception hook
948
949def DoNothing():
950    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
951    except when the debug option is set True in config.py
952    '''
953    pass 
954
955IPyBreak = DoNothing
956pdbBreak = DoNothing
957def InvokeDebugOpts():
958    'Called in GSASII.py to set up debug options'
959    if GetConfigValue('debug'):
960        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
961        import pdb
962        global pdbBreak
963        pdbBreak = pdb.set_trace
964        try:
965            import IPython
966            global IPyBreak
967            IPyBreak = IPyBreak_base
968            if any('SPYDER' in name for name in os.environ):
969                print('Running from Spyder, skipping exception trapping')
970            else:
971                sys.excepthook = exceptHook
972        except:
973            pass
974
975def TestSPG(fpth):
976    '''Test if pyspg.[so,.pyd] can be run from a location in the path
977    '''
978    if not os.path.exists(fpth): return False
979    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
980    savpath = sys.path[:]
981    sys.path = [fpth]
982    # test to see if a shared library can be used
983    try:
984        import pyspg
985        pyspg.sgforpy('P -1')
986    except Exception as err:
987        print(70*'=')
988        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
989        print(70*'=')
990        sys.path = savpath
991        return False
992    sys.path = savpath
993    return True
994   
995# see if a directory for local modifications is defined. If so, stick that in the path
996if os.path.exists(os.path.expanduser('~/.G2local/')):
997    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
998    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
999    files = ""
1000    prev = None
1001    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
1002        f = os.path.split(f)[1]
1003        if os.path.splitext(f)[0] == prev: continue
1004        prev = os.path.splitext(f)[0]
1005        if files: files += ", "
1006        files += f
1007    if files:
1008        print("*"*75)
1009        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
1010        print("  "+files)
1011        print("*"*75)
1012
1013BinaryPathLoaded = False
1014binaryPath = ''
1015def SetBinaryPath(printInfo=False, loadBinary=True):
1016    '''
1017    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
1018   
1019    This routine must be executed after GSASIIpath is imported and before any other
1020    GSAS-II imports are done.
1021    '''
1022    # do this only once no matter how many times it is called
1023    global BinaryPathLoaded,binaryPath
1024    if BinaryPathLoaded: return
1025    try:
1026        inpver = intver(np.__version__)
1027    except (AttributeError,TypeError): # happens on building docs
1028        return
1029    binpath = None
1030    binprfx = GetBinaryPrefix()
1031    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
1032        # Look at bin directory (created by a local compile) before looking for standard dist files
1033        searchpathlist = [os.path.join(loc,'bin')]
1034        # also look for matching binary dist in loc/AllBinaries
1035        versions = {}
1036        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
1037            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
1038            versions[v] = d
1039        searchpathlist = [os.path.join(loc,'bin')]
1040        vmin = None
1041        vmax = None
1042        for v in sorted(versions.keys()):
1043            if v <= inpver:
1044                vmin = v
1045            elif v > inpver:
1046                vmax = v
1047                break
1048        if vmin in versions:
1049            searchpathlist.append(versions[vmin])
1050        if vmax in versions:
1051            searchpathlist.append(versions[vmax])
1052        searchpathlist.append(os.path.join(loc,'bindist'))
1053        for fpth in searchpathlist:
1054            if TestSPG(fpth):
1055                binpath = fpth
1056                break       
1057        if binpath: break
1058    if binpath:                                            # were GSAS-II binaries found
1059        sys.path.insert(0,binpath)
1060        binaryPath = binpath
1061        if printInfo:
1062            print('GSAS-II binary directory: {}'.format(binpath))
1063        BinaryPathLoaded = True
1064    elif not loadBinary:
1065        raise Exception
1066    else:                                                  # try loading them
1067        if printInfo:
1068            print('Attempting to download GSAS-II binary files...')
1069        try:
1070            binpath = DownloadG2Binaries(g2home)
1071        except AttributeError:   # this happens when building in Read The Docs
1072            if printInfo:
1073                print('Problem with download')
1074        if binpath and TestSPG(binpath):
1075            if printInfo:
1076                print('GSAS-II binary directory: {}'.format(binpath))
1077            sys.path.insert(0,binpath)
1078            binaryPath = binpath
1079            BinaryPathLoaded = True
1080        # this must be imported before anything that imports any .pyd/.so file for GSASII
1081        else:
1082            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1083            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1084            # patch: use old location based on the host OS and the python version, 
1085            # path is relative to location of the script that is called as well as this file
1086            BinaryPathLoaded = True
1087            bindir = None
1088            if sys.platform == "win32":
1089                if platform.architecture()[0] == '64bit':
1090                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
1091                else:
1092                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
1093            elif sys.platform == "darwin":
1094                if platform.architecture()[0] == '64bit':
1095                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
1096                else:
1097                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
1098                #if platform.mac_ver()[0].startswith('10.5.'):
1099                #    bindir += '_10.5'
1100            elif sys.platform.startswith("linux"):
1101                if platform.architecture()[0] == '64bit':
1102                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
1103                else:
1104                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
1105            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
1106            # Look at bin directory (created by a local compile) before standard dist
1107            # that at the top of the path
1108                fpth = os.path.join(loc,bindir)
1109                binpath = fpth
1110                if TestSPG(fpth):
1111                    sys.path.insert(0,binpath)
1112                    binaryPath = binpath
1113                    if printInfo:
1114                        print('\n'+75*'*')
1115                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
1116                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
1117                        print('  so we can learn what is not working on your installation.')
1118                        print('GSAS-II binary directory: {}'.format(binpath))
1119                        print(75*'*')
1120                    break
1121            else:
1122            # end patch
1123            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1124            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1125                if printInfo:
1126                    print(75*'*')
1127                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
1128                    print(75*'*')
1129                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
1130
1131    # add the data import and export directory to the search path
1132    newpath = os.path.join(path2GSAS2,'imports')
1133    if newpath not in sys.path: sys.path.append(newpath)
1134    newpath = os.path.join(path2GSAS2,'exports')
1135    if newpath not in sys.path: sys.path.append(newpath)
1136
1137    # setup read of config.py, if present
1138    global configDict
1139    try:
1140        import config
1141        configDict = config.__dict__
1142        import inspect
1143        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
1144        if printInfo:
1145            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
1146    except ImportError:
1147        configDict = {'Clip_on':True}
1148    except Exception as err:
1149        print(60*'*',"\nError reading config.py file")
1150        if printInfo:
1151            import traceback
1152            print(traceback.format_exc())
1153        print(60*'*')
1154        configDict = {'Clip_on':True}
1155
1156def MacStartGSASII(g2script,project=''):
1157    '''Start a new instance of GSAS-II by opening a new terminal window and starting
1158    a new GSAS-II process. Used on Mac OS X only.
1159
1160    :param str g2script: file name for the GSASII.py script
1161    :param str project: GSAS-II project (.gpx) file to be opened, default is blank
1162      which opens a new project
1163    '''
1164    if project and os.path.splitext(project)[1] != '.gpx':
1165        print('file {} cannot be used. Not GSAS-II project (.gpx) file'.format(project))
1166        return
1167    if project and not os.path.exists(project):
1168        print('file {} cannot be found.'.format(project))
1169        return 
1170    elif project:
1171        project = os.path.abspath(project)
1172    g2script = os.path.abspath(g2script)
1173    pythonapp = sys.executable
1174    if os.path.exists(pythonapp+'w'): pythonapp += 'w'
1175    script = '''
1176set python to "{}"
1177set appwithpath to "{}"
1178set filename to "{}"
1179
1180tell application "Terminal"
1181     activate
1182     do script python & " " & appwithpath & " " & filename & "; exit"
1183end tell
1184'''.format(pythonapp,g2script,project)
1185    subprocess.Popen(["osascript","-e",script])
1186
1187def MacRunScript(script):
1188    '''Start a bash script in a new terminal window.
1189    Used on Mac OS X only.
1190
1191    :param str script: file name for a bash script
1192    '''
1193    script = os.path.abspath(script)
1194    osascript = '''
1195set bash to "/bin/bash"
1196set filename to "{}"
1197
1198tell application "Terminal"
1199     activate
1200     do script bash & " " & filename & "; exit"
1201end tell
1202'''.format(script)
1203    subprocess.Popen(["osascript","-e",osascript])
1204   
1205def findConda():
1206    '''Determines if GSAS-II has been installed as g2conda or gsas2full
1207    with conda located relative to this file.
1208    We could also look for conda relative to the python (sys.executable)
1209    image, but I don't want to muck around with python that someone else
1210    installed.
1211    '''
1212    parent = os.path.split(path2GSAS2)[0]
1213    if sys.platform != "win32":
1214        activate = os.path.join(parent,'bin','activate')
1215        conda = os.path.join(parent,'bin','conda')
1216    else:
1217        activate = os.path.join(parent,'Scripts','activate.bat')
1218        conda = os.path.join(parent,'condabin','conda.bat')
1219    if os.path.exists(activate) and os.path.exists(conda):
1220        return conda,activate
1221    else:
1222        return None
1223
1224def runScript(cmds=[], wait=False, G2frame=None):
1225    '''run a shell script of commands in an external process
1226   
1227    :param list cmds: a list of str's, each ietm containing a shell (cmd.exe
1228      or bash) command
1229    :param bool wait: if True indicates the commands should be run and then
1230      the script should return. If False, then the currently running Python
1231      will exit. Default is False
1232    :param wx.Frame G2frame: provides the location of the current .gpx file
1233      to be used to restart GSAS-II after running the commands, if wait
1234      is False. Default is None which prevents restarting GSAS-II regardless of
1235      the value of wait.
1236    '''
1237    import tempfile
1238    if not cmds:  #debug
1239        print('nothing to do in runScript')
1240        return
1241    if sys.platform != "win32":
1242        suffix = '.sh'
1243    else:
1244        suffix = '.bat'
1245       
1246    fp = tempfile.NamedTemporaryFile(mode='w', suffix=suffix, delete=False)
1247    shellname = fp.name
1248    for line in cmds:
1249        fp.write(line)
1250        fp.write('\n')
1251
1252    if not wait:
1253        if G2frame:
1254            projectfile = ''
1255            if G2frame.GSASprojectfile:
1256                projectfile = os.path.realpath(G2frame.GSASprojectfile)
1257            main = os.path.join(path2GSAS2,'GSASII.py')
1258            ex = sys.executable
1259            if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1260                if os.path.exists(ex+'w'): ex += 'w'
1261            print ('restart using ',' '.join([ex,main,projectfile]))
1262            fp.write(' '.join([ex,main,projectfile]))
1263            fp.write('\n')
1264    fp.close()
1265
1266    # start the upgrade in a separate interpreter (avoids loading .pyd files)
1267    if sys.platform != "win32":
1268        proc = subprocess.Popen(['bash',shellname])
1269    else:
1270        proc = subprocess.Popen([shellname],shell=True)
1271    if wait:
1272        proc.wait()
1273    else:
1274        if sys.platform != "win32": proc.wait()
1275        sys.exit()
1276   
1277if __name__ == '__main__':
1278    '''What follows is called to update (or downdate) GSAS-II in a separate process.
1279    '''
1280    import time
1281    time.sleep(1) # delay to give the main process a chance to exit
1282    # perform an update and restart GSAS-II
1283    try:
1284        project,version = sys.argv[1:3]
1285    except ValueError:
1286        project = None
1287        version = 'trunk'
1288    loc = os.path.dirname(__file__)
1289    if version == 'trunk':
1290        svnSwitch2branch('')
1291    elif '/' in version:
1292        svnSwitch2branch(version)
1293    elif version:
1294        print("Regress to version "+str(version))
1295        svnUpdateDir(loc,version=version)
1296    else:
1297        print("Update to current version")
1298        svnUpdateDir(loc)
1299    ex = sys.executable
1300    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1301        if os.path.exists(ex+'w'): ex += 'w'
1302    if project:
1303        print("Restart GSAS-II with project file "+str(project))
1304        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
1305    else:
1306        print("Restart GSAS-II without a project file ")
1307        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
1308    print ('exiting update process')
1309    sys.exit()
Note: See TracBrowser for help on using the repository browser.