source: trunk/GSASIIpath.py @ 4346

Last change on this file since 4346 was 4346, checked in by toby, 21 months ago

implement additional proxy access (documented in https://subversion.xray.aps.anl.gov/trac/pyGSAS/wiki/ProxyInfo)

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