This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 463083f doc: Standalone Stub Generation Doc (#427)
463083f is described below
commit 463083f9fdc7abeef3d0e0db22d4f27d5cf8e5ef
Author: Junru Shao <[email protected]>
AuthorDate: Thu Feb 5 09:36:04 2026 -0800
doc: Standalone Stub Generation Doc (#427)
---
docs/conf.py | 5 +-
docs/index.rst | 1 +
docs/packaging/python_packaging.rst | 186 +----------------
docs/packaging/stubgen.rst | 400 ++++++++++++++++++++++++++++++++++++
python/tvm_ffi/stub/consts.py | 2 +-
5 files changed, 417 insertions(+), 177 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
index a575ee0..8812dd1 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -93,7 +93,10 @@ PREDEFINED += TVM_FFI_DLL= TVM_FFI_DLL_EXPORT=
TVM_FFI_INLINE= \
__cplusplus=201703
EXCLUDE_SYMBOLS += *details* *TypeTraits* std \
*use_default_type_traits_v* *is_optional_type_v*
*operator* \
-EXCLUDE_PATTERNS += *details.h
+ TVM_FFI_LOG_EXCEPTION_CALL_BEGIN
TVM_FFI_LOG_EXCEPTION_CALL_END \
+ TVM_FFI_CLEAR_PTR_PADDING_IN_FFI_ANY
TVM_FFI_STATIC_INIT_BLOCK \
+ TVM_FFI_STATIC_INIT_BLOCK_DEF_
TVM_FFI_DEFINE_DEFAULT_COPY_MOVE_AND_ASSIGN
+EXCLUDE_PATTERNS += *details.h *internal*
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
WARNINGS = YES
diff --git a/docs/index.rst b/docs/index.rst
index 31c5efe..530c2ac 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -69,6 +69,7 @@ Table of Contents
:caption: Packaging
packaging/python_packaging.rst
+ packaging/stubgen.rst
packaging/cpp_tooling.rst
packaging/cpp_packaging.md
diff --git a/docs/packaging/python_packaging.rst
b/docs/packaging/python_packaging.rst
index c8c4b52..3fd1335 100644
--- a/docs/packaging/python_packaging.rst
+++ b/docs/packaging/python_packaging.rst
@@ -340,106 +340,14 @@ makes it easy to expose:
def sum(self) -> int: ...
-.. _sec-stubgen:
+Stub Generation
+---------------
-Stub Generation Tool
---------------------
-
-TVM-FFI comes with a command-line tool ``tvm-ffi-stubgen`` that automates
-the generation of type stubs for both global functions and classes.
-It turns reflection metadata into proper Python type hints, and generates
-corresponding Python code **inline** and **statically**.
-
-Inline Directives
-~~~~~~~~~~~~~~~~~
-
-Like linter tools, ``tvm-ffi-stubgen`` uses special comments
-to identify what to generate and where to write generated code.
-
-**Directive 1 (Global functions)**. The example below shows a directive
-``global/${prefix}`` that marks a type stub section for global functions.
-
-.. code-block:: python
-
- # tvm-ffi-stubgen(begin): global/my_ext.arith
- tvm_ffi.init_ffi_api("my_ext.arith", __name__)
- if TYPE_CHECKING:
- def add_one(_0: int, /) -> int: ...
- def add_two(_0: int, /) -> int: ...
- def add_three(_0: int, /) -> int: ...
- # tvm-ffi-stubgen(end)
-
-Running ``tvm-ffi-stubgen`` fills in the function stubs between the
-``begin`` and ``end`` markers based on the loaded registry, and in this case
-adds all the global functions named ``my_ext.arith.*``.
-
-**Directive 2 (Classes)**. The example below shows a directive
-``object/${type_key}`` that marks the fields and methods of a registered class.
-
-.. code-block:: python
-
- @tvm_ffi.register_object("my_ffi_extension.IntPair")
- class IntPair(_ffi_Object):
- # tvm-ffi-stubgen(begin): object/my_ffi_extension.IntPair
- a: int
- b: int
- if TYPE_CHECKING:
- def __init__(self, a: int, b: int) -> None: ...
- def sum(self) -> int: ...
- # tvm-ffi-stubgen(end)
-
-Directive-based Generation
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-After the TVM-FFI extension is built as a shared library, for example at
-``build/libmy_ffi_extension.so``:
-
-**Command line tool**. The command below generates stubs for
-the package located at ``python/my_ffi_extension``, updating
-all sections marked by directives.
-
-.. code-block:: bash
-
- tvm-ffi-stubgen \
- python/my_ffi_extension \
- --dlls build/libmy_ffi_extension.so \
-
-
-**CMake Integration**. CMake function ``tvm_ffi_configure_target``
-is integrated with this command and can be used to keep stubs up to date
-every time the target is built.
-
-.. code-block:: cmake
-
- tvm_ffi_configure_target(my_ffi_extension
- STUB_DIR "python"
- )
-
-Inside the function, CMake derives the proper ``--dlls`` arguments
-via ``$<TARGET_FILE:${target}>``.
-
-Scaffold Missing Directives
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-**Command line tool**. Beyond updating existing directives, ``tvm-ffi-stubgen``
-can scaffold missing directives with a few extra flags.
-
-.. code-block:: bash
+TVM-FFI provides a stub generation tool ``tvm-ffi-stubgen`` that creates
Python type hints
+from C++ reflection metadata. The tool integrates with CMake and can generate
complete
+stub files automatically, or update existing files using special directive
comments.
- tvm-ffi-stubgen \
- python/my_ffi_extension \
- --dlls build/libmy_ffi_extension.so \
- --init-pypkg my-ffi-extension \
- --init-lib my_ffi_extension \
- --init-prefix "my_ffi_extension." \
-
-- ``--init-pypkg <pypkg>``: Specifies the name of the Python package to
initialize, e.g. ``apache-tvm-ffi``, ``my-ffi-extension``;
-- ``--init-lib <libtarget>``: Specifies the name of the CMake target (shared
library) to load for reflection metadata;
-- ``--init-prefix <prefix>``: Specifies the registry prefix to include for
stub generation, e.g. ``my_ffi_extension.``. If global function or class names
start with this prefix, they will be included in the generated stubs.
-
-**CMake Integration**. CMake function ``tvm_ffi_configure_target``
-also supports scaffolding missing directives via the ``STUB_INIT``,
``STUB_PKG``,
-and ``STUB_PREFIX`` options.
+For most projects, enable automatic stub generation in CMake:
.. code-block:: cmake
@@ -448,82 +356,10 @@ and ``STUB_PREFIX`` options.
STUB_INIT ON
)
-The ``STUB_INIT`` option instructs CMake to scaffold missing directives
-based on the target and package information already specified.
-
-Other Directives
-~~~~~~~~~~~~~~~~
-
-All supported directives are documented via:
-
-.. code-block:: bash
-
- tvm-ffi-stubgen --help
-
-
-It includes:
-
-**Directive 3 (Import section)**. It populates all the imported names used by
generated stubs. Example:
-
-.. code-block:: python
-
- # tvm-ffi-stubgen(begin): import-section
- from __future__ import annotations
- from ..registry import init_ffi_api as _FFI_INIT_FUNC
- from typing import TYPE_CHECKING
- if TYPE_CHECKING:
- from collections.abc import Mapping, Sequence
- from tvm_ffi import Device, Object, Tensor, dtype
- from tvm_ffi.testing import TestIntPair
- from typing import Any, Callable
- # tvm-ffi-stubgen(end)
-
-**Directive 4 (Export)**. It re-exports names defined in `_ffi_api.__all__`
into the current file, usually
-in ``__init__.py`` to aggregate exported names. Example:
-
-.. code-block:: python
-
- # tvm-ffi-stubgen(begin): export/_ffi_api
- from ._ffi_api import * # noqa: F403
- from ._ffi_api import __all__ as _ffi_api__all__
- if "__all__" not in globals():
- __all__ = []
- __all__.extend(_ffi_api__all__)
- # tvm-ffi-stubgen(end)
-
-**Directive 5 (__all__)**. It populates the ``__all__`` variable with all
generated
-classes and functions, as well as ``LIB`` if present. It's usually placed at
the end of
-``_ffi_api.py``. Example:
-
-.. code-block:: python
-
- __all__ = [
- # tvm-ffi-stubgen(begin): __all__
- "LIB",
- "IntPair",
- "raise_error",
- # tvm-ffi-stubgen(end)
- ]
-
-**Directive 6 (ty-map)**. It maps the type key of a class to Python types used
in generation. Example:
-
-.. code-block:: python
-
- # tvm-ffi-stubgen(ty-map): ffi.reflection.AccessStep ->
ffi.access_path.AccessStep
-
-means the class with type key ``ffi.reflection.AccessStep`` is mapped to
``ffi.access_path.AccessStep``
-in Python.
-
-**Directive 7 (Import object)**. It injects a custom import into generated
code, optionally
-TYPE_CHECKING-only. Example:
-
-
-.. code-block:: python
-
- # tvm-ffi-stubgen(import-object): ffi.Object;False;_ffi_Object
+This generates ``_ffi_api.py`` and ``__init__.py`` files with proper type
hints for all
+registered global functions and classes.
-imports ``ffi.Object`` as ``_ffi_Object`` for use in generated code,
-where the second field ``False`` indicates the import is not
TYPE_CHECKING-only.
+.. seealso::
-**Directive 8 (Skip file)**. It prevents the stub generation tool from
modifying the file.
-This is useful when the file contains custom code that should not be altered.
+ :doc:`stubgen` for the complete stub generation guide, including
directive-based
+ customization and command-line usage.
diff --git a/docs/packaging/stubgen.rst b/docs/packaging/stubgen.rst
new file mode 100644
index 0000000..789ae28
--- /dev/null
+++ b/docs/packaging/stubgen.rst
@@ -0,0 +1,400 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+.. _sec-stubgen:
+
+Stub Generation
+===============
+
+TVM-FFI provides ``tvm-ffi-stubgen``, a tool that generates Python type stubs
from C++
+reflection metadata. It turns registered global functions and classes into
proper Python
+type hints, enabling IDE auto-completion and static type checking.
+
+.. admonition:: Prerequisite
+ :class: hint
+
+ This tutorial uses `examples/python_packaging
<https://github.com/apache/tvm-ffi/tree/main/examples/python_packaging>`_
+ as a running example. The package registers global functions
(``my_ffi_extension.add_one``,
+ ``my_ffi_extension.raise_error``) and an object type
(``my_ffi_extension.IntPair``).
+
+.. _sec-stubgen-cmake:
+
+CMake-based Generation
+----------------------
+
+The recommended approach is to use CMake's ``tvm_ffi_configure_target``
function.
+This runs stub generation automatically after each build.
+
+.. code-block:: cmake
+
+ tvm_ffi_configure_target(<target>
+ STUB_DIR <dir>
+ [STUB_INIT ON|OFF]
+ [STUB_PKG <pkg>]
+ [STUB_PREFIX <prefix>]
+ )
+
+From the example's
+`CMakeLists.txt
<https://github.com/apache/tvm-ffi/blob/main/examples/python_packaging/CMakeLists.txt>`_:
+
+.. code-block:: cmake
+
+ add_library(my_ffi_extension SHARED src/extension.cc)
+ tvm_ffi_configure_target(my_ffi_extension STUB_DIR "./python" STUB_INIT ON)
+
+STUB_DIR
+ Required. Directory containing Python source files, relative to
``CMAKE_CURRENT_SOURCE_DIR``.
+
+STUB_INIT (default: ``OFF``)
+ Optional. When ``OFF``, fills in existing inline directives without creating
+ new ones. When ``ON``, generates new files and directives.
+
+``STUB_INIT`` is ``OFF``
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+The tool fills in existing inline directives only. It scans files for
directive markers
+and regenerates the content within those blocks:
+
+- Class fields and methods (``object/<type_key>``)
+
+ .. code-block:: python
+
+ @tvm_ffi.register_object("my_ffi_extension.IntPair")
+ class IntPair(tvm_ffi.Object):
+ # tvm-ffi-stubgen(begin): object/my_ffi_extension.IntPair
+ a: int
+ b: int
+ if TYPE_CHECKING:
+ @staticmethod
+ def __c_ffi_init__(_0: int, _1: int, /) -> Object: ...
+ def sum(self, /) -> int: ...
+ # tvm-ffi-stubgen(end)
+
+- Global functions (``global/<prefix>``)
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(begin): global/my_ffi_extension
+ tvm_ffi.init_ffi_api("my_ffi_extension", __name__)
+ if TYPE_CHECKING:
+ def add_one(_0: int, /) -> int: ...
+ def raise_error(_0: str, /) -> None: ...
+ # tvm-ffi-stubgen(end)
+
+- Import sections, ``__all__`` lists, and exports (``import-section``,
``__all__``, ``export/<module>``)
+
+See :ref:`sec-stubgen-directives` for a complete reference of all inline
directives.
+
+.. important::
+
+ No new files or directives are created. Use this mode after you have
customized the
+ generated files and want to preserve your changes.
+
+``STUB_INIT`` is ``ON``
+~~~~~~~~~~~~~~~~~~~~~~~
+
+The tool generates new files (``_ffi_api.py``, ``__init__.py``) and new
directives from
+scratch. This is the recommended starting point for new projects. Two
additional options
+are available:
+
+STUB_PKG (default: ``SKBUILD_PROJECT_NAME`` or CMake target name)
+ Python package name. Determines the directory structure
(``<STUB_DIR>/<STUB_PKG>/``) and
+ generates the library loading code in ``_ffi_api.py``:
+
+ .. code-block:: python
+
+ LIB = tvm_ffi.libinfo.load_lib_module("<STUB_PKG>",
"<cmake-target-name>")
+
+ This uses :py:func:`tvm_ffi.libinfo.load_lib_module` to load the shared
library.
+
+STUB_PREFIX (default: ``<STUB_PKG>.``)
+ Registry prefix filter. Only functions and classes whose registered names
start with this
+ prefix are included. The tool generates:
+
+ - ``global/<prefix>`` directives for global functions. In
``<STUB_DIR>/<STUB_PKG>/_ffi_api.py``:
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(begin): global/<STUB_PREFIX>
+ ...
+ # tvm-ffi-stubgen(end)
+
+ Functions in nested namespaces (e.g., ``<STUB_PREFIX>submodule.func``)
are placed in
+ corresponding subdirectories.
+
+ - ``object/<type_key>`` directives for each registered class. For a class
with type key
+ ``<STUB_PREFIX>IntPair``:
+
+ .. code-block:: python
+
+ @tvm_ffi.register_object("<STUB_PREFIX>IntPair")
+ class IntPair(_ffi_Object):
+ # tvm-ffi-stubgen(begin): object/<STUB_PREFIX>IntPair
+ a: int
+ b: int
+ if TYPE_CHECKING:
+ @staticmethod
+ def __c_ffi_init__(_0: int, _1: int, /) -> Object: ...
+ def sum(self, /) -> int: ...
+ # tvm-ffi-stubgen(end)
+
+ Classes in nested namespaces are similarly placed in corresponding
subdirectories.
+
+
+.. _sec-stubgen-cli:
+
+CLI-based Generation
+--------------------
+
+For standalone usage or custom build systems, use the command-line tool
directly.
+The CLI options correspond to CMake options as follows:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 30 30 40
+
+ * - CLI Option
+ - CMake Option
+ - Description
+ * - ``<directory>``
+ - ``STUB_DIR``
+ - Directory containing Python source files
+ * - ``--init-*`` flags
+ - ``STUB_INIT ON``
+ - Presence of ``--init-*`` flags enables full generation
+ * - ``--init-pypkg``
+ - ``STUB_PKG``
+ - Python package name
+ * - ``--init-prefix``
+ - ``STUB_PREFIX``
+ - Registry prefix filter
+ * - ``--init-lib``
+ - *(target name)*
+ - CMake target name for the shared library
+
+Basic Usage
+~~~~~~~~~~~
+
+To generate complete stub files from scratch (equivalent to ``STUB_INIT ON``):
+
+.. code-block:: bash
+
+ tvm-ffi-stubgen \
+ python/my_ffi_extension \
+ --dlls build/libmy_ffi_extension.so \
+ --init-pypkg my-ffi-extension \
+ --init-lib my_ffi_extension \
+ --init-prefix "my_ffi_extension."
+
+To update only existing directives (equivalent to ``STUB_INIT OFF``), omit the
``--init-*`` flags:
+
+.. code-block:: bash
+
+ tvm-ffi-stubgen \
+ python/my_ffi_extension \
+ --dlls build/libmy_ffi_extension.so
+
+Command Line Options
+~~~~~~~~~~~~~~~~~~~~
+
+**Required arguments:**
+
+``<directory>`` (positional)
+ Directory to generate/update Python stubs. Corresponds to ``STUB_DIR``.
+
+``--dlls``
+ Shared libraries to load during generation. The stub generator loads these
DLLs to
+ discover registered global functions and object types. Without loading the
DLLs, the
+ generator cannot know what functions and classes exist. Use semicolons to
separate
+ multiple libraries.
+
+**Arguments for full generation** (corresponds to ``STUB_INIT ON``):
+
+All three are required together. When omitted, the tool operates in
directive-only mode
+(equivalent to ``STUB_INIT OFF``).
+
+``--init-pypkg``
+ Python package name. Corresponds to ``STUB_PKG``.
+
+``--init-lib``
+ CMake target name. Used as the second argument in the generated
+ ``load_lib_module("<STUB_PKG>", "<init-lib>")`` call.
+
+``--init-prefix``
+ Registry prefix filter. Corresponds to ``STUB_PREFIX``.
+
+**Optional arguments:**
+
+``--verbose``
+ Print a unified diff of changes to each file.
+
+``--dry-run``
+ Preview changes without writing to files.
+
+``--imports``
+ Additional Python modules to import before generation (semicolon-separated).
+
+``--indent``
+ Indentation width for generated code (default: 4).
+
+For a complete list of options, run ``tvm-ffi-stubgen --help``.
+
+.. _sec-stubgen-advanced:
+
+Advanced Topics
+---------------
+
+.. _sec-stubgen-directives:
+
+Inline Directives
+~~~~~~~~~~~~~~~~~
+
+Inline directives are special comments that mark regions where the tool should
generate
+or update code. They allow you to:
+
+- Add custom code alongside generated stubs
+- Control the exact placement of generated code
+- Maintain hand-written documentation or additional methods
+- Integrate with existing codebases
+
+The tool preserves content outside directive blocks, only modifying marked
regions.
+
+**Directive Syntax**
+
+Directives use comment markers to delimit generated regions:
+
+.. code-block:: python
+
+ # tvm-ffi-stubgen(begin): <directive-type>/<parameter>
+ # ... generated code appears here ...
+ # tvm-ffi-stubgen(end)
+
+When you run the tool, it:
+
+1. Parses files for directive markers
+2. Regenerates content between ``begin`` and ``end`` markers
+3. Preserves everything outside the markers
+
+**Directive Reference**
+
+``global/<prefix>`` - Global Functions
+ Marks a section for global function stubs. All functions registered with
the given
+ prefix are included.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(begin): global/my_ext.arith
+ # fmt: off
+ tvm_ffi.init_ffi_api("my_ext.arith", __name__)
+ if TYPE_CHECKING:
+ def add_one(_0: int, /) -> int: ...
+ def add_two(_0: int, /) -> int: ...
+ # fmt: on
+ # tvm-ffi-stubgen(end)
+
+``object/<type_key>`` - Object Types
+ Marks a section for class field and method stubs. Place inside a class
definition
+ decorated with ``@tvm_ffi.register_object``.
+
+ .. code-block:: python
+
+ @tvm_ffi.register_object("my_ffi_extension.IntPair")
+ class IntPair(_ffi_Object):
+ # tvm-ffi-stubgen(begin): object/my_ffi_extension.IntPair
+ # fmt: off
+ a: int
+ b: int
+ if TYPE_CHECKING:
+ def __init__(self, a: int, b: int) -> None: ...
+ def sum(self) -> int: ...
+ # fmt: on
+ # tvm-ffi-stubgen(end)
+
+``import-section`` - Import Section
+ Populates import statements for types used in generated stubs. The tool
automatically
+ determines which imports are needed.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(begin): import-section
+ # fmt: off
+ # isort: off
+ from __future__ import annotations
+ from tvm_ffi import init_ffi_api as _FFI_INIT_FUNC
+ from typing import TYPE_CHECKING
+ if TYPE_CHECKING:
+ from collections.abc import Mapping, Sequence
+ from tvm_ffi import Device, Object, Tensor, dtype
+ # isort: on
+ # fmt: on
+ # tvm-ffi-stubgen(end)
+
+``export/<module>`` - Export
+ Re-exports names from a submodule, typically used in ``__init__.py`` to
aggregate exports.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(begin): export/_ffi_api
+ # fmt: off
+ # isort: off
+ from ._ffi_api import * # noqa: F403
+ from ._ffi_api import __all__ as _ffi_api__all__
+ if "__all__" not in globals():
+ __all__ = []
+ __all__.extend(_ffi_api__all__)
+ # isort: on
+ # fmt: on
+ # tvm-ffi-stubgen(end)
+
+``__all__`` - Export List
+ Populates the ``__all__`` list with all generated class and function names.
+
+ .. code-block:: python
+
+ __all__ = [
+ # tvm-ffi-stubgen(begin): __all__
+ "LIB",
+ "IntPair",
+ "add_one",
+ # tvm-ffi-stubgen(end)
+ ]
+
+``ty-map`` - Type Mapping
+ Maps a C++ type key to a different Python type path. This is useful when
the Python
+ class is defined in a different module than the type key suggests.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(ty-map): ffi.reflection.AccessStep ->
ffi.access_path.AccessStep
+
+``import-object`` - Import Object
+ Injects a custom import into generated code. The format is
+ ``<full_name>;<type_checking_only>;<alias>``.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(import-object): ffi.Object;False;_ffi_Object
+
+ This imports ``ffi.Object`` as ``_ffi_Object`` for use in generated code.
The second
+ field (``False``) indicates the import is not TYPE_CHECKING-only.
+
+``skip-file`` - Skip File
+ Prevents the tool from modifying the file. Place anywhere in the file.
+
+ .. code-block:: python
+
+ # tvm-ffi-stubgen(skip-file)
diff --git a/python/tvm_ffi/stub/consts.py b/python/tvm_ffi/stub/consts.py
index 2beb388..d8b17bc 100644
--- a/python/tvm_ffi/stub/consts.py
+++ b/python/tvm_ffi/stub/consts.py
@@ -47,7 +47,7 @@ TERM_BLUE = "\033[34m"
TERM_MAGENTA = "\033[35m"
TERM_CYAN = "\033[36m"
TERM_WHITE = "\033[37m"
-DOC_URL =
"https://tvm.apache.org/ffi/packaging/python_packaging.html#stub-generation-tool"
+DOC_URL = "https://tvm.apache.org/ffi/packaging/stubgen.html"
DEFAULT_SOURCE_EXTS = {".py", ".pyi"}
TY_MAP_DEFAULTS = {