source: trunk/GSASIIpath.py @ 3372

Last change on this file since 3372 was 3372, checked in by vondreele, 4 years ago

disable printing of debug info for binary location & config controls
fix bug in image plotting wrt linescan
better (but not best) mag moment derivatives

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