diff --git a/.cruft.json b/.cruft.json index 338f454..6fe7c8f 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/sphinx-notes/cookiecutter", - "commit": "634c4022e575bd086ea47f3b42feafe24e14a939", + "commit": "62cd96195962da3392cdc34125c95e9144a5f5ca", "checkout": null, "context": { "cookiecutter": { @@ -20,7 +20,7 @@ "sphinx_version": "7.0", "development_status": "3 - Alpha", "_template": "https://github.com/sphinx-notes/cookiecutter", - "_commit": "634c4022e575bd086ea47f3b42feafe24e14a939" + "_commit": "62cd96195962da3392cdc34125c95e9144a5f5ca" } }, "directory": null diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 638156d..7f56f2c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,5 +12,14 @@ jobs: - uses: actions/setup-python@v5 with: python-version-file: 'pyproject.toml' - - run: python3 -m pip install .[dev] + - run: python3 -m pip install .[test] - run: make test + doctest: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v5 + with: + python-version-file: 'pyproject.toml' + - run: python3 -m pip install .[docs] + - run: make doctest diff --git a/Makefile b/Makefile index 06a9e9d..6e7539a 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,11 @@ fmt: .PHONY: test test: - $(PY) -m unittest discover -s tests -v + $(PY) -m pytest tests/ -v + +.PHONY: doctest +doctest: + $(MAKE) doctest -C docs/ ################################################################################ # Distribution Package diff --git a/docs/api.rst b/docs/api.rst index 7c6f31b..b153370 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -2,6 +2,66 @@ API Reference ============= -.. note:: WIP +Data Types +========== -.. automodule:: sphinxnotes.render.pipeline +.. autotype:: sphinxnotes.render.PlainValue +.. autotype:: sphinxnotes.render.Value + +.. autoclass:: sphinxnotes.render.RawData +.. autoclass:: sphinxnotes.render.ParsedData + +.. autoclass:: sphinxnotes.render.Field +.. autoclass:: sphinxnotes.render.Schema + +.. autoclass:: sphinxnotes.render.data.Registry + + .. automethod:: add_type + .. automethod:: add_form + .. automethod:: add_flag + .. automethod:: add_by_option + + .. autotype:: sphinxnotes.render.data.ByOptionStore + +The Render Pipeline +=================== + +Context +------- + +.. autoclass:: sphinxnotes.render.PendingContext +.. autotype:: sphinxnotes.render.ResolvedContext +.. autoclass:: sphinxnotes.render.UnparsedData + +.. autoclass:: sphinxnotes.render.pending_node + +Extra Context +------------- + +.. autoclass:: sphinxnotes.render.ExtraContextGenerator +.. autoclass:: sphinxnotes.render.ExtraContextRegistry + +Template +-------- + +.. autoclass:: sphinxnotes.render.Template +.. autoclass:: sphinxnotes.render.Phase + +Pipeline +-------- + +.. autoclass:: sphinxnotes.render.BaseContextRole +.. autoclass:: sphinxnotes.render.BaseContextDirective +.. autoclass:: sphinxnotes.render.BaseDataDefineRole +.. autoclass:: sphinxnotes.render.BaseDataDefineDirective +.. autoclass:: sphinxnotes.render.StrictDataDefineDirective + +Registry +======== + +.. autodata:: sphinxnotes.render.REGISTRY + +.. autoclass:: sphinxnotes.render.Registry + + .. autoproperty:: data + .. autoproperty:: extra_context diff --git a/docs/conf.py b/docs/conf.py index a38abc8..893b5a3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,6 +23,7 @@ # ones. extensions = [ 'sphinx.ext.githubpages', + 'sphinx.ext.doctest', 'sphinx_design', 'sphinx_copybutton', 'sphinx_last_updated_by_git', @@ -124,3 +125,5 @@ _ = extensions.pop() # no need to load extension primary_domain = 'py' + +extensions.append('sphinx.ext.doctest') diff --git a/docs/dsl.rst b/docs/dsl.rst new file mode 100644 index 0000000..0c1f945 --- /dev/null +++ b/docs/dsl.rst @@ -0,0 +1,239 @@ +===================== +Field Declaration DSL +===================== + +.. default-domain:: py +.. highlight:: python +.. role:: py(code) + :language: Python + + +The Field Declaration DSL is a Domain Specific Language (DSL) that used to +define the type and structure of field values. A DSL declaration consists of +one or more :term:`modifier`\ s separated by commas (``,``). + +Python API +========== + +User can create a :class:`sphinxnotes.render.Field` from DSL and use it to parse +string to :type:`sphinxnotes.render.Value`: + +>>> from sphinxnotes.render import Field +>>> Field.from_dsl('list of int').parse('1,2,3') +[1, 2, 3] + +Syntax +====== + +.. productionlist:: + dsl : modifier ("," modifier)* + modifier : type_modifier | form_modifier | flag | by_option + +.. glossary:: + + Modifier + There are four categories of modifiers: + + Type modifier + Specifies the element type (scalar value) + + Form modifier + Specifies a container type with element type + + Flag + A boolean flag (either on or off) + + By-Option + A key-value option + +Type +==== + +A type modifier specifies the data type of a single (scalar) value. + +.. list-table:: + :header-rows: 1 + + * - Modifier + - Type + - Aliases + - Description + * - ``bool`` + - :py:class:`bool` + - ``flag`` + - Boolean: ``true``/``yes``/``1``/``on``/``y`` → True, ``false``/``no``/``0``/``off``/``n`` → False + * - ``int`` + - :py:class:`int` + - ``integer`` + - Integer + * - ``float`` + - :py:class:`float` + - ``number``, ``num`` + - Floating-point number + * - ``str`` + - :py:class:`str` + - ``string`` + - String. If looks like a Python literal (e.g., ``"hello"``), it's parsed accordingly. + +Examples: + +======= ========= ============= +DSL Input Result +------- --------- ------------- +``int`` ``42`` :py:`42` +``str`` ``hello`` :py:`"hello"` +======= ========= ============= + +Form +==== + +A form modifier specifies a container type with its element type, using +``