pyantz.api package#

Subpackages#

Submodules#

pyantz.api.auth module#

Optional token authentication of the api.

require_token(authorization: str | None = None)[source]#

Reject requests without Authorization: Bearer <token>, if a token is set.

The token is read from PYANTZ_API_TOKEN on each request. Without one, every request is let through (the servers only listen on localhost in that case).

Parameters:

authorization (str | None) – the request’s Authorization header, if any.

Return type:

None

Returns:

None.

Raises:

HTTPException – 401 if a token is set and the header doesn’t carry it.

Example

import os

from fastapi import HTTPException

from pyantz.api.auth import require_token

os.environ["PYANTZ_API_TOKEN"] = "secret"
try:
    require_token("Bearer secret")  # accepted
    require_token("Bearer guess")
except HTTPException as exc:
    print(exc.status_code)
finally:
    del os.environ["PYANTZ_API_TOKEN"]
401

pyantz.api.backend module#

Where the application is defined.

app is the api application, served by pyantz-prod (and, reloading on changes, pyantz-dev).

app = <fastapi.applications.FastAPI object>#

The api application, configured from the environment when imported.

Parameters:
  • scope (MutableMapping[str, Any])

  • receive (Callable[[], Awaitable[MutableMapping[str, Any]]])

  • send (Callable[[MutableMapping[str, Any]], Awaitable[None]])

Return type:

None

create_app(frontend_dir: Path | None = None)[source]#

Create the application, reading its settings from the environment.

The cors origins are read from PYANTZ_CORS_ORIGINS, and the built web ui is served at / if PYANTZ_FRONTEND_DIR names it.

Parameters:

frontend_dir (Path | None) – the built web ui to serve at /, instead of the one which PYANTZ_FRONTEND_DIR names

Returns:

the application, with every endpoint under /api/v1.0 requiring the token (if one is set).

Return type:

FastAPI

Raises:

ValueError – if PYANTZ_FRONTEND_DIR is not a directory with an index.html

Example

from pyantz.api.backend import create_app

paths = create_app().openapi()["paths"]
print("/api/v1.0/jobs/get_all_jobs" in paths)
True
start_prod_server()[source]#

Start a production server.

Entry point of pyantz-prod; listens where PYANTZ_HOST and PYANTZ_PORT say, until stopped.

Parameters:

None.

Return type:

None

Returns:

None.

Raises:

ValueError – if the settings are invalid, or the host isn’t local and no token is set.

pyantz.api.dev_server module#

Primary server to run.

start_dev_server()[source]#

Start a development server.

Entry point of pyantz-dev; listens where PYANTZ_HOST and PYANTZ_PORT say, until stopped.

Parameters:

None.

Return type:

None

Returns:

None.

Raises:

ValueError – if the settings are invalid, or the host isn’t local and no token is set.

pyantz.api.dump_schemas module#

Dump the description and parameter schema of every job as json.

The frontend tests use the dump as a fixture, so they see the schemas as the api serves them. Run python -m pyantz.api.dump_schemas [FILE] (standard output without a file) to regenerate it.

dump_schemas()[source]#

Get the description and schema of each job, by job name and sorted by it.

Parameters:

None.

Returns:

for each registered job, its description and the json_schema of its parameters.

Return type:

dict[str, dict[str, object]]

Raises:

fastapi.HTTPException – if a registered job has no parameters model.

Example

from pyantz.api.dump_schemas import dump_schemas

copy = dump_schemas()["pyantz.jobs.files.simple.copy"]
print(copy["description"], sorted(copy["json_schema"]["properties"]))
Copy from src to destination. ['dst', 'src']
main()[source]#

Write the dump to the file given as argument, or to standard output.

Parameters:

None.

Return type:

None

Returns:

None.

Raises:

OSError – if the file can’t be written.

pyantz.api.frontend_server module#

Serve the api and the built web ui from one server (pyantz-frontend).

NOT_INSTALLED = "The web ui is not installed: install it with `pip install 'pyantz[frontend]'`"#

Error message when the frontend feature isn’t installed.

get_bundled_frontend_dir()[source]#

Get the directory with the web ui which comes with pyantz[frontend].

Parameters:

None.

Returns:

the directory of the built web ui.

Return type:

Path

Raises:

ValueError – if the frontend feature is not installed, or its ui is not built

start_frontend_server()[source]#

Start the api and serve the web ui at /.

The ui is the one installed with pyantz[frontend], unless PYANTZ_FRONTEND_DIR names another build. Entry point of pyantz-frontend.

Parameters:

None.

Return type:

None

Returns:

None.

Raises:

SystemExit – if the settings are invalid or the web ui isn’t available.

pyantz.api.settings module#

Settings of the api servers, read from the environment when a server starts.

API_TOKEN_VARIABLE = 'PYANTZ_API_TOKEN'#

Environment variable with the token clients must send, if any.

CORS_ORIGINS_VARIABLE = 'PYANTZ_CORS_ORIGINS'#

Environment variable with the comma separated origins allowed by CORS.

DEFAULT_HOST = '127.0.0.1'#

Address the server listens on by default (only this machine).

DEFAULT_PORT = 8000#

Port the server listens on by default.

FRONTEND_DIR_VARIABLE = 'PYANTZ_FRONTEND_DIR'#

Environment variable with the directory of a built web ui to serve.

HOST_VARIABLE = 'PYANTZ_HOST'#

Environment variable with the address the api server listens on.

LOCAL_HOSTS = frozenset({'127.0.0.1', '::1', 'localhost'})#

Addresses which only this machine can reach.

MAX_PORT = 65535#

Largest valid port.

PORT_VARIABLE = 'PYANTZ_PORT'#

Environment variable with the port the api server listens on.

RUNS_DIR_VARIABLE = 'PYANTZ_RUNS_DIR'#

Environment variable with the directory holding the runs’ directories.

class Settings(*, host: str = '127.0.0.1', port: int = 8000)[source]#

Bases: BaseModel

Where the api server listens.

Parameters:
  • host (str)

  • port (int)

_abc_impl = <_abc._abc_data object>#
host: str#

Address to listen on.

port: int#

Port to listen on.

check_safe_to_bind(settings: Settings)[source]#

Refuse to listen on a non-local address when no token protects the api.

Parameters:

settings (Settings) – where the server is to listen.

Return type:

None

Returns:

None.

Raises:

ValueError – if the host is reachable from other machines and there is no PYANTZ_API_TOKEN

Example

from pyantz.api.settings import Settings, check_safe_to_bind

check_safe_to_bind(Settings(host="localhost"))  # fine
try:
    check_safe_to_bind(Settings(host="0.0.0.0"))
except ValueError:
    print("refused")
refused
get_api_token()[source]#

Get the token which clients must send, if PYANTZ_API_TOKEN is set.

Parameters:

None.

Returns:

the token, or None if the variable isn’t set or is empty.

Return type:

str | None

Raises:

None. –

get_cors_origins()[source]#

Get the origins allowed to call the api from PYANTZ_CORS_ORIGINS.

The variable holds comma separated origins, e.g. http://localhost:5173.

Parameters:

None.

Returns:

the origins; empty if the variable isn’t set.

Return type:

list[str]

Raises:

None. –

Example

import os

from pyantz.api.settings import get_cors_origins

os.environ["PYANTZ_CORS_ORIGINS"] = "http://localhost:5173, ,http://a.b"
try:
    print(get_cors_origins())
finally:
    del os.environ["PYANTZ_CORS_ORIGINS"]
['http://localhost:5173', 'http://a.b']
get_frontend_dir()[source]#

Get the directory with the built web ui, if PYANTZ_FRONTEND_DIR is set.

Build it with npm run build in frontend/pyantz, which makes dist. The server serves it at / next to the api, without asking for the token: the page has to load to ask the user for it.

Parameters:

None.

Returns:

the directory, or None if the variable isn’t set.

Return type:

Path | None

Raises:

None. –

get_runs_dir()[source]#

Get the directory holding one working directory per run.

Parameters:

None.

Returns:

PYANTZ_RUNS_DIR if set, otherwise pyantz-runs in the system’s temporary directory.

Return type:

Path

Raises:

None. –

get_settings()[source]#

Read the settings from PYANTZ_HOST and PYANTZ_PORT.

Parameters:

None.

Returns:

the settings, with defaults for the variables which aren’t set.

Return type:

Settings

Raises:

ValueError – if a value is invalid; the message names the variable

Example

import os

from pyantz.api.settings import get_settings

os.environ["PYANTZ_PORT"] = "70000"
try:
    get_settings()
except ValueError as exc:
    print(str(exc).split(":")[1].strip())
finally:
    del os.environ["PYANTZ_PORT"]
PYANTZ_PORT='70000'

Module contents#

Simple REST API to allow a user to edit a config in a frontend.