Our Dockerfile uses a base image and multi-stage builds to allow for creation of a testing and development image that is separate from the production image, which does not contain any test files. NOTE: Adding a Python package in requirements.txt will require rebuilding to ensure the new package is contained in the image.
To build the testing and development Docker image for the program, run the following command:
docker build --target test -t llcsim:dev .
This command builds the Docker image using the test
stage from the Dockerfile and tags it as llcsim
with the dev
version.
The image can be run in a container which will be removed on exit using the following command:
docker run --rm -it llcsim:dev bash
NOTE: Do not use this for development or any persistent changes to code. The container is ephemeral and is best suited for troubleshooting and automated testing purposes only.
This image will run the tests automatically using the following command:
docker run --rm -it llcsim:dev
To build the production Docker image, run the following command:
docker build --target production -t llcsim:latest .
This command builds the production Docker image and tags it as llcsim
with the latest
version.
After building the image, you can run the container using the command:
docker run --rm llcsim:latest
This will run main.py
and exit. The --rm
flag ensures that the container is removed after it exits.
To start the development environment, use the following command:
docker compose up -d dev && docker compose exec dev bash
This command does two things:
- Starts the
dev
service in detached mode (-d
), allowing you to run it in the background. - Executes a bash shell in the running
dev
container, where you can run the program.
Once inside the container, you can execute your main program using:
python main.py
You can run tests using the following:
python -m unittest
This runs all the tests. Run specific tests with the following:
python -m unittest tests.utils.test_cache_logger
Example of running a specific test file - in this case, the the test_cache_logger.py
file in /tests/utils
.
It may sometimes be helpful to rebuild to remove any stale images:
docker compose build --no-cache
After exiting the shell, the dev
container will still be running because it was started with a command that keeps it alive. You can stop this container by running:
docker compose down
Or you can connect to the shell again using docker compose exec dev bash
.
If you want to run the main program directly using docker compose
and let the container exit upon completion, you can use:
docker compose up app
In this case, the container will run the app
service defined in the docker-compose.yml
file, execute the program, and then stop when the program finishes.
The compose.yml
services may be extended in the future to support a test
service, which could be used in a Github Actions environment.
We use Black for formatting, Flake8 for linting, and isort to maintain consistent dependency import ordering and formatting. This section explains how to run each tool on the src
directory to check code quality, and how to apply changes if any issues are found.
Black is used to automatically format Python code to adhere to a consistent style.
- Check Only: To check if there are any formatting issues without modifying files, run:
black --check src/
or for additional detail:
black --diff src/
- Apply Formatting: To automatically format all files in the
src
directory, run:
black src/
- Overriding Automated Formatting: If there are specific lines or code blocks where you need to override Black’s automated formatting, you can use
# fmt: off
and# fmt: on
comments. Black will skip formatting for code between these markers.
# fmt: off
example = [
"This", "is", "a", "long", "list", "that", "we", "want", "to", "format",
"manually", "for", "readability", "even", "though", "Black", "would",
"normally", "format", "it", "differently"
]
# fmt: on
Place # fmt: off
before the code you want to exclude from formatting, and # fmt: on
after it. Use these sparingly, as consistent formatting across the codebase is preferred. See the Black documentation for more details.
Flake8 is used to identify potential errors, code style issues, and other coding standard violations.
Note: Flake8 does not modify files, so the following command is sufficient to identify any issues that need to be manually addressed.
flake8 src/
isort is used to organize imports in Python files.
- Check Only: To check for import order issues without making any changes, run:
isort --check-only src/
- Sort Imports: To automatically sort and organize imports in all Python files within
src
, run:
isort src/
The pre-commit hook in /scripts/hooks
will run on any invocation of git commit
, and will prevent the commit from completing if any of the pre-commit checks fail. The script currently runs black
, isort
, and flake8
, all in check-only modes to prevent files from being automatically modified. After addressing any issues, you can stage the updated files and retry the commit.
To install the pre-commit hook, you can either run the installation script located at scripts/install_git_hooks.py
, or manually copy the pre-commit
script from scripts/hooks
to your .git/hooks
directory.