source: trunk/GSASIIpath.py @ 2347

Last change on this file since 2347 was 2347, checked in by vondreele, 7 years ago

implement Steven Weigand's fixes for Pilatus 'I' for 'L'
do a 'fix' for powderCif files with value(esd) for powder profile points
add a couple of comments for tutorial stuff

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 19.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
19import os
20import sys
21import platform
22# see if a directory for local modifications is defined. If so, stick that in the path
23if os.path.exists(os.path.expanduser('~/.G2local/')):
24    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
25    import glob
26    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
27    files = ""
28    prev = None
29    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
30        f = os.path.split(f)[1]
31        if os.path.splitext(f)[0] == prev: continue
32        prev = os.path.splitext(f)[0]
33        if files: files += ", "
34        files += f
35    if files:
36        print("*"*75)
37        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
38        print("  "+files)
39        print("*"*75)
40           
41
42# determine a binary path for the pyd files based on the host OS and the python version, 
43# path is relative to location of the script that is called as well as this file
44# this must be imported before anything that imports any .pyd/.so file for GSASII
45bindir = None
46if sys.platform == "win32":
47    if platform.architecture()[0] == '64bit':
48        bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
49    else:
50        bindir = 'binwin%d.%d' % sys.version_info[0:2]
51elif sys.platform == "darwin":
52    import platform
53    if platform.architecture()[0] == '64bit':
54        bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
55    else:
56        bindir = 'binmac%d.%d' % sys.version_info[0:2]
57    if platform.mac_ver()[0].startswith('10.5.'):
58        bindir += '_10.5'
59elif sys.platform == "linux2":
60    if platform.architecture()[0] == '64bit':
61        bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
62    else:
63        bindir = 'binlinux%d.%d' % sys.version_info[0:2]
64for loc in sys.path[0],os.path.abspath(os.path.split(__file__)[0]):
65    if bindir:
66        if os.path.exists(os.path.join(loc,bindir)) and os.path.join(loc,bindir) not in sys.path: 
67            sys.path.insert(0,os.path.join(loc,bindir))
68        # is there a bin directory? (created by a local compile), if so put
69        # that at the top of the path
70    if os.path.exists(os.path.join(loc,'bin')) and os.path.getsize(os.path.join(loc,'bin')):
71        bindir = 'bin'
72        if os.path.join(loc,'bin') not in sys.path: 
73            sys.path.insert(0,os.path.join(loc,bindir))
74print 'GSAS-II binary directory: ',os.path.join(loc,bindir)
75if bindir == None:
76    raise Exception,"**** ERROR GSAS-II binary libraries not found, GSAS-II fails ****"
77# add the data import and export directory to the search path
78path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file; save before any changes in pwd
79newpath = os.path.join(path2GSAS2,'imports')
80if newpath not in sys.path: sys.path.append(newpath)
81newpath = os.path.join(path2GSAS2,'exports')
82if newpath not in sys.path: sys.path.append(newpath)
83
84# setup read of config.py, if present
85try:
86    import config
87    configDict = config.__dict__
88    import inspect
89    vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
90    print str(len(vals))+' values read from config file '+os.path.abspath(config.__file__)
91except ImportError:
92    configDict = {}
93   
94def GetConfigValue(key,default=None):
95    '''Return the configuration file value for key or a default value if not present
96   
97    :param str key: a value to be found in the configuration (config.py) file
98    :param default: a value to be supplied is none is in the config file or
99      the config file is not found. Defaults to None
100    :returns: the value found or the default.
101    '''
102    return configDict.get(key,default)
103
104def SetConfigValue(parmdict):
105    '''Set configuration variables from a dictionary where elements are lists
106    First item in list is the default value and second is the value to use.
107    '''
108    global configDict
109    for var in parmdict:
110        if var in configDict:
111            del configDict[var]
112        if parmdict[var][1] is None: continue
113        if parmdict[var][1] == '': continue
114        if parmdict[var][0] == parmdict[var][1]: continue
115        configDict[var] = parmdict[var][1]
116
117# routines for looking a version numbers in files
118version = -1
119def SetVersionNumber(RevString):
120    '''Set the subversion version number
121
122    :param str RevString: something like "$Revision: 2347 $"
123      that is set by subversion when the file is retrieved from subversion.
124
125    Place ``GSASIIpath.SetVersionNumber("$Revision: 2347 $")`` in every python
126    file.
127    '''
128    try:
129        RevVersion = int(RevString.split(':')[1].split()[0])
130        global version
131        version = max(version,RevVersion)
132    except:
133        pass
134       
135def GetVersionNumber():
136    '''Return the maximum version number seen in :func:`SetVersionNumber`
137    '''
138    if version > 1000:
139        return version
140    else:
141        return "unknown"
142
143def LoadConfigFile(filename):
144    '''Read a GSAS-II configuration file.
145    Comments (starting with "%") are removed, as are empty lines
146   
147    :param str filename: base file name (such as 'file.dat'). Files with this name
148      are located from the path and the contents of each are concatenated.
149    :returns: a list containing each non-empty (after removal of comments) line
150      found in every matching config file.
151    '''
152    info = []
153    for path in sys.path:
154        fil = os.path.join(path,filename)
155        if not os.path.exists(fil): continue
156        try:
157            i = 0
158            fp = open(fil,'r')
159            for line in fp:
160                expr = line.split('#')[0].strip()
161                if expr:
162                    info.append(expr)
163                    i += 1
164            print(str(i)+' lines read from config file '+fil)
165        finally:
166            fp.close()
167    return info
168
169
170# routines to interface with subversion
171def whichsvn():
172    '''Returns a path to the subversion exe file, if any is found.
173    Searches the current path after adding likely places where GSAS-II
174    might install svn.
175
176    :returns: None if svn is not found or an absolute path to the subversion
177      executable file.
178    '''
179    def is_exe(fpath):
180        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
181    svnprog = 'svn'
182    if sys.platform == "win32": svnprog += '.exe'
183    pathlist = os.environ["PATH"].split(os.pathsep)
184    # add likely places to find subversion when installed with GSAS-II
185    for rpt in ('..','bin'),('..','Library','bin'),('svn','bin'),('svn',):
186        pt = os.path.normpath(os.path.join(os.path.split(__file__)[0],*rpt))
187        if os.path.exists(pt):
188            pathlist.insert(0,pt)
189    # search path for svn or svn.exe
190    for path in pathlist:
191        exe_file = os.path.join(path, svnprog)
192        if is_exe(exe_file):
193            return os.path.abspath(exe_file)
194
195def svnVersion():
196    '''Get the version number of the current subversion executable
197
198    :returns: a string with a version number such as "1.6.6" or None if
199      subversion is not found.
200
201    '''
202    import subprocess
203    svn = whichsvn()
204    if not svn: return
205
206    cmd = [svn,'--version','--quiet']
207    s = subprocess.Popen(cmd,
208                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
209    out,err = s.communicate()
210    if err:
211        print 'subversion error!\nout=',out
212        print 'err=',err
213        return None
214    return out.strip()
215
216def svnVersionNumber():
217    '''Get the version number of the current subversion executable
218
219    :returns: a fractional version number such as 1.6 or None if
220      subversion is not found.
221
222    '''
223    ver = svnVersion()
224    if not ver: return 
225    M,m = svnVersion().split('.')[:2]
226    return int(M)+int(m)/10.
227
228def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
229    '''Get the revision log information for a specific version of the specified package
230
231    :param str fpath: path to repository dictionary, defaults to directory where
232       the current file is located.
233    :param int version: the version number to be looked up or None (default)
234       for the latest version.
235
236    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
237
238    '''
239    import subprocess
240    import xml.etree.ElementTree as ET
241    svn = whichsvn()
242    if not svn: return
243    if version is not None:
244        vstr = '-r'+str(version)
245    else:
246        vstr = '-rHEAD'
247
248    cmd = [svn,'log',fpath,'--xml',vstr]
249    s = subprocess.Popen(cmd,
250                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
251    out,err = s.communicate()
252    if err:
253        print 'out=',out
254        print 'err=',err
255        return None
256    x = ET.fromstring(out)
257    d = {}
258    for i in x.iter('logentry'):
259        d = {'revision':i.attrib.get('revision','?')}
260        for j in i:
261            d[j.tag] = j.text
262        break # only need the first
263    return d
264
265def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
266    '''Obtain the version number for the either the last update of the local version
267    or contacts the subversion server to get the latest update version (# of Head).
268
269    :param str fpath: path to repository dictionary, defaults to directory where
270       the current file is located
271    :param bool local: determines the type of version number, where
272       True (default): returns the latest installed update
273       False: returns the version number of Head on the server
274
275    :Returns: the version number as an str or
276       None if there is a subversion error (likely because the path is
277       not a repository or svn is not found)
278    '''
279
280    import subprocess
281    import xml.etree.ElementTree as ET
282    svn = whichsvn()
283    if not svn: return
284    if local:
285        cmd = [svn,'info',fpath,'--xml']
286    else:
287        cmd = [svn,'info',fpath,'--xml','-rHEAD']
288    if svnVersionNumber() >= 1.6:
289        cmd += ['--non-interactive', '--trust-server-cert']
290    s = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
291    out,err = s.communicate()
292    if err:
293        print 'svn failed\n',out
294        print 'err=',err
295        return None
296    x = ET.fromstring(out)
297    for i in x.iter('entry'):
298        rev = i.attrib.get('revision')
299        if rev: return rev
300
301def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
302    '''Returns a list of files that were changed locally. If no files are changed,
303       the list has length 0
304
305    :param fpath: path to repository dictionary, defaults to directory where
306       the current file is located
307
308    :returns: None if there is a subversion error (likely because the path is
309       not a repository or svn is not found)
310
311    '''
312    import subprocess
313    import xml.etree.ElementTree as ET
314    svn = whichsvn()
315    if not svn: return
316    cmd = [svn,'status',fpath,'--xml']
317    s = subprocess.Popen(cmd,
318                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
319    out,err = s.communicate()
320    if err: return None
321    x = ET.fromstring(out)
322    changed = []
323    for i in x.iter('entry'):
324        if i.find('wc-status').attrib.get('item') == 'modified': 
325            changed.append(i.attrib.get('path'))
326    return changed
327
328def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None):
329    '''This performs an update of the files in a local directory from a server.
330
331    :param str fpath: path to repository dictionary, defaults to directory where
332       the current file is located
333    :param version: the number of the version to be loaded. Used only
334       cast as a string, but should be an integer or something that corresponds to a
335       string representation of an integer value when cast. A value of None (default)
336       causes the latest version on the server to be used.
337    '''
338    import subprocess
339    svn = whichsvn()
340    if not svn: return
341    if version:
342        verstr = '-r' + str(version)
343    else:
344        verstr = '-rHEAD'
345    cmd = [svn,'update',fpath,verstr,
346           '--non-interactive',
347           '--accept','theirs-conflict','--force']
348    if svnVersionNumber() >= 1.6:
349        cmd += ['--trust-server-cert']
350    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
351    out,err = s.communicate()
352    if err:
353        print(60*"=")
354        print ("****** An error was noted, see below *********")
355        print(60*"=")
356        print err
357        sys.exit()
358
359def svnUpgrade(fpath=os.path.split(__file__)[0]):
360    '''This reformats subversion files, which may be needed if an upgrade of subversion is
361    done.
362
363    :param str fpath: path to repository dictionary, defaults to directory where
364       the current file is located
365    '''
366    import subprocess
367    svn = whichsvn()
368    if not svn: return
369    cmd = [svn,'upgrade',fpath,'--non-interactive']
370    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
371    out,err = s.communicate()
372    if err:
373        print("svn upgrade did not happen (this is probably OK). Messages:")
374        print err
375           
376def svnUpdateProcess(version=None,projectfile=None):
377    '''perform an update of GSAS-II in a separate python process'''
378    import subprocess
379    if not projectfile:
380        projectfile = ''
381    else:
382        projectfile = os.path.realpath(projectfile)
383        print 'restart using',projectfile
384    if not version:
385        version = ''
386    else:
387        version = str(version)
388    # start the upgrade in a separate interpreter (avoids loading .pyd files)
389    subprocess.Popen([sys.executable,__file__,projectfile,version])
390    sys.exit()
391
392def svnSwitchDir(rpath,filename,baseURL,loadpath=None):
393    '''This performs a switch command to move files between subversion trees.
394
395    This is currently used for moving tutorial web pages and demo files
396    into the GSAS-II source tree. Note that if the files were previously downloaded
397    the switch command will update the files to the newest version.
398   
399    :param str rpath: path to locate files, relative to the GSAS-II
400      installation path (defaults to path2GSAS2)
401    :param str URL: the repository URL
402    :param str loadpath: the prefix for the path, if specified. Defaults to path2GSAS2
403    '''
404    import subprocess
405    svn = whichsvn()
406    if not svn: return
407    URL = baseURL[:]
408    if baseURL[-1] != '/':
409        URL = baseURL + '/' + filename
410    else:
411        URL = baseURL + filename
412    if loadpath:
413        fpath = os.path.join(loadpath,rpath,filename)
414    else:
415        fpath = os.path.join(path2GSAS2,rpath,filename)
416    cmd = [svn,'switch',URL,fpath,
417           '--non-interactive','--trust-server-cert',
418           '--accept','theirs-conflict','--force']
419    if svnVersionNumber() > 1.6: cmd += ['--ignore-ancestry']
420    print("Loading files from "+URL+'\n  to '+fpath)
421    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
422    out,err = s.communicate()
423    if err:
424        print(60*"=")
425        print ("****** An error was noted, see below *********")
426        print(60*"=")
427        print 'out=',out
428        print 'err=',err
429        return False
430    return True
431
432def svnInstallDir(URL,loadpath):
433    '''Load a subversion tree into a specified directory
434
435    :param str rpath: path to locate files, relative to the GSAS-II
436      installation path (defaults to path2GSAS2)
437    :param str URL: the repository URL
438    '''
439    import subprocess
440    svn = whichsvn()
441    if not svn: return
442    cmd = [svn,'co',URL,loadpath,'--non-interactive']
443    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
444    print("Loading files from "+URL)
445    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
446    out,err = s.communicate()   #this fails too easily
447    if err:
448        print(60*"=")
449        print ("****** An error was noted, see below *********")
450        print(60*"=")
451        print err
452        return False
453    return True
454           
455def IPyBreak_base():
456    '''A routine that invokes an IPython session at the calling location
457    This routine is only used when debug=True is set in config.py
458    '''
459    savehook = sys.excepthook # save the exception hook
460    try: 
461        from IPython.terminal.embed import InteractiveShellEmbed
462    except ImportError:
463        try:
464            # try the IPython 0.12 approach
465            from IPython.frontend.terminal.embed import InteractiveShellEmbed
466        except ImportError:
467            print 'IPython InteractiveShellEmbed not found'
468            return
469    import inspect
470    ipshell = InteractiveShellEmbed()
471
472    frame = inspect.currentframe().f_back
473    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
474    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
475    sys.excepthook = savehook # reset IPython's change to the exception hook
476
477def exceptHook(*args):
478    '''A routine to be called when an exception occurs. It prints the traceback
479    with fancy formatting and then calls an IPython shell with the environment
480    of the exception location.
481   
482    This routine is only used when debug=True is set in config.py   
483    '''
484    from IPython.core import ultratb
485    if 'win' in sys.platform:
486        ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
487    else:
488        ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
489    try: 
490        from IPython.terminal.embed import InteractiveShellEmbed
491    except ImportError:
492        try:
493            # try the IPython 0.12 approach
494            from IPython.frontend.terminal.embed import InteractiveShellEmbed
495        except ImportError:
496            print 'IPython InteractiveShellEmbed not found'
497            return
498    import inspect
499    frame = inspect.getinnerframes(args[2])[-1][0]
500    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
501    savehook = sys.excepthook # save the exception hook
502    InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
503    sys.excepthook = savehook # reset IPython's change to the exception hook
504
505def DoNothing():
506    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
507    except when the debug option is set True in config.py
508    '''
509    pass 
510
511IPyBreak = DoNothing
512pdbBreak = DoNothing
513def InvokeDebugOpts():
514    'Called in GSASII.py to set up debug options'
515    if GetConfigValue('debug'):
516        print 'Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()'
517        sys.excepthook = exceptHook
518        import pdb
519        global pdbBreak
520        pdbBreak = pdb.set_trace
521        global IPyBreak
522        IPyBreak = IPyBreak_base
523   
524if __name__ == '__main__':
525    '''What follows is called to update (or downdate) GSAS-II in a separate process.
526    '''
527    import subprocess
528    import time
529    time.sleep(1) # delay to give the main process a chance to exit
530    # perform an update and restart GSAS-II
531    project,version = sys.argv[1:3]
532    loc = os.path.dirname(__file__)
533    if version:
534        print("Regress to version "+str(version))
535        svnUpdateDir(loc,version=version)
536    else:
537        print("Update to current version")
538        svnUpdateDir(loc)
539    if project:
540        print("Restart GSAS-II with project file "+str(project))
541        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py'),project])
542    else:
543        print("Restart GSAS-II without a project file ")
544        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
545    print 'exiting update process'
546    sys.exit()
547   
Note: See TracBrowser for help on using the repository browser.