source: trunk/GSASIIpath.py @ 4080

Last change on this file since 4080 was 3971, checked in by toby, 6 years ago

skip exception processing from inside Spyder & if IPython is not present

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