diff options
Diffstat (limited to 'src')
28 files changed, 168 insertions, 165 deletions
diff --git a/src/apscheduler/abc.py b/src/apscheduler/abc.py index 58d74cd..929a8cc 100644 --- a/src/apscheduler/abc.py +++ b/src/apscheduler/abc.py @@ -3,7 +3,7 @@ from __future__ import annotations from abc import ABCMeta, abstractmethod from base64 import b64decode, b64encode from datetime import datetime -from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Optional +from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator from uuid import UUID from .enums import ConflictPolicy @@ -19,7 +19,7 @@ class Trigger(Iterator[datetime], metaclass=ABCMeta): __slots__ = () @abstractmethod - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: """ Return the next datetime to fire on. @@ -92,7 +92,7 @@ class EventSource(metaclass=ABCMeta): @abstractmethod def subscribe( self, callback: Callable[[Event], Any], - event_types: Optional[Iterable[type[Event]]] = None, + event_types: Iterable[type[Event]] | None = None, *, one_shot: bool = False ) -> Subscription: @@ -200,7 +200,7 @@ class DataStore: """ @abstractmethod - def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: """ Get schedules from the data store. @@ -249,7 +249,7 @@ class DataStore: """ @abstractmethod - def get_next_schedule_run_time(self) -> Optional[datetime]: + def get_next_schedule_run_time(self) -> datetime | None: """ Return the earliest upcoming run time of all the schedules in the store, or ``None`` if there are no active schedules. @@ -264,7 +264,7 @@ class DataStore: """ @abstractmethod - def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: """ Get the list of pending jobs. @@ -273,7 +273,7 @@ class DataStore: """ @abstractmethod - def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: """ Acquire unclaimed jobs for execution. @@ -296,7 +296,7 @@ class DataStore: """ @abstractmethod - def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + def get_job_result(self, job_id: UUID) -> JobResult | None: """ Retrieve the result of a job. @@ -358,7 +358,7 @@ class AsyncDataStore: """ @abstractmethod - async def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + async def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: """ Get schedules from the data store. @@ -407,7 +407,7 @@ class AsyncDataStore: """ @abstractmethod - async def get_next_schedule_run_time(self) -> Optional[datetime]: + async def get_next_schedule_run_time(self) -> datetime | None: """ Return the earliest upcoming run time of all the schedules in the store, or ``None`` if there are no active schedules. @@ -422,7 +422,7 @@ class AsyncDataStore: """ @abstractmethod - async def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + async def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: """ Get the list of pending jobs. @@ -431,7 +431,7 @@ class AsyncDataStore: """ @abstractmethod - async def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + async def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: """ Acquire unclaimed jobs for execution. @@ -454,7 +454,7 @@ class AsyncDataStore: """ @abstractmethod - async def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + async def get_job_result(self, job_id: UUID) -> JobResult | None: """ Retrieve the result of a job. diff --git a/src/apscheduler/converters.py b/src/apscheduler/converters.py index 103ab35..7a2680d 100644 --- a/src/apscheduler/converters.py +++ b/src/apscheduler/converters.py @@ -2,13 +2,12 @@ from __future__ import annotations from datetime import datetime, timedelta from enum import Enum -from typing import Optional from uuid import UUID from . import abc -def as_aware_datetime(value: datetime | str) -> Optional[datetime]: +def as_aware_datetime(value: datetime | str) -> datetime | None: """Convert the value from a string to a timezone aware datetime.""" if isinstance(value, str): # fromisoformat() does not handle the "Z" suffix diff --git a/src/apscheduler/datastores/async_adapter.py b/src/apscheduler/datastores/async_adapter.py index 3537772..945851f 100644 --- a/src/apscheduler/datastores/async_adapter.py +++ b/src/apscheduler/datastores/async_adapter.py @@ -3,7 +3,7 @@ from __future__ import annotations from contextlib import AsyncExitStack from datetime import datetime from functools import partial -from typing import Iterable, Optional +from typing import Iterable from uuid import UUID import attrs @@ -58,7 +58,7 @@ class AsyncDataStoreAdapter(AsyncDataStore): async def get_tasks(self) -> list[Task]: return await to_thread.run_sync(self.original.get_tasks) - async def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + async def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: return await to_thread.run_sync(self.original.get_schedules, ids) async def add_schedule(self, schedule: Schedule, conflict_policy: ConflictPolicy) -> None: @@ -73,20 +73,20 @@ class AsyncDataStoreAdapter(AsyncDataStore): async def release_schedules(self, scheduler_id: str, schedules: list[Schedule]) -> None: await to_thread.run_sync(self.original.release_schedules, scheduler_id, schedules) - async def get_next_schedule_run_time(self) -> Optional[datetime]: + async def get_next_schedule_run_time(self) -> datetime | None: return await to_thread.run_sync(self.original.get_next_schedule_run_time) async def add_job(self, job: Job) -> None: await to_thread.run_sync(self.original.add_job, job) - async def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + async def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: return await to_thread.run_sync(self.original.get_jobs, ids) - async def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + async def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: return await to_thread.run_sync(self.original.acquire_jobs, worker_id, limit) async def release_job(self, worker_id: str, task_id: str, result: JobResult) -> None: await to_thread.run_sync(self.original.release_job, worker_id, task_id, result) - async def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + async def get_job_result(self, job_id: UUID) -> JobResult | None: return await to_thread.run_sync(self.original.get_job_result, job_id) diff --git a/src/apscheduler/datastores/async_sqlalchemy.py b/src/apscheduler/datastores/async_sqlalchemy.py index 4ad1483..ca97330 100644 --- a/src/apscheduler/datastores/async_sqlalchemy.py +++ b/src/apscheduler/datastores/async_sqlalchemy.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections import defaultdict from datetime import datetime, timedelta, timezone -from typing import Any, Iterable, Optional +from typing import Any, Iterable from uuid import UUID import anyio @@ -216,7 +216,7 @@ class AsyncSQLAlchemyDataStore(_BaseSQLAlchemyDataStore, AsyncDataStore): for schedule_id in removed_ids: await self._events.publish(ScheduleRemoved(schedule_id=schedule_id)) - async def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + async def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: query = self.t_schedules.select().order_by(self.t_schedules.c.id) if ids: query = query.where(self.t_schedules.c.id.in_(ids)) @@ -321,7 +321,7 @@ class AsyncSQLAlchemyDataStore(_BaseSQLAlchemyDataStore, AsyncDataStore): for schedule_id in finished_schedule_ids: await self._events.publish(ScheduleRemoved(schedule_id=schedule_id)) - async def get_next_schedule_run_time(self) -> Optional[datetime]: + async def get_next_schedule_run_time(self) -> datetime | None: statenent = select(self.t_schedules.c.next_fire_time).\ where(self.t_schedules.c.next_fire_time.isnot(None)).\ order_by(self.t_schedules.c.next_fire_time).\ @@ -344,7 +344,7 @@ class AsyncSQLAlchemyDataStore(_BaseSQLAlchemyDataStore, AsyncDataStore): tags=job.tags) await self._events.publish(event) - async def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + async def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: query = self.t_jobs.select().order_by(self.t_jobs.c.id) if ids: job_ids = [job_id for job_id in ids] @@ -356,7 +356,7 @@ class AsyncSQLAlchemyDataStore(_BaseSQLAlchemyDataStore, AsyncDataStore): result = await conn.execute(query) return await self._deserialize_jobs(result) - async def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + async def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: async for attempt in self._retrying: with attempt: async with self.engine.begin() as conn: @@ -444,7 +444,7 @@ class AsyncSQLAlchemyDataStore(_BaseSQLAlchemyDataStore, AsyncDataStore): delete = self.t_jobs.delete().where(self.t_jobs.c.id == result.job_id) await conn.execute(delete) - async def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + async def get_job_result(self, job_id: UUID) -> JobResult | None: async for attempt in self._retrying: with attempt: async with self.engine.begin() as conn: diff --git a/src/apscheduler/datastores/memory.py b/src/apscheduler/datastores/memory.py index 6b8bc7b..65fad9e 100644 --- a/src/apscheduler/datastores/memory.py +++ b/src/apscheduler/datastores/memory.py @@ -4,7 +4,7 @@ from bisect import bisect_left, insort_right from collections import defaultdict from datetime import MAXYEAR, datetime, timedelta, timezone from functools import partial -from typing import Any, Iterable, Optional +from typing import Any, Iterable from uuid import UUID import attrs @@ -35,9 +35,9 @@ class TaskState: @attrs.define class ScheduleState: schedule: Schedule - next_fire_time: Optional[datetime] = attrs.field(init=False, eq=False) - acquired_by: Optional[str] = attrs.field(init=False, eq=False, default=None) - acquired_until: Optional[datetime] = attrs.field(init=False, eq=False, default=None) + next_fire_time: datetime | None = attrs.field(init=False, eq=False) + acquired_by: str | None = attrs.field(init=False, eq=False, default=None) + acquired_until: datetime | None = attrs.field(init=False, eq=False, default=None) def __attrs_post_init__(self): self.next_fire_time = self.schedule.next_fire_time @@ -61,8 +61,8 @@ class ScheduleState: class JobState: job: Job = attrs.field(order=False) created_at: datetime = attrs.field(init=False, factory=partial(datetime.now, timezone.utc)) - acquired_by: Optional[str] = attrs.field(eq=False, order=False, default=None) - acquired_until: Optional[datetime] = attrs.field(eq=False, order=False, default=None) + acquired_by: str | None = attrs.field(eq=False, order=False, default=None) + acquired_until: datetime | None = attrs.field(eq=False, order=False, default=None) def __eq__(self, other): return self.job.id == other.job.id @@ -85,12 +85,12 @@ class MemoryDataStore(DataStore): _jobs_by_task_id: dict[str, set[JobState]] = attrs.Factory(partial(defaultdict, set)) _job_results: dict[UUID, JobResult] = attrs.Factory(dict) - def _find_schedule_index(self, state: ScheduleState) -> Optional[int]: + def _find_schedule_index(self, state: ScheduleState) -> int | None: left_index = bisect_left(self._schedules, state) right_index = bisect_left(self._schedules, state) return self._schedules.index(state, left_index, right_index + 1) - def _find_job_index(self, state: JobState) -> Optional[int]: + def _find_job_index(self, state: JobState) -> int | None: left_index = bisect_left(self._jobs, state) right_index = bisect_left(self._jobs, state) return self._jobs.index(state, left_index, right_index + 1) @@ -106,7 +106,7 @@ class MemoryDataStore(DataStore): def events(self) -> EventSource: return self._events - def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: return [state.schedule for state in self._schedules if ids is None or state.schedule.id in ids] @@ -200,7 +200,7 @@ class MemoryDataStore(DataStore): index = self._find_schedule_index(schedule_state) del self._schedules[index] - # Readd the schedule to its new position + # Re-add the schedule to its new position schedule_state.next_fire_time = s.next_fire_time schedule_state.acquired_by = None schedule_state.acquired_until = None @@ -213,7 +213,7 @@ class MemoryDataStore(DataStore): # Remove schedules that didn't get a new next fire time self.remove_schedules(finished_schedule_ids) - def get_next_schedule_run_time(self) -> Optional[datetime]: + def get_next_schedule_run_time(self) -> datetime | None: return self._schedules[0].next_fire_time if self._schedules else None def add_job(self, job: Job) -> None: @@ -226,16 +226,16 @@ class MemoryDataStore(DataStore): tags=job.tags) self._events.publish(event) - def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: if ids is not None: ids = frozenset(ids) return [state.job for state in self._jobs if ids is None or state.job.id in ids] - def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: now = datetime.now(timezone.utc) jobs: list[Job] = [] - for index, job_state in enumerate(self._jobs): + for _index, job_state in enumerate(self._jobs): task_state = self._tasks[job_state.job.task_id] # Skip already acquired jobs (unless the acquisition lock has expired) @@ -288,5 +288,5 @@ class MemoryDataStore(DataStore): JobReleased(job_id=result.job_id, worker_id=worker_id, outcome=result.outcome) ) - def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + def get_job_result(self, job_id: UUID) -> JobResult | None: return self._job_results.pop(job_id, None) diff --git a/src/apscheduler/datastores/mongodb.py b/src/apscheduler/datastores/mongodb.py index b8c4db5..7d6fa76 100644 --- a/src/apscheduler/datastores/mongodb.py +++ b/src/apscheduler/datastores/mongodb.py @@ -5,7 +5,7 @@ from collections import defaultdict from contextlib import ExitStack from datetime import datetime, timedelta, timezone from logging import Logger, getLogger -from typing import Any, Callable, ClassVar, Iterable, Optional +from typing import Any, Callable, ClassVar, Iterable from uuid import UUID import attrs @@ -182,7 +182,7 @@ class MongoDBDataStore(DataStore): return tasks - def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: filters = {'_id': {'$in': list(ids)}} if ids is not None else {} for attempt in self._retrying: with attempt: @@ -311,7 +311,7 @@ class MongoDBDataStore(DataStore): for schedule_id in finished_schedule_ids: self._events.publish(ScheduleRemoved(schedule_id=schedule_id)) - def get_next_schedule_run_time(self) -> Optional[datetime]: + def get_next_schedule_run_time(self) -> datetime | None: for attempt in self._retrying: with attempt: document = self._schedules.find_one({'next_run_time': {'$ne': None}}, @@ -334,7 +334,7 @@ class MongoDBDataStore(DataStore): tags=job.tags) self._events.publish(event) - def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: filters = {'_id': {'$in': list(ids)}} if ids is not None else {} for attempt in self._retrying: with attempt: @@ -352,7 +352,7 @@ class MongoDBDataStore(DataStore): return jobs - def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: for attempt in self._retrying: with attempt, self.client.start_session() as session: cursor = self._jobs.find( @@ -438,7 +438,7 @@ class MongoDBDataStore(DataStore): JobReleased(job_id=result.job_id, worker_id=worker_id, outcome=result.outcome) ) - def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + def get_job_result(self, job_id: UUID) -> JobResult | None: for attempt in self._retrying: with attempt: document = self._jobs_results.find_one_and_delete({'_id': job_id}) diff --git a/src/apscheduler/datastores/sqlalchemy.py b/src/apscheduler/datastores/sqlalchemy.py index 86e8138..ee9fd34 100644 --- a/src/apscheduler/datastores/sqlalchemy.py +++ b/src/apscheduler/datastores/sqlalchemy.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict from datetime import datetime, timedelta, timezone from logging import Logger, getLogger -from typing import Any, Iterable, Optional +from typing import Any, Iterable from uuid import UUID import attrs @@ -66,10 +66,10 @@ class EmulatedInterval(TypeDecorator): @attrs.define(kw_only=True, eq=False) class _BaseSQLAlchemyDataStore: - schema: Optional[str] = attrs.field(default=None) + schema: str | None = attrs.field(default=None) serializer: Serializer = attrs.field(factory=PickleSerializer) lock_expiration_delay: float = attrs.field(default=30) - max_poll_time: Optional[float] = attrs.field(default=1) + max_poll_time: float | None = attrs.field(default=1) max_idle_time: float = attrs.field(default=60) retry_settings: RetrySettings = attrs.field(default=RetrySettings()) start_from_scratch: bool = attrs.field(default=False) @@ -342,7 +342,7 @@ class SQLAlchemyDataStore(_BaseSQLAlchemyDataStore, DataStore): for schedule_id in removed_ids: self._events.publish(ScheduleRemoved(schedule_id=schedule_id)) - def get_schedules(self, ids: Optional[set[str]] = None) -> list[Schedule]: + def get_schedules(self, ids: set[str] | None = None) -> list[Schedule]: query = self.t_schedules.select().order_by(self.t_schedules.c.id) if ids: query = query.where(self.t_schedules.c.id.in_(ids)) @@ -441,7 +441,7 @@ class SQLAlchemyDataStore(_BaseSQLAlchemyDataStore, DataStore): for schedule_id in finished_schedule_ids: self._events.publish(ScheduleRemoved(schedule_id=schedule_id)) - def get_next_schedule_run_time(self) -> Optional[datetime]: + def get_next_schedule_run_time(self) -> datetime | None: query = select(self.t_schedules.c.next_fire_time).\ where(self.t_schedules.c.next_fire_time.isnot(None)).\ order_by(self.t_schedules.c.next_fire_time).\ @@ -462,7 +462,7 @@ class SQLAlchemyDataStore(_BaseSQLAlchemyDataStore, DataStore): tags=job.tags) self._events.publish(event) - def get_jobs(self, ids: Optional[Iterable[UUID]] = None) -> list[Job]: + def get_jobs(self, ids: Iterable[UUID] | None = None) -> list[Job]: query = self.t_jobs.select().order_by(self.t_jobs.c.id) if ids: job_ids = [job_id for job_id in ids] @@ -473,7 +473,7 @@ class SQLAlchemyDataStore(_BaseSQLAlchemyDataStore, DataStore): result = conn.execute(query) return self._deserialize_jobs(result) - def acquire_jobs(self, worker_id: str, limit: Optional[int] = None) -> list[Job]: + def acquire_jobs(self, worker_id: str, limit: int | None = None) -> list[Job]: for attempt in self._retrying: with attempt, self.engine.begin() as conn: now = datetime.now(timezone.utc) @@ -563,7 +563,7 @@ class SQLAlchemyDataStore(_BaseSQLAlchemyDataStore, DataStore): JobReleased(job_id=result.job_id, worker_id=worker_id, outcome=result.outcome) ) - def get_job_result(self, job_id: UUID) -> Optional[JobResult]: + def get_job_result(self, job_id: UUID) -> JobResult | None: for attempt in self._retrying: with attempt, self.engine.begin() as conn: # Retrieve the result diff --git a/src/apscheduler/enums.py b/src/apscheduler/enums.py index 5a0929c..dec6669 100644 --- a/src/apscheduler/enums.py +++ b/src/apscheduler/enums.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from enum import Enum, auto diff --git a/src/apscheduler/eventbrokers/async_local.py b/src/apscheduler/eventbrokers/async_local.py index cb0fb96..753505c 100644 --- a/src/apscheduler/eventbrokers/async_local.py +++ b/src/apscheduler/eventbrokers/async_local.py @@ -38,7 +38,7 @@ class LocalAsyncEventBroker(AsyncEventBroker, BaseEventBroker): async def publish_local(self, event: Event) -> None: event_type = type(event) one_shot_tokens: list[object] = [] - for token, subscription in self._subscriptions.items(): + for _token, subscription in self._subscriptions.items(): if subscription.event_types is None or event_type in subscription.event_types: self._task_group.start_soon(self._deliver_event, subscription.callback, event) if subscription.one_shot: diff --git a/src/apscheduler/eventbrokers/base.py b/src/apscheduler/eventbrokers/base.py index 8ec4dcb..c917d2f 100644 --- a/src/apscheduler/eventbrokers/base.py +++ b/src/apscheduler/eventbrokers/base.py @@ -2,7 +2,7 @@ from __future__ import annotations from base64 import b64decode, b64encode from logging import Logger, getLogger -from typing import Any, Callable, Iterable, Optional +from typing import Any, Callable, Iterable import attrs @@ -15,7 +15,7 @@ from ..exceptions import DeserializationError @attrs.define(eq=False, frozen=True) class LocalSubscription(Subscription): callback: Callable[[Event], Any] - event_types: Optional[set[type[Event]]] + event_types: set[type[Event]] | None one_shot: bool token: object _source: BaseEventBroker @@ -33,7 +33,7 @@ class BaseEventBroker(EventBroker): self._logger = getLogger(self.__class__.__module__) def subscribe(self, callback: Callable[[Event], Any], - event_types: Optional[Iterable[type[Event]]] = None, *, + event_types: Iterable[type[Event]] | None = None, *, one_shot: bool = False) -> Subscription: types = set(event_types) if event_types else None token = object() @@ -57,7 +57,7 @@ class DistributedEventBrokerMixin: serialized = self.serializer.serialize(attrs.asdict(event)) return event.__class__.__name__ + ' ' + b64encode(serialized).decode('ascii') - def _reconstitute_event(self, event_type: str, serialized: bytes) -> Optional[Event]: + def _reconstitute_event(self, event_type: str, serialized: bytes) -> Event | None: try: kwargs = self.serializer.deserialize(serialized) except DeserializationError: @@ -78,7 +78,7 @@ class DistributedEventBrokerMixin: self._logger.exception('Error reconstituting event of type %s', event_type) return None - def reconstitute_event(self, payload: bytes) -> Optional[Event]: + def reconstitute_event(self, payload: bytes) -> Event | None: try: event_type_bytes, serialized = payload.split(b' ', 1) except ValueError: @@ -88,7 +88,7 @@ class DistributedEventBrokerMixin: event_type = event_type_bytes.decode('ascii', errors='replace') return self._reconstitute_event(event_type, serialized) - def reconstitute_event_str(self, payload: str) -> Optional[Event]: + def reconstitute_event_str(self, payload: str) -> Event | None: try: event_type, b64_serialized = payload.split(' ', 1) except ValueError: diff --git a/src/apscheduler/eventbrokers/local.py b/src/apscheduler/eventbrokers/local.py index fd345a1..f780aae 100644 --- a/src/apscheduler/eventbrokers/local.py +++ b/src/apscheduler/eventbrokers/local.py @@ -4,7 +4,7 @@ from asyncio import iscoroutinefunction from concurrent.futures import ThreadPoolExecutor from contextlib import ExitStack from threading import Lock -from typing import Any, Callable, Iterable, Optional +from typing import Any, Callable, Iterable import attrs @@ -31,7 +31,7 @@ class LocalEventBroker(BaseEventBroker): del self._executor def subscribe(self, callback: Callable[[Event], Any], - event_types: Optional[Iterable[type[Event]]] = None, *, + event_types: Iterable[type[Event]] | None = None, *, one_shot: bool = False) -> Subscription: if iscoroutinefunction(callback): raise ValueError('Coroutine functions are not supported as callbacks on a synchronous ' @@ -51,7 +51,7 @@ class LocalEventBroker(BaseEventBroker): event_type = type(event) with self._subscriptions_lock: one_shot_tokens: list[object] = [] - for token, subscription in self._subscriptions.items(): + for _token, subscription in self._subscriptions.items(): if subscription.event_types is None or event_type in subscription.event_types: self._executor.submit(self._deliver_event, subscription.callback, event) if subscription.one_shot: diff --git a/src/apscheduler/eventbrokers/mqtt.py b/src/apscheduler/eventbrokers/mqtt.py index 60e7195..04db95d 100644 --- a/src/apscheduler/eventbrokers/mqtt.py +++ b/src/apscheduler/eventbrokers/mqtt.py @@ -1,7 +1,7 @@ from __future__ import annotations from concurrent.futures import Future -from typing import Any, Optional +from typing import Any import attrs from paho.mqtt.client import Client, MQTTMessage @@ -43,7 +43,7 @@ class MQTTEventBroker(LocalEventBroker, DistributedEventBrokerMixin): return self def _on_connect(self, client: Client, userdata: Any, flags: dict[str, Any], - rc: ReasonCodes | int, properties: Optional[Properties] = None) -> None: + rc: ReasonCodes | int, properties: Properties | None = None) -> None: try: client.subscribe(self.topic, qos=self.subscribe_qos) except Exception as exc: diff --git a/src/apscheduler/events.py b/src/apscheduler/events.py index 9eb1c7a..f38c12e 100644 --- a/src/apscheduler/events.py +++ b/src/apscheduler/events.py @@ -2,7 +2,6 @@ from __future__ import annotations from datetime import datetime, timezone from functools import partial -from typing import Optional from uuid import UUID import attrs @@ -45,13 +44,13 @@ class TaskRemoved(DataStoreEvent): @attrs.define(kw_only=True, frozen=True) class ScheduleAdded(DataStoreEvent): schedule_id: str - next_fire_time: Optional[datetime] = attrs.field(converter=optional(as_aware_datetime)) + next_fire_time: datetime | None = attrs.field(converter=optional(as_aware_datetime)) @attrs.define(kw_only=True, frozen=True) class ScheduleUpdated(DataStoreEvent): schedule_id: str - next_fire_time: Optional[datetime] = attrs.field(converter=optional(as_aware_datetime)) + next_fire_time: datetime | None = attrs.field(converter=optional(as_aware_datetime)) @attrs.define(kw_only=True, frozen=True) @@ -63,7 +62,7 @@ class ScheduleRemoved(DataStoreEvent): class JobAdded(DataStoreEvent): job_id: UUID = attrs.field(converter=as_uuid) task_id: str - schedule_id: Optional[str] + schedule_id: str | None tags: frozenset[str] = attrs.field(converter=frozenset) @@ -100,7 +99,7 @@ class SchedulerStarted(SchedulerEvent): @attrs.define(kw_only=True, frozen=True) class SchedulerStopped(SchedulerEvent): - exception: Optional[BaseException] = None + exception: BaseException | None = None # @@ -119,7 +118,7 @@ class WorkerStarted(WorkerEvent): @attrs.define(kw_only=True, frozen=True) class WorkerStopped(WorkerEvent): - exception: Optional[BaseException] = None + exception: BaseException | None = None @attrs.define(kw_only=True, frozen=True) diff --git a/src/apscheduler/exceptions.py b/src/apscheduler/exceptions.py index b84e58a..64fbf6a 100644 --- a/src/apscheduler/exceptions.py +++ b/src/apscheduler/exceptions.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from uuid import UUID diff --git a/src/apscheduler/schedulers/async_.py b/src/apscheduler/schedulers/async_.py index 61e2e64..6d3287f 100644 --- a/src/apscheduler/schedulers/async_.py +++ b/src/apscheduler/schedulers/async_.py @@ -6,7 +6,7 @@ import random from contextlib import AsyncExitStack from datetime import datetime, timedelta, timezone from logging import Logger, getLogger -from typing import Any, Callable, Iterable, Mapping, Optional +from typing import Any, Callable, Iterable, Mapping from uuid import UUID, uuid4 import anyio @@ -37,11 +37,11 @@ class AsyncScheduler: data_store: AsyncDataStore = attrs.field(converter=as_async_datastore, factory=MemoryDataStore) identity: str = attrs.field(kw_only=True, default=None) start_worker: bool = attrs.field(kw_only=True, default=True) - logger: Optional[Logger] = attrs.field(kw_only=True, default=getLogger(__name__)) + logger: Logger | None = attrs.field(kw_only=True, default=getLogger(__name__)) _state: RunState = attrs.field(init=False, default=RunState.stopped) _wakeup_event: anyio.Event = attrs.field(init=False) - _worker: Optional[AsyncWorker] = attrs.field(init=False, default=None) + _worker: AsyncWorker | None = attrs.field(init=False, default=None) _events: LocalAsyncEventBroker = attrs.field(init=False, factory=LocalAsyncEventBroker) _exit_stack: AsyncExitStack = attrs.field(init=False) @@ -54,7 +54,7 @@ class AsyncScheduler: return self._events @property - def worker(self) -> Optional[AsyncWorker]: + def worker(self) -> AsyncWorker | None: return self._worker async def __aenter__(self): @@ -102,11 +102,11 @@ class AsyncScheduler: self._wakeup_event.set() async def add_schedule( - self, func_or_task_id: str | Callable, trigger: Trigger, *, id: Optional[str] = None, - args: Optional[Iterable] = None, kwargs: Optional[Mapping[str, Any]] = None, + self, func_or_task_id: str | Callable, trigger: Trigger, *, id: str | None = None, + args: Iterable | None = None, kwargs: Mapping[str, Any] | None = None, coalesce: CoalescePolicy = CoalescePolicy.latest, misfire_grace_time: float | timedelta | None = None, - max_jitter: float | timedelta | None = None, tags: Optional[Iterable[str]] = None, + max_jitter: float | timedelta | None = None, tags: Iterable[str] | None = None, conflict_policy: ConflictPolicy = ConflictPolicy.do_nothing ) -> str: id = id or str(uuid4()) @@ -139,8 +139,8 @@ class AsyncScheduler: await self.data_store.remove_schedules({schedule_id}) async def add_job( - self, func_or_task_id: str | Callable, *, args: Optional[Iterable] = None, - kwargs: Optional[Mapping[str, Any]] = None, tags: Optional[Iterable[str]] = None + self, func_or_task_id: str | Callable, *, args: Iterable | None = None, + kwargs: Mapping[str, Any] | None = None, tags: Iterable[str] | None = None ) -> UUID: """ Add a job to the data store. @@ -192,8 +192,8 @@ class AsyncScheduler: return result async def run_job( - self, func_or_task_id: str | Callable, *, args: Optional[Iterable] = None, - kwargs: Optional[Mapping[str, Any]] = None, tags: Optional[Iterable[str]] = () + self, func_or_task_id: str | Callable, *, args: Iterable | None = None, + kwargs: Mapping[str, Any] | None = None, tags: Iterable[str] | None = () ) -> Any: """ Convenience method to add a job and then return its result (or raise its exception). @@ -207,7 +207,7 @@ class AsyncScheduler: if event.job_id == job_id: job_complete_event.set() - job_id: Optional[UUID] = None + job_id: UUID | None = None with self.data_store.events.subscribe(listener, {JobReleased}): job_id = await self.add_job(func_or_task_id, args=args, kwargs=kwargs, tags=tags) await job_complete_event.wait() @@ -234,7 +234,7 @@ class AsyncScheduler: task_status.started() await self._events.publish(SchedulerStarted()) - exception: Optional[BaseException] = None + exception: BaseException | None = None try: while self._state is RunState.started: schedules = await self.data_store.acquire_schedules(self.identity, 100) diff --git a/src/apscheduler/schedulers/sync.py b/src/apscheduler/schedulers/sync.py index 22270f0..7ead9f8 100644 --- a/src/apscheduler/schedulers/sync.py +++ b/src/apscheduler/schedulers/sync.py @@ -8,7 +8,7 @@ from concurrent.futures import FIRST_COMPLETED, Future, ThreadPoolExecutor, wait from contextlib import ExitStack from datetime import datetime, timedelta, timezone from logging import Logger, getLogger -from typing import Any, Callable, Iterable, Mapping, Optional +from typing import Any, Callable, Iterable, Mapping from uuid import UUID, uuid4 import attrs @@ -36,11 +36,11 @@ class Scheduler: data_store: DataStore = attrs.field(factory=MemoryDataStore) identity: str = attrs.field(kw_only=True, default=None) start_worker: bool = attrs.field(kw_only=True, default=True) - logger: Optional[Logger] = attrs.field(kw_only=True, default=getLogger(__name__)) + logger: Logger | None = attrs.field(kw_only=True, default=getLogger(__name__)) _state: RunState = attrs.field(init=False, default=RunState.stopped) _wakeup_event: threading.Event = attrs.field(init=False) - _worker: Optional[Worker] = attrs.field(init=False, default=None) + _worker: Worker | None = attrs.field(init=False, default=None) _events: LocalEventBroker = attrs.field(init=False, factory=LocalEventBroker) _exit_stack: ExitStack = attrs.field(init=False) @@ -57,7 +57,7 @@ class Scheduler: return self._state @property - def worker(self) -> Optional[Worker]: + def worker(self) -> Worker | None: return self._worker def __enter__(self) -> Scheduler: @@ -112,11 +112,11 @@ class Scheduler: self._wakeup_event.set() def add_schedule( - self, func_or_task_id: str | Callable, trigger: Trigger, *, id: Optional[str] = None, - args: Optional[Iterable] = None, kwargs: Optional[Mapping[str, Any]] = None, + self, func_or_task_id: str | Callable, trigger: Trigger, *, id: str | None = None, + args: Iterable | None = None, kwargs: Mapping[str, Any] | None = None, coalesce: CoalescePolicy = CoalescePolicy.latest, misfire_grace_time: float | timedelta | None = None, - max_jitter: float | timedelta | None = None, tags: Optional[Iterable[str]] = None, + max_jitter: float | timedelta | None = None, tags: Iterable[str] | None = None, conflict_policy: ConflictPolicy = ConflictPolicy.do_nothing ) -> str: id = id or str(uuid4()) @@ -149,8 +149,8 @@ class Scheduler: self.data_store.remove_schedules({schedule_id}) def add_job( - self, func_or_task_id: str | Callable, *, args: Optional[Iterable] = None, - kwargs: Optional[Mapping[str, Any]] = None, tags: Optional[Iterable[str]] = None + self, func_or_task_id: str | Callable, *, args: Iterable | None = None, + kwargs: Mapping[str, Any] | None = None, tags: Iterable[str] | None = None ) -> UUID: """ Add a job to the data store. @@ -202,8 +202,8 @@ class Scheduler: return result def run_job( - self, func_or_task_id: str | Callable, *, args: Optional[Iterable] = None, - kwargs: Optional[Mapping[str, Any]] = None, tags: Optional[Iterable[str]] = () + self, func_or_task_id: str | Callable, *, args: Iterable | None = None, + kwargs: Mapping[str, Any] | None = None, tags: Iterable[str] | None = () ) -> Any: """ Convenience method to add a job and then return its result (or raise its exception). @@ -217,7 +217,7 @@ class Scheduler: if event.job_id == job_id: job_complete_event.set() - job_id: Optional[UUID] = None + job_id: UUID | None = None with self.data_store.events.subscribe(listener, {JobReleased}): job_id = self.add_job(func_or_task_id, args=args, kwargs=kwargs, tags=tags) job_complete_event.wait() diff --git a/src/apscheduler/serializers/pickle.py b/src/apscheduler/serializers/pickle.py index d03bdc0..5edf2d0 100644 --- a/src/apscheduler/serializers/pickle.py +++ b/src/apscheduler/serializers/pickle.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pickle import dumps, loads import attrs diff --git a/src/apscheduler/structures.py b/src/apscheduler/structures.py index 7f6457d..dd8204c 100644 --- a/src/apscheduler/structures.py +++ b/src/apscheduler/structures.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import datetime, timedelta, timezone from functools import partial -from typing import Any, Callable, Optional +from typing import Any, Callable from uuid import UUID, uuid4 import attrs @@ -27,8 +27,8 @@ def serialize(inst, field, value): class Task: id: str func: Callable = attrs.field(eq=False, order=False) - max_running_jobs: Optional[int] = attrs.field(eq=False, order=False, default=None) - misfire_grace_time: Optional[timedelta] = attrs.field(eq=False, order=False, default=None) + max_running_jobs: int | None = attrs.field(eq=False, order=False, default=None) + misfire_grace_time: timedelta | None = attrs.field(eq=False, order=False, default=None) state: Any = None def marshal(self, serializer: abc.Serializer) -> dict[str, Any]: @@ -55,15 +55,15 @@ class Schedule: kwargs: dict[str, Any] = attrs.field(eq=False, order=False, converter=dict, default=()) coalesce: CoalescePolicy = attrs.field(eq=False, order=False, default=CoalescePolicy.latest, converter=as_enum(CoalescePolicy)) - misfire_grace_time: Optional[timedelta] = attrs.field(eq=False, order=False, default=None, - converter=as_timedelta) - max_jitter: Optional[timedelta] = attrs.field(eq=False, order=False, converter=as_timedelta, - default=None) + misfire_grace_time: timedelta | None = attrs.field(eq=False, order=False, default=None, + converter=as_timedelta) + max_jitter: timedelta | None = attrs.field(eq=False, order=False, converter=as_timedelta, + default=None) tags: frozenset[str] = attrs.field(eq=False, order=False, converter=frozenset, default=()) - next_fire_time: Optional[datetime] = attrs.field(eq=False, order=False, default=None) - last_fire_time: Optional[datetime] = attrs.field(eq=False, order=False, default=None) - acquired_by: Optional[str] = attrs.field(eq=False, order=False, default=None) - acquired_until: Optional[datetime] = attrs.field(eq=False, order=False, default=None) + next_fire_time: datetime | None = attrs.field(eq=False, order=False, default=None) + last_fire_time: datetime | None = attrs.field(eq=False, order=False, default=None) + acquired_by: str | None = attrs.field(eq=False, order=False, default=None) + acquired_until: datetime | None = attrs.field(eq=False, order=False, default=None) def marshal(self, serializer: abc.Serializer) -> dict[str, Any]: marshalled = attrs.asdict(self, value_serializer=serialize) @@ -84,7 +84,7 @@ class Schedule: return cls(**marshalled) @property - def next_deadline(self) -> Optional[datetime]: + def next_deadline(self) -> datetime | None: if self.next_fire_time and self.misfire_grace_time: return self.next_fire_time + self.misfire_grace_time @@ -97,20 +97,20 @@ class Job: task_id: str = attrs.field(eq=False, order=False) args: tuple = attrs.field(eq=False, order=False, converter=tuple, default=()) kwargs: dict[str, Any] = attrs.field(eq=False, order=False, converter=dict, default=()) - schedule_id: Optional[str] = attrs.field(eq=False, order=False, default=None) - scheduled_fire_time: Optional[datetime] = attrs.field(eq=False, order=False, default=None) + schedule_id: str | None = attrs.field(eq=False, order=False, default=None) + scheduled_fire_time: datetime | None = attrs.field(eq=False, order=False, default=None) jitter: timedelta = attrs.field(eq=False, order=False, converter=as_timedelta, factory=timedelta) - start_deadline: Optional[datetime] = attrs.field(eq=False, order=False, default=None) + start_deadline: datetime | None = attrs.field(eq=False, order=False, default=None) tags: frozenset[str] = attrs.field(eq=False, order=False, converter=frozenset, default=()) created_at: datetime = attrs.field(eq=False, order=False, factory=partial(datetime.now, timezone.utc)) - started_at: Optional[datetime] = attrs.field(eq=False, order=False, default=None) - acquired_by: Optional[str] = attrs.field(eq=False, order=False, default=None) - acquired_until: Optional[datetime] = attrs.field(eq=False, order=False, default=None) + started_at: datetime | None = attrs.field(eq=False, order=False, default=None) + acquired_by: str | None = attrs.field(eq=False, order=False, default=None) + acquired_until: datetime | None = attrs.field(eq=False, order=False, default=None) @property - def original_scheduled_time(self) -> Optional[datetime]: + def original_scheduled_time(self) -> datetime | None: """The scheduled time without any jitter included.""" if self.scheduled_fire_time is None: return None @@ -138,10 +138,10 @@ class Job: class JobInfo: job_id: UUID task_id: str - schedule_id: Optional[str] - scheduled_fire_time: Optional[datetime] + schedule_id: str | None + scheduled_fire_time: datetime | None jitter: timedelta - start_deadline: Optional[datetime] + start_deadline: datetime | None tags: frozenset[str] @classmethod @@ -157,7 +157,7 @@ class JobResult: outcome: JobOutcome = attrs.field(eq=False, order=False, converter=as_enum(JobOutcome)) finished_at: datetime = attrs.field(eq=False, order=False, factory=partial(datetime.now, timezone.utc)) - exception: Optional[BaseException] = attrs.field(eq=False, order=False, default=None) + exception: BaseException | None = attrs.field(eq=False, order=False, default=None) return_value: Any = attrs.field(eq=False, order=False, default=None) def marshal(self, serializer: abc.Serializer) -> dict[str, Any]: diff --git a/src/apscheduler/triggers/calendarinterval.py b/src/apscheduler/triggers/calendarinterval.py index 8d0990d..fbb5896 100644 --- a/src/apscheduler/triggers/calendarinterval.py +++ b/src/apscheduler/triggers/calendarinterval.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import date, datetime, time, timedelta, tzinfo -from typing import Any, Optional +from typing import Any import attrs @@ -67,7 +67,7 @@ class CalendarIntervalTrigger(Trigger): end_date: date | None = attrs.field(converter=as_date, default=None) timezone: tzinfo = attrs.field(converter=as_timezone, default='local') _time: time = attrs.field(init=False, eq=False) - _last_fire_date: Optional[date] = attrs.field(init=False, eq=False, default=None) + _last_fire_date: date | None = attrs.field(init=False, eq=False, default=None) def __attrs_post_init__(self) -> None: self._time = time(self.hour, self.minute, self.second, tzinfo=self.timezone) @@ -78,7 +78,7 @@ class CalendarIntervalTrigger(Trigger): if self.start_date and self.end_date and self.start_date > self.end_date: raise ValueError('end_date cannot be earlier than start_date') - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: previous_date: date = self._last_fire_date while True: if previous_date: diff --git a/src/apscheduler/triggers/combining.py b/src/apscheduler/triggers/combining.py index fa0b5c9..bcee64a 100644 --- a/src/apscheduler/triggers/combining.py +++ b/src/apscheduler/triggers/combining.py @@ -2,7 +2,7 @@ from __future__ import annotations from abc import abstractmethod from datetime import datetime, timedelta -from typing import Any, Optional +from typing import Any import attrs @@ -15,7 +15,7 @@ from ..validators import as_timedelta, require_state_version @attrs.define class BaseCombiningTrigger(Trigger): triggers: list[Trigger] - _next_fire_times: list[Optional[datetime]] = attrs.field(init=False, eq=False, factory=list) + _next_fire_times: list[datetime | None] = attrs.field(init=False, eq=False, factory=list) def __getstate__(self) -> dict[str, Any]: return { @@ -51,17 +51,17 @@ class AndTrigger(BaseCombiningTrigger): """ threshold: timedelta = attrs.field(converter=as_timedelta, default=1) - max_iterations: Optional[int] = 10000 + max_iterations: int | None = 10000 - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: if not self._next_fire_times: # Fill out the fire times on the first run self._next_fire_times = [t.next() for t in self.triggers] for _ in range(self.max_iterations): # Find the earliest and latest fire times - earliest_fire_time: Optional[datetime] = None - latest_fire_time: Optional[datetime] = None + earliest_fire_time: datetime | None = None + latest_fire_time: datetime | None = None for fire_time in self._next_fire_times: # If any of the fire times is None, this trigger is finished if fire_time is None: @@ -74,7 +74,7 @@ class AndTrigger(BaseCombiningTrigger): latest_fire_time = fire_time # Replace all the fire times that were within the threshold - for i, trigger in enumerate(self.triggers): + for i, _trigger in enumerate(self.triggers): if self._next_fire_times[i] - earliest_fire_time <= self.threshold: self._next_fire_times[i] = self.triggers[i].next() @@ -114,14 +114,14 @@ class OrTrigger(BaseCombiningTrigger): :param triggers: triggers to combine """ - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: # Fill out the fire times on the first run if not self._next_fire_times: self._next_fire_times = [t.next() for t in self.triggers] # Find out the earliest of the fire times - earliest_time: Optional[datetime] = min([fire_time for fire_time in self._next_fire_times - if fire_time is not None], default=None) + earliest_time: datetime | None = min((fire_time for fire_time in self._next_fire_times + if fire_time is not None), default=None) if earliest_time is not None: # Generate new fire times for the trigger(s) that generated the earliest fire time for i, fire_time in enumerate(self._next_fire_times): diff --git a/src/apscheduler/triggers/cron/__init__.py b/src/apscheduler/triggers/cron/__init__.py index 1c1e582..df1f9e5 100644 --- a/src/apscheduler/triggers/cron/__init__.py +++ b/src/apscheduler/triggers/cron/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime, timedelta, tzinfo -from typing import Any, ClassVar, Optional, Sequence +from typing import Any, ClassVar, Sequence import attrs from tzlocal import get_localzone @@ -59,12 +59,12 @@ class CronTrigger(Trigger): end_time: datetime | None = None timezone: tzinfo | str = attrs.field(converter=as_timezone, factory=get_localzone) _fields: list[BaseField] = attrs.field(init=False, eq=False, factory=list) - _last_fire_time: Optional[datetime] = attrs.field(init=False, eq=False, default=None) + _last_fire_time: datetime | None = attrs.field(init=False, eq=False, default=None) def __attrs_post_init__(self) -> None: self._set_fields([self.year, self.month, self.day, self.week, self.day_of_week, self.hour, self.minute, self.second]) - self._last_fire_time: Optional[datetime] = None + self._last_fire_time: datetime | None = None def _set_fields(self, values: Sequence[int | str | None]) -> None: self._fields = [] @@ -80,7 +80,7 @@ class CronTrigger(Trigger): self._fields.append(field) @classmethod - def from_crontab(cls, expr: str, timezone: str | tzinfo = 'local') -> 'CronTrigger': + def from_crontab(cls, expr: str, timezone: str | tzinfo = 'local') -> CronTrigger: """ Create a :class:`~CronTrigger` from a standard crontab expression. @@ -153,7 +153,7 @@ class CronTrigger(Trigger): return datetime(**values, tzinfo=self.timezone) - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: if self._last_fire_time: start_time = self._last_fire_time + timedelta(microseconds=1) else: diff --git a/src/apscheduler/triggers/cron/expressions.py b/src/apscheduler/triggers/cron/expressions.py index e417dea..5c94afa 100644 --- a/src/apscheduler/triggers/cron/expressions.py +++ b/src/apscheduler/triggers/cron/expressions.py @@ -4,7 +4,6 @@ from __future__ import annotations import re from calendar import monthrange from datetime import datetime -from typing import Optional from ...validators import as_int @@ -35,7 +34,7 @@ class AllExpression: raise ValueError(f'the step value ({self.step}) is higher than the total range of the ' f'expression ({value_range})') - def get_next_value(self, dateval: datetime, field) -> Optional[int]: + def get_next_value(self, dateval: datetime, field) -> int | None: start = field.get_value(dateval) minval = field.get_min(dateval) maxval = field.get_max(dateval) @@ -144,7 +143,7 @@ class WeekdayRangeExpression(RangeExpression): value_re = re.compile(r'(?P<first>[a-z]+)(?:-(?P<last>[a-z]+))?', re.IGNORECASE) - def __init__(self, first: str, last: Optional[str] = None): + def __init__(self, first: str, last: str | None = None): first_num = get_weekday_index(first) last_num = get_weekday_index(last) if last else None super().__init__(first_num, last_num) @@ -171,7 +170,7 @@ class WeekdayPositionExpression(AllExpression): except ValueError: raise ValueError(f'Invalid weekday name {weekday_name!r}') from None - def get_next_value(self, dateval: datetime, field) -> Optional[int]: + def get_next_value(self, dateval: datetime, field) -> int | None: # Figure out the weekday of the month's first day and the number of days in that month first_day_wday, last_day = monthrange(dateval.year, dateval.month) diff --git a/src/apscheduler/triggers/cron/fields.py b/src/apscheduler/triggers/cron/fields.py index d15fcd5..e68fdca 100644 --- a/src/apscheduler/triggers/cron/fields.py +++ b/src/apscheduler/triggers/cron/fields.py @@ -4,7 +4,7 @@ from __future__ import annotations import re from calendar import monthrange from datetime import datetime -from typing import Any, ClassVar, List, Optional, Sequence +from typing import Any, ClassVar, Sequence from .expressions import ( WEEKDAYS, AllExpression, LastDayOfMonthExpression, MonthRangeExpression, RangeExpression, @@ -32,7 +32,7 @@ class BaseField: def __init__(self, name: str, exprs: int | str): self.name = name - self.expressions: List = [] + self.expressions: list = [] for expr in SEPARATOR.split(str(exprs).strip()): self.append_expression(expr) @@ -45,7 +45,7 @@ class BaseField: def get_value(self, dateval: datetime) -> int: return getattr(dateval, self.name) - def get_next_value(self, dateval: datetime) -> Optional[int]: + def get_next_value(self, dateval: datetime) -> int | None: smallest = None for expr in self.expressions: value = expr.get_next_value(dateval, self) diff --git a/src/apscheduler/triggers/date.py b/src/apscheduler/triggers/date.py index 08494c3..173c972 100644 --- a/src/apscheduler/triggers/date.py +++ b/src/apscheduler/triggers/date.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import Any, Optional +from typing import Any import attrs @@ -21,7 +21,7 @@ class DateTrigger(Trigger): run_time: datetime = attrs.field(converter=as_aware_datetime) _completed: bool = attrs.field(init=False, eq=False, default=False) - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: if not self._completed: self._completed = True return self.run_time diff --git a/src/apscheduler/triggers/interval.py b/src/apscheduler/triggers/interval.py index 0f85d36..629499b 100644 --- a/src/apscheduler/triggers/interval.py +++ b/src/apscheduler/triggers/interval.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime, timedelta -from typing import Any, Optional +from typing import Any import attrs @@ -37,9 +37,9 @@ class IntervalTrigger(Trigger): seconds: float = 0 microseconds: float = 0 start_time: datetime = attrs.field(converter=as_aware_datetime, factory=datetime.now) - end_time: Optional[datetime] = attrs.field(converter=as_aware_datetime, default=None) + end_time: datetime | None = attrs.field(converter=as_aware_datetime, default=None) _interval: timedelta = attrs.field(init=False, eq=False, repr=False) - _last_fire_time: Optional[datetime] = attrs.field(init=False, eq=False, default=None) + _last_fire_time: datetime | None = attrs.field(init=False, eq=False, default=None) def __attrs_post_init__(self) -> None: self._interval = timedelta(weeks=self.weeks, days=self.days, hours=self.hours, @@ -52,7 +52,7 @@ class IntervalTrigger(Trigger): if self.end_time and self.end_time < self.start_time: raise ValueError('end_time cannot be earlier than start_time') - def next(self) -> Optional[datetime]: + def next(self) -> datetime | None: if self._last_fire_time is None: self._last_fire_time = self.start_time else: diff --git a/src/apscheduler/validators.py b/src/apscheduler/validators.py index 179eabd..baa7b50 100644 --- a/src/apscheduler/validators.py +++ b/src/apscheduler/validators.py @@ -2,7 +2,7 @@ from __future__ import annotations import sys from datetime import date, datetime, timedelta, timezone, tzinfo -from typing import Any, Optional +from typing import Any import attrs from attrs import Attribute @@ -17,7 +17,7 @@ else: from backports.zoneinfo import ZoneInfo -def as_int(value) -> Optional[int]: +def as_int(value) -> int | None: """Convert the value into an integer.""" if value is None: return None @@ -49,7 +49,7 @@ def as_timezone(value: str | tzinfo | None) -> tzinfo: f'{value.__class__.__qualname__} instead') -def as_date(value: date | str | None) -> Optional[date]: +def as_date(value: date | str | None) -> date | None: """ Convert the value to a date. @@ -67,21 +67,21 @@ def as_date(value: date | str | None) -> Optional[date]: raise TypeError(f'Expected string or date, got {value.__class__.__qualname__} instead') -def as_timestamp(value: Optional[datetime]) -> Optional[float]: +def as_timestamp(value: datetime | None) -> float | None: if value is None: return None return value.timestamp() -def as_ordinal_date(value: Optional[date]) -> Optional[int]: +def as_ordinal_date(value: date | None) -> int | None: if value is None: return None return value.toordinal() -def as_aware_datetime(value: datetime | str | None) -> Optional[datetime]: +def as_aware_datetime(value: datetime | str | None) -> datetime | None: """ Convert the value to a timezone aware datetime. diff --git a/src/apscheduler/workers/async_.py b/src/apscheduler/workers/async_.py index 50f76a9..c581b0e 100644 --- a/src/apscheduler/workers/async_.py +++ b/src/apscheduler/workers/async_.py @@ -6,7 +6,7 @@ from contextlib import AsyncExitStack from datetime import datetime, timezone from inspect import isawaitable from logging import Logger, getLogger -from typing import Callable, Optional +from typing import Callable from uuid import UUID import anyio @@ -30,7 +30,7 @@ class AsyncWorker: data_store: AsyncDataStore = attrs.field(converter=as_async_datastore) max_concurrent_jobs: int = attrs.field(kw_only=True, validator=positive_integer, default=100) identity: str = attrs.field(kw_only=True, default=None) - logger: Optional[Logger] = attrs.field(kw_only=True, default=getLogger(__name__)) + logger: Logger | None = attrs.field(kw_only=True, default=getLogger(__name__)) _state: RunState = attrs.field(init=False, default=RunState.stopped) _wakeup_event: anyio.Event = attrs.field(init=False, factory=anyio.Event) @@ -92,7 +92,7 @@ class AsyncWorker: task_status.started() await self._events.publish(WorkerStarted()) - exception: Optional[BaseException] = None + exception: BaseException | None = None try: async with create_task_group() as tg: while self._state is RunState.started: diff --git a/src/apscheduler/workers/sync.py b/src/apscheduler/workers/sync.py index 69718b5..7a6bee9 100644 --- a/src/apscheduler/workers/sync.py +++ b/src/apscheduler/workers/sync.py @@ -8,7 +8,7 @@ from contextlib import ExitStack from contextvars import copy_context from datetime import datetime, timezone from logging import Logger, getLogger -from typing import Callable, Optional +from typing import Callable from uuid import UUID import attrs @@ -28,7 +28,7 @@ class Worker: data_store: DataStore max_concurrent_jobs: int = attrs.field(kw_only=True, validator=positive_integer, default=20) identity: str = attrs.field(kw_only=True, default=None) - logger: Optional[Logger] = attrs.field(kw_only=True, default=getLogger(__name__)) + logger: Logger | None = attrs.field(kw_only=True, default=getLogger(__name__)) _state: RunState = attrs.field(init=False, default=RunState.stopped) _wakeup_event: threading.Event = attrs.field(init=False) @@ -98,7 +98,7 @@ class Worker: self._events.publish(WorkerStarted()) executor = ThreadPoolExecutor(max_workers=self.max_concurrent_jobs) - exception: Optional[BaseException] = None + exception: BaseException | None = None try: while self._state is RunState.started: available_slots = self.max_concurrent_jobs - len(self._running_jobs) |