diff --git a/notes-to-self/file-read-latency.py b/notes-to-self/file-read-latency.py index 132e29dc4f..e4c8099603 100644 --- a/notes-to-self/file-read-latency.py +++ b/notes-to-self/file-read-latency.py @@ -24,7 +24,5 @@ seek = (end - between) / COUNT * 1e9 read = both - seek print( - "{:.2f} ns/(seek+read), {:.2f} ns/seek, estimate ~{:.2f} ns/read".format( - both, seek, read - ) + f"{both:.2f} ns/(seek+read), {seek:.2f} ns/seek, estimate ~{read:.2f} ns/read" ) diff --git a/notes-to-self/proxy-benchmarks.py b/notes-to-self/proxy-benchmarks.py index ea92e10c6f..303a6c23ed 100644 --- a/notes-to-self/proxy-benchmarks.py +++ b/notes-to-self/proxy-benchmarks.py @@ -51,12 +51,10 @@ def __init__(self, wrapped): def add_wrapper(cls, method): code = textwrap.dedent( - """ + f""" def wrapper(self, *args, **kwargs): - return self._wrapped.{}(*args, **kwargs) - """.format( - method - ) + return self._wrapped.{method}(*args, **kwargs) + """ ) ns = {} exec(code, ns) @@ -106,7 +104,7 @@ def __init__(self, wrapped): def add_wrapper(cls, attr): code = textwrap.dedent( - """ + f""" def getter(self): return self._wrapped.{attr} @@ -115,9 +113,7 @@ def setter(self, newval): def deleter(self): del self._wrapped.{attr} - """.format( - attr=attr - ) + """ ) ns = {} exec(code, ns) @@ -176,4 +172,4 @@ def check(cls): # obj.fileno end = time.perf_counter() per_usec = COUNT / (end - start) / 1e6 - print("{:7.2f} / us: {} ({})".format(per_usec, obj.strategy, obj.works_for)) + print(f"{per_usec:7.2f} / us: {obj.strategy} ({obj.works_for})") diff --git a/pyproject.toml b/pyproject.toml index e8f4b5344c..21e1a5b045 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ select = [ "E", # Error "W", # Warning "I", # isort + "UP", # pyupgrade "B", # flake8-bugbear "YTT", # flake8-2020 "ASYNC", # flake8-async diff --git a/trio/_channel.py b/trio/_channel.py index 48f80daf1c..b20008ecaa 100644 --- a/trio/_channel.py +++ b/trio/_channel.py @@ -155,9 +155,7 @@ def __attrs_post_init__(self) -> None: self._state.open_send_channels += 1 def __repr__(self) -> str: - return "".format( - id(self), id(self._state) - ) + return f"" def statistics(self) -> MemoryChannelStats: # XX should we also report statistics specific to this object? diff --git a/trio/_core/_io_epoll.py b/trio/_core/_io_epoll.py index a0373fb8fa..3888da9ba6 100644 --- a/trio/_core/_io_epoll.py +++ b/trio/_core/_io_epoll.py @@ -3,7 +3,7 @@ import select import sys from collections import defaultdict -from typing import TYPE_CHECKING, DefaultDict, Literal +from typing import TYPE_CHECKING, Literal import attr @@ -201,7 +201,7 @@ class _EpollStatistics: class EpollIOManager: _epoll: select.epoll = attr.ib(factory=select.epoll) # {fd: EpollWaiters} - _registered: DefaultDict[int, EpollWaiters] = attr.ib( + _registered: defaultdict[int, EpollWaiters] = attr.ib( factory=lambda: defaultdict(EpollWaiters) ) _force_wakeup: WakeupSocketpair = attr.ib(factory=WakeupSocketpair) diff --git a/trio/_core/_io_windows.py b/trio/_core/_io_windows.py index f1db1d9f1c..dc939873a8 100644 --- a/trio/_core/_io_windows.py +++ b/trio/_core/_io_windows.py @@ -11,7 +11,6 @@ Callable, Iterator, Literal, - Optional, TypeVar, cast, ) @@ -245,9 +244,9 @@ class CKeys(enum.IntEnum): # operation and start a new one. @attr.s(slots=True, eq=False) class AFDWaiters: - read_task: Optional[_core.Task] = attr.ib(default=None) - write_task: Optional[_core.Task] = attr.ib(default=None) - current_op: Optional[AFDPollOp] = attr.ib(default=None) + read_task: _core.Task | None = attr.ib(default=None) + write_task: _core.Task | None = attr.ib(default=None) + current_op: AFDPollOp | None = attr.ib(default=None) # We also need to bundle up all the info for a single op into a standalone @@ -585,9 +584,9 @@ def process_events(self, received: EventResult) -> None: pass else: exc = _core.TrioInternalError( - "Failed to cancel overlapped I/O in {} and didn't " + f"Failed to cancel overlapped I/O in {waiter.name} and didn't " "receive the completion either. Did you forget to " - "call register_with_iocp()?".format(waiter.name) + "call register_with_iocp()?" ) # Raising this out of handle_io ensures that # the user will see our message even if some diff --git a/trio/_core/_run.py b/trio/_core/_run.py index 2ee73868e5..ff30a56cb1 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -72,7 +72,7 @@ DEADLINE_HEAP_MIN_PRUNE_THRESHOLD: Final = 1000 # Passed as a sentinel -_NO_SEND: Final["Outcome[Any]"] = cast("Outcome[Any]", object()) +_NO_SEND: Final[Outcome[Any]] = cast("Outcome[Any]", object()) FnT = TypeVar("FnT", bound="Callable[..., Any]") StatusT = TypeVar("StatusT") @@ -537,8 +537,8 @@ def __enter__(self) -> Self: def _close(self, exc: BaseException | None) -> BaseException | None: if self._cancel_status is None: new_exc = RuntimeError( - "Cancel scope stack corrupted: attempted to exit {!r} " - "which had already been exited".format(self) + f"Cancel scope stack corrupted: attempted to exit {self!r} " + "which had already been exited" ) new_exc.__context__ = exc return new_exc @@ -559,10 +559,8 @@ def _close(self, exc: BaseException | None) -> BaseException | None: # cancel scope it's trying to close. Raise an error # without changing any state. new_exc = RuntimeError( - "Cancel scope stack corrupted: attempted to exit {!r} " - "from unrelated {!r}\n{}".format( - self, scope_task, MISNESTING_ADVICE - ) + f"Cancel scope stack corrupted: attempted to exit {self!r} " + f"from unrelated {scope_task!r}\n{MISNESTING_ADVICE}" ) new_exc.__context__ = exc return new_exc @@ -1773,9 +1771,7 @@ def task_exited(self, task: Task, outcome: Outcome[Any]) -> None: # traceback frame included raise RuntimeError( "Cancel scope stack corrupted: cancel scope surrounding " - "{!r} was closed before the task exited\n{}".format( - task, MISNESTING_ADVICE - ) + f"{task!r} was closed before the task exited\n{MISNESTING_ADVICE}" ) except RuntimeError as new_exc: if isinstance(outcome, Error): @@ -2596,10 +2592,10 @@ def unrolled_run( runner.task_exited(task, msg.final_outcome) else: exc = TypeError( - "trio.run received unrecognized yield message {!r}. " + f"trio.run received unrecognized yield message {msg!r}. " "Are you trying to use a library written for some " "other framework like asyncio? That won't work " - "without some kind of compatibility shim.".format(msg) + "without some kind of compatibility shim." ) # The foreign library probably doesn't adhere to our # protocol of unwrapping whatever outcome gets sent in. diff --git a/trio/_core/_windows_cffi.py b/trio/_core/_windows_cffi.py index d411770971..72bfa64d68 100644 --- a/trio/_core/_windows_cffi.py +++ b/trio/_core/_windows_cffi.py @@ -2,10 +2,10 @@ import enum import re -from typing import TYPE_CHECKING, NewType, Protocol, cast +from typing import TYPE_CHECKING, NewType, NoReturn, Protocol, cast if TYPE_CHECKING: - from typing_extensions import NoReturn, TypeAlias + from typing_extensions import TypeAlias import cffi diff --git a/trio/_file_io.py b/trio/_file_io.py index 6f30f89dd8..958740e15e 100644 --- a/trio/_file_io.py +++ b/trio/_file_io.py @@ -501,8 +501,8 @@ def has(attr: str) -> bool: if not (has("close") and (has("read") or has("write"))): raise TypeError( - "{} does not implement required duck-file methods: " - "close and (read or write)".format(file) + f"{file} does not implement required duck-file methods: " + "close and (read or write)" ) return AsyncIOWrapper(file) diff --git a/trio/_path.py b/trio/_path.py index 219f706825..508ad5d04d 100644 --- a/trio/_path.py +++ b/trio/_path.py @@ -413,12 +413,8 @@ async def group(self) -> str: ... async def is_mount(self) -> bool: ... if sys.version_info >= (3, 9): async def readlink(self) -> Path: ... - if sys.version_info >= (3, 8): - async def rename(self, target: StrPath) -> Path: ... - async def replace(self, target: StrPath) -> Path: ... - else: - async def rename(self, target: StrPath) -> None: ... - async def replace(self, target: StrPath) -> None: ... + async def rename(self, target: StrPath) -> Path: ... + async def replace(self, target: StrPath) -> Path: ... async def resolve(self, strict: bool = False) -> Path: ... async def rglob(self, pattern: str) -> Iterable[Path]: ... async def rmdir(self) -> None: ... @@ -426,10 +422,7 @@ async def symlink_to(self, target: StrPath, target_is_directory: bool = False) - if sys.version_info >= (3, 10): async def hardlink_to(self, target: str | pathlib.Path) -> None: ... async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: ... - if sys.version_info >= (3, 8): - async def unlink(self, missing_ok: bool = False) -> None: ... - else: - async def unlink(self) -> None: ... + async def unlink(self, missing_ok: bool = False) -> None: ... @classmethod async def home(self) -> Path: ... async def absolute(self) -> Path: ... diff --git a/trio/_subprocess.py b/trio/_subprocess.py index 9e93bb5919..9b70945ba5 100644 --- a/trio/_subprocess.py +++ b/trio/_subprocess.py @@ -803,7 +803,7 @@ async def killer() -> None: if sys.platform == "win32": async def open_process( - command: Union[StrOrBytesPath, Sequence[StrOrBytesPath]], + command: StrOrBytesPath | Sequence[StrOrBytesPath], *, stdin: int | HasFileno | None = None, stdout: int | HasFileno | None = None, diff --git a/trio/_subprocess_platform/kqueue.py b/trio/_subprocess_platform/kqueue.py index a65045670f..fcf72650ee 100644 --- a/trio/_subprocess_platform/kqueue.py +++ b/trio/_subprocess_platform/kqueue.py @@ -9,7 +9,7 @@ assert (sys.platform != "win32" and sys.platform != "linux") or not TYPE_CHECKING -async def wait_child_exiting(process: "_subprocess.Process") -> None: +async def wait_child_exiting(process: _subprocess.Process) -> None: kqueue = _core.current_kqueue() try: from select import KQ_NOTE_EXIT diff --git a/trio/_sync.py b/trio/_sync.py index 58a265cd97..b168db003b 100644 --- a/trio/_sync.py +++ b/trio/_sync.py @@ -453,9 +453,7 @@ def __repr__(self) -> str: max_value_str = "" else: max_value_str = f", max_value={self._max_value}" - return "".format( - self._value, max_value_str, id(self) - ) + return f"" @property def value(self) -> int: @@ -556,9 +554,7 @@ def __repr__(self) -> str: else: s1 = "unlocked" s2 = "" - return "<{} {} object at {:#x}{}>".format( - s1, self.__class__.__name__, id(self), s2 - ) + return f"<{s1} {self.__class__.__name__} object at {id(self):#x}{s2}>" def locked(self) -> bool: """Check whether the lock is currently held. diff --git a/trio/_tests/test_threads.py b/trio/_tests/test_threads.py index 4595b5867a..ff865d2ef0 100644 --- a/trio/_tests/test_threads.py +++ b/trio/_tests/test_threads.py @@ -8,7 +8,7 @@ import time import weakref from functools import partial -from typing import Callable, Optional +from typing import Callable import pytest import sniffio @@ -205,7 +205,7 @@ async def test_thread_name(name: str) -> None: await test_thread_name("💙") -def _get_thread_name(ident: Optional[int] = None) -> Optional[str]: +def _get_thread_name(ident: int | None = None) -> str | None: import ctypes import ctypes.util @@ -262,7 +262,7 @@ def f(name: str) -> Callable[[None], threading.Thread]: await to_thread_run_sync(f(default), thread_name=None) # test that you can set a custom name, and that it's reset afterwards - async def test_thread_name(name: str, expected: Optional[str] = None) -> None: + async def test_thread_name(name: str, expected: str | None = None) -> None: if expected is None: expected = name thread = await to_thread_run_sync(f(expected), thread_name=name) diff --git a/trio/_tests/test_unix_pipes.py b/trio/_tests/test_unix_pipes.py index 0b0d2ceb23..b26457584d 100644 --- a/trio/_tests/test_unix_pipes.py +++ b/trio/_tests/test_unix_pipes.py @@ -24,8 +24,7 @@ from .._unix_pipes import FdStream -# Have to use quoted types so import doesn't crash on windows -async def make_pipe() -> "tuple[FdStream, FdStream]": +async def make_pipe() -> tuple[FdStream, FdStream]: """Makes a new pair of pipes.""" (r, w) = os.pipe() return FdStream(w), FdStream(r) diff --git a/trio/_util.py b/trio/_util.py index 36020ca2d7..e9864cf2fd 100644 --- a/trio/_util.py +++ b/trio/_util.py @@ -135,28 +135,26 @@ def _return_value_looks_like_wrong_library(value: object) -> bool: raise TypeError( "Trio was expecting an async function, but instead it got " - "a coroutine object {async_fn!r}\n" + f"a coroutine object {async_fn!r}\n" "\n" "Probably you did something like:\n" "\n" - " trio.run({async_fn.__name__}(...)) # incorrect!\n" - " nursery.start_soon({async_fn.__name__}(...)) # incorrect!\n" + f" trio.run({async_fn.__name__}(...)) # incorrect!\n" + f" nursery.start_soon({async_fn.__name__}(...)) # incorrect!\n" "\n" "Instead, you want (notice the parentheses!):\n" "\n" - " trio.run({async_fn.__name__}, ...) # correct!\n" - " nursery.start_soon({async_fn.__name__}, ...) # correct!".format( - async_fn=async_fn - ) + f" trio.run({async_fn.__name__}, ...) # correct!\n" + f" nursery.start_soon({async_fn.__name__}, ...) # correct!" ) from None # Give good error for: nursery.start_soon(future) if _return_value_looks_like_wrong_library(async_fn): raise TypeError( "Trio was expecting an async function, but instead it got " - "{!r} – are you trying to use a library written for " + f"{async_fn!r} – are you trying to use a library written for " "asyncio/twisted/tornado or similar? That won't work " - "without some sort of compatibility shim.".format(async_fn) + "without some sort of compatibility shim." ) from None raise @@ -174,15 +172,15 @@ def _return_value_looks_like_wrong_library(value: object) -> bool: # Give good error for: nursery.start_soon(func_returning_future) if _return_value_looks_like_wrong_library(coro): raise TypeError( - "Trio got unexpected {!r} – are you trying to use a " + f"Trio got unexpected {coro!r} – are you trying to use a " "library written for asyncio/twisted/tornado or similar? " - "That won't work without some sort of compatibility shim.".format(coro) + "That won't work without some sort of compatibility shim." ) if inspect.isasyncgen(coro): raise TypeError( "start_soon expected an async function but got an async " - "generator {!r}".format(coro) + f"generator {coro!r}" ) # Give good error for: nursery.start_soon(some_sync_fn) @@ -370,7 +368,7 @@ def __call__(cls, *args: object, **kwargs: object) -> None: f"{cls.__module__}.{cls.__qualname__} has no public constructor" ) - def _create(cls: t.Type[T], *args: object, **kwargs: object) -> T: + def _create(cls: type[T], *args: object, **kwargs: object) -> T: return super().__call__(*args, **kwargs) # type: ignore diff --git a/trio/testing/_fake_net.py b/trio/testing/_fake_net.py index 74bda92e34..15399149e3 100644 --- a/trio/testing/_fake_net.py +++ b/trio/testing/_fake_net.py @@ -12,7 +12,7 @@ import errno import ipaddress import os -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Union import attr @@ -104,7 +104,7 @@ def reply(self, payload): @attr.frozen class FakeSocketFactory(trio.abc.SocketFactory): - fake_net: "FakeNet" + fake_net: FakeNet def socket(self, family: int, type: int, proto: int) -> FakeSocket: # type: ignore[override] return FakeSocket._create(self.fake_net, family, type, proto) @@ -112,7 +112,7 @@ def socket(self, family: int, type: int, proto: int) -> FakeSocket: # type: ign @attr.frozen class FakeHostnameResolver(trio.abc.HostnameResolver): - fake_net: "FakeNet" + fake_net: FakeNet async def getaddrinfo( self, @@ -151,7 +151,7 @@ def __init__(self) -> None: self.route_packet = None - def _bind(self, binding: UDPBinding, socket: "FakeSocket") -> None: + def _bind(self, binding: UDPBinding, socket: FakeSocket) -> None: if binding in self._bound: _fake_err(errno.EADDRINUSE) self._bound[binding] = socket @@ -203,7 +203,7 @@ def __init__( ](float("inf")) # This is the source-of-truth for what port etc. this socket is bound to - self._binding: Optional[UDPBinding] = None + self._binding: UDPBinding | None = None @property def type(self) -> SocketKind: