Custom Python module importing code

Add optional moduleimport attribute to %module so that the
default module import code can be overridden. See the
"Searching for the wrapper module" documentation in Python.html.
Example:

  %module(moduleimport="import _foo") foo

$module also expands to the low-level C/C++ module name, so
the following is the same as above

  %module(moduleimport="import $module") foo

Issue https://github.com/swig/swig/issues/769
This commit is contained in:
William S Fulton 2016-12-01 18:59:33 +00:00
commit 2a42031b08
6 changed files with 135 additions and 56 deletions

View file

@ -5,6 +5,19 @@ See the RELEASENOTES file for a summary of changes in each release.
Version 3.0.11 (in progress) Version 3.0.11 (in progress)
============================ ============================
2016-12-01: wsfulton
[Python] Issue https://github.com/swig/swig/issues/769
Add optional moduleimport attribute to %module so that the
default module import code can be overridden. See the "Searching for the wrapper module"
documentation in Python.html. Example:
%module(moduleimport="import _foo") foo
$module also expands to the low-level C/C++ module name, so the following is the
same as above
%module(moduleimport="import $module") foo
2016-11-30: olly 2016-11-30: olly
[PHP] Add support for PHP7. PHP5's C extension API has changed substantially [PHP] Add support for PHP7. PHP5's C extension API has changed substantially
so you need to use -php7 to specify you want PHP7 compatible wrappers. The so you need to use -php7 to specify you want PHP7 compatible wrappers. The

View file

@ -509,7 +509,7 @@ If using static linking, you might want to rely on a different approach
<p> <p>
To use your module, simply use the Python <tt>import</tt> statement. If To use your module, simply use the Python <tt>import</tt> statement. If
all goes well, you will be able to this: all goes well, you will be able to run this:
</p> </p>
<div class="targetlang"><pre> <div class="targetlang"><pre>
@ -6037,7 +6037,7 @@ zipimporter requires python-3.5.1 or newer to work with subpackages.
<p> <p>
When SWIG creates wrappers from an interface file, say foo.i, two Python modules are When SWIG creates wrappers from an interface file, say foo.i, two Python modules are
created. There is a pure Python module module (foo.py) and C/C++ code which is created. There is a pure Python module module (foo.py) and C/C++ code which is
built and linked into a dynamically (or statically) loaded module _foo built and linked into a dynamically (or statically) loaded low-level module _foo
(see the <a href="Python.html#Python_nn3">Preliminaries section</a> for details). So, the interface (see the <a href="Python.html#Python_nn3">Preliminaries section</a> for details). So, the interface
file really defines two Python modules. How these two modules are loaded is file really defines two Python modules. How these two modules are loaded is
covered next. covered next.
@ -6064,7 +6064,43 @@ pure Python module uses to find the C/C++ module is as follows:
</ol> </ol>
<p> <p>
As an example suppose foo.i is compiled into foo.py and _foo.so. Assuming The Python code implementing the loading logic described above is quite complex to handle multiple
versions of Python, but it can be replaced with custom code.
This is not recommended unless you understand the full intricacies of importing Python modules.
The custom code can be specified by setting the <tt>moduleimport</tt> option of the <tt>%module</tt> directive with the appropriate import code. For example:
</p>
<div class="code">
<pre>
%module(moduleimport="import _foo") foo
</pre>
</div>
<p>
The special variable <tt>$module</tt> will also be expanded into the low-level C/C++ module name, <tt>_foo</tt> in the case above.
When you have more than just a line or so then you can retain the easy
readability of the <tt>%module</tt> directive by using a macro. For
example:
</p>
<div class="code">
<pre>
%define MODULEIMPORT
"
print 'Loading low-level module $module'
import $module
print 'Module has loaded'
"
%enddef
%module(moduleimport=MODULEIMPORT) foo
</pre>
</div>
<p>
Now let's consider an example using the SWIG default loading logic.
Suppose foo.i is compiled into foo.py and _foo.so. Assuming
/dir is on PYTHONPATH, then the two modules can be installed and used in the /dir is on PYTHONPATH, then the two modules can be installed and used in the
following ways: following ways:
</p> </p>

View file

@ -63,6 +63,7 @@ CPP_TEST_CASES += \
python_director \ python_director \
python_docstring \ python_docstring \
python_extranative \ python_extranative \
python_moduleimport \
python_nondynamic \ python_nondynamic \
python_overload_simple_cast \ python_overload_simple_cast \
python_pickle \ python_pickle \

View file

@ -0,0 +1,7 @@
import python_moduleimport
if python_moduleimport.simple_function(99) != 99:
raise RuntimeError("simple_function")
if python_moduleimport.extra_import_variable != "custom import of _python_moduleimport":
raise RuntimeError("custom import")

View file

@ -0,0 +1,14 @@
%define MODULEIMPORT
"
#print 'Loading low-level module $module'
import $module
#print 'Module has loaded'
extra_import_variable = 'custom import of $module'
"
%enddef
%module(moduleimport=MODULEIMPORT) python_moduleimport
%inline %{
int simple_function(int i) { return i; }
%}

View file

@ -565,6 +565,7 @@ public:
* interface file to enable director generation. * interface file to enable director generation.
*/ */
String *mod_docstring = NULL; String *mod_docstring = NULL;
String *moduleimport = NULL;
{ {
Node *mod = Getattr(n, "module"); Node *mod = Getattr(n, "module");
if (mod) { if (mod) {
@ -605,6 +606,7 @@ public:
} }
mod_docstring = Getattr(options, "docstring"); mod_docstring = Getattr(options, "docstring");
package = Getattr(options, "package"); package = Getattr(options, "package");
moduleimport = Getattr(options, "moduleimport");
} }
} }
} }
@ -779,6 +781,7 @@ public:
} }
/* If shadow classing is enabled, we're going to change the module name to "_module" */ /* If shadow classing is enabled, we're going to change the module name to "_module" */
String *default_import_code = NewString("");
if (shadow) { if (shadow) {
String *filen = NewStringf("%s%s.py", SWIG_output_directory(), Char(module)); String *filen = NewStringf("%s%s.py", SWIG_output_directory(), Char(module));
// If we don't have an interface then change the module name X to _X // If we don't have an interface then change the module name X to _X
@ -814,13 +817,13 @@ public:
mod_docstring = NULL; mod_docstring = NULL;
} }
Printv(f_shadow, "\nfrom sys import version_info as _swig_python_version_info\n", NULL); Printv(default_import_code, "from sys import version_info as _swig_python_version_info\n", NULL);
if (!builtin && fastproxy) { if (!builtin && fastproxy) {
Printv(f_shadow, "if _swig_python_version_info >= (3, 0, 0):\n", NULL); Printv(default_import_code, "if _swig_python_version_info >= (3, 0, 0):\n", NULL);
Printf(f_shadow, tab4 "new_instancemethod = lambda func, inst, cls: %s.SWIG_PyInstanceMethod_New(func)\n", module); Printf(default_import_code, tab4 "new_instancemethod = lambda func, inst, cls: %s.SWIG_PyInstanceMethod_New(func)\n", module);
Printv(f_shadow, "else:\n", NULL); Printv(default_import_code, "else:\n", NULL);
Printv(f_shadow, tab4, "from new import instancemethod as new_instancemethod\n", NULL); Printv(default_import_code, tab4, "from new import instancemethod as new_instancemethod\n", NULL);
} }
/* Import the C-extension module. This should be a relative import, /* Import the C-extension module. This should be a relative import,
@ -840,42 +843,39 @@ public:
* ImportError then fallback and try and load just the module name from * ImportError then fallback and try and load just the module name from
* the global namespace. * the global namespace.
*/ */
Printv(f_shadow, "if _swig_python_version_info >= (2, 7, 0):\n", NULL); Printv(default_import_code, "if _swig_python_version_info >= (2, 7, 0):\n", NULL);
Printv(f_shadow, tab4, "def swig_import_helper():\n", NULL); Printv(default_import_code, tab4, "def swig_import_helper():\n", NULL);
Printv(f_shadow, tab8, "import importlib\n", NULL); Printv(default_import_code, tab8, "import importlib\n", NULL);
Printv(f_shadow, tab8, "pkg = __name__.rpartition('.')[0]\n", NULL); Printv(default_import_code, tab8, "pkg = __name__.rpartition('.')[0]\n", NULL);
Printf(f_shadow, tab8 "mname = '.'.join((pkg, '%s')).lstrip('.')\n", Printf(default_import_code, tab8 "mname = '.'.join((pkg, '%s')).lstrip('.')\n", module);
module); Printv(default_import_code, tab8, "try:\n", NULL);
Printv(f_shadow, tab8, "try:\n", NULL); Printv(default_import_code, tab8, tab4, "return importlib.import_module(mname)\n", NULL);
Printv(f_shadow, tab8, tab4, "return importlib.import_module(mname)\n", Printv(default_import_code, tab8, "except ImportError:\n", NULL);
NULL); Printf(default_import_code, tab8 tab4 "return importlib.import_module('%s')\n", module);
Printv(f_shadow, tab8, "except ImportError:\n", NULL); Printf(default_import_code, tab4 "%s = swig_import_helper()\n", module);
Printf(f_shadow, tab8 tab4 "return importlib.import_module('%s')\n", Printv(default_import_code, tab4, "del swig_import_helper\n", NULL);
module); Printv(default_import_code, "elif _swig_python_version_info >= (2, 6, 0):\n", NULL);
Printf(f_shadow, tab4 "%s = swig_import_helper()\n", module); Printv(default_import_code, tab4, "def swig_import_helper():\n", NULL);
Printv(f_shadow, tab4, "del swig_import_helper\n", NULL); Printv(default_import_code, tab8, "from os.path import dirname\n", NULL);
Printv(f_shadow, "elif _swig_python_version_info >= (2, 6, 0):\n", NULL); Printv(default_import_code, tab8, "import imp\n", NULL);
Printv(f_shadow, tab4, "def swig_import_helper():\n", NULL); Printv(default_import_code, tab8, "fp = None\n", NULL);
Printv(f_shadow, tab8, "from os.path import dirname\n", NULL); Printv(default_import_code, tab8, "try:\n", NULL);
Printv(f_shadow, tab8, "import imp\n", NULL); Printf(default_import_code, tab4 tab8 "fp, pathname, description = imp.find_module('%s', [dirname(__file__)])\n", module);
Printv(f_shadow, tab8, "fp = None\n", NULL); Printf(default_import_code, tab8 "except ImportError:\n");
Printv(f_shadow, tab8, "try:\n", NULL);
Printf(f_shadow, tab4 tab8 "fp, pathname, description = imp.find_module('%s', [dirname(__file__)])\n", module);
Printf(f_shadow, tab8 "except ImportError:\n");
/* At here, the module may already loaded, so simply import it. */ /* At here, the module may already loaded, so simply import it. */
Printf(f_shadow, tab4 tab8 "import %s\n", module); Printf(default_import_code, tab4 tab8 "import %s\n", module);
Printf(f_shadow, tab4 tab8 "return %s\n", module); Printf(default_import_code, tab4 tab8 "return %s\n", module);
Printv(f_shadow, tab8 "try:\n", NULL); Printv(default_import_code, tab8 "try:\n", NULL);
/* imp.load_module() handles fp being None. */ /* imp.load_module() handles fp being None. */
Printf(f_shadow, tab4 tab8 "_mod = imp.load_module('%s', fp, pathname, description)\n", module); Printf(default_import_code, tab4 tab8 "_mod = imp.load_module('%s', fp, pathname, description)\n", module);
Printv(f_shadow, tab8, "finally:\n", NULL); Printv(default_import_code, tab8, "finally:\n", NULL);
Printv(f_shadow, tab4 tab8 "if fp is not None:\n", NULL); Printv(default_import_code, tab4 tab8 "if fp is not None:\n", NULL);
Printv(f_shadow, tab8 tab8, "fp.close()\n", NULL); Printv(default_import_code, tab8 tab8, "fp.close()\n", NULL);
Printv(f_shadow, tab8, "return _mod\n", NULL); Printv(default_import_code, tab8, "return _mod\n", NULL);
Printf(f_shadow, tab4 "%s = swig_import_helper()\n", module); Printf(default_import_code, tab4 "%s = swig_import_helper()\n", module);
Printv(f_shadow, tab4, "del swig_import_helper\n", NULL); Printv(default_import_code, tab4, "del swig_import_helper\n", NULL);
Printv(f_shadow, "else:\n", NULL); Printv(default_import_code, "else:\n", NULL);
Printf(f_shadow, tab4 "import %s\n", module); Printf(default_import_code, tab4 "import %s\n", module);
if (builtin) { if (builtin) {
/* /*
@ -890,22 +890,22 @@ public:
* globals()[attr] = getattr(_foo, attr) * globals()[attr] = getattr(_foo, attr)
* *
*/ */
Printf(f_shadow, "# pull in all the attributes from %s\n", module); Printf(default_import_code, "# pull in all the attributes from %s\n", module);
Printv(f_shadow, "if __name__.rpartition('.')[0] != '':\n", NULL); Printv(default_import_code, "if __name__.rpartition('.')[0] != '':\n", NULL);
Printv(f_shadow, tab4, "if _swig_python_version_info >= (2, 7, 0):\n", NULL); Printv(default_import_code, tab4, "if _swig_python_version_info >= (2, 7, 0):\n", NULL);
Printv(f_shadow, tab8, "try:\n", NULL); Printv(default_import_code, tab8, "try:\n", NULL);
Printf(f_shadow, tab8 tab4 "from .%s import *\n", module); Printf(default_import_code, tab8 tab4 "from .%s import *\n", module);
Printv(f_shadow, tab8 "except ImportError:\n", NULL); Printv(default_import_code, tab8 "except ImportError:\n", NULL);
Printf(f_shadow, tab8 tab4 "from %s import *\n", module); Printf(default_import_code, tab8 tab4 "from %s import *\n", module);
Printv(f_shadow, tab4, "else:\n", NULL); Printv(default_import_code, tab4, "else:\n", NULL);
Printf(f_shadow, tab8 "from %s import *\n", module); Printf(default_import_code, tab8 "from %s import *\n", module);
Printv(f_shadow, "else:\n", NULL); Printv(default_import_code, "else:\n", NULL);
Printf(f_shadow, tab4 "from %s import *\n", module); Printf(default_import_code, tab4 "from %s import *\n", module);
} }
/* Delete the _swig_python_version_info symbol since we don't use it elsewhere in the /* Delete the _swig_python_version_info symbol since we don't use it elsewhere in the
* module. */ * module. */
Printv(f_shadow, "del _swig_python_version_info\n", NULL); Printv(default_import_code, "del _swig_python_version_info\n", NULL);
if (modern || !classic) { 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); 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);
@ -1053,8 +1053,13 @@ public:
Printv(f_shadow, "# This file is compatible with both classic and new-style classes.\n", NIL); Printv(f_shadow, "# This file is compatible with both classic and new-style classes.\n", NIL);
} }
Printv(f_shadow_py, "\n", f_shadow_begin, "\n", NIL); Printv(f_shadow_py, "\n", f_shadow_begin, "\n", NIL);
Printv(f_shadow_py, "\n", f_shadow_builtin_imports, "\n", NIL); if (moduleimport) {
Printv(f_shadow_py, f_shadow, "\n", NIL); Replaceall(moduleimport, "$module", module);
Printv(f_shadow_py, moduleimport, NIL);
} else {
Printv(f_shadow_py, default_import_code, NIL);
}
Printv(f_shadow_py, "\n", f_shadow, "\n", NIL);
Printv(f_shadow_py, f_shadow_stubs, "\n", NIL); Printv(f_shadow_py, f_shadow_stubs, "\n", NIL);
Delete(f_shadow_py); Delete(f_shadow_py);
} }
@ -1077,6 +1082,9 @@ public:
Printf(f_begin, "static PyTypeObject *builtin_bases[%d];\n\n", max_bases + 2); Printf(f_begin, "static PyTypeObject *builtin_bases[%d];\n\n", max_bases + 2);
Wrapper_pretty_print(f_init, f_begin); Wrapper_pretty_print(f_init, f_begin);
Delete(default_import_code);
Delete(f_shadow_begin);
Delete(f_shadow);
Delete(f_header); Delete(f_header);
Delete(f_wrappers); Delete(f_wrappers);
Delete(f_builtins); Delete(f_builtins);