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)
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)
- 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)
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)
Remove the Python 2 C API compatibility macros from pyhead.swg (PyClass_Check,
PyInt_*, PyString_*, Py_TPFLAGS_HAVE_CLASS, _PyLong_FromSsize_t) and the SWIG
string helper macros SWIG_Python_str_FromFormat and SWIG_Python_str_FromChar.
These mapped Python 2 names onto the Python 3 C API and were kept only for user
typemaps, which should now call the Python 3 C API directly.
SWIG_Python_str_FromChar was still used internally, so its call sites now use
PyUnicode_FromString directly.
Also remove the dead SWIG_PYTHON_SLOW_GETSET_THIS "fast getset" code in
pyrun.swg. That macro was unconditionally defined for Python 3, so the guarded
Python 2 paths (using PyInstance_Check) were never compiled; only the slow
getset path is kept.
Update the python_annotations_typing test typemap, which used PyString_Check
and PyString_AsString, to use PyBytes_Check and PyBytes_AsString.
Assisted-by: Claude Code (Opus 4.8)