source: trunk/GSASIIpath.py @ 3960

Last change on this file since 3960 was 3960, checked in by toby, 3 years ago

add more svn cleanup attempts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 35.5 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
19from __future__ import division, print_function
20import os
21import sys
22import platform
23import glob
24import subprocess
25import numpy as np
26g2home = 'https://subversion.xray.aps.anl.gov/pyGSAS'
27'Define the location of the GSAS-II subversion repository'
28   
29path2GSAS2 = os.path.dirname(os.path.abspath(os.path.expanduser(__file__))) # location of this file; save before any changes in pwd
30
31# convert version numbers as '1.2.3' to integers (1002) and back (to 1.2)
32fmtver = lambda v: str(v//1000)+'.'+str(v%1000)
33intver = lambda vs: sum([int(i) for i in vs.split('.')[0:2]]*np.array((1000,1)))
34
35def GetConfigValue(key,default=None):
36    '''Return the configuration file value for key or a default value if not present
37   
38    :param str key: a value to be found in the configuration (config.py) file
39    :param default: a value to be supplied is none is in the config file or
40      the config file is not found. Defaults to None
41    :returns: the value found or the default.
42    '''
43    try:
44        return configDict.get(key,default)
45    except NameError: # this happens when building docs
46        return None
47
48def SetConfigValue(parmdict):
49    '''Set configuration variables from a dictionary where elements are lists
50    First item in list is the default value and second is the value to use.
51    '''
52    global configDict
53    for var in parmdict:
54        if var in configDict:
55            del configDict[var]
56        if isinstance(parmdict[var],tuple):
57            configDict[var] = parmdict[var]
58        else:
59            if parmdict[var][1] is None: continue
60            if parmdict[var][1] == '': continue
61            if parmdict[var][0] == parmdict[var][1]: continue
62            configDict[var] = parmdict[var][1]
63
64# routines for looking a version numbers in files
65version = -1
66def SetVersionNumber(RevString):
67    '''Set the subversion version number
68
69    :param str RevString: something like "$Revision: 3960 $"
70      that is set by subversion when the file is retrieved from subversion.
71
72    Place ``GSASIIpath.SetVersionNumber("$Revision: 3960 $")`` in every python
73    file.
74    '''
75    try:
76        RevVersion = int(RevString.split(':')[1].split()[0])
77        global version
78        version = max(version,RevVersion)
79    except:
80        pass
81       
82def GetVersionNumber():
83    '''Return the maximum version number seen in :func:`SetVersionNumber`
84    '''
85    if version > 1000:
86        return version
87    else:
88        return "unknown"
89
90def LoadConfigFile(filename):
91    '''Read a GSAS-II configuration file.
92    Comments (starting with "%") are removed, as are empty lines
93   
94    :param str filename: base file name (such as 'file.dat'). Files with this name
95      are located from the path and the contents of each are concatenated.
96    :returns: a list containing each non-empty (after removal of comments) line
97      found in every matching config file.
98    '''
99    info = []
100    for path in sys.path:
101        fil = os.path.join(path,filename)
102        if not os.path.exists(fil): continue
103        try:
104            i = 0
105            fp = open(fil,'r')
106            for line in fp:
107                expr = line.split('#')[0].strip()
108                if expr:
109                    info.append(expr)
110                    i += 1
111            print(str(i)+' lines read from config file '+fil)
112        finally:
113            fp.close()
114    return info
115
116
117# routines to interface with subversion
118proxycmds = []
119'Used to hold proxy information for subversion, set if needed in whichsvn'
120svnLocCache = None
121'Cached location of svn to avoid multiple searches for it'
122
123def MakeByte2str(arg):
124    '''Convert output from subprocess pipes (bytes) to str (unicode) in Python 3.
125    In Python 2: Leaves output alone (already str).
126    Leaves stuff of other types alone (including unicode in Py2)
127    Works recursively for string-like stuff in nested loops and tuples.
128
129    typical use::
130
131        out = MakeByte2str(out)
132
133    or::
134
135        out,err = MakeByte2str(s.communicate())
136   
137    '''
138    if isinstance(arg,str): return arg
139    if isinstance(arg,bytes): return arg.decode()
140    if isinstance(arg,list):
141        return [MakeByte2str(i) for i in arg]
142    if isinstance(arg,tuple):
143        return tuple([MakeByte2str(i) for i in arg])
144    return arg
145               
146def getsvnProxy():
147    '''Loads a proxy for subversion from the file created by bootstrap.py
148    '''
149    proxyinfo = os.path.join(path2GSAS2,"proxyinfo.txt")
150    if os.path.exists(proxyinfo):
151        global proxycmds
152        proxycmds = []
153        fp = open(proxyinfo,'r')
154        host = fp.readline().strip()
155        port = fp.readline().strip()
156        fp.close()
157        setsvnProxy(host,port)
158        if not host.strip(): return '',''
159        return host,port
160    return '',''
161
162def setsvnProxy(host,port):
163    '''Sets the svn commands needed to use a proxy
164    '''
165    global proxycmds
166    proxycmds = []
167    host = host.strip()
168    port = port.strip()
169    if not host: return
170    proxycmds.append('--config-option')
171    proxycmds.append('servers:global:http-proxy-host='+host)
172    if port:
173        proxycmds.append('--config-option')
174        proxycmds.append('servers:global:http-proxy-port='+port)
175       
176def whichsvn():
177    '''Returns a path to the subversion exe file, if any is found.
178    Searches the current path after adding likely places where GSAS-II
179    might install svn.
180
181    :returns: None if svn is not found or an absolute path to the subversion
182      executable file.
183    '''
184    # use a previosuly cached svn location
185    global svnLocCache
186    if svnLocCache: return svnLocCache
187    # prepare to find svn
188    is_exe = lambda fpath: os.path.isfile(fpath) and os.access(fpath, os.X_OK)
189    svnprog = 'svn'
190    if sys.platform.startswith('win'): svnprog += '.exe'
191    host,port = getsvnProxy()
192    if GetConfigValue('debug') and host:
193        print('DBG_Using proxy host {} port {}'.format(host,port))
194    # add likely places to find subversion when installed with GSAS-II
195    pathlist = os.environ["PATH"].split(os.pathsep)
196    pathlist.insert(0,os.path.split(sys.executable)[0])
197    pathlist.insert(1,path2GSAS2)
198    for rpt in ('..','bin'),('..','Library','bin'),('svn','bin'),('svn',),('.'):
199        pt = os.path.normpath(os.path.join(path2GSAS2,*rpt))
200        if os.path.exists(pt):
201            pathlist.insert(0,pt)   
202    # search path for svn or svn.exe
203    for path in pathlist:
204        exe_file = os.path.join(path, svnprog)
205        if is_exe(exe_file):
206            try:
207                p = subprocess.Popen([exe_file,'help'],stdout=subprocess.PIPE)
208                res = p.stdout.read()
209                p.communicate()
210                svnLocCache = os.path.abspath(exe_file)
211                return svnLocCache
212            except:
213                pass       
214    svnLocCache = None
215
216def svnVersion(svn=None):
217    '''Get the version number of the current subversion executable
218
219    :returns: a string with a version number such as "1.6.6" or None if
220      subversion is not found.
221
222    '''
223    if not svn: svn = whichsvn()
224    if not svn: return
225
226    cmd = [svn,'--version','--quiet']
227    s = subprocess.Popen(cmd,
228                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
229    out,err = MakeByte2str(s.communicate())
230    if err:
231        print ('subversion error!\nout=%s'%out)
232        print ('err=%s'%err)
233        s = '\nsvn command:  '
234        for i in cmd: s += i + ' '
235        print(s)
236        return None
237    return out.strip()
238
239def svnVersionNumber(svn=None):
240    '''Get the version number of the current subversion executable
241
242    :returns: a fractional version number such as 1.6 or None if
243      subversion is not found.
244
245    '''
246    ver = svnVersion(svn)
247    if not ver: return 
248    M,m = ver.split('.')[:2]
249    return int(M)+int(m)/10.
250
251def svnGetLog(fpath=os.path.split(__file__)[0],version=None):
252    '''Get the revision log information for a specific version of the specified package
253
254    :param str fpath: path to repository dictionary, defaults to directory where
255       the current file is located.
256    :param int version: the version number to be looked up or None (default)
257       for the latest version.
258
259    :returns: a dictionary with keys (one hopes) 'author', 'date', 'msg', and 'revision'
260
261    '''
262    import xml.etree.ElementTree as ET
263    svn = whichsvn()
264    if not svn: return
265    if version is not None:
266        vstr = '-r'+str(version)
267    else:
268        vstr = '-rHEAD'
269
270    cmd = [svn,'log',fpath,'--xml',vstr]
271    if proxycmds: cmd += proxycmds
272    s = subprocess.Popen(cmd,
273                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
274    out,err = MakeByte2str(s.communicate())
275    if err:
276        print ('out=%s'%out)
277        print ('err=%s'%err)
278        s = '\nsvn command:  '
279        for i in cmd: s += i + ' '
280        print(s)
281        return None
282    x = ET.fromstring(out)
283    d = {}
284    for i in x.iter('logentry'):
285        d = {'revision':i.attrib.get('revision','?')}
286        for j in i:
287            d[j.tag] = j.text
288        break # only need the first
289    return d
290
291svnLastError = ''
292def svnGetRev(fpath=os.path.split(__file__)[0],local=True):
293    '''Obtain the version number for the either the last update of the local version
294    or contacts the subversion server to get the latest update version (# of Head).
295
296    :param str fpath: path to repository dictionary, defaults to directory where
297       the current file is located
298    :param bool local: determines the type of version number, where
299       True (default): returns the latest installed update
300       False: returns the version number of Head on the server
301
302    :Returns: the version number as an str or
303       None if there is a subversion error (likely because the path is
304       not a repository or svn is not found). The error message is placed in
305       global variable svnLastError
306    '''
307
308    import xml.etree.ElementTree as ET
309    svn = whichsvn()
310    if not svn: return
311    if local:
312        cmd = [svn,'info',fpath,'--xml']
313    else:
314        cmd = [svn,'info',fpath,'--xml','-rHEAD']
315    if svnVersionNumber() >= 1.6:
316        cmd += ['--non-interactive', '--trust-server-cert']
317    if proxycmds: cmd += proxycmds
318    s = subprocess.Popen(cmd, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
319    out,err = MakeByte2str(s.communicate())
320    if err:
321        print ('svn failed\n%s'%out)
322        print ('err=%s'%err)
323        s = '\nsvn command:  '
324        for i in cmd: s += i + ' '
325        print(s)
326        global svnLastError
327        svnLastError = err
328        return None
329    x = ET.fromstring(out)
330    for i in x.iter('entry'):
331        rev = i.attrib.get('revision')
332        if rev: return rev
333
334def svnFindLocalChanges(fpath=os.path.split(__file__)[0]):
335    '''Returns a list of files that were changed locally. If no files are changed,
336       the list has length 0
337
338    :param fpath: path to repository dictionary, defaults to directory where
339       the current file is located
340
341    :returns: None if there is a subversion error (likely because the path is
342       not a repository or svn is not found)
343
344    '''
345    import xml.etree.ElementTree as ET
346    svn = whichsvn()
347    if not svn: return
348    cmd = [svn,'status',fpath,'--xml']
349    if proxycmds: cmd += proxycmds
350    s = subprocess.Popen(cmd,
351                         stdout=subprocess.PIPE,stderr=subprocess.PIPE)
352    out,err = MakeByte2str(s.communicate())
353    if err: return None
354    x = ET.fromstring(out)
355    changed = []
356    for i in x.iter('entry'):
357        if i.find('wc-status').attrib.get('item') == 'modified': 
358            changed.append(i.attrib.get('path'))
359    return changed
360
361def svnCleanup(fpath=os.path.split(__file__)[0],verbose=True):
362    '''This runs svn cleanup on a selected local directory.
363
364    :param str fpath: path to repository dictionary, defaults to directory where
365       the current file is located
366    '''
367    svn = whichsvn()
368    if not svn: return
369    if verbose: print(u"Performing svn cleanup at "+fpath)
370    cmd = [svn,'cleanup',fpath]
371    if verbose:
372        s = 'subversion command:\n  '
373        for i in cmd: s += i + ' '
374        print(s)
375    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
376    out,err = MakeByte2str(s.communicate())
377    if err:
378        print(60*"=")
379        print("****** An error was noted, see below *********")
380        print(60*"=")
381        print(err)
382        s = '\nsvn command:  '
383        for i in cmd: s += i + ' '
384        print(s)
385        #raise Exception('svn cleanup failed')
386        return False
387    elif verbose:
388        print(out)
389    return True
390       
391def svnUpdateDir(fpath=os.path.split(__file__)[0],version=None,verbose=True):
392    '''This performs an update of the files in a local directory from a server.
393
394    :param str fpath: path to repository dictionary, defaults to directory where
395       the current file is located
396    :param version: the number of the version to be loaded. Used only
397       cast as a string, but should be an integer or something that corresponds to a
398       string representation of an integer value when cast. A value of None (default)
399       causes the latest version on the server to be used.
400    '''
401    svn = whichsvn()
402    if not svn: return
403    if version:
404        verstr = '-r' + str(version)
405    else:
406        verstr = '-rHEAD'
407    if verbose: print(u"Updating files at "+fpath)
408    cmd = [svn,'update',fpath,verstr,
409           '--non-interactive',
410           '--accept','theirs-conflict','--force']
411    if svnVersionNumber() >= 1.6:
412        cmd += ['--trust-server-cert']
413    if proxycmds: cmd += proxycmds
414    if verbose:
415        s = 'subversion command:\n  '
416        for i in cmd: s += i + ' '
417        print(s)
418    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
419    out,err = MakeByte2str(s.communicate())
420    if err:
421        print(60*"=")
422        print("****** An error was noted, see below *********")
423        print(60*"=")
424        print(err)
425        s = '\nsvn command:  '
426        for i in cmd: s += i + ' '
427        print(s)
428        if svnCleanup(fpath):
429            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
430            out,err = MakeByte2str(s.communicate())
431            if err:
432                print(60*"=")
433                print("****** Drat, failed again: *********")
434                print(60*"=")
435                print(err)
436            else:
437                return
438        raise Exception('svn update failed')
439    elif verbose:
440        print(out)
441
442def svnUpgrade(fpath=os.path.split(__file__)[0]):
443    '''This reformats subversion files, which may be needed if an upgrade of subversion is
444    done.
445
446    :param str fpath: path to repository dictionary, defaults to directory where
447       the current file is located
448    '''
449    svn = whichsvn()
450    if not svn: return
451    cmd = [svn,'upgrade',fpath,'--non-interactive']
452    if proxycmds: cmd += proxycmds
453    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
454    out,err = MakeByte2str(s.communicate())
455    if err:
456        print("svn upgrade did not happen (this is probably OK). Messages:")
457        print (err)
458        s = '\nsvn command:  '
459        for i in cmd: s += i + ' '
460        print(s)
461
462def svnUpdateProcess(version=None,projectfile=None,branch=None):
463    '''perform an update of GSAS-II in a separate python process'''
464    if not projectfile:
465        projectfile = ''
466    else:
467        projectfile = os.path.realpath(projectfile)
468        print ('restart using %s'%projectfile)
469    if branch:
470        version = branch
471    elif not version:
472        version = ''
473    else:
474        version = str(version)
475    # start the upgrade in a separate interpreter (avoids loading .pyd files)
476    subprocess.Popen([sys.executable,__file__,projectfile,version])
477    sys.exit()
478
479def svnSwitchDir(rpath,filename,baseURL,loadpath=None,verbose=True):
480    '''This performs a switch command to move files between subversion trees.
481    Note that if the files were previously downloaded,
482    the switch command will update the files to the newest version.
483   
484    :param str rpath: path to locate files, relative to the GSAS-II
485      installation path (defaults to path2GSAS2)
486    :param str URL: the repository URL
487    :param str loadpath: the prefix for the path, if specified. Defaults to path2GSAS2
488    :param bool verbose: if True (default) diagnostics are printed
489    '''
490    svn = whichsvn()
491    if not svn: return
492    URL = baseURL[:]
493    if baseURL[-1] != '/':
494        URL = baseURL + '/' + filename
495    else:
496        URL = baseURL + filename
497    if loadpath:
498        fpath = os.path.join(loadpath,rpath,filename)
499    else:
500        fpath = os.path.join(path2GSAS2,rpath,filename)
501    cmd = [svn,'switch',URL,fpath,
502           '--non-interactive','--trust-server-cert',
503           '--accept','theirs-conflict','--force']
504    if svnVersionNumber(svn) > 1.6: cmd += ['--ignore-ancestry']
505    if proxycmds: cmd += proxycmds
506    if verbose: print(u"Loading files to "+fpath+u"\n  from "+URL)
507    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
508    out,err = MakeByte2str(s.communicate())
509    if err:
510        print(60*"=")
511        print ("****** An error was noted, see below *********")
512        print(60*"=")
513        print ('out=%s'%out)
514        print ('err=%s'%err)
515        s = '\nsvn command:  '
516        for i in cmd: s += i + ' '
517        print(s)
518        if svnCleanup(fpath):
519            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
520            out,err = MakeByte2str(s.communicate())
521            if err:
522                print(60*"=")
523                print("****** Drat, failed again: *********")
524                print(60*"=")
525                print(err)
526            else:
527                return True
528        return False
529    if verbose:
530        print('=== Output from svn switch'+(43*'='))
531        print(out.strip())
532        print((70*'=')+'\n')
533    return True
534
535def svnInstallDir(URL,loadpath):
536    '''Load a subversion tree into a specified directory
537
538    :param str URL: the repository URL
539    :param str loadpath: path to locate files
540
541    '''
542    svn = whichsvn()
543    if not svn: return
544    cmd = [svn,'co',URL,loadpath,'--non-interactive']
545    if svnVersionNumber() >= 1.6: cmd += ['--trust-server-cert']
546    print("Loading files from "+URL)
547    if proxycmds: cmd += proxycmds
548    s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
549    out,err = MakeByte2str(s.communicate())   #this fails too easily
550    if err:
551        print(60*"=")
552        print ("****** An error was noted, see below *********")
553        print(60*"=")
554        print (err)
555        s = '\nsvn command:  '
556        for i in cmd: s += i + ' '
557        print(s)
558        if svnCleanup(fpath):
559            s = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
560            out,err = MakeByte2str(s.communicate())
561            if err:
562                print(60*"=")
563                print("****** Drat, failed again: *********")
564                print(60*"=")
565                print(err)
566                return False
567        else:
568            return False
569    print ("Files installed at: "+loadpath)
570    return True
571
572def GetBinaryPrefix():
573    if sys.platform == "win32":
574        prefix = 'win'
575    elif sys.platform == "darwin":
576        prefix = 'mac'
577    elif sys.platform.startswith("linux"):
578        prefix = 'linux'
579    else:
580        print(u'Unknown platform: '+sys.platform)
581        raise Exception('Unknown platform')
582    if platform.architecture()[0] == '64bit':
583        bits = '64'
584    else:
585        bits = '32'
586
587    # format current python version
588    pyver = 'p{}.{}'.format(*sys.version_info[0:2])
589
590    items = [prefix,bits,pyver]
591    return '_'.join(items)
592
593def svnList(URL,verbose=True):
594    '''Get a list of subdirectories from and svn repository
595    '''   
596    svn = whichsvn()
597    if not svn:
598        print('**** unable to load files: svn not found ****')
599        return ''
600    # get binaries matching the required type -- other than for the numpy version
601    cmd = [svn, 'list', URL,'--non-interactive', '--trust-server-cert']
602    if proxycmds: cmd += proxycmds
603    if verbose:
604        s = 'Running svn command:\n  '
605        for i in cmd: s += i + ' '
606        print(s)
607    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
608    res,err = MakeByte2str(p.communicate())
609    return res
610
611def DownloadG2Binaries(g2home,verbose=True):
612    '''Download GSAS-II binaries from appropriate section of the
613    GSAS-II svn repository based on the platform, numpy and Python
614    version
615    '''   
616    bindir = GetBinaryPrefix()
617    #npver = 'n{}.{}'.format(*np.__version__.split('.')[0:2])
618    inpver = intver(np.__version__)
619    svn = whichsvn()
620    if not svn:
621        print('**** unable to load files: svn not found ****')
622        return ''
623    # get binaries matching the required type -- other than for the numpy version
624    cmd = [svn, 'list', g2home + '/Binaries/','--non-interactive', '--trust-server-cert']
625    if proxycmds: cmd += proxycmds
626    if verbose:
627        s = 'Running svn command:\n  '
628        for i in cmd: s += i + ' '
629        print(s)
630    p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
631    res,err = MakeByte2str(p.communicate())
632    versions = {}
633    for d in res.split():
634        if d.startswith(bindir):
635            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
636            versions[v] = d
637    intVersionsList = sorted(versions.keys())
638    if not intVersionsList:
639        print('No binaries located matching',bindir)
640        return
641    elif inpver < min(intVersionsList):
642        vsel = min(intVersionsList)
643        print('Warning: The current numpy version, {}, is older than\n\tthe oldest dist version, {}'
644              .format(np.__version__,fmtver(vsel)))
645    elif inpver >= max(intVersionsList):
646        vsel = max(intVersionsList)
647        if verbose: print(
648                'FYI: The current numpy version, {}, is newer than the newest dist version {}'
649                .format(np.__version__,fmtver(vsel)))
650    else:
651        vsel = min(intVersionsList)
652        for v in intVersionsList:
653            if v <= inpver:
654                vsel = v
655            else:
656                if verbose: print(
657                        'FYI: Selecting dist version {} as the current numpy version, {},\n\tis older than the next dist version {}'
658                        .format(fmtver(vsel),np.__version__,fmtver(v)))
659                break
660    distdir = g2home + '/Binaries/' + versions[vsel]
661    # switch reset command: distdir = g2home + '/trunk/bindist'
662    svnSwitchDir('bindist','',distdir,verbose=verbose)
663    return os.path.join(path2GSAS2,'bindist')
664
665# def svnTestBranch(loc=None):
666#     '''Returns the name of the branch directory if the installation has been switched.
667#     Returns none, if not a branch
668#     the test 2frame branch. False otherwise
669#     '''
670#     if loc is None: loc = path2GSAS2
671#     svn = whichsvn()
672#     if not svn:
673#         print('**** unable to load files: svn not found ****')
674#         return ''
675#     cmd = [svn, 'info', loc]
676#     if proxycmds: cmd += proxycmds
677#     p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
678#     res,err = MakeByte2str(p.communicate())
679#     for l in res.split('\n'):
680#         if "Relative URL:" in l: break
681#     if "/branch/" in l:
682#         return l[l.find("/branch/")+8:].strip()
683#     else:
684#         return None
685   
686def svnSwitch2branch(branch=None,loc=None,svnHome=None):
687    '''Switch to a subversion branch if specified. Switches to trunk otherwise.
688    '''
689    if svnHome is None: svnHome = g2home
690    svnURL = svnHome + '/trunk'
691    if branch:
692        if svnHome.endswith('/'):
693            svnURL = svnHome[:-1]
694        else:
695            svnURL = svnHome
696        if branch.startswith('/'):
697            svnURL += branch
698        else:
699            svnURL += '/' + branch
700    svnSwitchDir('','',svnURL,loadpath=loc)
701   
702
703def IPyBreak_base(userMsg=None):
704    '''A routine that invokes an IPython session at the calling location
705    This routine is only used when debug=True is set in config.py
706    '''
707    savehook = sys.excepthook # save the exception hook
708    try: 
709        from IPython.terminal.embed import InteractiveShellEmbed
710    except ImportError:
711        try:
712            # try the IPython 0.12 approach
713            from IPython.frontend.terminal.embed import InteractiveShellEmbed
714        except ImportError:
715            print ('IPython InteractiveShellEmbed not found')
716            return
717    import inspect
718    ipshell = InteractiveShellEmbed()
719
720    frame = inspect.currentframe().f_back
721    msg   = 'Entering IPython console inside {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
722    if userMsg: msg += userMsg
723    ipshell(msg,stack_depth=2) # Go up one level, to see the calling routine
724    sys.excepthook = savehook # reset IPython's change to the exception hook
725
726try:
727    from IPython.core import ultratb
728except:
729    pass
730def exceptHook(*args):
731    '''A routine to be called when an exception occurs. It prints the traceback
732    with fancy formatting and then calls an IPython shell with the environment
733    of the exception location.
734   
735    This routine is only used when debug=True is set in config.py   
736    '''
737    import IPython.core
738    if sys.platform.startswith('win'):
739        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='NoColor')(*args)
740    else:
741        IPython.core.ultratb.FormattedTB(call_pdb=False,color_scheme='LightBG')(*args)
742
743    try: 
744        from IPython.terminal.embed import InteractiveShellEmbed
745    except ImportError:
746        try:
747            # try the IPython 0.12 approach
748            from IPython.frontend.terminal.embed import InteractiveShellEmbed
749        except ImportError:
750            print ('IPython InteractiveShellEmbed not found')
751            return
752    import inspect
753    frame = inspect.getinnerframes(args[2])[-1][0]
754    msg   = 'Entering IPython console at {0.f_code.co_filename} at line {0.f_lineno}\n'.format(frame)
755    savehook = sys.excepthook # save the exception hook
756    try: # try IPython 5 call 1st
757        class c(object): pass
758        pseudomod = c() # create something that acts like a module
759        pseudomod.__dict__ = frame.f_locals
760        InteractiveShellEmbed(banner1=msg)(module=pseudomod,global_ns=frame.f_globals)
761    except:
762        InteractiveShellEmbed(banner1=msg)(local_ns=frame.f_locals,global_ns=frame.f_globals)
763    sys.excepthook = savehook # reset IPython's change to the exception hook
764
765def DoNothing():
766    '''A routine that does nothing. This is called in place of IPyBreak and pdbBreak
767    except when the debug option is set True in config.py
768    '''
769    pass 
770
771IPyBreak = DoNothing
772pdbBreak = DoNothing
773def InvokeDebugOpts():
774    'Called in GSASII.py to set up debug options'
775    if GetConfigValue('debug'):
776        print ('Debug on: IPython: Exceptions and G2path.IPyBreak(); pdb: G2path.pdbBreak()')
777        sys.excepthook = exceptHook
778        import pdb
779        global pdbBreak
780        pdbBreak = pdb.set_trace
781        global IPyBreak
782        IPyBreak = IPyBreak_base
783
784def TestSPG(fpth):
785    '''Test if pyspg.[so,.pyd] can be run from a location in the path
786    '''
787    if not os.path.exists(fpth): return False
788    if not glob.glob(os.path.join(fpth,'pyspg.*')): return False
789    savpath = sys.path[:]
790    sys.path = [fpth]
791    # test to see if a shared library can be used
792    try:
793        import pyspg
794        pyspg.sgforpy('P -1')
795    except Exception as err:
796        print(70*'=')
797        print('Failed to run pyspg in {}\nerror: {}'.format(fpth,err))
798        print(70*'=')
799        sys.path = savpath
800        return False
801    sys.path = savpath
802    return True
803   
804# see if a directory for local modifications is defined. If so, stick that in the path
805if os.path.exists(os.path.expanduser('~/.G2local/')):
806    sys.path.insert(0,os.path.expanduser('~/.G2local/'))
807    fl = glob.glob(os.path.expanduser('~/.G2local/GSASII*.py*'))
808    files = ""
809    prev = None
810    for f in sorted(fl): # make a list of files, dropping .pyc files where a .py exists
811        f = os.path.split(f)[1]
812        if os.path.splitext(f)[0] == prev: continue
813        prev = os.path.splitext(f)[0]
814        if files: files += ", "
815        files += f
816    if files:
817        print("*"*75)
818        print("Warning: the following source files are locally overridden in "+os.path.expanduser('~/.G2local/'))
819        print("  "+files)
820        print("*"*75)
821
822BinaryPathLoaded = False
823def SetBinaryPath(printInfo=False, loadBinary=True):
824    '''
825    Add location of GSAS-II shared libraries (binaries: .so or .pyd files) to path
826   
827    This routine must be executed after GSASIIpath is imported and before any other
828    GSAS-II imports are done.
829    '''
830    # do this only once no matter how many times it is called
831    global BinaryPathLoaded
832    if BinaryPathLoaded: return
833    try:
834        inpver = intver(np.__version__)
835    except AttributeError: # happens on building docs
836        return
837    binpath = None
838    binprfx = GetBinaryPrefix()
839    for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
840        # Look at bin directory (created by a local compile) before looking for standard dist files
841        searchpathlist = [os.path.join(loc,'bin')]
842        # also look for matching binary dist in loc/AllBinaries
843        versions = {}
844        for d in glob.glob(os.path.join(loc,'AllBinaries',binprfx+'*')):
845            v = intver(d.rstrip('/').split('_')[3].lstrip('n'))
846            versions[v] = d
847        searchpathlist = [os.path.join(loc,'bin')]
848        vmin = None
849        vmax = None
850        for v in sorted(versions.keys()):
851            if v <= inpver:
852                vmin = v
853            elif v > inpver:
854                vmax = v
855                break
856        if vmin in versions:
857            searchpathlist.append(versions[vmin])
858        if vmax in versions:
859            searchpathlist.append(versions[vmax])
860        searchpathlist.append(os.path.join(loc,'bindist'))
861        for fpth in searchpathlist:
862            if TestSPG(fpth):
863                binpath = fpth
864                break       
865        if binpath: break
866    if binpath:                                            # were GSAS-II binaries found
867        sys.path.insert(0,binpath)
868        if printInfo:
869            print('GSAS-II binary directory: {}'.format(binpath))
870        BinaryPathLoaded = True
871    elif not loadBinary:
872        raise Exception
873    else:                                                  # try loading them
874        if printInfo:
875            print('Attempting to download GSAS-II binary files...')
876        try:
877            binpath = DownloadG2Binaries(g2home)
878        except AttributeError:   # this happens when building in Read The Docs
879            if printInfo:
880                print('Problem with download')
881        if binpath and TestSPG(binpath):
882            if printInfo:
883                print('GSAS-II binary directory: {}'.format(binpath))
884            sys.path.insert(0,binpath)
885            BinaryPathLoaded = True
886        # this must be imported before anything that imports any .pyd/.so file for GSASII
887        else:
888            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
889            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
890            # patch: use old location based on the host OS and the python version, 
891            # path is relative to location of the script that is called as well as this file
892            BinaryPathLoaded = True
893            bindir = None
894            if sys.platform == "win32":
895                if platform.architecture()[0] == '64bit':
896                    bindir = 'binwin64-%d.%d' % sys.version_info[0:2]
897                else:
898                    bindir = 'binwin%d.%d' % sys.version_info[0:2]
899            elif sys.platform == "darwin":
900                if platform.architecture()[0] == '64bit':
901                    bindir = 'binmac64-%d.%d' % sys.version_info[0:2]
902                else:
903                    bindir = 'binmac%d.%d' % sys.version_info[0:2]
904                #if platform.mac_ver()[0].startswith('10.5.'):
905                #    bindir += '_10.5'
906            elif sys.platform.startswith("linux"):
907                if platform.architecture()[0] == '64bit':
908                    bindir = 'binlinux64-%d.%d' % sys.version_info[0:2]
909                else:
910                    bindir = 'binlinux%d.%d' % sys.version_info[0:2]
911            for loc in os.path.abspath(sys.path[0]),os.path.abspath(os.path.split(__file__)[0]):
912            # Look at bin directory (created by a local compile) before standard dist
913            # that at the top of the path
914                fpth = os.path.join(loc,bindir)
915                binpath = fpth
916                if TestSPG(fpth):
917                    sys.path.insert(0,binpath)
918                    if printInfo:
919                        print('\n'+75*'*')
920                        print('  Warning. Using an old-style GSAS-II binary library. This is unexpected')
921                        print('  and will break in future GSAS-II versions. Please contact toby@anl.gov')
922                        print('  so we can learn what is not working on your installation.')
923                        print('GSAS-II binary directory: {}'.format(binpath))
924                        print(75*'*')
925                    break
926            else:
927            # end patch
928            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
929            #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
930                if printInfo:
931                    print(75*'*')
932                    print('Use of GSAS-II binary directory {} failed!'.format(binpath))
933                    print(75*'*')
934                raise Exception("**** ERROR GSAS-II binary libraries not found, GSAS-II cannot run ****")
935
936    # add the data import and export directory to the search path
937    newpath = os.path.join(path2GSAS2,'imports')
938    if newpath not in sys.path: sys.path.append(newpath)
939    newpath = os.path.join(path2GSAS2,'exports')
940    if newpath not in sys.path: sys.path.append(newpath)
941
942    # setup read of config.py, if present
943    global configDict
944    try:
945        import config
946        configDict = config.__dict__
947        import inspect
948        vals = [True for i in inspect.getmembers(config) if '__' not in i[0]]
949        if printInfo:
950            print (str(len(vals))+' values read from config file '+os.path.abspath(config.__file__))
951    except ImportError:
952        configDict = {'Clip_on':True}
953    except Exception as err:
954        if printInfo:
955            print("Error importing config.py file: "+str(err))
956        configDict = {'Clip_on':True}
957
958if __name__ == '__main__':
959    '''What follows is called to update (or downdate) GSAS-II in a separate process.
960    '''
961    import time
962    time.sleep(1) # delay to give the main process a chance to exit
963    # perform an update and restart GSAS-II
964    project,version = sys.argv[1:3]
965    loc = os.path.dirname(__file__)
966    if version == 'trunk':
967        svnSwitch2branch('')
968    elif '/' in version:
969        svnSwitch2branch(version)
970    elif version:
971        print("Regress to version "+str(version))
972        svnUpdateDir(loc,version=version)
973    else:
974        print("Update to current version")
975        svnUpdateDir(loc)
976    ex = sys.executable
977    if sys.platform == "darwin": # mac requires pythonw which is not always reported as sys.executable
978        if os.path.exists(ex+'w'): ex += 'w'
979    if project:
980        print("Restart GSAS-II with project file "+str(project))
981        subprocess.Popen([ex,os.path.join(loc,'GSASII.py'),project])
982    else:
983        print("Restart GSAS-II without a project file ")
984        subprocess.Popen([ex,os.path.join(loc,'GSASII.py')])
985    print ('exiting update process')
986    sys.exit()
Note: See TracBrowser for help on using the repository browser.