diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3b498cf..90fc91c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,9 +10,9 @@ jobs: - ubuntu-latest - windows-latest python: - - "3.8" - "3.9" - "3.10" + - "3.11" steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v1 diff --git a/src/uberjob/_execution/run_function_on_graph.py b/src/uberjob/_execution/run_function_on_graph.py index c53863c..153dc26 100644 --- a/src/uberjob/_execution/run_function_on_graph.py +++ b/src/uberjob/_execution/run_function_on_graph.py @@ -17,7 +17,7 @@ import os import threading from contextlib import contextmanager -from typing import Dict, List, NamedTuple, Set +from typing import NamedTuple from uberjob._errors import NodeError from uberjob._execution.scheduler import create_queue @@ -80,9 +80,9 @@ def worker_pool(queue, process_item, worker_count): class PreparedNodes(NamedTuple): - source_nodes: List[Node] - single_parent_nodes: Set[Node] - remaining_pred_count_mapping: Dict[Node, int] + source_nodes: list[Node] + single_parent_nodes: set[Node] + remaining_pred_count_mapping: dict[Node, int] def prepare_nodes(graph) -> PreparedNodes: diff --git a/src/uberjob/_execution/run_physical.py b/src/uberjob/_execution/run_physical.py index 3de78f9..fcb445e 100644 --- a/src/uberjob/_execution/run_physical.py +++ b/src/uberjob/_execution/run_physical.py @@ -14,7 +14,7 @@ # limitations under the License. # """Functionality for executing a physical plan""" -from typing import Any, Callable, Dict, NamedTuple, Optional +from typing import Any, Callable, NamedTuple, Optional from uberjob._errors import NodeError, create_chained_call_error from uberjob._execution.run_function_on_graph import run_function_on_graph @@ -45,7 +45,7 @@ def run(self, fn, retry): def _create_bound_call( - graph: Graph, call: Call, result_lookup: Dict[Node, Any] + graph: Graph, call: Call, result_lookup: dict[Node, Any] ) -> BoundCall: args, kwargs = get_argument_nodes(graph, call) args = [result_lookup[predecessor] for predecessor in args] @@ -71,7 +71,7 @@ def _create_bound_call_lookup_and_output_slot( class PrepRunPhysical(NamedTuple): - bound_call_lookup: Dict[Node, BoundCall] + bound_call_lookup: dict[Node, BoundCall] output_slot: Slot process: Callable[[Node], None] plan: Plan diff --git a/src/uberjob/_graph.py b/src/uberjob/_graph.py index 8603983..30b937b 100644 --- a/src/uberjob/_graph.py +++ b/src/uberjob/_graph.py @@ -13,13 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from typing import Tuple - from uberjob._util import fully_qualified_name from uberjob.graph import Call -def get_full_call_scope(call: Call) -> Tuple: +def get_full_call_scope(call: Call) -> tuple: return (*call.scope, fully_qualified_name(call.fn)) diff --git a/src/uberjob/_plan.py b/src/uberjob/_plan.py index 2f04482..3656979 100644 --- a/src/uberjob/_plan.py +++ b/src/uberjob/_plan.py @@ -14,9 +14,10 @@ # limitations under the License. # import operator +from collections.abc import Generator from contextlib import contextmanager from threading import RLock -from typing import Callable, Generator, Tuple +from typing import Callable from uberjob import _builtins from uberjob._util import validation @@ -140,7 +141,7 @@ def gather(self, value) -> Node: """ return self._gather(get_stack_frame(), value) - def unpack(self, iterable, length: int) -> Tuple[Node, ...]: + def unpack(self, iterable, length: int) -> tuple[Node, ...]: """ Unpack a symbolic iterable into a tuple of symbolic values. diff --git a/src/uberjob/_registry.py b/src/uberjob/_registry.py index cbd4909..43318a6 100644 --- a/src/uberjob/_registry.py +++ b/src/uberjob/_registry.py @@ -107,7 +107,7 @@ def keys(self) -> typing.KeysView[Node]: """ return self.mapping.keys() - def values(self) -> typing.List[ValueStore]: + def values(self) -> list[ValueStore]: """ Get all registered :class:`~uberjob.ValueStore` instances. @@ -115,7 +115,7 @@ def values(self) -> typing.List[ValueStore]: """ return [v.value_store for v in self.mapping.values()] - def items(self) -> typing.List[typing.Tuple[Node, ValueStore]]: + def items(self) -> list[tuple[Node, ValueStore]]: """ Get all registered (node, value_store) pairs. diff --git a/src/uberjob/_rendering.py b/src/uberjob/_rendering.py index e90bf4e..a488a04 100644 --- a/src/uberjob/_rendering.py +++ b/src/uberjob/_rendering.py @@ -130,7 +130,7 @@ class Scope: def render( - plan: typing.Union[Plan, Graph, typing.Tuple[Plan, typing.Optional[Node]]], + plan: typing.Union[Plan, Graph, tuple[Plan, typing.Optional[Node]]], *, registry: Registry = None, predicate: typing.Callable[[Node, dict], bool] = None, diff --git a/src/uberjob/_run.py b/src/uberjob/_run.py index 1502e8c..8b6d4f0 100644 --- a/src/uberjob/_run.py +++ b/src/uberjob/_run.py @@ -15,7 +15,8 @@ # import collections import datetime as dt -from typing import Callable, Iterable, Optional, Tuple, Union +from collections.abc import Iterable +from typing import Callable, Optional, Union from uberjob._errors import CallError from uberjob._execution.run_function_on_graph import NodeError @@ -84,7 +85,7 @@ def run( 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, + transform_physical: Optional[Callable[[Plan, Node], tuple[Plan, Node]]] = None, stale_check_max_workers: Optional[int] = None, ): """ diff --git a/src/uberjob/_transformations/caching.py b/src/uberjob/_transformations/caching.py index 348eca7..168d09c 100644 --- a/src/uberjob/_transformations/caching.py +++ b/src/uberjob/_transformations/caching.py @@ -15,7 +15,7 @@ # import collections import datetime as dt -from typing import Optional, Set, Tuple +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 @@ -47,7 +47,7 @@ def _to_naive_utc_time(value: Optional[dt.datetime]) -> Optional[dt.datetime]: ) -def _get_stale_scope(call: Call, registry: Registry) -> Tuple: +def _get_stale_scope(call: Call, registry: Registry) -> tuple: scope = get_full_call_scope(call) value_store = registry.get(call) if value_store is None: @@ -63,7 +63,7 @@ def _get_stale_nodes( max_workers: Optional[int] = None, fresh_time: Optional[dt.datetime] = None, progress_observer: ProgressObserver, -) -> Set[Node]: +) -> set[Node]: plan = prune_source_literals( plan, inplace=False, predicate=lambda node: node not in registry ) @@ -132,7 +132,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[Optional[Node], Node]: def nested_call(*args): call = plan._call(registry_value.stack_frame, *args) if type(node) is Call: @@ -191,7 +191,7 @@ def plan_with_value_stores( fresh_time: Optional[dt.datetime] = None, inplace: bool, progress_observer, -) -> Tuple[Plan, Optional[Node]]: +) -> tuple[Plan, Optional[Node]]: _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 1cf8595..24a3bb7 100644 --- a/src/uberjob/_transformations/pruning.py +++ b/src/uberjob/_transformations/pruning.py @@ -14,7 +14,8 @@ # limitations under the License. # import itertools -from typing import Callable, Iterable, Optional +from collections.abc import Iterable +from typing import Callable, Optional from uberjob._plan import Plan from uberjob._transformations import get_mutable_plan diff --git a/src/uberjob/graph.py b/src/uberjob/graph.py index 4dd7060..6af674e 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, Dict, List, Tuple +from typing import Callable import networkx as nx @@ -147,7 +147,7 @@ def __eq__(self, other): ) -def get_argument_nodes(graph: Graph, call: Call) -> Tuple[List[Node], Dict[str, Node]]: +def get_argument_nodes(graph: Graph, call: Call) -> tuple[list[Node], dict[str, Node]]: """ Return the symbolic args and kwargs of the given :class:`~uberjob.graph.Call`. @@ -173,7 +173,7 @@ def get_argument_nodes(graph: Graph, call: Call) -> Tuple[List[Node], Dict[str, return args, dict(keyword_arg_pairs) -def get_scope(graph: Graph, node: Node) -> Tuple: +def get_scope(graph: Graph, node: Node) -> tuple: """ Return the scope of the given :class:`~uberjob.graph.Node`. diff --git a/src/uberjob/progress/_composite_progress_observer.py b/src/uberjob/progress/_composite_progress_observer.py index 449a3e7..48afcb7 100644 --- a/src/uberjob/progress/_composite_progress_observer.py +++ b/src/uberjob/progress/_composite_progress_observer.py @@ -13,8 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from collections.abc import Iterable from contextlib import ExitStack -from typing import Iterable, Tuple from uberjob.progress._progress_observer import ProgressObserver @@ -39,21 +39,21 @@ def __enter__(self): def __exit__(self, exc_type, exc_val, exc_tb): self._stack.__exit__(exc_type, exc_val, exc_tb) - def increment_total(self, *, section: str, scope: Tuple, amount: int): + def increment_total(self, *, section: str, scope: tuple, amount: int): for progress_observer in self._progress_observers: progress_observer.increment_total( section=section, scope=scope, amount=amount ) - def increment_running(self, *, section: str, scope: Tuple): + def increment_running(self, *, section: str, scope: tuple): for progress_observer in self._progress_observers: progress_observer.increment_running(section=section, scope=scope) - def increment_completed(self, *, section: str, scope: Tuple): + def increment_completed(self, *, section: str, scope: tuple): for progress_observer in self._progress_observers: progress_observer.increment_completed(section=section, scope=scope) - def increment_failed(self, *, section: str, scope: Tuple, exception: Exception): + def increment_failed(self, *, section: str, scope: tuple, exception: Exception): for progress_observer in self._progress_observers: progress_observer.increment_failed( section=section, scope=scope, exception=exception diff --git a/src/uberjob/progress/_console_progress_observer.py b/src/uberjob/progress/_console_progress_observer.py index 7d3d89b..b9ee3dc 100644 --- a/src/uberjob/progress/_console_progress_observer.py +++ b/src/uberjob/progress/_console_progress_observer.py @@ -44,7 +44,7 @@ def _ralign(strings): def _print_section(print_, section, scope_mapping): scope_items = sorted_scope_items(scope_mapping) - print_("{}:".format(section)) + print_(f"{section}:") progress_strs = _ralign( scope_state.to_progress_string() for scope, scope_state in scope_items ) @@ -54,9 +54,7 @@ def _print_section(print_, section, scope_mapping): for progress_str, elapsed_str, (scope, _scope_state) in zip( progress_strs, elapsed_strs, scope_items ): - print_( - " {} | {} | {}".format(progress_str, elapsed_str, get_scope_string(scope)) - ) + print_(f" {progress_str} | {elapsed_str} | {get_scope_string(scope)}") def _print_new_exceptions(print_, new_exception_index, exception_tuples): diff --git a/src/uberjob/progress/_ipython_progress_observer.py b/src/uberjob/progress/_ipython_progress_observer.py index 0004f99..0fa4870 100644 --- a/src/uberjob/progress/_ipython_progress_observer.py +++ b/src/uberjob/progress/_ipython_progress_observer.py @@ -74,7 +74,7 @@ def _render(self, state, new_exception_index, exception_tuples, elapsed): title_widget = self._get( "section", section, "title", default=widgets.HTML ) - title_widget.value = "{}".format(html.escape(title)) + title_widget.value = f"{html.escape(title)}" children.append(title_widget) for scope, scope_state in sorted_scope_items(scope_mapping): progress_widget = self._get( @@ -137,9 +137,7 @@ def _get_exception_accordion(self, exception_tuples): ) ) exception_text_widgets.append(exception_text_widget) - exception_titles.append( - "Exception {}; {}".format(i + 1, get_scope_string(scope)) - ) + exception_titles.append(f"Exception {i + 1}; {get_scope_string(scope)}") exception_accordion.children = exception_text_widgets for i, exception_title in enumerate(exception_titles): exception_accordion.set_title(i, exception_title) diff --git a/src/uberjob/progress/_progress_observer.py b/src/uberjob/progress/_progress_observer.py index a971c33..3af2fdf 100644 --- a/src/uberjob/progress/_progress_observer.py +++ b/src/uberjob/progress/_progress_observer.py @@ -14,7 +14,6 @@ # limitations under the License. # from abc import ABC, abstractmethod -from typing import Tuple class ProgressObserver(ABC): @@ -29,7 +28,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): """Stop observing progress.""" @abstractmethod - def increment_total(self, *, section: str, scope: Tuple, amount: int): + def increment_total(self, *, section: str, scope: tuple, amount: int): """ Increment the number of entries in this section and scope by the specified amount. @@ -39,7 +38,7 @@ def increment_total(self, *, section: str, scope: Tuple, amount: int): """ @abstractmethod - def increment_running(self, *, section: str, scope: Tuple): + def increment_running(self, *, section: str, scope: tuple): """ Increment the number of running entries in this section and scope. This method must be thread-safe. @@ -48,7 +47,7 @@ def increment_running(self, *, section: str, scope: Tuple): """ @abstractmethod - def increment_completed(self, *, section: str, scope: Tuple): + def increment_completed(self, *, section: str, scope: tuple): """ Increment the number of completed entries in this section and scope. This method must be thread-safe. @@ -57,7 +56,7 @@ def increment_completed(self, *, section: str, scope: Tuple): """ @abstractmethod - def increment_failed(self, *, section: str, scope: Tuple, exception: Exception): + def increment_failed(self, *, section: str, scope: tuple, exception: Exception): """ Increment the number of failed entries in this section and scope. This method must be thread-safe. diff --git a/src/uberjob/progress/_simple_progress_observer.py b/src/uberjob/progress/_simple_progress_observer.py index 5978125..6f1820e 100644 --- a/src/uberjob/progress/_simple_progress_observer.py +++ b/src/uberjob/progress/_simple_progress_observer.py @@ -16,7 +16,6 @@ import threading import time from abc import ABC, abstractmethod -from typing import Tuple from uberjob.progress._progress_observer import ProgressObserver @@ -202,22 +201,22 @@ def _run_update_thread(self): if output_value is not None: self._output(output_value) - def increment_total(self, *, section: str, scope: Tuple, amount: int): + def increment_total(self, *, section: str, scope: tuple, amount: int): with self._lock: self._stale = True self._state.increment_total(section, scope, amount) - def increment_running(self, *, section: str, scope: Tuple): + def increment_running(self, *, section: str, scope: tuple): with self._lock: self._stale = True self._state.increment_running(section, scope) - def increment_completed(self, *, section: str, scope: Tuple): + def increment_completed(self, *, section: str, scope: tuple): with self._lock: self._stale = True self._state.increment_completed(section, scope) - def increment_failed(self, *, section: str, scope: Tuple, exception: Exception): + def increment_failed(self, *, section: str, scope: tuple, exception: Exception): with self._lock: self._stale = True self._state.increment_failed(section, scope) diff --git a/src/uberjob/stores/_file_store.py b/src/uberjob/stores/_file_store.py index 619a187..e3cae65 100644 --- a/src/uberjob/stores/_file_store.py +++ b/src/uberjob/stores/_file_store.py @@ -17,8 +17,9 @@ import os import pathlib from abc import ABC, abstractmethod +from collections.abc import Generator from contextlib import contextmanager -from typing import IO, AnyStr, Generator, Optional, Union +from typing import IO, AnyStr, Optional, Union from uberjob._util import repr_helper from uberjob._value_store import ValueStore diff --git a/src/uberjob/stores/_path_source.py b/src/uberjob/stores/_path_source.py index f06b6cf..1040911 100644 --- a/src/uberjob/stores/_path_source.py +++ b/src/uberjob/stores/_path_source.py @@ -63,7 +63,7 @@ def get_modified_time(self) -> typing.Optional[dt.datetime]: def _get_modified_time(self, required): modified_time = get_modified_time(self.path) if modified_time is None and required: - raise IOError( + raise OSError( f"Failed to get modified time of required source path {self.path!r}." ) return modified_time diff --git a/src/uberjob/stores/_touch_file_store.py b/src/uberjob/stores/_touch_file_store.py index a553c40..e1b408b 100644 --- a/src/uberjob/stores/_touch_file_store.py +++ b/src/uberjob/stores/_touch_file_store.py @@ -31,7 +31,7 @@ def read(self): """Return ``None`` after ensuring that the touch file exists and is empty.""" with open(self.path, "rb") as inputfile: if inputfile.read(1): - raise IOError(f"The path {self.path!r} exists but is not empty.") + raise OSError(f"The path {self.path!r} exists but is not empty.") return None def write(self, value: None) -> None: diff --git a/tests/test_plan.py b/tests/test_plan.py index 50a27d2..5b657b1 100644 --- a/tests/test_plan.py +++ b/tests/test_plan.py @@ -339,7 +339,7 @@ def test_retry_validation(self): def test_traceback_manipulation(self): def x(): - raise IOError("buzz") + raise OSError("buzz") def y(): try: