From f31d99099acb5b8463686ac91891ed09ab212970 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 11:27:06 +0100 Subject: [PATCH 1/7] Update docker image structure to match new convention --- docker-compose.yaml | 3 +++ docker/python/Dockerfile | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index e0e76721..3541fee5 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -78,5 +78,8 @@ services: - "8001:8000" volumes: - .:/python-api + - ./src/config.toml:/config/config.toml + environment: + OPENML_REST_API_CONFIG_DIRECTORY: "/config" depends_on: - database diff --git a/docker/python/Dockerfile b/docker/python/Dockerfile index 87516ed3..279be15a 100644 --- a/docker/python/Dockerfile +++ b/docker/python/Dockerfile @@ -5,16 +5,21 @@ RUN apt-get update \ ENV VIRTUAL_ENV=/opt/venv RUN python -m venv $VIRTUAL_ENV +# Prepending the path here means that `python` invocations below use the virtual environment ENV PATH="$VIRTUAL_ENV/bin:$PATH" RUN python -m pip install uv -WORKDIR /python-api +WORKDIR /app COPY pyproject.toml . RUN uv pip install -e ".[dev]" -COPY . /python-api +COPY . /app +RUN mkdir /config +COPY ./src/config.toml /config/config.toml +ENV OPENML_REST_API_CONFIG_FILE_PATH=/config/config.toml +COPY ./docker/python/README.md /README.md EXPOSE 8000 ENTRYPOINT ["python", "src/main.py"] From aeafc8a9c367cde29d5283f18ff7540246ab956c Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 11:39:21 +0100 Subject: [PATCH 2/7] Make configuration and dot files configurable --- src/config.py | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/config.py b/src/config.py index 0712271b..2d49246e 100644 --- a/src/config.py +++ b/src/config.py @@ -1,4 +1,5 @@ import functools +import logging import os import tomllib import typing @@ -6,9 +7,32 @@ from dotenv import load_dotenv +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + TomlTable = dict[str, typing.Any] -CONFIG_PATH = Path(__file__).parent / "config.toml" +CONFIG_DIRECTORY_ENV = "OPENML_REST_API_CONFIG_DIRECTORY" +CONFIG_FILE_ENV = "OPENML_REST_API_CONFIG_FILE" +DOTENV_FILE_ENV = "OPENML_REST_API_DOTENV_FILE" + +OPENML_DB_USERNAME_ENV = "OPENML_DATABASES_OPENML_USERNAME" +OPENML_DB_PASSWORD_ENV = "OPENML_DATABASES_OPENML_PASSWORD" # noqa: S105 # not a password +EXPDB_DB_USERNAME_ENV = "OPENML_DATABASES_EXPDB_USERNAME" +EXPDB_DB_PASSWORD_ENV = "OPENML_DATABASES_EXPDB_PASSWORD" # noqa: S105 # not a password + +_config_directory = Path(os.getenv(CONFIG_DIRECTORY_ENV, Path(__file__).parent)) +_config_directory = _config_directory.expanduser().absolute() +_config_file = Path(os.getenv(CONFIG_FILE_ENV, _config_directory / "config.toml")) +_config_file = _config_file.expanduser().absolute() +_dotenv_file = Path(os.getenv(DOTENV_FILE_ENV, _config_directory / ".env")) +_dotenv_file = _dotenv_file.expanduser().absolute() + +logger.info("Configuration directory is '%s'", _config_directory) +logger.info("Loading configuration file from '%s'", _config_file) +logger.info("Loading environment variables from '%s'", _dotenv_file) + +load_dotenv(dotenv_path=_dotenv_file) def _apply_defaults_to_siblings(configuration: TomlTable) -> TomlTable: @@ -25,35 +49,34 @@ def _load_configuration(file: Path) -> TomlTable: return tomllib.loads(file.read_text()) -def load_routing_configuration(file: Path = CONFIG_PATH) -> TomlTable: +def load_routing_configuration(file: Path = _config_file) -> TomlTable: return typing.cast("TomlTable", _load_configuration(file)["routing"]) @functools.cache -def load_database_configuration(file: Path = CONFIG_PATH) -> TomlTable: +def load_database_configuration(file: Path = _config_file) -> TomlTable: configuration = _load_configuration(file) database_configuration = _apply_defaults_to_siblings( configuration["databases"], ) - load_dotenv() database_configuration["openml"]["username"] = os.environ.get( - "OPENML_DATABASES_OPENML_USERNAME", + OPENML_DB_USERNAME_ENV, "root", ) database_configuration["openml"]["password"] = os.environ.get( - "OPENML_DATABASES_OPENML_PASSWORD", + OPENML_DB_PASSWORD_ENV, "ok", ) database_configuration["expdb"]["username"] = os.environ.get( - "OPENML_DATABASES_EXPDB_USERNAME", + EXPDB_DB_USERNAME_ENV, "root", ) database_configuration["expdb"]["password"] = os.environ.get( - "OPENML_DATABASES_EXPDB_PASSWORD", + EXPDB_DB_PASSWORD_ENV, "ok", ) return database_configuration -def load_configuration(file: Path = Path(__file__).parent / "config.toml") -> TomlTable: +def load_configuration(file: Path = _config_file) -> TomlTable: return tomllib.loads(file.read_text()) From 2d42e9a4909befd3ebd463e8fbbb68883f9a5293 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 11:48:01 +0100 Subject: [PATCH 3/7] Push docker readme file to Docker Hub on publish --- .github/workflows/docker-publish.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 414ca12d..5f6143e5 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -48,3 +48,12 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + + - name: Docker Hub Description + uses: peter-evans/dockerhub-description@v5 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + repository: openml/rest-api + short-description: "Experimental OpenML REST API" + readme-filepath: docker/python/README.md From 26891c91e0840e1791373ec21755f3e0e9c1b61f Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 11:52:24 +0100 Subject: [PATCH 4/7] Add a README for the docker image --- docker/python/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docker/python/README.md diff --git a/docker/python/README.md b/docker/python/README.md new file mode 100644 index 00000000..a9671dfd --- /dev/null +++ b/docker/python/README.md @@ -0,0 +1,32 @@ +# OpenML REST API +This is the work-in-progress Python-based REST API implementation for OpenML. +This should not be considered production ready. +New releases may contain breaking changes without deprecation warnings. + +## Usage +The image is not inteded to be used as a standalone image. +Please reference either the docker compose file at [server-api](https://www.github.com/openml/server-api) for development purposes, or [services](https://www.github.com/openml/services) for deployment purposes. + +## Configuration +Configuration is currently loaded from both a TOML file and a .env file. +Environment variables are used for configurations that either shouldn't be shared (secrets), or that inform how to load the configuration. +The TOML configuration file is used for all other settings. +By default both of these files are loaded from the `/config` directory. + +### Configuration File +A default configuration is available for reference at `/config/config.toml`, and can be used as reference. + +### Environment Variables +Environment variables are used for configurations that either shouldn't be shared (secrets), or that inform how to load the configuration: + + - `OPENML_REST_API_CONFIG_DIRECTORY`: points to the directory that contains configuration files (`config.toml`, `.env`) (default: `/config`) + - `OPENML_REST_API_CONFIG_FILE`: points to the file that contains the TOML configuration (default: not set). If set, takes precedence over `OPENML_REST_API_CONFIG_DIRECTORY`. + - `OPENML_REST_API_DOTENV_FILE`: points to the dot file that contains the environment variable settings (default: not set). If set, takes precedence over `OPENML_REST_API_CONFIG_DIRECTORY`. + - `OPENML_DATABASES_OPENML_USERNAME`: username for connecting to the `openml` database (default: `root`) + - `OPENML_DATABASES_OPENML_PASSWORD`: password for connecting to the `openml` database (default: `ok`) + - `OPENML_DATABASES_EXPDB_USERNAME`: username for connecting to the `openml_expdb` database (default: `root`) + - `OPENML_DATABASES_EXPDB_PASSWORD`: password for connecting to the `openml_expdb` database (default: `ok`) + + +## Repository +The code and dockerfile for this image are maintained [on GitHub](https://www.github.com/openml/server-api). From 9e26db9af37bc3425e7ef7615617e6533ca296de Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 12:02:31 +0100 Subject: [PATCH 5/7] Update variable name after rename --- docker/python/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/python/Dockerfile b/docker/python/Dockerfile index 279be15a..2837e165 100644 --- a/docker/python/Dockerfile +++ b/docker/python/Dockerfile @@ -18,7 +18,7 @@ COPY . /app RUN mkdir /config COPY ./src/config.toml /config/config.toml -ENV OPENML_REST_API_CONFIG_FILE_PATH=/config/config.toml +ENV OPENML_REST_API_CONFIG_FILE=/config/config.toml COPY ./docker/python/README.md /README.md EXPOSE 8000 From 5cf246e67cf8ea33c96dac07206e918aa1f65ab2 Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 12:03:16 +0100 Subject: [PATCH 6/7] fix typo --- docker/python/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/python/README.md b/docker/python/README.md index a9671dfd..d17f5063 100644 --- a/docker/python/README.md +++ b/docker/python/README.md @@ -4,7 +4,7 @@ This should not be considered production ready. New releases may contain breaking changes without deprecation warnings. ## Usage -The image is not inteded to be used as a standalone image. +The image is not intended to be used as a standalone image. Please reference either the docker compose file at [server-api](https://www.github.com/openml/server-api) for development purposes, or [services](https://www.github.com/openml/services) for deployment purposes. ## Configuration From db431b1d2553920bab2957dff8746e7273d55b8e Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Fri, 20 Feb 2026 12:12:52 +0100 Subject: [PATCH 7/7] Minor fixes --- docker-compose.yaml | 2 +- src/config.py | 1 - src/main.py | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 3541fee5..e93b548b 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -77,7 +77,7 @@ services: ports: - "8001:8000" volumes: - - .:/python-api + - .:/app - ./src/config.toml:/config/config.toml environment: OPENML_REST_API_CONFIG_DIRECTORY: "/config" diff --git a/src/config.py b/src/config.py index 2d49246e..4a234a51 100644 --- a/src/config.py +++ b/src/config.py @@ -7,7 +7,6 @@ from dotenv import load_dotenv -logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) TomlTable = dict[str, typing.Any] diff --git a/src/main.py b/src/main.py index d8e61b34..560b4c50 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ import argparse +import logging import uvicorn from fastapi import FastAPI @@ -58,6 +59,7 @@ def create_api() -> FastAPI: def main() -> None: + logging.basicConfig(level=logging.INFO) args = _parse_args() uvicorn.run( app="main:create_api",