source: trunk/GSASIIpath.py @ 1676

Last change on this file since 1676 was 1676, checked in by toby, 9 years ago

Ignore equivalences that are not in use; start on svn switch implementation for help

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 15.9 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: 1676 $"
90      that is set by subversion when the file is retrieved from subversion.
91
92    Place ``GSASIIpath.SetVersionNumber("$Revision: 1676 $")`` 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 svnSwitchDir(rpath,URL,loadpath=None):
312    '''This performs a switch command to move files between subversion trees.
313
314    This is currently used for moving tutorial web pages and demo files
315    into the GSAS-II source tree.
316   
317    :param str rpath: path to locate files, relative to the GSAS-II
318      installation path (defaults to path2GSAS2)
319    :param str URL: the repository URL
320    '''
321    import subprocess
322    svn = whichsvn()
323    if not svn: return
324    if loadpath:
325        fpath = os.path.join(loadpath,rpath)
326    else:
327        fpath = os.path.join(path2GSAS2,rpath)
328    cmd = [svn,'switch','--ignore-ancestry',URL,fpath,
329           '--non-interactive',
330           '--accept','theirs-conflict','--force']
331    print("Loading files from "+URL)
332    s = subprocess.Popen(cmd+['--trust-server-cert'], 
333                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
334    out,err = s.communicate()
335    print out
336    if err:
337        s = subprocess.Popen(cmd,
338                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
339        out,err = s.communicate()
340        if err:
341            print(60*"=")
342            print ("****** An error was noted, see below *********")
343            print(60*"=")
344            print err
345
346def IPyBreak_base():
347    '''A routine that invokes an IPython session at the calling location
348    This routine is only used when debug=True is set in config.py
349    '''
350    savehook = sys.excepthook # save the exception hook
351    try: 
352        from IPython.terminal.embed import InteractiveShellEmbed
353    except ImportError:
354        try:
355            # try the IPython 0.12 approach
356            from IPython.frontend.terminal.embed import InteractiveShellEmbed
357        except ImportError:
358            print 'IPython InteractiveShellEmbed not found'
359            return
360    import inspect
361    ipshell = InteractiveShellEmbed()
362
363    frame = inspect.currentframe().f_back
364    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
365    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
366    sys.excepthook = savehook # reset IPython's change to the exception hook
367
368def exceptHook(*args):
369    '''A routine to be called when an exception occurs. It prints the traceback
370    with fancy formatting and then calls an IPython shell with the environment
371    of the exception location.
372   
373    This routine is only used when debug=True is set in config.py   
374    '''
375    from IPython.core import ultratb
376    if 'win' in sys.platform:
377        ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
378    else:
379        ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
380    try: 
381        from IPython.terminal.embed import InteractiveShellEmbed
382    except ImportError:
383        try:
384            # try the IPython 0.12 approach
385            from IPython.frontend.terminal.embed import InteractiveShellEmbed
386        except ImportError:
387            print 'IPython InteractiveShellEmbed not found'
388            return
389    import inspect
390    frame = inspect.getinnerframes(args[2])[-1][0]
391    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
392    savehook = sys.excepthook # save the exception hook
393    InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
394    sys.excepthook = savehook # reset IPython's change to the exception hook
395
396def DoNothing():
397    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
398    except when the debug option is set True in config.py
399    '''
400    pass 
401
402IPyBreak = DoNothing
403pdbBreak = DoNothing
404def InvokeDebugOpts():
405    'Called in GSASII.py to set up debug options'
406    if GetConfigValue('debug'):
407        print 'Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()'
408        sys.excepthook = exceptHook
409        import pdb
410        global pdbBreak
411        pdbBreak = pdb.set_trace
412        global IPyBreak
413        IPyBreak = IPyBreak_base
414   
415if __name__ == '__main__':
416    '''What follows is called to update (or downdate) GSAS-II in a separate process.
417    '''
418    import subprocess
419    import time
420    time.sleep(1) # delay to give the main process a chance to exit
421    # perform an update and restart GSAS-II
422    project,version = sys.argv[1:3]
423    loc = os.path.dirname(__file__)
424    if version:
425        print("Regress to version "+str(version))
426        svnUpdateDir(loc,version=version)
427    else:
428        print("Update to current version")
429        svnUpdateDir(loc)
430    if project:
431        print("Restart GSAS-II with project file "+str(project))
432        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py'),project])
433    else:
434        print("Restart GSAS-II without a project file ")
435        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
436    print 'exiting update process'
437    sys.exit()
438   
Note: See TracBrowser for help on using the repository browser.