source: trunk/GSASIIpath.py @ 4352

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

expand proxy GUI for more lines

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