Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ef94c32
migrate pyproject.toml to uv using 'uvx migrate-to-uv'
osma Dec 12, 2025
27d1a6d
update README.md to use uv instead of Poetry
osma Dec 12, 2025
cc9389e
don't store uv.lock in version control (use library-style dependencie…
osma Dec 16, 2025
a2794de
migrate GitHub Actions to use uv instead of Poetry
osma Dec 17, 2025
f6b5f8f
fix uv sync extras syntax
osma Dec 17, 2025
2f09744
configure flake8 to ignore .venv and other uninteresting directories
osma Dec 17, 2025
cd93de6
add pip dependency to fix 'spacy download' command
osma Dec 17, 2025
5394f84
migrate Dockerfile to use uv instead of Poetry
osma Dec 17, 2025
114449f
include dev dependencies in docker container so pytest works
osma Dec 17, 2025
aa502b7
install spacy models later, so they don't get removed by uv sync
osma Dec 17, 2025
5e48b93
add .venv dir to .dockerignore to avoid inflating build context
osma Jan 8, 2026
e12b011
adjust Dockerfile to avoid doubling image size
osma Jan 8, 2026
65abb14
move pip dependency from top level into a spacy optional dependency
osma Jan 8, 2026
2c26fc6
avoid giving ownership to annif_user for the /Annif installation files
osma Jan 8, 2026
0ec3b99
adjust workdir & tests dir permissions
osma Jan 8, 2026
31a72a2
create projects.d dir (needed by hfh_util tests) and restore HF_HOME …
osma Jan 8, 2026
a1fae27
revert useless Dockerfile changes: create annif_user as a system user…
osma Jan 8, 2026
436253a
add chmod command to ensure that files and directories copied under /…
osma Jan 8, 2026
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
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
**/*.pyc
data*
venv
.venv
27 changes: 7 additions & 20 deletions .github/actions/prepare/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,18 @@ description:
inputs:
python-version:
required: true
poetry-version:
uv-version:
required: true
outputs:
cache-matched-key:
value: ${{ steps.restore-cache.outputs.cache-matched-key }}
runs:
using: "composite"
steps:
- name: Restore cached Poetry installation and its cache
id: restore-cache
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cache/pipx/venvs
~/.local/bin
~/.cache/pypoetry/
key: ignore-me
restore-keys: |
poetry-installation-and-cache-${{ inputs.python-version }}-${{ inputs.poetry-version }}-
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: ${{ inputs.python-version }}
- name: Install Poetry
shell: bash
run: |
pipx install poetry==${{ inputs.poetry-version }}
poetry env use ${{ inputs.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6
with:
version: ${{ inputs.uv-version }}
python-version: ${{ inputs.python-version }}
enable-cache: true
62 changes: 26 additions & 36 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
env:
PIPX_HOME: "/home/runner/.cache/pipx"
PIPX_BIN_DIR: "/home/runner/.local/bin"
POETRY_VERSION: "2.*"
UV_VERSION: "0.9.*"
permissions:
contents: read
jobs:
Expand All @@ -25,23 +25,23 @@ jobs:
egress-policy: audit

- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: "Prepare: restore caches, install Poetry, set up Python"
- name: "Prepare: restore caches, install uv, set up Python"
uses: ./.github/actions/prepare
with:
python-version: "3.12"
poetry-version: ${{ env.POETRY_VERSION }}
uv-version: ${{ env.UV_VERSION }}
- name: Install Python dev dependencies
run: |
poetry install --only dev
uv sync --only-dev
- name: Lint with isort
run: |
poetry run isort . --check-only --diff
uv run isort . --check-only --diff
- name: Lint with Black
run: |
poetry run black . --check --diff
uv run black . --check --diff
- name: Lint with flake8
run: |
poetry run flake8
uv run flake8

time-startup:
runs-on: ubuntu-22.04
Expand All @@ -53,18 +53,18 @@ jobs:
egress-policy: audit

- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: "Prepare: restore caches, install Poetry, set up Python"
- name: "Prepare: restore caches, install uv, set up Python"
id: prepare
uses: ./.github/actions/prepare
with:
python-version: "3.11"
poetry-version: ${{ env.POETRY_VERSION }}
uv-version: ${{ env.UV_VERSION }}
- name: Install Python dependencies
run: |
poetry install
uv sync
- name: Check startup time
run: |
poetry run tests/time-startup.sh
uv run tests/time-startup.sh

test:
runs-on: ubuntu-22.04
Expand All @@ -85,58 +85,48 @@ jobs:
sudo apt-get install \
libvoikko1 \
voikko-fi
- name: "Prepare: restore caches, install Poetry, set up Python"
- name: "Prepare: restore caches, install uv, set up Python"
id: prepare
uses: ./.github/actions/prepare
with:
python-version: ${{ matrix.python-version }}
poetry-version: ${{ env.POETRY_VERSION }}
uv-version: ${{ env.UV_VERSION }}
- name: Install Python dependencies
run: |
# Selectively install the optional dependencies for some Python versions
# For Python 3.10:
if [[ ${{ matrix.python-version }} == '3.10' ]]; then
poetry install -E "nn omikuji yake voikko stwfsa";
uv sync --extra nn --extra omikuji --extra yake --extra voikko --extra stwfsa;
fi
# For Python 3.11:
if [[ ${{ matrix.python-version }} == '3.11' ]]; then
poetry install -E "fasttext spacy estnltk";
uv sync --extra fasttext --extra spacy --extra estnltk;
# download the small English pretrained spaCy model needed by spacy analyzer
poetry run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
uv run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
fi
# For Python 3.12:
if [[ ${{ matrix.python-version }} == '3.12' ]]; then
poetry install -E "nn fasttext yake stwfsa voikko spacy";
uv sync --extra nn --extra fasttext --extra yake --extra stwfsa --extra voikko --extra spacy;
# download the small English pretrained spaCy model needed by spacy analyzer
poetry run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
uv run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
fi
# For Python 3.13:
if [[ ${{ matrix.python-version }} == '3.13' ]]; then
poetry install -E "fasttext yake voikko spacy";
uv sync --extra fasttext --extra yake --extra voikko --extra spacy;
# download the small English pretrained spaCy model needed by spacy analyzer
poetry run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
uv run python -m spacy download en_core_web_sm --upgrade-strategy only-if-needed
fi
poetry run python -m nltk.downloader punkt_tab
uv run python -m nltk.downloader punkt_tab
- name: Test with pytest
run: |
poetry run pytest --cov=./ --cov-report xml
uv run pytest --cov=./ --cov-report xml
if [[ ${{ matrix.python-version }} == '3.11' ]]; then
poetry run pytest --cov=./ --cov-report xml --cov-append -m slow
uv run pytest --cov=./ --cov-report xml --cov-append -m slow
fi
- name: Upload coverage to Codecov
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Save cache
if: steps.prepare.outputs.cache-matched-key != format('poetry-installation-and-cache-{0}-{1}-{2}', matrix.python-version, env.POETRY_VERSION, hashFiles('**/poetry.lock'))
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.cache/pipx/venvs
~/.local/bin
~/.cache/pypoetry/
# A new key is created to update the cache if some dependency has been updated
key: poetry-installation-and-cache-${{ matrix.python-version }}-${{ env.POETRY_VERSION }}-${{ hashFiles('**/poetry.lock') }}

test-docker-image:
name: "test Docker image"
Expand Down Expand Up @@ -206,14 +196,14 @@ jobs:
egress-policy: audit

- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: "Prepare: restore caches, install Poetry, set up Python"
- name: "Prepare: restore caches, install uv, set up Python"
uses: ./.github/actions/prepare
with:
python-version: '3.11'
poetry-version: ${{ env.POETRY_VERSION }}
uv-version: ${{ env.UV_VERSION }}
- name: Build distributions
run: |
poetry build
uv build
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ __pycache__
*.pyc
data
docs/_build/
poetry.lock
uv.lock
projects.cfg
venv/
.hypothesis/
62 changes: 34 additions & 28 deletions Dockerfile
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a possibilty to enable bytecode compilation for uv installs (apparently uv does not perform it by default?); we could add UV_COMPILE_BYTECODE=1 to Dockerfile (although when I checked its effect with plain annif command, I cannot see a difference in startup time).

Original file line number Diff line number Diff line change
@@ -1,54 +1,60 @@
FROM python:3.12-slim-bookworm
# Use a Python 3.12 + uv image (Debian bookworm-slim)
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
LABEL org.opencontainers.image.authors="grp-natlibfi-annif@helsinki.fi"
SHELL ["/bin/bash", "-c"]

ARG optional_dependencies="voikko fasttext nn omikuji yake spacy stwfsa"
ARG POETRY_VIRTUALENVS_CREATE=false

# Install system dependencies needed at runtime:
RUN apt-get update && apt-get upgrade -y && \
if [[ $optional_dependencies =~ "voikko" ]]; then \
apt-get install -y --no-install-recommends \
libvoikko1 \
voikko-fi; \
fi && \
# Install rsync for model transfers:
apt-get install -y --no-install-recommends rsync && \
rm -rf /var/lib/apt/lists/* /usr/include/*
if [[ $optional_dependencies =~ "voikko" ]]; then \
apt-get install -y --no-install-recommends \
libvoikko1 \
voikko-fi; \
fi && \
# Install rsync for model transfers:
apt-get install -y --no-install-recommends rsync && \
rm -rf /var/lib/apt/lists/* /usr/include/*

WORKDIR /Annif
RUN pip install --upgrade pip "poetry~=2.0" --no-cache-dir

# Copy only project metadata first to maximize Docker layer caching
COPY pyproject.toml setup.cfg README.md LICENSE.txt CITATION.cff projects.cfg.dist /Annif/

# First round of installation for Docker layer caching:
RUN echo "Installing dependencies for optional features: $optional_dependencies" \
&& poetry install -E "$optional_dependencies" --no-root \
&& rm -rf /root/.cache/pypoetry # No need for cache because of poetry.lock
# First round: install dependencies only (no project), with selected extras.
RUN extras=(); \
for e in ${optional_dependencies}; do extras+=(--extra "$e"); done; \
uv sync --no-install-project "${extras[@]}"

# Download nltk data
RUN python -m nltk.downloader punkt_tab -d /usr/share/nltk_data
RUN uv run --no-sync python -m nltk.downloader punkt_tab -d /usr/share/nltk_data

# Download spaCy models, if the optional feature was selected
# Second round: add source and install the actual project (editable by default)
COPY annif /Annif/annif
COPY tests /Annif/tests
RUN extras=(); \
for e in ${optional_dependencies}; do extras+=(--extra "$e"); done; \
uv sync "${extras[@]}"

# Download spaCy models only if 'spacy' extra is selected
ARG spacy_models=en_core_web_sm
RUN if [[ $optional_dependencies =~ "spacy" ]]; then \
for model in $(echo $spacy_models | tr "," "\n"); do \
python -m spacy download $model; \
done; \
fi
for model in $(echo "$spacy_models" | tr "," "\n"); do \
uv run --no-sync python -m spacy download "$model"; \
done; \
fi

# Second round of installation with the actual code:
COPY annif /Annif/annif
COPY tests /Annif/tests
RUN poetry install -E "$optional_dependencies"
# Make virtualenv executables available to shell and entrypoint
ENV PATH="/Annif/.venv/bin:${PATH}"

WORKDIR /annif-projects
# Enable Annif bash completion (now available on PATH)
RUN annif completion --bash >> /etc/bash.bashrc # Enable tab completion

# Switch user to non-root:
# Set up working dir & non-root user for running annif commands
WORKDIR /annif-projects
RUN groupadd -g 998 annif_user && \
useradd -r -u 998 -g annif_user annif_user && \
chmod -R a+rX /Annif && \
chmod -R a+rX /Annif/* && \
mkdir -p /Annif/tests/data /Annif/projects.d && \
chown -R annif_user:annif_user /annif-projects /Annif/tests/data
USER annif_user
Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Annif can be tried out in the [GitHub Codespaces](https://docs.github.com/en/cod

A development version of Annif can be installed by cloning the [GitHub
repository](https://github.com/NatLibFi/Annif).
[Poetry](https://python-poetry.org/) is used for managing dependencies and virtual environment for the development version; Poetry 2.0+ is required.
[uv](https://docs.astral.sh/uv/) is used for managing dependencies and virtual environment for the development version.

See [CONTRIBUTING.md](CONTRIBUTING.md) for information on [unit tests](CONTRIBUTING.md#unit-tests), [code style](CONTRIBUTING.md#code-style), [development flow](CONTRIBUTING.md#development-flow) etc. details that are useful when participating in Annif development.

Expand All @@ -81,26 +81,34 @@ Clone the repository.

Switch into the repository directory.

Install [pipx](https://pypa.github.io/pipx/) and Poetry if you don't have them. First pipx:
Install [pipx](https://pypa.github.io/pipx/) and uv if you don't have them. First pipx:

python3 -m pip install --user pipx
python3 -m pipx ensurepath

Open a new shell, and then install Poetry:
Open a new shell, and then install uv:

pipx install poetry==2.*
pipx install uv

Poetry can be installed also without pipx: check the [Poetry documentation](https://python-poetry.org/docs/master/#installation).
uv can be installed also without pipx: check the [uv documentation](https://docs.astral.sh/uv/getting-started/installation/).

Create a virtual environment and install dependencies:

poetry install
uv sync

By default development dependencies are included. Use option `-E` to install dependencies for selected optional features (`-E "extra1 extra2"` for multiple extras), or install all of them with `--all-extras`. By default the virtual environment directory is not under the project directory, but there is a [setting for selecting this](https://python-poetry.org/docs/configuration/#virtualenvsin-project).
By default development dependencies are included. Use option `--extra` to install dependencies for selected optional features (`--extra "extra1 extra2"` for multiple extras), or install all of them with `--all-extras`. By default the virtual environment directory is `.venv` under the project directory.

You can run Annif in one of two ways:

### 1. One-off using `uv run`

uv run annif

### 2. Activating the virtual environment

Enter the virtual environment:

eval $(poetry env activate)
source .venv/bin/activate

Start up the application:

Expand Down
Loading