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:
William S Fulton 2016-05-24 22:48:37 +01:00
commit d01efd82e1
12 changed files with 230 additions and 3 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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.

View 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

View 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.

View file

@ -0,0 +1,5 @@
# import robin as a module in the global namespace
import robin
assert(robin.run() == "AWAY!")

View 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!")

View file

@ -0,0 +1,7 @@
%module robin
%inline %{
const char *run(void) {
return "AWAY!";
}
%}

View 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()

View 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!")

View file

@ -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!")

View file

@ -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);