From 19bd8f02abd0a3987b382ca4282e742e876845c2 Mon Sep 17 00:00:00 2001 From: Daniel Shields Date: Thu, 4 Apr 2024 11:42:59 -0500 Subject: [PATCH] use python3.10 conveniences --- .github/workflows/coverage.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/tests.yml | 1 - .readthedocs.yml | 2 +- pyproject.toml | 2 +- src/uberjob/_execution/run_physical.py | 21 ++++++++------- src/uberjob/_plan.py | 3 +-- src/uberjob/_registry.py | 2 +- src/uberjob/_rendering.py | 8 +++--- src/uberjob/_run.py | 27 +++++++++---------- .../_testing/test_mounted_file_store.py | 2 +- src/uberjob/_testing/test_store.py | 3 +-- src/uberjob/_transformations/caching.py | 17 ++++++------ src/uberjob/_transformations/pruning.py | 7 +++-- src/uberjob/_value_store.py | 3 +-- src/uberjob/graph.py | 2 +- src/uberjob/progress/__init__.py | 6 ++--- .../progress/_html_progress_observer.py | 4 +-- src/uberjob/progress/_progress.py | 2 +- src/uberjob/stores/_file_store.py | 14 +++++----- src/uberjob/stores/_json_file_store.py | 8 +----- src/uberjob/stores/_literal_source.py | 5 ++-- src/uberjob/stores/_modified_time_source.py | 7 +++-- src/uberjob/stores/_path_source.py | 7 +++-- src/uberjob/stores/_text_file_store.py | 8 +----- 25 files changed, 70 insertions(+), 95 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7fb47a7..9540750 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: "3.9" + python-version: "3.10" architecture: x64 - name: Setup Graphviz uses: ts-graphviz/setup-graphviz@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index deffb4a..0ab5c39 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 with: - python-version: "3.9" + python-version: "3.10" architecture: x64 - name: Setup Graphviz uses: ts-graphviz/setup-graphviz@v1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90fc91c..7a357da 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,6 @@ jobs: - ubuntu-latest - windows-latest python: - - "3.9" - "3.10" - "3.11" steps: diff --git a/.readthedocs.yml b/.readthedocs.yml index 44855d1..e51f213 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,7 +3,7 @@ sphinx: configuration: docs/conf.py formats: all python: - version: 3.8 + version: 3.10 install: - requirements: docs/requirements.txt - path: . \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9a776c7..e721433 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ version = "1.0.1" [tool.poetry.dependencies] networkx = "^2.5" nxv = "^0.1.3" -python = "^3.8" +python = "^3.10" [tool.poetry.dev-dependencies] black = "^22.8" diff --git a/src/uberjob/_execution/run_physical.py b/src/uberjob/_execution/run_physical.py index fcb445e..3294665 100644 --- a/src/uberjob/_execution/run_physical.py +++ b/src/uberjob/_execution/run_physical.py @@ -14,7 +14,8 @@ # limitations under the License. # """Functionality for executing a physical plan""" -from typing import Any, Callable, NamedTuple, Optional +from collections.abc import Callable +from typing import Any, NamedTuple from uberjob._errors import NodeError, create_chained_call_error from uberjob._execution.run_function_on_graph import run_function_on_graph @@ -55,7 +56,7 @@ def _create_bound_call( def _create_bound_call_lookup_and_output_slot( - plan: Plan, output_node: Optional[Node] = None + plan: Plan, output_node: Node | None = None ): result_lookup = { node: node if type(node) is Literal else Slot(None) @@ -81,9 +82,9 @@ def prep_run_physical( plan: Plan, *, inplace: bool, - output_node: Optional[Node] = None, - retry: Optional[Callable[[Callable], Callable]] = None, - progress_observer: Optional[ProgressObserver] = None, + output_node: Node | None = None, + retry: Callable[[Callable], Callable] | None = None, + progress_observer: ProgressObserver | None = None, ): bound_call_lookup, output_slot = _create_bound_call_lookup_and_output_slot( plan, output_node @@ -119,11 +120,11 @@ def run_physical( plan: Plan, *, inplace: bool, - output_node: Optional[Node] = None, - retry: Optional[Callable[[Callable], Callable]] = None, - max_workers: Optional[int] = None, - max_errors: Optional[int] = 0, - scheduler: Optional[str] = None, + output_node: Node | None = None, + retry: Callable[[Callable], Callable] | None = None, + max_workers: int | None = None, + max_errors: int | None = 0, + scheduler: str | None = None, progress_observer: ProgressObserver, ) -> Any: _, output_slot, process, plan = prep_run_physical( diff --git a/src/uberjob/_plan.py b/src/uberjob/_plan.py index 3656979..9280ea5 100644 --- a/src/uberjob/_plan.py +++ b/src/uberjob/_plan.py @@ -14,10 +14,9 @@ # limitations under the License. # import operator -from collections.abc import Generator +from collections.abc import Callable, Generator from contextlib import contextmanager from threading import RLock -from typing import Callable from uberjob import _builtins from uberjob._util import validation diff --git a/src/uberjob/_registry.py b/src/uberjob/_registry.py index 43318a6..c024443 100644 --- a/src/uberjob/_registry.py +++ b/src/uberjob/_registry.py @@ -89,7 +89,7 @@ def __getitem__(self, node: Node) -> ValueStore: """ return self.mapping[node].value_store - def get(self, node: Node) -> typing.Optional[ValueStore]: + def get(self, node: Node) -> ValueStore | None: """ Get the :class:`~uberjob.ValueStore` for a :class:`~uberjob.graph.Node` if it has one, or ``None``. diff --git a/src/uberjob/_rendering.py b/src/uberjob/_rendering.py index a488a04..d7a9a5d 100644 --- a/src/uberjob/_rendering.py +++ b/src/uberjob/_rendering.py @@ -130,13 +130,13 @@ class Scope: def render( - plan: typing.Union[Plan, Graph, tuple[Plan, typing.Optional[Node]]], + plan: Plan | Graph | tuple[Plan, Node | None], *, registry: Registry = None, predicate: typing.Callable[[Node, dict], bool] = None, - level: typing.Optional[int] = None, - format: typing.Optional[str] = None -) -> typing.Optional[bytes]: + level: int | None = None, + format: str | None = None +) -> bytes | None: """ Use :mod:`nxv` to render a plan's symbolic call graph. diff --git a/src/uberjob/_run.py b/src/uberjob/_run.py index 8b6d4f0..3b6418d 100644 --- a/src/uberjob/_run.py +++ b/src/uberjob/_run.py @@ -15,8 +15,7 @@ # import collections import datetime as dt -from collections.abc import Iterable -from typing import Callable, Optional, Union +from collections.abc import Callable, Iterable from uberjob._errors import CallError from uberjob._execution.run_function_on_graph import NodeError @@ -47,9 +46,7 @@ def _update_run_totals(plan: Plan, progress_observer: ProgressObserver) -> None: progress_observer.increment_total(section="run", scope=scope, amount=count) -def _coerce_progress( - progress: Union[None, bool, Progress, Iterable[Progress]] -) -> Progress: +def _coerce_progress(progress: None | bool | Progress | Iterable[Progress]) -> Progress: if not progress: return null_progress try: @@ -66,7 +63,7 @@ def _coerce_progress( def _coerce_retry( - retry: Union[None, int, Callable[[Callable], Callable]] + retry: None | int | Callable[[Callable], Callable] ) -> Callable[[Callable], Callable]: if callable(retry): return retry @@ -77,16 +74,16 @@ def run( plan: Plan, *, output=None, - registry: Optional[Registry] = None, + registry: Registry | None = None, dry_run: bool = False, - max_workers: Optional[int] = None, - max_errors: Optional[int] = 0, - retry: Union[None, int, Callable[[Callable], Callable]] = None, - fresh_time: Optional[dt.datetime] = None, - progress: Union[None, bool, Progress, Iterable[Progress]] = True, - scheduler: Optional[str] = None, - transform_physical: Optional[Callable[[Plan, Node], tuple[Plan, Node]]] = None, - stale_check_max_workers: Optional[int] = None, + max_workers: int | None = None, + max_errors: int | None = 0, + retry: None | int | Callable[[Callable], Callable] = None, + fresh_time: dt.datetime | None = None, + progress: None | bool | Progress | Iterable[Progress] = True, + scheduler: str | None = None, + transform_physical: Callable[[Plan, Node], tuple[Plan, Node]] | None = None, + stale_check_max_workers: int | None = None, ): """ Run a :class:`~uberjob.Plan`. diff --git a/src/uberjob/_testing/test_mounted_file_store.py b/src/uberjob/_testing/test_mounted_file_store.py index fa88a4f..36f3cbf 100644 --- a/src/uberjob/_testing/test_mounted_file_store.py +++ b/src/uberjob/_testing/test_mounted_file_store.py @@ -35,7 +35,7 @@ def copy_to_local(self, local_path): with open(local_path, "wb") as f: f.write(self.remote_store.read()) - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: return self.remote_store.get_modified_time() def __repr__(self): diff --git a/src/uberjob/_testing/test_store.py b/src/uberjob/_testing/test_store.py index 2250454..c9c111b 100644 --- a/src/uberjob/_testing/test_store.py +++ b/src/uberjob/_testing/test_store.py @@ -14,7 +14,6 @@ # limitations under the License. # import datetime as dt -import typing from uberjob._util import Missing, repr_helper from uberjob._value_store import ValueStore @@ -79,7 +78,7 @@ def write(self, value): self.value = value self.modified_time = dt.datetime.utcnow() - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: if not self.can_get_modified_time: raise Exception("This test store cannot get modified time.") return self.modified_time diff --git a/src/uberjob/_transformations/caching.py b/src/uberjob/_transformations/caching.py index 168d09c..c5b4110 100644 --- a/src/uberjob/_transformations/caching.py +++ b/src/uberjob/_transformations/caching.py @@ -15,7 +15,6 @@ # import collections import datetime as dt -from typing import Optional from uberjob._errors import NodeError, create_chained_call_error from uberjob._execution.run_function_on_graph import run_function_on_graph @@ -39,7 +38,7 @@ def __repr__(self): Barrier = BarrierType() -def _to_naive_utc_time(value: Optional[dt.datetime]) -> Optional[dt.datetime]: +def _to_naive_utc_time(value: dt.datetime | None) -> dt.datetime | None: return ( value.astimezone(dt.timezone.utc).replace(tzinfo=None) if value and value.tzinfo @@ -60,8 +59,8 @@ def _get_stale_nodes( registry: Registry, *, retry, - max_workers: Optional[int] = None, - fresh_time: Optional[dt.datetime] = None, + max_workers: int | None = None, + fresh_time: dt.datetime | None = None, progress_observer: ProgressObserver, ) -> set[Node]: plan = prune_source_literals( @@ -132,7 +131,7 @@ def process_with_callbacks(node): def _add_value_store( plan: Plan, node: Node, registry_value: RegistryValue, *, is_stale: bool -) -> tuple[Optional[Node], Node]: +) -> tuple[Node | None, Node]: def nested_call(*args): call = plan._call(registry_value.stack_frame, *args) if type(node) is Call: @@ -185,13 +184,13 @@ def plan_with_value_stores( plan: Plan, registry: Registry, *, - output_node: Optional[Node], - max_workers: Optional[int] = None, + output_node: Node | None, + max_workers: int | None = None, retry, - fresh_time: Optional[dt.datetime] = None, + fresh_time: dt.datetime | None = None, inplace: bool, progress_observer, -) -> tuple[Plan, Optional[Node]]: +) -> tuple[Plan, Node | None]: _update_stale_totals(plan, registry, progress_observer) plan = get_mutable_plan(plan, inplace=inplace) stale_nodes = _get_stale_nodes( diff --git a/src/uberjob/_transformations/pruning.py b/src/uberjob/_transformations/pruning.py index 24a3bb7..c4a20f5 100644 --- a/src/uberjob/_transformations/pruning.py +++ b/src/uberjob/_transformations/pruning.py @@ -14,8 +14,7 @@ # limitations under the License. # import itertools -from collections.abc import Iterable -from typing import Callable, Optional +from collections.abc import Callable, Iterable from uberjob._plan import Plan from uberjob._transformations import get_mutable_plan @@ -27,7 +26,7 @@ def prune_plan( plan: Plan, *, required_nodes: Iterable[Node], - output_node: Optional[Node], + output_node: Node | None, inplace: bool ) -> Plan: required_nodes = set(required_nodes) @@ -73,7 +72,7 @@ def _prune_literal_if_trivial(plan: Plan, literal: Literal) -> None: def prune_source_literals( - plan: Plan, *, inplace: bool, predicate: Optional[Callable[[Literal], bool]] = None + plan: Plan, *, inplace: bool, predicate: Callable[[Literal], bool] | None = None ) -> Plan: """ Prunes source literals. When predicate is present, the literal will only be pruned if it returns true. diff --git a/src/uberjob/_value_store.py b/src/uberjob/_value_store.py index a0e5821..77a4f36 100644 --- a/src/uberjob/_value_store.py +++ b/src/uberjob/_value_store.py @@ -14,7 +14,6 @@ # limitations under the License. # import datetime as dt -import typing from abc import ABC, abstractmethod @@ -36,5 +35,5 @@ def write(self, value) -> None: """ @abstractmethod - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: """Get the modified time of the stored value, or ``None`` if there is no stored value.""" diff --git a/src/uberjob/graph.py b/src/uberjob/graph.py index 6af674e..5577d72 100644 --- a/src/uberjob/graph.py +++ b/src/uberjob/graph.py @@ -15,7 +15,7 @@ # """Provides the underlying graph, node, and edge classes used by the :class:`~uberjob.Plan`.""" import warnings -from typing import Callable +from collections.abc import Callable import networkx as nx diff --git a/src/uberjob/progress/__init__.py b/src/uberjob/progress/__init__.py index 859f6a2..d2a9b65 100644 --- a/src/uberjob/progress/__init__.py +++ b/src/uberjob/progress/__init__.py @@ -15,8 +15,8 @@ # """The state of a running Plan can be observed through the use of Progress.""" import pathlib +from collections.abc import Callable from functools import partial -from typing import Callable, Union from uberjob._util import is_ipython @@ -54,9 +54,7 @@ """Display observed progress using IPython widgets.""" -def html_progress( - output: Union[str, pathlib.Path, Callable[[bytes], None]] -) -> Progress: +def html_progress(output: str | pathlib.Path | Callable[[bytes], None]) -> Progress: """ Write observed progress to an HTML file. diff --git a/src/uberjob/progress/_html_progress_observer.py b/src/uberjob/progress/_html_progress_observer.py index 95fa11a..6748bff 100644 --- a/src/uberjob/progress/_html_progress_observer.py +++ b/src/uberjob/progress/_html_progress_observer.py @@ -16,8 +16,8 @@ import datetime as dt import pathlib import traceback +from collections.abc import Callable from html import escape -from typing import Callable, Union from uberjob.progress._simple_progress_observer import ( ScopeState, @@ -209,7 +209,7 @@ class HtmlProgressObserver(SimpleProgressObserver): def __init__( self, - output: Union[str, pathlib.Path, Callable[[bytes], None]], + output: str | pathlib.Path | Callable[[bytes], None], *, initial_update_delay, min_update_interval, diff --git a/src/uberjob/progress/_progress.py b/src/uberjob/progress/_progress.py index 07dae5b..85350cc 100644 --- a/src/uberjob/progress/_progress.py +++ b/src/uberjob/progress/_progress.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import Callable +from collections.abc import Callable from uberjob.progress._progress_observer import ProgressObserver diff --git a/src/uberjob/stores/_file_store.py b/src/uberjob/stores/_file_store.py index e3cae65..1d84783 100644 --- a/src/uberjob/stores/_file_store.py +++ b/src/uberjob/stores/_file_store.py @@ -19,7 +19,7 @@ from abc import ABC, abstractmethod from collections.abc import Generator from contextlib import contextmanager -from typing import IO, AnyStr, Optional, Union +from typing import IO, AnyStr from uberjob._util import repr_helper from uberjob._value_store import ValueStore @@ -27,7 +27,7 @@ STAGING_SUFFIX = ".STAGING" -def get_modified_time(path: Union[str, pathlib.Path]) -> Optional[dt.datetime]: +def get_modified_time(path: str | pathlib.Path) -> dt.datetime | None: """ Gets the modified time of the path, or ``None`` if it does not exist or is inaccessible. @@ -49,8 +49,8 @@ def _try_remove(path): @contextmanager def staged_write_path( - path: Union[str, pathlib.Path] -) -> Generator[Union[str, pathlib.Path], None, None]: + path: str | pathlib.Path, +) -> Generator[str | pathlib.Path, None, None]: """ Context manager for writing a file atomically. @@ -72,7 +72,7 @@ def staged_write_path( @contextmanager def staged_write( - path: Union[str, pathlib.Path], mode="w", **kwargs + path: str | pathlib.Path, mode="w", **kwargs ) -> Generator[IO[AnyStr], None, None]: """ Context manager for writing a file atomically. @@ -100,7 +100,7 @@ class FileStore(ValueStore, ABC): __slots__ = ("path",) - def __init__(self, path: Union[str, pathlib.Path]): + def __init__(self, path: str | pathlib.Path): self.path = path @abstractmethod @@ -115,7 +115,7 @@ def write(self, value) -> None: :param value: The value. """ - def get_modified_time(self) -> Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: """Get the modified time of the file, or ``None`` if it does not exist or is inaccessible.""" return get_modified_time(self.path) diff --git a/src/uberjob/stores/_json_file_store.py b/src/uberjob/stores/_json_file_store.py index c608c34..54b9431 100644 --- a/src/uberjob/stores/_json_file_store.py +++ b/src/uberjob/stores/_json_file_store.py @@ -15,7 +15,6 @@ # import json import pathlib -import typing from uberjob._util import repr_helper from uberjob.stores._file_store import FileStore, staged_write @@ -31,12 +30,7 @@ class JsonFileStore(FileStore): __slots__ = ("encoding",) - def __init__( - self, - path: typing.Union[str, pathlib.Path], - *, - encoding: typing.Optional[str] = None - ): + def __init__(self, path: str | pathlib.Path, *, encoding: str | None = None): super().__init__(path) self.encoding = encoding diff --git a/src/uberjob/stores/_literal_source.py b/src/uberjob/stores/_literal_source.py index c9c0ea1..b658ee9 100644 --- a/src/uberjob/stores/_literal_source.py +++ b/src/uberjob/stores/_literal_source.py @@ -14,7 +14,6 @@ # limitations under the License. # import datetime as dt -import typing from uberjob._util import repr_helper from uberjob._value_store import ValueStore @@ -31,7 +30,7 @@ class LiteralSource(ValueStore): __slots__ = ("value", "modified_time") - def __init__(self, value, modified_time: typing.Optional[dt.datetime]): + def __init__(self, value, modified_time: dt.datetime | None): self.value = value self.modified_time = modified_time @@ -43,7 +42,7 @@ def write(self, value): """Not implemented.""" raise NotImplementedError() - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: """Get the modified time.""" return self.modified_time diff --git a/src/uberjob/stores/_modified_time_source.py b/src/uberjob/stores/_modified_time_source.py index 46b74a8..eb5b233 100644 --- a/src/uberjob/stores/_modified_time_source.py +++ b/src/uberjob/stores/_modified_time_source.py @@ -14,7 +14,6 @@ # limitations under the License. # import datetime as dt -import typing from uberjob._util import repr_helper from uberjob._value_store import ValueStore @@ -31,12 +30,12 @@ class ModifiedTimeSource(ValueStore): __slots__ = ("modified_time",) - def __init__(self, modified_time: typing.Optional[dt.datetime]): + def __init__(self, modified_time: dt.datetime | None): if modified_time is not None and not isinstance(modified_time, dt.datetime): raise TypeError("The modified_time must be a datetime or None.") self.modified_time = modified_time - def read(self) -> typing.Optional[dt.datetime]: + def read(self) -> dt.datetime | None: """Get the modified time.""" return self.modified_time @@ -44,7 +43,7 @@ def write(self, value): """Not implemented.""" raise NotImplementedError() - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: """Get the modified time.""" return self.modified_time diff --git a/src/uberjob/stores/_path_source.py b/src/uberjob/stores/_path_source.py index 1040911..e5f6d0d 100644 --- a/src/uberjob/stores/_path_source.py +++ b/src/uberjob/stores/_path_source.py @@ -15,7 +15,6 @@ # import datetime as dt import pathlib -import typing from uberjob._util import repr_helper from uberjob._value_store import ValueStore @@ -33,11 +32,11 @@ class PathSource(ValueStore): __slots__ = ("path", "required") - def __init__(self, path: typing.Union[str, pathlib.Path], *, required: bool = True): + def __init__(self, path: str | pathlib.Path, *, required: bool = True): self.path = path self.required = required - def read(self) -> typing.Union[str, pathlib.Path]: + def read(self) -> str | pathlib.Path: """ Get the path. @@ -51,7 +50,7 @@ def write(self, value): """Not implemented.""" raise NotImplementedError() - def get_modified_time(self) -> typing.Optional[dt.datetime]: + def get_modified_time(self) -> dt.datetime | None: """ Get the modified time of the file. diff --git a/src/uberjob/stores/_text_file_store.py b/src/uberjob/stores/_text_file_store.py index 0477db2..5ccec2a 100644 --- a/src/uberjob/stores/_text_file_store.py +++ b/src/uberjob/stores/_text_file_store.py @@ -14,7 +14,6 @@ # limitations under the License. # import pathlib -import typing from uberjob._util import repr_helper from uberjob.stores._file_store import FileStore, staged_write @@ -30,12 +29,7 @@ class TextFileStore(FileStore): __slots__ = ("encoding",) - def __init__( - self, - path: typing.Union[str, pathlib.Path], - *, - encoding: typing.Optional[str] = None - ): + def __init__(self, path: str | pathlib.Path, *, encoding: str | None = None): super().__init__(path) self.encoding = encoding