source: trunk/GSASIIpath.py @ 4085

Last change on this file since 4085 was 4085, checked in by toby, 3 years ago

Add menu accelerator keys & recently used menu

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 36.5 KB
Line 
1# -*- coding: utf-8 -*-
2'''
3*GSASIIpath: locations & updates*
4---------------------------------
5
6Routines for dealing with file locations, etc.
7
8Determines the location of the compiled (.pyd or .so) libraries.
9
10Interfaces with subversion (svn):
11Determine the subversion release number by determining the highest version number
12where :func:`SetVersionNumber` is called (best done in every GSASII file).
13Other routines will update GSASII from the subversion server if svn can be
14found.
15
16Accesses configuration options, as defined in config.py
17'''
18
19from __future__ import division, print_function
20import os
21import sys
22import platform
23import glob
24import subprocess
25import numpy as np
26g2home = 'https://subversion.xray.aps.anl.gov/pyGSAS'
27'Define the location of the GSAS-II subversion repository'
28   
29path2GSAS2 = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) # location of this file; save before any changes in pwd
30
31# convert version numbers as '1.2.3' to integers (1002) and back (to 1.2)
32fmtver = lambda v: str(v//1000)+'.'+str(v%1000)
33intver = lambda vs: sum([int(i) for i in vs.split('.')[0:2]]*np.array((1000,1)))
34
35def GetConfigValue(key,default=None):
36    '''Return the configuration file value for key or a default value if not present
37   
38    :param str key: a value to be found in the configuration (config.py) file
39    :param default: a value to be supplied is none is in the config file or
40      the config file is not found. Defaults to None
41    :returns: the value found or the default.
42    '''
43    try:
44        return configDict.get(key,default)
45    except NameError: # this happens when building docs
46        return None
47
48def SetConfigValue(parmdict):
49    '''Set configuration variables from a dictionary where elements are lists
50    First item in list is the default value and second is the value to use.
51    '''
52    global configDict
53    for var in parmdict:
54        if var in configDict:
55            del configDict[var]
56        if isinstance(parmdict[var],tuple):
57            configDict[var] = parmdict[var]
58        else:
59            if parmdict[var][1] is None: continue
60            if parmdict[var][1] == '': continue
61            if parmdict[var][0] == parmdict[var][1]: continue
62            configDict[var] = parmdict[var][1]
63
64def addPrevGPX(fil,configDict):
65    '''Add a GPX file to the list of previous files.
66    Move previous names to start of list. Keep most recent five files
67    '''
68    fil = os.path.abspath(os.path.expanduser(fil))
69    if 'previous_GPX_files' not in configDict: return
70    try:
71        pos = configDict['previous_GPX_files'][1].index(fil) 
72        if pos == 0: return
73        configDict['previous_GPX_files'][1].pop(pos) # if present, remove if not 1st
74    except ValueError:
75        pass
76    except AttributeError:
77        configDict['previous_GPX_files'][1] = []
78    configDict['previous_GPX_files'][1].insert(0,fil)
79    configDict['previous_GPX_files'][1] = configDict['previous_GPX_files'][1][:5]
80
81# routines for looking a version numbers in files
82version = -1
83def SetVersionNumber(RevString):
84    '''Set the subversion version number
85
86    :param str RevString: something like "$Revision: 4085 $"
87      that is set by subversion when the file is retrieved from subversion.
88
89    Place ``GSASIIpath.SetVersionNumber("$Revision: 4085 $")`` in every python
90    file.
91    '''
92    try:
93        RevVersion = int(RevString.split(':')[1].split()[0])
94        global version
95        version = max(version,RevVersion)
96    except:
97        pass
98       
99def GetVersionNumber():
100    '''Return the maximum version number seen in :func:`SetVersionNumber`
101    '''
102    if version > 1000:
103        return version
104    else:
105        return "unknown"
106
107def LoadConfigFile(filename):
108    '''Read a GSAS-II configuration file.
109    Comments (starting with "%") are removed, as are empty lines
110   
111    :param str filename: base file name (such as 'file.dat'). Files with this name
112      are located from the path and the contents of each are concatenated.
113    :returns: a list containing each non-empty (after removal of comments) line
114      found in every matching config file.
115    '''
116    info = []
117    for path in sys.path:
118        fil = os.path.join(path,filename)
119        if not os.path.exists(fil): continue
120        try:
121            i = 0
122            fp = open(fil,'r')
123            for line in fp:
124                expr = line.split('#')[0].strip()
125                if expr:
126                    info.append(expr)
127                    i += 1
128            print(str(i)+' lines read from config file '+fil)
129        finally:
130            fp.close()
131    return info
132
133
134# routines to interface with subversion
135proxycmds = []
136'Used to hold proxy information for subversion, set if needed in whichsvn'
137svnLocCache = None
138'Cached location of svn to avoid multiple searches for it'
139
140def MakeByte2str(arg):
141    '''Convert output from subprocess pipes (bytes) to str (unicode) in Python 3.
142    In Python 2: Leaves output alone (already str).
143    Leaves stuff of other types alone (including unicode in Py2)
144    Works recursively for string-like stuff in nested loops and tuples.
145
146    typical use::
147
148        out = MakeByte2str(out)
149
150    or::
151
152        out,err = MakeByte2str(s.communicate())
153   
154    '''
155    if isinstance(arg,str): return arg
156    if isinstance(arg,bytes): return arg.decode()
157    if isinstance(arg,list):
158        return [MakeByte2str(i) for i in arg]
159    if isinstance(arg,tuple):
160        return tuple([MakeByte2str(i) for i in arg])
161    return arg
162               
163def getsvnProxy():
164    '''Loads a proxy for subversion from the file created by bootstrap.py
165    '''
166    proxyinfo = os.path.join(path2GSAS2,"proxyinfo.txt")
167    if os.path.exists(proxyinfo):
168        global proxycmds
169        proxycmds = []
170        fp = open(proxyinfo,'r')
171        host = fp.readline().strip()
172        port = fp.readline().strip()
173        fp.close()
174        setsvnProxy(host,port)
175        if not host.strip(): return '',''
176        return host,port
177    return '',''
178
179def setsvnProxy(host,port):
180    '''Sets the svn commands needed to use a proxy
181    '''
182    global proxycmds
183    proxycmds = []
184    host = host.strip()
185    port = port.strip()
186    if not host: return
187    proxycmds.append('--config-option')
188    proxycmds.append('servers:global:http-proxy-host='+host)
189    if port:
190        proxycmds.append('--config-option')
191        proxycmds.append('servers:global:http-proxy-port='+port)
192       
193def whichsvn():
194    '''Returns a path to the subversion exe file, if any is found.
195    Searches the current path after adding likely places where GSAS-II
196    might install svn.
197
198    :returns: None if svn is not found or an absolute path to the subversion
199      executable file.
200    '''
201    # use a previosuly cached svn location
202    global svnLocCache
203    if svnLocCache: return svnLocCache
204    # prepare to find svn
205    is_exe = lambda fpath: os.path.isfile(fpath) and os.access(fpath, os.X_OK)
206    svnprog = 'svn'
207    if sys.platform.startswith('win'): svnprog += '.exe'
208    host,port = getsvnProxy()
209    if GetConfigValue('debug') and host:
210        print('DBG_Using proxy host {} port {}'.format(host,port))
211    # add likely places to find subversion when installed with GSAS-II
212    pathlist = os.environ["PATH"].split(os.pathsep)
213    pathlist.insert(0,os.path.split(sys.executable)[0])
214    pathlist.insert(1,path2GSAS2)
215    for rpt in ('..','bin'),('..','Library','bin'),('svn','bin'),('svn',),('.'):
216        pt = os.path.normpath(os.path.join(path2GSAS2,*rpt))
217        if os.path.exists(pt):
218            pathlist.insert(0,pt)   
219    # search path for svn or svn.exe
220    for path in pathlist:
221        exe_file = os.path.join(path, svnprog)
222        if is_exe(exe_file):
223            try:
224                p = subprocess.Popen([exe_file,'help'],stdout=subprocess.PIPE)
225                res = p.stdout.read()
226                p.communicate()
227                svnLocCache = os.path.abspath(exe_file)
228                return svnLocCache
229            except:
230                pass       
231    svnLocCache = None
232
233def svnVersion(svn=None):
234    '''Get the version number of the current subversion executable
235
236    :returns: a string with a version number such as "1.6.6" or None if
237      subversion is not found.
238
239    '''
240    if not svn: svn = whichsvn()
241    if not svn: return
242
243    cmd = [svn,'--version','--quiet']
244    s = subprocess.Popen(cmd,
245                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
246    out,err = MakeByte2str(s.communicate())
247    if err:
248        print ('subversion error!\nout=%s'%out)
249        print ('err=%s'%err)
250        s = '\nsvn command:  '
251        for i in cmd: s += i + ' '
252        print(s)
253        return None
254    return out.strip()
255
256def svnVersionNumber(svn=None):
257    '''Get the version number of the current subversion executable
258
259    :returns: a fractional version number such as 1.6 or None if
260      subversion is not found.
261
262    '''
263    ver = svnVersion(svn)
264    if not ver: return 
265    M,m = ver.split('.')[:2]
266    return int(M)+int(m)/10.
267
268def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
269    '''Get the revision log information for a specific version of the specified package
270
271    :param str fpath: path to repository dictionary, defaults to directory where
272       the current file is located.
273    :param int version: the version number to be looked up or None (default)
274       for the latest version.
275
276    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
277
278    '''
279    import xml.etree.ElementTree as ET
280    svn = whichsvn()
281    if not svn: return
282    if version is not None:
283        vstr = '-r'+str(version)
284    else:
285        vstr = '-rHEAD'
286
287    cmd = [svn,'log',fpath,'--xml',vstr]
288    if proxycmds: cmd += proxycmds
289    s = subprocess.Popen(cmd,
290                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
291    out,err = MakeByte2str(s.communicate())
292    if err:
293        print ('out=%s'%out)
294        print ('err=%s'%err)
295        s = '\nsvn command:  '
296        for i in cmd: s += i + ' '
297        print(s)
298        return None
299    x = ET.fromstring(out)
300    d = {}
301    for i in x.iter('logentry'):
302        d = {'revision':i.attrib.get('revision','?')}
303        for j in i:
304            d[j.tag] = j.text
305        break # only need the first
306    return d
307
308svnLastError = ''
309def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
310    '''Obtain the version number for the either the last update of the local version
311    or contacts the subversion server to get the latest update version (# of Head).
312
313    :param str fpath: path to repository dictionary, defaults to directory where
314       the current file is located
315    :param bool local: determines the type of version number, where
316       True (default): returns the latest installed update
317       False: returns the version number of Head on the server
318
319    :Returns: the version number as an str or
320       None if there is a subversion error (likely because the path is
321       not a repository or svn is not found). The error message is placed in
322       global variable svnLastError
323    '''
324
325    import xml.etree.ElementTree as ET
326    svn = whichsvn()
327    if not svn: return
328    if local:
329        cmd = [svn,'info',fpath,'--xml']
330    else:
331        cmd = [svn,'info',fpath,'--xml','-rHEAD']
332    if svnVersionNumber() >= 1.6:
333        cmd += ['--non-interactive', '--trust-server-cert']
334    if proxycmds: cmd += proxycmds
335    s = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
336    out,err = MakeByte2str(s.communicate())
337    if err:
338        print ('svn failed\n%s'%out)
339        print ('err=%s'%err)
340        s = '\nsvn command:  '
341        for i in cmd: s += i + ' '
342        print(s)
343        global svnLastError
344        svnLastError = err
345        return None
346    x = ET.fromstring(out)
347    for i in x.iter('entry'):
348        rev = i.attrib.get('revision')
349        if rev: return rev
350
351def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
352    '''Returns a list of files that were changed locally. If no files are changed,
353       the list has length 0
354
355    :param fpath: path to repository dictionary, defaults to directory where
356       the current file is located
357
358    :returns: None if there is a subversion error (likely because the path is
359       not a repository or svn is not found)
360
361    '''
362    import xml.etree.ElementTree as ET
363    svn = whichsvn()
364    if not svn: return
365    cmd = [svn,'status',fpath,'--xml']
366    if proxycmds: cmd += proxycmds
367    s = subprocess.Popen(cmd,
368                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
369    out,err = MakeByte2str(s.communicate())
370    if err: return None
371    x = ET.fromstring(out)
372    changed = []
373    for i in x.iter('entry'):
374        if i.find('wc-status').attrib.get('item') == 'modified': 
375            changed.append(i.attrib.get('path'))
376    return changed
377
378def svnCleanup(fpath=os.path.split(__file__)[0],verbose=True):
379    '''This runs svn cleanup on a selected local directory.
380
381    :param str fpath: path to repository dictionary, defaults to directory where
382       the current file is located
383    '''
384    svn = whichsvn()
385    if not svn: return
386    if verbose: print(u"Performing svn cleanup at "+fpath)
387    cmd = [svn,'cleanup',fpath]
388    if verbose:
389        s = 'subversion command:\n  '
390        for i in cmd: s += i + ' '
391        print(s)
392    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
393    out,err = MakeByte2str(s.communicate())
394    if err:
395        print(60*"=")
396        print("****** An error was noted, see below *********")
397        print(60*"=")
398        print(err)
399        s = '\nsvn command:  '
400        for i in cmd: s += i + ' '
401        print(s)
402        #raise Exception('svn cleanup failed')
403        return False
404    elif verbose:
405        print(out)
406    return True
407       
408def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None,verbose=True):
409    '''This performs an update of the files in a local directory from a server.
410
411    :param str fpath: path to repository dictionary, defaults to directory where
412       the current file is located
413    :param version: the number of the version to be loaded. Used only
414       cast as a string, but should be an integer or something that corresponds to a
415       string representation of an integer value when cast. A value of None (default)
416       causes the latest version on the server to be used.
417    '''
418    svn = whichsvn()
419    if not svn: return
420    if version:
421        verstr = '-r' + str(version)
422    else:
423        verstr = '-rHEAD'
424    if verbose: print(u"Updating files at "+fpath)
425    cmd = [svn,'update',fpath,verstr,
426           '--non-interactive',
427           '--accept','theirs-conflict','--force']
428    if svnVersionNumber() >= 1.6:
429        cmd += ['--trust-server-cert']
430    if proxycmds: cmd += proxycmds
431    if verbose:
432        s = 'subversion command:\n  '
433        for i in cmd: s += i + ' '
434        print(s)
435    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
436    out,err = MakeByte2str(s.communicate())
437    if err:
438        print(60*"=")
439        print("****** An error was noted, see below *********")
440        print(60*"=")
441        print(err)
442        s = '\nsvn command:  '
443        for i in cmd: s += i + ' '
444        print(s)
445        if svnCleanup(fpath):
446            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
447            out,err = MakeByte2str(s.communicate())
448            if err:
449                print(60*"=")
450                print("****** Drat, failed again: *********")
451                print(60*"=")
452                print(err)
453            else:
454                return
455        raise Exception('svn update failed')
456    elif verbose:
457        print(out)
458
459def svnUpgrade(fpath=os.path.split(__file__)[0]):
460    '''This reformats subversion files, which may be needed if an upgrade of subversion is
461    done.
462
463    :param str fpath: path to repository dictionary, defaults to directory where
464       the current file is located
465    '''
466    svn = whichsvn()
467    if not svn: return
468    cmd = [svn,'upgrade',fpath,'--non-interactive']
469    if proxycmds: cmd += proxycmds
470    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
471    out,err = MakeByte2str(s.communicate())
472    if err:
473        print("svn upgrade did not happen (this is probably OK). Messages:")
474        print (err)
475        s = '\nsvn command:  '
476        for i in cmd: s += i + ' '
477        print(s)
478
479def svnUpdateProcess(version=None,projectfile=None,branch=None):
480    '''perform an update of GSAS-II in a separate python process'''
481    if not projectfile:
482        projectfile = ''
483    else:
484        projectfile = os.path.realpath(projectfile)
485        print ('restart using %s'%projectfile)
486    if branch:
487        version = branch
488    elif not version:
489        version = ''
490    else:
491        version = str(version)
492    # start the upgrade in a separate interpreter (avoids loading .pyd files)
493    subprocess.Popen([sys.executable,__file__,projectfile,version])
494    sys.exit()
495
496def svnSwitchDir(rpath,filename,baseURL,loadpath=None,verbose=True):
497    '''This performs a switch command to move files between subversion trees.
498    Note that if the files were previously downloaded,
499    the switch command will update the files to the newest version.
500   
501    :param str rpath: path to locate files, relative to the GSAS-II
502      installation path (defaults to path2GSAS2)
503    :param str URL: the repository URL
504    :param str loadpath: the prefix for the path, if specified. Defaults to path2GSAS2
505    :param bool verbose: if True (default) diagnostics are printed
506    '''
507    svn = whichsvn()
508    if not svn: return
509    URL = baseURL[:]
510    if baseURL[-1] != '/':
511        URL = baseURL + '/' + filename
512    else:
513        URL = baseURL + filename
514    if loadpath:
515        fpath = os.path.join(loadpath,rpath,filename)
516    else:
517        fpath = os.path.join(path2GSAS2,rpath,filename)
518    cmd = [svn,'switch',URL,fpath,
519           '--non-interactive','--trust-server-cert',
520           '--accept','theirs-conflict','--force']
521    if svnVersionNumber(svn) > 1.6: cmd += ['--ignore-ancestry']
522    if proxycmds: cmd += proxycmds
523    if verbose: print(u"Loading files to "+fpath+u"\n  from "+URL)
524    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
525    out,err = MakeByte2str(s.communicate())
526    if err:
527        print(60*"=")
528        print ("****** An error was noted, see below *********")
529        print(60*"=")
530        print ('out=%s'%out)
531        print ('err=%s'%err)
532        s = '\nsvn command:  '
533        for i in cmd: s += i + ' '
534        print(s)
535        if svnCleanup(fpath):
536            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
537            out,err = MakeByte2str(s.communicate())
538            if err:
539                print(60*"=")
540                print("****** Drat, failed again: *********")
541                print(60*"=")
542                print(err)
543            else:
544                return True
545        return False
546    if verbose:
547        print('=== Output from svn switch'+(43*'='))
548        print(out.strip())
549        print((70*'=')+'\n')
550    return True
551
552def svnInstallDir(URL,loadpath):
553    '''Load a subversion tree into a specified directory
554
555    :param str URL: the repository URL
556    :param str loadpath: path to locate files
557
558    '''
559    svn = whichsvn()
560    if not svn: return
561    cmd = [svn,'co',URL,loadpath,'--non-interactive']
562    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
563    print("Loading files from "+URL)
564    if proxycmds: cmd += proxycmds
565    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
566    out,err = MakeByte2str(s.communicate())   #this fails too easily
567    if err:
568        print(60*"=")
569        print ("****** An error was noted, see below *********")
570        print(60*"=")
571        print (err)
572        s = '\nsvn command:  '
573        for i in cmd: s += i + ' '
574        print(s)
575        if svnCleanup(fpath):
576            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
577            out,err = MakeByte2str(s.communicate())
578            if err:
579                print(60*"=")
580                print("****** Drat, failed again: *********")
581                print(60*"=")
582                print(err)
583                return False
584        else:
585            return False
586    print ("Files installed at: "+loadpath)
587    return True
588
589def GetBinaryPrefix():
590    if sys.platform == "win32":
591        prefix = 'win'
592    elif sys.platform == "darwin":
593        prefix = 'mac'
594    elif sys.platform.startswith("linux"):
595        prefix = 'linux'
596    else:
597        print(u'Unknown platform: '+sys.platform)
598        raise Exception('Unknown platform')
599    if platform.architecture()[0] == '64bit':
600        bits = '64'
601    else:
602        bits = '32'
603
604    # format current python version
605    pyver = 'p{}.{}'.format(*sys.version_info[0:2])
606
607    items = [prefix,bits,pyver]
608    return '_'.join(items)
609
610def svnList(URL,verbose=True):
611    '''Get a list of subdirectories from and svn repository
612    '''   
613    svn = whichsvn()
614    if not svn:
615        print('**** unable to load files: svn not found ****')
616        return ''
617    # get binaries matching the required type -- other than for the numpy version
618    cmd = [svn, 'list', URL,'--non-interactive', '--trust-server-cert']
619    if proxycmds: cmd += proxycmds
620    if verbose:
621        s = 'Running svn command:\n  '
622        for i in cmd: s += i + ' '
623        print(s)
624    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
625    res,err = MakeByte2str(p.communicate())
626    return res
627
628def DownloadG2Binaries(g2home,verbose=True):
629    '''Download GSAS-II binaries from appropriate section of the
630    GSAS-II svn repository based on the platform, numpy and Python
631    version
632    '''   
633    bindir = GetBinaryPrefix()
634    #npver = 'n{}.{}'.format(*np.__version__.split('.')[0:2])
635    inpver = intver(np.__version__)
636    svn = whichsvn()
637    if not svn:
638        print('**** unable to load files: svn not found ****')
639        return ''
640    # get binaries matching the required type -- other than for the numpy version
641    cmd = [svn, 'list', g2home + '/Binaries/','--non-interactive', '--trust-server-cert']
642    if proxycmds: cmd += proxycmds
643    if verbose:
644        s = 'Running svn command:\n  '
645        for i in cmd: s += i + ' '
646        print(s)
647    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
648    res,err = MakeByte2str(p.communicate())
649    versions = {}
650    for d in res.split():
651        if d.startswith(bindir):
652            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
653            versions[v] = d
654    intVersionsList = sorted(versions.keys())
655    if not intVersionsList:
656        print('No binaries located matching',bindir)
657        return
658    elif inpver < min(intVersionsList):
659        vsel = min(intVersionsList)
660        print('Warning: The current numpy version, {}, is older than\n\tthe oldest dist version, {}'
661              .format(np.__version__,fmtver(vsel)))
662    elif inpver >= max(intVersionsList):
663        vsel = max(intVersionsList)
664        if verbose: print(
665                'FYI: The current numpy version, {}, is newer than the newest dist version {}'
666                .format(np.__version__,fmtver(vsel)))
667    else:
668        vsel = min(intVersionsList)
669        for v in intVersionsList:
670            if v <= inpver:
671                vsel = v
672            else:
673                if verbose: print(
674                        'FYI: Selecting dist version {} as the current numpy version, {},\n\tis older than the next dist version {}'
675                        .format(fmtver(vsel),np.__version__,fmtver(v)))
676                break
677    distdir = g2home + '/Binaries/' + versions[vsel]
678    # switch reset command: distdir = g2home + '/trunk/bindist'
679    svnSwitchDir('bindist','',distdir,verbose=verbose)
680    return os.path.join(path2GSAS2,'bindist')
681
682# def svnTestBranch(loc=None):
683#     '''Returns the name of the branch directory if the installation has been switched.
684#     Returns none, if not a branch
685#     the test 2frame branch. False otherwise
686#     '''
687#     if loc is None: loc = path2GSAS2
688#     svn = whichsvn()
689#     if not svn:
690#         print('**** unable to load files: svn not found ****')
691#         return ''
692#     cmd = [svn, 'info', loc]
693#     if proxycmds: cmd += proxycmds
694#     p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
695#     res,err = MakeByte2str(p.communicate())
696#     for l in res.split('\n'):
697#         if "Relative URL:" in l: break
698#     if "/branch/" in l:
699#         return l[l.find("/branch/")+8:].strip()
700#     else:
701#         return None
702   
703def svnSwitch2branch(branch=None,loc=None,svnHome=None):
704    '''Switch to a subversion branch if specified. Switches to trunk otherwise.
705    '''
706    if svnHome is None: svnHome = g2home
707    svnURL = svnHome + '/trunk'
708    if branch:
709        if svnHome.endswith('/'):
710            svnURL = svnHome[:-1]
711        else:
712            svnURL = svnHome
713        if branch.startswith('/'):
714            svnURL += branch
715        else:
716            svnURL += '/' + branch
717    svnSwitchDir('','',svnURL,loadpath=loc)
718   
719
720def IPyBreak_base(userMsg=None):
721    '''A routine that invokes an IPython session at the calling location
722    This routine is only used when debug=True is set in config.py
723    '''
724    savehook = sys.excepthook # save the exception hook
725    try: 
726        from IPython.terminal.embed import InteractiveShellEmbed
727    except ImportError:
728        try:
729            # try the IPython 0.12 approach
730            from IPython.frontend.terminal.embed import InteractiveShellEmbed
731        except ImportError:
732            print ('IPython InteractiveShellEmbed not found')
733            return
734    import inspect
735    ipshell = InteractiveShellEmbed()
736
737    frame = inspect.currentframe().f_back
738    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
739    if userMsg: msg += userMsg
740    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
741    sys.excepthook = savehook # reset IPython's change to the exception hook
742
743try:
744    from IPython.core import ultratb
745except:
746    pass
747def exceptHook(*args):
748    '''A routine to be called when an exception occurs. It prints the traceback
749    with fancy formatting and then calls an IPython shell with the environment
750    of the exception location.
751   
752    This routine is only used when debug=True is set in config.py   
753    '''
754    import IPython.core
755    if sys.platform.startswith('win'):
756        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
757    else:
758        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
759
760    try: 
761        from IPython.terminal.embed import InteractiveShellEmbed
762    except ImportError:
763        try:
764            # try the IPython 0.12 approach
765            from IPython.frontend.terminal.embed import InteractiveShellEmbed
766        except ImportError:
767            print ('IPython InteractiveShellEmbed not found')
768            return
769    import inspect
770    frame = inspect.getinnerframes(args[2])[-1][0]
771    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
772    savehook = sys.excepthook # save the exception hook
773    try: # try IPython 5 call 1st
774        class c(object): pass
775        pseudomod = c() # create something that acts like a module
776        pseudomod.__dict__ = frame.f_locals
777        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
778    except:
779        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
780    sys.excepthook = savehook # reset IPython's change to the exception hook
781
782def DoNothing():
783    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
784    except when the debug option is set True in config.py
785    '''
786    pass 
787
788IPyBreak = DoNothing
789pdbBreak = DoNothing
790def InvokeDebugOpts():
791    'Called in GSASII.py to set up debug options'
792    if GetConfigValue('debug'):
793        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
794        import pdb
795        global pdbBreak
796        pdbBreak = pdb.set_trace
797        try:
798            import IPython
799            global IPyBreak
800            IPyBreak = IPyBreak_base
801            if any('SPYDER' in name for name in os.environ):
802                print('Running from Spyder, skipping exception trapping')
803            else:
804                sys.excepthook = exceptHook
805        except:
806            pass
807
808def TestSPG(fpth):
809    '''Test if pyspg.[so,.pyd] can be run from a location in the path
810    '''
811    if not os.path.exists(fpth): return False
812    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
813    savpath = sys.path[:]
814    sys.path = [fpth]
815    # test to see if a shared library can be used
816    try:
817        import pyspg
818        pyspg.sgforpy('P -1')
819    except Exception as err:
820        print(70*'=')
821        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
822        print(70*'=')
823        sys.path = savpath
824        return False
825    sys.path = savpath
826    return True
827   
828# see if a directory for local modifications is defined. If so, stick that in the path
829if os.path.exists(os.path.expanduser('~/.G2local/')):
830    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
831    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
832    files = ""
833    prev = None
834    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
835        f = os.path.split(f)[1]
836        if os.path.splitext(f)[0] == prev: continue
837        prev = os.path.splitext(f)[0]
838        if files: files += ", "
839        files += f
840    if files:
841        print("*"*75)
842        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
843        print("  "+files)
844        print("*"*75)
845
846BinaryPathLoaded = False
847def SetBinaryPath(printInfo=False, loadBinary=True):
848    '''
849    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
850   
851    This routine must be executed after GSASIIpath is imported and before any other
852    GSAS-II imports are done.
853    '''
854    # do this only once no matter how many times it is called
855    global BinaryPathLoaded
856    if BinaryPathLoaded: return
857    try:
858        inpver = intver(np.__version__)
859    except AttributeError: # happens on building docs
860        return
861    binpath = None
862    binprfx = GetBinaryPrefix()
863    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
864        # Look at bin directory (created by a local compile) before looking for standard dist files
865        searchpathlist = [os.path.join(loc,'bin')]
866        # also look for matching binary dist in loc/AllBinaries
867        versions = {}
868        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
869            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
870            versions[v] = d
871        searchpathlist = [os.path.join(loc,'bin')]
872        vmin = None
873        vmax = None
874        for v in sorted(versions.keys()):
875            if v <= inpver:
876                vmin = v
877            elif v > inpver:
878                vmax = v
879                break
880        if vmin in versions:
881            searchpathlist.append(versions[vmin])
882        if vmax in versions:
883            searchpathlist.append(versions[vmax])
884        searchpathlist.append(os.path.join(loc,'bindist'))
885        for fpth in searchpathlist:
886            if TestSPG(fpth):
887                binpath = fpth
888                break       
889        if binpath: break
890    if binpath:                                            # were GSAS-II binaries found
891        sys.path.insert(0,binpath)
892        if printInfo:
893            print('GSAS-II binary directory: {}'.format(binpath))
894        BinaryPathLoaded = True
895    elif not loadBinary:
896        raise Exception
897    else:                                                  # try loading them
898        if printInfo:
899            print('Attempting to download GSAS-II binary files...')
900        try:
901            binpath = DownloadG2Binaries(g2home)
902        except AttributeError:   # this happens when building in Read The Docs
903            if printInfo:
904                print('Problem with download')
905        if binpath and TestSPG(binpath):
906            if printInfo:
907                print('GSAS-II binary directory: {}'.format(binpath))
908            sys.path.insert(0,binpath)
909            BinaryPathLoaded = True
910        # this must be imported before anything that imports any .pyd/.so file for GSASII
911        else:
912            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
913            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
914            # patch: use old location based on the host OS and the python version, 
915            # path is relative to location of the script that is called as well as this file
916            BinaryPathLoaded = True
917            bindir = None
918            if sys.platform == "win32":
919                if platform.architecture()[0] == '64bit':
920                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
921                else:
922                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
923            elif sys.platform == "darwin":
924                if platform.architecture()[0] == '64bit':
925                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
926                else:
927                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
928                #if platform.mac_ver()[0].startswith('10.5.'):
929                #    bindir += '_10.5'
930            elif sys.platform.startswith("linux"):
931                if platform.architecture()[0] == '64bit':
932                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
933                else:
934                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
935            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
936            # Look at bin directory (created by a local compile) before standard dist
937            # that at the top of the path
938                fpth = os.path.join(loc,bindir)
939                binpath = fpth
940                if TestSPG(fpth):
941                    sys.path.insert(0,binpath)
942                    if printInfo:
943                        print('\n'+75*'*')
944                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
945                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
946                        print('  so we can learn what is not working on your installation.')
947                        print('GSAS-II binary directory: {}'.format(binpath))
948                        print(75*'*')
949                    break
950            else:
951            # end patch
952            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
953            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
954                if printInfo:
955                    print(75*'*')
956                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
957                    print(75*'*')
958                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
959
960    # add the data import and export directory to the search path
961    newpath = os.path.join(path2GSAS2,'imports')
962    if newpath not in sys.path: sys.path.append(newpath)
963    newpath = os.path.join(path2GSAS2,'exports')
964    if newpath not in sys.path: sys.path.append(newpath)
965
966    # setup read of config.py, if present
967    global configDict
968    try:
969        import config
970        configDict = config.__dict__
971        import inspect
972        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
973        if printInfo:
974            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
975    except ImportError:
976        configDict = {'Clip_on':True}
977    except Exception as err:
978        if printInfo:
979            print("Error importing config.py file: "+str(err))
980        configDict = {'Clip_on':True}
981
982if __name__ == '__main__':
983    '''What follows is called to update (or downdate) GSAS-II in a separate process.
984    '''
985    import time
986    time.sleep(1) # delay to give the main process a chance to exit
987    # perform an update and restart GSAS-II
988    project,version = sys.argv[1:3]
989    loc = os.path.dirname(__file__)
990    if version == 'trunk':
991        svnSwitch2branch('')
992    elif '/' in version:
993        svnSwitch2branch(version)
994    elif version:
995        print("Regress to version "+str(version))
996        svnUpdateDir(loc,version=version)
997    else:
998        print("Update to current version")
999        svnUpdateDir(loc)
1000    ex = sys.executable
1001    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
1002        if os.path.exists(ex+'w'): ex += 'w'
1003    if project:
1004        print("Restart GSAS-II with project file "+str(project))
1005        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
1006    else:
1007        print("Restart GSAS-II without a project file ")
1008        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
1009    print ('exiting update process')
1010    sys.exit()
Note: See TracBrowser for help on using the repository browser.