source: trunk/GSASIIpath.py @ 1558

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

add some debug options with pdb and IPython

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 13.6 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# determine a binary path for the pyd files based on the host OS and the python version, 
23# path is relative to location of the script that is called as well as this file
24# this must be imported before anything that imports any .pyd/.so file for GSASII
25bindir = None
26if sys.platform == "win32":
27    if platform.architecture()[0] == '64bit':
28        bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
29    else:
30        bindir = 'binwin%d.%d' % sys.version_info[0:2]
31elif sys.platform == "darwin":
32    import platform
33    if platform.architecture()[0] == '64bit':
34        bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
35    else:
36        bindir = 'binmac%d.%d' % sys.version_info[0:2]
37    if platform.mac_ver()[0].startswith('10.5.'):
38        bindir += '_10.5'
39elif sys.platform == "linux2":
40    if platform.architecture()[0] == '64bit':
41        bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
42    else:
43        bindir = 'binlinux%d.%d' % sys.version_info[0:2]
44for loc in sys.path[0],os.path.abspath(os.path.split(__file__)[0]):
45    if bindir:
46        if os.path.exists(os.path.join(loc,bindir)) and os.path.join(loc,bindir) not in sys.path: 
47            sys.path.insert(0,os.path.join(loc,bindir))
48        # is there a bin directory? (created by a local compile), if so put
49        # that at the top of the path
50    if os.path.exists(os.path.join(loc,'bin')) and os.path.getsize(os.path.join(loc,'bin')):
51        bindir = 'bin'
52        if os.path.join(loc,'bin') not in sys.path: 
53            sys.path.insert(0,os.path.join(loc,bindir))
54print 'GSAS-II binary directory: ',os.path.join(loc,bindir)
55if bindir == None:
56    raise Exception,"**** ERROR GSAS-II binary libraries not found, GSAS-II fails ****"
57# add the data import and export directory to the search path
58path2GSAS2 = os.path.dirname(os.path.realpath(__file__)) # location of this file; save before any changes in pwd
59newpath = os.path.join(path2GSAS2,'imports')
60if newpath not in sys.path: sys.path.append(newpath)
61newpath = os.path.join(path2GSAS2,'exports')
62if newpath not in sys.path: sys.path.append(newpath)
63
64# setup read of config.py, if present
65try:
66    import config
67    configDict = config.__dict__
68    import inspect
69    vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
70    print str(len(vals))+' values read from config file '+os.path.abspath(config.__file__)
71except ImportError:
72    configDict = {}
73   
74def GetConfigValue(key,default=None):
75    '''Return the configuration file value for key or a default value if not present
76   
77    :param str key: a value to be found in the configuration (config.py) file
78    :param default: a value to be supplied is none is in the config file or
79      the config file is not found. Defaults to None
80    :returns: the value found or the default.
81    '''
82    return configDict.get(key,default)
83
84# routines for looking a version numbers in files
85version = -1
86def SetVersionNumber(RevString):
87    '''Set the subversion version number
88
89    :param str RevString: something like "$Revision: 1558 $"
90      that is set by subversion when the file is retrieved from subversion.
91
92    Place ``GSASIIpath.SetVersionNumber("$Revision: 1558 $")`` in every python
93    file.
94    '''
95    try:
96        RevVersion = int(RevString.split(':')[1].split()[0])
97        global version
98        version = max(version,RevVersion)
99    except:
100        pass
101       
102def GetVersionNumber():
103    '''Return the maximum version number seen in :func:`SetVersionNumber`
104    '''
105    return version
106
107def LoadConfigFile(filename):
108    '''Read a GSAS-II configuration file.
109    Comments (starting with "%") are removed, as are empty lines
110   
111    :param str filename: base file name (such as 'file.dat'). Files with this name
112      are located from the path and the contents of each are concatenated.
113    :returns: a list containing each non-empty (after removal of comments) line
114      found in every matching config file.
115    '''
116    info = []
117    for path in sys.path:
118        fil = os.path.join(path,filename)
119        if not os.path.exists(fil): continue
120        try:
121            i = 0
122            fp = open(fil,'r')
123            for line in fp:
124                expr = line.split('#')[0].strip()
125                if expr:
126                    info.append(expr)
127                    i += 1
128            print(str(i)+' lines read from config file '+fil)
129        finally:
130            fp.close()
131    return info
132
133
134# routines to interface with subversion
135def whichsvn():
136    '''Returns a path to the subversion exe file, if any is found.
137    Searches the current path as well as subdirectory "svn" and
138    "svn/bin" in the location of the GSASII source files.
139
140    :returns: None if svn is not found or an absolute path to the subversion
141      executable file.
142    '''
143    def is_exe(fpath):
144        return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
145    svnprog = 'svn'
146    if sys.platform == "win32": svnprog += '.exe'
147    pathlist = os.environ["PATH"].split(os.pathsep)
148    pathlist.insert(0,os.path.join(os.path.split(__file__)[0],'svn'))
149    pathlist.insert(1,os.path.join(os.path.split(__file__)[0],'svn','bin'))
150    for path in pathlist:
151        exe_file = os.path.join(path, svnprog)
152        if is_exe(exe_file):
153            return os.path.abspath(exe_file)
154
155def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
156    '''Get the revision log information for a specific version of the
157
158    :param str fpath: path to repository dictionary, defaults to directory where
159       the current file is located.
160    :param int version: the version number to be looked up or None (default)
161       for the latest version.
162
163    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
164
165    '''
166    import subprocess
167    import xml.etree.ElementTree as ET
168    svn = whichsvn()
169    if not svn: return
170    if version is not None:
171        vstr = '-r'+str(version)
172    else:
173        vstr = '-rHEAD'
174
175    cmd = [svn,'log',fpath,'--xml',vstr]
176    s = subprocess.Popen(cmd,
177                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
178    out,err = s.communicate()
179    if err:
180        print 'out=',out
181        print 'err=',err
182        return None
183    x = ET.fromstring(out)
184    d = {}
185    for i in x.iter('logentry'):
186        d = {'revision':i.attrib.get('revision','?')}
187        for j in i:
188            d[j.tag] = j.text
189        break # only need the first
190    return d
191
192def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
193    '''Obtain the version number for the either the last update of the local version
194    or contacts the subversion server to get the latest update version (# of Head).
195
196    :param str fpath: path to repository dictionary, defaults to directory where
197       the current file is located
198    :param bool local: determines the type of version number, where
199       True (default): returns the latest installed update
200       False: returns the version number of Head on the server
201
202    :Returns: the version number as an str or
203       None if there is a subversion error (likely because the path is
204       not a repository or svn is not found)
205    '''
206
207    import subprocess
208    import xml.etree.ElementTree as ET
209    svn = whichsvn()
210    if not svn: return
211    if local:
212        cmd = [svn,'info',fpath,'--xml']
213    else:
214        cmd = [svn,'info',fpath,'--xml','-rHEAD']
215    s = subprocess.Popen(cmd+['--non-interactive', '--trust-server-cert'],
216                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
217    out,err = s.communicate()
218    if err:
219        print 'svn failed, retry w/o --trust...\nout=',out
220        print 'err=',err
221        s = subprocess.Popen(cmd,
222                            stdout=subprocess.PIPE,stderr=subprocess.PIPE)
223        out,err = s.communicate()
224        if err:
225            print 'out=',out
226            print 'err=',err
227            return None
228    x = ET.fromstring(out)
229    for i in x.iter('entry'):
230        rev = i.attrib.get('revision')
231        if rev: return rev
232
233def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
234    '''Returns a list of files that were changed locally. If no files are changed,
235       the list has length 0
236
237    :param fpath: path to repository dictionary, defaults to directory where
238       the current file is located
239
240    :returns: None if there is a subversion error (likely because the path is
241       not a repository or svn is not found)
242
243    '''
244    import subprocess
245    import xml.etree.ElementTree as ET
246    svn = whichsvn()
247    if not svn: return
248    cmd = [svn,'status',fpath,'--xml']
249    s = subprocess.Popen(cmd,
250                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
251    out,err = s.communicate()
252    if err: return None
253    x = ET.fromstring(out)
254    changed = []
255    for i in x.iter('entry'):
256        if i.find('wc-status').attrib.get('item') == 'modified': 
257            changed.append(i.attrib.get('path'))
258    return changed
259
260def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None):
261    '''This performs an update of the files in a local directory from a server.
262
263    :param str fpath: path to repository dictionary, defaults to directory where
264       the current file is located
265    :param version: the number of the version to be loaded. Used only
266       cast as a string, but should be an integer or something that corresponds to a
267       string representation of an integer value when cast. A value of None (default)
268       causes the latest version on the server to be used.
269    '''
270    import subprocess
271    svn = whichsvn()
272    if not svn: return
273    if version:
274        verstr = '-r' + str(version)
275    else:
276        verstr = '-rHEAD'
277    cmd = [svn,'update',fpath,verstr,
278           '--non-interactive',
279           '--accept','theirs-conflict','--force']
280    s = subprocess.Popen(cmd+['--trust-server-cert'], 
281                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
282    out,err = s.communicate()
283    print out
284    if err:
285        s = subprocess.Popen(cmd,
286                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
287        out,err = s.communicate()
288        if err:
289            print(60*"=")
290            print ("****** An error was noted, see below *********")
291            print(60*"=")
292            print err
293            sys.exit()
294
295def svnUpdateProcess(version=None,projectfile=None):
296    '''perform an update of GSAS-II in a separate python process'''
297    import subprocess
298    if not projectfile:
299        projectfile = ''
300    else:
301        projectfile = os.path.realpath(projectfile)
302        print 'restart using',projectfile
303    if not version:
304        version = ''
305    else:
306        version = str(version)
307    # start the upgrade in a separate interpreter (avoids loading .pyd files)
308    subprocess.Popen([sys.executable,__file__,projectfile,version])
309    sys.exit()
310
311def IPyBreak():
312    '''A routine that invokes an IPython session at the calling location
313    This routine is only used when debug=True is set in config.py
314    '''
315    savehook = sys.excepthook # save the exception hook
316    from IPython.terminal.embed import InteractiveShellEmbed
317    import inspect
318    ipshell = InteractiveShellEmbed()
319
320    frame = inspect.currentframe().f_back
321    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
322    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
323    sys.excepthook = savehook # reset IPython's change to the exception hook
324
325def exceptHook(*args):
326    '''A routine to be called when an exception occurs. It prints the traceback
327    with fancy formatting and then calls an IPython shell with the environment
328    of the exception location.
329   
330    This routine is only used when debug=True is set in config.py   
331    '''
332    from IPython.core import ultratb
333    ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
334    from IPython.terminal.embed import InteractiveShellEmbed
335    import inspect
336    frame = inspect.getinnerframes(args[2])[-1][0]
337    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
338    InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
339
340def DoNothing():
341    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
342    except when the debug option is set True in config.py
343    '''
344    pass 
345
346if GetConfigValue('debug'):
347    print 'Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()'
348    sys.excepthook = exceptHook
349    import pdb
350    pdbBreak = pdb.set_trace
351else:
352    IPyBreak = DoNothing
353    pdbBreak = DoNothing
354   
355if __name__ == '__main__':
356    import subprocess
357    import time
358    time.sleep(1) # delay to give the main process a chance to exit
359    # perform an update and restart GSAS-II
360    project,version = sys.argv[1:3]
361    loc = os.path.dirname(__file__)
362    if version:
363        print("Regress to version "+str(version))
364        svnUpdateDir(loc,version=version)
365    else:
366        print("Update to current version")
367        svnUpdateDir(loc)
368    if project:
369        print("Restart GSAS-II with project file "+str(project))
370        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py'),project])
371    else:
372        print("Restart GSAS-II without a project file ")
373        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
374    print 'exiting update process'
375    sys.exit()
376   
Note: See TracBrowser for help on using the repository browser.