Skip to content

Commit a91d8d7

Browse files
committed
Merge pull request #701 from skrah/namespace_package
Namespace package
2 parents 7221091 + 6d229f3 commit a91d8d7

9 files changed

Lines changed: 378 additions & 181 deletions

File tree

dynd/__init__.py

Lines changed: 21 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,29 @@
1-
import os
2-
if os.name == 'nt':
3-
# Manually load dlls before loading the extension modules.
4-
# This is handled via rpaths on Unix based systems.
5-
import ctypes
6-
import os.path
7-
def load_dynd_dll(rootpath):
8-
try:
9-
ctypes.cdll.LoadLibrary(os.path.join(rootpath, 'libdyndt.dll'))
10-
ctypes.cdll.LoadLibrary(os.path.join(rootpath, 'libdynd.dll'))
11-
return True;
12-
except OSError:
13-
return False;
14-
# If libdynd.dll has been placed in the dynd-python installation, use that
15-
loaded = load_dynd_dll(os.path.dirname(__file__))
16-
# Next, try the default DLL search path
17-
loaded = loaded or load_dynd_dll('')
18-
if not loaded:
19-
# Try to load it from the Program Files directories where libdynd
20-
# installs by default. This matches the search path for libdynd used
21-
# in the CMake build for dynd-python.
22-
import sys
23-
is_64_bit = sys.maxsize > 2**32
24-
processor_arch = os.environ.get('PROCESSOR_ARCHITECTURE')
25-
err_str = ('Fallback search for libdynd.dll failed because the "{}" '
26-
'environment variable was not set. Please make sure that '
27-
'either libdynd is on the DLL search path or that it is '
28-
'in the default install directory and the runtime '
29-
'environment has the necessary system-specified '
30-
'environment variables properly set. On 64 bit Windows '
31-
'with 64 bit Python the needed variables are '
32-
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles". On 64 bit '
33-
'Windows with 32 bit Python the needed variables are '
34-
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles(x86)". On 32 '
35-
'bit Windows the needed variables are '
36-
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles".')
37-
if processor_arch is None:
38-
raise RuntimeError(err_str.format('PROCESSOR_ARCHITECTURE'))
39-
is_32_on_64_bit = (is_64_bit and not processor_arch.endswith('64'))
40-
if not is_32_on_64_bit:
41-
prog_files = os.environ.get('ProgramFiles')
42-
if prog_files is None:
43-
raise RuntimeError(err_str.format('ProgramFiles'))
44-
else:
45-
prog_files = os.environ.get('ProgramFiles(x86)')
46-
if prog_files is None:
47-
raise RuntimeError(err_str.format('ProgramFiles(x86)'))
48-
dynd_lib_dir = os.path.join(prog_files, 'libdynd', 'lib')
49-
if os.path.isdir(dynd_lib_dir):
50-
loaded = load_dynd_dll(dynd_lib_dir)
51-
if not loaded:
52-
raise ctypes.WinError(126, 'Could not load libdynd.dll '
53-
'or libdyndt.dll')
1+
# DyND is a namespace package with the components dynd.ndt and dynd.nd.
2+
try:
3+
import pkg_resources
4+
pkg_resources.declare_namespace(__name__)
5+
except ImportError:
6+
import pkgutil
7+
__path__ = pkgutil.extend_path(__path__, __name__)
548

55-
from .config import _dynd_version_string as __libdynd_version__, \
56-
_dynd_python_version_string as __version__, \
57-
_dynd_git_sha1 as __libdynd_git_sha1__, \
58-
_dynd_python_git_sha1 as __git_sha1__
9+
try: # Only the import from dynd.ndt is expected to succeed.
10+
from .common import *
11+
except ImportError:
12+
pass
5913

60-
def annotate(*args, **kwds):
61-
def wrap(func):
62-
func.__annotations__ = {}
14+
__all__ = [
15+
'__libdynd_version__', '__version__', '__libdynd_git_sha1__', ' __git_sha1__',
16+
'annotate', 'test', 'load'
17+
]
6318

64-
try:
65-
func.__annotations__['return'] = args[0]
66-
except IndexError:
67-
pass
6819

69-
if len(args[1:]) > func.__code__.co_argcount:
70-
raise TypeError('{0} takes {1} positional arguments but {2} positional annotations were given'.format(func,
71-
func.__code__.co_argcount, len(args) - 1))
20+
try: del common
21+
except: pass
7222

73-
for key, value in zip(func.__code__.co_varnames, args[1:]):
74-
func.__annotations__[key] = value
23+
try: del pkgutil
24+
except: pass
7525

76-
for key, value in kwds.items():
77-
if key not in func.__code__.co_varnames:
78-
raise TypeError("{0} got an unexpected keyword annotation '{1}'".format(func, key))
79-
if key in func.__annotations__:
80-
raise TypeError("{0} got multiple values for annotation '{1}'".format(func, key))
26+
try: del pkg_resources
27+
except: pass
8128

82-
func.__annotations__[key] = value
8329

84-
return func
85-
86-
return wrap
87-
88-
def test(verbosity=1, xunitfile=None, exit=False):
89-
"""
90-
Runs the full DyND test suite, outputing
91-
the results of the tests to sys.stdout.
92-
Parameters
93-
----------
94-
verbosity : int, optional
95-
Value 0 prints very little, 1 prints a little bit,
96-
and 2 prints the test names while testing.
97-
xunitfile : string, optional
98-
If provided, writes the test results to an xunit
99-
style xml file. This is useful for running the tests
100-
in a CI server such as Jenkins.
101-
exit : bool, optional
102-
If True, the function will call sys.exit with an
103-
error code after the tests are finished.
104-
"""
105-
import os, sys, subprocess
106-
import numpy
107-
from .ndt.test import get_tst_module as ndt_test
108-
from .nd.test import get_tst_module as nd_test
109-
import unittest
110-
111-
print('Running unit tests for the DyND Python bindings')
112-
print('Python version: %s' % sys.version)
113-
print('Python prefix: %s' % sys.prefix)
114-
print('DyND-Python module: %s' % os.path.dirname(__file__))
115-
print('DyND-Python version: %s' % __version__)
116-
print('DyND-Python git sha1: %s' % __git_sha1__)
117-
print('LibDyND version: %s' % __libdynd_version__)
118-
print('LibDyND git sha1: %s' % __libdynd_git_sha1__)
119-
print('NumPy version: %s' % numpy.__version__)
120-
sys.stdout.flush()
121-
if xunitfile is None:
122-
# Run all the tests
123-
all_suites = []
124-
for fn in os.listdir(os.path.join(os.path.dirname(__file__), 'ndt/test')):
125-
if fn.startswith('test_') and fn.endswith('.py'):
126-
tst = ndt_test(fn[:-3])
127-
all_suites.append(unittest.defaultTestLoader.loadTestsFromModule(tst))
128-
for fn in os.listdir(os.path.join(os.path.dirname(__file__), 'nd/test')):
129-
if fn.startswith('test_') and fn.endswith('.py'):
130-
tst = nd_test(fn[:-3])
131-
all_suites.append(unittest.defaultTestLoader.loadTestsFromModule(tst))
132-
runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=verbosity)
133-
result = runner.run(unittest.TestSuite(all_suites))
134-
if exit:
135-
sys.exit(not result.wasSuccessful())
136-
else:
137-
return result
138-
else:
139-
import nose
140-
import os
141-
argv = ['nosetests', '--verbosity=%d' % verbosity]
142-
# Output an xunit file if requested
143-
if xunitfile:
144-
argv.extend(['--with-xunit', '--xunit-file=%s' % xunitfile])
145-
# Add all 'tests' subdirectories to the options
146-
rootdir = os.path.dirname(__file__)
147-
for root, dirs, files in os.walk(rootdir):
148-
if 'test' in dirs:
149-
testsdir = os.path.join(root, 'test')
150-
argv.append(testsdir)
151-
print('Test dir: %s' % testsdir[len(rootdir)+1:])
152-
# Ask nose to do its thing
153-
return nose.main(argv=argv, exit=exit)
154-
155-
from .config import load

dynd/common/__init__.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from ..config import _dynd_version_string as __libdynd_version__, \
2+
_dynd_python_version_string as __version__, \
3+
_dynd_git_sha1 as __libdynd_git_sha1__, \
4+
_dynd_python_git_sha1 as __git_sha1__, \
5+
load
6+
7+
__all__ = [
8+
'__libdynd_version__', '__version__', '__libdynd_git_sha1__', '__git_sha1__',
9+
'annotate', 'test', 'load'
10+
]
11+
12+
def annotate(*args, **kwds):
13+
def wrap(func):
14+
func.__annotations__ = {}
15+
16+
try:
17+
func.__annotations__['return'] = args[0]
18+
except IndexError:
19+
pass
20+
21+
if len(args[1:]) > func.__code__.co_argcount:
22+
raise TypeError('{0} takes {1} positional arguments but {2} positional annotations were given'.format(func,
23+
func.__code__.co_argcount, len(args) - 1))
24+
25+
for key, value in zip(func.__code__.co_varnames, args[1:]):
26+
func.__annotations__[key] = value
27+
28+
for key, value in kwds.items():
29+
if key not in func.__code__.co_varnames:
30+
raise TypeError("{0} got an unexpected keyword annotation '{1}'".format(func, key))
31+
if key in func.__annotations__:
32+
raise TypeError("{0} got multiple values for annotation '{1}'".format(func, key))
33+
34+
func.__annotations__[key] = value
35+
36+
return func
37+
38+
return wrap
39+
40+
def test(verbosity=1, xunitfile=None, exit=False):
41+
"""
42+
Runs the full DyND test suite, outputing
43+
the results of the tests to sys.stdout.
44+
Parameters
45+
----------
46+
verbosity : int, optional
47+
Value 0 prints very little, 1 prints a little bit,
48+
and 2 prints the test names while testing.
49+
xunitfile : string, optional
50+
If provided, writes the test results to an xunit
51+
style xml file. This is useful for running the tests
52+
in a CI server such as Jenkins.
53+
exit : bool, optional
54+
If True, the function will call sys.exit with an
55+
error code after the tests are finished.
56+
"""
57+
import os, sys, subprocess
58+
import numpy
59+
import unittest
60+
import dynd.ndt.test as ndt_test
61+
try:
62+
import dynd.nd.test as nd_test
63+
except ImportError:
64+
nd_test = None
65+
66+
print('Running unit tests for the DyND Python bindings')
67+
print('Python version: %s' % sys.version)
68+
print('Python prefix: %s' % sys.prefix)
69+
print('DyND-Python module: %s' % os.path.dirname(os.path.dirname(__file__)))
70+
print('DyND-Python version: %s' % __version__)
71+
print('DyND-Python git sha1: %s' % __git_sha1__)
72+
print('LibDyND version: %s' % __libdynd_version__)
73+
print('LibDyND git sha1: %s' % __libdynd_git_sha1__)
74+
print('NumPy version: %s' % numpy.__version__)
75+
sys.stdout.flush()
76+
77+
if xunitfile is None:
78+
s1 = ndt_test.discover()
79+
s2 = nd_test.discover() if nd_test else []
80+
runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=verbosity)
81+
result = runner.run(unittest.TestSuite(s1 + s2))
82+
if exit:
83+
sys.exit(not result.wasSuccessful())
84+
else:
85+
return result
86+
else:
87+
import nose
88+
import os
89+
argv = ['nosetests', '--verbosity=%d' % verbosity]
90+
# Output an xunit file if requested
91+
if xunitfile:
92+
argv.extend(['--with-xunit', '--xunit-file=%s' % xunitfile])
93+
# Add all 'tests' subdirectories to the options
94+
rootdir = os.path.dirname(__file__)
95+
for root, dirs, files in os.walk(rootdir):
96+
if 'test' in dirs:
97+
testsdir = os.path.join(root, 'test')
98+
argv.append(testsdir)
99+
print('Test dir: %s' % testsdir[len(rootdir)+1:])
100+
# Ask nose to do its thing
101+
return nose.main(argv=argv, exit=exit)

dynd/nd/__init__.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,58 @@
1+
from .. import ndt # ensure that the module is loaded first
12
import sys
3+
import os
4+
5+
if os.name == 'nt':
6+
# Manually load dlls before loading the extension modules.
7+
# This is handled via rpaths on Unix based systems.
8+
import ctypes
9+
import os.path
10+
def load_dynd_dll(rootpath):
11+
try:
12+
ctypes.cdll.LoadLibrary(os.path.join(rootpath, 'libdynd.dll'))
13+
return True
14+
except OSError:
15+
return False
16+
# If libdynd.dll has been placed in the dynd-python installation, use that
17+
nddir = os.path.dirname(os.path.dirname(__file__))
18+
loaded = load_dynd_dll(nddir)
19+
# Next, try the default DLL search path
20+
loaded = loaded or load_dynd_dll('')
21+
if not loaded:
22+
# Try to load it from the Program Files directories where libdynd
23+
# installs by default. This matches the search path for libdynd used
24+
# in the CMake build for dynd-python.
25+
is_64_bit = sys.maxsize > 2**32
26+
processor_arch = os.environ.get('PROCESSOR_ARCHITECTURE')
27+
err_str = ('Fallback search for libdynd.dll failed because the "{}" '
28+
'environment variable was not set. Please make sure that '
29+
'either libdynd is on the DLL search path or that it is '
30+
'in the default install directory and the runtime '
31+
'environment has the necessary system-specified '
32+
'environment variables properly set. On 64 bit Windows '
33+
'with 64 bit Python the needed variables are '
34+
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles". On 64 bit '
35+
'Windows with 32 bit Python the needed variables are '
36+
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles(x86)". On 32 '
37+
'bit Windows the needed variables are '
38+
'"PROCESSOR_ARCHITECTURE" and "ProgramFiles".')
39+
if processor_arch is None:
40+
raise RuntimeError(err_str.format('PROCESSOR_ARCHITECTURE'))
41+
is_32_on_64_bit = (is_64_bit and not processor_arch.endswith('64'))
42+
if not is_32_on_64_bit:
43+
prog_files = os.environ.get('ProgramFiles')
44+
if prog_files is None:
45+
raise RuntimeError(err_str.format('ProgramFiles'))
46+
else:
47+
prog_files = os.environ.get('ProgramFiles(x86)')
48+
if prog_files is None:
49+
raise RuntimeError(err_str.format('ProgramFiles(x86)'))
50+
dynd_lib_dir = os.path.join(prog_files, 'libdynd', 'lib')
51+
if os.path.isdir(dynd_lib_dir):
52+
loaded = load_dynd_dll(dynd_lib_dir)
53+
if not loaded:
54+
raise ctypes.WinError(126, 'Could not load libdynd.dll')
55+
256

357
from dynd.config import *
458

@@ -20,3 +74,7 @@
2074
# return _parse(tp, obj)
2175

2276
publish_callables(sys.modules[__name__])
77+
78+
del os
79+
del sys
80+
del ndt

dynd/nd/test/__init__.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,18 @@
1-
def get_tst_module(m):
2-
l = dict(locals())
3-
exec('from . import %s as tst' % m, globals(), l)
4-
return l['tst']
1+
import os, sys
2+
import unittest
3+
import importlib
4+
5+
def discover():
6+
all_suites = []
7+
for f in os.listdir(os.path.dirname(__file__)):
8+
if f.startswith('test') and f.endswith('.py'):
9+
name = '.' + os.path.splitext(f)[0]
10+
m = importlib.import_module(name, package=__name__)
11+
suite = unittest.defaultTestLoader.loadTestsFromModule(m)
12+
all_suites.append(suite)
13+
return all_suites
14+
15+
def run(stream=sys.stderr, verbosity=2):
16+
all_suites = discover()
17+
runner = unittest.TextTestRunner(stream=stream, verbosity=verbosity)
18+
return runner.run(unittest.TestSuite(all_suites))

dynd/nd/test/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import sys
2+
from . import run
3+
result = run()
4+
sys.exit(not result.wasSuccessful())

0 commit comments

Comments
 (0)