Commit Graph

1282 Commits

Author SHA1 Message Date
William S Fulton 7a137da8f3 [Python] Check annotations in .pyi stubs with -builtin and -fastproxy
The annotation tests skipped their checks with -builtin or -fastproxy as
neither adds __annotations__ to the generated runtime objects. That is no
longer a reason to skip when -pyi is also used, as the annotations are then
in the generated stub file, but the stub lookup only worked for -builtin.

With -fastproxy a method is the flat C wrapper function, so its __qualname__
is Class_method instead of Class.method. Pass a fastproxy flag from each
runme down to the stub lookup so that such a name is split on the stub class
names only when it really is a flat name. Reword the comment explaining when
annotations_supported is false, which was misleading with -pyi.

Assisted-by: Claude Code (Opus 5)
2026-08-04 23:41:57 +01:00
William S Fulton f06d86749c [Python] Fix empty classes and imported bases in .pyi files
A generated stub class with no wrapped members had an empty suite, which
is invalid Python syntax. Stubs could also refer to base classes from
%imported modules without importing the corresponding Python proxy
module, leaving those names unresolved.

Track whether each stub class emits a member and use an ellipsis when
its body would otherwise be empty. Collect modules referenced through
%import and emit them before declarations, using the same package and
relative import rules as the generated Python proxy.

Keep annotation tests active with -pyi by parsing annotations from the
generated stub instead of expecting them on runtime objects. Include
each generated .pyi file in multi-module Pyrefly checks.

Add Python 3.14 Linux CI configurations for -pyi -typehints with and
without -builtin.

See #3473.

Assisted-by: Codex (GPT-5.6 Sol)
2026-08-04 23:41:57 +01:00
William S Fulton afc39844b4 [Python] Add %pythonstubcode and %pythonstubbegin directives
These are the .pyi stub file equivalents of %pythoncode and %pythonbegin,
for adding Python code to the stub file generated by the -pyi and -pyifile
options. They do nothing unless a stub file is being generated.

%pythonstubcode inserts the code at the point the directive appears,
indenting it into the class body when used inside a class. %pythonstubbegin
inserts the code just after the SWIG banner, before any real code.

The stub file is generated independently of the .py file, so code added with
%pythoncode or %pythonbegin does not appear in it. Classes configured with
%pythonabc use names from collections.abc as generated base classes and
pyabc.i imports this module into the .py file, but a separately generated
stub had no import, leaving its base class unresolved. Add the equivalent
%pythonstubcode block to pyabc.i.

Extend the python_pyi test with a collections.abc base class and blocks
exercising both new directives.

See #3473.

Assisted-by: Claude Code (Opus 5)
2026-08-04 23:41:57 +01:00
William S Fulton b9cbc4e624 [Python] Clean all generated stub files
Remove generated .pyi files alongside Python proxy files during
test-suite and example cleanup. Use .py* patterns so stubs produced
with SWIG_FEATURES=-pyi and other Python sidecar files are covered by
the existing clean rules.
2026-08-04 23:41:57 +01:00
William S Fulton c8f052749e [Python] Fix alternate constructors with -builtin
With -builtin, alternate constructor aliases such as pair = new_pair
were emitted as pure Python code. Pyrefly could not resolve the internal
new_ entry points imported from the native module, so these assignments
caused type checking to fail.

Register alternate constructor wrappers under their public names in the
low-level module. Do not expose internal new_ names or generate Python
assignments to provide the public names.

This covers explicitly renamed constructors, named constructor template
instantiations and implicit constructors for renamed C structs.

Add a Python 3.14 CI build with -builtin -typehints to exercise the
generated type hints with Pyrefly.

Assisted-by: Codex (GPT-5.6 Sol)
2026-08-04 23:41:57 +01:00
William S Fulton ff961037ea [Python] Add -typehints and Pyrefly checks
Add -typehints to enable PEP 484 annotations for a whole interface.
Run Pyrefly on generated wrappers in the Python examples and test suite
with SWIG_FEATURES=-typehints.

Keep C/C++ annotation tests in their original mode, and add a Python
3.14 Linux CI build that runs with -typehints.

See #735.

Assisted-by: Codex (GPT-5.6 Sol)
2026-08-04 23:41:57 +01:00
Nerixyz 5b13fbf23e [Python] Support multi-argument argout typing
Match pytyping against the full wrapper parameter list so a
multi-argument argout typemap supplies one return type.

See #3469.
2026-08-04 23:41:13 +01:00
William S Fulton 7458ea361a [Python] Fix annotations for multiple outputs
Build PEP 484 annotations from every Python output value, including a
native return that is not void. Use typing.List[typing.Union[...]] for
multiple results.

See #3469.

Assisted-by: Codex (GPT-5.6 Sol)
2026-08-04 23:41:13 +01:00
William S Fulton 65b1e662e3 [Test-suite] Add a common director_langobj test for the language object type
The directorin typemap fixed a couple of commits ago is in the Unified
Typemap Library and so is shared by every language defining SWIG_Object,
but the test for it was written twice, once against PyObject * and once
against VALUE.  Replace python_director_pyobject and ruby_director_value
with director_langobj, written against SWIG_Object in the same spirit as
the existing langobj test, and run it from the common test list so that
every language at least compiles it.

Runme files are added for the four languages that define both
SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is the four that generate
director code and were affected: Python, Ruby, Perl and Octave.  Tcl, R
and Scilab define SWIG_Object but have no directors, so they get compile
only coverage.

python_director_pyobject is kept for the Python specific parts that have
no equivalent in the other languages: the swig::SwigPtr_PyObject and
swig::SwigVar_PyObject smart pointers, by value and by const reference,
and a null PyObject * argument.

Assisted-by: Claude Code (Opus 5)
2026-08-01 20:41:20 +01:00
William S Fulton ff57c2aa6b Fix director arguments taking a const ref to the language object type
There is a directorin typemap for SWIG_Object but none for
SWIG_Object const &, so the const reference form fell through to the
generic SWIGTYPE *const& typemap and was wrapped as an opaque proxy
object instead of being passed through unchanged.  Affects every
language defining both SWIG_Object and SWIG_DIRECTOR_TYPEMAPS, that is
Python, Ruby, Perl and Octave.  For Ruby the generated code did not even
compile, as SWIG_as_voidptr cannot cast a VALUE to void *.

For Python this also covers swig::SwigPtr_PyObject const& and
swig::SwigVar_PyObject const&, which are %applied from PyObject *const&.

Additionally fix a reference count leak for a swig::SwigVar_PyObject
director method argument passed by value.  Assigning it into the
SwigVar_PyObject wrapper variable selects the implicitly declared copy
assignment operator, which adds a reference of its own, so the typemap's
SWIG_Py_XINCREF was a second increment against a single decrement on
scope exit.  Casting to PyObject * selects
SwigVar_PyObject::operator=(PyObject *), which does not adjust the
count, making the SWIG_Py_XINCREF the one and only increment for every
argument type the typemap handles.

The existing python_director_pyobject test did not catch the leak
because its C++ caller built the SwigVar_PyObject from a borrowed raw
pointer, so the temporary stole a reference and cancelled it out.  The
callers are now reference count neutral, the const reference forms are
covered, and the test checks that the object arriving in Python is the
object passed from C++, not just that the count is stable.

Assisted-by: Claude Code (Opus 5)
2026-08-01 16:35:12 +01:00
William S Fulton 7c4215cb56 [Python] Director PyObject * typemap fixes and test tidy up
Follow on fixes to the previous commit:

Use SWIG_Py_XINCREF instead of Py_INCREF. Py_INCREF crashes on a null
PyObject * argument, which a C++ caller may legitimately pass, turning
what was a recoverable director error into a segfault. SWIG_Py_XINCREF
also honours the stable ABI, where it expands to Py_IncRef, and matches
the macros used elsewhere in pyclasses.swg.

Declare the typemap before the %apply directives in pyclasses.swg. %apply
copies the typemaps that exist at the point it appears, so a directorin
typemap declared after them was never propagated to swig::SwigPtr_PyObject
or swig::SwigVar_PyObject, which suffered from the same underflow. Passing
a swig::SwigPtr_PyObject by value was worse, as the wrapper variable stole
the reference held by the argument.

Rename the test to python_director_pyobject following the naming used for
the other Python only tests, and extend it to cover swig::SwigPtr_PyObject,
swig::SwigVar_PyObject and a null argument. Remove the unused variable and
the no-op if from the runme file, check the reference count after the
garbage collection loop and drop the success message, as the tests are
silent when they pass.

Add the CHANGES.current entry.

Assisted-by: Claude Code (Opus 5)
2026-08-01 15:11:22 +01:00
Julien Schueller 495723408e [Python] Fix refcount underflow in director
Each director upcall for a method taking PyObject* created a
SwigVar_PyObject, assigned the C++ parameter (a borrowed reference)
to it, then let the SwigVar_PyObject destructor DECREF it on scope
exit -- with no matching INCREF.  After enough calls the object's
reference count underflowed, the object was freed prematurely, and
the next call crashed with 'deletion of interned string failed'.

Fix: add a %typemap(directorin,noblock=1) for PyObject* that INCREFs
the parameter after assignment, balancing the destructor's DECREF.

Fixes #2015.

Assisted-by: opencode (deepseek-v4-flash)
2026-08-01 14:21:22 +01:00
William S Fulton de573cda78 Pass SWIG_FromCharPtrAndSize and friends on to the C compiler
A bare #define at the start of a line in a brace delimited %fragment body is
consumed by the SWIG preprocessor and never reaches the generated wrapper. The
macro therefore only exists at SWIG level and code that SWIG does not macro
expand, such as a typemap delimited with %{ ... %}, is left referring to an
undefined name. Use %#define so the definition is emitted for the C compiler.

Fixes SWIG_FromCharPtrAndSize for Python, SWIG_FromBinaryCharPtrAndSize for
Octave, Perl, R and Ruby, SWIG_ToUint8Array and SWIG_FromUint8Array for
JavaScript, and the SWIG_AsVal_* and SWIG_From_* macros in the Scilab char,
short, signed char, unsigned char, unsigned long and long long files.

Lib/scilab/scipointer.swg is deliberately left alone. Its fragments are never
requested by any typemap, so they are only emitted as SWIG preprocessor
definitions; switching them to %#define drops SWIG_ConvertPtr and
SWIG_NewPointerObj from the generated code entirely. Fixing those needs the
definitions moving to a runtime insert, as the other target languages do.

Add a common test case charptr_fragment. It uses a %{ ... %} delimited typemap
so the macro name survives into the wrapper, which only compiles when the
library defines the macro for the C compiler. The target languages without
these macros wrap the same functions using their default typemaps. Verified
that the generated Python module fails to load before this change with an
undefined symbol for SWIG_FromCharPtrAndSize.

Follows on from #3522.

Assisted-by: Claude Code (Opus 5)
2026-07-31 00:10:40 +01:00
William S Fulton 9302b31f57 [Python] Suppress .py annotations and TYPE_CHECKING guard when -pyi is active
A .pyi always takes precedence over its .py companion for type
checking, so once -pyi/-pyifile is active, annotations left in the .py
file are never consulted by any type checker - dead weight. Suppress
them there; the .pyi keeps the full annotations.

- returnTypeAnnotation()/variableAnnotation() now return empty once
  pyi_stub is set. The previous always-full versions are renamed to
  returnTypeAnnotationForStubFile()/variableAnnotationForStubFile()
  and used only for the .pyi output.
- make_pyParmList() gains a for_stub flag to suppress per-parameter
  annotations in a def's parameter list the same way.
- emitTypeWrapperClasses()/emitTypeWrapperClass() take a
  guard_with_type_checking flag: the opaque SWIGTYPE_* wrapper classes
  stay guarded by 'if typing.TYPE_CHECKING:' in the .py file (meaningful
  there - they must not exist at runtime), but are emitted unconditionally
  in the .pyi (a stub file has no runtime, so the guard is vacuous there).

Documentation: 33.12.1.2 (PEP 484 annotation types) now includes a
worked example of the SWIGTYPE_* opaque type wrapper class fallback
(a single extra function plus its generated .py output), and
33.12.1.3 (Generating .pyi stub files) reuses that same
OptionalInt/Unwrapped example instead of a separate Shape class,
showing the type wrapper class in the .pyi output too - which
visibly lacks the 'if typing.TYPE_CHECKING:' guard the .py version
has. All shown output verified against actual swig output (mypy
clean on the .pyi, ast-parsed the .py).

Assisted-by: Claude Code (Opus 4.8)
2026-07-29 19:34:35 +01:00
William S Fulton 43ca691f0f [Python] Fix -pyi stub generation, rename option, add docs and test
Fixes and finishes off the -pyi-stub feature (generates a .pyi PEP 484
stub file alongside the wrapped module):

- printClassHeader() called _swig_add_metaclass, a runtime helper that
  no longer exists, which would raise a NameError for any class using
  %feature("python:nondynamic"). Switched to the current metaclass=
  keyword-argument approach used elsewhere in this file.

- classHandler() called addSymbol() unconditionally, so plain -noproxy
  builds (unrelated to -pyi) could fail with spurious "multiply
  defined" errors. Gated back to (shadow || pyi_stub).

- The opaque SWIGTYPE_* wrapper classes are now emitted into the .pyi
  stub too, not just the .py file, so $pytypename annotations falling
  back to an opaque type resolve to a name actually defined in the stub.

- Unannotated variables/constants were emitted into the .pyi as a bare
  name with nothing else on the line, which is not a valid attribute
  declaration. Falls back to ": typing.Any".

- Regular instance methods and static methods were both silently
  missing from the .pyi under -builtin (the option's primary intended
  use case), because two separate code paths never reached the shared
  pyi_stub emission logic.

- Renamed the command line option from -pyi-stub to -pyi, and improved
  the -help text with the PYI ("Python Interface") acronym.

- Added header comment blocks to the new helper methods, matching the
  file's existing convention.

- Documented -pyi in the manual with a worked, verified example, and
  marked the pytyping/-pyi work as experimental/still evolving in
  CHANGES.current.

- Added a python test-suite case (python_pyi.i) built with -builtin
  -pyi, covering a constructor, regular method, static method, member
  variable and an opaque-type fallback in one go. Clean up generated
  .pyi files in the Makefiles the same way .py files already are.

Assisted-by: Claude Code (Sonnet 5)
2026-07-29 18:58:46 +01:00
William S Fulton a0da96aff8 [Python] #3390 Add $pytypename special variable for pytyping typemaps
Finalises the PR keeping the $pytypename mechanism but leaving the default
pytyping SWIGTYPE typemaps as typing.Any. The special variables are exercised
only through explicit typemaps; the default annotation change and the
library-wide pytyping sweep are left for a follow-up.

python.cxx:
- getProxyClassLocalName() uses import_name_string() so a class from an
  imported module gets its fully qualified, package-aware name, as in
  classDeclaration().
- Guard substitutePytypingVars() against a null typemap and skip work when
  there is no special variable to substitute.
- Error when $*pytypename is applied to a non-pointer type instead of
  emitting the literal.
- Remove the _swig_python_version_info >= (3, 5) guards; SWIG supports Python
  3.5 and later, so import typing unconditionally and use a bare
  if typing.TYPE_CHECKING.
- Fix two leaks (SwigType_manglestr, SwigType_typedef_resolve_all).

Doc/Manual/Python.html: correct the feature value pytyping to typing and
document the new special variables with valid HTML.

Tests: python_annotations_typing opts in to proxy-name annotations via explicit
typemaps and covers $*pytypename, class-typed member variables, a class-typed
%constant, and the opaque fallback for a forward-declared class; add
python_annotations_import for the cross-module module-qualified name.

Assisted-by: Claude Code (Opus 4.8)
2026-07-27 23:40:48 +01:00
Nerixyz e237098474 Python: update $pytypename annotation tests after merge 2026-07-27 23:39:31 +01:00
William S Fulton c6d16a0bd8 Python: follow-up fixes for $pytypename PEP 484 annotations
- HTML doc cleanup/edits.
- Drop a stray Printf argument in emitIncompleteClass; the format string
  has no conversion for it.
- Take ownership of the getProxyClassLocalName() result directly instead
  of copying it, fixing a small string leak.
- Use SwigType_lstr instead of SwigType_str for the opaque class docstring
  so the shown type matches the mangled SWIGTYPE_ name - short &, short *
  and short[] all resolve to "short *" for SWIGTYPE_p_short.
- Emit the generated SWIGTYPE_ type wrapper classes inside an
  'if typing.TYPE_CHECKING' block instead of as real runtime classes. They
  exist only to give the PEP 484 annotations a named type to refer to, so
  declaring them for static type checkers only keeps them out of the runtime
  module namespace. A preceding comment replaces the previous per-class
  "only used for type annotations ..." docstring line.
- Quote the type in the type wrapper docstrings with single quotes rather
  than reStructuredText double backticks as this is the SWIG convention
  and the tools that would actually render RST docstrings — Sphinx autodoc,
  help()/pydoc (to inspect runtime objects) wouldn't even see them as
  they inspect runtime objects.
- Replace the "is this always correct?" FIXME in the enum branch of
  substituteTypenameSpecialVariable with a comment describing when it is
  reached and why int is correct. The default 'enum SWIGTYPE' typemap maps
  straight to "int" without substituting, so enums only reach this branch
  when a pytyping typemap uses $pytypename on an enum type; int is right
  because Python wraps enums as ints. Add a test that exercises this via a
  custom typemap (without it the type would wrongly resolve to an opaque
  SWIGTYPE_ class).
- Assert in the python_annotations_typing_runme test that the SWIGTYPE_
  type wrapper classes are not present at runtime, confirming they are
  declared for type checkers only.

Assisted-by: Claude Code (Opus 4.8)
2026-07-27 23:39:31 +01:00
Nerixyz 7a38effaac Python: Show class typenames in PEP 484 annotations 2026-07-27 23:39:31 +01:00
William S Fulton 50df6ced1f [Python] Only define pytyping typemaps where in typemaps are defined
Follow up fixes to #3408.

A pytyping typemap must be defined alongside the in and out typemaps for the
type, otherwise a type wrapped as a pointer to an opaque type is annotated as
though it were a native Python type. Move the remaining pytyping typemaps that
were defined by default but whose in typemaps are not:

  wchar_t and wchar_t * to pywstrings.swg, which is included by wchar.i,
  cwstring.i and std_wstring.i
  float _Complex, double _Complex and _Complex to ccomplex.i

Delete the long double pytyping typemap from pytyping.swg. Unlike the types
above there is no library file to move it to, as long double has no in and out
typemaps anywhere in the SWIG library. It is always wrapped as a pointer to an
opaque type and a Python float is never accepted for it, so annotating it as
float was always wrong. It now falls back to the SWIGTYPE default typing.Any.

Include pytyping.swg from pytypemaps.swg before the Unified Typemap Library
instead of from python.swg after it. A %apply in the library, such as
%apply size_t { std::size_t } in typemaps/misctypes.swg, only copies the
typemaps defined at that point, so std::size_t and std::ptrdiff_t now pick up
the pytyping typemaps instead of having to be listed again.

Also fix the const long double & annotation which was wrongly changed to float.

Add the CHANGES.current entry, correct the PEP 484 example in the Python
documentation for the char * annotation change, and align the indentation of
the char typemap with the other grouped typemaps.

Assisted-by: Claude Code (Opus 4.8)
2026-07-23 17:30:59 +01:00
Nerixyz 3ce7a62a8b Python: Declare `char*` as optional 2026-07-23 17:30:59 +01:00
Nerixyz 42942f4cae Python: Map more primitives for PEP 484 annotations
Co-authored-by: William S Fulton <wsf@fultondesigns.co.uk>
2026-07-23 17:30:59 +01:00
Julien Schueller c3983ffabb [Python] Fix metaclass conflict under -builtin
The SwigPyObjectType metaclass and SwigPyStaticVar type created via
PyType_FromSpec in the heap types path of builtin.swg were allocated as
distinct heap type instances in each compiled module's copy of the
TypeOnce functions. When a class inherited from types defined in
different SWIG extension modules -- such as a director class from
module A and a non-director class from module B -- PyType_FromSpecWithBases
detected incompatible metaclasses and raised:

  TypeError: metaclass conflict

Both TypeOnce functions now check the shared runtime data module for an
existing instance before creating a new one, so all modules share a
single metaclass. This regression was introduced when SWIG_HEAPTYPES
was enabled by default (SWIG 4.4) and only manifests on Python 3.12+.

Fixes #3315.

Assisted-by: opencode (deepseek-v4-flash)
2026-07-22 16:54:04 +02:00
William S Fulton 32a3e5b464 R: Honour %rename of enum items in the generated R code
R::enumvalueDeclaration built the enum item label passed to
defineEnumeration from the C++ name rather than sym:name, so %rename of an
enum item was not honoured in the generated R code. Use sym:name for the
label.

Add test coverage for %rename of an enum class and an enum item to the
cpp11_strongly_typed_enumerations runme of every target language that has
one, locking in the behaviour for the languages that were already correct.
The enum_thorough R runme now checks the renamed enum item too.

Assisted-by: Claude Code (Opus 4.8)
2026-07-15 00:24:05 +01:00
William S Fulton f156c99907 Fix protected/private nested class referenced in generated upcast helper
Reported on the swig-devel mailing list during early testing of swig-4.5,
where CrossWire SWORD's Perl bindings (GBFHTMLHREF::MyUserData deriving
from BasicFilterUserData) failed to build: a protected or private nested
class deriving from a class used elsewhere in the wrapped API could produce
a runtime upcast helper function referencing the nested class by its
inaccessible qualified name, a C++ compile error. No target language ever
wraps a non-public nested class, so typepass.cxx now simply skips
registering it for the cast table. Fixes it for Lua too, which hits the
same bug as Perl/Python/Ruby/Tcl despite having full nested class support,
since Java/C# are structurally immune (they don't use SWIG's generic
runtime type table at all). Added regression tests to nested_scope.i and
director_protected.i (covering directors/dirprot), with a runtime check in
every director_protected runme confirming polymorphic dispatch through the
wrapped base still works correctly.

Assisted-by: Claude Code (Sonnet 5)
2026-07-11 21:02:49 +01:00
William S Fulton 3dda830ac9 Add char *& string typemaps to all languages and char_strings runme parity
A 'char *&' (a reference to a char pointer) was only marshalled as a string by
C#, D, Go, Java and PHP; every other language treated it as an opaque char **
pointer.  Add char *& string typemaps to the languages that were missing them,
so a char *& function argument, return value or variable is marshalled as a
string in every target language.  SWIG's const reference stripping means this
also covers char *const&.

- Lib/typemaps/strings.swg: add Char *& to the %typemaps_string in, freearg,
  out, typecheck and director typemaps.  This gives char *& to the languages
  that use the Unified Typemap Library (Python, Ruby, Perl, Tcl, Octave, Scilab,
  R and JavaScript).  The shared in typemap now casts &buf to $1_ltype so that a
  const char * reference hidden behind a typedef also compiles.
- Lib/lua/luatypemaps.swg, Lib/guile/typemaps.i, Lib/ocaml/ocaml.swg: these
  define their own char * string typemaps; give each char *& and const char *&
  in/out/freearg typemaps too.
- Lib/r/rtype.swg: the C wrapper returned the char *& string correctly but the R
  proxy wrapped it as an undefined _p_p_char S4 class; add char *& to the
  scoerceout char list so it is returned as a plain character value.

char_strings.i is now exercised by a runme in every target language, all testing
the same set of functions (get/set/pingpong/global variables and all four char *&
functions), giving complete char *& typemap coverage and testing.

Where a language genuinely cannot support part of the char array portion of the
test (a char[] global has no varin typemap in most scripting languages, a char[16]
parameter is bounds checked, and Guile/OCaml reject char[] parameters), that one
assertion is adapted or skipped with an inline comment; the char *& coverage is
complete everywhere.

Assisted-by: Claude Code (Opus 4.8)
2026-07-09 21:55:58 +01:00
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
Julien Schueller 8f0cbe1195 Drop SWIG_PYTHON_2_UNICODE 2026-07-06 23:50:31 +01:00
Julien Schueller a1c738f02f Python: Drop python2 build 2026-07-06 23:50:30 +01:00
Julien Schueller cb6e7c542a Python: Drop sys.version_info<3 examples code 2026-07-06 23:50:30 +01:00
Julien Schueller da2f0f9c03 Python: Drop operators compatibility 2026-07-06 23:50:30 +01:00
Julien Schueller fb18e24fa5 Python: Drop collections.abc compatibility 2026-07-06 23:50:30 +01:00
Olly Betts 9140467c2d Improve handling of NULL vs nullptr vs 0 vs 0L
For some target languages (Octave, Python and Ruby), SWIG has previously
treated nullptr or NULL as an integer 0 if used in a situation where
the type wasn't known to be a pointer.

For nullptr this is never helpful, because it has type nullptr_t which
does not implicitly convert to 0, so we no longer do this.

For NULL it's rather dubious - C and C++ allow NULL to be defined as
integer 0, so `int i = NULL` may work and is occassionally seen in real
code, but it is semantically wrong.  Also GCC and clang define NULL to a
magic value and by default will warn about such misuse, so it's likely
to be less common than before they did this.  So now SWIG only converts
NULL to 0 if used in a context where we know the underlying type is an
arithmetic type.

Using an integer zero (or equivalent value such as 0L) for a NULL
pointer is valid, and SWIG will still treat it as a NULL pointer if used
in a context where know the type is a pointer.  This is now done based
on the value of the integer constant so also applies to 0L (previously
it was only done if the value was written in the code as literally `0`).

Fixes: #3472
2026-06-29 14:36:06 +12:00
William S Fulton c0754fbde2 Add test coverage and docs for alias templates as a base class and using-declaration qualifier
The test suite previously only exercised C++11 alias templates as function return
types (cpp11_alias_templates).  These add coverage for an alias template instantiation
used in inheritance positions:

  - cpp11_alias_template_inheritance: an identity alias template as a base with a member
    using-declaration, an identity alias whose inherited enum is imported through the alias
    qualifier (with the alias also naming a function parameter type), a non-identity alias
    as the base of a class template where the using-declaration imports an overload that
    merges with a local overload, and an inheriting constructor whose scope qualifier is an
    alias template
  - cpp17_using_pack_alias_template: a C++17 using-declaration pack over an alias template
    base pack, both for inherited member functions ('using Identity<Ts>::g...;') and for
    inherited constructors ('using Identity<Ts>::Identity...;') - the alias template
    analogue of cpp17_inheriting_constructors_pack, which inherits through direct bases

Each follows the documented pattern: the underlying template is instantiated with a
named %template and the alias instantiation is registered with an empty %template(),
after which the alias resolves in these positions just like the underlying type.

A new using_member_typedef_overload test covers the related typedef-qualifier cases: an
overload imported through a typedef-to-template-instantiation qualifier that merges with a
local overload, and an inherited enum imported through a typedef qualifier.

Investigated as issue #3478: an alias template used as a base or using-declaration
qualifier appears to be unresolved (Warning 401 / Warning 315), but this is the
documented requirement that the alias instantiation be registered with %template();
it is not a SWIG defect.

In cpp17_using_pack_alias_template the Over method-pack overloads are ignored for D only
('#if defined(SWIGD) %ignore Over<A, B>::g;'): D wraps multiple inheritance as single
inheritance plus mix-in methods and marks the g(int) overload inherited from the second
base 'override', which ldmd2 rejects.  The class is still wrapped for D and every other
language exercises the overloads.  For Visual C++ the alias-template-as-base-pack classes
are guarded with '#ifndef _MSC_VER', falling back to the equivalent direct 'Ts...' base
(MSVC rejects an alias template base in a pack expansion with C3770); SWIG still parses and
wraps the alias form so coverage is unchanged, mirroring cpp17_using_typename_pack.

The C++11 chapter's 'Type alias and alias templates' section is split into separate
'Type aliases' and 'Alias templates' sections, and the alias template section is
rewritten to explain the two-%template requirement and the base class / using-declaration
usage that was previously only shown for return types.

The swig-doc and swig-test skills gain a note that code examples and .i test cases must
not place a class or struct definition on a single line.  The swig-test skill also gains
guidance on handling a failure in a single target language (fix or work around it in that
language rather than excluding the test, except for experimental backends) and on
reporting which languages and tests were run.

Assisted-by: Claude Opus 4.8
2026-06-20 07:20:37 +01:00
William S Fulton c0d4f290d6 Fix C++17 inheriting constructor pack dropping the last base constructor
The inheriting constructor pack 'using T::T ...;' over a variadic base pack
wrapped the constructors of every base except the last, and emitted a spurious
Warning 526 naming the using declaration after the derived class instead of the
base.

During template instantiation the pack using declaration is expanded into one
using declaration per base: the first element is patched in place and the rest
are appended as siblings.  The appended siblings were only revisited by the
class child walk when the using declaration was not the last child, because that
walk captures the next sibling before recursing so that an empty pack can detach
itself.  As the pack using declaration is normally the last member, the appended
siblings kept the unexpanded template name and were never registered as
inheriting constructors of the instantiated class.

Expand and splice each appended sibling in place immediately after the first
element, so every base constructor is wrapped regardless of the using
declaration's position and the empty pack detach path is left untouched.

Closes #3481

Assisted-by: Claude Opus 4.8
2026-06-18 22:01:51 +01:00
William S Fulton 6723e09480 Fix syntax error for inherited conversion operator in a using declaration (#3480)
A using declaration naming an inherited conversion function, such as
'using Base::operator int;', was rejected with a syntax error. Other
using-declaration declarator-ids parsed fine, but a conversion-operator-id was
handled only by the standalone conversion operator rule, which requires a full
function definition; no using-declaration production accepted it.

Add a dedicated using-declaration production for a scope qualified conversion
operator. This is kept separate from the shared idcolon rule: idcolon would
greedily consume the trailing scope qualifier and never reach the
CONVERSIONOPERATOR token, and extending idcolon itself would change how out of
class conversion operator definitions are parsed. The conversion operator is now
brought into the derived class like any other inherited member.

New test typedef_inherit_using imports a privately inherited conversion operator
with a using declaration, exercised by mirrored Python and Java runme scripts.

Fixes #3480

Assisted-by: Claude Code (Opus 4.8)
2026-06-18 21:42:06 +01:00
William S Fulton 85ed426a49 Fix syntax error for typename member type pack in a using declaration (#3479)
A using declaration that combines the 'typename' disambiguator with a C++17
pack expansion to import a member type from each base in a base pack, such as
'using typename Bases::value_type ...;', was rejected with a syntax error.
The typename form and the pack form each parsed on their own, but there was no
grammar production for the combination.

Add the missing USING TYPENAME idcolon ELLIPSIS SEMI production to cpp_using_decl,
mirroring the existing pack expansion rule and setting the pack flag.

New test cpp17_using_typename_pack exercises the construct with mirrored Python
and Java runme scripts.

Fixes #3479

Assisted-by: Claude Code (Opus 4.8)
2026-06-18 21:42:06 +01:00
William S Fulton c5bfe9766f Fix template parameter expansion for members inherited via a typedef qualifier
A using declaration that brings an inherited member into a derived class
through a typedef or C++11 alias of a template instantiation base lost the
template arguments of the qualifier, so template parameters in the member's
type were left unexpanded and the generated wrapper failed to compile.

For example, given:

  template <typename LinksT> class NodeI {
  public:
    using links_type = LinksT;
    Owners<links_type> owners;
  };
  template <typename LinksT> class Cluster : public NodeI<LinksT> {
  public:
    using NodeIT = NodeI<LinksT>;
    using NodeIT::owners;
  };

the wrapped owners member was emitted as Owners<links_type> rather than
Owners<NodeI<int>::links_type> for Cluster<int>.

Swig_symbol_type_qualify resolved the typedef qualifier but rebuilt the
scope from the found member's symbol table, which is the bare template name
and drops the template arguments. The qualifier is now rewritten to the
template-id, with the template arguments typedef reduced, so the existing
template handling preserves them. Inheriting constructor using declarations
and global scope or operator qualifiers are left untouched.

This is the same root cause as a member function whose parameter or return
type is written in terms of the base template parameter, for example a method
taking Owners<links_type>& inherited through the typedef qualifier (#1153).
Both forms are now expanded correctly.

Fixes #1042
Fixes #1153

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 22:28:32 +01:00
William S Fulton 65fbe448ac Move the #2933 duplicate using declaration regression test to its own test case
The regression test was added to li_std_vector.i, but it has nothing to do
with std::vector - move to its own duplicate using declarations test.
2026-06-17 01:53:49 +01:00
William S Fulton 7541a2af27 Fix infinite loop inheriting through a self-referential typedef (#1827)
A class inheriting from a base named through a redundant self-referential typedef,
for example

  typedef struct foo foo;
  struct foo { };
  class bar : public foo { };

sent SWIG into an infinite loop. cplus_inherit_types_impl chases typedef
declarations to find the base class, but looking up 'foo' returns the typedef node
whose underlying type is again 'foo', which resolves by name back to the same
typedef node, so the loop never terminates.

Detect this self-reference (the underlying type resolves to the same node) and
resolve to the aliased class of the same name instead, which is a sibling of the
typedef in the symbol table found by walking the csym:nextSibling chain. The base
class is then resolved correctly so bar derives from foo.

Add the same self-reference guard to Swig_symbol_clookup_resolve_typedef, which is
used for base class lookup in a later pass and had the same latent loop.

Extend the typedef_inherit test with a regression case.

Assisted-by: Claude Code (Opus 4.8)
2026-06-17 01:47:06 +01:00
William S Fulton 638742fbcc Detect inheriting constructors by base class match in the parser
The parser flagged an inheriting constructor (using Base::Base) only when the
terminal name of the nested-name-specifier equalled the unqualified-id, which
missed the form 'using Alias::Base' where Alias is a typedef for the direct base
Base.  The parser now also flags a candidate when the unqualified-id matches a
base class of the enclosing class, resolved through typedefs.  The type pass
verifies the candidate and clears it when the nested-name-specifier does not
resolve to an immediate base whose own name the unqualified-id is, so an ordinary
member using declaration is imported instead.

Extend cpp11_template_using_base and cpp11_inheriting_constructors_typedef with
the typedef-qualifier forms, including the base's own member typedef as the
qualifier, the base named by its own name through a typedef, and a member typedef
and protected method import.

Assisted-by: Claude Opus 4.8
2026-06-17 01:47:06 +01:00
William S Fulton b78dba177a Inherit constructors named through a typedef base
C++11 inheriting constructors (using Base::Base;) now work when the immediate
base class is named through a typedef, a chain of typedefs, a typedef whose
template argument is itself a typedef, a scope-qualified or namespaced name, or
a type-template parameter used directly as the base class (the mixin idiom).

The inheriting constructor's using-declaration qualifier is normalized in the
typepass stage; the inheriting base is then found by identity in the resolved base
class list and the base class' constructors are used to implement the inherited
constructors.

An inheriting-constructor using declaration whose qualifier is not an immediate
base class is reported with Warning 329 (uses base '...' which is not an
immediate base of '...') rather than the generic Warning 315.

Closes #2951)

Assisted-by: Claude Opus 4.8
2026-06-17 01:47:06 +01:00
William S Fulton 055fe0e793 Inject a base class name into the derived class so it can be named there
In C++ a base class' name is a member of the base (its injected class name) and so
is visible from within a derived class, named either unqualified ('Base') or scope
qualified through the derived class ('Derived::Base').  SWIG did not model this, so
a base in a namespace named without its namespace qualifier was not resolved: a
using declaration through such a typedef gave a spurious Warning 315 and dropped the
member, and the base named as a type was either unresolved or treated as a distinct
type from the base named directly.

  namespace Space { struct Base { ... }; }
  struct Derived : Space::Base {
    typedef Base base_type;        // Base, not Space::Base
    using base_type::method;       // no longer Warning 315
    Base m(Base b);                // 'Base' resolves to Space::Base
  };
  Derived::Base f(Derived::Base);  // 'Derived::Base' resolves to Space::Base

This is handled in three places, mirroring the same C++ rule:

 - Swig_symbol_inherit() adds the base class' name to the derived class' C symbol
   table, so the symbol table resolves the base named from within the derived class.
   The node added is the base's own entry in its enclosing scope, where every class
   is registered; it does not clash, as the derived class' constructors carry the
   derived name.

 - The type system (typepass) aliases the base class' name to the base's own scope
   within the derived class, so a base named unqualified as a type resolves to the
   base's own type.

 - SwigType_typedef_qualified() resolves a scope qualified name that itself names a
   scope (such as Derived::Base) to that scope's canonical name, so the base named
   through the derived class resolves to the same type as the base named directly.

Closes #2659

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 01:47:06 +01:00
William S Fulton 978f675ad7 Fix spurious Warning 315 for a member using-declaration through a typedef base with a constructor
When a base class declares a constructor of its own, the constructor shares the
base class name.  Resolving a typedef that names the base to its scope looked the
base class name up through the derived class' inherited scope and found the base
constructor ahead of the class itself, so no typedef scope alias was created and a
later 'using base_type::member;' was reported as Warning 315 and the member
silently dropped.

Skip constructor nodes (including the using-declaration nodes that inherit base
constructors, which also carry the class name) when resolving a typedef to its
scope, so the class node is found instead.

Resolving the typedef now goes through Swig_symbol_clookup_check, whose
using-declaration chase loop was missing the self-reference guard that
Swig_symbol_clookup already has.  Add it to avoid infinite recursion (a stack
overflow and crash) on a self-referential using declaration; the existing test
Examples/test-suite/using2.i, with a top-level 'using ::baz;', exercises this.

Assisted-by: Claude Opus 4.8 (1M context)
2026-06-17 01:47:06 +01:00
William S Fulton ef8c5af40a Add a 3-level deep nested test 2026-06-08 18:34:56 +01:00
William S Fulton 6ec4b440d6 Keep unsupported nested classes as ignored classes, not forward declarations
For a target language that does not implement nested class support, a C++
class nested inside another class was discarded after parsing and replaced
by a synthesized forward class declaration in the enclosing scope. That
forward declaration carries no symbol table or members, so names that
resolve through the nested class could not be resolved. In particular a
using declaration whose scope qualifier reaches the nested class through a
typedef, such as 'using Outer::Nested::Me::method;' where 'Me' is a typedef
naming the nested class, gave a spurious 'Nothing known about ...' warning
(Warning 315) and the member was silently dropped.

The nested class is now kept as the real parsed class node and marked with
feature:ignore, instead of being replaced by a forward declaration. It is
still not wrapped (and still reported with Warning 325), but its scope,
members and typedefs remain available for name resolution. This also
resolves an out of line nested class definition written inside the
enclosing class, such as 'struct Outer::Nested { ... };', which previously
reported 'Outer is not defined as a valid scope'.

Nested template classes are kept the same way. A %template instantiation of
such a class still cannot be wrapped as a proxy class and continues to be
reported with Warning 331.

Add the using_nested_member_typedef test and the cpp_nested_out_of_line_scope
error test. The nested_scope test was previously run only for Java and C#
to avoid the out of line scope error above; it now runs for all languages.

Assisted-by: Claude Code (Opus 4.8)
2026-06-08 07:57:22 +01:00
Nerixyz ddb72429fb Python: Support multi-argument typemaps for pytyping 2026-06-04 23:48:53 +01:00
William S Fulton 15cab25669 Qualify typedef-to-template scope qualifier before lookup
When a using declaration's scope qualifier is a typedef to a template
instantiation, symbol_scope_lookup reduced the typedef target but did not
scope qualify it before looking up the instantiated template scope. The
reduce step qualifies template arguments but not the template name itself,
so a template name introduced by a using declaration (rather than being
directly visible or brought in by a using directive) was left unqualified
and the instantiated scope was not found, giving a spurious 'Nothing known
about ...' warning (Warning 315), eg:

  namespace Other { template <typename T> struct OtherBase { typedef int Integer; typedef OtherBase<T> Me; }; }
  using Other::OtherBase;
  struct Derived : OtherBase<int> {
    typedef OtherBase<int> Base;
    using Base::Me::Integer;
  };

symbol_scope_lookup now qualifies the reduced type with
Swig_symbol_type_qualify before the lookup, following the reduce/qualify
idiom used elsewhere (eg Swig_symbol_template_reduce). This resolves the
template name through the using declaration.

Expand the using_member_typedef_template testcase for testing.

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00
William S Fulton bdbf3586a8 Resolve using declarations qualified by a typedef to a template instantiation
Extends the #2694 fix. When a using declaration's scope qualifier is a
typedef to a template instantiation whose template arguments are
themselves typedefs, eg:

  typedef int IntAlias;
  typedef UsingBase<IntAlias> UsingBaseClass;
  using UsingBaseClass::Me::Integer;

the template arguments were not reduced, so 'UsingBase<IntAlias>' did not
match the instantiated 'UsingBase<int>' scope and SWIG issued a 'Nothing
known about ...' warning (Warning 315).

symbol_scope_lookup now reduces the template arguments of a template
instantiation typedef target via Swig_symbol_typedef_reduce before
looking up its scope, so the instantiated scope is found. The reduction
follows a typedef chain in the template argument too.

Add the using_member_typedef_template test, a template-based variant of
using_member_typedef, and the using_method_typedefs test (a protected
base method brought into the derived public interface via a using
declaration through various typedef forms, covering issue #2951 for
members - the symbol lookup fix above resolves it without a separate
allocate change).

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00
William S Fulton 71856ff1a5 Fix resolution of names with a typedef scope qualifier (#2694)
A using declaration that introduces a base class member via a typedef
used as a scope qualifier, such as 'using BaseAlias::Me::Integer;' where
BaseAlias and Me are typedefs naming a class, was not resolved and SWIG
issued a spurious 'Nothing known about ...' warning (Warning 315).

When the parser qualifies the using declaration target name, the
qualified name lookup only matched scope qualifiers against registered
scope names and did not resolve typedefs that appear as a scope
qualifier. The stored uname was therefore left unqualified and later
symbol and typedef resolution failed. Swig_symbol_clookup and
Swig_symbol_clookup_check now fall back to resolving a typedef scope
qualifier through to its real scope before looking up the member. Only a
qualifier reached through a typedef is resolved here; one that resolves
directly to a class or namespace scope (for example a namespace made
visible by a using namespace directive) is left to the normal lookup. A
template instantiation used as a scope qualifier (eg 'Base<T>::member')
is left to the template machinery, as resolving it here can pick up an
unsubstituted template parameter.

Extend the using_member_typedef test with single, double and namespace
qualified typedef scope qualifiers and add Java and Python runtime tests
that check every member and global function round trips an int.

Assisted-by: Claude Code (Opus 4.8)
2026-06-04 22:45:38 +01:00