source: trunk/GSASIIpath.py @ 1936

Last change on this file since 1936 was 1729, checked in by toby, 10 years ago

switch default download dir; remove old tutorial help item; pretty-up help dialog; fix bug on directory change

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 18.0 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: 1729 $"
90      that is set by subversion when the file is retrieved from subversion.
91
92    Place ``GSASIIpath.SetVersionNumber("$Revision: 1729 $")`` 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 svnVersion():
156    '''Get the version number of the current subversion executable
157
158    :returns: a string with a version number such as "1.6.6" or None if
159      subversion is not found.
160
161    '''
162    import subprocess
163    svn = whichsvn()
164    if not svn: return
165
166    cmd = [svn,'--version','--quiet']
167    s = subprocess.Popen(cmd,
168                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
169    out,err = s.communicate()
170    if err:
171        print 'subversion error!\nout=',out
172        print 'err=',err
173        return None
174    return out.strip()
175
176def svnVersionNumber():
177    '''Get the version number of the current subversion executable
178
179    :returns: a fractional version number such as 1.6 or None if
180      subversion is not found.
181
182    '''
183    ver = svnVersion()
184    if not ver: return 
185    M,m = svnVersion().split('.')[:2]
186    return int(M)+int(m)/10.
187
188def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
189    '''Get the revision log information for a specific version of the specified package
190
191    :param str fpath: path to repository dictionary, defaults to directory where
192       the current file is located.
193    :param int version: the version number to be looked up or None (default)
194       for the latest version.
195
196    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
197
198    '''
199    import subprocess
200    import xml.etree.ElementTree as ET
201    svn = whichsvn()
202    if not svn: return
203    if version is not None:
204        vstr = '-r'+str(version)
205    else:
206        vstr = '-rHEAD'
207
208    cmd = [svn,'log',fpath,'--xml',vstr]
209    s = subprocess.Popen(cmd,
210                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
211    out,err = s.communicate()
212    if err:
213        print 'out=',out
214        print 'err=',err
215        return None
216    x = ET.fromstring(out)
217    d = {}
218    for i in x.iter('logentry'):
219        d = {'revision':i.attrib.get('revision','?')}
220        for j in i:
221            d[j.tag] = j.text
222        break # only need the first
223    return d
224
225def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
226    '''Obtain the version number for the either the last update of the local version
227    or contacts the subversion server to get the latest update version (# of Head).
228
229    :param str fpath: path to repository dictionary, defaults to directory where
230       the current file is located
231    :param bool local: determines the type of version number, where
232       True (default): returns the latest installed update
233       False: returns the version number of Head on the server
234
235    :Returns: the version number as an str or
236       None if there is a subversion error (likely because the path is
237       not a repository or svn is not found)
238    '''
239
240    import subprocess
241    import xml.etree.ElementTree as ET
242    svn = whichsvn()
243    if not svn: return
244    if local:
245        cmd = [svn,'info',fpath,'--xml']
246    else:
247        cmd = [svn,'info',fpath,'--xml','-rHEAD']
248    if svnVersionNumber() >= 1.6:
249        cmd += ['--non-interactive', '--trust-server-cert']
250    s = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
251    out,err = s.communicate()
252    if err:
253        print 'svn failed\n',out
254        print 'err=',err
255        return None
256    x = ET.fromstring(out)
257    for i in x.iter('entry'):
258        rev = i.attrib.get('revision')
259        if rev: return rev
260
261def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
262    '''Returns a list of files that were changed locally. If no files are changed,
263       the list has length 0
264
265    :param fpath: path to repository dictionary, defaults to directory where
266       the current file is located
267
268    :returns: None if there is a subversion error (likely because the path is
269       not a repository or svn is not found)
270
271    '''
272    import subprocess
273    import xml.etree.ElementTree as ET
274    svn = whichsvn()
275    if not svn: return
276    cmd = [svn,'status',fpath,'--xml']
277    s = subprocess.Popen(cmd,
278                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
279    out,err = s.communicate()
280    if err: return None
281    x = ET.fromstring(out)
282    changed = []
283    for i in x.iter('entry'):
284        if i.find('wc-status').attrib.get('item') == 'modified': 
285            changed.append(i.attrib.get('path'))
286    return changed
287
288def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None):
289    '''This performs an update of the files in a local directory from a server.
290
291    :param str fpath: path to repository dictionary, defaults to directory where
292       the current file is located
293    :param version: the number of the version to be loaded. Used only
294       cast as a string, but should be an integer or something that corresponds to a
295       string representation of an integer value when cast. A value of None (default)
296       causes the latest version on the server to be used.
297    '''
298    import subprocess
299    svn = whichsvn()
300    if not svn: return
301    if version:
302        verstr = '-r' + str(version)
303    else:
304        verstr = '-rHEAD'
305    cmd = [svn,'update',fpath,verstr,
306           '--non-interactive',
307           '--accept','theirs-conflict','--force']
308    if svnVersionNumber() >= 1.6:
309        cmd += ['--trust-server-cert']
310    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
311    out,err = s.communicate()
312    if err:
313        print(60*"=")
314        print ("****** An error was noted, see below *********")
315        print(60*"=")
316        print err
317        sys.exit()
318
319def svnUpgrade(fpath=os.path.split(__file__)[0]):
320    '''This reformats subversion files, which may be needed if an upgrade of subversion is
321    done.
322
323    :param str fpath: path to repository dictionary, defaults to directory where
324       the current file is located
325    '''
326    import subprocess
327    svn = whichsvn()
328    if not svn: return
329    cmd = [svn,'upgrade',fpath,'--non-interactive']
330    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
331    out,err = s.communicate()
332    if err:
333        print("svn upgrade did not happen (this is probably OK). Messages:")
334        print err
335           
336def svnUpdateProcess(version=None,projectfile=None):
337    '''perform an update of GSAS-II in a separate python process'''
338    import subprocess
339    if not projectfile:
340        projectfile = ''
341    else:
342        projectfile = os.path.realpath(projectfile)
343        print 'restart using',projectfile
344    if not version:
345        version = ''
346    else:
347        version = str(version)
348    # start the upgrade in a separate interpreter (avoids loading .pyd files)
349    subprocess.Popen([sys.executable,__file__,projectfile,version])
350    sys.exit()
351
352def svnSwitchDir(rpath,filename,baseURL,loadpath=None):
353    '''This performs a switch command to move files between subversion trees.
354
355    This is currently used for moving tutorial web pages and demo files
356    into the GSAS-II source tree. Note that if the files were previously downloaded
357    the switch command will update the files to the newest version.
358   
359    :param str rpath: path to locate files, relative to the GSAS-II
360      installation path (defaults to path2GSAS2)
361    :param str URL: the repository URL
362    :param str loadpath: the prefix for the path, if specified. Defaults to path2GSAS2
363    '''
364    import subprocess
365    svn = whichsvn()
366    if not svn: return
367    URL = baseURL[:]
368    if baseURL[-1] != '/':
369        URL = baseURL + '/' + filename
370    else:
371        URL = baseURL + filename
372    if loadpath:
373        fpath = os.path.join(loadpath,rpath,filename)
374    else:
375        fpath = os.path.join(path2GSAS2,rpath,filename)
376    cmd = [svn,'switch',URL,fpath,
377           '--non-interactive','--trust-server-cert',
378           '--accept','theirs-conflict','--force']
379    if svnVersionNumber() > 1.6: cmd += ['--ignore-ancestry']
380    print("Loading files from "+URL+'\n  to '+fpath)
381    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
382    out,err = s.communicate()
383    if err:
384        print(60*"=")
385        print ("****** An error was noted, see below *********")
386        print(60*"=")
387        print 'out=',out
388        print 'err=',err
389        return False
390    return True
391
392def svnInstallDir(URL,loadpath):
393    '''Load a subversion tree into a specified directory
394
395    :param str rpath: path to locate files, relative to the GSAS-II
396      installation path (defaults to path2GSAS2)
397    :param str URL: the repository URL
398    '''
399    import subprocess
400    svn = whichsvn()
401    if not svn: return
402    cmd = [svn,'co',URL,loadpath,'--non-interactive']
403    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
404    print("Loading files from "+URL)
405    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
406    out,err = s.communicate()
407    if err:
408        print(60*"=")
409        print ("****** An error was noted, see below *********")
410        print(60*"=")
411        print err
412        return False
413    return True
414           
415def IPyBreak_base():
416    '''A routine that invokes an IPython session at the calling location
417    This routine is only used when debug=True is set in config.py
418    '''
419    savehook = sys.excepthook # save the exception hook
420    try: 
421        from IPython.terminal.embed import InteractiveShellEmbed
422    except ImportError:
423        try:
424            # try the IPython 0.12 approach
425            from IPython.frontend.terminal.embed import InteractiveShellEmbed
426        except ImportError:
427            print 'IPython InteractiveShellEmbed not found'
428            return
429    import inspect
430    ipshell = InteractiveShellEmbed()
431
432    frame = inspect.currentframe().f_back
433    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
434    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
435    sys.excepthook = savehook # reset IPython's change to the exception hook
436
437def exceptHook(*args):
438    '''A routine to be called when an exception occurs. It prints the traceback
439    with fancy formatting and then calls an IPython shell with the environment
440    of the exception location.
441   
442    This routine is only used when debug=True is set in config.py   
443    '''
444    from IPython.core import ultratb
445    if 'win' in sys.platform:
446        ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
447    else:
448        ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
449    try: 
450        from IPython.terminal.embed import InteractiveShellEmbed
451    except ImportError:
452        try:
453            # try the IPython 0.12 approach
454            from IPython.frontend.terminal.embed import InteractiveShellEmbed
455        except ImportError:
456            print 'IPython InteractiveShellEmbed not found'
457            return
458    import inspect
459    frame = inspect.getinnerframes(args[2])[-1][0]
460    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}'.format(frame)
461    savehook = sys.excepthook # save the exception hook
462    InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
463    sys.excepthook = savehook # reset IPython's change to the exception hook
464
465def DoNothing():
466    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
467    except when the debug option is set True in config.py
468    '''
469    pass 
470
471IPyBreak = DoNothing
472pdbBreak = DoNothing
473def InvokeDebugOpts():
474    'Called in GSASII.py to set up debug options'
475    if GetConfigValue('debug'):
476        print 'Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()'
477        sys.excepthook = exceptHook
478        import pdb
479        global pdbBreak
480        pdbBreak = pdb.set_trace
481        global IPyBreak
482        IPyBreak = IPyBreak_base
483   
484if __name__ == '__main__':
485    '''What follows is called to update (or downdate) GSAS-II in a separate process.
486    '''
487    import subprocess
488    import time
489    time.sleep(1) # delay to give the main process a chance to exit
490    # perform an update and restart GSAS-II
491    project,version = sys.argv[1:3]
492    loc = os.path.dirname(__file__)
493    if version:
494        print("Regress to version "+str(version))
495        svnUpdateDir(loc,version=version)
496    else:
497        print("Update to current version")
498        svnUpdateDir(loc)
499    if project:
500        print("Restart GSAS-II with project file "+str(project))
501        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py'),project])
502    else:
503        print("Restart GSAS-II without a project file ")
504        subprocess.Popen([sys.executable,os.path.join(loc,'GSASII.py')])
505    print 'exiting update process'
506    sys.exit()
507   
Note: See TracBrowser for help on using the repository browser.