source: trunk/GSASIIpath.py @ 2825

Last change on this file since 2825 was 2682, checked in by toby, 8 years ago

fix EditTable? bug

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