Skip to content

Commit

Permalink
Update FastAPI Safir UWS app for latest Safir
Browse files Browse the repository at this point in the history
The UWS integration in Safir has changed dramatically in 9.0.1 to
use the parameter model directly in dependencies, require an
additional XML model, register that model in the UWS configuration,
and no longer require a command-line interface since each UWS app
no longer needs to manage its own database. Update the template
accordingly.
  • Loading branch information
rra committed Dec 13, 2024
1 parent f238ee7 commit 13fc065
Show file tree
Hide file tree
Showing 18 changed files with 69 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ requires-python = ">=3.12"
dependencies = []
dynamic = ["version"]

[project.scripts]
example-uws = "exampleuws.cli:main"

[project.urls]
Homepage = "https://example-uws.lsst.io"
Source = "https://github.com/lsst-sqre/example-uws"
Expand All @@ -33,8 +30,6 @@ Source = "https://github.com/lsst-sqre/example-uws"
requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]

[tool.coverage.run]
parallel = true
branch = true
Expand Down Expand Up @@ -111,3 +106,5 @@ format = "md"
md_header_level = "2"
new_fragment_template = "file:changelog.d/_template.md.jinja"
skip_fragments = "_template.md.jinja"

[tool.setuptools_scm]
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@
# After editing, update requirements/main.txt by running:
# make update-deps

# These dependencies are for fastapi including some optional features.
fastapi>=0.100
uvicorn[standard]

# Other dependencies.
pydantic>2
pydantic-settings
safir[uws]>=6.2.0
safir[uws]>=9.0.1
uvicorn[standard]

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from pydantic_settings import SettingsConfigDict
from safir.logging import LogLevel, Profile
from safir.uws import UWSApplication, UWSAppSettings, UWSConfig, UWSRoute
from vo_models.uws import JobSummary

from .dependencies import post_params_dependency
from .models import ExampleuwsParameters
from .models import ExampleuwsParameters, ExampleuwsXmlParameters

__all__ = ["Config", "config"]

Expand All @@ -20,12 +21,12 @@ class Config(UWSAppSettings):
env_prefix="EXAMPLE_UWS_", case_sensitive=False
)

name: str = Field("example-uws", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

name: str = Field("example-uws", title="Name of application")

path_prefix: str = Field(
"/example-uws", title="URL prefix for application"
)
Expand All @@ -44,6 +45,7 @@ class Config(UWSAppSettings):
def uws_config(self) -> UWSConfig:
"""Corresponding configuration for the UWS subsystem."""
return self.build_uws_config(
job_summary_type=JobSummary[ExampleuwsXmlParameters],
parameters_type=ExampleuwsParameters,
worker="example_uws",
async_post_route=UWSRoute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from typing import Annotated

from fastapi import Depends
from safir.uws import UWSJobParameter

from .models import ExampleuwsParameters

__all__ = [
"post_params_dependency",
Expand All @@ -15,7 +16,6 @@ async def post_params_dependency(
# Add POST parameters here. All of them should be Form() parameters.
# Use str | None for single-valued attributes and list[str] | None for
# parameters that can be given more than one time.
) -> list[UWSJobParameter]:
"""Parse POST parameters into job parameters."""
params: list[UWSJobParameter] = []
# Populate params with the values of all form parameters that were set.
) -> ExampleuwsParameters:
# Populate class with the values of all form parameters that were set.
return ExampleuwsParameters()
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from pydantic import BaseModel, Field
from safir.metadata import Metadata as SafirMetadata
from safir.uws import ParametersModel, UWSJobParameter
from safir.uws import ParametersModel
from vo_models.uws import Parameters

from .domain import WorkerExampleuwsModel

Expand All @@ -25,17 +26,26 @@ class Index(BaseModel):
metadata: SafirMetadata = Field(..., title="Package metadata")


class ExampleuwsParameters(ParametersModel[WorkerExampleuwsModel]):
class ExampleuwsXmlParameters(Parameters):
"""XML representation of job parameters.
Add fields here for all the input parameters to the job in the format
suitable for the IVOA UWS standard (key/value parameters). If a key can be
repeated, use ``MultiValuedParameter`` as its type. Otherwise, use
``Parameter``.
"""


class ExampleuwsParameters(ParametersModel[WorkerExampleuwsModel, ExampleuwsXmlParameters]):
"""Model for job parameters.
Add fields here for all the input parameters to a job, and then update
``from_job_parameters`` and ``to_worker_parameters`` to do the appropriate
``to_worker_parameters`` and ``to_xml_model`` to do the appropriate
conversions.
"""

@classmethod
def from_job_parameters(cls, params: list[UWSJobParameter]) -> Self:
return cls()

def to_worker_parameters(self) -> WorkerExampleuwsModel:
return WorkerExampleuwsModel()

def to_xml_model(self) -> ExampleuwsXmlParameters:
return ExampleuwsXmlParameters()
7 changes: 2 additions & 5 deletions project_templates/fastapi_safir_app/example/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ requires-python = ">=3.12"
dependencies = []
dynamic = ["version"]

[project.scripts]
example = "example.cli:main"

[project.urls]
Homepage = "https://example.lsst.io"
Source = "https://github.com/lsst-sqre/example"
Expand All @@ -33,8 +30,6 @@ Source = "https://github.com/lsst-sqre/example"
requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]

[tool.coverage.run]
parallel = true
branch = true
Expand Down Expand Up @@ -111,3 +106,5 @@ format = "md"
md_header_level = "2"
new_fragment_template = "file:changelog.d/_template.md.jinja"
skip_fragments = "_template.md.jinja"

[tool.setuptools_scm]
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@
# After editing, update requirements/main.txt by running:
# make update-deps

# These dependencies are for fastapi including some optional features.
fastapi>=0.100
uvicorn[standard]

# Other dependencies.
pydantic>2
pydantic-settings
safir>=5
uvicorn[standard]
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ class Config(BaseSettings):
env_prefix="EXAMPLE_", case_sensitive=False
)

name: str = Field("example", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

name: str = Field("example", title="Name of application")

path_prefix: str = Field(
"/example", title="URL prefix for application"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@
os.remove("scripts/install-worker.sh")
os.remove("scripts/install-worker-packages.sh")
os.remove("scripts/start-worker.sh")
os.remove(f"src/{module_name}/cli.py")
os.remove(f"src/{module_name}/dependencies.py")
os.remove(f"src/{module_name}/domain.py")
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ requires-python = ">=3.12"
dependencies = []
dynamic = ["version"]

[project.scripts]
{{cookiecutter.name | lower}} = "{{cookiecutter.module_name}}.cli:main"

[project.urls]
Homepage = "https://{{cookiecutter.name | lower}}.lsst.io"
Source = "https://github.com/{{cookiecutter.github_org}}/{{cookiecutter.name}}"
Expand All @@ -33,8 +30,6 @@ Source = "https://github.com/{{cookiecutter.github_org}}/{{cookiecutter.name}}"
requires = ["setuptools>=61", "wheel", "setuptools_scm[toml]>=6.2"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]

[tool.coverage.run]
parallel = true
branch = true
Expand Down Expand Up @@ -111,3 +106,5 @@ format = "md"
md_header_level = "2"
new_fragment_template = "file:changelog.d/_template.md.jinja"
skip_fragments = "_template.md.jinja"

[tool.setuptools_scm]
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@
# After editing, update requirements/main.txt by running:
# make update-deps

# These dependencies are for fastapi including some optional features.
fastapi>=0.100
uvicorn[standard]

# Other dependencies.
pydantic>2
pydantic-settings
{%- if cookiecutter.flavor == "UWS" %}
safir[uws]>=6.2.0
safir[uws]>=9.0.1
{%- else %}
safir>=5
{%- endif %}
uvicorn[standard]

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
from safir.logging import LogLevel, Profile
{%- if cookiecutter.flavor == "UWS" %}
from safir.uws import UWSApplication, UWSAppSettings, UWSConfig, UWSRoute
from vo_models.uws import JobSummary

from .dependencies import post_params_dependency
from .models import {{ cookiecutter.module_name | capitalize }}Parameters
from .models import {{ cookiecutter.module_name | capitalize }}Parameters, {{ cookiecutter.module_name | capitalize }}XmlParameters
{%- endif %}

__all__ = ["Config", "config"]
Expand All @@ -22,12 +23,12 @@ class Config({% if cookiecutter.flavor == "UWS" %}UWSAppSettings{% else %}BaseSe
env_prefix="{{ cookiecutter.name | upper | replace('-', '_') }}_", case_sensitive=False
)

name: str = Field("{{ cookiecutter.name }}", title="Name of application")

log_level: LogLevel = Field(
LogLevel.INFO, title="Log level of the application's logger"
)

name: str = Field("{{ cookiecutter.name }}", title="Name of application")

path_prefix: str = Field(
"/{{ cookiecutter.name | lower }}", title="URL prefix for application"
)
Expand All @@ -47,6 +48,7 @@ class Config({% if cookiecutter.flavor == "UWS" %}UWSAppSettings{% else %}BaseSe
def uws_config(self) -> UWSConfig:
"""Corresponding configuration for the UWS subsystem."""
return self.build_uws_config(
job_summary_type=JobSummary[{{ cookiecutter.module_name | capitalize }}XmlParameters],
parameters_type={{ cookiecutter.module_name | capitalize }}Parameters,
worker="{{ cookiecutter.name | replace('-', '_') }}",
async_post_route=UWSRoute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from typing import Annotated

from fastapi import Depends
from safir.uws import UWSJobParameter

from .models import {{ cookiecutter.module_name | capitalize }}Parameters

__all__ = [
"post_params_dependency",
Expand All @@ -16,8 +17,7 @@ async def post_params_dependency(
# Add POST parameters here. All of them should be Form() parameters.
# Use str | None for single-valued attributes and list[str] | None for
# parameters that can be given more than one time.
) -> list[UWSJobParameter]:
"""Parse POST parameters into job parameters."""
params: list[UWSJobParameter] = []
# Populate params with the values of all form parameters that were set.
) -> {{ cookiecutter.module_name | capitalize }}Parameters:
# Populate class with the values of all form parameters that were set.
return {{ cookiecutter.module_name | capitalize }}Parameters()
{%- endif %}
Loading

0 comments on commit 13fc065

Please sign in to comment.