source: trunk/GSASIIpath.py @ 1657

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

migrate help from G2grid; set instparms in Powder imports; seach for imports in data path; start on tutorial migration

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