Skip to content

Commit dc0dd2f

Browse files
authored
doc: Improve python packaging doc (#355)
Made a few changes: - Migrate `examples/packaging` -> `examples/python_packaging` - Add a prerequisite section - Use `literalinclude` to include code from `examples/python_packaging` to the tutorial - Point to Python package docs in `tvm-ffi-stubgen` helper messages
1 parent 9268e67 commit dc0dd2f

File tree

12 files changed

+175
-389
lines changed

12 files changed

+175
-389
lines changed

.github/workflows/ci_mainline_only.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,25 +163,25 @@ jobs:
163163
cd examples\stable_c_abi
164164
call run_all.bat
165165
166-
- name: Run example/packaging [posix]
166+
- name: Run example/python_packaging [posix]
167167
if: ${{ runner.os != 'Windows' }}
168168
env:
169169
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
170170
run: |
171-
pushd examples/packaging
171+
pushd examples/python_packaging
172172
# This directory will be auto-generated in `CMakeLists.txt` by setting `STUB_INIT ON`
173173
rm -rf python/my_ffi_extension
174174
uv pip install --verbose . --no-build-isolation
175175
python run_example.py
176176
popd
177177
178-
- name: Run example/packaging [windows]
178+
- name: Run example/python_packaging [windows]
179179
if: ${{ runner.os == 'Windows' }}
180180
shell: pwsh
181181
env:
182182
CMAKE_BUILD_PARALLEL_LEVEL: ${{ steps.env_vars.outputs.cpu_count }}
183183
run: |
184-
Set-Location examples/packaging
184+
Set-Location examples/python_packaging
185185
Remove-Item -Recurse -Force python/my_ffi_extension
186186
uv pip install --verbose . --no-build-isolation
187187
python run_example.py

docs/packaging/python_packaging.rst

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ internals. We will cover three checkpoints:
2727
- Build Python wheel;
2828
- Automatic Python package generation tools.
2929

30+
.. note::
31+
32+
All code used in this guide lives under
33+
`examples/python_packaging <https://github.com/apache/tvm-ffi/tree/main/examples/python_packaging>`_.
34+
35+
.. admonition:: Prerequisite
36+
:class: hint
37+
38+
- Python: 3.9 or newer (for the ``tvm_ffi.config``/``tvm-ffi-config`` helpers)
39+
- Compiler: C11-capable toolchain (GCC/Clang/MSVC)
40+
- TVM-FFI installed via
41+
42+
.. code-block:: bash
43+
44+
pip install --reinstall --upgrade apache-tvm-ffi
45+
46+
3047
Export C++ to Python
3148
--------------------
3249

@@ -53,13 +70,10 @@ C symbols are easier to call into.
5370
Macro :c:macro:`TVM_FFI_DLL_EXPORT_TYPED_FUNC` exports the function ``AddTwo`` as
5471
a C symbol ``__tvm_ffi_add_two`` inside the shared library.
5572

56-
.. code-block:: cpp
57-
58-
static int AddTwo(int x) {
59-
return x + 2;
60-
}
61-
62-
TVM_FFI_DLL_EXPORT_TYPED_FUNC(add_two, AddTwo);
73+
.. literalinclude:: ../../examples/python_packaging/src/extension.cc
74+
:language: cpp
75+
:start-after: [tvm_ffi_abi.begin]
76+
:end-before: [tvm_ffi_abi.end]
6377

6478
.. group-tab:: Python (User)
6579

@@ -97,17 +111,10 @@ It registry handles type translation, error handling, and metadata.
97111
C++ function ``AddOne`` is registered with name ``my_ffi_extension.add_one``
98112
in the global registry using :cpp:class:`tvm::ffi::reflection::GlobalDef`.
99113

100-
.. code-block:: cpp
101-
102-
static int AddOne(int x) {
103-
return x + 1;
104-
}
105-
106-
TVM_FFI_STATIC_INIT_BLOCK() {
107-
namespace refl = tvm::ffi::reflection;
108-
refl::GlobalDef()
109-
.def("my_ffi_extension.add_one", AddOne);
110-
}
114+
.. literalinclude:: ../../examples/python_packaging/src/extension.cc
115+
:language: cpp
116+
:start-after: [global_function.begin]
117+
:end-before: [global_function.end]
111118

112119
.. group-tab:: Python (User)
113120

@@ -166,33 +173,10 @@ makes it easy to expose:
166173
- a constructor, and
167174
- a method ``Sum`` that returns the sum of the two fields.
168175

169-
.. code-block:: cpp
170-
171-
class IntPairObj : public ffi::Object {
172-
public:
173-
int64_t a;
174-
int64_t b;
175-
IntPairObj(int64_t a, int64_t b) : a(a), b(b) {}
176-
177-
int64_t Sum() const {
178-
return a + b;
179-
}
180-
181-
TVM_FFI_DECLARE_OBJECT_INFO_FINAL(
182-
/*type_key=*/"my_ffi_extension.IntPair",
183-
/*class=*/IntPairObj,
184-
/*parent_class=*/ffi::Object
185-
);
186-
};
187-
188-
TVM_FFI_STATIC_INIT_BLOCK() {
189-
namespace refl = tvm::ffi::reflection;
190-
refl::ObjectDef<IntPairObj>()
191-
.def(refl::init<int64_t, int64_t>())
192-
.def_rw("a", &IntPairObj::a, "the first field")
193-
.def_rw("b", &IntPairObj::b, "the second field")
194-
.def("sum", &IntPairObj::Sum, "IntPairObj::Sum() method");
195-
}
176+
.. literalinclude:: ../../examples/python_packaging/src/extension.cc
177+
:language: cpp
178+
:start-after: [object.begin]
179+
:end-before: [object.end]
196180

197181
.. group-tab:: Python (User)
198182

@@ -238,15 +222,14 @@ CMake Target
238222
Assume the source tree contains ``src/extension.cc``. Create a ``CMakeLists.txt`` that
239223
creates a shared target ``my_ffi_extension`` and configures it against TVM-FFI.
240224

241-
.. code-block:: cmake
242-
243-
add_library(my_ffi_extension SHARED src/extension.cc)
244-
tvm_ffi_configure_target(my_ffi_extension STUB_DIR "./python")
245-
install(TARGETS my_ffi_extension DESTINATION .)
246-
tvm_ffi_install(my_ffi_extension DESTINATION .)
225+
.. literalinclude:: ../../examples/python_packaging/CMakeLists.txt
226+
:language: cmake
227+
:start-after: [example.cmake.begin]
228+
:end-before: [example.cmake.end]
247229

248230
Function ``tvm_ffi_configure_target`` sets up TVM-FFI include paths, link against TVM-FFI library,
249-
generates stubs under the specified directory, and optionally debug symbols.
231+
generates stubs under ``STUB_DIR``, and can scaffold stub files when ``STUB_INIT`` is
232+
enabled.
250233

251234
Function ``tvm_ffi_install`` places necessary information, e.g. debug symbols in macOS, next to
252235
the shared library for proper packaging.
@@ -261,19 +244,10 @@ Define a :pep:`517` build backend in ``pyproject.toml``, with the following step
261244
- Specify the source directory of the package via ``wheel.packages``, and the installation
262245
destination via ``wheel.install-dir``.
263246

264-
.. code-block:: toml
265-
266-
[build-system]
267-
requires = ["scikit-build-core>=0.10.0", "apache-tvm-ffi"]
268-
build-backend = "scikit_build_core.build"
269-
270-
[tool.scikit-build]
271-
# The wheel is Python ABI-agnostic
272-
wheel.py-api = "py3"
273-
# The package contains the Python module at `python/my_ffi_extension`
274-
wheel.packages = ["python/my_ffi_extension"]
275-
# The install dir matches the import name
276-
wheel.install-dir = "my_ffi_extension"
247+
.. literalinclude:: ../../examples/python_packaging/pyproject.toml
248+
:language: toml
249+
:start-after: [pyproject.build.begin]
250+
:end-before: [pyproject.build.end]
277251

278252
Once fully specified, scikit-build-core will invoke CMake and drive the extension building process.
279253

examples/packaging/src/extension.cc

Lines changed: 0 additions & 131 deletions
This file was deleted.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ find_package(
2323
REQUIRED
2424
)
2525
find_package(tvm_ffi CONFIG REQUIRED)
26+
# [example.cmake.begin]
2627
add_library(my_ffi_extension SHARED src/extension.cc)
2728
tvm_ffi_configure_target(my_ffi_extension STUB_DIR "./python" STUB_INIT ON)
2829
install(TARGETS my_ffi_extension DESTINATION .)
29-
tvm_ffi_install(my_ffi_extension)
30+
tvm_ffi_install(my_ffi_extension DESTINATION .)
31+
# [example.cmake.end]
Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ packaging as well.
3131
Use `uv pip` (the same tooling used in CI) to build and install the example wheel:
3232

3333
```bash
34-
cd examples/packaging
34+
cd examples/python_packaging
3535
uv pip install --reinstall --verbose .
3636
```
3737

@@ -42,20 +42,12 @@ Note: When running the auditwheel process, make sure to skip
4242

4343
## Run the example
4444

45-
After installing the `my_ffi_extension` example package, you can run the following example
46-
that invokes the `add_one` function exposed.
45+
After installing the `my_ffi_extension` example package, you can run the following example.
4746

4847
```bash
4948
python run_example.py
5049
```
5150

52-
This runs three flows: calling `add_one`, demonstrating `raise_error` with a propagated traceback, and constructing/using the `IntPair` object.
53-
54-
When possible, tvm_ffi will try to preserve backtrace across language boundary. You will see output like
55-
56-
```text
57-
File "src/extension.cc", line 45, in void my_ffi_extension::RaiseError(tvm::ffi::String)
58-
```
59-
60-
If you are in an IDE like VSCode, you can click and jump to the C++ lines of error when
61-
the debug symbols are preserved.
51+
This runs four flows: calling `add_two` via the TVM-FFI ABI, calling `add_one` via the global
52+
registry, calling `raise_error` to demonstrate error propagation, and constructing/using the
53+
`IntPair` object.
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,19 @@ requires-python = ">=3.9"
3333

3434
dependencies = ["apache-tvm-ffi"]
3535

36+
# [pyproject.build.begin]
3637
[build-system]
3738
requires = ["scikit-build-core>=0.10.0", "apache-tvm-ffi"]
3839
build-backend = "scikit_build_core.build"
3940

4041
[tool.scikit-build]
41-
# the wheel is abi agnostic
42+
# The wheel is Python ABI-agnostic
4243
wheel.py-api = "py3"
43-
minimum-version = "build-system.requires"
44+
# The package contains the Python module at `python/my_ffi_extension`
45+
wheel.packages = ["python/my_ffi_extension"]
46+
# The install dir matches the import name
47+
wheel.install-dir = "my_ffi_extension"
48+
# [pyproject.build.end]
4449

4550
# Build configuration
4651
build-dir = "build"
@@ -52,7 +57,4 @@ cmake.build-type = "RelWithDebInfo"
5257

5358
# Logging
5459
logging.level = "INFO"
55-
56-
# Wheel configuration
57-
wheel.packages = ["python/my_ffi_extension"]
58-
wheel.install-dir = "my_ffi_extension"
60+
minimum-version = "build-system.requires"

examples/packaging/python/my_ffi_extension/__init__.py renamed to examples/python_packaging/python/my_ffi_extension/__init__.py

File renamed without changes.

0 commit comments

Comments
 (0)