Fixed SF bug #1297 (Python imports)

This changeset resolves several issues related to python imports.
For example, it's possible now to import modules having same module
names, but belonging to different packages.

From the user's viewpoint, this patch gives a little bit more control on
import directives generated by swig. The user may choose to use relative
or absolute imports (docs are provided in separate PR).

Some details:
  - we (still) generate import directives in form 'import a.b.c' which
    corresponds to absolute imports in python3 and (the only available)
    ambiguous one in python2.
  - added -relativeimport option to use explicit relative import syntax
    (python3),

Tests are under Examples/python, these are in fact regression tests but
with the current swig testing framework it seems to be impossible to put
appropriate tests under test-suite.

Closes #7
This commit is contained in:
Paweł Tomulik 2013-05-28 05:33:56 +02:00 committed by William S Fulton
commit 5562deec62
146 changed files with 1676 additions and 30 deletions

View file

@ -0,0 +1,25 @@
TOP = ../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
PY3 =
ifeq (,$(PY3))
PKG1DIR = "py2"
else
PKG1DIR = "py3"
endif
check: build
$(MAKE) -f $(TOP)/Makefile python_run
build:
cd $(PKG1DIR) && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT) -relativeimport' LIBS='$(LIBS)' build
static:
cd $(PKG1DIR) && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT) -relativeimport' LIBS='$(LIBS)' static
clean:
$(MAKE) -f $(TOP)/Makefile python_clean
cd py2 && $(MAKE) clean
cd py3 && $(MAKE) clean

View file

@ -0,0 +1,81 @@
This example tests the %import directive and python import from __init__.py.
This case is not correctly handled by swig 2.
The issue was reported as Source Forge bug #1297 and later as GitHub issue #7.
Use 'python runme.py' to run a test.
Overview:
---------
The example defines 2 different extension modules--each wrapping a separate C++
class.
pyX/pkg2/pkg3/foo.i - Pkg3_Foo class
pyX/pkg2/bar.i - Pkg2_Bar class derived from Pkg3_Foo
and the package pyX.pkg2 has:
pyX/pkg2/__init__.py - which imports something from "bar" module
For example with python 2.x the py2/pkg2/__init__.py imports Pkg2_Bar class as
follows
from bar import Pkg2_Bar # [1]
Such cases doesn't work when fully qualified python module names are used by
swig to generate python import directives (SF bug #1297). The generated file
"py2/pkg2/bar.py" has following lines:
import py2.pkg2.pkg3.foo # [2]
class Pkg2_Bar(py2.pkg2.pkg3.foo.Pkg3_Foo): # [3]
and it's not possible to import anything from py2.pkg2 subpackage, e.g.
import py2.pkg2
fails with the following exception:
Traceback (most recent call last):
File "runme.py", line 3, in <module>
import py2.pkg2
File "py2/pkg2/__init__.py", line 4, in <module>
from bar import Pkg2_Bar
File "py2/pkg2/bar.py", line 71, in <module>
class Pkg2_Bar(py2.pkg2.pkg3.foo.Pkg3_Foo):
AttributeError: 'module' object has no attribute 'pkg2'
It seems like during the import [1], the subpackage pkg2 is not yet fully
initialized, so pyX.pkg2 is not known. The above exception is raised at line [3].
The problem disappears, for example, if we force swig to use relative package
names.
The difference between this ('from_init2') case and the case
'from_init1' is that here it's not sufficient to import relative module
by just ignoring the package part of the fully qualified module name. IOW
it is not correct to force swig to put:
import foo
class Pkg2_Bar(foo.Pkg3_Foo)
into pyX/pkg2/bar.py (note, that this would work for 'from_init1' case).
The import directive shall be rather:
import pkg3.foo
for python 2.x and:
from . import pkg3
import pkg3.foo
for python 3, and the class definition shall begin with:
class Pkg2_Bar(pkg3.foo.Pkg3_Foo)
If everything works well, the package pyX.pkg2 shall load properly.
Unix:
-----
- Run make
- Run the test as described above

View file

@ -0,0 +1,14 @@
TOP = ../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
cd pkg2 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' build
static:
cd pkg2 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' static
clean:
$(MAKE) -f $(TOP)/Makefile python_clean
cd pkg2 && $(MAKE) clean

View file

@ -0,0 +1,18 @@
TOP = ../../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='bar' INTERFACE='bar.i' python_cpp
cd pkg3 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' build
static:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='bar' INTERFACE='bar.i' python_cpp
cd pkg3 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' static
clean:
$(MAKE) -f $(TOP)/Makefile TARGET='bar' python_clean
cd pkg3 && $(MAKE) clean

View file

@ -0,0 +1 @@
from bar import Pkg2_Bar

View file

@ -0,0 +1,5 @@
#ifndef PY2_PKG2_BAR_HPP
#define PY2_PKG2_BAR_HPP
#include "../../py2/pkg2/pkg3/foo.hpp"
struct Pkg2_Bar : Pkg3_Foo {};
#endif /* PY2_PKG2_BAR_HPP */

View file

@ -0,0 +1,6 @@
%module(package="py2.pkg2") bar
%{
#include "../../py2/pkg2/bar.hpp"
%}
%import "../../py2/pkg2/pkg3/foo.i"
%include "../../py2/pkg2/bar.hpp"

View file

@ -0,0 +1,15 @@
TOP = ../../../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='foo' INTERFACE='foo.i' python_cpp
static:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='foo' INTERFACE='foo.i' python_cpp_static
clean:
$(MAKE) -f $(TOP)/Makefile TARGET='foo' python_clean

View file

@ -0,0 +1,4 @@
#ifndef PY2_PKG2_PKG3_FOO_HPP
#define PY2_PKG2_PKG3_FOO_HPP
struct Pkg3_Foo {};
#endif /* PY2_PKG2_PKG3_FOO_HPP */

View file

@ -0,0 +1,5 @@
%module(package="py2.pkg2.pkg3") foo
%{
#include "../../../py2/pkg2/pkg3/foo.hpp"
%}
%include "../../../py2/pkg2/pkg3/foo.hpp"

View file

@ -0,0 +1,14 @@
TOP = ../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
cd pkg2 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' build
static:
cd pkg2 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' static
clean:
$(MAKE) -f $(TOP)/Makefile python_clean
cd pkg2 && $(MAKE) clean

View file

@ -0,0 +1,18 @@
TOP = ../../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='bar' INTERFACE='bar.i' python_cpp
cd pkg3 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' build
static:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='bar' INTERFACE='bar.i' python_cpp
cd pkg3 && $(MAKE) SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' LIBS='$(LIBS)' static
clean:
$(MAKE) -f $(TOP)/Makefile TARGET='bar' python_clean
cd pkg3 && $(MAKE) clean

View file

@ -0,0 +1 @@
from .bar import Pkg2_Bar

View file

@ -0,0 +1,5 @@
#ifndef PY3_PKG2_BAR_HPP
#define PY3_PKG2_BAR_HPP
#include "../../py3/pkg2/pkg3/foo.hpp"
struct Pkg2_Bar : Pkg3_Foo {};
#endif /* PY3_PKG2_BAR_HPP */

View file

@ -0,0 +1,6 @@
%module(package="py3.pkg2") bar
%{
#include "../../py3/pkg2/bar.hpp"
%}
%import "../../py3/pkg2/pkg3/foo.i"
%include "../../py3/pkg2/bar.hpp"

View file

@ -0,0 +1,15 @@
TOP = ../../../../../..
SWIG = $(realpath $(TOP)/../preinst-swig)
SWIGOPT =
LIBS =
build:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='foo' INTERFACE='foo.i' python_cpp
static:
$(MAKE) -f $(TOP)/Makefile SWIG='$(SWIG)' SWIGOPT='$(SWIGOPT)' \
LIBS='$(LIBS)' TARGET='foo' INTERFACE='foo.i' python_cpp_static
clean:
$(MAKE) -f $(TOP)/Makefile TARGET='foo' python_clean

View file

@ -0,0 +1,4 @@
#ifndef PY3_PKG2_PKG3_FOO_HPP
#define PY3_PKG2_PKG3_FOO_HPP
struct Pkg3_Foo {};
#endif /* PY3_PKG2_PKG3_FOO_HPP */

View file

@ -0,0 +1,5 @@
%module(package="py3.pkg2.pkg3") foo
%{
#include "../../../py3/pkg2/pkg3/foo.hpp"
%}
%include "../../../py3/pkg2/pkg3/foo.hpp"

View file

@ -0,0 +1,9 @@
# Test import of modules content from within __init__.py
print "Testing %module(package=...) + python 'import' in __init__.py"
import sys
if sys.version_info < (3,0):
import py2.pkg2
print " Finished importing py2.pkg2"
else:
import py3.pkg2
print " Finished importing py3.pkg2"