Skip to content

Commit 3aabd2c

Browse files
nicknick
authored andcommitted
version 1.2 - additional exports & CLI functionality
1 parent 95fa1cf commit 3aabd2c

29 files changed

Lines changed: 4838 additions & 45 deletions

‎.github/workflows/python-tests.yml‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# Runs the Python test suite (tests/python) added in v1.2.
2+
#
3+
# The existing cmake-multi-platform.yml workflow covers the C++ suite; this one
4+
# covers the Python package that wraps it — the export layer, the CLI, and the
5+
# backward-compatibility regression tests.
6+
7+
name: Python tests
8+
9+
on:
10+
push:
11+
branches: [ "main" ]
12+
pull_request:
13+
branches: [ "main" ]
14+
15+
jobs:
16+
# ── Full suite, with every optional dependency installed ────────────────────
17+
test:
18+
name: ${{ matrix.os }} / Python ${{ matrix.python-version }}
19+
runs-on: ${{ matrix.os }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os: [ubuntu-latest, windows-latest, macos-latest]
24+
python-version: ["3.11", "3.13"]
25+
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- uses: actions/setup-python@v5
30+
with:
31+
python-version: ${{ matrix.python-version }}
32+
33+
# Windows uses the vendored zlib header; macOS uses the system SDK.
34+
- name: Install zlib (Linux)
35+
if: runner.os == 'Linux'
36+
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
37+
38+
- name: Install package with all extras
39+
run: |
40+
python -m pip install --upgrade pip
41+
pip install ".[all]" pytest pytest-cov
42+
43+
- name: Run tests
44+
run: pytest -v --cov=docx_comment_parser --cov-report=term-missing
45+
46+
# ── Minimum supported Python, with NO optional dependencies ────────────────
47+
# Guards two promises at once: the package still imports and works on 3.9,
48+
# and the base install really does need nothing else. Tests that require
49+
# pandas, polars or typer skip themselves via pytest.importorskip.
50+
test-minimal:
51+
name: Python 3.9, zero dependencies
52+
runs-on: ubuntu-latest
53+
54+
steps:
55+
- uses: actions/checkout@v4
56+
57+
- uses: actions/setup-python@v5
58+
with:
59+
python-version: "3.9"
60+
61+
- run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
62+
63+
- name: Install without extras
64+
run: |
65+
python -m pip install --upgrade pip
66+
pip install .
67+
pip install pytest
68+
69+
- name: Confirm nothing extra was pulled in
70+
run: |
71+
! pip show pandas > /dev/null 2>&1
72+
! pip show polars > /dev/null 2>&1
73+
! pip show typer > /dev/null 2>&1
74+
echo "core install is dependency-free"
75+
76+
- name: Run tests
77+
run: pytest -v
78+
79+
# ── Static typing ──────────────────────────────────────────────────────────
80+
typecheck:
81+
name: mypy --strict
82+
runs-on: ubuntu-latest
83+
84+
steps:
85+
- uses: actions/checkout@v4
86+
87+
- uses: actions/setup-python@v5
88+
with:
89+
python-version: "3.13"
90+
91+
- run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
92+
93+
- name: Install package and type checker
94+
run: |
95+
python -m pip install --upgrade pip
96+
pip install ".[all]" mypy
97+
98+
- name: Type check
99+
run: mypy

‎.gitignore‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,22 @@ BUILDING.md
1111
/build_mingw64
1212
/build_msvc
1313
/dist
14-
/out
14+
/out
15+
16+
# ── Python ───────────────────────────────────────────────────────────────────
17+
__pycache__/
18+
*.py[cod]
19+
*.egg-info/
20+
.pytest_cache/
21+
.mypy_cache/
22+
.coverage
23+
coverage.xml
24+
htmlcov/
25+
26+
# The compiled extension is built in place, next to the pure-Python package
27+
# (see LIBRARY_OUTPUT_DIRECTORY for docx_py_ext in CMakeLists.txt). The .py
28+
# sources in that directory ARE tracked; only the build output is not.
29+
/src/docx_comment_parser/*.pyd
30+
/src/docx_comment_parser/*.so
31+
/src/docx_comment_parser/*.dll
32+
/src/docx_comment_parser/*.dylib

‎CMakeLists.txt‎

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,37 @@ if(DEFINED SKBUILD)
104104
endif()
105105

106106
# Single module — all sources compiled in, no runtime DLL dependency.
107-
pybind11_add_module(docx_comment_parser MODULE
107+
#
108+
# The CMake target is called docx_core, but OUTPUT_NAME makes the built file
109+
# "_core.pyd" / "_core.so" so Python finds PyInit__core, matching
110+
# PYBIND11_MODULE(_core, m) in python_bindings.cpp. It is installed *inside*
111+
# the docx_comment_parser package directory, next to the pure-Python sources
112+
# collected via [tool.scikit-build] wheel.packages.
113+
pybind11_add_module(docx_core MODULE
108114
python/python_bindings.cpp
109115
src/docx_parser.cpp
110116
src/batch_parser.cpp
111117
src/zip_reader.cpp
112118
src/xml_parser.cpp
113119
)
114120

121+
set_target_properties(docx_core PROPERTIES OUTPUT_NAME "_core")
122+
115123
# DOCX_BUILDING_DLL is correct here: the extension IS building the symbols
116124
# (not importing them from a separate DLL).
117-
target_compile_definitions(docx_comment_parser PRIVATE DOCX_BUILDING_DLL)
125+
target_compile_definitions(docx_core PRIVATE DOCX_BUILDING_DLL)
118126

119-
target_include_directories(docx_comment_parser PRIVATE
127+
target_include_directories(docx_core PRIVATE
120128
${CMAKE_CURRENT_SOURCE_DIR}/include
121129
# Vendored zlib.h for MSVC (activated inside zip_reader.cpp)
122130
$<$<CXX_COMPILER_ID:MSVC>:${CMAKE_CURRENT_SOURCE_DIR}/vendor>
123131
)
124132

125-
docx_apply_compile_options(docx_comment_parser)
126-
docx_link_platform_libs(docx_comment_parser)
133+
docx_apply_compile_options(docx_core)
134+
docx_link_platform_libs(docx_core)
127135

128-
install(TARGETS docx_comment_parser
129-
DESTINATION "${SKBUILD_PLATLIB_DIR}"
136+
install(TARGETS docx_core
137+
DESTINATION "${SKBUILD_PLATLIB_DIR}/docx_comment_parser"
130138
)
131139

132140
# Nothing else to build in wheel mode — skip the rest of this file.
@@ -202,19 +210,37 @@ if(BUILD_PYTHON_BINDINGS)
202210

203211
if(pybind11_FOUND)
204212
# Distinct CMake target name avoids collision with the SHARED library
205-
# target above. OUTPUT_NAME makes the file "docx_comment_parser.pyd"
206-
# so Python's import machinery finds PyInit_docx_comment_parser.
207-
# (The old name "_docx_comment_parser" caused an ImportError because
208-
# Python looked for PyInit__docx_comment_parser — note the double
209-
# underscore — which never existed.)
213+
# target above. OUTPUT_NAME makes the file "_core.pyd" / "_core.so" so
214+
# Python's import machinery finds PyInit__core, matching
215+
# PYBIND11_MODULE(_core, m) in python_bindings.cpp.
216+
#
217+
# The extension is a *submodule* of the docx_comment_parser package, so
218+
# it is written straight into src/docx_comment_parser/ — that makes an
219+
# in-place CMake build immediately importable via PYTHONPATH=src, and
220+
# the install rule below drops it beside the installed package.
210221
pybind11_add_module(docx_py_ext MODULE
211222
python/python_bindings.cpp
212223
)
213224

225+
set(_docx_pkg_dir "${CMAKE_CURRENT_SOURCE_DIR}/src/docx_comment_parser")
226+
214227
set_target_properties(docx_py_ext PROPERTIES
215-
OUTPUT_NAME "docx_comment_parser"
228+
OUTPUT_NAME "_core"
229+
LIBRARY_OUTPUT_DIRECTORY "${_docx_pkg_dir}"
230+
RUNTIME_OUTPUT_DIRECTORY "${_docx_pkg_dir}"
216231
)
217232

233+
# Multi-config generators (Visual Studio, Xcode) append a per-config
234+
# subdirectory to *_OUTPUT_DIRECTORY unless the per-config variant is
235+
# set explicitly. Without this the .pyd lands in
236+
# src/docx_comment_parser/Release/ and is not importable in place.
237+
foreach(_cfg IN ITEMS DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
238+
set_target_properties(docx_py_ext PROPERTIES
239+
LIBRARY_OUTPUT_DIRECTORY_${_cfg} "${_docx_pkg_dir}"
240+
RUNTIME_OUTPUT_DIRECTORY_${_cfg} "${_docx_pkg_dir}"
241+
)
242+
endforeach()
243+
218244
# Do NOT define DOCX_BUILDING_DLL: this extension links against the
219245
# shared library above, so DOCX_API must expand to __declspec(dllimport).
220246
target_include_directories(docx_py_ext PRIVATE
@@ -231,9 +257,18 @@ if(BUILD_PYTHON_BINDINGS)
231257
target_link_libraries(docx_py_ext PRIVATE -lpthread)
232258
endif()
233259

260+
# Install the extension *and* the pure-Python package that wraps it, so
261+
# a plain `cmake --install` yields a working `import docx_comment_parser`.
234262
install(TARGETS docx_py_ext
235-
LIBRARY DESTINATION "${Python3_SITEARCH}"
236-
RUNTIME DESTINATION "${Python3_SITEARCH}"
263+
LIBRARY DESTINATION "${Python3_SITEARCH}/docx_comment_parser"
264+
RUNTIME DESTINATION "${Python3_SITEARCH}/docx_comment_parser"
265+
)
266+
267+
install(DIRECTORY "${_docx_pkg_dir}/"
268+
DESTINATION "${Python3_SITEARCH}/docx_comment_parser"
269+
FILES_MATCHING
270+
PATTERN "*.py"
271+
PATTERN "py.typed"
237272
)
238273
else()
239274
message(WARNING

‎MANIFEST.in‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ recursive-include include *.h
22
recursive-include vendor *.h *.c
33
recursive-include src *.cpp *.h
44
recursive-include python *.cpp
5+
6+
# Pure-Python wrapper package (exporters, filters, CLI) and its type markers.
7+
recursive-include src/docx_comment_parser *.py *.pyi
8+
include src/docx_comment_parser/py.typed

0 commit comments

Comments
 (0)