Contributing#

This file is describing our code style and some other documentation about contributions. You must read it before your first contribution.

Also note that this all is just recommendations, you can use anything in some cases, if it will be better than solution that we propose here. However, we will prefer these recommendations when we will review your contribution.

Language#

All contributions, all code, all comments, all commits and everything else must be in English.

make test#

This «magic» command collects almost all of our CI. If you’re on Windows, try Chocolatey to run make.

Also, because of conflict between pytest-testmon and pytest-cov we use option --no-cov in pytest, so in this way we give prioritize to pytest-testmon. If you want to generate a report with pytest-cov, use make test ci=1.

pre-commit#

Furthermore, you can bind make test (plus some additional useful checks) to run on every commit, so you will always sure that CI will never fail. Just run pre-commit install.

Commit naming style#

Every commit must have one small change. We also use the Tim Pope commit message template for commits“ messages.

If you worry about clean git log - just don’t. We use squash strategy for merging PRs.

Code Style#

We use black for almost all style control. We’re also trying to use formatters instead of linters, where it is possible.

Furthermore, we also have some rules that black doesn’t cover. It includes:

Imports#

We have isort and pycln for imports control. The first used for sorting imports, and second, to remove unused imports. All other rules are not covered by linters/formatters etc. You should check those yourself.

You should use import module and from package import module whenever it’s possible, but sometimes from module import ... way is more useful. For example:

from dataclasses import dataclass

If you’re using only one variable from a module, and it’s readable without its parent name, you should use from module import .... But if you’re using many variables from module, better would be import module/from package import module:

from functools import cached_property

And can you use as? You must not use as when alias will be the same as actual name because in that way type checker add import statement to auto-generated __all__ variable.

# some/file.py
from some import more as more
# some/main.py
from some import file

file.more(...)  # no error!

You maybe notice that import module and from package import module are written with /, this is because you must use first one, if no package exist. But if there is a package - you must use from package import module. Look at these examples:

import os
from my_project import config
from my_project import models

Notice that there aren’t any relative imports, you can’t use it here.

Also, you must specify __all__ variable in all __init__.py files with any code (not one docstring). Reason of this limitation is that pycln and docs can’t know exactly, do you want to add imports as alias, or this import is for using in code which in this file. pycln will ignore these imports, and docs will duplicate documentation for anything that you will import.

Docstrings#

We’re using flake8 for checking docstrings presence and their quality. They later in API documentation. You must write docstrings everywhere, except __init__ methods (not the same as __init__.py files) because those will not go to documentation.

I also recommend reading Google styleguide about docstrings because we’re using Google style in docstrings.

Markup#

Because of Sphinx’s limitations, we must use ReST markup in docstrings. This allows us to use cross-references to other functions or even projects.

Read more about ReST markup and Sphinx’s cross-references.

__init__.py docstrings#

They describe a package (folder) with modules (.py files).

Module, function, class, method based docstrings#

It is a short description of an item. They must follow Google styleguide about docstrings.

Variable-based docstrings#

They must follow in format:

some_variable = "abc"
"""This ``some`` variable used in :class:`.SomeClass`."""

This applies for the module level, and class attributes in dataclasses/classes generated by attrs. This won’t work in __init__ methods because they don’t actually go to documentation. Attributes should be documented in class-based docstring, in Attributes section.

At now, linter doesn’t detect them. Better sometimes check that all in API documentation actually documented.

pyproject.toml#

In this file, we configure only poetry (except for black, it supports only pyproject.toml file for configuration).

Groups#

Since poetry 1.2 a new feature appeared - groups. It allows downloading only those groups of packages, which you will need. We have four groups - make, tests, docs and github_hooks. Every of them answers about their function with name.

  • make: All required dependencies for make test.

  • tests: All required dependencies for tests.

  • docs: All required dependencies for building documentation.

  • github_hooks: All required dependencies for GitHub hooks.

Please see Managing dependencies in poetry.

Versions#

All versions must follow in format X.Y.z (absolute version) or (~X.Y). Last one will compile in >=X.Y.0,<X.Y+1.0. So for example we have last version for abstract dependency 1.2.3, we specify its version to ~1.2, so it will compile to >=1.2.0,<1.3.0 (any patch version for 1.2 is accepted, but not 1.3+). If 1.3.0 will be released - dependabot will create PR for it.

Read more about Semantic Versions and Dependency specification in poetry.

Translations#

If you don’t know languages which we support - left translation on us.

To update .po files run make translate, after that, you can edit translations in .po files, which can be found as locales/<language's tag>/LC_MESSAGES/messages.po or locale/<language's tag>/LC_MESSAGES/ in docs. After editing, for compilation, you can run one more time make translate (or make html in docs).

To add new language, use pybabel init -i locales/base.pot -l <language's tag> -d locales or sphinx-intl update -l <language's tag> for docs.

P.S. Language’s tag it is short name of this language, example en or en_EN. A full list of supported languages can be found with pybabel --list-locales.

Documentation#

We use Sphinx for documentation and docstrings for API documentation. At now, there is no actual styles here, except doc8.

Other Help#

You can contribute by spreading a word about this library. It would also be a huge contribution to write a short article on how you are using this project. You can also share your best practices with us.