typsphinx 0.6.0__tar.gz → 0.6.2__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.
Files changed (99) hide show
  1. {typsphinx-0.6.0/typsphinx.egg-info → typsphinx-0.6.2}/PKG-INFO +14 -21
  2. {typsphinx-0.6.0 → typsphinx-0.6.2}/README.md +13 -20
  3. {typsphinx-0.6.0 → typsphinx-0.6.2}/pyproject.toml +3 -3
  4. typsphinx-0.6.2/tests/test_abbr_pep_separator_render_gate.py +236 -0
  5. typsphinx-0.6.2/tests/test_builder_output_stem.py +327 -0
  6. typsphinx-0.6.2/tests/test_codly_caption_listitem_leak_render_gate.py +205 -0
  7. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_config_other_options.py +0 -41
  8. typsphinx-0.6.2/tests/test_confval_field_spacing_render_gate.py +222 -0
  9. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_corpus_gate.py +40 -21
  10. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_cross_doc_label_namespace_render_gate.py +20 -4
  11. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_deflist_term_concat_render_gate.py +132 -0
  12. typsphinx-0.6.2/tests/test_desc_bodyless_concat_render_gate.py +154 -0
  13. typsphinx-0.6.2/tests/test_desc_sig_space_render_gate.py +254 -0
  14. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_desc_signature_concat_render_gate.py +101 -0
  15. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_documentation_configuration.py +1 -1
  16. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_examples_basic.py +20 -6
  17. typsphinx-0.6.2/tests/test_examples_charged_ieee_gate.py +249 -0
  18. typsphinx-0.6.2/tests/test_external_link_style_render_gate.py +303 -0
  19. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_field_list_in_list_item_render_gate.py +93 -3
  20. typsphinx-0.6.2/tests/test_inline_literal_overflow_render_gate.py +232 -0
  21. typsphinx-0.6.2/tests/test_missing_and_malformed_master_gate.py +230 -0
  22. typsphinx-0.6.2/tests/test_nested_master_render_gate.py +692 -0
  23. typsphinx-0.6.2/tests/test_package_only_config_gate.py +538 -0
  24. typsphinx-0.6.2/tests/test_package_template_routing.py +199 -0
  25. typsphinx-0.6.2/tests/test_paragraph_concat_render_gate.py +157 -0
  26. typsphinx-0.6.2/tests/test_paragraph_soft_newline_render_gate.py +198 -0
  27. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_pdf_generation.py +222 -12
  28. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_pdf_render_gate.py +559 -1
  29. typsphinx-0.6.2/tests/test_readme_version_sync.py +75 -0
  30. typsphinx-0.6.2/tests/test_rubric_option_concat_render_gate.py +166 -0
  31. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_table_in_list_item_render_gate.py +8 -2
  32. typsphinx-0.6.2/tests/test_target_name_render_gate.py +164 -0
  33. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_template_engine.py +203 -50
  34. typsphinx-0.6.2/tests/test_template_import_path.py +302 -0
  35. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_translator.py +19 -5
  36. typsphinx-0.6.2/tests/test_wide_table_render_gate.py +209 -0
  37. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/__init__.py +1 -3
  38. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/builder.py +262 -23
  39. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/pdf.py +55 -17
  40. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/template_engine.py +87 -63
  41. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/templates/base.typ +12 -0
  42. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx/translator.py +420 -28
  43. typsphinx-0.6.2/typsphinx/writer.py +247 -0
  44. {typsphinx-0.6.0 → typsphinx-0.6.2/typsphinx.egg-info}/PKG-INFO +14 -21
  45. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx.egg-info/SOURCES.txt +20 -0
  46. typsphinx-0.6.0/typsphinx/writer.py +0 -153
  47. {typsphinx-0.6.0 → typsphinx-0.6.2}/LICENSE +0 -0
  48. {typsphinx-0.6.0 → typsphinx-0.6.2}/setup.cfg +0 -0
  49. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_admonitions.py +0 -0
  50. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_builder.py +0 -0
  51. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_builder_requirement13.py +0 -0
  52. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_config.py +0 -0
  53. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_config_template_mapping.py +0 -0
  54. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_config_toctree_defaults.py +0 -0
  55. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_confval_field_body_render_gate.py +0 -0
  56. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_deflist_definition_multiblock_render_gate.py +0 -0
  57. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_deflist_nested_definition_render_gate.py +0 -0
  58. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_deflist_term_inline_children_gate.py +0 -0
  59. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_desc_container_propagated_target_render_gate.py +0 -0
  60. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_desc_signature_anchor_render_gate.py +0 -0
  61. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_documentation_installation.py +0 -0
  62. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_documentation_usage.py +0 -0
  63. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_duplicate_include_label_render_gate.py +0 -0
  64. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_entry_points.py +0 -0
  65. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_epigraph_render_gate.py +0 -0
  66. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_extension.py +0 -0
  67. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_footnotes.py +0 -0
  68. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_glob_image_render_gate.py +0 -0
  69. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_inline_references.py +0 -0
  70. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_integration_advanced.py +0 -0
  71. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_integration_basic.py +0 -0
  72. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_integration_multi_doc.py +0 -0
  73. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_integration_nested_toctree.py +0 -0
  74. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_label_at_char_render_gate.py +0 -0
  75. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_line_blocks.py +0 -0
  76. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_list_item_nested_block_render_gate.py +0 -0
  77. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_math_fallback.py +0 -0
  78. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_math_mitex.py +0 -0
  79. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_math_native.py +0 -0
  80. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_nested_toctree_paths.py +0 -0
  81. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_paragraph_propagated_target_render_gate.py +0 -0
  82. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_preview_smoke_gate.py +0 -0
  83. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_preview_version_sync.py +0 -0
  84. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_ref_target_nested_list_render_gate.py +0 -0
  85. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_rubric_propagated_target_render_gate.py +0 -0
  86. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_static_asset_copy_gate.py +0 -0
  87. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_substitution_definition_render_gate.py +0 -0
  88. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_target_label_render_gate.py +0 -0
  89. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_template_assets.py +0 -0
  90. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_template_codly.py +0 -0
  91. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_template_mitex.py +0 -0
  92. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_toctree_requirement13.py +0 -0
  93. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_topics.py +0 -0
  94. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_typst_string_escape_gate.py +0 -0
  95. {typsphinx-0.6.0 → typsphinx-0.6.2}/tests/test_xref_orphan_degrade_render_gate.py +0 -0
  96. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx.egg-info/dependency_links.txt +0 -0
  97. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx.egg-info/entry_points.txt +0 -0
  98. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx.egg-info/requires.txt +0 -0
  99. {typsphinx-0.6.0 → typsphinx-0.6.2}/typsphinx.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typsphinx
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Sphinx extension for Typst output
5
5
  Author-email: YuSabo <yusabo90002@gmail.com>
6
6
  License-Expression: MIT
@@ -71,7 +71,7 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
71
71
  - Syntax highlighting for multiple languages
72
72
  - Highlight specific lines
73
73
  - **Images and figures**: Embed images with captions and references
74
- - **Cross-references and citations**: Maintain document structure with internal links
74
+ - **Cross-references**: Maintain document structure with internal links
75
75
  - **Customizable templates**: Use default or custom Typst templates
76
76
  - **Direct PDF generation**: Self-contained PDF generation via typst-py (no external Typst CLI required)
77
77
  - **Multi-document support**: Generate multiple Typst files with toctree integration using `#include()`
@@ -177,12 +177,6 @@ Create a custom Typst template:
177
177
  ```python
178
178
  # conf.py
179
179
  typst_template = '_templates/custom.typ'
180
-
181
- typst_elements = {
182
- 'papersize': 'a4',
183
- 'fontsize': '11pt',
184
- 'lang': 'ja',
185
- }
186
180
  ```
187
181
 
188
182
  ### Template Parameter Mapping
@@ -192,12 +186,14 @@ Map Sphinx metadata to template parameters:
192
186
  ```python
193
187
  # conf.py
194
188
  typst_template_mapping = {
195
- 'title': 'project',
196
- 'authors': ['author'],
197
- 'date': 'release',
189
+ 'project': 'doc_title',
190
+ 'author': 'doc_authors',
191
+ 'release': 'version',
198
192
  }
199
193
  ```
200
194
 
195
+ Keys are Sphinx metadata names; values are the template parameter names they map to.
196
+
201
197
  ### Multi-Document Projects
202
198
 
203
199
  Use toctree to combine multiple documents:
@@ -246,8 +242,9 @@ For more details, see the [Sphinx Extension API documentation](https://www.sphin
246
242
 
247
243
  ## Configuration Options
248
244
 
249
- See [docs/configuration.rst](docs/configuration.rst) for all available configuration options:
245
+ Below are the main configuration options. This is not the complete set — see [docs/source/user_guide/configuration.rst](docs/source/user_guide/configuration.rst) for the full reference:
250
246
 
247
+ - `typst_documents`: Master documents to build, as `[(source, target, title, author), ...]` — required for PDF output
251
248
  - `typst_use_mitex`: Enable/disable mitex for LaTeX math
252
249
  - `typst_template`: Custom template path
253
250
  - `typst_elements`: Template parameters (paper size, fonts, etc.)
@@ -264,7 +261,7 @@ This project uses uv for fast package management and follows TDD (Test-Driven De
264
261
  # Install with development dependencies
265
262
  uv sync --extra dev
266
263
 
267
- # Run tests (413 tests)
264
+ # Run tests
268
265
  uv run pytest
269
266
 
270
267
  # Run tests with coverage report
@@ -284,10 +281,9 @@ uv run tox -e docs # Build both HTML and PDF docs
284
281
 
285
282
  ### Testing Strategy
286
283
 
287
- - **Unit tests**: 413 tests covering all major components
284
+ - **Unit tests**: Cover all major components
288
285
  - **Integration tests**: Full build process validation
289
286
  - **Example projects**: `examples/basic/` and `examples/advanced/`
290
- - **Code coverage**: 93% overall
291
287
 
292
288
  ### Project Structure
293
289
 
@@ -309,9 +305,7 @@ typsphinx/
309
305
  ## Known Limitations
310
306
 
311
307
  - **Bibliography**: BibTeX integration not yet supported
312
- - **Glossary**: Glossary generation not yet supported
313
-
314
- See full requirements verification in project documentation.
308
+ - **Citations**: reStructuredText citation directives are not yet supported
315
309
 
316
310
  ## Documentation
317
311
 
@@ -340,7 +334,6 @@ Contributions are welcome! Please:
340
334
  ### Development Guidelines
341
335
 
342
336
  - Follow TDD (Test-Driven Development)
343
- - Maintain 80%+ code coverage
344
337
  - Use black for code formatting
345
338
  - Follow Sphinx extension conventions
346
339
  - Add tests for all new features
@@ -356,7 +349,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
356
349
  - Integrates [mitex](https://github.com/mitex-rs/mitex) for LaTeX math
357
350
  - Uses [codly](https://typst.app/universe/package/codly) for code highlighting
358
351
  - Uses [gentle-clues](https://typst.app/universe/package/gentle-clues) for admonitions
359
- - Developed with [Claude Code](https://claude.ai/code) and Kiro-style Spec-Driven Development
352
+ - Developed with [Claude Code](https://claude.ai/code) and [GSD](https://github.com/open-gsd/gsd-core) (spec-driven development for AI coding agents)
360
353
 
361
354
  ## Version History
362
355
 
@@ -364,5 +357,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
364
357
 
365
358
  ---
366
359
 
367
- **Status**: Stable (v0.5.0) - Production ready
360
+ **Status**: Stable (v0.6.2) - Production ready
368
361
  **Python**: 3.12+ | **Sphinx**: 9.1+ | **Typst**: 0.15+
@@ -27,7 +27,7 @@ typsphinx is a Sphinx extension that enables generating Typst documents from reS
27
27
  - Syntax highlighting for multiple languages
28
28
  - Highlight specific lines
29
29
  - **Images and figures**: Embed images with captions and references
30
- - **Cross-references and citations**: Maintain document structure with internal links
30
+ - **Cross-references**: Maintain document structure with internal links
31
31
  - **Customizable templates**: Use default or custom Typst templates
32
32
  - **Direct PDF generation**: Self-contained PDF generation via typst-py (no external Typst CLI required)
33
33
  - **Multi-document support**: Generate multiple Typst files with toctree integration using `#include()`
@@ -133,12 +133,6 @@ Create a custom Typst template:
133
133
  ```python
134
134
  # conf.py
135
135
  typst_template = '_templates/custom.typ'
136
-
137
- typst_elements = {
138
- 'papersize': 'a4',
139
- 'fontsize': '11pt',
140
- 'lang': 'ja',
141
- }
142
136
  ```
143
137
 
144
138
  ### Template Parameter Mapping
@@ -148,12 +142,14 @@ Map Sphinx metadata to template parameters:
148
142
  ```python
149
143
  # conf.py
150
144
  typst_template_mapping = {
151
- 'title': 'project',
152
- 'authors': ['author'],
153
- 'date': 'release',
145
+ 'project': 'doc_title',
146
+ 'author': 'doc_authors',
147
+ 'release': 'version',
154
148
  }
155
149
  ```
156
150
 
151
+ Keys are Sphinx metadata names; values are the template parameter names they map to.
152
+
157
153
  ### Multi-Document Projects
158
154
 
159
155
  Use toctree to combine multiple documents:
@@ -202,8 +198,9 @@ For more details, see the [Sphinx Extension API documentation](https://www.sphin
202
198
 
203
199
  ## Configuration Options
204
200
 
205
- See [docs/configuration.rst](docs/configuration.rst) for all available configuration options:
201
+ Below are the main configuration options. This is not the complete set — see [docs/source/user_guide/configuration.rst](docs/source/user_guide/configuration.rst) for the full reference:
206
202
 
203
+ - `typst_documents`: Master documents to build, as `[(source, target, title, author), ...]` — required for PDF output
207
204
  - `typst_use_mitex`: Enable/disable mitex for LaTeX math
208
205
  - `typst_template`: Custom template path
209
206
  - `typst_elements`: Template parameters (paper size, fonts, etc.)
@@ -220,7 +217,7 @@ This project uses uv for fast package management and follows TDD (Test-Driven De
220
217
  # Install with development dependencies
221
218
  uv sync --extra dev
222
219
 
223
- # Run tests (413 tests)
220
+ # Run tests
224
221
  uv run pytest
225
222
 
226
223
  # Run tests with coverage report
@@ -240,10 +237,9 @@ uv run tox -e docs # Build both HTML and PDF docs
240
237
 
241
238
  ### Testing Strategy
242
239
 
243
- - **Unit tests**: 413 tests covering all major components
240
+ - **Unit tests**: Cover all major components
244
241
  - **Integration tests**: Full build process validation
245
242
  - **Example projects**: `examples/basic/` and `examples/advanced/`
246
- - **Code coverage**: 93% overall
247
243
 
248
244
  ### Project Structure
249
245
 
@@ -265,9 +261,7 @@ typsphinx/
265
261
  ## Known Limitations
266
262
 
267
263
  - **Bibliography**: BibTeX integration not yet supported
268
- - **Glossary**: Glossary generation not yet supported
269
-
270
- See full requirements verification in project documentation.
264
+ - **Citations**: reStructuredText citation directives are not yet supported
271
265
 
272
266
  ## Documentation
273
267
 
@@ -296,7 +290,6 @@ Contributions are welcome! Please:
296
290
  ### Development Guidelines
297
291
 
298
292
  - Follow TDD (Test-Driven Development)
299
- - Maintain 80%+ code coverage
300
293
  - Use black for code formatting
301
294
  - Follow Sphinx extension conventions
302
295
  - Add tests for all new features
@@ -312,7 +305,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
312
305
  - Integrates [mitex](https://github.com/mitex-rs/mitex) for LaTeX math
313
306
  - Uses [codly](https://typst.app/universe/package/codly) for code highlighting
314
307
  - Uses [gentle-clues](https://typst.app/universe/package/gentle-clues) for admonitions
315
- - Developed with [Claude Code](https://claude.ai/code) and Kiro-style Spec-Driven Development
308
+ - Developed with [Claude Code](https://claude.ai/code) and [GSD](https://github.com/open-gsd/gsd-core) (spec-driven development for AI coding agents)
316
309
 
317
310
  ## Version History
318
311
 
@@ -320,5 +313,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
320
313
 
321
314
  ---
322
315
 
323
- **Status**: Stable (v0.5.0) - Production ready
316
+ **Status**: Stable (v0.6.2) - Production ready
324
317
  **Python**: 3.12+ | **Sphinx**: 9.1+ | **Typst**: 0.15+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "typsphinx"
7
- version = "0.6.0"
7
+ version = "0.6.2"
8
8
  description = "Sphinx extension for Typst output"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -119,8 +119,8 @@ ignore = [
119
119
  "E501", # Line too long (handled by black)
120
120
  "T201", # print found (used in tests for debugging)
121
121
  "B017", # asserting blind exception in tests
122
- "UP035", # typing.Dict/List/Set deprecation (Python 3.10+ support)
123
- "UP006", # Use dict instead of Dict (Python 3.10+ support)
122
+ "UP035", # typing.Dict/List/Set deprecation; modernization deferred (see .planning/todos/pending/2026-07-22-modernize-typing-imports-drop-up006-up035-ignore.md)
123
+ "UP006", # Use dict instead of Dict; same deferral as UP035 above
124
124
  "UP028", # yield from (minor optimization)
125
125
  "N802", # Function naming (docutils visitor pattern uses PascalCase)
126
126
  "A001", # Shadowing builtins (copyright in conf.py is Sphinx convention)
@@ -0,0 +1,236 @@
1
+ """
2
+ Real-compile regression gate for the narrow-scope abbr-title suppression fix
3
+ (FID-14, Phase 21 Plan 02, GATE-01).
4
+
5
+ The shipping bug: Sphinx's Python-domain signature renderer wraps the
6
+ auto-generated PEP 3102 keyword-only ``*`` and PEP 570 positional-only ``/``
7
+ signature separators in a genuine ``docutils.nodes.abbreviation`` node whose
8
+ own visible text (``node.astext()``) is exactly the single character ``*``
9
+ or ``/``. ``depart_abbreviation`` unconditionally appended the node's
10
+ ``<abbr>`` hover-title explanation inline for EVERY ``abbreviation`` node,
11
+ so every affected signature rendered with the clutter ``"*
12
+ (Keyword-only parameters separator (PEP 3102))"`` / ``"/ (Positional-only
13
+ parameter separator (PEP 570))"`` inline.
14
+
15
+ Fix: ``depart_abbreviation`` now narrows its existing ``if explanation:``
16
+ guard to ``if explanation and node.astext() not in ("*", "/"):`` -- the
17
+ explanation is suppressed ONLY for the two auto-generated separators (whose
18
+ own node text is exactly ``"*"``/``"/"``); a genuine ``:abbr:`` role's
19
+ acronym text is never bare ``"*"``/``"/"``, so it keeps its inline
20
+ expansion unchanged (D-Disc-3, narrow scope).
21
+
22
+ Confirmed against the pre-fix translator this session: ``index.typ``
23
+ contained ``text(" (Positional-only parameter separator (PEP 570))")`` and
24
+ ``text(" (Keyword-only parameters separator (PEP 3102))")`` inline in the
25
+ signature's parameter-list concat, and the extracted PDF text read
26
+ ``"abbrpepsepfunc(pos_only, / (Positional-only parameter separator (PEP
27
+ 570)), both, * (Keyword-only parameters separator (PEP 3102)), kw_only)"``
28
+ -- both fail the asserts below pre-fix. The genuine ``:abbr:`` usage in the
29
+ same fixture (``ABBRSENTINELACRONYM (ABBRSENTINELEXPANSIONPHRASE)``) already
30
+ passes pre-fix and must continue to pass post-fix (D-Disc-3 regression
31
+ guard).
32
+
33
+ Verification is Shape-B (structural + pypdf, D-10): a structural ``.typ``
34
+ assert plus a pypdf extracted-text adjacency assert, since FID-14's fix IS
35
+ text-extractable (unlike FID-11's non-extractable vertical-layout property).
36
+ """
37
+
38
+ import subprocess
39
+ import sys
40
+ from pathlib import Path
41
+
42
+ import pytest
43
+
44
+ try:
45
+ import typst # noqa: F401
46
+
47
+ TYPST_AVAILABLE = True
48
+ except ImportError:
49
+ TYPST_AVAILABLE = False
50
+
51
+ try:
52
+ import pypdf
53
+
54
+ PYPDF_AVAILABLE = True
55
+ except ImportError:
56
+ PYPDF_AVAILABLE = False
57
+
58
+
59
+ @pytest.fixture
60
+ def abbr_pep_separator_render_gate_dir():
61
+ """Return the path to the abbr_pep_separator_render_gate fixture."""
62
+ return Path(__file__).parent / "fixtures" / "abbr_pep_separator_render_gate"
63
+
64
+
65
+ @pytest.fixture
66
+ def temp_build_dir(tmp_path):
67
+ """Provide a temporary directory for build output."""
68
+ return tmp_path / "_build"
69
+
70
+
71
+ def _run_sphinx_build_typstpdf(
72
+ source_dir: Path, build_dir: Path
73
+ ) -> subprocess.CompletedProcess:
74
+ """
75
+ Run ``sphinx-build -b typstpdf`` as a subprocess and return the completed
76
+ process (stdout/stderr captured as text).
77
+
78
+ Invoked as ``sys.executable -m sphinx`` (never ``uv run sphinx-build``)
79
+ so the exact interpreter/venv running this test is reused, sidestepping
80
+ the documented NixOS-sandbox PATH-shadowing hazard.
81
+ """
82
+ return subprocess.run(
83
+ [
84
+ sys.executable,
85
+ "-m",
86
+ "sphinx",
87
+ "-b",
88
+ "typstpdf",
89
+ str(source_dir),
90
+ str(build_dir),
91
+ ],
92
+ capture_output=True,
93
+ text=True,
94
+ )
95
+
96
+
97
+ @pytest.mark.skipif(
98
+ not TYPST_AVAILABLE,
99
+ reason="typst-py is required for the abbr-pep-separator render gate",
100
+ )
101
+ class TestAbbrPepSeparatorRenderGate:
102
+ """
103
+ Real-compile regression gate proving the PEP 3102 ``*`` / PEP 570 ``/``
104
+ signature separators no longer inject their ``<abbr>`` hover-title text
105
+ inline, while a genuine ``:abbr:`` role usage in the same document keeps
106
+ its inline expansion unchanged.
107
+
108
+ Requirements: FID-14, GATE-01.
109
+ """
110
+
111
+ def test_typstpdf_abbr_pep_separator_produces_pdf_with_structural_suppression(
112
+ self, abbr_pep_separator_render_gate_dir, temp_build_dir
113
+ ):
114
+ """
115
+ Build the fixture through ``-b typstpdf`` and confirm, at the
116
+ SOURCE (``.typ``) level:
117
+
118
+ - the build exits cleanly and did not report a compile failure;
119
+ - the signature's parameter-list concat does NOT append the
120
+ PEP-separator hover-title explanation text for the ``*``/``/``
121
+ cases;
122
+ - the genuine ``:abbr:`` usage DOES still append its expansion
123
+ text -- ``text(" (ABBRSENTINELEXPANSIONPHRASE)")`` is present;
124
+ - ``index.pdf`` exists, is non-empty, and begins with the ``%PDF``
125
+ magic bytes (real ``typst.compile()`` succeeded).
126
+ """
127
+ result = _run_sphinx_build_typstpdf(
128
+ abbr_pep_separator_render_gate_dir, temp_build_dir
129
+ )
130
+ assert result.returncode == 0, (
131
+ f"sphinx-build -b typstpdf failed:\n"
132
+ f"stdout: {result.stdout}\n"
133
+ f"stderr: {result.stderr}"
134
+ )
135
+ assert "Typst compilation failed" not in result.stderr, (
136
+ "TypstPDFBuilder.finish() logged a compilation failure:\n"
137
+ f"stderr: {result.stderr}"
138
+ )
139
+
140
+ typ_output = temp_build_dir / "index.typ"
141
+ assert typ_output.exists(), "index.typ was not emitted"
142
+ typ_text = typ_output.read_text(encoding="utf-8")
143
+
144
+ # Structural: the PEP-separator hover-title text must not be
145
+ # appended anywhere in the emitted .typ for the *"/"/ cases.
146
+ assert "Keyword-only parameters separator" not in typ_text, (
147
+ "Found the PEP 3102 '*' separator's hover-title text in the "
148
+ f"emitted .typ -- the FID-14 fix is not applied:\n{typ_text}"
149
+ )
150
+ assert "Positional-only parameter separator" not in typ_text, (
151
+ "Found the PEP 570 '/' separator's hover-title text in the "
152
+ f"emitted .typ -- the FID-14 fix is not applied:\n{typ_text}"
153
+ )
154
+
155
+ # Regression: the genuine :abbr: usage in the SAME fixture must
156
+ # still append its expansion (D-Disc-3's narrow-scope requirement).
157
+ assert 'text(" (ABBRSENTINELEXPANSIONPHRASE)")' in typ_text, (
158
+ "Expected the genuine :abbr: role's expansion to still be "
159
+ "appended inline -- the FID-14 fix must not suppress a real "
160
+ f":abbr: usage:\n{typ_text}"
161
+ )
162
+
163
+ # The emitted .typ must have compiled to a real, non-empty PDF.
164
+ pdf_output = temp_build_dir / "index.pdf"
165
+ assert pdf_output.exists(), (
166
+ "index.pdf was not produced:\n" f"stderr: {result.stderr}"
167
+ )
168
+ assert pdf_output.stat().st_size > 0, "PDF file is empty"
169
+ with open(pdf_output, "rb") as f:
170
+ magic = f.read(4)
171
+ assert magic == b"%PDF", "Generated file is not a valid PDF"
172
+
173
+ @pytest.mark.skipif(
174
+ not PYPDF_AVAILABLE,
175
+ reason="pypdf is required for the extracted-text adjacency assert",
176
+ )
177
+ def test_pdf_extracted_text_suppresses_pep_separator_titles_only(
178
+ self, abbr_pep_separator_render_gate_dir, temp_build_dir
179
+ ):
180
+ """
181
+ Build the fixture through ``-b typstpdf``, extract the compiled
182
+ PDF's text with pypdf, and confirm (D-10 required adjacency
183
+ assert):
184
+
185
+ - ``"(Keyword-only parameters separator"`` is ABSENT from the
186
+ extracted signature text;
187
+ - ``"(Positional-only parameter separator"`` is ABSENT from the
188
+ extracted signature text;
189
+ - the plain ``/`` and ``*`` separator characters are still present
190
+ in the signature (suppression removes only the appended
191
+ explanation segment, not the separator itself);
192
+ - the genuine ``:abbr:`` expansion sentinel phrase IS present.
193
+ """
194
+ result = _run_sphinx_build_typstpdf(
195
+ abbr_pep_separator_render_gate_dir, temp_build_dir
196
+ )
197
+ assert result.returncode == 0, (
198
+ f"sphinx-build -b typstpdf failed:\n"
199
+ f"stdout: {result.stdout}\n"
200
+ f"stderr: {result.stderr}"
201
+ )
202
+
203
+ pdf_output = temp_build_dir / "index.pdf"
204
+ assert pdf_output.exists(), (
205
+ "index.pdf was not produced:\n" f"stderr: {result.stderr}"
206
+ )
207
+
208
+ reader = pypdf.PdfReader(str(pdf_output))
209
+ full_text = "\n".join(page.extract_text() for page in reader.pages)
210
+
211
+ assert "(Keyword-only parameters separator" not in full_text, (
212
+ "Found the PEP 3102 '*' separator's hover-title text in the "
213
+ f"extracted PDF text -- the FID-14 fix is not in effect:\n"
214
+ f"{full_text}"
215
+ )
216
+ assert "(Positional-only parameter separator" not in full_text, (
217
+ "Found the PEP 570 '/' separator's hover-title text in the "
218
+ f"extracted PDF text -- the FID-14 fix is not in effect:\n"
219
+ f"{full_text}"
220
+ )
221
+
222
+ # The separator characters themselves must remain in the signature.
223
+ assert "abbrpepsepfunc(pos_only, /" in full_text, (
224
+ "Expected the '/' positional-only separator to remain in the "
225
+ f"rendered signature:\n{full_text}"
226
+ )
227
+ assert ", both, *" in full_text, (
228
+ "Expected the '*' keyword-only separator to remain in the "
229
+ f"rendered signature:\n{full_text}"
230
+ )
231
+
232
+ # The genuine :abbr: usage must keep its inline expansion.
233
+ assert "ABBRSENTINELACRONYM (ABBRSENTINELEXPANSIONPHRASE)" in full_text, (
234
+ "Expected the genuine :abbr: role's inline expansion to remain "
235
+ f"present -- the FID-14 fix must not suppress it:\n{full_text}"
236
+ )