StrictDoc Documentation
Developer Guide

Developer Guide

VERSION: Git commit: 0.22.0-37-g948818e, Git branch: HEAD
DATE: 2026-06-03 23:28:22
STATEMENT:

This section contains everything that a StrictDoc developer/contributor should know to get the job done.

StrictDoc is based on Python and maintained as any other Python package on GitHub: with linting, tests, and hopefully enough best practice put into the codebase.

The instructions and conventions described below are a summary of what is considered to be the currently preferred development style for StrictDoc.

Any feedback on this development guide is appreciated.

DEVGUIDE_GETTING_STARTED

1. Getting started

MID: c069e4c7b4d04dd09eb73f940e947bfd
UID: DEVGUIDE_GETTING_STARTED

1.1. System dependencies

MID: 7477d397d0ca493f8e47532455d44965
STATEMENT:

StrictDoc itself mostly depends on other Python Pip packages, so there is nothing special to be installed.

You may need to install libtidy if you want to run the integration tests. The HTML markup validation tests depend on libtidy.

On Linux Ubuntu:

sudo apt install tidy

From the core Python packages, StrictDoc needs Invoke, Tox and TOML:

pip install invoke tox toml

1.1.1. Windows-specific: Long Path support

MID: b7a21564c30043acbf1733d9b47bd0bc
STATEMENT:

As reported by a user, Windows Long Path support has to be enabled on a Windows system.

You can find information on how to enable the long paths support at https://pip.pypa.io/warnings/enable-long-paths.

1.2. Installing StrictDoc from GitHub (developer mode)

MID: 89ec30b9f67c489da635be391b0b0e15
STATEMENT:

Note

Use this way of installing StrictDoc only if you want to make changes in StrictDoc's source code. Otherwise, install StrictDoc as a normal Pip package by running pip install strictdoc.

git clone https://github.com/strictdoc-project/strictdoc.git && cd strictdoc
pip install toml
python3 developer/pip_install_strictdoc_deps.py
python3 strictdoc/cli/main.py

The pip_install_strictdoc_deps.py installs all dependencies of StrictDoc, but not StrictDoc itself.

1.2.1. Development within a virtual environment

MID: 1814a8c25afc40cba6dca1696cae119b
STATEMENT:

An alternative approach for those familiar with development in a virtual environment:

git clone https://github.com/strictdoc-project/strictdoc.git && cd strictdoc
python3 -m venv .venv
. ./.venv/bin/activate
pip install -e .
python3 strictdoc/cli/main.py

Note

Windows users, substitute: .\.venv\Scripts\activate

After this, running developer/pip_install_strictdoc_deps.py should report that all packages are already installed.

This installs all the default dependencies of StrictDoc. To also install the dev dependencies specified in pyproject.toml:

pip install ".[development]"

2. Invoke for development tasks

MID: 90d5d79146e64660a71cd74ac23f6811
STATEMENT:

All development tasks are managed using Invoke in the tasks.py file. On macOS and Linux, all tasks run in dedicated virtual environments. On Windows, invoke uses the parent pip environment, which can be a system environment or a user's virtual environment.

Make sure to familiarize yourself with the available developer tasks by running:

invoke --list

3. Main "Check" task

MID: e7ce6a2f13614974b43aba56f6ae7253
STATEMENT:

Before doing anything else, run the main check command to make sure that StrictDoc passes all tests on your system:

invoke check

The check command runs all StrictDoc lint and test tasks with the only exception of end-to-end Web tests that are run with a separate task (see below).

DEVGUIDE_PYTHON_CODE

4. Python code

MID: 07d9a3c17be8475089844f5eb2cd8299
UID: DEVGUIDE_PYTHON_CODE
STATEMENT:
  • The version of Python is set to be as low as possible given some constraints of StrictDoc's dependencies. Ideally, the lowest Python version should only be raised when it is consistently deprecated by major software platforms like Ubuntu or GitHub Actions.
  • All developer tasks are collected in the tasks.py which is run by Invoke tool. Run the invoke --list command to see the list of available commands.
  • Formatting is governed by black which reformats the code automatically when the invoke check command is run.
    • If a string literal gets too long, it should be split into a multiline literal with each line being a meaningful word or a subsentence.
  • Avoid shortening variable names unnecessarily. For example, use 'buffer' instead of 'buf', 'document' instead of 'doc', 'function' instead of 'func', 'length' instead of 'len', etc. Note: While some older parts of StrictDoc may not adhere to this guideline, they are planned to be refactored in the future.
  • For "element is non-None" checks, a full form shall be used, for example: if foo is not None instead of if foo. This helps to avoid any confusion with all sorts of strings (empty or non-empty str, Optional[str]) that are used extensively in StrictDoc's codebase. The non-None and non-empty string check shall therefore be as follows: if foo is not None and len(foo) > 0. The explicit check also applies to any other kinds of objects besides strings: if foo is not None instead of if foo. Rationale: if foo makes it unclear whether the intention is to check is not None or also len(foo) > 0.
  • For lambdas and short for loops, the recent convention is to add _ to the variables of a for loop or a lambda to visually highlight their temporary use within the current scope which is done to counter the fact that these variables can leak and be used outside of the scope of the loop. Example:
for foo_, bar_ in baz:
    # use foo_, bar_ within the loop.
  • The function arguments with the default values shall be avoided. This convention improves the visibility of the function interfaces at the coast of increased verbosity which is the price that StrictDoc development is willing to pay, maintaining the software long-term. The all-explicit function parameters indication is especially useful when the large code refactorings are made.
  • StrictDoc has been making a gradual shift towards a stronger type system. Although type annotations haven't been added everywhere in the codebase, it is preferred to include them for all new code that is written.
  • For opening files, use the helpers file_open_read_utf8 and file_open_read_bytes. These helpers perform normal file opening but also strip the UTF-8 BOM character, which is added by some Windows tools.
  • If a contribution includes changes in StrictDoc's code, at least the integration-level tests should be added to the tests/integration. If the contributed code needs a fine-grained control over the added behavior, adding both unit and integration tests is preferred. The only exception where a contribution can contain no tests is "code climate" which is work which introduces changes in code but no change to the functionality.
DEVGUIDE_GIT_WORKFLOW

5. Git workflow

MID: 6e9707c99199464583f091056d1ae458
UID: DEVGUIDE_GIT_WORKFLOW
STATEMENT:

The preferred Git workflow is "1 commit per 1 PR". If the work truly deserves a sequence of commits, each commit shall be self-contained and pass all checks from the invoke check command.

For larger pull requests involving too many changes, the preferred approach: split the work into several independent PRs to simplify the work of the reviewer.

The branch should be always rebased against the main branch. The git fetch && git rebase origin/main is preferred over git fetch && git merge main.

The Git commit message should follow the format of Conventional Commits:

<type>(<optional scope>): <description>

where the scope can be a major feature being added or a folder.

A form of <type>(scope): <subscope>: <description> is also an option.

Examples:

feat(html2pdf): add a new option to force page breaks
fix(backend/sdoc_source_code): add Rust support
refactor(cli): migrate "import excel" to command pattern
chore(cli): rename shared.py -> _shared.py
chore(.github): switch the macOS tests to macos-latest
docs: update release notes

If the committed work is dedicated to more than one topic, use comma-separated scopes. Example:

refactor(server, UI): update to new requirement styles

Note

The Conventional Commits specification lists scope as optional. However, most StrictDoc commits do have a scope. Most of the time, there is a specific feature or area being worked on, so it should be straightforward to identify the right scope.

6. Frontend development

MID: 2420b2d7a4624bd99b0f8088dfa9abff
STATEMENT:

The shortest path to run the server when the StrictDoc's source code is cloned:

invoke server

7. Running End-to-End Web tests

MID: 73c717608cce4abca2c768f1dbfb705d
STATEMENT:
invoke test-end2end

8. Running integration tests

MID: 240dc1426d994397ac1401834b4f0ef0
STATEMENT:

The integration tests are run using Invoke:

invoke test-integration

The --focus parameter can be used to run only selected tests that match a given substring. This helps to avoid running all tests all the time.

invoke test-integration --focus <keyword>

See How to test command-line programs with Python tools: LIT and FileCheck to learn more about LIT and FileCheck, which enable the StrictDoc integration tests.

9. Documentation

MID: 7b4bf839b1c44b0d82001060596d8cbf
STATEMENT:
  • Every change in the functionality or the infrastructure should be documented.
  • Every line of documentation shall be no longer than 80 characters. StrictDoc's own documentation has a few exceptions, however, the latest preference is given to 80 characters per line. Unfortunately, until there is automatic support for mixed SDoc/RST content, all long lines shall be edited and split by a contributor manually.
  • The invoke docs task should be used for re-generating documentation on a developer machine.

10. Conventions

MID: a44e3b99323b4ac291f4b4d3948c3cc0
STATEMENT:
  • snake_case everywhere, no kebab-case.
    • This rule applies everywhere where applicable: file and folder names, HTML attributes.
    • Exception: HTML data-attributes and testid identifiers.

11. Playbooks

MID: 57300bfdfcaf47c2ab7b29c3e6ffb75d

11.1. How to update release notes

MID: e30a730c91d94de28b62f471f0a67e04
STATEMENT:
  • Add to the release notes (docs/strictdoc_04_release_notes.sdoc) a high-level, human-readable summary of all work since the latest Git tag.
  • Summarize all changes under the top section called "Unreleased". If that section does not exist yet, create it. It is this section that will later be turned into the actual release notes.
  • The section shall start with This release contains the following enhancements:<full empty line break>.
  • Follow the example of the existing Release Notes.
  • Do not summarize each and every commit. Only include the most important user-facing changes that are implemented with feat: and fix: commits. Don't consider commits with any other prefix.
  • Ignore the changelog file CHANGELOG.md completely. It is an outdated auto-generated file that is going to be removed in the future.
  • Do not try to create a new version yourself. This is handled with another playbook (TBD).
  • Do not modify any existing content from the previous releases.