Back to index

python-biopython  1.60
Functions | Variables
do2to3 Namespace Reference

Functions

def run2to3
def do_update
def main

Variables

string python2_source = "."
string python3_source = "build/py%i.%i"

Detailed Description

Helper script for building and installing Biopython on Python 3.

Note that we can't just use distutils.command.build_py function build_py_2to3
in setup.py since (as far as I can see) that does not allow us to alter the
2to3 options. In particular, we need to turn off the long fixer for some of
our files.

This code is intended to be called from setup.py automatically under Python 3,
and is not intended for end users. The basic idea follows the approach taken
by NumPy with their setup.py file calling tools/py3tool.py to do the 2to3
conversion automatically.

This calls the lib2to3 library functions to convert the Biopython source code
from Python 2 to Python 3, tracking changes to files so that unchanged files
need not be reconverted making development much easier (i.e. if you edit one
source file, doing 'python setup.py install' will only reconvert the one file).
This is done by the last modified date stamps (which will be updated by git if
you switch branches).

NOTE - This is intended to be run under Python 3 (not under Python 2), but
care has been taken to make it run under Python 2 enough to give a clear error
message. In particular, this meant avoiding with statements etc.

Function Documentation

def do2to3.do_update (   py2folder,
  py3folder,
  verbose = False 
)

Definition at line 75 of file do2to3.py.

00075 
00076 def do_update(py2folder, py3folder, verbose=False):
00077     if not os.path.isdir(py2folder):
00078         raise ValueError("Python 2 folder %r does not exist" % py2folder)
00079     if not os.path.isdir(py3folder):
00080         os.mkdir(py3folder)
00081     #First remove any files from the 3to2 conversion which no
00082     #longer existing the Python 2 origin (only expected to happen
00083     #on a development machine).
00084     for dirpath, dirnames, filenames in os.walk(py3folder):
00085         relpath = os.path.relpath(dirpath, py3folder)
00086         for d in dirnames:
00087             new = os.path.join(py3folder, relpath, d)
00088             old = os.path.join(py2folder, relpath, d)
00089             if not os.path.isdir(old):
00090                 print("Removing %s" % new)
00091                 shutil.rmtree(new)
00092         for f in filenames:
00093             new = os.path.join(py3folder, relpath, f)
00094             old = os.path.join(py2folder, relpath, f)
00095             if not os.path.isfile(old):
00096                 print("Removing %s" % new)
00097                 os.remove(new)
00098     #Check all the Python 2 original files have been copied/converted
00099     #Note we need to do all the conversions *after* copying the files
00100     #so that 2to3 can detect local imports successfully.
00101     to_convert = []
00102     for dirpath, dirnames, filenames in os.walk(py2folder):
00103         if verbose: print("Processing %s" % dirpath)
00104         relpath = os.path.relpath(dirpath, py2folder)
00105         #This is just to give cleaner filenames
00106         if relpath[:2] == "/.":
00107             relpath = relpath[2:]
00108         elif relpath == ".":
00109             relpath = ""
00110         for d in dirnames:
00111             new = os.path.join(py3folder, relpath, d)
00112             if not os.path.isdir(new):
00113                 os.mkdir(new)
00114         for f in filenames:
00115             if f.startswith("."):
00116                 #Ignore hidden files
00117                 continue
00118             elif f.endswith("~") or f.endswith(".bak") \
00119             or f.endswith(".swp"):
00120                 #Ignore backup files
00121                 continue
00122             elif f.endswith(".pyc") or f.endswith("$py.class"):
00123                 #Ignore compiled python
00124                 continue
00125             old = os.path.join(py2folder, relpath, f)
00126             new = os.path.join(py3folder, relpath, f)
00127             #The filesystem can (in Linux) record nanoseconds, but
00128             #when copying only microsecond accuracy is used.
00129             #See http://bugs.python.org/issue10148
00130             #Compare modified times down to milliseconds only. In theory
00131             #might able to use times down to microseconds (10^-6), but
00132             #that doesn't work on this Windows machine I'm testing on.
00133             if os.path.isfile(new) \
00134             and round(os.stat(new).st_mtime*1000) >= \
00135                 round(os.stat(old).st_mtime*1000):
00136                 if verbose: print("Current: %s" % new)
00137                 continue
00138             #Python, C code, data files, etc - copy with date stamp etc
00139             shutil.copy2(old, new)
00140             assert abs(os.stat(old).st_mtime-os.stat(new).st_mtime)<0.0001, \
00141                    "Modified time not copied! %0.8f vs %0.8f, diff %f" \
00142                    % (os.stat(old).st_mtime, os.stat(new).st_mtime,
00143                       abs(os.stat(old).st_mtime-os.stat(new).st_mtime))
00144             if f.endswith(".py"):
00145                 #Also run 2to3 on it
00146                 to_convert.append(new)
00147                 if verbose: print("Will convert %s" % new)
00148             else:
00149                 if verbose: print("Updated %s" % new)
00150     if to_convert:
00151         print("Have %i python files to convert" % len(to_convert))
00152         run2to3(to_convert)
00153 
            

Here is the call graph for this function:

Here is the caller graph for this function:

def do2to3.main (   python2_source,
  python3_source,
  children = ["Bio",
  BioSQL,
  Tests,
  Scripts,
  Doc 
)

Definition at line 155 of file do2to3.py.

00155 
00156          children=["Bio", "BioSQL", "Tests", "Scripts", "Doc"]):
00157     #Note want to use different folders for Python 3.1, 3.2, etc
00158     #since the 2to3 libraries have changed so the conversion
00159     #may differ slightly.
00160     print("The 2to3 library will be called automatically now,")
00161     print("and the converted files cached under %s" % python3_source)
00162     if not os.path.isdir(python3_source):
00163         os.mkdir(python3_source)
00164     for child in children:
00165         print("Processing %s" % child)
00166         do_update(os.path.join(python2_source, child),
00167                   os.path.join(python3_source, child))
00168     print("Python 2to3 processing done.")
              

Here is the call graph for this function:

Here is the caller graph for this function:

def do2to3.run2to3 (   filenames)

Definition at line 35 of file do2to3.py.

00035 
00036 def run2to3(filenames):
00037     stderr = sys.stderr
00038     handle = StringIO()
00039     try:
00040         #Want to capture stderr (otherwise too noisy)
00041         sys.stderr = handle
00042         while filenames:
00043             filename = filenames.pop(0)
00044             print("Converting %s" % filename)
00045             #TODO - Configurable options per file?
00046             args = ["--nofix=long", "--no-diffs", "-n", "-w"]
00047             e = lib2to3.main.main("lib2to3.fixes", args + [filename])
00048             if e != 0:
00049                 sys.stderr = stderr
00050                 sys.stderr.write(handle.getvalue())
00051                 os.remove(filename) #Don't want a half edited file!
00052                 raise RuntimeError("Error %i from 2to3 on %s" \
00053                                    % (e, filename))
00054             #And again for any doctests,
00055             e = lib2to3.main.main("lib2to3.fixes", args + ["-d", filename])
00056             if e != 0:
00057                 sys.stderr = stderr
00058                 sys.stderr.write(handle.getvalue())
00059                 os.remove(filename) #Don't want a half edited file!
00060                 raise RuntimeError("Error %i from 2to3 (doctests) on %s" \
00061                                    % (e, filename))
00062     except KeyboardInterrupt:
00063         sys.stderr = stderr
00064         sys.stderr.write("Interrupted during %s\n" % filename)
00065         os.remove(filename) #Don't want a half edited file!
00066         for filename in filenames:
00067             if os.path.isfile(filename):
00068                 #Don't want uncoverted files left behind:
00069                 os.remove(filename)
00070         sys.exit(1)
00071     finally:
00072         #Restore stderr
00073         sys.stderr = stderr
00074 

Here is the caller graph for this function:


Variable Documentation

string do2to3.python2_source = "."

Definition at line 170 of file do2to3.py.

string do2to3.python3_source = "build/py%i.%i"

Definition at line 171 of file do2to3.py.