diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml deleted file mode 100644 index 75f11d0..0000000 --- a/.github/workflows/mypy.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: mypy - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [main] - pull_request: - branches: [main] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -jobs: - default-shell: - name: Default shell - if: ${{ false }} # desabilita esta accion ya que se ejecuta en pre-commit - runs-on: "ubuntu-latest" - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - name: Test with mypy - run: | - pip install mypy - mypy diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index a261f6a..fc85ef3 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -19,25 +19,25 @@ jobs: default-shell: name: Default shell runs-on: "ubuntu-latest" - defaults: - run: - shell: bash -l {0} + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.11" - cache: "pip" + python-version: ${{ matrix.python-version }} - name: Test with pytest run: | - pip install -r requirements.txt - pip install pytest-cov - pip install -e . - pytest --cov=./ --cov-report=xml --optional + pip install uv + uv pip install --system -r requirements-dev.txt + uv pip install --system pytest-cov + uv pip install --system -e . + pytest --cov=./ --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} name: codecov-umbrella - fail_ci_if_error: true + fail_ci_if_error: false verbose: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6c9181..3167a05 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,8 @@ repos: rev: v1.6.1 hooks: - id: mypy + args: ["--install-types", "--non-interactive"] + additional_dependencies: [numpy] # Esto tiene la desventaja de que siempre se va a ejecutar mypy # sobre todos los ficheros del repo (incluidos en la conf de mypy) # Se puede sustituir por `files:` @@ -59,20 +61,21 @@ repos: hooks: - id: prettier types_or: [markdown, yaml] - - repo: https://github.com/jazzband/pip-tools - rev: 7.3.0 + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.1.28 hooks: - id: pip-compile - name: pip-compile requirements - args: [pyproject.toml] - files: ^(pyproject\.toml|requirements\.txt)$ + name: pip-compile requirements.in + args: [-o, requirements.txt, requirements.in] - id: pip-compile - name: pip-compile requirements-dev + name: pip-compile requirements-dev.in args: [ - --constraint=requirements.txt, - --extra=dev, - --output-file=requirements-dev.txt, - pyproject.toml, + -o, + requirements-dev.txt, + -c, + requirements.txt, + requirements.in, + requirements-dev.in, ] - files: ^(pyproject\.toml|requirements-dev\.txt)$ + files: ^(requirements\.(in|txt)|requirements-dev\.(in|txt))$ diff --git a/Dockerfile b/Dockerfile index 35a55ad..6de322b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,8 @@ ENV PIP_ROOT_USER_ACTION=ignore \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ PIP_NO_CACHE_DIR=1 -RUN pip install -r /code/requirements.txt +RUN pip install uv +RUN uv pip install --system --no-cache -r /code/requirements.txt COPY ./template /code/template COPY ./pyproject.toml /code/pyproject.toml diff --git a/Makefile b/Makefile index ee23820..bacba21 100644 --- a/Makefile +++ b/Makefile @@ -1,33 +1,44 @@ .ONESHELL: -.PHONY: env install compile sync hooks docker build run debug push +.PHONY: env install compile sync hooks ruff test mypy docker build run debug push SHELL=/bin/bash DOCKER_IMG_NAME=ghcr.io/komorebi-ai/python-template DOCKER_CONTAINER=template -GH_USER=albertotb -GH_TOKEN_FILE=/home/atorres/GITHUB_TOKEN.txt +GH_USER=GITHUB_USERNAME +GH_TOKEN_FILE=GITHUB_TOKEN_PATH env: conda create -n python-template python=3.11 install: - pip install -r requirements-dev.txt - pip install -e .[dev] + pip install uv + uv pip install -r requirements-dev.txt + uv pip install -e .[dev] compile: - pip install pip-tools - pip-compile pyproject.toml - pip-compile --extra dev -o requirements-dev.txt -c requirements.txt pyproject.toml + pip install uv + uv pip compile -o requirements.txt requirements.in + uv pip compile -o requirements-dev.txt -c requirements.txt requirements.in requirements-dev.in sync: - pip install pip-tools - pip-sync requirements-dev.txt - pip install -e .[dev] + pip install uv + uv pip sync requirements-dev.txt + uv pip install -e .[dev] hooks: pre-commit install pre-commit run --all-files +ruff: + ruff check . + ruff format . + +test: + python -m pytest + +mypy: + mypy --install-types --non-interactive + docker: build run build: @@ -36,7 +47,7 @@ build: # https://stackoverflow.com/questions/26564825/what-is-the-meaning-of-a-double-dollar-sign-in-bash-makefile run: [ "$$(docker ps -a | grep $(DOCKER_CONTAINER))" ] && docker stop $(DOCKER_CONTAINER) && docker rm $(DOCKER_CONTAINER) - docker run -d --restart=unless-stopped --name $(DOCKER_CONTAINER) -p 7654:80 $(DOCKER_IMG_NAME) + docker run -d --restart=unless-stopped --name $(DOCKER_CONTAINER) -p 80:80 $(DOCKER_IMG_NAME) debug: docker run -it $(DOCKER_IMG_NAME) /bin/bash diff --git a/README.md b/README.md index 5ef39d1..e64595e 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Tools: - [ruff](https://docs.astral.sh/ruff/) - [mypy](https://mypy.readthedocs.io/) - [pytest](https://docs.pytest.org/en/) -- [pip-tools](https://github.com/jazzband/pip-tools) +- [uv](https://github.com/astral-sh/uv) - [pre-commit](https://pre-commit.com/) - [prettier](https://prettier.io/) - [codespell](https://github.com/codespell-project/codespell) @@ -38,12 +38,12 @@ Install a specific version of the package with `pip` pip install git+ssh://git@github.com/Komorebi-AI/template.git@0.1.0 ``` -## Install dependencies +## Setup development environment -Create isolated environment with required Python version. This can be done with tools like venv or conda: +Create isolated environment with required Python version. This assumes that you have `conda` installed ([see instructions](https://docs.anaconda.com/free/miniconda/)): ```{bash} -conda create -n python-template python=3.9 +make env ``` Then, activate the environment: @@ -55,34 +55,12 @@ conda activate python-template Install dependencies: ```{bash} -pip install -r requirements.txt -``` - -Install package in editable mode: - -```{bash} -pip install -e .[dev] +make install ``` Install and run pre-commit hooks: ```{bash} -pre-commit install -pre-commit run --all-files -``` - -Alternatively, you can use the included `env.yml` file that performs all the previous steps (except the pre-commit hooks): - -```{bash} -conda env create -f env.yml -``` - -or `make` - -```{bash} -make env -conda activate python-template -make install make hooks ``` @@ -91,33 +69,29 @@ make hooks The `requirements.txt` is generated automatically with `pip-tools` and it should not be edited manually. Add abstract dependencies to `requirements.in` and `requirements-dev.in`. If necessary, add version requirements but try to be as flexible as possible. Then, update the `requirements.txt` file with: ```{bash} -pip-compile --extra dev pyproject.toml +make compile ``` -If you want to pin separately production and dev dependencies you can use instead: +Sync the local environment with the `requirements-dev.txt` file: ```{bash} -pip-compile pyproject.toml +make sync ``` -And: +## Run linter and formatter ```{bash} -pip-compile --extra dev -o requirements-dev.txt -c requirements.txt pyproject.toml +make ruff ``` -Or simply: +## Run tests ```{bash} -make compile +make test ``` -Flag `-c` constrains the `dev` dependencies to be the same exact versions as the production dependencies. `pip-tools` also has a `pip-sync` command to make sure that the local environment is in sync with the `requirements.txt` or `requirements-dev.txt` file: +## Run type checker ```{bash} -make sync +make mypy ``` - -## Run tests - -- Run tests: `python -m pytest` diff --git a/env.yml b/env.yml deleted file mode 100644 index e5f1590..0000000 --- a/env.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: python-template -channels: - - defaults -dependencies: - - python==3.11 - - pip - - pip: - - -r requirements-dev.txt - - -e .[dev] diff --git a/pyproject.toml b/pyproject.toml index d697eb3..33dbdda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"] build-backend = "setuptools.build_meta" [project] -requires-python = ">=3.11,<3.12" +requires-python = ">=3.9,<3.12" name = "template" # La versión se obtiene automáticamente de git con `setuptools_scm` #version = "0.1.0" @@ -30,8 +30,8 @@ optional-dependencies.dev = { file = ["requirements-dev.in"] } [tool.ruff] line-length = 88 -target-version = "py311" -lint.select = [ +[tool.ruff.lint] +select = [ "E", # pycodestyle "F", # Pyflakes "UP", # pyupgrade @@ -49,7 +49,7 @@ lint.select = [ # Ignore D100 Missing docstring in public module ignore = ["D100"] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # Also ignore `D104` in all `__init__.py` files. "__init__.py" = ["D104"] "tests/*.py" = ["D"] @@ -57,14 +57,6 @@ ignore = ["D100"] [tool.ruff.lint.pydocstyle] convention = "numpy" -[tool.pytest.ini_options] -minversion = "7" -testpaths = ["tests"] -log_cli_level = "INFO" -xfail_strict = true -addopts = ["-ra", "--strict-config", "--strict-markers"] -filterwarnings = ["error", "ignore::DeprecationWarning"] - [tool.mypy] files = "./template" strict = true @@ -73,5 +65,14 @@ ignore_missing_imports = true python_version = "3.11" warn_return_any = true warn_unused_configs = true -allow_untyped_decorators = true enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] + +[tool.pytest.ini_options] +minversion = "7" +testpaths = ["tests"] +log_cli_level = "INFO" +xfail_strict = true +addopts = ["-ra", "--strict-config", "--strict-markers"] +filterwarnings = ["error", "ignore::DeprecationWarning"] + + diff --git a/requirements-dev.in b/requirements-dev.in index aabcbbe..ae01948 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -1,4 +1,4 @@ -pip-tools +uv pytest ruff mypy diff --git a/requirements-dev.txt b/requirements-dev.txt index 15d7886..c038864 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,136 +1,76 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --constraint=requirements.txt --extra=dev --output-file=requirements-dev.txt pyproject.toml -# +# This file was autogenerated by uv via the following command: +# uv pip compile -o requirements-dev.txt -c requirements.txt requirements.in requirements-dev.in annotated-types==0.6.0 - # via - # -c requirements.txt - # pydantic + # via pydantic anyio==3.7.1 # via - # -c requirements.txt # fastapi # starlette -build==1.0.3 - # via pip-tools cfgv==3.4.0 # via pre-commit click==8.1.7 # via - # -c requirements.txt - # pip-tools # typer # uvicorn distlib==0.3.7 # via virtualenv fastapi==0.104.0 - # via - # -c requirements.txt - # template (pyproject.toml) filelock==3.12.4 # via virtualenv h11==0.14.0 - # via - # -c requirements.txt - # uvicorn + # via uvicorn identify==2.5.30 # via pre-commit idna==3.4 - # via - # -c requirements.txt - # anyio + # via anyio iniconfig==2.0.0 # via pytest mypy==1.6.1 - # via template (pyproject.toml) mypy-extensions==1.0.0 # via mypy nodeenv==1.8.0 # via pre-commit numpy==1.26.1 - # via - # -c requirements.txt - # pandas - # template (pyproject.toml) + # via pandas packaging==23.2 - # via - # build - # pytest + # via pytest pandas==2.1.1 - # via - # -c requirements.txt - # template (pyproject.toml) -pip-tools==7.3.0 - # via template (pyproject.toml) platformdirs==3.11.0 # via virtualenv pluggy==1.3.0 # via pytest pre-commit==3.5.0 - # via template (pyproject.toml) pydantic==2.4.2 - # via - # -c requirements.txt - # fastapi + # via fastapi pydantic-core==2.10.1 - # via - # -c requirements.txt - # pydantic -pyproject-hooks==1.0.0 - # via build + # via pydantic pytest==7.4.2 - # via template (pyproject.toml) python-dateutil==2.8.2 - # via - # -c requirements.txt - # pandas + # via pandas pytz==2023.3.post1 - # via - # -c requirements.txt - # pandas + # via pandas pyyaml==6.0.1 # via pre-commit ruff==0.1.7 - # via template (pyproject.toml) +setuptools==69.2.0 + # via nodeenv six==1.16.0 - # via - # -c requirements.txt - # python-dateutil + # via python-dateutil sniffio==1.3.0 - # via - # -c requirements.txt - # anyio + # via anyio starlette==0.27.0 - # via - # -c requirements.txt - # fastapi + # via fastapi typer==0.9.0 - # via - # -c requirements.txt - # template (pyproject.toml) typing-extensions==4.8.0 # via - # -c requirements.txt # fastapi # mypy # pydantic # pydantic-core # typer tzdata==2023.3 - # via - # -c requirements.txt - # pandas + # via pandas +uv==0.1.28 uvicorn==0.23.2 - # via - # -c requirements.txt - # template (pyproject.toml) virtualenv==20.24.5 # via pre-commit -wheel==0.41.2 - # via pip-tools - -# The following packages are considered to be unsafe in a requirements file: -# pip -# setuptools diff --git a/requirements.txt b/requirements.txt index 736b441..4d21afc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile pyproject.toml -# +# This file was autogenerated by uv via the following command: +# uv pip compile -o requirements.txt requirements.in annotated-types==0.6.0 # via pydantic anyio==3.7.1 @@ -15,17 +11,13 @@ click==8.1.7 # typer # uvicorn fastapi==0.104.0 - # via template (pyproject.toml) h11==0.14.0 # via uvicorn idna==3.4 # via anyio numpy==1.26.1 - # via - # pandas - # template (pyproject.toml) + # via pandas pandas==2.1.1 - # via template (pyproject.toml) pydantic==2.4.2 # via fastapi pydantic-core==2.10.1 @@ -41,7 +33,6 @@ sniffio==1.3.0 starlette==0.27.0 # via fastapi typer==0.9.0 - # via template (pyproject.toml) typing-extensions==4.8.0 # via # fastapi @@ -51,4 +42,3 @@ typing-extensions==4.8.0 tzdata==2023.3 # via pandas uvicorn==0.23.2 - # via template (pyproject.toml) diff --git a/template/api.py b/template/api.py index f39896d..c519f07 100644 --- a/template/api.py +++ b/template/api.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from fastapi import FastAPI from template.main import __version__ @@ -5,7 +7,7 @@ app = FastAPI() -@app.get("/") +@app.get("/") # type:ignore[misc] def read_root() -> dict[str, str]: """Check API version.""" return {"template-api": f"version {__version__}"} diff --git a/template/main.py b/template/main.py index 435cf96..0630461 100755 --- a/template/main.py +++ b/template/main.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +from __future__ import annotations + from importlib.metadata import PackageNotFoundError, version import typer