Developer Guide

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.

Getting started

System dependencies

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

Windows-specific: Long Path support

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.

Installing StrictDoc from GitHub (developer mode)

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
python 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.

Invoke for development tasks

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

Main “Check” task

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).

Python code

  • 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.

  • 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 a_, b_ in foo:
    # use a_, b_ 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.

  • 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.

Git workflow

  • 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. The preferred approach: split the work into several independent Pull Requests 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:

context: description

where the context can be a major feature being added or a folder. A form of context: subcontext: description is also an option. Typical examples:

docs: fix links to the grammar.py

reqif: native: export/import roundtrip for multiline requirement fields

backend/dsl: switch to dynamic fields, with validation

Poetry: add filecheck as a dependency

  • Use comma-separated contexts, if the committed work is dedicated to more than one topic. Example:

server, UI: update to new requirement styles
  • When a contribution is simply an improvement of existing code without a change in the functionality, the commit should be named: Code climate: description. Example:

Code climate: fix all remaining major Pylint warnings

Frontend development

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

invoke server

Running End-to-End Web tests

invoke test-end2end

Running integration tests

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.

Documentation

  • 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.

Conventions

  • 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.