This file is intended for AI coding agents operating in this repository.
QJazz is a QGIS server framework built as a uv workspace monorepo with:
- qjazz-contrib: Core Python library — config system, logger, component manager, QGIS C++ binding
- qjazz-processes: OGC Processes implementation — Celery workers, aiohttp server, QGIS Processing integration
- qjazz-server: Mixed Rust + Python — gRPC server, HTTP proxy, worker pool manager
- Python 3.12+ required throughout
- C++ extension built via
setup.pyinqjazz-contrib/(Qt5/PyQt5 and Qt6/PyQt6) - Rust workspace at
qjazz-server/Cargo.toml(4 crates: rpc, map, mon, pool) - gRPC/protobuf code generation outputs go to
_grpc/directories (excluded from linting)
Key reference files:
config/ruff.toml— shared ruff linting and formatting rulesconfig/mypy.ini— shared mypy configurationconfig/python.mk— shared Makefile targets for Python projectsconfig/rules.mk— top-level Makefile target definitions
make configure
Generates pyproject.toml from .in template (substitutes version variables).
make install # install packages in development mode
make reinstall # reinstall
make upgrade # upgrade all dependencies
make build # build C++ extension + install packages
make build dist # build C++ extension + create sdist
make manifest # (qjazz-contrib) create manifest.json with commit ID
All tools are invoked via uv run from config/python.mk.
make lint # ruff check + mypy (default)
make lint-preview # ruff --preview checks
make lint-fix # ruff auto-fix
make lint-fix-preview # ruff --preview --fix
make typecheck # mypy only
make format # ruff format
make format-diff # show formatting diff without modifying
make scan # bandit security scan
make deadcode # vulture dead code detection
make prepare_commit # runs: lint scan test (blocks on failure)
make test # runs pytest -v $(TEST_OPTS) $(TESTDIR)
make test TESTDIR=tests/test_config.py
make test TESTDIR=tests/ TEST_OPTS="-k test_name"
make test TESTDIR=tests/ TEST_OPTS="-m 'not minio'" # skip MinIO tests
make test TESTDIR=tests/ TEST_OPTS="-m 'not services'" # skip Celery service tests
make coverage # runs coverage + generates HTML report
Each sub-package sets its own TESTDIR and TEST_OPTS in its Makefile:
qjazz-contrib:TESTDIR=tests,TEST_OPTS=-m "not minio"qjazz-processes: same defaultsqjazz-server/python: same defaults
All test directories have a pytest.ini with asyncio_mode=auto, log_cli=1, and markers definitions.
- Line length: 110 characters
- Target Python: 3.12
- Indentation: spaces
- Formatter: docstring code formatting enabled
- Key rule sets:
E,F,I(isort),ANN(annotations),W,T,COM,RUF,C4,SIM,TC,RET - Disabled rules:
ANN002,ANN003,ANN401(Any is permitted),COM812,SIM108,SIM102 - Test files:
T201(bareprint) is allowed intests/*
- Python version: 3.12
- Plugin:
pydantic.mypyenabled allow_redefinition = true- All packages having no type hints available (QGIS, gRPC, Celery, etc.) use
ignore_missing_imports = true
Absolute imports are used throughout. Relative imports are used within a package.
Import order (enforced by ruff/isort, lines-between-types = 1):
__future__- Standard library (
os,sys,pathlib,typing, etc.) - Third-party (
pydantic,aiohttp,celery, etc.) qgissection (qgis,processingmodules — custom isort section)- First-party (
qjazz_core,qjazz_cache,qjazz_rpc, etc.) - Local folder (relative imports)
Use TYPE_CHECKING guards to avoid circular imports:
if TYPE_CHECKING:
from pathlib import Path- All function signatures must be annotated (return type + parameters), enforced by ruff
ANNrules - PEP 695 generic syntax is used:
type X[T: Bound] = ... - Type aliases:
PascalCasenames (HttpCORS,JobStatusCode) Protocolclasses for structural typing without inheritancepy.typedmarker files are present in every packageOptional[T]andT | Noneboth appear (newer code prefers union syntax)
| Element | Convention | Example |
|---|---|---|
| Modules/packages | snake_case |
qjazz_core, componentmanager |
| Classes | PascalCase |
ConfBuilder, ConfigProxy |
| Functions/methods | snake_case |
load_configuration() |
| Variables | snake_case |
conf_service, rendez_vous |
| Constants | UPPER_SNAKE_CASE |
DEFAULT_INTERFACE, SERVER_HEADER |
| Private helpers | leading _ |
_validate_netinterface() |
| Internal attrs | leading _ |
self._celery, self._conf |
| Type aliases | PascalCase |
HttpCORS, ServiceDict |
Each package defines its own base exception:
QJazzException(Exception)—qjazz_coreProcessesException(Exception)—qjazz_processes→DismissedTaskError,ServiceNotAvailable,ProcessNotFound, etc.ComponentManagerError(Exception)—qjazz_core→FactoryNotFoundError,NoRegisteredFactoryError
raise ... from None: use when re-raising to suppress exception chainingassert_precondition()/assert_postcondition(): prefer these over bareassert(avoids-Oflag issues)assert_never()+match/case: use for exhaustiveness checking in match expressions, withcase _ as unreachable: assert_never(unreachable)as the final branch- HTTP errors: use
web.HTTPExceptionsubclasses in aiohttp handlers - Middleware catch-all: unhandled exceptions are caught by middleware and returned as structured JSON errors
Services register with URI-style IDs: "@3liz.org/config-service;1". The ComponentManager resolves these at runtime, enabling loose coupling.
Pydantic models with @section("name") decorator. ConfBuilder assembles configurations incrementally. ConfigProxy[T] provides live access to sub-configurations.
Protocol classes (e.g., ConfigProto, RendezVous, ExecutorProtocol) define interfaces used in type annotations without requiring inheritance.
qjazz-pool (Rust) manages Python worker subprocesses via msgpack-encoded pipe communication. Workers signal state via a named pipe at RENDEZ_VOUS.
qjazz-rpc (Rust) is the gRPC server that spawns Python worker processes. The qjazz_rpc Python package provides the worker-side implementation.
Workers self-register via Celery broadcast/control. The Executor aggregates presence replies to route jobs to available workers.
qjazz/
├── Makefile # top-level orchestrator (includes config/*.mk)
├── config/
│ ├── config.mk # shared Makefile vars (uv, ruff, mypy, bandit)
│ ├── python.mk # shared Python targets (lint, test, format, etc.)
│ ├── rules.mk # top-level target definitions
│ ├── ruff.toml # ruff linting/formatting config
│ └── mypy.ini # mypy config
├── qjazz-contrib/
│ ├── Makefile # TESTDIR=tests, TEST_OPTS=-m "not minio"
│ ├── setup.py # C++ extension build
│ ├── src/qjazz_core/ # config, logger, component manager
│ ├── src/qjazz_cache/ # QGIS project cache
│ ├── src/qjazz_ogc/ # OGC catalog/collections
│ └── src/qjazz_store/ # S3/MinIO object store
├── qjazz-processes/
│ ├── Makefile
│ └── src/
│ ├── qjazz_processes/ # HTTP server, executor, schemas, worker
│ └── qjazz_processing/ # QGIS Processing integration
├── qjazz-server/
│ ├── qjazz-rpc/ # Rust: gRPC server (tonic)
│ ├── qjazz-map/ # Rust: HTTP frontend proxy
│ ├── qjazz-mon/ # Rust: monitoring
│ ├── qjazz-pool/ # Rust: worker pool
│ └── python/
│ └── src/
│ ├── qjazz_rpc/ # Python: gRPC worker
│ ├── qjazz_admin/ # Admin API
│ └── qjazz_map/ # Map server Python components
└── tests/ # integration/client tests