docxtpl-lint 1.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.
- docxtpl_lint-1.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +31 -0
- docxtpl_lint-1.1.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- docxtpl_lint-1.1.0/.github/ISSUE_TEMPLATE/rule_request.yml +20 -0
- docxtpl_lint-1.1.0/.github/pull_request_template.md +3 -0
- docxtpl_lint-1.1.0/.github/workflows/ci.yml +84 -0
- docxtpl_lint-1.1.0/.github/workflows/pages.yml +39 -0
- docxtpl_lint-1.1.0/.github/workflows/publish-pypi.yml +45 -0
- docxtpl_lint-1.1.0/.gitignore +9 -0
- docxtpl_lint-1.1.0/.pre-commit-hooks.yaml +7 -0
- docxtpl_lint-1.1.0/CHANGELOG.md +100 -0
- docxtpl_lint-1.1.0/CONTRIBUTING.md +75 -0
- docxtpl_lint-1.1.0/LICENSE +21 -0
- docxtpl_lint-1.1.0/PKG-INFO +257 -0
- docxtpl_lint-1.1.0/PLAN.md +46 -0
- docxtpl_lint-1.1.0/README.md +228 -0
- docxtpl_lint-1.1.0/README.zh.md +219 -0
- docxtpl_lint-1.1.0/action.yml +46 -0
- docxtpl_lint-1.1.0/benchmark/adversarial_adjudicated.json +114 -0
- docxtpl_lint-1.1.0/benchmark/expected.json +18 -0
- docxtpl_lint-1.1.0/docs/PLAN-v0.2.md +34 -0
- docxtpl_lint-1.1.0/docs/PLAN-v1.0.md +29 -0
- docxtpl_lint-1.1.0/docs/PLAN-v1.1.md +37 -0
- docxtpl_lint-1.1.0/docs/decisions.md +107 -0
- docxtpl_lint-1.1.0/docs/deepseek-review.md +954 -0
- docxtpl_lint-1.1.0/docs/demo-zh.svg +271 -0
- docxtpl_lint-1.1.0/docs/demo.svg +279 -0
- docxtpl_lint-1.1.0/docs/eval/adversarial_cases.json +88 -0
- docxtpl_lint-1.1.0/docs/eval/adversarial_results_v1.0.0.json +995 -0
- docxtpl_lint-1.1.0/docs/eval/deepseek-eval.md +59 -0
- docxtpl_lint-1.1.0/docs/eval/self-eval-transcript-v1.0.0.txt +387 -0
- docxtpl_lint-1.1.0/docs/eval/self-eval-transcript-v1.1.0.txt +135 -0
- docxtpl_lint-1.1.0/docs/eval/self-eval.md +69 -0
- docxtpl_lint-1.1.0/docs/playground.png +0 -0
- docxtpl_lint-1.1.0/docs/rules.md +471 -0
- docxtpl_lint-1.1.0/examples/clean.data.json +12 -0
- docxtpl_lint-1.1.0/examples/clean.docx +0 -0
- docxtpl_lint-1.1.0/examples/issue378_unclosed.docx +0 -0
- docxtpl_lint-1.1.0/examples/issue603_table_loop.docx +0 -0
- docxtpl_lint-1.1.0/examples/issue644_percent.docx +0 -0
- docxtpl_lint-1.1.0/pyproject.toml +48 -0
- docxtpl_lint-1.1.0/scripts/adversarial.py +114 -0
- docxtpl_lint-1.1.0/scripts/benchmark.py +112 -0
- docxtpl_lint-1.1.0/scripts/build_site.sh +24 -0
- docxtpl_lint-1.1.0/scripts/make_demo.py +42 -0
- docxtpl_lint-1.1.0/scripts/pyodide_smoke.mjs +57 -0
- docxtpl_lint-1.1.0/scripts/realworld.py +105 -0
- docxtpl_lint-1.1.0/site/index.html +115 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/__init__.py +6 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/__main__.py +3 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/cli.py +411 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/config.py +77 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/contract.py +298 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/core.py +866 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/fixes.py +187 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/messages.py +203 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/reader.py +123 -0
- docxtpl_lint-1.1.0/src/docxtpl_lint/verify.py +70 -0
- docxtpl_lint-1.1.0/tests/conftest.py +12 -0
- docxtpl_lint-1.1.0/tests/make_fixtures.py +104 -0
- docxtpl_lint-1.1.0/tests/rule_cases.py +184 -0
- docxtpl_lint-1.1.0/tests/test_docxtpl_behaviour.py +35 -0
- docxtpl_lint-1.1.0/tests/test_reader.py +122 -0
- docxtpl_lint-1.1.0/tests/test_rules.py +285 -0
- docxtpl_lint-1.1.0/tests/test_v02.py +128 -0
- docxtpl_lint-1.1.0/tests/test_v1.py +230 -0
- docxtpl_lint-1.1.0/tests/test_v11.py +79 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Bug report / false positive
|
|
2
|
+
description: docxtpl-lint reported something wrong, missed a problem, or crashed
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: dropdown
|
|
6
|
+
id: kind
|
|
7
|
+
attributes:
|
|
8
|
+
label: Kind
|
|
9
|
+
options: [False positive, Missed problem, Crash, Other]
|
|
10
|
+
validations: { required: true }
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: output
|
|
13
|
+
attributes:
|
|
14
|
+
label: docxtpl-lint output
|
|
15
|
+
description: Paste the command and its output (`--no-color`).
|
|
16
|
+
render: text
|
|
17
|
+
validations: { required: true }
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: docxtpl
|
|
20
|
+
attributes:
|
|
21
|
+
label: What docxtpl does with this template
|
|
22
|
+
description: Does `DocxTemplate(...).render(context)` raise, or is the output wrong? Paste the error if any.
|
|
23
|
+
- type: input
|
|
24
|
+
id: versions
|
|
25
|
+
attributes:
|
|
26
|
+
label: Versions
|
|
27
|
+
placeholder: docxtpl-lint 0.1.0, docxtpl 0.20.2, Python 3.12
|
|
28
|
+
validations: { required: true }
|
|
29
|
+
- type: markdown
|
|
30
|
+
attributes:
|
|
31
|
+
value: Please attach a **minimal .docx** that reproduces the problem (remove confidential content). Without one we usually cannot confirm the behaviour.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Rule request
|
|
2
|
+
description: Propose a new deterministic check
|
|
3
|
+
labels: [rule-request]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Template mistake
|
|
9
|
+
description: What do people write in the .docx, and what goes wrong?
|
|
10
|
+
validations: { required: true }
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: behaviour
|
|
13
|
+
attributes:
|
|
14
|
+
label: What docxtpl does
|
|
15
|
+
description: Error message, or how the rendered output is wrong. Link upstream issues if any.
|
|
16
|
+
validations: { required: true }
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: detect
|
|
19
|
+
attributes:
|
|
20
|
+
label: How it could be detected without false positives
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
pull_request:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: "0 3 * * 1" # weekly: catch docxtpl upstream changes
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ${{ matrix.os }}
|
|
10
|
+
strategy:
|
|
11
|
+
fail-fast: false
|
|
12
|
+
matrix:
|
|
13
|
+
os: [ubuntu-latest]
|
|
14
|
+
python: ["3.10", "3.11", "3.12", "3.13"]
|
|
15
|
+
include:
|
|
16
|
+
- os: windows-latest
|
|
17
|
+
python: "3.12"
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v7
|
|
20
|
+
- uses: actions/setup-python@v7
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python }}
|
|
23
|
+
- run: pip install -e ".[dev]"
|
|
24
|
+
- run: ruff check . && ruff format --check .
|
|
25
|
+
- run: pytest -q
|
|
26
|
+
docxtpl-upstream:
|
|
27
|
+
# Ground truth against the newest docxtpl release and upstream master.
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
continue-on-error: true
|
|
30
|
+
strategy:
|
|
31
|
+
matrix:
|
|
32
|
+
docxtpl: ["docxtpl", "git+https://github.com/elapouya/python-docx-template.git"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v7
|
|
35
|
+
- uses: actions/setup-python@v7
|
|
36
|
+
with:
|
|
37
|
+
python-version: "3.12"
|
|
38
|
+
- run: pip install -e ".[dev]" && pip install --upgrade "${{ matrix.docxtpl }}"
|
|
39
|
+
- run: pytest -q tests/test_docxtpl_behaviour.py
|
|
40
|
+
benchmark:
|
|
41
|
+
# 0 false positives on python-docx-template's own test templates (benchmark/expected.json).
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v7
|
|
45
|
+
- uses: actions/setup-python@v7
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.12"
|
|
48
|
+
- run: pip install -e .[render]
|
|
49
|
+
- run: git clone --depth 1 https://github.com/elapouya/python-docx-template /tmp/docxtpl
|
|
50
|
+
- run: python scripts/benchmark.py /tmp/docxtpl/tests/templates --json benchmark-result.json
|
|
51
|
+
- run: cat benchmark-result.json >> "$GITHUB_STEP_SUMMARY"
|
|
52
|
+
if: always()
|
|
53
|
+
playground:
|
|
54
|
+
# The wheel must keep working inside Pyodide (browser playground).
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v7
|
|
58
|
+
- uses: actions/setup-python@v7
|
|
59
|
+
with:
|
|
60
|
+
python-version: "3.12"
|
|
61
|
+
- uses: actions/setup-node@v7
|
|
62
|
+
with:
|
|
63
|
+
node-version: "24"
|
|
64
|
+
- run: pip install build && python -m build --wheel && pip install dist/*.whl && bash scripts/build_site.sh
|
|
65
|
+
- run: npm install --no-save pyodide@314.0.7 && node scripts/pyodide_smoke.mjs _site
|
|
66
|
+
action:
|
|
67
|
+
# Dogfood the composite action on the bundled examples (path with a space included).
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v7
|
|
71
|
+
- run: mkdir -p "tmp dir" && cp examples/clean.docx "tmp dir/clean copy.docx"
|
|
72
|
+
- uses: ./
|
|
73
|
+
with:
|
|
74
|
+
files: |
|
|
75
|
+
examples/clean.docx
|
|
76
|
+
tmp dir/*.docx
|
|
77
|
+
build:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v7
|
|
81
|
+
- uses: actions/setup-python@v7
|
|
82
|
+
with:
|
|
83
|
+
python-version: "3.12"
|
|
84
|
+
- run: pip install build twine && python -m build && twine check dist/*
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Playground (GitHub Pages)
|
|
2
|
+
# Deploys site/ + the current wheel to GitHub Pages.
|
|
3
|
+
# Needs: Settings → Pages → Source: "GitHub Actions". Runs on pushes to main
|
|
4
|
+
# that touch the package or the site, or by hand. (A `release` trigger runs on the
|
|
5
|
+
# tag ref, which the github-pages environment rejects by default.)
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths: ["src/**", "site/**", "scripts/build_site.sh", "examples/**", "pyproject.toml"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pages: write
|
|
14
|
+
id-token: write
|
|
15
|
+
concurrency:
|
|
16
|
+
group: pages
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
- uses: actions/setup-python@v7
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
- run: pip install build && python -m build --wheel && pip install dist/*.whl && bash scripts/build_site.sh
|
|
27
|
+
- uses: actions/configure-pages@v6
|
|
28
|
+
- uses: actions/upload-pages-artifact@v5
|
|
29
|
+
with:
|
|
30
|
+
path: _site
|
|
31
|
+
deploy:
|
|
32
|
+
needs: build
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
environment:
|
|
35
|
+
name: github-pages
|
|
36
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
37
|
+
steps:
|
|
38
|
+
- id: deployment
|
|
39
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
# PyPI Trusted Publishing (OIDC, no token stored in GitHub).
|
|
3
|
+
# Prepared but deliberately manual: it only runs when started from the Actions
|
|
4
|
+
# tab ("Run workflow") with the tag to publish. One-time setup on pypi.org:
|
|
5
|
+
# Publishing → Add a pending publisher → owner yq6666-66, repo docxtpl-lint,
|
|
6
|
+
# workflow publish-pypi.yml, environment pypi.
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
inputs:
|
|
10
|
+
tag:
|
|
11
|
+
description: Tag to publish, e.g. v0.2.0
|
|
12
|
+
required: true
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
with:
|
|
19
|
+
ref: ${{ inputs.tag }}
|
|
20
|
+
- uses: actions/setup-python@v7
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
- run: pip install build twine && python -m build && twine check dist/*
|
|
24
|
+
- name: Version must match tag
|
|
25
|
+
run: |
|
|
26
|
+
v=$(python -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
27
|
+
test "v$v" = "${{ inputs.tag }}"
|
|
28
|
+
- uses: actions/upload-artifact@v7
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
publish:
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
environment:
|
|
36
|
+
name: pypi
|
|
37
|
+
url: https://pypi.org/project/docxtpl-lint/
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v8
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.2
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.1.0] - 2026-10-16
|
|
10
|
+
|
|
11
|
+
Found by an adversarial run (86 cases designed by DeepSeek, disputes settled by rendering with docxtpl) and a
|
|
12
|
+
self-evaluation from a fresh install (docs/eval/).
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- DTL210: `{%p %}` block opened and closed in the same paragraph (docxtpl raises).
|
|
16
|
+
- `--explain RULE`.
|
|
17
|
+
- `fix` suggests the missing `}` of a `{{ … }` tag.
|
|
18
|
+
- `scripts/adversarial.py` + `benchmark/adversarial_adjudicated.json`: the 86 cases run in the test-suite.
|
|
19
|
+
- `scripts/realworld.py`: invoice / Chinese contract / broken / 3000-paragraph templates used by the self-evaluation.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- `}}` or `%}` inside a string literal no longer ends the tag (false DTL103).
|
|
23
|
+
- A tag missing its closing brace no longer swallows the following paragraphs (it was only a DTL002 warning and
|
|
24
|
+
hid later errors); it is reported as DTL001 and scanning continues at the next tag.
|
|
25
|
+
- Unclosed `{% raw %}` is reported (DTL101).
|
|
26
|
+
- DTL209: comments are not counted as lost text; text in other cells of a `{%tr %}` row and in other paragraphs of a
|
|
27
|
+
`{%tc %}` cell is now checked; no DTL209 next to DTL201/202/203 or a same-paragraph `{%p %}` pair.
|
|
28
|
+
- `--data` / `--schema` JSON with a UTF-8 BOM (Windows Notepad) is accepted.
|
|
29
|
+
- `infer` / `verify` / `diff` / `fix` exit with code 2 and a one-line message on unreadable input instead of a traceback.
|
|
30
|
+
- Playground: the 1.0.0 page failed to load (escaped newlines in the embedded Python); the Pyodide smoke test now runs
|
|
31
|
+
the page's own code. A hint appears if loading takes over 30 s.
|
|
32
|
+
- `fix` diff headers: no `a//tmp/...` double slash.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- `infer` writes `x-docxtpl-templates` only when several templates are merged.
|
|
36
|
+
- Clearer `--data` help text.
|
|
37
|
+
|
|
38
|
+
## [1.0.0] - 2026-10-16
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- `docxtpl-lint infer`: data contract of one or more templates as JSON Schema (2020-12) plus sample data; lists from
|
|
42
|
+
`for` loops, numbers from `round`/`int`/`float`/`abs`, arithmetic and numeric comparisons, objects from `.items()`,
|
|
43
|
+
optional fields from `is defined`/`default`; merged schemas list the templates using each field
|
|
44
|
+
(`x-docxtpl-templates`).
|
|
45
|
+
- `docxtpl-lint verify` (optional extra `[render]`): render with the real docxtpl and sentinel data; DTL401 = rendered
|
|
46
|
+
value silently missing from the output, DTL402 = render fails.
|
|
47
|
+
- `docxtpl-lint diff old.docx new.docx [--fail-on-breaking]`: data contract changes between template versions,
|
|
48
|
+
with rename hints.
|
|
49
|
+
- `--schema schema.json`: DTL304 (variable not declared), DTL305 (loop over a non-iterable type).
|
|
50
|
+
- `docxtpl-lint fix`: fix suggestions as unified diffs of the paragraph text for DTL202, DTL203, DTL204, DTL102 (stray tag
|
|
51
|
+
or misspelt opening tag), DTL103 (unknown tag) and DTL301 (with `--data`); conservative matching (≤ 2 edits, no ties).
|
|
52
|
+
- DTL209: text next to a `{%p %}`/`{%tr %}`/`{%tc %}` tag is silently dropped by docxtpl (found with `verify`).
|
|
53
|
+
- Playground: fix suggestions, inferred JSON Schema and sample data.
|
|
54
|
+
- Benchmark runs `verify` on docxtpl's 40 templates (37 render with inferred data, 0 false DTL401).
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- Pages deploy runs on pushes to `main` (the `release` trigger ran on the tag ref, which the `github-pages` environment
|
|
58
|
+
rejects).
|
|
59
|
+
- Development status: Production/Stable.
|
|
60
|
+
|
|
61
|
+
## [0.2.0] - 2026-10-16
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
- Browser playground (Pyodide, runs fully client-side), deployed to GitHub Pages by `pages.yml`.
|
|
65
|
+
- `[tool.docxtpl-lint]` configuration in `pyproject.toml` (`--config`, `--no-config`); unknown keys are errors.
|
|
66
|
+
- `--context N`: show neighbouring paragraphs next to each issue (default 1 on a terminal, 0 otherwise).
|
|
67
|
+
- DTL208: `cellbg`/`colspan`/`vm`/`hm` tag outside a table cell.
|
|
68
|
+
- `docs/rules.md` with one anchor per rule; `help_url` in JSON output and `helpUri` in SARIF.
|
|
69
|
+
- Benchmark against docxtpl's own 40 test templates (`scripts/benchmark.py`, CI job); stale allow-list entries fail it.
|
|
70
|
+
- Manual-only PyPI Trusted Publishing workflow (`publish-pypi.yml`).
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
- False positives on docxtpl cell tags (`{% cellbg %}`, `{% colspan %}`, `{% vm %}`, `{% hm %}`) and on Word smart quotes inside tags: 7 → 0 on the benchmark.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- GitHub Actions updated to Node 24 versions (`checkout@v7`, `setup-python@v7`, ...); the composite action uses `setup-python@v7`.
|
|
77
|
+
- New dependency on Python < 3.11: `tomli`.
|
|
78
|
+
|
|
79
|
+
## [0.1.0] - 2026-10-16
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
- First release.
|
|
83
|
+
- Locations down to the table cell (`T1 R2 C2 > T2 R2 C1`, nested tables) for body, headers, footers and footnotes.
|
|
84
|
+
- Tag and block rules: DTL001 (tag never closed), DTL002 (tag spans paragraphs, warning), DTL101 (unclosed block),
|
|
85
|
+
DTL102 (stray end/else), DTL103 (other Jinja2 syntax errors, using docxtpl's default environment).
|
|
86
|
+
- docxtpl tag rules: DTL201 (`%`/`}` in prefixed tags, #644), DTL202 (space before prefix), DTL203 (tr/tc outside
|
|
87
|
+
a table), DTL204 (prefix mismatch, #603), DTL205 (tr block spans tables), DTL206 (tr open/close in the same row),
|
|
88
|
+
DTL207 (tc block spans rows).
|
|
89
|
+
- Data rules with `--data` (repeatable): DTL301 (missing path, nested and loop-aware, respects `is defined` /
|
|
90
|
+
`| default`), DTL302 (unused key, with `--report-unused-data`), DTL303 (loop over a non-iterable).
|
|
91
|
+
- `--format text|json|github|sarif`, `--lang en|zh`, `--select`, `--ignore`, `--warnings-as-errors`, `--list-vars`,
|
|
92
|
+
`--list-rules`, `--allow-cross-paragraph`; exit codes 0/1/2.
|
|
93
|
+
- File-level suppression comment `{# docxtpl-lint: disable=… #}`.
|
|
94
|
+
- GitHub Action, pre-commit hook, and test cases checked against real docxtpl 0.20.2 rendering.
|
|
95
|
+
|
|
96
|
+
[Unreleased]: https://github.com/yq6666-66/docxtpl-lint/compare/v1.1.0...HEAD
|
|
97
|
+
[1.1.0]: https://github.com/yq6666-66/docxtpl-lint/compare/v1.0.0...v1.1.0
|
|
98
|
+
[1.0.0]: https://github.com/yq6666-66/docxtpl-lint/compare/v0.2.0...v1.0.0
|
|
99
|
+
[0.2.0]: https://github.com/yq6666-66/docxtpl-lint/compare/v0.1.0...v0.2.0
|
|
100
|
+
[0.1.0]: https://github.com/yq6666-66/docxtpl-lint/releases/tag/v0.1.0
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Contributing to docxtpl-lint
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving docxtpl-lint. This document covers the
|
|
4
|
+
practical steps for setting up, testing, and submitting changes.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m venv .venv
|
|
10
|
+
. .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
11
|
+
pip install -e ".[dev]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Checks before submitting
|
|
15
|
+
|
|
16
|
+
Run the test suite:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pytest
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Run linting and formatting checks:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ruff check . && ruff format --check .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Both must pass. Please keep changes focused; unrelated refactors make review
|
|
29
|
+
harder.
|
|
30
|
+
|
|
31
|
+
## Project layout
|
|
32
|
+
|
|
33
|
+
- `src/docxtpl_lint/messages.py` — rule definitions: `id`, `severity`,
|
|
34
|
+
`summary`, and the `en`/`zh` messages.
|
|
35
|
+
- `src/docxtpl_lint/core.py` — rule implementations.
|
|
36
|
+
|
|
37
|
+
## Adding a rule
|
|
38
|
+
|
|
39
|
+
Rules must be deterministic. Prefer missing a report over emitting a false
|
|
40
|
+
positive. Automatic fixes are out of scope — do not add them.
|
|
41
|
+
|
|
42
|
+
When adding a docxtpl-related rule:
|
|
43
|
+
|
|
44
|
+
1. Define the rule in `src/docxtpl_lint/messages.py` and implement it in
|
|
45
|
+
`src/docxtpl_lint/core.py`.
|
|
46
|
+
2. Add a **bad** case to `tests/rule_cases.py`, and a **good** case whenever
|
|
47
|
+
one is meaningful.
|
|
48
|
+
3. `tests/test_docxtpl_behaviour.py` renders the cases with the real `docxtpl`
|
|
49
|
+
package to prove the problem actually exists (either it `raises`, or it
|
|
50
|
+
fails `silent`). Make sure your case is covered there.
|
|
51
|
+
|
|
52
|
+
## Fixtures and demo assets
|
|
53
|
+
|
|
54
|
+
- Example `.docx` files are generated by `tests/make_fixtures.py`.
|
|
55
|
+
- README screenshots are generated by `scripts/make_demo.py` (requires
|
|
56
|
+
`rich`).
|
|
57
|
+
|
|
58
|
+
Regenerate these when your change affects them, and commit the results.
|
|
59
|
+
|
|
60
|
+
## Reporting bugs
|
|
61
|
+
|
|
62
|
+
Open an issue with a minimal `.docx` that reproduces the problem and the
|
|
63
|
+
`docxtpl` version you are using. Without a reproduction we usually cannot
|
|
64
|
+
confirm the behaviour.
|
|
65
|
+
|
|
66
|
+
## Commits and changelog
|
|
67
|
+
|
|
68
|
+
- Write commit messages in the imperative mood (e.g. "Add rule for ...").
|
|
69
|
+
- Add an entry to the `Unreleased` section of `CHANGELOG.md`.
|
|
70
|
+
|
|
71
|
+
## Pull requests
|
|
72
|
+
|
|
73
|
+
Keep each PR scoped to one change. Describe what the rule or fix does, and
|
|
74
|
+
reference the issue it addresses. Ensure `pytest` and `ruff` pass locally
|
|
75
|
+
before requesting review.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 docxtpl-lint contributors
|
|
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.
|