Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 150 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Python Project Template with Docker Compose

This is a template for a simple Python project with Docker Compose.
It is used by create_app [create_app](https://github.com/application-creators/create_app)
to create a new Python simple project with Docker Compose.
This is a template used by [create_app](https://github.com/application-creators/create_app) to create a
new Python project with Docker Compose.

To create your new project from this template, simply run:

Expand All @@ -15,11 +14,157 @@ python -m create_app python_compose
## What's in this template

* Project structure
* Containerization with Docker Compose
* Virtualenv
* Unit tests
* Docker Compose containerization
* Pre-commit GIT hooks
* [Black](https://github.com/psf/black)
* [Isort](https://pycqa.github.io/isort/)
* [Flake8](https://flake8.pycqa.org/en/latest/)
* Makefile with useful commands



## Git hooks

This template uses [pre-commit](https://pre-commit.com/) to run GIT hooks in your repo:
* [Black](https://github.com/psf/black)
* [Isort](https://pycqa.github.io/isort/)
* [Flake8](https://flake8.pycqa.org/en/latest/)

This helps developers to keep the same code styling in the project.

To install the hooks in your repo, first [install pre-commit](https://pre-commit.com/#install) in your system. Then run:
```shell
make install_git_hooks
```


## Project structure

The project structure has been designed keeping in mind that you may need to have multiple services in your project
(such as an API and a database, for example). Thus, Docker compose is a good way to get them running together. If you
need just one container, [python_simple](https://github.com/application-creators/python_simple) may be the way to go.

By default, you get a [service](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D)
named the same as you project package name. You can add as many services you want to the
[docker-compose.yml](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/docker-compose.yml) file. If you need to build
a new service, create a new folder at the [root](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D) of the repo and
put all its files in there. This will keep your repo organized and all your services decoupled from each other.

Say you have a project named "my_project". The following is an example structure, with multiple services:

```
my_project/ (repo root)
│ ...
└───service_a/
│ │ ...
│ │ Dockerfile
└───service_b/
│ │ ...
│ │ Dockerfile
│ docker-compose.yml
│ ...
```

Each Python service (although you may have services using other technologies) has the following structure:

```
service_a/ (service folder)
└───service_a/ (contains sources)
│ │
│ └───tests/
│ │
│ │run.py
│ Dockerfile
│ Makefile
│ requirements.frozen
│ requirements.test.frozen
```

* The [Dockerfile](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/Dockerfile)
provides instructions to build the image
* [Makefile](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/Makefile)
is not required, but it's useful to keep some everyday commands in there
* Declare your dependencies in
[requirements.frozen](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/requirements.frozen)
and
[requirements.test.frozen](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/requirements.test.frozen)
(refer to [requirements](#requirements))
* [run.py](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/run.py)
is the entry point to the service
* Put the unit tests in the [tests](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/tests)
package (refer to [unit tests](#unit-tests))


## Docker compose

To build the images, go into the project repo and run:
```shell
docker compose build
```

And to run the containers:
```shell
docker compose up
```


## Virtualenv

It is recommended to keep your system's Python interpreter clean, and install your project's dependencies in a virtual
environment (_venv_). Doing this has advantages like preventing dependencies conflicts between different projects
you may have in your system.

### Create the virtualenv

After you've installed [venv](https://docs.python.org/3/library/venv.html) in your system, go to the
[service folder](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D)
and run the following to create the venv:

```shell
make create_virtualenv
```


### Requirements

Use the [requirements.frozen](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/requirements.frozen)
file to declare the project's dependencies, and [requirements.test.frozen](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/requirements.test.frozen)
to declare dependencies that are only required to run tests. As indicated in the filenames, it is advised to declare
the dependencies with explicit versions (example: _requests==2.28.1_). This will allow you to control when to upgrade
dependencies versions, and will save you headaches when a new dependency version is released right when you were
running a deployment pipeline.

To install the requirements in the venv, go to the [service folder](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D)
and run:
```shell
make install_requirements
```

To install the test requirements in the venv, run:
```shell
make install_test_requirements
```

To install requirements and test requirements with a single, command, run:
```shell
make install_all_requirements
```


## Unit tests

Add your unit tests to the
[tests package](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/tests).

To run all unit tests, go to the [service folder](/%7B%7B%20cookiecutter.project_package_name%20%7D%7D/%7B%7B%20cookiecutter.project_package_name%20%7D%7D)
and run:
```shell
make run_unit_tests
```
8 changes: 8 additions & 0 deletions {{ cookiecutter.project_package_name }}/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@


install_git_hooks:
pre-commit install


run_git_hooks:
pre-commit run --all-files

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ ARG REQUIREMENTS_FILENAME=requirements.frozen
WORKDIR ${WORK_DIR}


COPY ${PROJECT_PACKAGE_NAME} .


COPY ${REQUIREMENTS_FILENAME} .
RUN pip install -r ${REQUIREMENTS_FILENAME}


COPY ${PROJECT_PACKAGE_NAME} .



CMD python run.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,42 @@ REQUIREMENTS_FILE_PATH=./requirements.frozen
TEST_REQUIREMENTS_FILE_PATH=./requirements.test.frozen


install_venv:
@echo "Installing virtualenv..."
sudo apt install python3.10-venv
@echo "Done!"


create_venv:
create_virtualenv:
@echo "Creating virtualenv..."
python3 -m venv "${VIRTUALENV_PATH}"
@echo "Done!"


install_reqs_in_venv:
install_requirements:
@echo "Installing requirements..."
${VIRTUALENV_PATH}/bin/pip install -r "${REQUIREMENTS_FILE_PATH}"
@echo "Done!"


install_test_reqs_in_venv:
install_test_requirements:
@echo "Installing test requirements..."
${VIRTUALENV_PATH}/bin/pip install -r "${TEST_REQUIREMENTS_FILE_PATH}"
@echo "Done!"


install_all_requirements: install_requirements install_test_requirements


run_unit_tests:
@echo "Running unit tests..."
. ${VIRTUALENV_PATH}/bin/activate && ./scripts/run_unit_tests.sh && deactivate
@. ${VIRTUALENV_PATH}/bin/activate && \
cd {{ cookiecutter.project_package_name }} && \
python -m unittest discover -s . -p '*_test.py' && \
cd ..
@echo "Done!"


docker_build:
docker build -t {{ cookiecutter.project_package_name }} .


docker_run:
docker run -t {{ cookiecutter.project_package_name }}


docker_build_and_run: docker_build docker_run