As an open source project, all cihai projects accept contributions through GitHub, GitLab and Codeberg. Below you will find resources on the internals of the project.


This guide applies to all cihai projects, not just the cihai repo.

Cihai projects use standard conventions and patterns based on best practices in python.

To be efficient at debugging, developing, testing, documenting, etc. it helps to familiarize yourself with the tool within, independently if needed.

<cihai-project> can be assumed to be an existing or future cihai project, including cihai, cihai-cli, unihan-etl, unihan-db. See GitHub, GitLab and Codeberg.

Development environment#

poetry is a required package to develop.

$ git clone<cihai-project>.git
$ cd <cihai-project>

So if <cihai-project> is [cihai]:

$ git clone
$ cd cihai

Install dependencies#

$ poetry install -E "docs test coverage lint format"

Makefile commands prefixed with watch_ will watch files and rerun.


[pytest] is used for tests.

$ poetry run py.test

Rerun on file change#

via pytest-watcher (works out of the box):

$ make start

via entr(1) (requires installation):

$ make watch_test

Manual (just the command, please)#

$ poetry run py.test


$ make test

pytest options#

For filename / test names within, examples will be for [cihai], if using a different cihai project check the filename and test names accordingly:

PYTEST_ADDOPTS can be set in the commands below. For more information read for the latest documentation.


$ env PYTEST_ADDOPTS="-verbose" make start

Pick a file:

$ env PYTEST_ADDOPTS="tests/" poetry run make start

Drop into test_cihai_version() in tests/

$ env PYTEST_ADDOPTS="-s -x -vv tests/" poetry run make start

Drop into test_cihai_version() in tests/ and stop on first error:

$ env PYTEST_ADDOPTS="-s -x -vv tests/" poetry run make start

Drop into pdb on first error:

$ env PYTEST_ADDOPTS="-x -s --pdb" make start

If you have ipython installed:

$ env PYTEST_ADDOPTS="--pdbcls=IPython.terminal.debugger:TerminalPdb" make start
$ make test

You probably didn’t see anything but tests scroll by.

If you found a problem or are trying to write a test, you can file an on the tracker for the relevant cihai project.

Manual invocation#

Test only a file:

$ py.test tests/

will test the tests/ tests.

$ py.test tests/

tests test_cihai_version() inside of tests/

Multiple can be separated by spaces:

$ py.test tests/test_{conversion,exc}.py tests/


sphinx-autobuild will automatically build the docs, watch for file changes and launch a server.

From home directory: make start_docs From inside docs/: make start

Manual documentation (the hard way)#

cd docs/ and make html to build. make serve to start http server.

Helpers: make build_docs, make serve_docs

Rebuild docs on file change: make watch_docs (requires entr(1))

Rebuild docs and run server via one terminal: make dev_docs (requires above, and a make(1) with -J support, e.g. GNU Make)

View documentation locally#

To find the URL of the preview server, read the terminal, the URL may very depending on the project! An example of what to look for:

[I 220816 14:43:41 server:335] Serving on


The project uses black and isort (one after the other). Configurations are in pyproject.toml and setup.cfg:

  • make black isort: Run black first, then isort to handle import nuances


flake8 and mypy run via CI in our GitHub Actions. See the configuration in pyproject.toml and setup.cfg.


flake8 provides fast, reliable, barebones styling and linting.


$ poetry run flake8

If you setup manually:

$ flake8
$ make flake8
$ make watch_flake8

requires entr(1).

See [flake8] in setup.cfg.

inline-quotes = single
max-line-length = 88
max-complexity = 10
exclude = .git,
# Stuff we ignore thanks to black:
extend-ignore = E203,W503


mypy is used for static type checking.


$ poetry run mypy .

If you setup manually:

$ mypy .
$ make mypy
$ make watch_mypy

requires entr(1).


Since this software used in production projects, we don’t want to release breaking changes.

Choose what the next version is. Assuming it’s version 0.9.0, it could be:

  • 0.9.0post0: postrelease, if there was a packaging issue

  • 0.9.1: bugfix / security / tweak

  • 0.10.0: breaking changes, new features

Let’s assume we pick 0.9.1

CHANGES: Assure any PRs merged since last release are mentioned. Give a thank you to the contributor. Set the header with the new version and the date. Leave the “current” header and Insert changes/features/fixes for next release here at the top:

package-name 0.10.x (unreleased)
- *Insert changes/features/fixes for next release here*

package-name 0.9.1 (2020-10-12)
- :issue:`1`: Fix bug

package_name/ and - Set version

$ git commit -m 'Tag v0.9.1'
$ git push

Important: Create and push the tag. Make sure the version is correct and the pyproject.toml and match the version being deployed.

$ git tag v0.9.1
$ git push --tags

Automated deployment#

CI will automatically push to the PyPI index when a tag is pushed.

Manual deployment#


This requires PyPI access.

In addition to virtualenv creation and installing dependencies, poetry handles building the package and publishing. Therefore there is no or requirements files.

$ poetry build
$ poetry deploy