Skip to content

Jobs

jobs

Modules:

Name Description
job
jobs
manager
sql

Functions:

Name Description
get_parameters

Get the job's input parameters.

update_progress

Update the job's progress (0-100) with an optional message.

fail

Mark the job as failed with an error message.

complete

Mark the job as completed with a result dictionary.

get_context

Get the context object from caller's globals (set by runpy.run_path).

Attributes:

Name Type Description
T

T module-attribute

T = TypeVar('T')

get_parameters

get_parameters() -> dict[str, Any]

Get the job's input parameters.

Source code in src/jobs/__init__.py
def get_parameters() -> dict[str, Any]:
    """Get the job's input parameters."""
    return _get_job().parameters

update_progress

update_progress(
    progress: int, message: str | None = None
) -> None

Update the job's progress (0-100) with an optional message.

Source code in src/jobs/__init__.py
def update_progress(progress: int, message: str | None = None) -> None:
    """Update the job's progress (0-100) with an optional message."""
    _get_job().update_progress(progress, message)

fail

fail(error: str) -> None

Mark the job as failed with an error message.

Source code in src/jobs/__init__.py
def fail(error: str) -> None:
    """Mark the job as failed with an error message."""
    _get_job().fail(error)

complete

complete(result: dict[str, Any]) -> None

Mark the job as completed with a result dictionary.

Source code in src/jobs/__init__.py
def complete(result: dict[str, Any]) -> None:
    """Mark the job as completed with a result dictionary."""
    _get_job().complete(result)

get_context

get_context(type: type[T]) -> T
get_context(type: None = None) -> Any
get_context(type: type[T] | None = None) -> T | Any

Get the context object from caller's globals (set by runpy.run_path).

If a type is provided, validates that the context is an instance of that type and returns it with the correct type annotation.

Source code in src/jobs/__init__.py
def get_context(type: type[T] | None = None) -> T | Any:
    """Get the context object from caller's globals (set by runpy.run_path).

    If a type is provided, validates that the context is an instance of that type
    and returns it with the correct type annotation.
    """
    frame = inspect.currentframe()
    try:
        caller = frame.f_back if frame else None
        while caller:
            context = caller.f_globals.get("__CONTEXT__")
            if context is not None:
                if type is not None:
                    # Compare by fully-qualified name instead of isinstance()
                    # to handle runpy.run_path() module namespace issues
                    expected = f"{type.__module__}.{type.__qualname__}"
                    actual = f"{context.__class__.__module__}.{context.__class__.__qualname__}"
                    if expected != actual:
                        raise TypeError(
                            f"Context is not an instance of {type.__name__}, got {context.__class__.__name__}"
                        )
                return context
            caller = caller.f_back
        raise RuntimeError("__CONTEXT__ not found in call stack. Are you running inside a job with context?")
    finally:
        del frame

jobs.job

Classes:

Name Description
Job

Attributes:

Name Type Description
JobStatus

JobStatus module-attribute

JobStatus = Literal[
    "pending", "running", "completed", "failed", "cancelled"
]

Job

Bases: ABC

Methods:

Name Description
update_progress

Updates the job's progress.

start

Starts the job.

cancel

Cancels the job.

complete

Completes the job.

fail

Sets the job into failed state.

restart

Restarts the job.

remove

Remove the job from the repository.

Attributes:

Name Type Description
id str

The unique id of the job.

type str

A category of the job. To allow applications to distinguish between different process types.

name str

A unique technical name for the job.

title str

A human-readable title which might be shown to the end-user.

file Path

A Python file which defines the actual job logic.

parameters dict[str, Any]

Serialized set of input parameters for the job.

created datetime

The datetime when the job has been created (UTC).

started_at datetime | None

The dateime when the job has been started.

completed_at datetime | None

The datetime when the job was last updated.

updated_at datetime

The datetime when the job was last updated.

no_progres_timeout_seconds int

The time in seconds the job should be considered as stale when no update has been recorded.

retention_seconds int

Time in seconds the job should be kept in database after completion before automatic deletion.

progress int

The progress as percent value (0 - 100).

progress_message str | None

A message (for users) which describes the current progress.

status JobStatus

The status of the job.

max_retries int

The number of maximum retries for the job.

retries int

Number of current retry attempts.

retry_timeout_seconds int

The retry timeout in seconds.

result dict[str, Any] | None

A serialized result of the job.

error str | None

If job has failed, this property will contain the error message.

id abstractmethod property

id: str

The unique id of the job.

type abstractmethod property

type: str

A category of the job. To allow applications to distinguish between different process types.

name abstractmethod property

name: str

A unique technical name for the job.

title abstractmethod property

title: str

A human-readable title which might be shown to the end-user.

file abstractmethod property

file: Path

A Python file which defines the actual job logic.

parameters abstractmethod property

parameters: dict[str, Any]

Serialized set of input parameters for the job.

created abstractmethod property

created: datetime

The datetime when the job has been created (UTC).

started_at abstractmethod property

started_at: datetime | None

The dateime when the job has been started.

completed_at abstractmethod property

completed_at: datetime | None

The datetime when the job was last updated.

updated_at abstractmethod property

updated_at: datetime

The datetime when the job was last updated.

no_progres_timeout_seconds abstractmethod property

no_progres_timeout_seconds: int

The time in seconds the job should be considered as stale when no update has been recorded. When this timeout is reached, the job process will be killed and it will be restarted.

retention_seconds abstractmethod property

retention_seconds: int

Time in seconds the job should be kept in database after completion before automatic deletion.

progress abstractmethod property

progress: int

The progress as percent value (0 - 100).

progress_message abstractmethod property

progress_message: str | None

A message (for users) which describes the current progress.

status abstractmethod property

status: JobStatus

The status of the job.

max_retries abstractmethod property

max_retries: int

The number of maximum retries for the job.

retries abstractmethod property

retries: int

Number of current retry attempts.

retry_timeout_seconds abstractmethod property

retry_timeout_seconds: int

The retry timeout in seconds.

result abstractmethod property

result: dict[str, Any] | None

A serialized result of the job.

error abstractmethod property

error: str | None

If job has failed, this property will contain the error message.

update_progress

update_progress(
    progress: int, progress_message: str | None = None
) -> None

Updates the job's progress.

Source code in src/jobs/job.py
def update_progress(self, progress: int, progress_message: str | None = None) -> None:
    """Updates the job's progress."""

    self._update(
        updated_at=datetime_operators.utc_now(),
        progress=min(100, max(0, progress)),
        progress_message=progress_message,
        started_at=self.started_at,
        completed_at=self.completed_at,
        status=self.status,
        result=self.result,
        error=self.error,
        retries=self.retries,
    )

start

start() -> None

Starts the job.

Source code in src/jobs/job.py
def start(self) -> None:
    """Starts the job."""
    self._update(
        updated_at=datetime_operators.utc_now(),
        started_at=datetime_operators.utc_now(),
        status="running",
        completed_at=self.completed_at,
        progress=0,
        progress_message=None,
        result={},
        error=None,
        retries=self.retries,
    )

cancel

cancel() -> None

Cancels the job.

Source code in src/jobs/job.py
def cancel(self) -> None:
    """Cancels the job."""

    self._update(
        updated_at=datetime_operators.utc_now(),
        completed_at=datetime_operators.utc_now(),
        status="cancelled",
        started_at=self.started_at,
        progress=100,
        progress_message=None,
        result=self.result,
        error=None,
        retries=self.retries,
    )

complete

complete(result: dict[str, Any]) -> None

Completes the job.

Source code in src/jobs/job.py
def complete(self, result: dict[str, Any]) -> None:
    """Completes the job."""

    self._update(
        updated_at=datetime_operators.utc_now(),
        completed_at=datetime_operators.utc_now(),
        status="completed",
        result=result,
        started_at=self.started_at,
        progress=100,
        progress_message=None,
        error=None,
        retries=self.retries,
    )

fail

fail(error: str) -> None

Sets the job into failed state.

Source code in src/jobs/job.py
def fail(self, error: str) -> None:
    """Sets the job into failed state."""

    self._update(
        updated_at=datetime_operators.utc_now(),
        completed_at=datetime_operators.utc_now(),
        status="failed",
        error=error,
        started_at=self.started_at,
        progress=self.progress,
        progress_message=self.progress_message,
        result=self.result,
        retries=self.retries,
    )

restart

restart() -> None

Restarts the job.

Source code in src/jobs/job.py
def restart(self) -> None:
    """Restarts the job."""

    self._update(
        updated_at=datetime_operators.utc_now(),
        started_at=datetime_operators.utc_now(),
        status="running",
        retries=self.retries + 1,
        completed_at=self.completed_at,
        progress=0,
        progress_message=None,
        result={},
        error=None,
    )

remove abstractmethod

remove() -> None

Remove the job from the repository.

Source code in src/jobs/job.py
@abstractmethod
def remove(self) -> None:
    """Remove the job from the repository."""

jobs.jobs

Classes:

Name Description
JobsRepositoryPort

Attributes:

Name Type Description
JobOrder

JobOrder module-attribute

JobOrder = Literal['created_asc', 'created_desc']

JobsRepositoryPort

Bases: ABC

Methods:

Name Description
find_job
get_job
get_jobs
create

find_job abstractmethod

find_job(id: str) -> Job | None
Source code in src/jobs/jobs.py
@abstractmethod
def find_job(self, id: str) -> Job | None: ...

get_job

get_job(id: str) -> Job
Source code in src/jobs/jobs.py
def get_job(self, id: str) -> Job:
    job = self.find_job(id)
    assert job is not None, f"No job with id `{id}`"
    return job

get_jobs abstractmethod

get_jobs(
    type: str | None = None,
    status: JobStatus | None = None,
    name: str | None = None,
    order: JobOrder = "created_desc",
) -> list[Job]
Source code in src/jobs/jobs.py
@abstractmethod
def get_jobs(
    self,
    type: str | None = None,
    status: JobStatus | None = None,
    name: str | None = None,
    order: JobOrder = "created_desc",
) -> list[Job]: ...

create

create(
    *,
    file: Path,
    id: str | None = None,
    type: str = "job",
    name: str | None = None,
    title: str | None = None,
    parameters: dict[str, Any] | None = None,
    no_progres_timeout_seconds: int = 60 * 5,
    retention_seconds: int = 60 * 60 * 24,
    max_retries: int = 0,
    retry_timeout_seconds: int = 60
) -> Job
Source code in src/jobs/jobs.py
def create(
    self,
    *,
    file: Path,
    id: str | None = None,
    type: str = "job",
    name: str | None = None,
    title: str | None = None,
    parameters: dict[str, Any] | None = None,
    no_progres_timeout_seconds: int = 60 * 5,
    retention_seconds: int = 60 * 60 * 24,
    max_retries: int = 0,
    retry_timeout_seconds: int = 60,
) -> Job:
    if id is None:
        id = new_id()

    if name is None:
        name = random_name([job.name for job in self.get_jobs()])

    if title is None:
        title = name

    if parameters is None:
        parameters = {}

    return self._create_job(
        file=file,
        id=id,
        type=type,
        name=name,
        title=title,
        parameters=parameters,
        no_progres_timeout_seconds=no_progres_timeout_seconds,
        retention_seconds=retention_seconds,
        max_retries=max_retries,
        retry_timeout_seconds=retry_timeout_seconds,
        created=utc_now(),
        started_at=None,
        completed_at=None,
        updated_at=utc_now(),
        progress=0,
        progress_message=None,
        status="pending",
        retries=0,
        result=None,
        error=None,
    )

jobs.manager

Classes:

Name Description
JobUpdate
JobsUpdate
JobsManager

Attributes:

Name Type Description
logger

logger module-attribute

logger = logging.getLogger(__name__)

JobUpdate dataclass

JobUpdate(
    id: str,
    progress: int,
    last_update: datetime,
    progress_message: str | None,
    status: JobStatus,
    result: dict[str, Any] | None,
    error: str | None,
)

Attributes:

Name Type Description
id str
progress int
last_update datetime
progress_message str | None
status JobStatus
result dict[str, Any] | None
error str | None

id instance-attribute

id: str

progress instance-attribute

progress: int

last_update instance-attribute

last_update: datetime

progress_message instance-attribute

progress_message: str | None

status instance-attribute

status: JobStatus

result instance-attribute

result: dict[str, Any] | None

error instance-attribute

error: str | None

JobsUpdate dataclass

JobsUpdate(
    progress: int,
    last_update: datetime,
    progress_message: str | None,
    running_count: int,
    failed: int,
    runtime: timedelta,
)

Attributes:

Name Type Description
progress int
last_update datetime
progress_message str | None
running_count int
failed int
runtime timedelta

progress instance-attribute

progress: int

last_update instance-attribute

last_update: datetime

progress_message instance-attribute

progress_message: str | None

running_count instance-attribute

running_count: int

failed instance-attribute

failed: int

runtime instance-attribute

runtime: timedelta

JobsManager

JobsManager(
    jobs: JobsRepositoryPort,
    max_workers: int = 4,
    recover_running: bool = False,
    context: Any | None = None,
)

Methods:

Name Description
start

Start the executor and polling thread.

stop

Stop the executor gracefully.

subscribe

Listen to updates of a job.

subscribe_all

Listen to updates from multiple jobs, yielding aggregated progress.

Attributes:

Name Type Description
jobs
Source code in src/jobs/manager.py
def __init__(
    self, jobs: JobsRepositoryPort, max_workers: int = 4, recover_running: bool = False, context: Any | None = None
) -> None:
    self.jobs = jobs
    self._max_workers = max_workers
    self._recover_running = recover_running
    self._executor: ThreadPoolExecutor | None = None
    self._poll_thread: Thread | None = None
    self._stop_event = Event()
    self._active_jobs: dict[str, Future] = {}  # job_id -> Future
    self._context = context

jobs instance-attribute

jobs = jobs

start

start() -> None

Start the executor and polling thread.

Source code in src/jobs/manager.py
def start(self) -> None:
    """Start the executor and polling thread."""
    if self._recover_running:
        self._recover_running_jobs()
    self._stop_event.clear()
    self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
    self._poll_thread = Thread(target=self._poll_loop, daemon=True)
    self._poll_thread.start()
    logger.info("JobsManager started with %d workers", self._max_workers)

stop

stop(
    wait: bool = True, timeout: float | None = None
) -> None

Stop the executor gracefully.

Source code in src/jobs/manager.py
def stop(self, wait: bool = True, timeout: float | None = None) -> None:
    """Stop the executor gracefully."""
    logger.info("JobsManager stopping")
    self._stop_event.set()
    if self._executor:
        self._executor.shutdown(wait=wait, cancel_futures=True)
    if self._poll_thread and timeout != 0:
        self._poll_thread.join(timeout=timeout)

subscribe

subscribe(
    id: str, poll_interval: float = 0.5
) -> Iterator[JobUpdate]

Listen to updates of a job.

The iterator will complete when processing of the job is done.

Parameters:

Name Type Description Default
id str

The id of the job to listen to.

required
poll_interval float

How often to poll for updates in seconds.

0.5
Source code in src/jobs/manager.py
def subscribe(self, id: str, poll_interval: float = 0.5) -> Iterator[JobUpdate]:
    """
    Listen to updates of a job.

    The iterator will complete when processing of the job is done.

    Args:
        id: The id of the job to listen to.
        poll_interval: How often to poll for updates in seconds.
    """
    terminal_statuses = ("completed", "failed", "cancelled")
    last_update: datetime | None = None
    last_progress: int | None = None

    while True:
        job = self.jobs.find_job(id)
        if job is None:
            return  # Job not found or was removed

        # Yield if this is a new update
        if job.updated_at != last_update or job.progress != last_progress:
            last_update = job.updated_at
            last_progress = job.progress
            yield JobUpdate(
                id=job.id,
                progress=job.progress,
                last_update=job.updated_at,
                progress_message=job.progress_message,
                status=job.status,
                result=job.result,
                error=job.error,
            )

        # Stop if terminal
        if job.status in terminal_statuses:
            return

        time.sleep(poll_interval)

subscribe_all

subscribe_all(
    ids: list[str], poll_interval: float = 0.5
) -> Iterator[JobsUpdate]

Listen to updates from multiple jobs, yielding aggregated progress.

The iterator will complete when all jobs reach a terminal status.

Parameters:

Name Type Description Default
ids list[str]

List of job ids to subscribe to.

required
poll_interval float

How often to poll for updates in seconds.

0.5
Source code in src/jobs/manager.py
def subscribe_all(self, ids: list[str], poll_interval: float = 0.5) -> Iterator[JobsUpdate]:
    """
    Listen to updates from multiple jobs, yielding aggregated progress.

    The iterator will complete when all jobs reach a terminal status.

    Args:
        ids: List of job ids to subscribe to.
        poll_interval: How often to poll for updates in seconds.
    """
    if not ids:
        return

    terminal_statuses = ("completed", "failed", "cancelled")
    last_states: dict[str, tuple[datetime | None, int]] = {id: (None, -1) for id in ids}
    last_yield_time: float = 0
    time_update_interval: float = 5.0  # Yield at least every 5 seconds for runtime updates
    batch_start_time: datetime | None = None  # Track when first job started

    while True:
        jobs = [self.jobs.find_job(id) for id in ids]
        active_jobs = [j for j in jobs if j is not None]

        if not active_jobs:
            return  # All jobs removed

        # Check if any job has changed
        changed = False
        for job in active_jobs:
            last_update, last_progress = last_states.get(job.id, (None, -1))
            if job.updated_at != last_update or job.progress != last_progress:
                last_states[job.id] = (job.updated_at, job.progress)
                changed = True

        # Also yield periodically for runtime updates
        time_since_yield = time.monotonic() - last_yield_time
        should_yield = changed or time_since_yield >= time_update_interval

        if should_yield:
            # Calculate aggregates
            total_progress = sum(j.progress for j in active_jobs)
            progress = total_progress // len(active_jobs)

            # Find newest update and its message
            newest_job = max(active_jobs, key=lambda j: j.updated_at)
            last_update = newest_job.updated_at
            progress_message = newest_job.progress_message

            # Count failed jobs
            failed = sum(1 for j in active_jobs if j.status == "failed")

            # Calculate runtime from when first job in batch started
            if batch_start_time is None:
                started_times = [j.started_at for j in active_jobs if j.started_at is not None]
                if started_times:
                    batch_start_time = min(started_times)
                    if batch_start_time.tzinfo is None:
                        batch_start_time = batch_start_time.replace(tzinfo=timezone.utc)

            if batch_start_time is not None:
                runtime = utc_now() - batch_start_time
            else:
                runtime = timedelta(0)

            yield JobsUpdate(
                progress=progress,
                last_update=last_update,
                progress_message=progress_message,
                running_count=len(active_jobs),
                failed=failed,
                runtime=runtime,
            )
            last_yield_time = time.monotonic()

        # Stop if all jobs are terminal
        if all(j.status in terminal_statuses for j in active_jobs):
            return

        time.sleep(poll_interval)

jobs.sql

Classes:

Name Description
Base
JobPE

Persistence entity for Job.

SQLJob

SQL implementation of Job.

SQLJobsRepository

SQL implementation of JobsRepositoryPort.

Base

Bases: MappedAsDataclass, DeclarativeBase

JobPE

Bases: Base

Persistence entity for Job.

Attributes:

Name Type Description
__tablename__
id Mapped[str]
type Mapped[str]
name Mapped[str]
title Mapped[str]
file Mapped[str]
created Mapped[datetime]
updated_at Mapped[datetime]
no_progress_timeout_seconds Mapped[int]
retention_seconds Mapped[int]
max_retries Mapped[int]
retry_timeout_seconds Mapped[int]
status Mapped[str]
parameters_json Mapped[str]
progress Mapped[int]
retries Mapped[int]
result_json Mapped[str]
started_at Mapped[Optional[datetime]]
completed_at Mapped[Optional[datetime]]
progress_message Mapped[Optional[str]]
error Mapped[Optional[str]]

__tablename__ class-attribute instance-attribute

__tablename__ = 'jobs'

id class-attribute instance-attribute

id: Mapped[str] = mapped_column(
    String(64), primary_key=True
)

type class-attribute instance-attribute

type: Mapped[str] = mapped_column(String(64), index=True)

name class-attribute instance-attribute

name: Mapped[str] = mapped_column(String(128), unique=True)

title class-attribute instance-attribute

title: Mapped[str] = mapped_column(String(256))

file class-attribute instance-attribute

file: Mapped[str] = mapped_column(String)

created class-attribute instance-attribute

created: Mapped[datetime] = mapped_column(DateTime)

updated_at class-attribute instance-attribute

updated_at: Mapped[datetime] = mapped_column(DateTime)

no_progress_timeout_seconds class-attribute instance-attribute

no_progress_timeout_seconds: Mapped[int] = mapped_column(
    Integer
)

retention_seconds class-attribute instance-attribute

retention_seconds: Mapped[int] = mapped_column(Integer)

max_retries class-attribute instance-attribute

max_retries: Mapped[int] = mapped_column(Integer)

retry_timeout_seconds class-attribute instance-attribute

retry_timeout_seconds: Mapped[int] = mapped_column(Integer)

status class-attribute instance-attribute

status: Mapped[str] = mapped_column(String(32), index=True)

parameters_json class-attribute instance-attribute

parameters_json: Mapped[str] = mapped_column(
    String, default="{}"
)

progress class-attribute instance-attribute

progress: Mapped[int] = mapped_column(Integer, default=0)

retries class-attribute instance-attribute

retries: Mapped[int] = mapped_column(Integer, default=0)

result_json class-attribute instance-attribute

result_json: Mapped[str] = mapped_column(
    String, default="{}"
)

started_at class-attribute instance-attribute

started_at: Mapped[Optional[datetime]] = mapped_column(
    DateTime, nullable=True, default=None
)

completed_at class-attribute instance-attribute

completed_at: Mapped[Optional[datetime]] = mapped_column(
    DateTime, nullable=True, default=None
)

progress_message class-attribute instance-attribute

progress_message: Mapped[Optional[str]] = mapped_column(
    String, nullable=True, default=None
)

error class-attribute instance-attribute

error: Mapped[Optional[str]] = mapped_column(
    String, nullable=True, default=None
)

SQLJob

SQLJob(engine: Engine, job_pe: JobPE)

Bases: Job

SQL implementation of Job.

Methods:

Name Description
remove

Attributes:

Name Type Description
id str
type str
name str
title str
file Path
parameters dict[str, Any]
created datetime
started_at datetime | None
completed_at datetime | None
updated_at datetime
no_progres_timeout_seconds int
retention_seconds int
progress int
progress_message str | None
status JobStatus
max_retries int
retries int
retry_timeout_seconds int
result dict[str, Any] | None
error str | None
Source code in src/jobs/sql.py
def __init__(self, engine: Engine, job_pe: JobPE) -> None:
    self._engine = engine
    self._create_session = sessionmaker(bind=self._engine)
    self._id = job_pe.id
    self._type = job_pe.type
    self._name = job_pe.name
    self._title = job_pe.title
    self._file = job_pe.file
    self._parameters_json = job_pe.parameters_json
    self._created = job_pe.created
    self._started_at = job_pe.started_at
    self._completed_at = job_pe.completed_at
    self._updated_at = job_pe.updated_at
    self._no_progress_timeout_seconds = job_pe.no_progress_timeout_seconds
    self._retention_seconds = job_pe.retention_seconds
    self._max_retries = job_pe.max_retries
    self._retry_timeout_seconds = job_pe.retry_timeout_seconds
    self._progress = job_pe.progress
    self._progress_message = job_pe.progress_message
    self._status = job_pe.status
    self._retries = job_pe.retries
    self._result_json = job_pe.result_json
    self._error = job_pe.error

id property

id: str

type property

type: str

name property

name: str

title property

title: str

file property

file: Path

parameters property

parameters: dict[str, Any]

created property

created: datetime

started_at property

started_at: datetime | None

completed_at property

completed_at: datetime | None

updated_at property

updated_at: datetime

no_progres_timeout_seconds property

no_progres_timeout_seconds: int

retention_seconds property

retention_seconds: int

progress property

progress: int

progress_message property

progress_message: str | None

status property

status: JobStatus

max_retries property

max_retries: int

retries property

retries: int

retry_timeout_seconds property

retry_timeout_seconds: int

result property

result: dict[str, Any] | None

error property

error: str | None

remove

remove() -> None
Source code in src/jobs/sql.py
def remove(self) -> None:
    with self._create_session() as s:
        job_pe = s.query(JobPE).filter(JobPE.id == self._id).first()
        if job_pe:
            s.delete(job_pe)
            s.commit()

SQLJobsRepository

SQLJobsRepository(engine: Engine)

Bases: JobsRepositoryPort

SQL implementation of JobsRepositoryPort.

Methods:

Name Description
create_from_settings
find_job
get_jobs
Source code in src/jobs/sql.py
def __init__(self, engine: Engine) -> None:
    self._engine = engine
    Base.metadata.create_all(self._engine)
    self._create_session = sessionmaker(bind=self._engine)

create_from_settings staticmethod

create_from_settings() -> SQLJobsRepository
Source code in src/jobs/sql.py
@staticmethod
def create_from_settings() -> "SQLJobsRepository":
    return SQLJobsRepository(create_engine())

find_job

find_job(id: str) -> Job | None
Source code in src/jobs/sql.py
def find_job(self, id: str) -> Job | None:
    with self._create_session() as s:
        job_pe = s.query(JobPE).filter(JobPE.id == id).first()

        if job_pe:
            return SQLJob(self._engine, job_pe)
        return None

get_jobs

get_jobs(
    type: str | None = None,
    status: JobStatus | None = None,
    name: str | None = None,
    order: JobOrder = "created_desc",
) -> list[Job]
Source code in src/jobs/sql.py
def get_jobs(
    self,
    type: str | None = None,
    status: JobStatus | None = None,
    name: str | None = None,
    order: JobOrder = "created_desc",
) -> list[Job]:
    with self._create_session() as s:
        query = s.query(JobPE)

        if type is not None:
            query = query.filter(JobPE.type == type)

        if status is not None:
            query = query.filter(JobPE.status == status)

        if name is not None:
            query = query.filter(JobPE.name == name)

        if order == "created_asc":
            query = query.order_by(JobPE.created.asc())
        else:
            query = query.order_by(JobPE.created.desc())

        return [SQLJob(self._engine, job_pe) for job_pe in query.all()]