office-stencil 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- office_stencil-0.1.0/.github/dependabot.yml +20 -0
- office_stencil-0.1.0/.github/workflows/publish.yml +48 -0
- office_stencil-0.1.0/.gitignore +225 -0
- office_stencil-0.1.0/LICENSE +21 -0
- office_stencil-0.1.0/PKG-INFO +242 -0
- office_stencil-0.1.0/README.md +211 -0
- office_stencil-0.1.0/examples/README.md +67 -0
- office_stencil-0.1.0/examples/data/invoice.json +24 -0
- office_stencil-0.1.0/examples/data/status-report.json +29 -0
- office_stencil-0.1.0/examples/scripts/create_styled_status_template.py +115 -0
- office_stencil-0.1.0/examples/templates/invoice.docx +0 -0
- office_stencil-0.1.0/examples/templates/styled-status-report.docx +0 -0
- office_stencil-0.1.0/pyproject.toml +84 -0
- office_stencil-0.1.0/src/stencil/__init__.py +7 -0
- office_stencil-0.1.0/src/stencil/_version.py +3 -0
- office_stencil-0.1.0/src/stencil/cli.py +56 -0
- office_stencil-0.1.0/src/stencil/docx.py +55 -0
- office_stencil-0.1.0/src/stencil/errors.py +36 -0
- office_stencil-0.1.0/src/stencil/render.py +44 -0
- office_stencil-0.1.0/tests/.gitkeep +1 -0
- office_stencil-0.1.0/tests/test_render.py +21 -0
- office_stencil-0.1.0/uv.lock +1332 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
updates:
|
|
4
|
+
- package-ecosystem: "uv"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
open-pull-requests-limit: 5
|
|
9
|
+
commit-message:
|
|
10
|
+
prefix: "deps"
|
|
11
|
+
include: "scope"
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: "github-actions"
|
|
14
|
+
directory: "/"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: "weekly"
|
|
17
|
+
open-pull-requests-limit: 5
|
|
18
|
+
commit-message:
|
|
19
|
+
prefix: "ci"
|
|
20
|
+
include: "scope"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
name: Test, build, and publish
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Check out repository
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: uv sync --dev
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: uv run pytest
|
|
37
|
+
|
|
38
|
+
- name: Run lint
|
|
39
|
+
run: uv run ruff check .
|
|
40
|
+
|
|
41
|
+
- name: Build package
|
|
42
|
+
run: uv build
|
|
43
|
+
|
|
44
|
+
- name: Publish to PyPI
|
|
45
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
46
|
+
with:
|
|
47
|
+
user: __token__
|
|
48
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
.cache/
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py.cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
# Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# UV
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
# uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
# poetry.lock
|
|
110
|
+
# poetry.toml
|
|
111
|
+
|
|
112
|
+
# pdm
|
|
113
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
114
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
115
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
116
|
+
# pdm.lock
|
|
117
|
+
# pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# pixi
|
|
122
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
123
|
+
# pixi.lock
|
|
124
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
125
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
126
|
+
.pixi
|
|
127
|
+
|
|
128
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
129
|
+
__pypackages__/
|
|
130
|
+
|
|
131
|
+
# Celery stuff
|
|
132
|
+
celerybeat-schedule
|
|
133
|
+
celerybeat.pid
|
|
134
|
+
|
|
135
|
+
# Redis
|
|
136
|
+
*.rdb
|
|
137
|
+
*.aof
|
|
138
|
+
*.pid
|
|
139
|
+
|
|
140
|
+
# RabbitMQ
|
|
141
|
+
mnesia/
|
|
142
|
+
rabbitmq/
|
|
143
|
+
rabbitmq-data/
|
|
144
|
+
|
|
145
|
+
# ActiveMQ
|
|
146
|
+
activemq-data/
|
|
147
|
+
|
|
148
|
+
# SageMath parsed files
|
|
149
|
+
*.sage.py
|
|
150
|
+
|
|
151
|
+
# Environments
|
|
152
|
+
.env
|
|
153
|
+
.envrc
|
|
154
|
+
.venv
|
|
155
|
+
env/
|
|
156
|
+
venv/
|
|
157
|
+
ENV/
|
|
158
|
+
env.bak/
|
|
159
|
+
venv.bak/
|
|
160
|
+
|
|
161
|
+
# Spyder project settings
|
|
162
|
+
.spyderproject
|
|
163
|
+
.spyproject
|
|
164
|
+
|
|
165
|
+
# Rope project settings
|
|
166
|
+
.ropeproject
|
|
167
|
+
|
|
168
|
+
# mkdocs documentation
|
|
169
|
+
/site
|
|
170
|
+
|
|
171
|
+
# mypy
|
|
172
|
+
.mypy_cache/
|
|
173
|
+
.dmypy.json
|
|
174
|
+
dmypy.json
|
|
175
|
+
|
|
176
|
+
# Pyre type checker
|
|
177
|
+
.pyre/
|
|
178
|
+
|
|
179
|
+
# pytype static type analyzer
|
|
180
|
+
.pytype/
|
|
181
|
+
|
|
182
|
+
# Cython debug symbols
|
|
183
|
+
cython_debug/
|
|
184
|
+
|
|
185
|
+
# PyCharm
|
|
186
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
187
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
188
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
189
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
190
|
+
# .idea/
|
|
191
|
+
|
|
192
|
+
# Abstra
|
|
193
|
+
# Abstra is an AI-powered process automation framework.
|
|
194
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
195
|
+
# Learn more at https://abstra.io/docs
|
|
196
|
+
.abstra/
|
|
197
|
+
|
|
198
|
+
# Visual Studio Code
|
|
199
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
200
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
201
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
202
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
203
|
+
# .vscode/
|
|
204
|
+
# Temporary file for partial code execution
|
|
205
|
+
tempCodeRunnerFile.py
|
|
206
|
+
|
|
207
|
+
# Ruff stuff:
|
|
208
|
+
.ruff_cache/
|
|
209
|
+
|
|
210
|
+
# PyPI configuration file
|
|
211
|
+
.pypirc
|
|
212
|
+
|
|
213
|
+
# Marimo
|
|
214
|
+
marimo/_static/
|
|
215
|
+
marimo/_lsp/
|
|
216
|
+
__marimo__/
|
|
217
|
+
|
|
218
|
+
# Streamlit
|
|
219
|
+
.streamlit/secrets.toml
|
|
220
|
+
|
|
221
|
+
# Local development docs and planning notes
|
|
222
|
+
docs/
|
|
223
|
+
|
|
224
|
+
# Rendered example outputs
|
|
225
|
+
examples/out/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bin Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: office-stencil
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A reusable Office document template render engine for DOCX, XLSX, and PPTX.
|
|
5
|
+
Author: Bin Zhang
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: documents,docx,office,pptx,stencil,templates,xlsx
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Office/Business
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Requires-Dist: docxtpl>=0.16.8
|
|
19
|
+
Requires-Dist: jinja2>=3.1.0
|
|
20
|
+
Requires-Dist: lxml>=5.0.0
|
|
21
|
+
Requires-Dist: openpyxl>=3.1.0
|
|
22
|
+
Requires-Dist: typer>=0.12.0
|
|
23
|
+
Provides-Extra: api
|
|
24
|
+
Requires-Dist: fastapi>=0.115.0; extra == 'api'
|
|
25
|
+
Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'api'
|
|
26
|
+
Provides-Extra: workers
|
|
27
|
+
Requires-Dist: celery>=5.4.0; extra == 'workers'
|
|
28
|
+
Requires-Dist: redis>=5.0.0; extra == 'workers'
|
|
29
|
+
Requires-Dist: rq>=2.0.0; extra == 'workers'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# Stencil
|
|
33
|
+
|
|
34
|
+
Stencil is a Python render engine for reusable Office document templates.
|
|
35
|
+
|
|
36
|
+
It is designed for workflows where a `.docx`, `.xlsx`, or `.pptx` template contains placeholders, loops, and conditionals, and application data fills the template to produce a finished Office document or PDF.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
stencil render invoice.docx data.json --output invoice.pdf
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The project starts with DOCX because it is the fastest path to a useful internal tool. XLSX and PPTX are planned as separate format engines behind the same public API.
|
|
43
|
+
|
|
44
|
+
## Status
|
|
45
|
+
|
|
46
|
+
Pre-alpha DOCX MVP.
|
|
47
|
+
|
|
48
|
+
What exists now:
|
|
49
|
+
|
|
50
|
+
- `uv`-compatible Python package metadata
|
|
51
|
+
- Python `>=3.12`
|
|
52
|
+
- `stencil` import package
|
|
53
|
+
- `render()` API for DOCX templates
|
|
54
|
+
- `stencil render` CLI command for JSON data
|
|
55
|
+
- DOCX-first roadmap in local ignored docs
|
|
56
|
+
|
|
57
|
+
What does not exist yet:
|
|
58
|
+
|
|
59
|
+
- Stable public API
|
|
60
|
+
- PDF conversion worker
|
|
61
|
+
- XLSX rendering
|
|
62
|
+
- PPTX rendering
|
|
63
|
+
|
|
64
|
+
## Naming
|
|
65
|
+
|
|
66
|
+
The product, import package, and CLI command are named `stencil`.
|
|
67
|
+
|
|
68
|
+
The PyPI distribution name is currently `office-stencil` because `stencil` is already taken on PyPI by an old package.
|
|
69
|
+
|
|
70
|
+
```toml
|
|
71
|
+
[project]
|
|
72
|
+
name = "office-stencil"
|
|
73
|
+
|
|
74
|
+
[project.scripts]
|
|
75
|
+
stencil = "stencil.cli:app"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from stencil import render
|
|
82
|
+
|
|
83
|
+
document = render(
|
|
84
|
+
template_path="invoice.docx",
|
|
85
|
+
data={
|
|
86
|
+
"customer": "Acme",
|
|
87
|
+
"line_items": [
|
|
88
|
+
{"description": "Implementation", "amount": 1200},
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
output_format="docx",
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Phase 1 supports DOCX input and DOCX output only. The exact API may change while the project is pre-alpha.
|
|
96
|
+
|
|
97
|
+
## CLI
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
stencil render invoice.docx data.json --output invoice.docx
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The CLI reads a top-level JSON object from the data file and writes the rendered document bytes to the output path.
|
|
104
|
+
|
|
105
|
+
PDF output is planned for a later phase.
|
|
106
|
+
|
|
107
|
+
## Example
|
|
108
|
+
|
|
109
|
+
See [examples/](examples/) for a runnable DOCX example with:
|
|
110
|
+
|
|
111
|
+
- `examples/templates/invoice.docx`
|
|
112
|
+
- `examples/data/invoice.json`
|
|
113
|
+
- `examples/templates/styled-status-report.docx`
|
|
114
|
+
- `examples/data/status-report.json`
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv sync --dev
|
|
118
|
+
uv run stencil render examples/templates/invoice.docx examples/data/invoice.json --output examples/out/invoice.docx
|
|
119
|
+
uv run stencil render examples/templates/styled-status-report.docx examples/data/status-report.json --output examples/out/styled-status-report.docx
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Roadmap
|
|
123
|
+
|
|
124
|
+
1. Foundation and proof of shape
|
|
125
|
+
2. DOCX MVP
|
|
126
|
+
3. PDF conversion and reliability
|
|
127
|
+
4. Internal package polish
|
|
128
|
+
5. XLSX engine
|
|
129
|
+
6. PPTX engine
|
|
130
|
+
7. Optional service layer and operations
|
|
131
|
+
|
|
132
|
+
The detailed local roadmap lives in `docs/`, which is intentionally ignored by git because it is personal planning material.
|
|
133
|
+
|
|
134
|
+
## Dependencies
|
|
135
|
+
|
|
136
|
+
Runtime dependencies:
|
|
137
|
+
|
|
138
|
+
- `jinja2`: template expressions, conditionals, and loops
|
|
139
|
+
- `docxtpl`: DOCX rendering for the first production milestone
|
|
140
|
+
- `lxml`: Office XML parsing and manipulation
|
|
141
|
+
- `openpyxl`: XLSX workbook inspection and future spreadsheet support
|
|
142
|
+
- `typer`: CLI framework
|
|
143
|
+
|
|
144
|
+
Optional API dependencies:
|
|
145
|
+
|
|
146
|
+
- `fastapi`: future HTTP service layer
|
|
147
|
+
- `uvicorn[standard]`: ASGI server for the optional API
|
|
148
|
+
|
|
149
|
+
Optional worker dependencies:
|
|
150
|
+
|
|
151
|
+
- `rq`: lightweight Redis-backed queue option
|
|
152
|
+
- `celery`: larger distributed worker option
|
|
153
|
+
- `redis`: queue/backend client
|
|
154
|
+
|
|
155
|
+
Development dependencies:
|
|
156
|
+
|
|
157
|
+
- `pytest`: test runner
|
|
158
|
+
- `pytest-cov`: coverage reporting
|
|
159
|
+
- `ruff`: linting and import sorting
|
|
160
|
+
- `mypy`: static type checking
|
|
161
|
+
|
|
162
|
+
External system dependency:
|
|
163
|
+
|
|
164
|
+
- LibreOffice / `soffice`: planned conversion engine for PDF output
|
|
165
|
+
|
|
166
|
+
## Local Development
|
|
167
|
+
|
|
168
|
+
This repo uses `uv`, but dependencies have not been installed yet.
|
|
169
|
+
|
|
170
|
+
Install dependencies when ready:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
uv sync --dev
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Run tests:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
uv run pytest
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Run linting:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
uv run ruff check .
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Run type checking:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
uv run mypy
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Publishing
|
|
195
|
+
|
|
196
|
+
The repo includes a GitHub Actions workflow at `.github/workflows/publish.yml`.
|
|
197
|
+
|
|
198
|
+
It publishes to PyPI when changes land on `main`:
|
|
199
|
+
|
|
200
|
+
1. Check out the repo.
|
|
201
|
+
2. Install `uv`.
|
|
202
|
+
3. Set up Python 3.12.
|
|
203
|
+
4. Install dependencies with `uv sync --dev`.
|
|
204
|
+
5. Run tests with `uv run pytest`.
|
|
205
|
+
6. Run linting with `uv run ruff check .`.
|
|
206
|
+
7. Build the package with `uv build`.
|
|
207
|
+
8. Publish with the `PYPI_API_TOKEN` GitHub Actions secret.
|
|
208
|
+
|
|
209
|
+
One-time PyPI token setup:
|
|
210
|
+
|
|
211
|
+
- Create or use the PyPI project named `office-stencil`.
|
|
212
|
+
- Create a PyPI API token. Prefer a project-scoped token for `office-stencil` after the first release exists.
|
|
213
|
+
- Add the token to GitHub as an Actions secret named `PYPI_API_TOKEN`.
|
|
214
|
+
- The publish workflow uses `user: __token__` and `password: ${{ secrets.PYPI_API_TOKEN }}`.
|
|
215
|
+
|
|
216
|
+
PyPI requires every upload to have a new version. Before merging a feature branch into `main`, update the `version` in `pyproject.toml`.
|
|
217
|
+
|
|
218
|
+
## Dependabot
|
|
219
|
+
|
|
220
|
+
The repo includes `.github/dependabot.yml`.
|
|
221
|
+
|
|
222
|
+
Dependabot checks weekly for:
|
|
223
|
+
|
|
224
|
+
- `uv` dependency updates from `pyproject.toml`
|
|
225
|
+
- GitHub Actions updates from `.github/workflows/`
|
|
226
|
+
|
|
227
|
+
Dependabot will open pull requests. Those PRs should go through the same CI and branch-protection rules as feature branches.
|
|
228
|
+
|
|
229
|
+
## Design Notes
|
|
230
|
+
|
|
231
|
+
Office files are zipped XML packages, but each format has different failure modes.
|
|
232
|
+
|
|
233
|
+
- DOCX can split template tags across Word text runs.
|
|
234
|
+
- XLSX often stores text in shared strings, so naive replacement can mutate unrelated cells.
|
|
235
|
+
- PPTX repeated slides require cloning slide XML, relationships, presentation ordering, and content type entries.
|
|
236
|
+
- PDF conversion through LibreOffice needs timeouts, retries, cleanup, and worker isolation.
|
|
237
|
+
|
|
238
|
+
Stencil should stay milestone-driven: ship a reliable DOCX engine first, then use real needs to decide when XLSX, PPTX, and service infrastructure are worth the extra complexity.
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
MIT. See [LICENSE](LICENSE).
|