Skip to content

Commit f0af999

Browse files
committed
first steps to supporting python-3
This commit will build, and quantities can be imported and used in python-3. However, the unit tests are not being found, so pq.test() runs 0 tests. The test suite can currently be run using the following steps: python setup.py build cd build/py3k/quantities nosetests3 tests/ which yields 6 errors and 16 failures
1 parent e1b487d commit f0af999

File tree

12 files changed

+487
-133
lines changed

12 files changed

+487
-133
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
include distribute_setup.py
22
include CHANGES.txt
3+
include py3tool.py

py3tool.py

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#!/usr/bin/env python3
2+
# -*- python -*-
3+
"""
4+
%prog SUBMODULE...
5+
6+
Hack to pipe submodules of Numpy through 2to3 and build them in-place
7+
one-by-one.
8+
9+
Example usage:
10+
11+
python3 tools/py3tool.py testing distutils core
12+
13+
This will copy files to _py3k/numpy, add a dummy __init__.py and
14+
version.py on the top level, and copy and 2to3 the files of the three
15+
submodules.
16+
17+
When running py3tool again, only changed files are re-processed, which
18+
makes the test-bugfix cycle faster.
19+
20+
"""
21+
from optparse import OptionParser
22+
import shutil
23+
import os
24+
import sys
25+
import re
26+
import subprocess
27+
import fnmatch
28+
29+
BASE = os.path.normpath(os.path.join(os.path.dirname(__file__), '..'))
30+
TEMP = os.path.normpath(os.path.join(BASE, '_py3k'))
31+
32+
SCRIPT_2TO3 = os.path.join(BASE, 'tools', '2to3.py')
33+
34+
EXTRA_2TO3_FLAGS = {
35+
'*/*.py': '-x import',
36+
# 'numpy/core/code_generators/generate_umath.py': '-x import',
37+
# 'numpy/core/code_generators/generate_numpy_api.py': '-x import',
38+
# 'numpy/core/code_generators/generate_ufunc_api.py': '-x import',
39+
# 'numpy/core/defchararray.py': '-x unicode',
40+
# 'numpy/compat/py3k.py': '-x unicode',
41+
# 'numpy/ma/timer_comparison.py': 'skip',
42+
# 'numpy/distutils/system_info.py': '-x reduce',
43+
# 'numpy/f2py/auxfuncs.py': '-x reduce',
44+
# 'numpy/lib/arrayterator.py': '-x reduce',
45+
# 'numpy/lib/tests/test_arrayterator.py': '-x reduce',
46+
# 'numpy/ma/core.py': '-x reduce',
47+
# 'numpy/ma/tests/test_core.py': '-x reduce',
48+
# 'numpy/ma/tests/test_old_ma.py': '-x reduce',
49+
# 'numpy/ma/timer_comparison.py': '-x reduce',
50+
# 'numpy/oldnumeric/ma.py': '-x reduce',
51+
}
52+
53+
def main():
54+
p = OptionParser(usage=__doc__.strip())
55+
p.add_option("--clean", "-c", action="store_true",
56+
help="clean source directory")
57+
options, args = p.parse_args()
58+
59+
if not args:
60+
p.error('no submodules given')
61+
else:
62+
dirs = ['quantities/%s' % x for x in map(os.path.basename, args)]
63+
64+
# Prepare
65+
if not os.path.isdir(TEMP):
66+
os.makedirs(TEMP)
67+
68+
# Set up dummy files (for building only submodules)
69+
dummy_files = {
70+
# '__init__.py': 'from quantities.version __version__',
71+
# 'version.py': 'version = "1.4.0.dev"'
72+
}
73+
74+
for fn, content in dummy_files.items():
75+
fn = os.path.join(TEMP, 'quantities', fn)
76+
if not os.path.isfile(fn):
77+
try:
78+
os.makedirs(os.path.dirname(fn))
79+
except OSError:
80+
pass
81+
f = open(fn, 'wb+')
82+
f.write(content.encode('ascii'))
83+
f.close()
84+
85+
# Environment
86+
pp = [os.path.abspath(TEMP)]
87+
def getenv():
88+
env = dict(os.environ)
89+
env.update({'PYTHONPATH': ':'.join(pp)})
90+
return env
91+
92+
# Copy
93+
for d in dirs:
94+
src = os.path.join(BASE, d)
95+
dst = os.path.join(TEMP, d)
96+
97+
# Run 2to3
98+
sync_2to3(dst=dst,
99+
src=src,
100+
patchfile=os.path.join(TEMP, os.path.basename(d) + '.patch'),
101+
clean=options.clean)
102+
103+
# Run setup.py, falling back to Pdb post-mortem on exceptions
104+
setup_py = os.path.join(dst, 'setup.py')
105+
if os.path.isfile(setup_py):
106+
code = """\
107+
import pdb, sys, traceback
108+
p = pdb.Pdb()
109+
try:
110+
import __main__
111+
__main__.__dict__.update({
112+
"__name__": "__main__", "__file__": "setup.py",
113+
"__builtins__": __builtins__})
114+
fp = open("setup.py", "rb")
115+
try:
116+
exec(compile(fp.read(), "setup.py", 'exec'))
117+
finally:
118+
fp.close()
119+
except SystemExit:
120+
raise
121+
except:
122+
traceback.print_exc()
123+
t = sys.exc_info()[2]
124+
p.interaction(None, t)
125+
"""
126+
ret = subprocess.call([sys.executable, '-c', code,
127+
'build_ext', '-i'],
128+
cwd=dst,
129+
env=getenv())
130+
if ret != 0:
131+
raise RuntimeError("Build failed.")
132+
133+
# Run nosetests
134+
subprocess.call(['nosetests3', '-v', d], cwd=TEMP)
135+
136+
def custom_mangling(filename):
137+
import_mangling = [
138+
# os.path.join('core', '__init__.py'),
139+
# os.path.join('core', 'numeric.py'),
140+
# os.path.join('core', '_internal.py'),
141+
# os.path.join('core', 'arrayprint.py'),
142+
# os.path.join('core', 'fromnumeric.py'),
143+
# os.path.join('numpy', '__init__.py'),
144+
# os.path.join('lib', 'io.py'),
145+
# os.path.join('lib', 'function_base.py'),
146+
# os.path.join('fft', 'fftpack.py'),
147+
# os.path.join('random', '__init__.py'),
148+
]
149+
150+
if any(filename.endswith(x) for x in import_mangling):
151+
f = open(filename, 'r')
152+
text = f.read()
153+
f.close()
154+
for mod in ['multiarray', 'scalarmath', 'umath', '_sort',
155+
'_compiled_base', 'core', 'lib', 'testing', 'fft',
156+
'polynomial', 'random', 'ma', 'linalg', 'compat',
157+
'mtrand']:
158+
text = re.sub(r'^(\s*)import %s' % mod,
159+
r'\1from . import %s' % mod,
160+
text, flags=re.M)
161+
text = re.sub(r'^(\s*)from %s import' % mod,
162+
r'\1from .%s import' % mod,
163+
text, flags=re.M)
164+
text = text.replace('from matrixlib', 'from .matrixlib')
165+
f = open(filename, 'w')
166+
f.write(text)
167+
f.close()
168+
169+
if filename.endswith(os.path.join('lib', 'io.py')):
170+
f = open(filename, 'r')
171+
text = f.read()
172+
f.close()
173+
text = text.replace('from . import io', 'import io')
174+
f = open(filename, 'w')
175+
f.write(text)
176+
f.close()
177+
178+
def walk_sync(dir1, dir2, _seen=None):
179+
if _seen is None:
180+
seen = {}
181+
else:
182+
seen = _seen
183+
184+
if not dir1.endswith(os.path.sep):
185+
dir1 = dir1 + os.path.sep
186+
187+
# Walk through stuff (which we haven't yet gone through) in dir1
188+
for root, dirs, files in os.walk(dir1):
189+
sub = root[len(dir1):]
190+
if sub in seen:
191+
dirs = [x for x in dirs if x not in seen[sub][0]]
192+
files = [x for x in files if x not in seen[sub][1]]
193+
seen[sub][0].extend(dirs)
194+
seen[sub][1].extend(files)
195+
else:
196+
seen[sub] = (dirs, files)
197+
if not dirs and not files:
198+
continue
199+
yield os.path.join(dir1, sub), os.path.join(dir2, sub), dirs, files
200+
201+
if _seen is None:
202+
# Walk through stuff (which we haven't yet gone through) in dir2
203+
for root2, root1, dirs, files in walk_sync(dir2, dir1, _seen=seen):
204+
yield root1, root2, dirs, files
205+
206+
def sync_2to3(src, dst, patchfile=None, clean=False):
207+
import lib2to3.main
208+
from io import StringIO
209+
210+
to_convert = []
211+
212+
for src_dir, dst_dir, dirs, files in walk_sync(src, dst):
213+
for fn in dirs + files:
214+
src_fn = os.path.join(src_dir, fn)
215+
dst_fn = os.path.join(dst_dir, fn)
216+
217+
# skip temporary etc. files
218+
if fn.startswith('.#') or fn.endswith('~'):
219+
continue
220+
221+
# remove non-existing
222+
if os.path.exists(dst_fn) and not os.path.exists(src_fn):
223+
if clean:
224+
if os.path.isdir(dst_fn):
225+
shutil.rmtree(dst_fn)
226+
else:
227+
os.unlink(dst_fn)
228+
continue
229+
230+
# make directories
231+
if os.path.isdir(src_fn):
232+
if not os.path.isdir(dst_fn):
233+
os.makedirs(dst_fn)
234+
continue
235+
236+
dst_dir = os.path.dirname(dst_fn)
237+
if os.path.isfile(dst_fn) and not os.path.isdir(dst_dir):
238+
os.makedirs(dst_dir)
239+
240+
# don't replace up-to-date files
241+
try:
242+
if os.path.isfile(dst_fn) and \
243+
os.stat(dst_fn).st_mtime >= os.stat(src_fn).st_mtime:
244+
continue
245+
except OSError:
246+
pass
247+
248+
# copy file
249+
shutil.copyfile(src_fn, dst_fn)
250+
251+
# add .py files to 2to3 list
252+
if dst_fn.endswith('.py'):
253+
to_convert.append((src_fn, dst_fn))
254+
255+
# run 2to3
256+
flag_sets = {}
257+
for fn, dst_fn in to_convert:
258+
flag = ''
259+
for pat, opt in EXTRA_2TO3_FLAGS.items():
260+
if fnmatch.fnmatch(fn, pat):
261+
flag = opt
262+
break
263+
flag_sets.setdefault(flag, []).append(dst_fn)
264+
265+
if patchfile:
266+
p = open(patchfile, 'wb+')
267+
else:
268+
p = open(os.devnull, 'wb')
269+
270+
for flags, filenames in flag_sets.items():
271+
if flags == 'skip':
272+
continue
273+
274+
_old_stdout = sys.stdout
275+
try:
276+
sys.stdout = StringIO()
277+
lib2to3.main.main("lib2to3.fixes", ['-w'] + flags.split()+filenames)
278+
finally:
279+
sys.stdout = _old_stdout
280+
281+
for fn, dst_fn in to_convert:
282+
# perform custom mangling
283+
custom_mangling(dst_fn)
284+
285+
p.close()
286+
287+
if __name__ == "__main__":
288+
main()

quantities/__init__.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -244,24 +244,24 @@
244244
245245
"""
246246

247-
#from __future__ import absolute_import
247+
from __future__ import absolute_import
248248

249-
from version import __version__
249+
from .version import __version__
250250

251-
import quantity
252-
from quantity import Quantity
251+
from . import quantity
252+
from .quantity import Quantity
253253

254-
import uncertainquantity
255-
from uncertainquantity import UncertainQuantity
254+
from . import uncertainquantity
255+
from .uncertainquantity import UncertainQuantity
256256

257-
import unitquantity
258-
from unitquantity import *
257+
from . import unitquantity
258+
from .unitquantity import *
259259

260-
from units import *
260+
from .units import *
261261

262-
import constants
262+
from . import constants
263263

264-
from umath import *
264+
from .umath import *
265265

266266
from numpy.testing import Tester
267267
test = Tester().test

quantities/constants/__init__.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
#from __future__ import absolute_import
1+
from __future__ import absolute_import
22

3-
from alpha import *
4-
from astronomy import *
5-
from atomicunits import *
6-
from deuteron import *
7-
from electromagnetism import *
8-
from electron import *
9-
from helion import *
10-
from mathematical import *
11-
from muon import *
12-
from naturalunits import *
13-
from neutron import *
14-
from proton import *
15-
from quantum import *
16-
from relationships import *
17-
from statisticalmechanics import *
18-
from tau import *
19-
from triton import *
20-
from weak import *
21-
from xray import *
3+
from .alpha import *
4+
from .astronomy import *
5+
from .atomicunits import *
6+
from .deuteron import *
7+
from .electromagnetism import *
8+
from .electron import *
9+
from .helion import *
10+
from .mathematical import *
11+
from .muon import *
12+
from .naturalunits import *
13+
from .neutron import *
14+
from .proton import *
15+
from .quantum import *
16+
from .relationships import *
17+
from .statisticalmechanics import *
18+
from .tau import *
19+
from .triton import *
20+
from .weak import *
21+
from .xray import *

0 commit comments

Comments
 (0)