swig/Examples/python/import_packages/from_init2
William S Fulton 2598574915 Complete the Python 2 removal and fix issues found reviewing it
Follow-up work on top of the initial Python 2.x removal: it finishes the
removal and fixes several issues found while reviewing the change.

Correctness fixes:
- pyrun.swg: SwigPyPacked_str passed the type name straight to
  PyUnicode_FromFormat as its format string, so a type name containing a
  '%' would be misinterpreted. Use PyUnicode_FromString instead.
- Doc/Manual/Varargs.html: the (...) varargs freearg typemap example lost
  its free() loop when the surrounding Python 2 guard was removed, leaking
  the memory the in typemap allocates. Restore the loop, now unconditional.
- Doc/Manual/Typemaps.html: the PyInt_Check to PyLong_Check substitution
  left two typecheck excerpts reading PyLong_Check || PyLong_Check; collapse
  each back to a single check.

Code generator (Source/Modules/python.cxx):
- Emit the native class X(..., metaclass=_SwigNonDynamicMeta) form for
  nondynamic classes in all three base-list branches (object, Exception and
  explicit bases), and drop the Python 2 _swig_add_metaclass helper.
- Emit a plain import builtins as __builtin__ instead of the Python 2
  try/except import fallback.
- Update a stale Python 2.x comment.

Remove the deprecated embed.i library (it only ever worked with Python 2):
- Delete Lib/python/embed.i and the Lib/python/Makefile.in reference to it.
- Remove the python_static and python_static_cpp targets from
  Examples/Makefile.in and the now-orphaned static: targets that used them
  from the Examples/python example Makefiles.
- Remove the embed.i section from the manual.

Python test suite (Examples/test-suite/python):
- profiletest_runme.py: convert the Python 2 print statements to print().
- doxygen_constructors_runme.py: drop the dead sys.version_info < (3, 0)
  branch, keeping the Python 3 super().__init__() form.
- li_cdata_bytes_runme.py and li_cdata_bytes_cpp_runme.py: drop the dead
  exit-on-Python-2 version guard.
- file_test_runme.py and python_abstractbase_runme.py: drop the now-unused
  import sys left behind by guard removal.

Documentation (Doc/Manual/Python.html):
- Drop the embed.i and SWIG_PYTHON_STRICT_UNICODE_WCHAR sections; the latter
  macro was Python 2 only and no longer exists, wide strings are unicode-only
  by default.
- De-duplicate the %pythonabc example and drop a stale collections.abc
  compatibility note.
- Update the version support statement and other stale Python 2 mentions.

Other cleanups:
- Tools/mkdist.py: raise the minimum Python version check to Python 3.
- Reword stale Python 2 comments in pyrun.swg, pyiterators.swg and
  pycontainer.swg, and fix a PyString_FromFormat left in a pyclasses.swg
  doc comment.
- CHANGES.current: record that Python 2 support has been dropped.

Assisted-by: Claude Code (Opus 4.8)
2026-07-06 23:50:31 +01:00
..
py3 Complete the Python 2 removal and fix issues found reviewing it 2026-07-06 23:50:31 +01:00
Makefile Complete the Python 2 removal and fix issues found reviewing it 2026-07-06 23:50:31 +01:00
README Fixed SF bug #1297 (Python imports) 2013-12-24 17:22:25 +00:00
runme.py Python: Drop sys.version_info<3 examples code 2026-07-06 23:50:30 +01:00

README

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