Merge branch 'mromberg-implpkg'
* mromberg-implpkg: Minor edits to Python implicit namespace package docs use %inline for test use relative import for -builtin and python2 Python3 removes support for relative imports Document implicit namespace packages for python disable namespace package build Attempt to calm the testing gods... use whatever name winders uses for .so files. Examples (and tests) for python namespace packages disable namespace package build spelling Attempt to calm the testing gods... use whatever name winders uses for .so files. Don't run example for old pythons Examples (and tests) for python namespace packages use importlib to load C extension modules for python 2.7 and newer
This commit is contained in:
commit
d01efd82e1
12 changed files with 230 additions and 3 deletions
|
|
@ -1594,6 +1594,7 @@
|
|||
<li><a href="Python.html#Python_absrelimports">Absolute and relative imports</a>
|
||||
<li><a href="Python.html#Python_absimport">Enforcing absolute import semantics</a>
|
||||
<li><a href="Python.html#Python_importfrominit">Importing from __init__.py</a>
|
||||
<li><a href="Python.html#Python_implicit_namespace_packages">Implicit Namespace Packages</a>
|
||||
</ul>
|
||||
<li><a href="Python.html#Python_python3support">Python 3 Support</a>
|
||||
<ul>
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@
|
|||
<li><a href="#Python_absrelimports">Absolute and relative imports</a>
|
||||
<li><a href="#Python_absimport">Enforcing absolute import semantics</a>
|
||||
<li><a href="#Python_importfrominit">Importing from __init__.py</a>
|
||||
<li><a href="#Python_implicit_namespace_packages">Implicit Namespace Packages</a>
|
||||
</ul>
|
||||
<li><a href="#Python_python3support">Python 3 Support</a>
|
||||
<ul>
|
||||
|
|
@ -5865,6 +5866,82 @@ class Bar(pkg3.foo.Foo): pass
|
|||
effect (note, that the Python 2 case also needs the <tt>-relativeimport</tt>
|
||||
workaround).</p>
|
||||
|
||||
<H3><a name="Python_implicit_namespace_packages">36.11.5 Implicit Namespace Packages</a></H3>
|
||||
|
||||
|
||||
<p> Python 3.3 introduced
|
||||
<a href="https://www.python.org/dev/peps/pep-0420/">PEP 0420</a> which
|
||||
implements implicit namespace packages. In a nutshell, implicit namespace
|
||||
packages remove the requirement of an __init__.py file and allow packages
|
||||
to be split across multiple PATH elements. For example:
|
||||
</p>
|
||||
|
||||
<div class="diagram">
|
||||
<pre>
|
||||
/fragment1/pkg1/mod1.py
|
||||
/fragment2/pkg1/mod2.py
|
||||
/fragment3/pkg1/mod3.py
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>If PYTHONPATH is set to "/fragment1:/fragment2:/fragment3", then mod1, mod2
|
||||
and mod3 will be part of pkg1. This allows for splitting of packages into
|
||||
separate pieces. This can be useful for SWIG generated wrappers in the
|
||||
following way.
|
||||
</p>
|
||||
|
||||
<p> Suppose you create a SWIG wrapper for a module called robin. The SWIG
|
||||
generated code consists of two files robin.py and _robin.so. You wish to
|
||||
make these modules part of a subpackage (brave.sir). With implicit namespace
|
||||
packages you can place these files in the following configurations:
|
||||
</p>
|
||||
|
||||
<p>Using PYTHONPATH="/some/path"</p>
|
||||
<div class="diagram">
|
||||
<pre>
|
||||
/some/path/brave/sir/robin.py
|
||||
/some/path/brave/sir/_robin.so
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>Using PYTHONPATH="/some/path:/some/other/path"
|
||||
|
||||
<div class="diagram">
|
||||
<pre>
|
||||
/some/path/brave/sir/robin.py
|
||||
/some/other/path/brave/sir/_robin.so
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p> Finally suppose that your pure python code is stored in a .zip file or
|
||||
some other way (database, web service connection, etc). Python can load the
|
||||
robin.py module using a custom importer. But the _robin.so module will need
|
||||
to be located on a file system. Implicit namespace packages make this
|
||||
possible. For example, using PYTHONPATH="/some/path/foo.zip:/some/other/path"
|
||||
|
||||
<p> Contents of foo.zip</p>
|
||||
<div class="diagram">
|
||||
<pre>
|
||||
brave/
|
||||
brave/sir/
|
||||
brave/sir/robin.py
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p> File system contents</p>
|
||||
<div class="diagram">
|
||||
<pre>
|
||||
/some/other/path/brave/sir/_robin.so
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>Support for implicit namespace packages was added to python-3.3. The
|
||||
zipimporter requires python-3.5.1 or newer to work with subpackages.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<b>Compatibility Note:</b> Support for implicit namespace packages was added in SWIG-3.0.9.
|
||||
</p>
|
||||
|
||||
<H2><a name="Python_python3support">36.12 Python 3 Support</a></H2>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
These are actually regression tests for SF bug #1297 (GH issue #7).
|
||||
See individual READMEs in subdirectories.
|
||||
The namespace_pkg is an example of python3's namespace packages.
|
||||
|
||||
See individual READMEs in subdirectories.
|
||||
17
Examples/python/import_packages/namespace_pkg/Makefile
Normal file
17
Examples/python/import_packages/namespace_pkg/Makefile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
TOP = ../../..
|
||||
SWIGEXE = $(TOP)/../swig
|
||||
SWIG_LIB_DIR = $(TOP)/../$(TOP_BUILDDIR_TO_TOP_SRCDIR)Lib
|
||||
TARGET = robin
|
||||
INTERFACE = robin.i
|
||||
|
||||
check: build
|
||||
$(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' python_run
|
||||
|
||||
build:
|
||||
$(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' SRCS='$(SRCS)' \
|
||||
SWIG_LIB_DIR='$(SWIG_LIB_DIR)' SWIGEXE='$(SWIGEXE)' \
|
||||
TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' python
|
||||
|
||||
clean:
|
||||
$(MAKE) -f $(TOP)/Makefile SRCDIR='$(SRCDIR)' TARGET='$(TARGET)' python_clean
|
||||
rm -rf path1 path2 path3 path4.zip
|
||||
25
Examples/python/import_packages/namespace_pkg/README
Normal file
25
Examples/python/import_packages/namespace_pkg/README
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
This is an example (and test) of using swig generated modules in the context
|
||||
of python3's namespace packages:
|
||||
|
||||
https://www.python.org/dev/peps/pep-0420/
|
||||
|
||||
Consequently, this example requires python (3.3 or newer) to build and run.
|
||||
|
||||
This example creates a simple swig module named robin. The robin.py module
|
||||
has a companion C module named _robin.so. The robin module is tested in four
|
||||
ways:
|
||||
|
||||
1) As a non-package module (tested by nonpkg.py)
|
||||
|
||||
2) With robin.py and _robin.so in the brave package under the path1
|
||||
subdirectory. (tested by normal.py)
|
||||
|
||||
3) With robin.py in path2/brave and _robin.so in path3/brave
|
||||
(tested by split.py)
|
||||
|
||||
4) With robin.py contained in a zip file (path4.zip) as brave/robin.py and
|
||||
_robin.so found on the filesystem under path3/brave (tested by zipsplit.py)
|
||||
|
||||
Note: Using namespace packages with subpackages (such as brave.sir.robin) where
|
||||
robin.py is located in a zipfile requires python-3.5.1 or newer as
|
||||
python's zipimporter only worked with packages of depth 1 until then.
|
||||
5
Examples/python/import_packages/namespace_pkg/nonpkg.py
Normal file
5
Examples/python/import_packages/namespace_pkg/nonpkg.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# import robin as a module in the global namespace
|
||||
|
||||
import robin
|
||||
|
||||
assert(robin.run() == "AWAY!")
|
||||
7
Examples/python/import_packages/namespace_pkg/normal.py
Normal file
7
Examples/python/import_packages/namespace_pkg/normal.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import sys
|
||||
# Package brave found under one path
|
||||
sys.path.insert(0, 'path1')
|
||||
|
||||
from brave import robin
|
||||
|
||||
assert(robin.run() == "AWAY!")
|
||||
7
Examples/python/import_packages/namespace_pkg/robin.i
Normal file
7
Examples/python/import_packages/namespace_pkg/robin.i
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
%module robin
|
||||
|
||||
%inline %{
|
||||
const char *run(void) {
|
||||
return "AWAY!";
|
||||
}
|
||||
%}
|
||||
51
Examples/python/import_packages/namespace_pkg/runme.py
Normal file
51
Examples/python/import_packages/namespace_pkg/runme.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# These examples rely on namespace packages. Don't
|
||||
# run them for old python interpreters.
|
||||
import sys
|
||||
if sys.version_info < (3, 3, 0):
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
|
||||
def copyMods():
|
||||
dirs = ['path1', 'path2', 'path3']
|
||||
|
||||
# Clean out any old package paths
|
||||
for d in dirs:
|
||||
if os.path.isdir(d):
|
||||
shutil.rmtree(d)
|
||||
|
||||
for d in dirs:
|
||||
os.mkdir(d)
|
||||
os.mkdir(os.path.join(d, 'brave'))
|
||||
|
||||
shutil.copy('robin.py', os.path.join('path1', 'brave'))
|
||||
os.system('cp _robin.* ' + os.path.join('path1', 'brave'))
|
||||
|
||||
shutil.copy('robin.py', os.path.join('path2', 'brave'))
|
||||
os.system('cp _robin.* ' + os.path.join('path3', 'brave'))
|
||||
|
||||
mkzip()
|
||||
|
||||
def mkzip():
|
||||
zf = zipfile.ZipFile("path4.zip", "w")
|
||||
zf.writestr("brave/", b'')
|
||||
zf.write('robin.py', 'brave/robin.py')
|
||||
zf.close()
|
||||
|
||||
|
||||
def main():
|
||||
copyMods()
|
||||
|
||||
# Run each test with a separate interpreter
|
||||
os.system(sys.executable + " nonpkg.py")
|
||||
os.system(sys.executable + " normal.py")
|
||||
os.system(sys.executable + " split.py")
|
||||
os.system(sys.executable + " zipsplit.py")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
9
Examples/python/import_packages/namespace_pkg/split.py
Normal file
9
Examples/python/import_packages/namespace_pkg/split.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import sys
|
||||
# Package brave split into two paths.
|
||||
# path2/brave/robin.py and path3/brave/_robin.so
|
||||
sys.path.insert(0, 'path2')
|
||||
sys.path.insert(0, 'path3')
|
||||
|
||||
from brave import robin
|
||||
|
||||
assert(robin.run() == "AWAY!")
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import sys
|
||||
# Package brave split into two paths.
|
||||
# brave/robin.py (in path4.zip) and path3/brave/_robin.so
|
||||
sys.path.insert(0, 'path4.zip')
|
||||
sys.path.insert(0, 'path3')
|
||||
|
||||
from brave import robin
|
||||
|
||||
assert(robin.run() == "AWAY!")
|
||||
|
|
@ -828,7 +828,16 @@ public:
|
|||
* isn't available in python 2.4 or earlier, so we have to write some
|
||||
* code conditional on the python version.
|
||||
*/
|
||||
Printv(f_shadow, "if version_info >= (2, 6, 0):\n", NULL);
|
||||
Printv(f_shadow, "if version_info >= (2, 7, 0):\n", NULL);
|
||||
Printv(f_shadow, tab4, "def swig_import_helper():\n", NULL);
|
||||
Printv(f_shadow, tab8, "import importlib\n", NULL);
|
||||
Printv(f_shadow, tab8, "pkg = __name__.rpartition('.')[0]\n", NULL);
|
||||
Printf(f_shadow, tab8 "mname = '.'.join((pkg, '%s')).lstrip('.')\n",
|
||||
module);
|
||||
Printv(f_shadow, tab8, "return importlib.import_module(mname)\n", NULL);
|
||||
Printf(f_shadow, tab4 "%s = swig_import_helper()\n", module);
|
||||
Printv(f_shadow, tab4, "del swig_import_helper\n", NULL);
|
||||
Printv(f_shadow, "elif version_info >= (2, 6, 0):\n", NULL);
|
||||
Printv(f_shadow, tab4, "def swig_import_helper():\n", NULL);
|
||||
Printv(f_shadow, tab8, "from os.path import dirname\n", NULL);
|
||||
Printv(f_shadow, tab8, "import imp\n", NULL);
|
||||
|
|
@ -855,7 +864,15 @@ public:
|
|||
Printv(f_shadow, "del version_info\n", NULL);
|
||||
|
||||
if (builtin) {
|
||||
Printf(f_shadow, "from %s import *\n", module);
|
||||
/*
|
||||
* Python3 removes relative imports. So 'from _foo import *'
|
||||
* will only work for non-package modules.
|
||||
*/
|
||||
Printv(f_shadow, "if __name__.rpartition('.')[0] != '':\n", NULL);
|
||||
Printf(f_shadow, tab4 "from %s%s import *\n", (py3 ? "." : ""),
|
||||
module);
|
||||
Printv(f_shadow, "else:\n", NULL);
|
||||
Printf(f_shadow, tab4 "from %s import *\n", module);
|
||||
}
|
||||
if (modern || !classic) {
|
||||
Printv(f_shadow, "try:\n", tab4, "_swig_property = property\n", "except NameError:\n", tab4, "pass # Python < 2.2 doesn't have 'property'.\n\n", NULL);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue