rstobj 2.0.0__tar.gz → 2.0.1__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.
- {rstobj-2.0.0/rstobj.egg-info → rstobj-2.0.1}/PKG-INFO +42 -23
- {rstobj-2.0.0 → rstobj-2.0.1}/README.rst +21 -8
- rstobj-2.0.1/pyproject.toml +131 -0
- rstobj-2.0.1/rstobj/base.py +139 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/base.py +4 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/codeblock.py +4 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/image.py +6 -2
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/miscellaneous.py +2 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/table.py +2 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/toc.py +6 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/enum_list.py +2 -1
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/header.py +27 -7
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/paths.py +2 -2
- rstobj-2.0.1/rstobj/templates/__init__.py +12 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.base.Directive.rst +8 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.codeblock.Code.rst +1 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.codeblock.CodeBlockBase.rst +9 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.codeblock.CodeBlockEmpty.rst +3 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.image.Image.rst +18 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.miscellaneous.Include.rst +30 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.table.ListTable.rst +29 -0
- rstobj-2.0.1/rstobj/templates/rstobj.directives.toc.TableOfContent.rst +12 -0
- rstobj-2.0.1/rstobj/templates/rstobj.markup.bullet_list.BulletList.rst +3 -0
- rstobj-2.0.1/rstobj/templates/rstobj.markup.enum_list.EnumList.rst +4 -0
- rstobj-2.0.1/rstobj/templates/rstobj.markup.header.Header.rst +5 -0
- rstobj-2.0.1/rstobj/templates/rstobj.markup.hyperlink.Reference.rst +1 -0
- rstobj-2.0.1/rstobj/templates/rstobj.markup.hyperlink.URI.rst +1 -0
- {rstobj-2.0.0 → rstobj-2.0.1/rstobj.egg-info}/PKG-INFO +42 -23
- rstobj-2.0.1/rstobj.egg-info/SOURCES.txt +47 -0
- rstobj-2.0.1/rstobj.egg-info/requires.txt +25 -0
- rstobj-2.0.1/tests/test_base.py +85 -0
- rstobj-2.0.1/tests/test_option.py +52 -0
- rstobj-2.0.0/pyproject.toml +0 -122
- rstobj-2.0.0/rstobj/_version.py +0 -4
- rstobj-2.0.0/rstobj/base.py +0 -86
- rstobj-2.0.0/rstobj/templates/__init__.py +0 -6
- rstobj-2.0.0/rstobj.egg-info/SOURCES.txt +0 -33
- rstobj-2.0.0/rstobj.egg-info/requires.txt +0 -26
- {rstobj-2.0.0 → rstobj-2.0.1}/AUTHORS.rst +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/LICENSE.txt +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/__init__.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/api.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/__init__.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/directives/api.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/__init__.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/api.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/bullet_list.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/markup/hyperlink.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/option.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/vendor/__init__.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj/vendor/pytest_cov_helper.py +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj.egg-info/dependency_links.txt +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/rstobj.egg-info/top_level.txt +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/setup.cfg +0 -0
- {rstobj-2.0.0 → rstobj-2.0.1}/tests/test_api.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rstobj
|
|
3
|
-
Version: 2.0.
|
|
4
|
-
Summary:
|
|
5
|
-
Author-email: Sanhe Hu <husanhe@
|
|
6
|
-
Maintainer-email: Sanhe Hu <husanhe@
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Construct reStructuredText markup and directives from Python code.
|
|
5
|
+
Author-email: Sanhe Hu <husanhe@gmail.com>
|
|
6
|
+
Maintainer-email: Sanhe Hu <husanhe@gmail.com>
|
|
7
7
|
License-Expression: MIT
|
|
8
8
|
Project-URL: Homepage, https://github.com/MacHu-GWU/rstobj-project
|
|
9
9
|
Project-URL: Documentation, https://rstobj.readthedocs.io/en/latest/
|
|
@@ -17,30 +17,36 @@ Classifier: Natural Language :: English
|
|
|
17
17
|
Classifier: Operating System :: Microsoft :: Windows
|
|
18
18
|
Classifier: Operating System :: MacOS
|
|
19
19
|
Classifier: Operating System :: Unix
|
|
20
|
-
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Requires-Python: <4.0,>=3.11
|
|
21
26
|
Description-Content-Type: text/x-rst
|
|
22
27
|
License-File: LICENSE.txt
|
|
23
28
|
License-File: AUTHORS.rst
|
|
24
|
-
Requires-Dist: jinja2
|
|
29
|
+
Requires-Dist: jinja2<4.0.0,>=3.1.0
|
|
25
30
|
Provides-Extra: dev
|
|
26
|
-
Requires-Dist: rich<
|
|
31
|
+
Requires-Dist: rich<15.0.0,>=14.3.4; extra == "dev"
|
|
27
32
|
Provides-Extra: test
|
|
28
|
-
Requires-Dist: pytest<
|
|
29
|
-
Requires-Dist: pytest-cov<
|
|
33
|
+
Requires-Dist: pytest<10.0.0,>=9.1.1; extra == "test"
|
|
34
|
+
Requires-Dist: pytest-cov<8.0.0,>=7.1.0; extra == "test"
|
|
35
|
+
Requires-Dist: docutils<1.0.0,>=0.21.2; extra == "test"
|
|
30
36
|
Provides-Extra: doc
|
|
31
|
-
Requires-Dist: Sphinx<
|
|
37
|
+
Requires-Dist: Sphinx<9.0.0,>=8.2.3; extra == "doc"
|
|
32
38
|
Requires-Dist: sphinx-copybutton<1.0.0,>=0.5.2; extra == "doc"
|
|
33
|
-
Requires-Dist: sphinx-design<1.0.0,>=0.
|
|
39
|
+
Requires-Dist: sphinx-design<1.0.0,>=0.7.0; extra == "doc"
|
|
34
40
|
Requires-Dist: sphinx-jinja<3.0.0,>=2.0.2; extra == "doc"
|
|
35
|
-
Requires-Dist: furo==
|
|
41
|
+
Requires-Dist: furo==2025.12.19; extra == "doc"
|
|
36
42
|
Requires-Dist: pygments<3.0.0,>=2.18.0; extra == "doc"
|
|
37
43
|
Requires-Dist: ipython<8.19.0,>=8.18.1; extra == "doc"
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist:
|
|
44
|
+
Requires-Dist: ipykernel<8.0.0,>=7.3.0; extra == "doc"
|
|
45
|
+
Requires-Dist: nbsphinx<1.0.0,>=0.9.8; extra == "doc"
|
|
46
|
+
Requires-Dist: docfly<4.0.0,>=3.0.3; extra == "doc"
|
|
40
47
|
Provides-Extra: mise
|
|
41
48
|
Requires-Dist: PyGithub<3.0.0,>=2.8.0; extra == "mise"
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist: tomli<3.0.0,>=2.0.0; python_version < "3.11" and extra == "mise"
|
|
49
|
+
Requires-Dist: httpx2<3.0.0,>=2.13.1; extra == "mise"
|
|
44
50
|
Dynamic: license-file
|
|
45
51
|
|
|
46
52
|
|
|
@@ -116,23 +122,21 @@ I have a `Blog Post <https://github.com/MacHu-GWU/Tech-Blog/issues/6>`_ to share
|
|
|
116
122
|
|
|
117
123
|
.. code-block:: python
|
|
118
124
|
|
|
119
|
-
import rstobj
|
|
125
|
+
import rstobj.api as rstobj
|
|
120
126
|
|
|
121
127
|
header = rstobj.markup.Header(title="Section1", header_level=1, auto_label=True)
|
|
122
|
-
|
|
123
|
-
print(rst_header)
|
|
128
|
+
print(header.render())
|
|
124
129
|
|
|
125
130
|
ltable = rstobj.directives.ListTable(
|
|
126
131
|
data=[["id", "name"], [1, "Alice"], [2, "Bob"]],
|
|
127
132
|
title="Users",
|
|
128
133
|
header=True,
|
|
129
134
|
)
|
|
130
|
-
|
|
131
|
-
print(rst_ltable)
|
|
135
|
+
print(ltable.render())
|
|
132
136
|
|
|
133
137
|
Output::
|
|
134
138
|
|
|
135
|
-
..
|
|
139
|
+
.. _Section1:
|
|
136
140
|
|
|
137
141
|
Section1
|
|
138
142
|
========
|
|
@@ -148,7 +152,9 @@ Output::
|
|
|
148
152
|
* - 2
|
|
149
153
|
- Bob
|
|
150
154
|
|
|
151
|
-
|
|
155
|
+
See the `Rendering Gallery <https://rstobj.readthedocs.io/en/latest/01-Rendering-Gallery/index.html>`_ for every supported markup and directive, with its Python code, its output, and how it renders.
|
|
156
|
+
|
|
157
|
+
I recommend to use this in your jinja2 template, content of ``output.rst``::
|
|
152
158
|
|
|
153
159
|
{{ header.render() }}
|
|
154
160
|
{{ ltable.render() }}
|
|
@@ -195,3 +201,16 @@ To upgrade to latest version:
|
|
|
195
201
|
.. code-block:: console
|
|
196
202
|
|
|
197
203
|
$ pip install --upgrade rstobj
|
|
204
|
+
|
|
205
|
+
.. _use-with-ai-coding-agents:
|
|
206
|
+
|
|
207
|
+
Use with AI Coding Agents
|
|
208
|
+
------------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
This repo ships an `Agent Skill <https://code.claude.com/docs/en/skills>`_ that documents the public API of ``rstobj``, so an AI coding agent such as Claude Code can use the library without reading its source. It lives in `.claude/skills/pypi-rstobj <https://github.com/MacHu-GWU/rstobj-project/tree/main/.claude/skills/pypi-rstobj>`_. To use it in your project, copy it into your project's ``.claude/skills/`` folder. Run this from your project's root:
|
|
211
|
+
|
|
212
|
+
.. code-block:: console
|
|
213
|
+
|
|
214
|
+
$ git clone --depth 1 https://github.com/MacHu-GWU/rstobj-project.git /tmp/rstobj-project
|
|
215
|
+
$ mkdir -p .claude/skills
|
|
216
|
+
$ cp -R /tmp/rstobj-project/.claude/skills/pypi-rstobj .claude/skills/
|
|
@@ -71,23 +71,21 @@ I have a `Blog Post <https://github.com/MacHu-GWU/Tech-Blog/issues/6>`_ to share
|
|
|
71
71
|
|
|
72
72
|
.. code-block:: python
|
|
73
73
|
|
|
74
|
-
import rstobj
|
|
74
|
+
import rstobj.api as rstobj
|
|
75
75
|
|
|
76
76
|
header = rstobj.markup.Header(title="Section1", header_level=1, auto_label=True)
|
|
77
|
-
|
|
78
|
-
print(rst_header)
|
|
77
|
+
print(header.render())
|
|
79
78
|
|
|
80
79
|
ltable = rstobj.directives.ListTable(
|
|
81
80
|
data=[["id", "name"], [1, "Alice"], [2, "Bob"]],
|
|
82
81
|
title="Users",
|
|
83
82
|
header=True,
|
|
84
83
|
)
|
|
85
|
-
|
|
86
|
-
print(rst_ltable)
|
|
84
|
+
print(ltable.render())
|
|
87
85
|
|
|
88
86
|
Output::
|
|
89
87
|
|
|
90
|
-
..
|
|
88
|
+
.. _Section1:
|
|
91
89
|
|
|
92
90
|
Section1
|
|
93
91
|
========
|
|
@@ -103,7 +101,9 @@ Output::
|
|
|
103
101
|
* - 2
|
|
104
102
|
- Bob
|
|
105
103
|
|
|
106
|
-
|
|
104
|
+
See the `Rendering Gallery <https://rstobj.readthedocs.io/en/latest/01-Rendering-Gallery/index.html>`_ for every supported markup and directive, with its Python code, its output, and how it renders.
|
|
105
|
+
|
|
106
|
+
I recommend to use this in your jinja2 template, content of ``output.rst``::
|
|
107
107
|
|
|
108
108
|
{{ header.render() }}
|
|
109
109
|
{{ ltable.render() }}
|
|
@@ -149,4 +149,17 @@ To upgrade to latest version:
|
|
|
149
149
|
|
|
150
150
|
.. code-block:: console
|
|
151
151
|
|
|
152
|
-
$ pip install --upgrade rstobj
|
|
152
|
+
$ pip install --upgrade rstobj
|
|
153
|
+
|
|
154
|
+
.. _use-with-ai-coding-agents:
|
|
155
|
+
|
|
156
|
+
Use with AI Coding Agents
|
|
157
|
+
------------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
This repo ships an `Agent Skill <https://code.claude.com/docs/en/skills>`_ that documents the public API of ``rstobj``, so an AI coding agent such as Claude Code can use the library without reading its source. It lives in `.claude/skills/pypi-rstobj <https://github.com/MacHu-GWU/rstobj-project/tree/main/.claude/skills/pypi-rstobj>`_. To use it in your project, copy it into your project's ``.claude/skills/`` folder. Run this from your project's root:
|
|
160
|
+
|
|
161
|
+
.. code-block:: console
|
|
162
|
+
|
|
163
|
+
$ git clone --depth 1 https://github.com/MacHu-GWU/rstobj-project.git /tmp/rstobj-project
|
|
164
|
+
$ mkdir -p .claude/skills
|
|
165
|
+
$ cp -R /tmp/rstobj-project/.claude/skills/pypi-rstobj .claude/skills/
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# [project] table: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
|
|
2
|
+
[project]
|
|
3
|
+
name = "rstobj"
|
|
4
|
+
# Bump before each release (https://semver.org/), then ``mise run inst`` to update ``uv.lock``.
|
|
5
|
+
version = "2.0.1"
|
|
6
|
+
description = "Construct reStructuredText markup and directives from Python code."
|
|
7
|
+
# An SPDX license expression (PEP 639); keep ``LICENSE.txt`` in sync.
|
|
8
|
+
# Read: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license-and-license-files
|
|
9
|
+
# Valid identifiers: https://spdx.org/licenses/
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE.txt", "AUTHORS.rst"]
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sanhe Hu", email = "husanhe@gmail.com" },
|
|
14
|
+
]
|
|
15
|
+
maintainers = [
|
|
16
|
+
{ name = "Sanhe Hu", email = "husanhe@gmail.com" },
|
|
17
|
+
]
|
|
18
|
+
keywords = []
|
|
19
|
+
readme = "README.rst"
|
|
20
|
+
requires-python = ">=3.11,<4.0"
|
|
21
|
+
# Read: https://pypi.org/classifiers/
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"Natural Language :: English",
|
|
26
|
+
"Operating System :: Microsoft :: Windows",
|
|
27
|
+
"Operating System :: MacOS",
|
|
28
|
+
"Operating System :: Unix",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Programming Language :: Python :: 3.14",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# --- Dependencies ---
|
|
37
|
+
# Edit these lists, then ``mise run inst`` to update ``uv.lock`` and ``.venv``.
|
|
38
|
+
dependencies = [
|
|
39
|
+
"jinja2>=3.1.0,<4.0.0", # renders the reST templates in rstobj/templates
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# --- Optional dependencies (extras) ---
|
|
43
|
+
# Read: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#dependencies-optional-dependencies
|
|
44
|
+
# Each one must support every Python version in ``requires-python``.
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
|
|
47
|
+
# local development
|
|
48
|
+
dev = [
|
|
49
|
+
"rich>=14.3.4,<15.0.0",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# tests and coverage
|
|
53
|
+
test = [
|
|
54
|
+
"pytest>=9.1.1,<10.0.0",
|
|
55
|
+
"pytest-cov>=7.1.0,<8.0.0",
|
|
56
|
+
"docutils>=0.21.2,<1.0.0", # checks that the rendered reST is valid
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# docs build
|
|
60
|
+
doc = [
|
|
61
|
+
"Sphinx>=8.2.3,<9.0.0", # held below 9 until its autodoc rewrite is verified
|
|
62
|
+
"sphinx-copybutton>=0.5.2,<1.0.0", # copy button on code blocks
|
|
63
|
+
"sphinx-design>=0.7.0,<1.0.0", # cards, tabs, grids
|
|
64
|
+
"sphinx-jinja>=2.0.2,<3.0.0", # jinja in reST
|
|
65
|
+
"furo==2025.12.19", # theme
|
|
66
|
+
"pygments>=2.18.0,<3.0.0", # syntax highlighting
|
|
67
|
+
"ipython>=8.18.1,<8.19.0", # notebook execution
|
|
68
|
+
"ipykernel>=7.3.0,<8.0.0", # notebook kernel
|
|
69
|
+
"nbsphinx>=0.9.8,<1.0.0", # notebooks as doc pages
|
|
70
|
+
"docfly>=3.0.3,<4.0.0", # toctree and API reference
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# mise tasks
|
|
74
|
+
mise = [
|
|
75
|
+
"PyGithub>=2.8.0,<3.0.0", # GitHub API client
|
|
76
|
+
"httpx2>=2.13.1,<3.0.0", # Codecov and Read the Docs APIs
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[project.urls]
|
|
80
|
+
Homepage = "https://github.com/MacHu-GWU/rstobj-project"
|
|
81
|
+
Documentation = "https://rstobj.readthedocs.io/en/latest/"
|
|
82
|
+
Repository = "https://github.com/MacHu-GWU/rstobj-project"
|
|
83
|
+
Issues = "https://github.com/MacHu-GWU/rstobj-project/issues"
|
|
84
|
+
Changelog = "https://github.com/MacHu-GWU/rstobj-project/blob/main/release-history.rst"
|
|
85
|
+
Download = "https://pypi.org/pypi/rstobj#files"
|
|
86
|
+
|
|
87
|
+
# Read: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts
|
|
88
|
+
[project.scripts]
|
|
89
|
+
|
|
90
|
+
[tool.setuptools.packages.find]
|
|
91
|
+
where = ["."]
|
|
92
|
+
include = [
|
|
93
|
+
"rstobj*",
|
|
94
|
+
]
|
|
95
|
+
exclude = [
|
|
96
|
+
"rstobj.tests",
|
|
97
|
+
"rstobj.tests.*",
|
|
98
|
+
"rstobj.docs",
|
|
99
|
+
"rstobj.docs.*",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# Non-Python files shipped in the wheel: the jinja2 templates rendered by ``rstobj.templates``.
|
|
103
|
+
[tool.setuptools.package-data]
|
|
104
|
+
rstobj = [
|
|
105
|
+
"templates/*.rst",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# --- ruff: ``mise run format`` / ``mise run lint`` ---
|
|
109
|
+
[tool.ruff]
|
|
110
|
+
extend-exclude = [
|
|
111
|
+
"rstobj/vendor",
|
|
112
|
+
".claude/skills/*/assets", # templates, not valid Python
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[tool.ruff.lint.per-file-ignores]
|
|
116
|
+
# re-export modules
|
|
117
|
+
"**/__init__.py" = ["F401"]
|
|
118
|
+
"**/api.py" = ["F401"]
|
|
119
|
+
|
|
120
|
+
# --- pytest 9+ native table ---
|
|
121
|
+
# Read: https://docs.pytest.org/en/stable/reference/customize.html#pyproject-toml
|
|
122
|
+
[tool.pytest]
|
|
123
|
+
testpaths = ["tests"]
|
|
124
|
+
# strict = true # only with a pinned pytest
|
|
125
|
+
# filterwarnings = ["error"]
|
|
126
|
+
# markers = ["slow: tests that take more than a few seconds"]
|
|
127
|
+
|
|
128
|
+
# --- Build (``mise run build``); setuptools 77+ for PEP 639 ``license`` ---
|
|
129
|
+
[build-system]
|
|
130
|
+
requires = ["setuptools>=77.0"]
|
|
131
|
+
build-backend = "setuptools.build_meta"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
RestructuredText Object abstraction.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import typing as T
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
if T.TYPE_CHECKING: # pragma: no cover
|
|
13
|
+
from jinja2 import Template
|
|
14
|
+
|
|
15
|
+
from .templates import env
|
|
16
|
+
from .option import Options
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _indent_line(line: str, prefix: str) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Prefix a line and strip its trailing whitespace. A blank line stays empty,
|
|
22
|
+
so indenting never leaves whitespace-only lines.
|
|
23
|
+
"""
|
|
24
|
+
line = line.rstrip()
|
|
25
|
+
if line:
|
|
26
|
+
return prefix + line
|
|
27
|
+
else:
|
|
28
|
+
return ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _indent_continuation(text: str, width: int, force: bool) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Indent every line after the first by ``width`` spaces, so a multi-line
|
|
34
|
+
text stays inside the list item or table cell it starts in.
|
|
35
|
+
|
|
36
|
+
:param force: if False, text whose continuation lines are all indented by
|
|
37
|
+
at least ``width`` already is returned unchanged, so callers that
|
|
38
|
+
indented their strings by hand keep the same output.
|
|
39
|
+
"""
|
|
40
|
+
lines = text.split("\n")
|
|
41
|
+
rest = lines[1:]
|
|
42
|
+
if not force:
|
|
43
|
+
is_indented = all(
|
|
44
|
+
(not line.strip()) or (len(line) - len(line.lstrip(" ")) >= width)
|
|
45
|
+
for line in rest
|
|
46
|
+
)
|
|
47
|
+
if is_indented:
|
|
48
|
+
return text
|
|
49
|
+
prefix = " " * width
|
|
50
|
+
return "\n".join([lines[0]] + [_indent_line(line, prefix) for line in rest])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(kw_only=True)
|
|
54
|
+
class RstObj:
|
|
55
|
+
"""
|
|
56
|
+
The base restructured text object.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def template_name(self) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Find template file.
|
|
63
|
+
"""
|
|
64
|
+
return "{}.{}.rst".format(self.__module__, self.__class__.__name__)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def template(self) -> Template:
|
|
68
|
+
"""
|
|
69
|
+
Return ``jinja2.Template`` instance.
|
|
70
|
+
"""
|
|
71
|
+
return env.get_template(self.template_name)
|
|
72
|
+
|
|
73
|
+
def render(
|
|
74
|
+
self,
|
|
75
|
+
indent: int | None = None,
|
|
76
|
+
first_line_indent: int | None = None,
|
|
77
|
+
**kwargs,
|
|
78
|
+
) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Render this object into text.
|
|
81
|
+
|
|
82
|
+
:param indent: global indent. Indent length can be changed in
|
|
83
|
+
:attr:`rstobj.option.Options.tab`.
|
|
84
|
+
|
|
85
|
+
:param first_line_indent: sometimes we only need to indent
|
|
86
|
+
first line, this option will overwrite the ``indent`` argument.
|
|
87
|
+
|
|
88
|
+
:param kwargs: other optional arguments.
|
|
89
|
+
"""
|
|
90
|
+
out = self.template.render(obj=self, tab=Options.tab)
|
|
91
|
+
if indent or first_line_indent:
|
|
92
|
+
origin_lines = out.split("\n")
|
|
93
|
+
target_lines = [
|
|
94
|
+
_indent_line(line, Options.tab * (indent or 0)) for line in origin_lines
|
|
95
|
+
]
|
|
96
|
+
if first_line_indent is not None:
|
|
97
|
+
if first_line_indent >= 0:
|
|
98
|
+
target_lines[0] = _indent_line(
|
|
99
|
+
origin_lines[0],
|
|
100
|
+
Options.tab * first_line_indent,
|
|
101
|
+
)
|
|
102
|
+
else: # pragma: no cover
|
|
103
|
+
raise TypeError
|
|
104
|
+
out = "\n".join(target_lines)
|
|
105
|
+
return out
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def str_or_render(
|
|
109
|
+
value: str | RstObj,
|
|
110
|
+
**kwargs,
|
|
111
|
+
) -> str:
|
|
112
|
+
"""
|
|
113
|
+
If it is a string type, then just return. If it is a RstObj type,
|
|
114
|
+
then return the rendered string.
|
|
115
|
+
"""
|
|
116
|
+
if isinstance(value, RstObj):
|
|
117
|
+
return value.render(**kwargs)
|
|
118
|
+
else:
|
|
119
|
+
return str(value)
|
|
120
|
+
|
|
121
|
+
@staticmethod
|
|
122
|
+
def _render_nested(
|
|
123
|
+
value: str | RstObj,
|
|
124
|
+
width: int,
|
|
125
|
+
) -> str:
|
|
126
|
+
"""
|
|
127
|
+
Render a list item or table cell whose text starts after a marker
|
|
128
|
+
``width`` characters wide, such as ``"- "`` or ``" * - "``.
|
|
129
|
+
|
|
130
|
+
A :class:`RstObj` is rendered without its leading and trailing blank
|
|
131
|
+
lines, and all its continuation lines are indented. A string is taken
|
|
132
|
+
as reStructuredText; its continuation lines are indented unless they
|
|
133
|
+
already are.
|
|
134
|
+
"""
|
|
135
|
+
if isinstance(value, RstObj):
|
|
136
|
+
text = value.render().strip("\n")
|
|
137
|
+
return _indent_continuation(text, width, force=True)
|
|
138
|
+
else:
|
|
139
|
+
return _indent_continuation(str(value), width, force=False)
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import typing as T
|
|
6
|
-
from dataclasses import dataclass
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import field
|
|
7
8
|
from ..base import RstObj
|
|
8
9
|
|
|
9
10
|
|
|
@@ -13,6 +14,8 @@ class Directive(RstObj):
|
|
|
13
14
|
name: str | None = field(default=None)
|
|
14
15
|
|
|
15
16
|
meta_directive_keyword: T.ClassVar[str | None] = None
|
|
17
|
+
#: False for a directive that has no ``:name:`` option, such as ``contents``.
|
|
18
|
+
meta_supports_name: T.ClassVar[bool] = True
|
|
16
19
|
|
|
17
20
|
@property
|
|
18
21
|
def arg(self): # pragma: no cover
|
|
@@ -22,6 +22,9 @@ class Code(RstObj):
|
|
|
22
22
|
@dataclass(kw_only=True)
|
|
23
23
|
class CodeBlockEmpty(Directive):
|
|
24
24
|
"""
|
|
25
|
+
A literal block (``::``). It has no options, so ``class_`` and ``name``
|
|
26
|
+
are not rendered.
|
|
27
|
+
|
|
25
28
|
Example::
|
|
26
29
|
|
|
27
30
|
code = "your code ..."
|
|
@@ -92,7 +95,7 @@ Example::
|
|
|
92
95
|
|
|
93
96
|
Output::
|
|
94
97
|
|
|
95
|
-
.. code-block
|
|
98
|
+
.. code-block:: {}
|
|
96
99
|
|
|
97
100
|
your code ...
|
|
98
101
|
""".strip()
|
|
@@ -7,7 +7,8 @@ image related directives.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import typing as T
|
|
10
|
-
from dataclasses import dataclass
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from dataclasses import field
|
|
11
12
|
from .base import Directive
|
|
12
13
|
|
|
13
14
|
|
|
@@ -21,9 +22,12 @@ class Image(Directive):
|
|
|
21
22
|
:param uri: required.
|
|
22
23
|
:param height: optional.
|
|
23
24
|
:param width: optional.
|
|
24
|
-
:param scale: optional
|
|
25
|
+
:param scale: optional, a percentage, rendered as ``:scale: 50%``.
|
|
25
26
|
:param alt_text: optional.
|
|
26
27
|
:param align: optional. one of :class:`Image.AlignOptions`.
|
|
28
|
+
``"top"``, ``"middle"`` and ``"bottom"`` are only valid for an image
|
|
29
|
+
inside a substitution definition; a standalone image accepts
|
|
30
|
+
``"left"``, ``"center"`` and ``"right"``.
|
|
27
31
|
|
|
28
32
|
Example::
|
|
29
33
|
|
|
@@ -7,7 +7,8 @@ table of content directive.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
import typing as T
|
|
10
|
-
from dataclasses import dataclass
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from dataclasses import field
|
|
11
12
|
from .base import Directive
|
|
12
13
|
|
|
13
14
|
|
|
@@ -22,6 +23,9 @@ class TableOfContent(Directive):
|
|
|
22
23
|
:param depth: int, optional.
|
|
23
24
|
:param local: bool, optional.
|
|
24
25
|
|
|
26
|
+
The ``contents`` directive has no ``:name:`` option, so ``name`` is
|
|
27
|
+
not rendered.
|
|
28
|
+
|
|
25
29
|
:type backlinks: str
|
|
26
30
|
:param backlinks: optional. one of
|
|
27
31
|
:attr:`TableOfContent.BacklinksOptions`.
|
|
@@ -43,6 +47,7 @@ class TableOfContent(Directive):
|
|
|
43
47
|
backlinks: str | None = field(default=None)
|
|
44
48
|
|
|
45
49
|
meta_directive_keyword: T.ClassVar[str] = "contents"
|
|
50
|
+
meta_supports_name: T.ClassVar[bool] = False
|
|
46
51
|
|
|
47
52
|
class BacklinksOptions:
|
|
48
53
|
"""
|
|
@@ -16,8 +16,9 @@ Default `section header line <http://docutils.sourceforge.net/docs/user/rst/quic
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
import
|
|
20
|
-
from dataclasses import dataclass
|
|
19
|
+
import unicodedata
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from dataclasses import field
|
|
21
22
|
from ..base import RstObj
|
|
22
23
|
|
|
23
24
|
dash_char_list = " _~"
|
|
@@ -39,6 +40,23 @@ def to_label(title: str) -> str:
|
|
|
39
40
|
])
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
def _display_width(text: str) -> int:
|
|
44
|
+
"""
|
|
45
|
+
The number of columns ``text`` takes on screen, as docutils counts them
|
|
46
|
+
to check a section underline: wide East Asian characters and emoji count
|
|
47
|
+
as two columns, combining characters as zero.
|
|
48
|
+
"""
|
|
49
|
+
width = 0
|
|
50
|
+
for char in text:
|
|
51
|
+
if unicodedata.combining(char):
|
|
52
|
+
continue
|
|
53
|
+
if unicodedata.east_asian_width(char) in ("W", "F"):
|
|
54
|
+
width += 2
|
|
55
|
+
else:
|
|
56
|
+
width += 1
|
|
57
|
+
return width
|
|
58
|
+
|
|
59
|
+
|
|
42
60
|
HEADER_CHAR_MAPPER = {
|
|
43
61
|
1: "=",
|
|
44
62
|
2: "-",
|
|
@@ -92,7 +110,7 @@ class Header(RstObj):
|
|
|
92
110
|
if self.auto_label and (self.ref_label is None):
|
|
93
111
|
self.ref_label = to_label(self.title)
|
|
94
112
|
|
|
95
|
-
title_length =
|
|
113
|
+
title_length = _display_width(self.title)
|
|
96
114
|
if self.bar_length is None:
|
|
97
115
|
self.bar_length = title_length
|
|
98
116
|
elif self.bar_length < title_length:
|
|
@@ -112,12 +130,14 @@ class HeaderLevel(Header):
|
|
|
112
130
|
pass
|
|
113
131
|
|
|
114
132
|
|
|
133
|
+
# Starts with a newline: autodoc dedents every line after the first, so the
|
|
134
|
+
# literal block must be indented relative to "Example::" on a later line.
|
|
115
135
|
header_doc_string = """
|
|
116
|
-
Example::
|
|
136
|
+
Example::
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
"""
|
|
138
|
+
Header{level}
|
|
139
|
+
{bar}
|
|
140
|
+
"""
|
|
121
141
|
|
|
122
142
|
|
|
123
143
|
def _build_doc_string(header_level: int) -> str:
|
|
@@ -15,6 +15,7 @@ class PathEnum:
|
|
|
15
15
|
absolute paths to eliminate current directory dependencies and ensure consistent path
|
|
16
16
|
resolution across different execution contexts and DevOps workflows.
|
|
17
17
|
"""
|
|
18
|
+
|
|
18
19
|
@cached_property
|
|
19
20
|
def dir_home(self):
|
|
20
21
|
return Path.home()
|
|
@@ -24,10 +25,9 @@ class PathEnum:
|
|
|
24
25
|
|
|
25
26
|
# Source Code
|
|
26
27
|
dir_package = _dir_here
|
|
27
|
-
path_version_py = dir_package / "_version.py"
|
|
28
28
|
path_pyproject_toml = dir_project_root / "pyproject.toml"
|
|
29
29
|
path_requirements_txt = dir_project_root / "requirements.txt"
|
|
30
|
-
path_authors = dir_project_root / "AUTHORS.
|
|
30
|
+
path_authors = dir_project_root / "AUTHORS.rst"
|
|
31
31
|
path_license = dir_project_root / "LICENSE.txt"
|
|
32
32
|
path_release_history = dir_project_root / "release-history.rst"
|
|
33
33
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from jinja2 import Environment
|
|
5
|
+
from jinja2 import FileSystemLoader
|
|
6
|
+
from jinja2 import StrictUndefined
|
|
7
|
+
|
|
8
|
+
# StrictUndefined: a typo in a template raises instead of rendering nothing.
|
|
9
|
+
env = Environment(
|
|
10
|
+
loader=FileSystemLoader(os.path.dirname(__file__)),
|
|
11
|
+
undefined=StrictUndefined,
|
|
12
|
+
)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
.. {{ obj.meta_directive_keyword }}::{% if obj.arg %} {{ obj.arg }}{% endif %}
|
|
2
|
+
{%- if obj.class_ is not none %}
|
|
3
|
+
{{ tab }}:class: {{ obj.class_ }}
|
|
4
|
+
{%- endif %}
|
|
5
|
+
{%- if obj.name is not none and obj.meta_supports_name %}
|
|
6
|
+
{{ tab }}:name: {{ obj.name }}
|
|
7
|
+
{%- endif %}
|
|
8
|
+
{%- block other %}{% endblock %}
|