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
33 changes: 11 additions & 22 deletions .github/workflows/annotationengine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,43 +33,32 @@ jobs:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: 3.9
- uses: actions/cache@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/test_requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- uses: conda-incubator/setup-miniconda@v2
with:
auto-update-conda: true
auto-activate-base: true
python-version: 3.9
- name: Install dependencies
shell: bash -l {0}
version: "latest"
enable-cache: true
- name: Validate lockfile
run: |
pip install flake8 pytest
pip install -r requirements.txt
if [ -f test_requirements.txt ]; then pip install -r test_requirements.txt; fi
uv lock --check
- name: Lint with flake8
shell: bash -l {0}
run: |
# stop the build if there are Python syntax errors or undefined names
# flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
uv run --frozen --with flake8 flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

- name: Test with pytest
shell: bash -l {0}
env:
AUTH_DISABLED: true
run: |
python -c "import os; print('AUTH_DISABLED:', os.getenv('AUTH_DISABLED'), type(os.getenv('AUTH_DISABLED')))"
pytest --cov annotationengine
uv run --frozen python -c "import os; print('AUTH_DISABLED:', os.getenv('AUTH_DISABLED'), type(os.getenv('AUTH_DISABLED')))"
uv run --frozen pytest --cov annotationengine
- name: Upload coverage to codecov
uses: codecov/codecov-action@v5
with:
Expand Down
30 changes: 26 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
FROM tiangolo/uwsgi-nginx-flask:python3.9

RUN git config --global http.sslVerify false && \
mkdir -p /home/nginx/.cloudvolume/secrets && chown -R nginx /home/nginx && usermod -d /home/nginx -s /bin/bash nginx
RUN apt-get update && apt-get install -y gcc curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN pip install uv

# Enable bytecode compilation and avoid hardlinks in mounted cache volumes.
ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy
ENV UV_PYTHON_DOWNLOADS=0

RUN git config --global http.sslVerify false && \
mkdir -p /home/nginx/.cloudvolume/secrets && \
chown -R nginx /home/nginx && \
usermod -d /home/nginx -s /bin/bash nginx

WORKDIR /app

# Install runtime dependencies from the lockfile before copying source.
COPY uv.lock pyproject.toml ./
ENV UV_PROJECT_ENVIRONMENT="/usr/local/"
RUN --mount=type=cache,target=/root/.cache/uv \
UV_VENV_ARGS="--system-site-packages" uv sync --frozen --no-install-project --no-default-groups

ENV UWSGI_INI /app/uwsgi.ini
ENV PATH /app/.venv/bin:/home/nginx/google-cloud-sdk/bin:/root/google-cloud-sdk/bin:$PATH
ENV PYTHONNOUSERSITE=1

COPY requirements.txt /app/.
RUN pip install -r requirements.txt
COPY timeout.conf /etc/nginx/conf.d/
COPY . /app

24 changes: 24 additions & 0 deletions annotationengine/anno_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@ def get_db(aligned_volume) -> DynamicAnnotationInterface:
return cache[aligned_volume]


def clear_db_cache():
for interface in cache.values():
for database in (
getattr(interface, "_database", None),
getattr(getattr(interface, "_annotation", None), "db", None),
getattr(getattr(interface, "_segmentation", None), "db", None),
):
if database is None:
continue
try:
database.close_session()
except Exception:
pass
try:
database.session.remove()
except Exception:
pass
try:
database.engine.dispose()
except Exception:
pass
cache.clear()


def check_write_permission(db, table_name):
metadata = db.database.get_table_metadata(table_name)
if metadata["user_id"] != str(g.auth_user["id"]):
Expand Down
64 changes: 64 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[build-system]
requires = ["hatchling>=1.26.1"]
build-backend = "hatchling.build"

[project]
name = "annotationengine"
dynamic = ["version"]
description = "A service for storing arbitrary annotation data on EM volumes stored in a cloud volume"
readme = "README.md"
requires-python = ">=3.9,<3.10"
authors = [
{ name = "Forrest Collman", email = "forrestc@alleninstitute.org" }
]
classifiers = [
"License :: OSI Approved :: MIT License",
]
dependencies = [
"Flask<2.0",
"MarkupSafe<2.1",
"emannotationschemas>=5.26.3",
"dynamicannotationdb>=5.15.0",
"caveclient>=4.20.2",
"multiwrapper",
"jsonschema<4.0",
"middle-auth-client>=3.11.1",
"flask_sqlalchemy",
"flask_cors",
"flask-marshmallow==0.14.0",
"flask-admin",
"flask-restx",
"flask-accepts",
"marshmallow-sqlalchemy",
"requests",
"cachetools",
]

[dependency-groups]
dev = [
"pytest>=3.0.5",
"pytest-cov>=2.2.1",
"pytest-postgresql",
"pytest-env",
"pytest-mock",
"requests-mock",
"docker",
"coverage",
]

[tool.hatch.version]
path = "annotationengine/__init__.py"

[tool.hatch.build.targets.wheel]
packages = ["annotationengine"]

[tool.hatch.build.targets.sdist]
include = [
"/annotationengine",
"/test",
"/README.md",
"/LICENSE",
"/requirements.txt",
"/requirements.in",
"/test_requirements.txt",
]
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Flask<2.0
emannotationschemas>=5.26.3
dynamicannotationdb>=5.5.1
dynamicannotationdb>=5.15.0
caveclient>=4.20.2
multiwrapper
jsonschema<4.0
Expand Down
3 changes: 3 additions & 0 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import psycopg2
import pytest
from annotationengine import create_app, db
from annotationengine.anno_database import clear_db_cache
from flask import appcontext_pushed, g, current_app

logging.basicConfig(level=logging.DEBUG)
Expand Down Expand Up @@ -67,6 +68,8 @@ def client():
with flask_app.app_context():
db.create_all()
yield testing_client
clear_db_cache()
db.session.remove()
db.drop_all()


Expand Down
Loading
Loading