This code should work equivalently on MacOS, Linux, and Windows.
This project uses UV to manage Python packaging and dependencies. Most day-to-day tasks (such as running unit tests from the command line) are orchestrated through UV.
A coding standard is enforced using Ruff. Python 3 type hinting is validated using MyPy.
We rely on pre-commit hooks to ensure that the code is properly-formatted,
clean, and type-safe when it's checked in. The run install step described
below installs the project pre-commit hooks into your repository. These hooks
are configured in .pre-commit-config.yaml.
If necessary, you can temporarily disable a hook using Git's --no-verify
switch. However, keep in mind that the CI build on GitHub enforces these
checks, so the build will fail.
The .gitattributes file controls line endings for the files
in this repository. Instead of relying on automatic behavior, the
.gitattributes file forces most files to have UNIX line endings.
All prerequisites are managed by UV. All you need to do install UV itself, following the instructions. UV will take care of installing the required Python interpreter and all of the dependencies.
Note: The development environment (the
runscript, etc.) expects a bash shell to be available. On Windows, it works fine with the standard Git Bash.
The run script provides shortcuts for common developer tasks:
$ ./run --help
------------------------------------
Shortcuts for common developer tasks
------------------------------------
Basic tasks:
- run install: Install the Python virtualenv and pre-commit hooks
- run update: Update all dependencies, or a subset passed as arguments
- run outdated: Find top-level dependencies with outdated constraints
- run rebuild: Rebuild all dependencies flagged as no-binary-package
- run format: Run the code formatters
- run checks: Run the code checkers
- run build: Build artifacts in the dist/ directory
- run test: Run the unit tests
- run test -c: Run the unit tests with coverage
- run test -ch: Run the unit tests with coverage and open the HTML report
- run suite: Run the complete test suite, as for the GitHub Actions CI build
- run suite -f: Run a faster version of the test suite, omitting some steps
- run clean: Clean the source tree
Additional tasks:
- run docs: Build the Sphinx documentation for readthedocs.io
- run docs -o: Build the Sphinx documentation and open in a browser
- run release: Tag and release the code, triggering GHA to publish artifacts
The Python interpreter version is controlled by the .python-version file. To
test with a different version of Python temporarily, set $UV_PYTHON in your
shell, and execute 'run install'. Make sure to unset and reinstall when done.
Visual Studio Code does a good job of separating user preferences from
workspace (or project) preferences, so workspace preferences are checked
into .vscode/settings.json. The only thing you really need to configure
is the interpreter.
Before going any further, make sure sure that you have installed UV and have a working bash shell. Then, run the suite and confirm that everything is working:
./run suite
Once you have a working shell development environment, open the uci-parse
directory in the IDE (i.e. code .).
Go to the command palette and search for interpreter. Choose Python:
Select Interpreter, then select the interpreter executable in the workspace.
This will be .venv\Scripts\python.exe on Windows, or .venv/bin/python on
Linux or MacOS.
Project configuration in .vscode/settings.json should configure the tests
properly. You can right click on src/tests in the explorer and run tests
from there, or do something similar for individual directories or .py files.
Workspace configuration includes a launch.json that should allow you to
run unit tests in the debugger.
Workspace configuration in .vscode/settings.json assumes that you have installed
the following plugins:
The Ruff and MyPy linters will run automatically on any file that you open, while you are editing the file. Language errors and linter warnings all show up in the Problems view.
Ruff is set up as the default workspace formatter for [python] code. If you
want, you can configure Ruff to format code and optimize imports on save, or
you can run those actions manually. (For instance, on Windows SHIFT-ALT-F
will format code using Ruff, and SHIFT-ALT-O will organize imports.)
PyCharm offers a good developer experience. However, the underlying configuration on disk mixes together project policy (i.e. preferences about which test runner to use) with system-specific settings (such as the name and version of the active Python interpreter). This makes it impossible to commit complete PyCharm configuration to the Git repository. Instead, the repository contains partial configuration, and there are instructions below about how to manually configure the remaining items.
Before going any further, make sure sure that you have installed UV and have a working bash shell. Then, run the suite and confirm that everything is working:
./run suite
Once you have a working shell development environment, Open (do not
Import) the uci-parse directory in PyCharm, then follow the remaining
instructions below. By using Open, the existing .idea directory will be
retained and all of the existing settings will be used.
As a security precaution, PyCharm does not trust any virtual environment
installed within the repository, such as the UV .venv directory. In the
status bar on the bottom right, PyCharm will report No interpreter. Click
on this error and select Add Interpreter. In the resulting dialog, click
Ok to accept the selected environment, which should be the UV virtual
environment.
Go to the PyCharm settings and find the uci-parse project. Under
Project Structure, mark src as a source folder and tests as a test
folder. In the Exclude Files box, enter the following:
LICENSE;NOTICE;PyPI.md;build;dist;docs/_build;out;uv.lock;run;.coverage;.coverage.lcov;.coveragerc;.gitattributes;.github;.gitignore;.htmlcov;.idea;.mypy_cache;.pre-commit-config.yaml;.pytest_cache;.python-version;.readthedocs.yaml;.ruff_cache;.run;.tabignore;.venv
When you're done, click Ok. Then, go to the gear icon in the project panel and uncheck Show Excluded Files. This will hide the files and directories in the list above.
In the PyCharm settings, go to Editor > Inspections and be sure that the Project Default profile is selected.
Unit tests are written using Pytest, and API documentation is written using Google Style Python Docstring. However, neither of these is the default in PyCharm. In the PyCharm settings, go to Python > Integrated Tools. Under Testing > Default test runner, select pytest. Under Docstrings > Docstring format, select Google.
Right click on the src/tests folder in the project explorer and choose Run
'pytest in tests'. Make sure that all of the tests pass. If you see a slightly
different option (i.e. for "Unittest" instead of "pytest") then you probably
skipped the preferences setup discussed above. You may need to remove the
run configuration before PyCharm will find the right test suite.
Optionally, you might want to set up external tools for some of common developer tasks: code reformatting and the Ruff and MyPy checks. One nice advantage of doing this is that you can configure an output filter, which makes the Ruff linter and MyPy errors clickable. To set up external tools, go to PyCharm settings and find Tools > External Tools. Add the tools as described below.
On Linux or MacOS, you can set up the external tools to invoke the run script
directly.
For this to work, it's important that tools like uv are on the system
path used by PyCharm. On Linux, depending on how you start PyCharm, your
normal shell environment may or may not be inherited. For instance, I had to
adjust the target of my LXDE desktop shortcut to be the script below, which
sources my profile before running the pycharm.sh shell script:
#!/bin/bash
source ~/.bash_profile
/opt/local/lib/pycharm/pycharm-community-2020.3.2/bin/pycharm.sh| Field | Value |
|---|---|
| Name | Format Code |
| Description | Run the Ruff code formatter |
| Group | Developer Tools |
| Program | $ProjectFileDir$/run |
| Arguments | format |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Checked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Unchecked |
| Make console active on message in stderr | Unchecked |
| Output filters | Empty |
| Field | Value |
|---|---|
| Name | Run MyPy Checks |
| Description | Run the MyPy code checks |
| Group | Developer Tools |
| Program | $ProjectFileDir$/run |
| Arguments | mypy |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Unchecked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Checked |
| Make console active on message in stderr | Checked |
| Output filters | $FILE_PATH$:$LINE$ |
| Field | Value |
|---|---|
| Name | Run Ruff Linter |
| Description | Run the Ruff linter code checks |
| Group | Developer Tools |
| Program | $ProjectFileDir$/run |
| Arguments | ruff |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Unchecked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Checked |
| Make console active on message in stderr | Checked |
| Output filters | $FILE_PATH$:$LINE$ |
On Windows, PyCharm has problems invoking the run script. The trick is to
invoke the Bash interpreter and tell it to invoke the run script. The
examples below assume that you have installed Git Bash in its standard location
under C:\Program Files\Git. If it is somewhere else on your system, just
change the path for bash.exe.
| Field | Value |
|---|---|
| Name | Format Code |
| Description | Run the Ruff code formatter |
| Group | Developer Tools |
| Program | C:\Program Files\Git\bin\bash.exe |
| Arguments | ./run format |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Checked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Unchecked |
| Make console active on message in stderr | Unchecked |
| Output filters | Empty |
| Field | Value |
|---|---|
| Name | Run MyPy Checks |
| Description | Run the MyPy code checks |
| Group | Developer Tools |
| Program | C:\Program Files\Git\bin\bash.exe |
| Arguments | ./run mypy |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Unchecked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Checked |
| Make console active on message in stderr | Checked |
| Output filters | $FILE_PATH$:$LINE$ |
| Field | Value |
|---|---|
| Name | Run Ruff Linter |
| Description | Run the Ruff linter code checks |
| Group | Developer Tools |
| Program | C:\Program Files\Git\bin\bash.exe |
| Arguments | ./run ruff |
| Working directory | $ProjectFileDir$ |
| Synchronize files after execution | Unchecked |
| Open console for tool outout | Checked |
| Make console active on message in stdout | Checked |
| Make console active on message in stderr | Checked |
| Output filters | $FILE_PATH$:$LINE$ |
Documentation at Read the Docs is generated via a GitHub hook. So, there is no formal release process for the documentation.
Code is released to PyPI. There is a partially-automated process to publish a new release.
Note: In order to publish code, you must must have push permissions to the GitHub repo.
Ensure that you are on the main branch. Releases must always be done from
main.
Ensure that the Changelog is up-to-date and reflects all of the changes that
will be published. The top line must show your version as unreleased:
Version 0.1.29 unreleased
Run the release command:
./run release 0.1.29
This command updates NOTICE and Changelog to reflect the release version
and release date, commits those changes, tags the code, and pushes to GitHub.
The new tag triggers a GitHub Actions build that runs the test suite, generates
the artifacts, publishes to PyPI, and finally creates a release from the tag.
Note: This process relies on a PyPI API token with upload permissions for the project. This token is stored in a GitHub Actions secret called
PYPI_TOKEN.