From: Daniele Varrazzo Date: Wed, 29 May 2024 23:58:04 +0000 (+0200) Subject: refactor: drop use of typing.Optional X-Git-Tag: 3.2.0~19^2~9 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=5e909a66bcce07797c4a5110548050b39d487613;p=thirdparty%2Fpsycopg.git refactor: drop use of typing.Optional --- diff --git a/docs/advanced/typing.rst b/docs/advanced/typing.rst index f1f901268..98efb4067 100644 --- a/docs/advanced/typing.rst +++ b/docs/advanced/typing.rst @@ -36,7 +36,7 @@ annotations such as `!Connection[Any]` and `!Cursor[Any]`. cur = conn.cursor() # type is psycopg.Cursor[tuple[Any, ...]] - rec = cur.fetchone() # type is Optional[tuple[Any, ...]] + rec = cur.fetchone() # type is tuple[Any, ...] | None recs = cur.fetchall() # type is List[tuple[Any, ...]] @@ -63,7 +63,7 @@ cursors and annotate the returned objects accordingly. See # dcur type is psycopg.Cursor[dict[str, Any]] in both cases drec = dcur.fetchone() - # drec type is Optional[dict[str, Any]] + # drec type is dict[str, Any] | None .. _pool-generic: @@ -97,7 +97,7 @@ otherwise the typing system and the runtime will not agree. # reveal_type(conn): Connection[dict[str, Any]] row = conn.execute("SELECT now()").fetchone() - # reveal_type(row): Optional[dict[str, Any]] + # reveal_type(row): dict[str, Any] | None print(row) # {"now": datetime.datetime(...)} @@ -118,7 +118,7 @@ type is not parametric) then it's not necessary to specify `!kwargs`: # reveal_type(conn): MyConnection row = conn.execute("SELECT now()").fetchone() - # reveal_type(row): Optional[dict[str, Any]] + # reveal_type(row): dict[str, Any] | None print(row) # {"now": datetime.datetime(...)} diff --git a/psycopg/psycopg/_adapters_map.py b/psycopg/psycopg/_adapters_map.py index 904ec71d1..d76c8247f 100644 --- a/psycopg/psycopg/_adapters_map.py +++ b/psycopg/psycopg/_adapters_map.py @@ -4,7 +4,9 @@ Mapping from types/oids to Dumpers/Loaders # Copyright (C) 2020 The Psycopg Team -from typing import Any, Dict, List, Optional, Type, Union +from __future__ import annotations + +from typing import Any, Dict, List, Type, Union from typing import cast, TYPE_CHECKING from . import pq @@ -69,8 +71,8 @@ class AdaptersMap: def __init__( self, - template: Optional["AdaptersMap"] = None, - types: Optional[TypesRegistry] = None, + template: "AdaptersMap" | None = None, + types: TypesRegistry | None = None, ): if template: self._dumpers = template._dumpers.copy() @@ -105,7 +107,7 @@ class AdaptersMap: return self @property - def connection(self) -> Optional["BaseConnection[Any]"]: + def connection(self) -> "BaseConnection[Any]" | None: return None def register_dumper( @@ -256,7 +258,7 @@ class AdaptersMap: ) raise e.ProgrammingError(msg) - def get_loader(self, oid: int, format: pq.Format) -> Optional[Type["Loader"]]: + def get_loader(self, oid: int, format: pq.Format) -> Type["Loader"] | None: """ Return the loader class for the given oid and format. diff --git a/psycopg/psycopg/_cmodule.py b/psycopg/psycopg/_cmodule.py index 288ef1ba0..33162bc10 100644 --- a/psycopg/psycopg/_cmodule.py +++ b/psycopg/psycopg/_cmodule.py @@ -4,11 +4,11 @@ Simplify access to the _psycopg module # Copyright (C) 2021 The Psycopg Team -from typing import Optional +from __future__ import annotations from . import pq -__version__: Optional[str] = None +__version__: str | None = None # Note: "c" must the first attempt so that mypy associates the variable the # right module interface. It will not result Optional, but hey. diff --git a/psycopg/psycopg/_column.py b/psycopg/psycopg/_column.py index 7c879a60f..2e441b678 100644 --- a/psycopg/psycopg/_column.py +++ b/psycopg/psycopg/_column.py @@ -4,7 +4,9 @@ The Column object in Cursor.description # Copyright (C) 2020 The Psycopg Team -from typing import Any, Optional, Sequence, TYPE_CHECKING +from __future__ import annotations + +from typing import Any, Sequence, TYPE_CHECKING from operator import attrgetter if TYPE_CHECKING: @@ -76,27 +78,27 @@ class Column(Sequence[Any]): return self._ftype @property - def display_size(self) -> Optional[int]: + def display_size(self) -> int | None: """The field size, for string types such as :sql:`varchar(n)`.""" return self._type.get_display_size(self._fmod) if self._type else None @property - def internal_size(self) -> Optional[int]: + def internal_size(self) -> int | None: """The internal field size for fixed-size types, None otherwise.""" fsize = self._fsize return fsize if fsize >= 0 else None @property - def precision(self) -> Optional[int]: + def precision(self) -> int | None: """The number of digits for fixed precision types.""" return self._type.get_precision(self._fmod) if self._type else None @property - def scale(self) -> Optional[int]: + def scale(self) -> int | None: """The number of digits after the decimal point if available.""" return self._type.get_scale(self._fmod) if self._type else None @property - def null_ok(self) -> Optional[bool]: + def null_ok(self) -> bool | None: """Always `!None`""" return None diff --git a/psycopg/psycopg/_connection_base.py b/psycopg/psycopg/_connection_base.py index dde4e1f7c..2b862e803 100644 --- a/psycopg/psycopg/_connection_base.py +++ b/psycopg/psycopg/_connection_base.py @@ -4,10 +4,12 @@ psycopg connection objects # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import sys import logging from typing import Callable, Generic -from typing import List, NamedTuple, Optional, Tuple, Union +from typing import List, NamedTuple, Tuple, Union from typing import TYPE_CHECKING from weakref import ref, ReferenceType from warnings import warn @@ -104,7 +106,7 @@ class BaseConnection(Generic[Row]): self._autocommit = False # None, but set to a copy of the global adapters map as soon as requested. - self._adapters: Optional[AdaptersMap] = None + self._adapters: AdaptersMap | None = None self._notice_handlers: List[NoticeHandler] = [] self._notify_handlers: List[NotifyHandler] = [] @@ -114,7 +116,7 @@ class BaseConnection(Generic[Row]): self._closed = False # closed by an explicit close() self._prepared: PrepareManager = PrepareManager() - self._tpc: Optional[Tuple[Xid, bool]] = None # xid, prepared + self._tpc: Tuple[Xid, bool] | None = None # xid, prepared wself = ref(self) pgconn.notice_handler = partial(BaseConnection._notice_handler, wself) @@ -122,16 +124,16 @@ class BaseConnection(Generic[Row]): # Attribute is only set if the connection is from a pool so we can tell # apart a connection in the pool too (when _pool = None) - self._pool: Optional["BasePool"] + self._pool: "BasePool" | None - self._pipeline: Optional[BasePipeline] = None + self._pipeline: BasePipeline | None = None # Time after which the connection should be closed self._expire_at: float - self._isolation_level: Optional[IsolationLevel] = None - self._read_only: Optional[bool] = None - self._deferrable: Optional[bool] = None + self._isolation_level: IsolationLevel | None = None + self._read_only: bool | None = None + self._deferrable: bool | None = None self._begin_statement = b"" def __del__(self) -> None: @@ -190,58 +192,58 @@ class BaseConnection(Generic[Row]): self._autocommit = bool(value) @property - def isolation_level(self) -> Optional[IsolationLevel]: + def isolation_level(self) -> IsolationLevel | None: """ The isolation level of the new transactions started on the connection. """ return self._isolation_level @isolation_level.setter - def isolation_level(self, value: Optional[IsolationLevel]) -> None: + def isolation_level(self, value: IsolationLevel | None) -> None: self._set_isolation_level(value) - def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None: + def _set_isolation_level(self, value: IsolationLevel | None) -> None: raise NotImplementedError - def _set_isolation_level_gen(self, value: Optional[IsolationLevel]) -> PQGen[None]: + def _set_isolation_level_gen(self, value: IsolationLevel | None) -> PQGen[None]: yield from self._check_intrans_gen("isolation_level") self._isolation_level = IsolationLevel(value) if value is not None else None self._begin_statement = b"" @property - def read_only(self) -> Optional[bool]: + def read_only(self) -> bool | None: """ The read-only state of the new transactions started on the connection. """ return self._read_only @read_only.setter - def read_only(self, value: Optional[bool]) -> None: + def read_only(self, value: bool | None) -> None: self._set_read_only(value) - def _set_read_only(self, value: Optional[bool]) -> None: + def _set_read_only(self, value: bool | None) -> None: raise NotImplementedError - def _set_read_only_gen(self, value: Optional[bool]) -> PQGen[None]: + def _set_read_only_gen(self, value: bool | None) -> PQGen[None]: yield from self._check_intrans_gen("read_only") self._read_only = bool(value) if value is not None else None self._begin_statement = b"" @property - def deferrable(self) -> Optional[bool]: + def deferrable(self) -> bool | None: """ The deferrable state of the new transactions started on the connection. """ return self._deferrable @deferrable.setter - def deferrable(self, value: Optional[bool]) -> None: + def deferrable(self, value: bool | None) -> None: self._set_deferrable(value) - def _set_deferrable(self, value: Optional[bool]) -> None: + def _set_deferrable(self, value: bool | None) -> None: raise NotImplementedError - def _set_deferrable_gen(self, value: Optional[bool]) -> PQGen[None]: + def _set_deferrable_gen(self, value: bool | None) -> PQGen[None]: yield from self._check_intrans_gen("deferrable") self._deferrable = bool(value) if value is not None else None self._begin_statement = b"" @@ -382,7 +384,7 @@ class BaseConnection(Generic[Row]): cb(n) @property - def prepare_threshold(self) -> Optional[int]: + def prepare_threshold(self) -> int | None: """ Number of times a query is executed before it is prepared. @@ -396,11 +398,11 @@ class BaseConnection(Generic[Row]): return self._prepared.prepare_threshold @prepare_threshold.setter - def prepare_threshold(self, value: Optional[int]) -> None: + def prepare_threshold(self, value: int | None) -> None: self._prepared.prepare_threshold = value @property - def prepared_max(self) -> Optional[int]: + def prepared_max(self) -> int | None: """ Maximum number of prepared statements on the connection. @@ -411,7 +413,7 @@ class BaseConnection(Generic[Row]): return rv if rv != sys.maxsize else None @prepared_max.setter - def prepared_max(self, value: Optional[int]) -> None: + def prepared_max(self, value: int | None) -> None: if value is None: value = sys.maxsize self._prepared.prepared_max = value @@ -437,7 +439,7 @@ class BaseConnection(Generic[Row]): def _exec_command( self, command: Query, result_format: pq.Format = TEXT - ) -> PQGen[Optional["PGresult"]]: + ) -> PQGen["PGresult" | None]: """ Generator to send a command and receive the result to the backend. @@ -481,7 +483,7 @@ class BaseConnection(Generic[Row]): ) return result - def _deallocate(self, name: Optional[bytes]) -> PQGen[None]: + def _deallocate(self, name: bytes | None) -> PQGen[None]: """ Deallocate one, or all, prepared statement in the session. diff --git a/psycopg/psycopg/_copy_base.py b/psycopg/psycopg/_copy_base.py index 47a028bf0..3d239ad50 100644 --- a/psycopg/psycopg/_copy_base.py +++ b/psycopg/psycopg/_copy_base.py @@ -11,7 +11,7 @@ import sys import struct from abc import ABC, abstractmethod from typing import Any, Dict, Generic, List, Match -from typing import Optional, Sequence, Tuple, Union, TYPE_CHECKING +from typing import Sequence, Tuple, Union, TYPE_CHECKING from . import pq from . import adapt @@ -80,7 +80,7 @@ class BaseCopy(Generic[ConnectionType]): self, cursor: "BaseCursor[ConnectionType, Any]", *, - binary: Optional[bool] = None, + binary: bool | None = None, ): self.cursor = cursor self.connection = cursor.connection @@ -165,7 +165,7 @@ class BaseCopy(Generic[ConnectionType]): self.cursor._rowcount = nrows if nrows is not None else -1 return memoryview(b"") - def _read_row_gen(self) -> PQGen[Optional[Tuple[Any, ...]]]: + def _read_row_gen(self) -> PQGen[Tuple[Any, ...] | None]: data = yield from self._read_gen() if not data: return None @@ -200,7 +200,7 @@ class Formatter(ABC): self._row_mode = False # true if the user is using write_row() @abstractmethod - def parse_row(self, data: Buffer) -> Optional[Tuple[Any, ...]]: ... + def parse_row(self, data: Buffer) -> Tuple[Any, ...] | None: ... @abstractmethod def write(self, buffer: Union[Buffer, str]) -> Buffer: ... @@ -219,7 +219,7 @@ class TextFormatter(Formatter): super().__init__(transformer) self._encoding = encoding - def parse_row(self, data: Buffer) -> Optional[Tuple[Any, ...]]: + def parse_row(self, data: Buffer) -> Tuple[Any, ...] | None: if data: return parse_row_text(data, self.transformer) else: @@ -263,7 +263,7 @@ class BinaryFormatter(Formatter): super().__init__(transformer) self._signature_sent = False - def parse_row(self, data: Buffer) -> Optional[Tuple[Any, ...]]: + def parse_row(self, data: Buffer) -> Tuple[Any, ...] | None: if not self._signature_sent: if data[: len(_binary_signature)] != _binary_signature: raise e.DataError( @@ -328,7 +328,7 @@ class BinaryFormatter(Formatter): def _format_row_text( - row: Sequence[Any], tx: Transformer, out: Optional[bytearray] = None + row: Sequence[Any], tx: Transformer, out: bytearray | None = None ) -> bytearray: """Convert a row of objects to the data to send for copy.""" if out is None: @@ -348,7 +348,7 @@ def _format_row_text( def _format_row_binary( - row: Sequence[Any], tx: Transformer, out: Optional[bytearray] = None + row: Sequence[Any], tx: Transformer, out: bytearray | None = None ) -> bytearray: """Convert a row of objects to the data to send for binary copy.""" if out is None: @@ -376,7 +376,7 @@ def _parse_row_text(data: Buffer, tx: Transformer) -> Tuple[Any, ...]: def _parse_row_binary(data: Buffer, tx: Transformer) -> Tuple[Any, ...]: - row: List[Optional[Buffer]] = [] + row: List[Buffer | None] = [] nfields = _unpack_int2(data, 0)[0] pos = 2 for i in range(nfields): diff --git a/psycopg/psycopg/_cursor_base.py b/psycopg/psycopg/_cursor_base.py index 24a2cb1a2..587b82190 100644 --- a/psycopg/psycopg/_cursor_base.py +++ b/psycopg/psycopg/_cursor_base.py @@ -4,9 +4,11 @@ Psycopg BaseCursor object # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from functools import partial from typing import Any, Generic, Iterable, List -from typing import Optional, NoReturn, Sequence, Tuple, Type +from typing import NoReturn, Sequence, Tuple, Type from typing import TYPE_CHECKING from . import pq @@ -63,18 +65,18 @@ class BaseCursor(Generic[ConnectionType, Row]): self._adapters = adapt.AdaptersMap(connection.adapters) self.arraysize = 1 self._closed = False - self._last_query: Optional[Query] = None + self._last_query: Query | None = None self._reset() def _reset(self, reset_query: bool = True) -> None: self._results: List["PGresult"] = [] - self.pgresult: Optional["PGresult"] = None + self.pgresult: "PGresult" | None = None self._pos = 0 self._iresult = 0 self._rowcount = -1 - self._query: Optional[PostgresQuery] + self._query: PostgresQuery | None # None if executemany() not executing, True/False according to returning state - self._execmany_returning: Optional[bool] = None + self._execmany_returning: bool | None = None if reset_query: self._query = None @@ -104,7 +106,7 @@ class BaseCursor(Generic[ConnectionType, Row]): return self._closed @property - def description(self) -> Optional[List[Column]]: + def description(self) -> List[Column] | None: """ A list of `Column` objects describing the current resultset. @@ -128,7 +130,7 @@ class BaseCursor(Generic[ConnectionType, Row]): return self._rowcount @property - def rownumber(self) -> Optional[int]: + def rownumber(self) -> int | None: """Index of the next row to fetch in the current result. `!None` if there is no result to fetch. @@ -140,11 +142,11 @@ class BaseCursor(Generic[ConnectionType, Row]): # no-op pass - def setoutputsize(self, size: Any, column: Optional[int] = None) -> None: + def setoutputsize(self, size: Any, column: int | None = None) -> None: # no-op pass - def nextset(self) -> Optional[bool]: + def nextset(self) -> bool | None: """ Move to the result set of the next query executed through `executemany()` or to the next result set if `execute()` returned more than one. @@ -159,7 +161,7 @@ class BaseCursor(Generic[ConnectionType, Row]): return None @property - def statusmessage(self) -> Optional[str]: + def statusmessage(self) -> str | None: """ The command status tag from the last SQL command executed. @@ -182,10 +184,10 @@ class BaseCursor(Generic[ConnectionType, Row]): def _execute_gen( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - prepare: Optional[bool] = None, - binary: Optional[bool] = None, + prepare: bool | None = None, + binary: bool | None = None, ) -> PQGen[None]: """Generator implementing `Cursor.execute()`.""" yield from self._start_query(query) @@ -263,8 +265,8 @@ class BaseCursor(Generic[ConnectionType, Row]): self, pgq: PostgresQuery, *, - prepare: Optional[bool] = None, - binary: Optional[bool] = None, + prepare: bool | None = None, + binary: bool | None = None, ) -> PQGen[None]: # Check if the query is prepared or needs preparing prep, name = self._get_prepared(pgq, prepare) @@ -304,16 +306,16 @@ class BaseCursor(Generic[ConnectionType, Row]): self._set_results(results) def _get_prepared( - self, pgq: PostgresQuery, prepare: Optional[bool] = None + self, pgq: PostgresQuery, prepare: bool | None = None ) -> Tuple[Prepare, bytes]: return self._conn._prepared.get(pgq, prepare) def _stream_send_gen( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - binary: Optional[bool] = None, + binary: bool | None = None, ) -> PQGen[None]: """Generator to send the query for `Cursor.stream()`.""" yield from self._start_query(query) @@ -323,7 +325,7 @@ class BaseCursor(Generic[ConnectionType, Row]): self._last_query = query yield from send(self._pgconn) - def _stream_fetchone_gen(self, first: bool) -> PQGen[Optional["PGresult"]]: + def _stream_fetchone_gen(self, first: bool) -> PQGen["PGresult" | None]: res = yield from fetch(self._pgconn) if res is None: return None @@ -350,7 +352,7 @@ class BaseCursor(Generic[ConnectionType, Row]): # Errors, unexpected values return self._raise_for_result(res) - def _start_query(self, query: Optional[Query] = None) -> PQGen[None]: + def _start_query(self, query: Query | None = None) -> PQGen[None]: """Generator to start the processing of a query. It is implemented as generator because it may send additional queries, @@ -366,7 +368,7 @@ class BaseCursor(Generic[ConnectionType, Row]): yield from self._conn._start_query() def _start_copy_gen( - self, statement: Query, params: Optional[Params] = None + self, statement: Query, params: Params | None = None ) -> PQGen[None]: """Generator implementing sending a command for `Cursor.copy().""" @@ -398,7 +400,7 @@ class BaseCursor(Generic[ConnectionType, Row]): query: PostgresQuery, *, force_extended: bool = False, - binary: Optional[bool] = None, + binary: bool | None = None, ) -> None: """ Implement part of execute() before waiting common to sync and async. @@ -439,7 +441,7 @@ class BaseCursor(Generic[ConnectionType, Row]): self._pgconn.send_query(query.query) def _convert_query( - self, query: Query, params: Optional[Params] = None + self, query: Query, params: Params | None = None ) -> PostgresQuery: pgq = self._query_cls(self._tx) pgq.convert(query, params) @@ -478,9 +480,7 @@ class BaseCursor(Generic[ConnectionType, Row]): "unexpected result status from query:" f" {pq.ExecStatus(status).name}" ) - def _select_current_result( - self, i: int, format: Optional[pq.Format] = None - ) -> None: + def _select_current_result(self, i: int, format: pq.Format | None = None) -> None: """ Select one of the results in the cursor as the active one. """ @@ -540,7 +540,7 @@ class BaseCursor(Generic[ConnectionType, Row]): self._pgconn.send_prepare(name, query.query, param_types=query.types) def _send_query_prepared( - self, name: bytes, pgq: PostgresQuery, *, binary: Optional[bool] = None + self, name: bytes, pgq: PostgresQuery, *, binary: bool | None = None ) -> None: if binary is None: fmt = self.format diff --git a/psycopg/psycopg/_dns.py b/psycopg/psycopg/_dns.py index a9619b56d..0701de482 100644 --- a/psycopg/psycopg/_dns.py +++ b/psycopg/psycopg/_dns.py @@ -5,11 +5,13 @@ DNS query support # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import os import re import warnings from random import randint -from typing import Any, DefaultDict, Dict, List, NamedTuple, Optional, Sequence +from typing import Any, DefaultDict, Dict, List, NamedTuple, Sequence from typing import TYPE_CHECKING from collections import defaultdict @@ -88,7 +90,7 @@ class HostPort(NamedTuple): host: str port: str totry: bool = False - target: Optional[str] = None + target: str | None = None class Rfc2782Resolver: diff --git a/psycopg/psycopg/_encodings.py b/psycopg/psycopg/_encodings.py index 4949e26c6..cc2981a21 100644 --- a/psycopg/psycopg/_encodings.py +++ b/psycopg/psycopg/_encodings.py @@ -4,10 +4,12 @@ Mappings between PostgreSQL and Python encodings. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re import string import codecs -from typing import Any, Dict, Optional, TYPE_CHECKING +from typing import Any, Dict, TYPE_CHECKING from .pq._enums import ConnStatus from .errors import NotSupportedError @@ -78,7 +80,7 @@ py_codecs.update( pg_codecs = {v: k.encode() for k, v in _py_codecs.items()} -def conn_encoding(conn: "Optional[BaseConnection[Any]]") -> str: +def conn_encoding(conn: "BaseConnection[Any] | None") -> str: """ Return the Python encoding name of a psycopg connection. diff --git a/psycopg/psycopg/_pipeline.py b/psycopg/psycopg/_pipeline.py index 0c4671e2b..268872234 100644 --- a/psycopg/psycopg/_pipeline.py +++ b/psycopg/psycopg/_pipeline.py @@ -4,9 +4,11 @@ commands pipeline management # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import logging from types import TracebackType -from typing import Any, List, Optional, Union, Tuple, Type, TYPE_CHECKING +from typing import Any, List, Union, Tuple, Type, TYPE_CHECKING from . import pq from . import errors as e @@ -27,7 +29,7 @@ if TYPE_CHECKING: PendingResult: TypeAlias = Union[ - None, Tuple["BaseCursor[Any, Any]", Optional[Tuple[Key, Prepare, bytes]]] + None, Tuple["BaseCursor[Any, Any]", Tuple[Key, Prepare, bytes] | None] ] FATAL_ERROR = pq.ExecStatus.FATAL_ERROR @@ -78,7 +80,7 @@ class BasePipeline: yield from self._sync_gen() self.level += 1 - def _exit(self, exc: Optional[BaseException]) -> None: + def _exit(self, exc: BaseException | None) -> None: self.level -= 1 if self.level == 0 and self.pgconn.status != BAD: try: @@ -212,9 +214,9 @@ class Pipeline(BasePipeline): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: try: with self._conn.lock: @@ -252,9 +254,9 @@ class AsyncPipeline(BasePipeline): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: try: async with self._conn.lock: diff --git a/psycopg/psycopg/_preparing.py b/psycopg/psycopg/_preparing.py index 92385c029..9c017e73e 100644 --- a/psycopg/psycopg/_preparing.py +++ b/psycopg/psycopg/_preparing.py @@ -4,8 +4,10 @@ Support for prepared statements # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from enum import IntEnum, auto -from typing import Optional, Sequence, Tuple, TYPE_CHECKING +from typing import Sequence, Tuple, TYPE_CHECKING from collections import OrderedDict from . import pq @@ -32,7 +34,7 @@ class Prepare(IntEnum): class PrepareManager: # Number of times a query is executed before it is prepared. - prepare_threshold: Optional[int] = 5 + prepare_threshold: int | None = 5 # Maximum number of prepared statements on the connection. prepared_max: int = 100 @@ -47,14 +49,14 @@ class PrepareManager: # Counter to generate prepared statements names self._prepared_idx = 0 - self._to_flush = Deque[Optional[bytes]]() + self._to_flush = Deque[bytes | None]() @staticmethod def key(query: PostgresQuery) -> Key: return (query.query, query.types) def get( - self, query: PostgresQuery, prepare: Optional[bool] = None + self, query: PostgresQuery, prepare: bool | None = None ) -> Tuple[Prepare, bytes]: """ Check if a query is prepared, tell back whether to prepare it. @@ -122,7 +124,7 @@ class PrepareManager: def maybe_add_to_cache( self, query: PostgresQuery, prep: Prepare, name: bytes - ) -> Optional[Key]: + ) -> Key | None: """Handle 'query' for possible addition to the cache. If a new entry has been added, return its key. Return None otherwise diff --git a/psycopg/psycopg/_py_transformer.py b/psycopg/psycopg/_py_transformer.py index dd7f54759..6c3b8550e 100644 --- a/psycopg/psycopg/_py_transformer.py +++ b/psycopg/psycopg/_py_transformer.py @@ -9,7 +9,9 @@ dependencies problems). # Copyright (C) 2020 The Psycopg Team -from typing import Any, Dict, List, Optional, Sequence, Tuple +from __future__ import annotations + +from typing import Any, Dict, List, Sequence, Tuple from typing import DefaultDict, TYPE_CHECKING from collections import defaultdict @@ -54,14 +56,14 @@ class Transformer(AdaptContext): _oid_dumpers _oid_types _row_dumpers _row_loaders """.split() - types: Optional[Tuple[int, ...]] - formats: Optional[List[pq.Format]] + types: Tuple[int, ...] | None + formats: List[pq.Format] | None _adapters: "AdaptersMap" - _pgresult: Optional["PGresult"] + _pgresult: "PGresult" | None _none_oid: int - def __init__(self, context: Optional[AdaptContext] = None): + def __init__(self, context: AdaptContext | None = None): self._pgresult = self.types = self.formats = None # WARNING: don't store context, or you'll create a loop with the Cursor @@ -80,13 +82,13 @@ class Transformer(AdaptContext): # mapping fmt, oid -> Dumper instance # Not often used, so create it only if needed. - self._oid_dumpers: Optional[Tuple[OidDumperCache, OidDumperCache]] + self._oid_dumpers: Tuple[OidDumperCache, OidDumperCache] | None self._oid_dumpers = None # mapping fmt, oid -> Loader instance self._loaders: Tuple[LoaderCache, LoaderCache] = ({}, {}) - self._row_dumpers: Optional[List[abc.Dumper]] = None + self._row_dumpers: List[abc.Dumper] | None = None # sequence of load functions from value to python # the length of the result columns @@ -98,7 +100,7 @@ class Transformer(AdaptContext): self._encoding = "" @classmethod - def from_context(cls, context: Optional[AdaptContext]) -> "Transformer": + def from_context(cls, context: AdaptContext | None) -> "Transformer": """ Return a Transformer from an AdaptContext. @@ -110,7 +112,7 @@ class Transformer(AdaptContext): return cls(context) @property - def connection(self) -> Optional["BaseConnection[Any]"]: + def connection(self) -> "BaseConnection[Any]" | None: return self._conn @property @@ -124,15 +126,15 @@ class Transformer(AdaptContext): return self._adapters @property - def pgresult(self) -> Optional["PGresult"]: + def pgresult(self) -> "PGresult" | None: return self._pgresult def set_pgresult( self, - result: Optional["PGresult"], + result: "PGresult" | None, *, set_loaders: bool = True, - format: Optional[pq.Format] = None, + format: pq.Format | None = None, ) -> None: self._pgresult = result @@ -168,9 +170,9 @@ class Transformer(AdaptContext): def dump_sequence( self, params: Sequence[Any], formats: Sequence[PyFormat] - ) -> Sequence[Optional[Buffer]]: + ) -> Sequence[Buffer | None]: nparams = len(params) - out: List[Optional[Buffer]] = [None] * nparams + out: List[Buffer | None] = [None] * nparams # If we have dumpers, it means set_dumper_types had been called, in # which case self.types and self.formats are set to sequences of the @@ -316,7 +318,7 @@ class Transformer(AdaptContext): return records - def load_row(self, row: int, make_row: RowMaker[Row]) -> Optional[Row]: + def load_row(self, row: int, make_row: RowMaker[Row]) -> Row | None: res = self._pgresult if not res: return None @@ -332,7 +334,7 @@ class Transformer(AdaptContext): return make_row(record) - def load_sequence(self, record: Sequence[Optional[Buffer]]) -> Tuple[Any, ...]: + def load_sequence(self, record: Sequence[Buffer | None]) -> Tuple[Any, ...]: if len(self._row_loaders) != len(record): raise e.ProgrammingError( f"cannot load sequence of {len(record)} items:" diff --git a/psycopg/psycopg/_queries.py b/psycopg/psycopg/_queries.py index 947b51a9e..b868d51d0 100644 --- a/psycopg/psycopg/_queries.py +++ b/psycopg/psycopg/_queries.py @@ -4,8 +4,10 @@ Utility module to manipulate queries # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re -from typing import Any, Callable, Dict, List, Mapping, Match, NamedTuple, Optional +from typing import Any, Callable, Dict, List, Mapping, Match, NamedTuple from typing import Sequence, Tuple, Union, TYPE_CHECKING from functools import lru_cache @@ -43,20 +45,20 @@ class PostgresQuery: def __init__(self, transformer: "Transformer"): self._tx = transformer - self.params: Optional[Sequence[Optional[Buffer]]] = None + self.params: Sequence[Buffer | None] | None = None # these are tuples so they can be used as keys e.g. in prepared stmts self.types: Tuple[int, ...] = () # The format requested by the user and the ones to really pass Postgres - self._want_formats: Optional[List[PyFormat]] = None - self.formats: Optional[Sequence[pq.Format]] = None + self._want_formats: List[PyFormat] | None = None + self.formats: Sequence[pq.Format] | None = None self._encoding = conn_encoding(transformer.connection) self._parts: List[QueryPart] self.query = b"" - self._order: Optional[List[str]] = None + self._order: List[str] | None = None - def convert(self, query: Query, vars: Optional[Params]) -> None: + def convert(self, query: Query, vars: Params | None) -> None: """ Set up the query and parameters to convert. @@ -93,7 +95,7 @@ class PostgresQuery: self.dump(vars) - def dump(self, vars: Optional[Params]) -> None: + def dump(self, vars: Params | None) -> None: """ Process a new set of variables on the query processed by `convert()`. @@ -131,7 +133,7 @@ class PostgresQuery: @staticmethod def validate_and_reorder_params( - parts: List[QueryPart], vars: Params, order: Optional[List[str]] + parts: List[QueryPart], vars: Params, order: List[str] | None ) -> Sequence[Any]: """ Verify the compatibility between a query and a set of params. @@ -167,13 +169,13 @@ class PostgresQuery: # The type of the _query2pg() and _query2pg_nocache() methods _Query2Pg: TypeAlias = Callable[ - [bytes, str], Tuple[bytes, List[PyFormat], Optional[List[str]], List[QueryPart]] + [bytes, str], Tuple[bytes, List[PyFormat], List[str] | None, List[QueryPart]] ] def _query2pg_nocache( query: bytes, encoding: str -) -> Tuple[bytes, List[PyFormat], Optional[List[str]], List[QueryPart]]: +) -> Tuple[bytes, List[PyFormat], List[str] | None, List[QueryPart]]: """ Convert Python query and params into something Postgres understands. @@ -185,7 +187,7 @@ def _query2pg_nocache( ``parts`` (splits of queries and placeholders). """ parts = _split_query(query, encoding) - order: Optional[List[str]] = None + order: List[str] | None = None chunks: List[bytes] = [] formats = [] @@ -236,7 +238,7 @@ class PostgresClientQuery(PostgresQuery): __slots__ = ("template",) - def convert(self, query: Query, vars: Optional[Params]) -> None: + def convert(self, query: Query, vars: Params | None) -> None: """ Set up the query and parameters to convert. @@ -266,7 +268,7 @@ class PostgresClientQuery(PostgresQuery): self.dump(vars) - def dump(self, vars: Optional[Params]) -> None: + def dump(self, vars: Params | None) -> None: """ Process a new set of variables on the query processed by `convert()`. @@ -283,18 +285,18 @@ class PostgresClientQuery(PostgresQuery): _Query2PgClient: TypeAlias = Callable[ - [bytes, str], Tuple[bytes, Optional[List[str]], List[QueryPart]] + [bytes, str], Tuple[bytes, List[str] | None, List[QueryPart]] ] def _query2pg_client_nocache( query: bytes, encoding: str -) -> Tuple[bytes, Optional[List[str]], List[QueryPart]]: +) -> Tuple[bytes, List[str] | None, List[QueryPart]]: """ Convert Python query and params into a template to perform client-side binding """ parts = _split_query(query, encoding, collapse_double_percent=False) - order: Optional[List[str]] = None + order: List[str] | None = None chunks: List[bytes] = [] if isinstance(parts[0].item, int): @@ -345,7 +347,7 @@ _re_placeholder = re.compile( def _split_query( query: bytes, encoding: str = "ascii", collapse_double_percent: bool = True ) -> List[QueryPart]: - parts: List[Tuple[bytes, Optional[Match[bytes]]]] = [] + parts: List[Tuple[bytes, Match[bytes] | None]] = [] cur = 0 # pairs [(fragment, match], with the last match None diff --git a/psycopg/psycopg/_struct.py b/psycopg/psycopg/_struct.py index 7232a20bd..6d8da5a12 100644 --- a/psycopg/psycopg/_struct.py +++ b/psycopg/psycopg/_struct.py @@ -4,8 +4,10 @@ Utility functions to deal with binary structs. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import struct -from typing import Callable, cast, Optional, Protocol, Tuple +from typing import Callable, cast, Protocol, Tuple from . import errors as e from .abc import Buffer @@ -18,7 +20,7 @@ UnpackFloat: TypeAlias = Callable[[Buffer], Tuple[float]] class UnpackLen(Protocol): - def __call__(self, data: Buffer, start: Optional[int]) -> Tuple[int]: ... + def __call__(self, data: Buffer, start: int | None) -> Tuple[int]: ... pack_int2 = cast(PackInt, struct.Struct("!h").pack) diff --git a/psycopg/psycopg/_tpc.py b/psycopg/psycopg/_tpc.py index 35281881c..839db6829 100644 --- a/psycopg/psycopg/_tpc.py +++ b/psycopg/psycopg/_tpc.py @@ -4,10 +4,12 @@ psycopg two-phase commit support # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import re import datetime as dt from base64 import b64encode, b64decode -from typing import Optional, Union +from typing import Union from dataclasses import dataclass, replace _re_xid = re.compile(r"^(\d+)_([^_]*)_([^_]*)$") @@ -22,12 +24,12 @@ class Xid: """ - format_id: Optional[int] + format_id: int | None gtrid: str - bqual: Optional[str] - prepared: Optional[dt.datetime] = None - owner: Optional[str] = None - database: Optional[str] = None + bqual: str | None + prepared: dt.datetime | None = None + owner: str | None = None + database: str | None = None @classmethod def from_string(cls, s: str) -> "Xid": @@ -61,9 +63,7 @@ class Xid: return cls.from_parts(format_id, gtrid, bqual) @classmethod - def from_parts( - cls, format_id: Optional[int], gtrid: str, bqual: Optional[str] - ) -> "Xid": + def from_parts(cls, format_id: int | None, gtrid: str, bqual: str | None) -> "Xid": if format_id is not None: if bqual is None: raise TypeError("if format_id is specified, bqual must be too") diff --git a/psycopg/psycopg/_tz.py b/psycopg/psycopg/_tz.py index 813ed6261..b7ae95739 100644 --- a/psycopg/psycopg/_tz.py +++ b/psycopg/psycopg/_tz.py @@ -4,8 +4,10 @@ Timezone utility functions. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import logging -from typing import Dict, Optional, Union +from typing import Dict, Union from datetime import timezone, tzinfo from .pq.abc import PGconn @@ -19,7 +21,7 @@ _timezones: Dict[Union[None, bytes], tzinfo] = { } -def get_tzinfo(pgconn: Optional[PGconn]) -> tzinfo: +def get_tzinfo(pgconn: PGconn | None) -> tzinfo: """Return the Python timezone info of the connection's timezone.""" tzname = pgconn.parameter_status(b"TimeZone") if pgconn else None try: diff --git a/psycopg/psycopg/abc.py b/psycopg/psycopg/abc.py index 11e10b161..74bc3f1be 100644 --- a/psycopg/psycopg/abc.py +++ b/psycopg/psycopg/abc.py @@ -4,8 +4,10 @@ Protocol objects representing different implementations of the same classes. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from typing import Any, Dict, Callable, Generator, Mapping -from typing import List, Optional, Protocol, Sequence, Tuple, Union +from typing import List, Protocol, Sequence, Tuple, Union from typing import TYPE_CHECKING from . import pq @@ -56,13 +58,13 @@ class WaitFunc(Protocol): """ def __call__( - self, gen: PQGen[RV], fileno: int, interval: Optional[float] = None + self, gen: PQGen[RV], fileno: int, interval: float | None = None ) -> RV: ... # Adaptation types -DumpFunc: TypeAlias = Callable[[Any], Optional[Buffer]] +DumpFunc: TypeAlias = Callable[[Any], Buffer | None] LoadFunc: TypeAlias = Callable[[Buffer], Any] @@ -84,7 +86,7 @@ class AdaptContext(Protocol): ... @property - def connection(self) -> Optional["BaseConnection[Any]"]: + def connection(self) -> "BaseConnection[Any]" | None: """The connection used by this object, if available. :rtype: `~psycopg.Connection` or `~psycopg.AsyncConnection` or `!None` @@ -108,9 +110,9 @@ class Dumper(Protocol): oid: int """The oid to pass to the server, if known; 0 otherwise (class attribute).""" - def __init__(self, cls: type, context: Optional[AdaptContext] = None): ... + def __init__(self, cls: type, context: AdaptContext | None = None): ... - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: """Convert the object `!obj` to PostgreSQL representation. :param obj: the object to convert. @@ -188,7 +190,7 @@ class Loader(Protocol): This is a class attribute. """ - def __init__(self, oid: int, context: Optional[AdaptContext] = None): ... + def __init__(self, oid: int, context: AdaptContext | None = None): ... def load(self, data: Buffer) -> Any: """ @@ -200,16 +202,16 @@ class Loader(Protocol): class Transformer(Protocol): - types: Optional[Tuple[int, ...]] - formats: Optional[List[pq.Format]] + types: Tuple[int, ...] | None + formats: List[pq.Format] | None - def __init__(self, context: Optional[AdaptContext] = None): ... + def __init__(self, context: AdaptContext | None = None): ... @classmethod - def from_context(cls, context: Optional[AdaptContext]) -> "Transformer": ... + def from_context(cls, context: AdaptContext | None) -> "Transformer": ... @property - def connection(self) -> Optional["BaseConnection[Any]"]: ... + def connection(self) -> "BaseConnection[Any]" | None: ... @property def encoding(self) -> str: ... @@ -218,14 +220,14 @@ class Transformer(Protocol): def adapters(self) -> "AdaptersMap": ... @property - def pgresult(self) -> Optional["PGresult"]: ... + def pgresult(self) -> "PGresult" | None: ... def set_pgresult( self, - result: Optional["PGresult"], + result: "PGresult" | None, *, set_loaders: bool = True, - format: Optional[pq.Format] = None + format: pq.Format | None = None, ) -> None: ... def set_dumper_types(self, types: Sequence[int], format: pq.Format) -> None: ... @@ -234,7 +236,7 @@ class Transformer(Protocol): def dump_sequence( self, params: Sequence[Any], formats: Sequence[PyFormat] - ) -> Sequence[Optional[Buffer]]: ... + ) -> Sequence[Buffer | None]: ... def as_literal(self, obj: Any) -> bytes: ... @@ -244,8 +246,8 @@ class Transformer(Protocol): self, row0: int, row1: int, make_row: "RowMaker[Row]" ) -> List["Row"]: ... - def load_row(self, row: int, make_row: "RowMaker[Row]") -> Optional["Row"]: ... + def load_row(self, row: int, make_row: "RowMaker[Row]") -> "Row" | None: ... - def load_sequence(self, record: Sequence[Optional[Buffer]]) -> Tuple[Any, ...]: ... + def load_sequence(self, record: Sequence[Buffer | None]) -> Tuple[Any, ...]: ... def get_loader(self, oid: int, format: pq.Format) -> Loader: ... diff --git a/psycopg/psycopg/adapt.py b/psycopg/psycopg/adapt.py index 07b7b9a5b..fa590332c 100644 --- a/psycopg/psycopg/adapt.py +++ b/psycopg/psycopg/adapt.py @@ -4,8 +4,10 @@ Entry point into the adaptation system. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from abc import ABC, abstractmethod -from typing import Any, Optional, TYPE_CHECKING +from typing import Any, TYPE_CHECKING from . import pq, abc @@ -33,11 +35,10 @@ class Dumper(abc.Dumper, ABC): format: pq.Format = pq.Format.TEXT """The format of the data dumped.""" - def __init__(self, cls: type, context: Optional[abc.AdaptContext] = None): + def __init__(self, cls: type, context: abc.AdaptContext | None = None): self.cls = cls - self.connection: Optional["BaseConnection[Any]"] = ( - context.connection if context else None - ) + self.connection: "BaseConnection[Any]" | None + self.connection = context.connection if context else None def __repr__(self) -> str: return ( @@ -46,7 +47,7 @@ class Dumper(abc.Dumper, ABC): ) @abstractmethod - def dump(self, obj: Any) -> Optional[Buffer]: ... + def dump(self, obj: Any) -> Buffer | None: ... def quote(self, obj: Any) -> Buffer: """ @@ -125,11 +126,10 @@ class Loader(abc.Loader, ABC): format: pq.Format = pq.Format.TEXT """The format of the data loaded.""" - def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): + def __init__(self, oid: int, context: abc.AdaptContext | None = None): self.oid = oid - self.connection: Optional["BaseConnection[Any]"] = ( - context.connection if context else None - ) + self.connection: "BaseConnection[Any]" | None + self.connection = context.connection if context else None @abstractmethod def load(self, data: Buffer) -> Any: @@ -140,7 +140,7 @@ class Loader(abc.Loader, ABC): class RecursiveDumper(Dumper): """Dumper with a transformer to help dumping recursive types.""" - def __init__(self, cls: type, context: Optional[abc.AdaptContext] = None): + def __init__(self, cls: type, context: abc.AdaptContext | None = None): super().__init__(cls, context) self._tx = Transformer.from_context(context) @@ -148,6 +148,6 @@ class RecursiveDumper(Dumper): class RecursiveLoader(Loader): """Loader with a transformer to help loading recursive types.""" - def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): + def __init__(self, oid: int, context: abc.AdaptContext | None = None): super().__init__(oid, context) self._tx = Transformer.from_context(context) diff --git a/psycopg/psycopg/client_cursor.py b/psycopg/psycopg/client_cursor.py index 24d7b45cb..48630758b 100644 --- a/psycopg/psycopg/client_cursor.py +++ b/psycopg/psycopg/client_cursor.py @@ -4,7 +4,9 @@ psycopg client-side binding cursors # Copyright (C) 2022 The Psycopg Team -from typing import Optional, Tuple, TYPE_CHECKING +from __future__ import annotations + +from typing import Tuple, TYPE_CHECKING from functools import partial from ._queries import PostgresQuery, PostgresClientQuery @@ -31,7 +33,7 @@ BINARY = pq.Format.BINARY class ClientCursorMixin(BaseCursor[ConnectionType, Row]): _query_cls = PostgresClientQuery - def mogrify(self, query: Query, params: Optional[Params] = None) -> str: + def mogrify(self, query: Query, params: Params | None = None) -> str: """ Return the query and parameters merged. @@ -48,7 +50,7 @@ class ClientCursorMixin(BaseCursor[ConnectionType, Row]): query: PostgresQuery, *, force_extended: bool = False, - binary: Optional[bool] = None, + binary: bool | None = None, ) -> None: if binary is None: fmt = self.format @@ -76,7 +78,7 @@ class ClientCursorMixin(BaseCursor[ConnectionType, Row]): self._pgconn.send_query(query.query) def _get_prepared( - self, pgq: PostgresQuery, prepare: Optional[bool] = None + self, pgq: PostgresQuery, prepare: bool | None = None ) -> Tuple[Prepare, bytes]: return (Prepare.NO, b"") diff --git a/psycopg/psycopg/connection.py b/psycopg/psycopg/connection.py index c2be01560..7adad16c4 100644 --- a/psycopg/psycopg/connection.py +++ b/psycopg/psycopg/connection.py @@ -12,7 +12,7 @@ from __future__ import annotations import logging from time import monotonic from types import TracebackType -from typing import Any, Generator, Iterator, List, Optional +from typing import Any, Generator, Iterator, List from typing import Type, Union, cast, overload, TYPE_CHECKING from contextlib import contextmanager @@ -65,7 +65,7 @@ class Connection(BaseConnection[Row]): cursor_factory: Type[Cursor[Row]] server_cursor_factory: Type[ServerCursor[Row]] row_factory: RowFactory[Row] - _pipeline: Optional[Pipeline] + _pipeline: Pipeline | None def __init__( self, @@ -84,10 +84,10 @@ class Connection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, - prepare_threshold: Optional[int] = 5, - context: Optional[AdaptContext] = None, - row_factory: Optional[RowFactory[Row]] = None, - cursor_factory: Optional[Type[Cursor[Row]]] = None, + prepare_threshold: int | None = 5, + context: AdaptContext | None = None, + row_factory: RowFactory[Row] | None = None, + cursor_factory: Type[Cursor[Row]] | None = None, **kwargs: ConnParam, ) -> Self: """ @@ -135,9 +135,9 @@ class Connection(BaseConnection[Row]): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: if self.closed: return @@ -185,7 +185,7 @@ class Connection(BaseConnection[Row]): name: str, *, binary: bool = False, - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ) -> ServerCursor[Row]: ... @@ -196,7 +196,7 @@ class Connection(BaseConnection[Row]): *, binary: bool = False, row_factory: RowFactory[CursorRow], - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ) -> ServerCursor[CursorRow]: ... @@ -205,8 +205,8 @@ class Connection(BaseConnection[Row]): name: str = "", *, binary: bool = False, - row_factory: Optional[RowFactory[Any]] = None, - scrollable: Optional[bool] = None, + row_factory: RowFactory[Any] | None = None, + scrollable: bool | None = None, withhold: bool = False, ) -> Union[Cursor[Any], ServerCursor[Any]]: """ @@ -237,9 +237,9 @@ class Connection(BaseConnection[Row]): def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - prepare: Optional[bool] = None, + prepare: bool | None = None, binary: bool = False, ) -> Cursor[Row]: """Execute a query and return a cursor to read its results.""" @@ -296,7 +296,7 @@ class Connection(BaseConnection[Row]): @contextmanager def transaction( - self, savepoint_name: Optional[str] = None, force_rollback: bool = False + self, savepoint_name: str | None = None, force_rollback: bool = False ) -> Iterator[Transaction]: """ Start a context block with a new transaction or nested transaction. @@ -316,7 +316,7 @@ class Connection(BaseConnection[Row]): yield tx def notifies( - self, *, timeout: Optional[float] = None, stop_after: Optional[int] = None + self, *, timeout: float | None = None, stop_after: int | None = None ) -> Generator[Notify, None, None]: """ Yield `Notify` objects as soon as they are received from the database. @@ -386,7 +386,7 @@ class Connection(BaseConnection[Row]): assert pipeline is self._pipeline self._pipeline = None - def wait(self, gen: PQGen[RV], interval: Optional[float] = _WAIT_INTERVAL) -> RV: + def wait(self, gen: PQGen[RV], interval: float | None = _WAIT_INTERVAL) -> RV: """ Consume a generator operating on the connection. @@ -414,26 +414,26 @@ class Connection(BaseConnection[Row]): with self.lock: self.wait(self._set_autocommit_gen(value)) - def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None: + def _set_isolation_level(self, value: IsolationLevel | None) -> None: self.set_isolation_level(value) - def set_isolation_level(self, value: Optional[IsolationLevel]) -> None: + def set_isolation_level(self, value: IsolationLevel | None) -> None: """Method version of the `~Connection.isolation_level` setter.""" with self.lock: self.wait(self._set_isolation_level_gen(value)) - def _set_read_only(self, value: Optional[bool]) -> None: + def _set_read_only(self, value: bool | None) -> None: self.set_read_only(value) - def set_read_only(self, value: Optional[bool]) -> None: + def set_read_only(self, value: bool | None) -> None: """Method version of the `~Connection.read_only` setter.""" with self.lock: self.wait(self._set_read_only_gen(value)) - def _set_deferrable(self, value: Optional[bool]) -> None: + def _set_deferrable(self, value: bool | None) -> None: self.set_deferrable(value) - def set_deferrable(self, value: Optional[bool]) -> None: + def set_deferrable(self, value: bool | None) -> None: """Method version of the `~Connection.deferrable` setter.""" with self.lock: self.wait(self._set_deferrable_gen(value)) diff --git a/psycopg/psycopg/connection_async.py b/psycopg/psycopg/connection_async.py index b0c8b753e..cf3f7230d 100644 --- a/psycopg/psycopg/connection_async.py +++ b/psycopg/psycopg/connection_async.py @@ -9,7 +9,7 @@ from __future__ import annotations import logging from time import monotonic from types import TracebackType -from typing import Any, AsyncGenerator, AsyncIterator, List, Optional +from typing import Any, AsyncGenerator, AsyncIterator, List from typing import Type, Union, cast, overload, TYPE_CHECKING from contextlib import asynccontextmanager @@ -71,7 +71,7 @@ class AsyncConnection(BaseConnection[Row]): cursor_factory: Type[AsyncCursor[Row]] server_cursor_factory: Type[AsyncServerCursor[Row]] row_factory: AsyncRowFactory[Row] - _pipeline: Optional[AsyncPipeline] + _pipeline: AsyncPipeline | None def __init__( self, @@ -90,10 +90,10 @@ class AsyncConnection(BaseConnection[Row]): conninfo: str = "", *, autocommit: bool = False, - prepare_threshold: Optional[int] = 5, - context: Optional[AdaptContext] = None, - row_factory: Optional[AsyncRowFactory[Row]] = None, - cursor_factory: Optional[Type[AsyncCursor[Row]]] = None, + prepare_threshold: int | None = 5, + context: AdaptContext | None = None, + row_factory: AsyncRowFactory[Row] | None = None, + cursor_factory: Type[AsyncCursor[Row]] | None = None, **kwargs: ConnParam, ) -> Self: """ @@ -151,9 +151,9 @@ class AsyncConnection(BaseConnection[Row]): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: if self.closed: return @@ -201,7 +201,7 @@ class AsyncConnection(BaseConnection[Row]): name: str, *, binary: bool = False, - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ) -> AsyncServerCursor[Row]: ... @@ -212,7 +212,7 @@ class AsyncConnection(BaseConnection[Row]): *, binary: bool = False, row_factory: AsyncRowFactory[CursorRow], - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ) -> AsyncServerCursor[CursorRow]: ... @@ -221,8 +221,8 @@ class AsyncConnection(BaseConnection[Row]): name: str = "", *, binary: bool = False, - row_factory: Optional[AsyncRowFactory[Any]] = None, - scrollable: Optional[bool] = None, + row_factory: AsyncRowFactory[Any] | None = None, + scrollable: bool | None = None, withhold: bool = False, ) -> Union[AsyncCursor[Any], AsyncServerCursor[Any]]: """ @@ -253,9 +253,9 @@ class AsyncConnection(BaseConnection[Row]): async def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - prepare: Optional[bool] = None, + prepare: bool | None = None, binary: bool = False, ) -> AsyncCursor[Row]: """Execute a query and return a cursor to read its results.""" @@ -316,7 +316,7 @@ class AsyncConnection(BaseConnection[Row]): @asynccontextmanager async def transaction( - self, savepoint_name: Optional[str] = None, force_rollback: bool = False + self, savepoint_name: str | None = None, force_rollback: bool = False ) -> AsyncIterator[AsyncTransaction]: """ Start a context block with a new transaction or nested transaction. @@ -336,7 +336,7 @@ class AsyncConnection(BaseConnection[Row]): yield tx async def notifies( - self, *, timeout: Optional[float] = None, stop_after: Optional[int] = None + self, *, timeout: float | None = None, stop_after: int | None = None ) -> AsyncGenerator[Notify, None]: """ Yield `Notify` objects as soon as they are received from the database. @@ -406,9 +406,7 @@ class AsyncConnection(BaseConnection[Row]): assert pipeline is self._pipeline self._pipeline = None - async def wait( - self, gen: PQGen[RV], interval: Optional[float] = _WAIT_INTERVAL - ) -> RV: + async def wait(self, gen: PQGen[RV], interval: float | None = _WAIT_INTERVAL) -> RV: """ Consume a generator operating on the connection. @@ -439,35 +437,35 @@ class AsyncConnection(BaseConnection[Row]): async with self.lock: await self.wait(self._set_autocommit_gen(value)) - def _set_isolation_level(self, value: Optional[IsolationLevel]) -> None: + def _set_isolation_level(self, value: IsolationLevel | None) -> None: if True: # ASYNC self._no_set_async("isolation_level") else: self.set_isolation_level(value) - async def set_isolation_level(self, value: Optional[IsolationLevel]) -> None: + async def set_isolation_level(self, value: IsolationLevel | None) -> None: """Method version of the `~Connection.isolation_level` setter.""" async with self.lock: await self.wait(self._set_isolation_level_gen(value)) - def _set_read_only(self, value: Optional[bool]) -> None: + def _set_read_only(self, value: bool | None) -> None: if True: # ASYNC self._no_set_async("read_only") else: self.set_read_only(value) - async def set_read_only(self, value: Optional[bool]) -> None: + async def set_read_only(self, value: bool | None) -> None: """Method version of the `~Connection.read_only` setter.""" async with self.lock: await self.wait(self._set_read_only_gen(value)) - def _set_deferrable(self, value: Optional[bool]) -> None: + def _set_deferrable(self, value: bool | None) -> None: if True: # ASYNC self._no_set_async("deferrable") else: self.set_deferrable(value) - async def set_deferrable(self, value: Optional[bool]) -> None: + async def set_deferrable(self, value: bool | None) -> None: """Method version of the `~Connection.deferrable` setter.""" async with self.lock: await self.wait(self._set_deferrable_gen(value)) diff --git a/psycopg/psycopg/crdb/connection.py b/psycopg/psycopg/crdb/connection.py index d4690045b..950b6d66e 100644 --- a/psycopg/psycopg/crdb/connection.py +++ b/psycopg/psycopg/crdb/connection.py @@ -4,8 +4,10 @@ CockroachDB-specific connections. # Copyright (C) 2022 The Psycopg Team +from __future__ import annotations + import re -from typing import Any, Optional, Union, TYPE_CHECKING +from typing import Any, Union, TYPE_CHECKING from .. import errors as e from ..rows import Row @@ -20,7 +22,7 @@ if TYPE_CHECKING: class _CrdbConnectionMixin: - _adapters: Optional[AdaptersMap] + _adapters: AdaptersMap | None pgconn: "PGconn" @classmethod @@ -97,7 +99,7 @@ class CrdbConnectionInfo(ConnectionInfo): return ver @classmethod - def parse_crdb_version(self, sver: str) -> Optional[int]: + def parse_crdb_version(self, sver: str) -> int | None: m = re.search(r"\bv(\d+)\.(\d+)\.(\d+)", sver) if not m: return None diff --git a/psycopg/psycopg/cursor.py b/psycopg/psycopg/cursor.py index 42d5d8e45..9ef66b852 100644 --- a/psycopg/psycopg/cursor.py +++ b/psycopg/psycopg/cursor.py @@ -10,7 +10,7 @@ Psycopg Cursor object. from __future__ import annotations from types import TracebackType -from typing import Any, Iterator, Iterable, List, Optional, Type +from typing import Any, Iterator, Iterable, List, Type from typing import TYPE_CHECKING, overload from contextlib import contextmanager @@ -42,10 +42,7 @@ class Cursor(BaseCursor["Connection[Any]", Row]): ): ... def __init__( - self, - connection: Connection[Any], - *, - row_factory: Optional[RowFactory[Row]] = None, + self, connection: Connection[Any], *, row_factory: RowFactory[Row] | None = None ): super().__init__(connection) self._row_factory = row_factory or connection.row_factory @@ -55,9 +52,9 @@ class Cursor(BaseCursor["Connection[Any]", Row]): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: self.close() @@ -84,10 +81,10 @@ class Cursor(BaseCursor["Connection[Any]", Row]): def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - prepare: Optional[bool] = None, - binary: Optional[bool] = None, + prepare: bool | None = None, + binary: bool | None = None, ) -> Self: """ Execute a query or command to the database. @@ -132,11 +129,7 @@ class Cursor(BaseCursor["Connection[Any]", Row]): raise ex.with_traceback(None) def stream( - self, - query: Query, - params: Optional[Params] = None, - *, - binary: Optional[bool] = None, + self, query: Query, params: Params | None = None, *, binary: bool | None = None ) -> Iterator[Row]: """ Iterate row-by-row on a result from the database. @@ -173,13 +166,13 @@ class Cursor(BaseCursor["Connection[Any]", Row]): except Exception: pass - def fetchone(self) -> Optional[Row]: + def fetchone(self) -> Row | None: """ Return the next record from the current recordset. Return `!None` the recordset is finished. - :rtype: Optional[Row], with Row defined by `row_factory` + :rtype: Row | None, with Row defined by `row_factory` """ self._fetch_pipeline() self._check_result_for_fetch() @@ -225,7 +218,7 @@ class Cursor(BaseCursor["Connection[Any]", Row]): self._fetch_pipeline() self._check_result_for_fetch() - def load(pos: int) -> Optional[Row]: + def load(pos: int) -> Row | None: return self._tx.load_row(pos, self._make_row) while True: @@ -253,9 +246,9 @@ class Cursor(BaseCursor["Connection[Any]", Row]): def copy( self, statement: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - writer: Optional[Writer] = None, + writer: Writer | None = None, ) -> Iterator[Copy]: """ Initiate a :sql:`COPY` operation and return an object to manage it. diff --git a/psycopg/psycopg/cursor_async.py b/psycopg/psycopg/cursor_async.py index 37a0d255f..12c066b6c 100644 --- a/psycopg/psycopg/cursor_async.py +++ b/psycopg/psycopg/cursor_async.py @@ -7,7 +7,7 @@ Psycopg AsyncCursor object. from __future__ import annotations from types import TracebackType -from typing import Any, AsyncIterator, Iterable, List, Optional, Type +from typing import Any, AsyncIterator, Iterable, List, Type from typing import TYPE_CHECKING, overload from contextlib import asynccontextmanager @@ -42,7 +42,7 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): self, connection: AsyncConnection[Any], *, - row_factory: Optional[AsyncRowFactory[Row]] = None, + row_factory: AsyncRowFactory[Row] | None = None, ): super().__init__(connection) self._row_factory = row_factory or connection.row_factory @@ -52,9 +52,9 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: await self.close() @@ -81,10 +81,10 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): async def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - prepare: Optional[bool] = None, - binary: Optional[bool] = None, + prepare: bool | None = None, + binary: bool | None = None, ) -> Self: """ Execute a query or command to the database. @@ -133,11 +133,7 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): raise ex.with_traceback(None) async def stream( - self, - query: Query, - params: Optional[Params] = None, - *, - binary: Optional[bool] = None, + self, query: Query, params: Params | None = None, *, binary: bool | None = None ) -> AsyncIterator[Row]: """ Iterate row-by-row on a result from the database. @@ -180,13 +176,13 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): except Exception: pass - async def fetchone(self) -> Optional[Row]: + async def fetchone(self) -> Row | None: """ Return the next record from the current recordset. Return `!None` the recordset is finished. - :rtype: Optional[Row], with Row defined by `row_factory` + :rtype: Row | None, with Row defined by `row_factory` """ await self._fetch_pipeline() self._check_result_for_fetch() @@ -234,7 +230,7 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): await self._fetch_pipeline() self._check_result_for_fetch() - def load(pos: int) -> Optional[Row]: + def load(pos: int) -> Row | None: return self._tx.load_row(pos, self._make_row) while True: @@ -262,9 +258,9 @@ class AsyncCursor(BaseCursor["AsyncConnection[Any]", Row]): async def copy( self, statement: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - writer: Optional[AsyncWriter] = None, + writer: AsyncWriter | None = None, ) -> AsyncIterator[AsyncCopy]: """ Initiate a :sql:`COPY` operation and return an object to manage it. diff --git a/psycopg/psycopg/dbapi20.py b/psycopg/psycopg/dbapi20.py index 919b0506c..2536f3bcd 100644 --- a/psycopg/psycopg/dbapi20.py +++ b/psycopg/psycopg/dbapi20.py @@ -4,10 +4,12 @@ Compatibility objects with DBAPI 2.0 # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import time import datetime as dt from math import floor -from typing import Any, Optional, Sequence, Union +from typing import Any, Sequence, Union from . import _oids from .abc import AdaptContext, Buffer @@ -76,7 +78,7 @@ class Binary: class BinaryBinaryDumper(BytesBinaryDumper): - def dump(self, obj: Union[Buffer, Binary]) -> Optional[Buffer]: + def dump(self, obj: Union[Buffer, Binary]) -> Buffer | None: if isinstance(obj, Binary): return super().dump(obj.obj) else: @@ -84,7 +86,7 @@ class BinaryBinaryDumper(BytesBinaryDumper): class BinaryTextDumper(BytesDumper): - def dump(self, obj: Union[Buffer, Binary]) -> Optional[Buffer]: + def dump(self, obj: Union[Buffer, Binary]) -> Buffer | None: if isinstance(obj, Binary): return super().dump(obj.obj) else: diff --git a/psycopg/psycopg/errors.py b/psycopg/psycopg/errors.py index 0f5218ba5..082976be9 100644 --- a/psycopg/psycopg/errors.py +++ b/psycopg/psycopg/errors.py @@ -18,8 +18,10 @@ DBAPI-defined Exceptions are defined in the following hierarchy:: # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from dataclasses import dataclass, field, fields -from typing import Any, Callable, Dict, List, NoReturn, Optional, Sequence, Tuple, Type +from typing import Any, Callable, Dict, List, NoReturn, Sequence, Tuple, Type from typing import Union, TYPE_CHECKING from asyncio import CancelledError @@ -30,7 +32,7 @@ from ._compat import TypeAlias, TypeGuard if TYPE_CHECKING: from .pq.misc import PGnotify, ConninfoOption -ErrorInfo: TypeAlias = Union[None, PGresult, Dict[int, Optional[bytes]]] +ErrorInfo: TypeAlias = Union[None, PGresult, Dict[int, bytes | None]] _sqlcodes: Dict[str, "Type[Error]"] = {} @@ -67,8 +69,8 @@ class FinishedPGconn: nonblocking: int = 0 - notice_handler: Optional[Callable[["PGresult"], None]] = None - notify_handler: Optional[Callable[["PGnotify"], None]] = None + notice_handler: Callable[["PGresult"], None] | None = None + notify_handler: Callable[["PGnotify"], None] | None = None @staticmethod def _raise() -> NoReturn: @@ -256,14 +258,14 @@ class Error(Exception): __module__ = "psycopg" - sqlstate: Optional[str] = None + sqlstate: str | None = None def __init__( self, *args: Sequence[Any], info: ErrorInfo = None, encoding: str = "utf-8", - pgconn: Optional[PGconn] = None, + pgconn: PGconn | None = None, ): super().__init__(*args) self._info = info @@ -275,18 +277,18 @@ class Error(Exception): self.sqlstate = self.diag.sqlstate @property - def pgconn(self) -> Optional[PGconn]: + def pgconn(self) -> PGconn | None: """The connection object, if the error was raised from a connection attempt. - :rtype: Optional[psycopg.pq.PGconn] + :rtype: psycopg.pq.PGconn | None """ return self._pgconn if self._pgconn else None @property - def pgresult(self) -> Optional[PGresult]: + def pgresult(self) -> PGresult | None: """The result object, if the exception was raised after a failed query. - :rtype: Optional[psycopg.pq.PGresult] + :rtype: psycopg.pq.PGresult | None """ return self._info if _is_pgresult(self._info) else None @@ -322,7 +324,7 @@ class DatabaseError(Error): __module__ = "psycopg" - def __init_subclass__(cls, code: Optional[str] = None, name: Optional[str] = None): + def __init_subclass__(cls, code: str | None = None, name: str | None = None): if code: _sqlcodes[code] = cls cls.sqlstate = code @@ -429,78 +431,78 @@ class Diagnostic: self._encoding = encoding @property - def severity(self) -> Optional[str]: + def severity(self) -> str | None: return self._error_message(DiagnosticField.SEVERITY) @property - def severity_nonlocalized(self) -> Optional[str]: + def severity_nonlocalized(self) -> str | None: return self._error_message(DiagnosticField.SEVERITY_NONLOCALIZED) @property - def sqlstate(self) -> Optional[str]: + def sqlstate(self) -> str | None: return self._error_message(DiagnosticField.SQLSTATE) @property - def message_primary(self) -> Optional[str]: + def message_primary(self) -> str | None: return self._error_message(DiagnosticField.MESSAGE_PRIMARY) @property - def message_detail(self) -> Optional[str]: + def message_detail(self) -> str | None: return self._error_message(DiagnosticField.MESSAGE_DETAIL) @property - def message_hint(self) -> Optional[str]: + def message_hint(self) -> str | None: return self._error_message(DiagnosticField.MESSAGE_HINT) @property - def statement_position(self) -> Optional[str]: + def statement_position(self) -> str | None: return self._error_message(DiagnosticField.STATEMENT_POSITION) @property - def internal_position(self) -> Optional[str]: + def internal_position(self) -> str | None: return self._error_message(DiagnosticField.INTERNAL_POSITION) @property - def internal_query(self) -> Optional[str]: + def internal_query(self) -> str | None: return self._error_message(DiagnosticField.INTERNAL_QUERY) @property - def context(self) -> Optional[str]: + def context(self) -> str | None: return self._error_message(DiagnosticField.CONTEXT) @property - def schema_name(self) -> Optional[str]: + def schema_name(self) -> str | None: return self._error_message(DiagnosticField.SCHEMA_NAME) @property - def table_name(self) -> Optional[str]: + def table_name(self) -> str | None: return self._error_message(DiagnosticField.TABLE_NAME) @property - def column_name(self) -> Optional[str]: + def column_name(self) -> str | None: return self._error_message(DiagnosticField.COLUMN_NAME) @property - def datatype_name(self) -> Optional[str]: + def datatype_name(self) -> str | None: return self._error_message(DiagnosticField.DATATYPE_NAME) @property - def constraint_name(self) -> Optional[str]: + def constraint_name(self) -> str | None: return self._error_message(DiagnosticField.CONSTRAINT_NAME) @property - def source_file(self) -> Optional[str]: + def source_file(self) -> str | None: return self._error_message(DiagnosticField.SOURCE_FILE) @property - def source_line(self) -> Optional[str]: + def source_line(self) -> str | None: return self._error_message(DiagnosticField.SOURCE_LINE) @property - def source_function(self) -> Optional[str]: + def source_function(self) -> str | None: return self._error_message(DiagnosticField.SOURCE_FUNCTION) - def _error_message(self, field: DiagnosticField) -> Optional[str]: + def _error_message(self, field: DiagnosticField) -> str | None: if self._info: if isinstance(self._info, dict): val = self._info.get(field) diff --git a/psycopg/psycopg/generators.py b/psycopg/psycopg/generators.py index 96cc3dea3..823c65535 100644 --- a/psycopg/psycopg/generators.py +++ b/psycopg/psycopg/generators.py @@ -20,9 +20,11 @@ generator should probably yield the same value again in order to wait more. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import logging from time import monotonic -from typing import List, Optional, Union +from typing import List, Union from . import pq from . import errors as e @@ -195,7 +197,7 @@ def _fetch_many(pgconn: PGconn) -> PQGen[List[PGresult]]: return results -def _fetch(pgconn: PGconn) -> PQGen[Optional[PGresult]]: +def _fetch(pgconn: PGconn) -> PQGen[PGresult | None]: """ Generator retrieving a single result from the database without blocking. @@ -361,7 +363,7 @@ def copy_to(pgconn: PGconn, buffer: Buffer, flush: bool = True) -> PQGen[None]: break -def copy_end(pgconn: PGconn, error: Optional[bytes]) -> PQGen[PGresult]: +def copy_end(pgconn: PGconn, error: bytes | None) -> PQGen[PGresult]: # Retry enqueuing end copy message until successful while pgconn.put_copy_end(error) == 0: while True: diff --git a/psycopg/psycopg/pq/_pq_ctypes.py b/psycopg/psycopg/pq/_pq_ctypes.py index a76a89871..101ec8384 100644 --- a/psycopg/psycopg/pq/_pq_ctypes.py +++ b/psycopg/psycopg/pq/_pq_ctypes.py @@ -4,6 +4,8 @@ libpq access using ctypes # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import sys import ctypes import ctypes.util @@ -769,7 +771,7 @@ def generate_stub() -> None: if narg is not None: return "bytes" else: - return "Optional[bytes]" + return "bytes | None" elif t.__name__ in ( "LP_PGconn_struct", @@ -778,7 +780,7 @@ def generate_stub() -> None: "LP_PGcancel_struct", ): if narg is not None: - return f"Optional[{t.__name__[3:]}]" + return f"{t.__name__[3:]} | None" else: return str(t.__name__[3:]) diff --git a/psycopg/psycopg/pq/_pq_ctypes.pyi b/psycopg/psycopg/pq/_pq_ctypes.pyi index 3828e9469..94e0af908 100644 --- a/psycopg/psycopg/pq/_pq_ctypes.pyi +++ b/psycopg/psycopg/pq/_pq_ctypes.pyi @@ -4,7 +4,7 @@ types stub for ctypes functions # Copyright (C) 2020 The Psycopg Team -from typing import Any, Callable, Optional, Sequence +from typing import Any, Callable, Sequence from ctypes import Array, pointer, _Pointer from ctypes import c_char, c_char_p, c_int, c_ubyte, c_uint, c_ulong @@ -42,31 +42,31 @@ class PGresAttDesc_struct: typlen: int atttypmod: int -def PQhostaddr(arg1: Optional[PGconn_struct]) -> bytes: ... -def PQerrorMessage(arg1: Optional[PGconn_struct]) -> bytes: ... -def PQresultErrorMessage(arg1: Optional[PGresult_struct]) -> bytes: ... +def PQhostaddr(arg1: PGconn_struct | None) -> bytes: ... +def PQerrorMessage(arg1: PGconn_struct | None) -> bytes: ... +def PQresultErrorMessage(arg1: PGresult_struct | None) -> bytes: ... def PQexecPrepared( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: bytes, arg3: int, - arg4: Optional[Array[c_char_p]], - arg5: Optional[Array[c_int]], - arg6: Optional[Array[c_int]], + arg4: Array[c_char_p] | None, + arg5: Array[c_int] | None, + arg6: Array[c_int] | None, arg7: int, ) -> PGresult_struct: ... def PQprepare( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: bytes, arg3: bytes, arg4: int, - arg5: Optional[Array[c_uint]], + arg5: Array[c_uint] | None, ) -> PGresult_struct: ... def PQgetvalue( - arg1: Optional[PGresult_struct], arg2: int, arg3: int + arg1: PGresult_struct | None, arg2: int, arg3: int ) -> _Pointer[c_char]: ... -def PQcmdTuples(arg1: Optional[PGresult_struct]) -> bytes: ... +def PQcmdTuples(arg1: PGresult_struct | None) -> bytes: ... def PQescapeStringConn( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: c_char_p, arg3: bytes, arg4: int, @@ -74,31 +74,31 @@ def PQescapeStringConn( ) -> int: ... def PQescapeString(arg1: c_char_p, arg2: bytes, arg3: int) -> int: ... def PQsendPrepare( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: bytes, arg3: bytes, arg4: int, - arg5: Optional[Array[c_uint]], + arg5: Array[c_uint] | None, ) -> int: ... def PQsendQueryPrepared( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: bytes, arg3: int, - arg4: Optional[Array[c_char_p]], - arg5: Optional[Array[c_int]], - arg6: Optional[Array[c_int]], + arg4: Array[c_char_p] | None, + arg5: Array[c_int] | None, + arg6: Array[c_int] | None, arg7: int, ) -> int: ... -def PQcancelCreate(arg1: Optional[PGconn_struct]) -> PGcancelConn_struct: ... -def PQcancelBlocking(arg1: Optional[PGcancelConn_struct]) -> int: ... -def PQcancelStart(arg1: Optional[PGcancelConn_struct]) -> int: ... -def PQcancelPoll(arg1: Optional[PGcancelConn_struct]) -> int: ... -def PQcancelStatus(arg1: Optional[PGcancelConn_struct]) -> int: ... -def PQcancelSocket(arg1: Optional[PGcancelConn_struct]) -> int: ... -def PQcancelErrorMessage(arg1: Optional[PGcancelConn_struct]) -> bytes: ... -def PQcancelReset(arg1: Optional[PGcancelConn_struct]) -> None: ... -def PQcancelFinish(arg1: Optional[PGcancelConn_struct]) -> None: ... -def PQcancel(arg1: Optional[PGcancel_struct], arg2: c_char_p, arg3: int) -> int: ... +def PQcancelCreate(arg1: PGconn_struct | None) -> PGcancelConn_struct: ... +def PQcancelBlocking(arg1: PGcancelConn_struct | None) -> int: ... +def PQcancelStart(arg1: PGcancelConn_struct | None) -> int: ... +def PQcancelPoll(arg1: PGcancelConn_struct | None) -> int: ... +def PQcancelStatus(arg1: PGcancelConn_struct | None) -> int: ... +def PQcancelSocket(arg1: PGcancelConn_struct | None) -> int: ... +def PQcancelErrorMessage(arg1: PGcancelConn_struct | None) -> bytes: ... +def PQcancelReset(arg1: PGcancelConn_struct | None) -> None: ... +def PQcancelFinish(arg1: PGcancelConn_struct | None) -> None: ... +def PQcancel(arg1: PGcancel_struct | None, arg2: c_char_p, arg3: int) -> int: ... def PQsetNoticeReceiver( arg1: PGconn_struct, arg2: Callable[[Any], PGresult_struct], arg3: Any ) -> Callable[[Any], PGresult_struct]: ... @@ -107,117 +107,121 @@ def PQsetNoticeReceiver( # Type argument "psycopg.pq._pq_ctypes.PGnotify_struct" of "pointer" must be # a subtype of "ctypes._CData" def PQnotifies( - arg1: Optional[PGconn_struct], -) -> Optional[_Pointer[PGnotify_struct]]: ... # type: ignore -def PQputCopyEnd(arg1: Optional[PGconn_struct], arg2: Optional[bytes]) -> int: ... + arg1: PGconn_struct | None, +) -> _Pointer[PGnotify_struct] | None: ... # type: ignore +def PQputCopyEnd(arg1: PGconn_struct | None, arg2: bytes | None) -> int: ... # Arg 2 is a _Pointer, reported as _CArgObject by mypy -def PQgetCopyData(arg1: Optional[PGconn_struct], arg2: Any, arg3: int) -> int: ... +def PQgetCopyData(arg1: PGconn_struct | None, arg2: Any, arg3: int) -> int: ... def PQsetResultAttrs( - arg1: Optional[PGresult_struct], + arg1: PGresult_struct | None, arg2: int, arg3: Array[PGresAttDesc_struct], # type: ignore ) -> int: ... def PQtrace( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: _Pointer[FILE], # type: ignore[type-var] ) -> None: ... -def PQsetTraceFlags(arg1: Optional[PGconn_struct], arg2: int) -> None: ... +def PQsetTraceFlags(arg1: PGconn_struct | None, arg2: int) -> None: ... def PQencryptPasswordConn( - arg1: Optional[PGconn_struct], + arg1: PGconn_struct | None, arg2: bytes, arg3: bytes, - arg4: Optional[bytes], + arg4: bytes | None, ) -> bytes: ... -def PQpipelineStatus(pgconn: Optional[PGconn_struct]) -> int: ... -def PQenterPipelineMode(pgconn: Optional[PGconn_struct]) -> int: ... -def PQexitPipelineMode(pgconn: Optional[PGconn_struct]) -> int: ... -def PQpipelineSync(pgconn: Optional[PGconn_struct]) -> int: ... -def PQsendFlushRequest(pgconn: Optional[PGconn_struct]) -> int: ... +def PQpipelineStatus(pgconn: PGconn_struct | None) -> int: ... +def PQenterPipelineMode(pgconn: PGconn_struct | None) -> int: ... +def PQexitPipelineMode(pgconn: PGconn_struct | None) -> int: ... +def PQpipelineSync(pgconn: PGconn_struct | None) -> int: ... +def PQsendFlushRequest(pgconn: PGconn_struct | None) -> int: ... + +# Autogenerated section. +# In order to refresh, run: +# python -m psycopg.pq._pq_ctypes # fmt: off # autogenerated: start def PQlibVersion() -> int: ... def PQconnectdb(arg1: bytes) -> PGconn_struct: ... def PQconnectStart(arg1: bytes) -> PGconn_struct: ... -def PQconnectPoll(arg1: Optional[PGconn_struct]) -> int: ... +def PQconnectPoll(arg1: PGconn_struct | None) -> int: ... def PQconndefaults() -> Sequence[PQconninfoOption_struct]: ... def PQconninfoFree(arg1: Sequence[PQconninfoOption_struct]) -> None: ... -def PQconninfo(arg1: Optional[PGconn_struct]) -> Sequence[PQconninfoOption_struct]: ... +def PQconninfo(arg1: PGconn_struct | None) -> Sequence[PQconninfoOption_struct]: ... def PQconninfoParse(arg1: bytes, arg2: _Pointer[c_char_p]) -> Sequence[PQconninfoOption_struct]: ... -def PQfinish(arg1: Optional[PGconn_struct]) -> None: ... -def PQreset(arg1: Optional[PGconn_struct]) -> None: ... -def PQresetStart(arg1: Optional[PGconn_struct]) -> int: ... -def PQresetPoll(arg1: Optional[PGconn_struct]) -> int: ... +def PQfinish(arg1: PGconn_struct | None) -> None: ... +def PQreset(arg1: PGconn_struct | None) -> None: ... +def PQresetStart(arg1: PGconn_struct | None) -> int: ... +def PQresetPoll(arg1: PGconn_struct | None) -> int: ... def PQping(arg1: bytes) -> int: ... -def PQdb(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQuser(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQpass(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQhost(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQport(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQtty(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQoptions(arg1: Optional[PGconn_struct]) -> Optional[bytes]: ... -def PQstatus(arg1: Optional[PGconn_struct]) -> int: ... -def PQtransactionStatus(arg1: Optional[PGconn_struct]) -> int: ... -def PQparameterStatus(arg1: Optional[PGconn_struct], arg2: bytes) -> Optional[bytes]: ... -def PQprotocolVersion(arg1: Optional[PGconn_struct]) -> int: ... -def PQserverVersion(arg1: Optional[PGconn_struct]) -> int: ... -def PQsocket(arg1: Optional[PGconn_struct]) -> int: ... -def PQbackendPID(arg1: Optional[PGconn_struct]) -> int: ... -def PQconnectionNeedsPassword(arg1: Optional[PGconn_struct]) -> int: ... -def PQconnectionUsedPassword(arg1: Optional[PGconn_struct]) -> int: ... -def PQsslInUse(arg1: Optional[PGconn_struct]) -> int: ... -def PQexec(arg1: Optional[PGconn_struct], arg2: bytes) -> PGresult_struct: ... -def PQexecParams(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> PGresult_struct: ... -def PQdescribePrepared(arg1: Optional[PGconn_struct], arg2: bytes) -> PGresult_struct: ... -def PQdescribePortal(arg1: Optional[PGconn_struct], arg2: bytes) -> PGresult_struct: ... -def PQclosePrepared(arg1: Optional[PGconn_struct], arg2: bytes) -> PGresult_struct: ... -def PQclosePortal(arg1: Optional[PGconn_struct], arg2: bytes) -> PGresult_struct: ... -def PQresultStatus(arg1: Optional[PGresult_struct]) -> int: ... -def PQresultErrorField(arg1: Optional[PGresult_struct], arg2: int) -> Optional[bytes]: ... -def PQclear(arg1: Optional[PGresult_struct]) -> None: ... -def PQntuples(arg1: Optional[PGresult_struct]) -> int: ... -def PQnfields(arg1: Optional[PGresult_struct]) -> int: ... -def PQfname(arg1: Optional[PGresult_struct], arg2: int) -> Optional[bytes]: ... -def PQftable(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQftablecol(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQfformat(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQftype(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQfmod(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQfsize(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQbinaryTuples(arg1: Optional[PGresult_struct]) -> int: ... -def PQgetisnull(arg1: Optional[PGresult_struct], arg2: int, arg3: int) -> int: ... -def PQgetlength(arg1: Optional[PGresult_struct], arg2: int, arg3: int) -> int: ... -def PQnparams(arg1: Optional[PGresult_struct]) -> int: ... -def PQparamtype(arg1: Optional[PGresult_struct], arg2: int) -> int: ... -def PQcmdStatus(arg1: Optional[PGresult_struct]) -> Optional[bytes]: ... -def PQoidValue(arg1: Optional[PGresult_struct]) -> int: ... -def PQescapeLiteral(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int) -> Optional[bytes]: ... -def PQescapeIdentifier(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int) -> Optional[bytes]: ... -def PQescapeByteaConn(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int, arg4: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ... +def PQdb(arg1: PGconn_struct | None) -> bytes | None: ... +def PQuser(arg1: PGconn_struct | None) -> bytes | None: ... +def PQpass(arg1: PGconn_struct | None) -> bytes | None: ... +def PQhost(arg1: PGconn_struct | None) -> bytes | None: ... +def PQport(arg1: PGconn_struct | None) -> bytes | None: ... +def PQtty(arg1: PGconn_struct | None) -> bytes | None: ... +def PQoptions(arg1: PGconn_struct | None) -> bytes | None: ... +def PQstatus(arg1: PGconn_struct | None) -> int: ... +def PQtransactionStatus(arg1: PGconn_struct | None) -> int: ... +def PQparameterStatus(arg1: PGconn_struct | None, arg2: bytes) -> bytes | None: ... +def PQprotocolVersion(arg1: PGconn_struct | None) -> int: ... +def PQserverVersion(arg1: PGconn_struct | None) -> int: ... +def PQsocket(arg1: PGconn_struct | None) -> int: ... +def PQbackendPID(arg1: PGconn_struct | None) -> int: ... +def PQconnectionNeedsPassword(arg1: PGconn_struct | None) -> int: ... +def PQconnectionUsedPassword(arg1: PGconn_struct | None) -> int: ... +def PQsslInUse(arg1: PGconn_struct | None) -> int: ... +def PQexec(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... +def PQexecParams(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> PGresult_struct: ... +def PQdescribePrepared(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... +def PQdescribePortal(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... +def PQclosePrepared(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... +def PQclosePortal(arg1: PGconn_struct | None, arg2: bytes) -> PGresult_struct: ... +def PQresultStatus(arg1: PGresult_struct | None) -> int: ... +def PQresultErrorField(arg1: PGresult_struct | None, arg2: int) -> bytes | None: ... +def PQclear(arg1: PGresult_struct | None) -> None: ... +def PQntuples(arg1: PGresult_struct | None) -> int: ... +def PQnfields(arg1: PGresult_struct | None) -> int: ... +def PQfname(arg1: PGresult_struct | None, arg2: int) -> bytes | None: ... +def PQftable(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQftablecol(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQfformat(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQftype(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQfmod(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQfsize(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQbinaryTuples(arg1: PGresult_struct | None) -> int: ... +def PQgetisnull(arg1: PGresult_struct | None, arg2: int, arg3: int) -> int: ... +def PQgetlength(arg1: PGresult_struct | None, arg2: int, arg3: int) -> int: ... +def PQnparams(arg1: PGresult_struct | None) -> int: ... +def PQparamtype(arg1: PGresult_struct | None, arg2: int) -> int: ... +def PQcmdStatus(arg1: PGresult_struct | None) -> bytes | None: ... +def PQoidValue(arg1: PGresult_struct | None) -> int: ... +def PQescapeLiteral(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> bytes | None: ... +def PQescapeIdentifier(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> bytes | None: ... +def PQescapeByteaConn(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ... def PQescapeBytea(arg1: bytes, arg2: int, arg3: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ... def PQunescapeBytea(arg1: bytes, arg2: _Pointer[c_ulong]) -> _Pointer[c_ubyte]: ... -def PQsendQuery(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... -def PQsendQueryParams(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> int: ... -def PQsendDescribePrepared(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... -def PQsendDescribePortal(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... -def PQsendClosePrepared(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... -def PQsendClosePortal(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... -def PQgetResult(arg1: Optional[PGconn_struct]) -> PGresult_struct: ... -def PQconsumeInput(arg1: Optional[PGconn_struct]) -> int: ... -def PQisBusy(arg1: Optional[PGconn_struct]) -> int: ... -def PQsetnonblocking(arg1: Optional[PGconn_struct], arg2: int) -> int: ... -def PQisnonblocking(arg1: Optional[PGconn_struct]) -> int: ... -def PQflush(arg1: Optional[PGconn_struct]) -> int: ... -def PQsetSingleRowMode(arg1: Optional[PGconn_struct]) -> int: ... -def PQsetChunkedRowsMode(arg1: Optional[PGconn_struct], arg2: int) -> int: ... -def PQgetCancel(arg1: Optional[PGconn_struct]) -> PGcancel_struct: ... -def PQfreeCancel(arg1: Optional[PGcancel_struct]) -> None: ... -def PQputCopyData(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int) -> int: ... -def PQuntrace(arg1: Optional[PGconn_struct]) -> None: ... +def PQsendQuery(arg1: PGconn_struct | None, arg2: bytes) -> int: ... +def PQsendQueryParams(arg1: PGconn_struct | None, arg2: bytes, arg3: int, arg4: _Pointer[c_uint], arg5: _Pointer[c_char_p], arg6: _Pointer[c_int], arg7: _Pointer[c_int], arg8: int) -> int: ... +def PQsendDescribePrepared(arg1: PGconn_struct | None, arg2: bytes) -> int: ... +def PQsendDescribePortal(arg1: PGconn_struct | None, arg2: bytes) -> int: ... +def PQsendClosePrepared(arg1: PGconn_struct | None, arg2: bytes) -> int: ... +def PQsendClosePortal(arg1: PGconn_struct | None, arg2: bytes) -> int: ... +def PQgetResult(arg1: PGconn_struct | None) -> PGresult_struct: ... +def PQconsumeInput(arg1: PGconn_struct | None) -> int: ... +def PQisBusy(arg1: PGconn_struct | None) -> int: ... +def PQsetnonblocking(arg1: PGconn_struct | None, arg2: int) -> int: ... +def PQisnonblocking(arg1: PGconn_struct | None) -> int: ... +def PQflush(arg1: PGconn_struct | None) -> int: ... +def PQsetSingleRowMode(arg1: PGconn_struct | None) -> int: ... +def PQsetChunkedRowsMode(arg1: PGconn_struct | None, arg2: int) -> int: ... +def PQgetCancel(arg1: PGconn_struct | None) -> PGcancel_struct: ... +def PQfreeCancel(arg1: PGcancel_struct | None) -> None: ... +def PQputCopyData(arg1: PGconn_struct | None, arg2: bytes, arg3: int) -> int: ... +def PQuntrace(arg1: PGconn_struct | None) -> None: ... def PQfreemem(arg1: Any) -> None: ... -def PQchangePassword(arg1: Optional[PGconn_struct], arg2: bytes, arg3: bytes) -> PGresult_struct: ... -def PQmakeEmptyPGresult(arg1: Optional[PGconn_struct], arg2: int) -> PGresult_struct: ... +def PQchangePassword(arg1: PGconn_struct | None, arg2: bytes, arg3: bytes) -> PGresult_struct: ... +def PQmakeEmptyPGresult(arg1: PGconn_struct | None, arg2: int) -> PGresult_struct: ... def PQinitOpenSSL(arg1: int, arg2: int) -> None: ... # autogenerated: end # fmt: on diff --git a/psycopg/psycopg/pq/abc.py b/psycopg/psycopg/pq/abc.py index 8b98c7d01..0f6c29edf 100644 --- a/psycopg/psycopg/pq/abc.py +++ b/psycopg/psycopg/pq/abc.py @@ -4,7 +4,9 @@ Protocol objects to represent objects exposed by different pq implementations. # Copyright (C) 2020 The Psycopg Team -from typing import Any, Callable, List, Optional, Protocol, Sequence, Tuple +from __future__ import annotations + +from typing import Any, Callable, List, Protocol, Sequence, Tuple from typing import Union, TYPE_CHECKING from ._enums import Format, Trace @@ -18,8 +20,8 @@ Buffer: TypeAlias = Union[bytes, bytearray, memoryview] class PGconn(Protocol): - notice_handler: Optional[Callable[["PGresult"], None]] - notify_handler: Optional[Callable[["PGnotify"], None]] + notice_handler: Callable[["PGresult"], None] | None + notify_handler: Callable[["PGnotify"], None] | None @classmethod def connect(cls, conninfo: bytes) -> "PGconn": ... @@ -73,7 +75,7 @@ class PGconn(Protocol): @property def transaction_status(self) -> int: ... - def parameter_status(self, name: bytes) -> Optional[bytes]: ... + def parameter_status(self, name: bytes) -> bytes | None: ... @property def error_message(self) -> bytes: ... @@ -103,18 +105,18 @@ class PGconn(Protocol): def exec_params( self, command: bytes, - param_values: Optional[Sequence[Optional[Buffer]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[Buffer | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> "PGresult": ... def send_query_params( self, command: bytes, - param_values: Optional[Sequence[Optional[Buffer]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[Buffer | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: ... @@ -122,14 +124,14 @@ class PGconn(Protocol): self, name: bytes, command: bytes, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> None: ... def send_query_prepared( self, name: bytes, - param_values: Optional[Sequence[Optional[Buffer]]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[Buffer | None] | None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: ... @@ -137,14 +139,14 @@ class PGconn(Protocol): self, name: bytes, command: bytes, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> "PGresult": ... def exec_prepared( self, name: bytes, - param_values: Optional[Sequence[Buffer]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[Buffer] | None, + param_formats: Sequence[int] | None = None, result_format: int = 0, ) -> "PGresult": ... @@ -164,7 +166,7 @@ class PGconn(Protocol): def send_close_portal(self, name: bytes) -> None: ... - def get_result(self) -> Optional["PGresult"]: ... + def get_result(self) -> "PGresult" | None: ... def consume_input(self) -> None: ... @@ -186,11 +188,11 @@ class PGconn(Protocol): def get_cancel(self) -> "PGcancel": ... - def notifies(self) -> Optional["PGnotify"]: ... + def notifies(self) -> "PGnotify" | None: ... def put_copy_data(self, buffer: Buffer) -> int: ... - def put_copy_end(self, error: Optional[bytes] = None) -> int: ... + def put_copy_end(self, error: bytes | None = None) -> int: ... def get_copy_data(self, async_: int) -> Tuple[int, memoryview]: ... @@ -201,7 +203,7 @@ class PGconn(Protocol): def untrace(self) -> None: ... def encrypt_password( - self, passwd: bytes, user: bytes, algorithm: Optional[bytes] = None + self, passwd: bytes, user: bytes, algorithm: bytes | None = None ) -> bytes: ... def change_password(self, user: bytes, passwd: bytes) -> None: ... @@ -229,7 +231,7 @@ class PGresult(Protocol): @property def error_message(self) -> bytes: ... - def error_field(self, fieldcode: int) -> Optional[bytes]: ... + def error_field(self, fieldcode: int) -> bytes | None: ... @property def ntuples(self) -> int: ... @@ -237,7 +239,7 @@ class PGresult(Protocol): @property def nfields(self) -> int: ... - def fname(self, column_number: int) -> Optional[bytes]: ... + def fname(self, column_number: int) -> bytes | None: ... def ftable(self, column_number: int) -> int: ... @@ -254,7 +256,7 @@ class PGresult(Protocol): @property def binary_tuples(self) -> int: ... - def get_value(self, row_number: int, column_number: int) -> Optional[bytes]: ... + def get_value(self, row_number: int, column_number: int) -> bytes | None: ... @property def nparams(self) -> int: ... @@ -262,10 +264,10 @@ class PGresult(Protocol): def param_type(self, param_number: int) -> int: ... @property - def command_status(self) -> Optional[bytes]: ... + def command_status(self) -> bytes | None: ... @property - def command_tuples(self) -> Optional[int]: ... + def command_tuples(self) -> int | None: ... @property def oid_value(self) -> int: ... @@ -312,7 +314,7 @@ class Conninfo(Protocol): class Escaping(Protocol): - def __init__(self, conn: Optional[PGconn] = None): ... + def __init__(self, conn: PGconn | None = None): ... def escape_literal(self, data: Buffer) -> bytes: ... diff --git a/psycopg/psycopg/pq/misc.py b/psycopg/psycopg/pq/misc.py index c671bf25f..793c95d89 100644 --- a/psycopg/psycopg/pq/misc.py +++ b/psycopg/psycopg/pq/misc.py @@ -4,12 +4,14 @@ Various functionalities to make easier to work with the libpq. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re import os import sys import logging import ctypes.util -from typing import cast, NamedTuple, Optional, Union +from typing import cast, NamedTuple, Union from .abc import PGconn, PGresult from ._enums import ConnStatus, TransactionStatus, PipelineStatus @@ -29,9 +31,9 @@ class PGnotify(NamedTuple): class ConninfoOption(NamedTuple): keyword: bytes - envvar: Optional[bytes] - compiled: Optional[bytes] - val: Optional[bytes] + envvar: bytes | None + compiled: bytes | None + val: bytes | None label: bytes dispchar: bytes dispsize: int @@ -48,7 +50,7 @@ class PGresAttDesc(NamedTuple): @cache -def find_libpq_full_path() -> Optional[str]: +def find_libpq_full_path() -> str | None: if sys.platform == "win32": libname = ctypes.util.find_library("libpq.dll") diff --git a/psycopg/psycopg/pq/pq_ctypes.py b/psycopg/psycopg/pq/pq_ctypes.py index eba9872c4..fc171e5ba 100644 --- a/psycopg/psycopg/pq/pq_ctypes.py +++ b/psycopg/psycopg/pq/pq_ctypes.py @@ -8,6 +8,8 @@ implementation. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import sys import logging from os import getpid @@ -15,7 +17,7 @@ from weakref import ref from ctypes import Array, POINTER, cast, string_at, create_string_buffer, byref from ctypes import addressof, c_char_p, c_int, c_size_t, c_ulong, c_void_p, py_object -from typing import Any, Callable, List, Optional, Sequence, Tuple +from typing import Any, Callable, List, Sequence, Tuple from typing import cast as t_cast, TYPE_CHECKING from .. import errors as e @@ -78,9 +80,9 @@ class PGconn: ) def __init__(self, pgconn_ptr: impl.PGconn_struct): - self._pgconn_ptr: Optional[impl.PGconn_struct] = pgconn_ptr - self.notice_handler: Optional[Callable[["abc.PGresult"], None]] = None - self.notify_handler: Optional[Callable[[PGnotify], None]] = None + self._pgconn_ptr: impl.PGconn_struct | None = pgconn_ptr + self.notice_handler: Callable[["abc.PGresult"], None] | None = None + self.notify_handler: Callable[[PGnotify], None] | None = None # Keep alive for the lifetime of PGconn self._self_ptr = py_object(ref(self)) @@ -128,7 +130,7 @@ class PGconn: PQfinish(p) @property - def pgconn_ptr(self) -> Optional[int]: + def pgconn_ptr(self) -> int | None: """The pointer to the underlying `!PGconn` structure, as integer. `!None` if the connection is closed. @@ -211,7 +213,7 @@ class PGconn: def transaction_status(self) -> int: return impl.PQtransactionStatus(self._pgconn_ptr) - def parameter_status(self, name: bytes) -> Optional[bytes]: + def parameter_status(self, name: bytes) -> bytes | None: self._ensure_pgconn() return impl.PQparameterStatus(self._pgconn_ptr, name) @@ -278,9 +280,9 @@ class PGconn: def exec_params( self, command: bytes, - param_values: Optional[Sequence[Optional["abc.Buffer"]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence["abc.Buffer" | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> "PGresult": args = self._query_params_args( @@ -295,9 +297,9 @@ class PGconn: def send_query_params( self, command: bytes, - param_values: Optional[Sequence[Optional["abc.Buffer"]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence["abc.Buffer" | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: args = self._query_params_args( @@ -313,9 +315,9 @@ class PGconn: self, name: bytes, command: bytes, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> None: - atypes: Optional[Array[impl.Oid]] + atypes: Array[impl.Oid] | None if not param_types: nparams = 0 atypes = None @@ -332,8 +334,8 @@ class PGconn: def send_query_prepared( self, name: bytes, - param_values: Optional[Sequence[Optional["abc.Buffer"]]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence["abc.Buffer" | None] | None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: # repurpose this function with a cheeky replacement of query with name, @@ -352,16 +354,16 @@ class PGconn: def _query_params_args( self, command: bytes, - param_values: Optional[Sequence[Optional["abc.Buffer"]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence["abc.Buffer" | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> Any: if not isinstance(command, bytes): raise TypeError(f"bytes expected, got {type(command)} instead") - aparams: Optional[Array[c_char_p]] - alenghts: Optional[Array[c_int]] + aparams: Array[c_char_p] | None + alenghts: Array[c_int] | None if param_values: nparams = len(param_values) aparams = (c_char_p * nparams)( @@ -376,7 +378,7 @@ class PGconn: nparams = 0 aparams = alenghts = None - atypes: Optional[Array[impl.Oid]] + atypes: Array[impl.Oid] | None if not param_types: atypes = None else: @@ -412,7 +414,7 @@ class PGconn: self, name: bytes, command: bytes, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> "PGresult": if not isinstance(name, bytes): raise TypeError(f"'name' must be bytes, got {type(name)} instead") @@ -436,15 +438,15 @@ class PGconn: def exec_prepared( self, name: bytes, - param_values: Optional[Sequence["abc.Buffer"]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence["abc.Buffer"] | None, + param_formats: Sequence[int] | None = None, result_format: int = 0, ) -> "PGresult": if not isinstance(name, bytes): raise TypeError(f"'name' must be bytes, got {type(name)} instead") - aparams: Optional[Array[c_char_p]] - alenghts: Optional[Array[c_int]] + aparams: Array[c_char_p] | None + alenghts: Array[c_int] | None if param_values: nparams = len(param_values) aparams = (c_char_p * nparams)( @@ -557,7 +559,7 @@ class PGconn: f"sending close portal failed: {error_message(self)}" ) - def get_result(self) -> Optional["PGresult"]: + def get_result(self) -> "PGresult" | None: rv = impl.PQgetResult(self._pgconn_ptr) return PGresult(rv) if rv else None @@ -618,7 +620,7 @@ class PGconn: raise e.OperationalError("couldn't create cancel object") return PGcancel(rv) - def notifies(self) -> Optional[PGnotify]: + def notifies(self) -> PGnotify | None: ptr = impl.PQnotifies(self._pgconn_ptr) if ptr: c = ptr.contents @@ -636,7 +638,7 @@ class PGconn: raise e.OperationalError(f"sending copy data failed: {error_message(self)}") return rv - def put_copy_end(self, error: Optional[bytes] = None) -> int: + def put_copy_end(self, error: bytes | None = None) -> int: rv = impl.PQputCopyEnd(self._pgconn_ptr, error) if rv < 0: raise e.OperationalError(f"sending copy end failed: {error_message(self)}") @@ -687,7 +689,7 @@ class PGconn: impl.PQuntrace(self._pgconn_ptr) def encrypt_password( - self, passwd: bytes, user: bytes, algorithm: Optional[bytes] = None + self, passwd: bytes, user: bytes, algorithm: bytes | None = None ) -> bytes: """ Return the encrypted form of a PostgreSQL password. @@ -768,9 +770,7 @@ class PGconn: if impl.PQsendFlushRequest(self._pgconn_ptr) == 0: raise e.OperationalError(f"flush request failed: {error_message(self)}") - def _call_bytes( - self, func: Callable[[impl.PGconn_struct], Optional[bytes]] - ) -> bytes: + def _call_bytes(self, func: Callable[[impl.PGconn_struct], bytes | None]) -> bytes: """ Call one of the pgconn libpq functions returning a bytes pointer. """ @@ -809,7 +809,7 @@ class PGresult: __slots__ = ("_pgresult_ptr",) def __init__(self, pgresult_ptr: impl.PGresult_struct): - self._pgresult_ptr: Optional[impl.PGresult_struct] = pgresult_ptr + self._pgresult_ptr: impl.PGresult_struct | None = pgresult_ptr def __del__(self) -> None: self.clear() @@ -825,7 +825,7 @@ class PGresult: PQclear(p) @property - def pgresult_ptr(self) -> Optional[int]: + def pgresult_ptr(self) -> int | None: """The pointer to the underlying `!PGresult` structure, as integer. `!None` if the result was cleared. @@ -847,7 +847,7 @@ class PGresult: def error_message(self) -> bytes: return impl.PQresultErrorMessage(self._pgresult_ptr) - def error_field(self, fieldcode: int) -> Optional[bytes]: + def error_field(self, fieldcode: int) -> bytes | None: return impl.PQresultErrorField(self._pgresult_ptr, fieldcode) @property @@ -858,7 +858,7 @@ class PGresult: def nfields(self) -> int: return impl.PQnfields(self._pgresult_ptr) - def fname(self, column_number: int) -> Optional[bytes]: + def fname(self, column_number: int) -> bytes | None: return impl.PQfname(self._pgresult_ptr, column_number) def ftable(self, column_number: int) -> int: @@ -883,7 +883,7 @@ class PGresult: def binary_tuples(self) -> int: return impl.PQbinaryTuples(self._pgresult_ptr) - def get_value(self, row_number: int, column_number: int) -> Optional[bytes]: + def get_value(self, row_number: int, column_number: int) -> bytes | None: length: int = impl.PQgetlength(self._pgresult_ptr, row_number, column_number) if length: v = impl.PQgetvalue(self._pgresult_ptr, row_number, column_number) @@ -902,11 +902,11 @@ class PGresult: return impl.PQparamtype(self._pgresult_ptr, param_number) @property - def command_status(self) -> Optional[bytes]: + def command_status(self) -> bytes | None: return impl.PQcmdStatus(self._pgresult_ptr) @property - def command_tuples(self) -> Optional[int]: + def command_tuples(self) -> int | None: rv = impl.PQcmdTuples(self._pgresult_ptr) return int(rv) if rv else None @@ -934,7 +934,7 @@ class PGcancelConn: __slots__ = ("pgcancelconn_ptr",) def __init__(self, pgcancelconn_ptr: impl.PGcancelConn_struct): - self.pgcancelconn_ptr: Optional[impl.PGcancelConn_struct] = pgcancelconn_ptr + self.pgcancelconn_ptr: impl.PGcancelConn_struct | None = pgcancelconn_ptr def __del__(self) -> None: self.finish() @@ -1011,7 +1011,7 @@ class PGcancel: __slots__ = ("pgcancel_ptr",) def __init__(self, pgcancel_ptr: impl.PGcancel_struct): - self.pgcancel_ptr: Optional[impl.PGcancel_struct] = pgcancel_ptr + self.pgcancel_ptr: impl.PGcancel_struct | None = pgcancel_ptr def __del__(self) -> None: self.free() @@ -1103,7 +1103,7 @@ class Escaping: Utility object to escape strings for SQL interpolation. """ - def __init__(self, conn: Optional[PGconn] = None): + def __init__(self, conn: PGconn | None = None): self.conn = conn def escape_literal(self, data: "abc.Buffer") -> bytes: diff --git a/psycopg/psycopg/raw_cursor.py b/psycopg/psycopg/raw_cursor.py index d0984da7e..52211e23b 100644 --- a/psycopg/psycopg/raw_cursor.py +++ b/psycopg/psycopg/raw_cursor.py @@ -4,7 +4,9 @@ psycopg raw queries cursors # Copyright (C) 2023 The Psycopg Team -from typing import Optional, TYPE_CHECKING +from __future__ import annotations + +from typing import TYPE_CHECKING from .abc import ConnectionType, Query, Params from .sql import Composable @@ -22,7 +24,7 @@ if TYPE_CHECKING: class PostgresRawQuery(PostgresQuery): - def convert(self, query: Query, vars: Optional[Params]) -> None: + def convert(self, query: Query, vars: Params | None) -> None: if isinstance(query, str): bquery = query.encode(self._encoding) elif isinstance(query, Composable): @@ -34,7 +36,7 @@ class PostgresRawQuery(PostgresQuery): self._want_formats = self._order = None self.dump(vars) - def dump(self, vars: Optional[Params]) -> None: + def dump(self, vars: Params | None) -> None: if vars is not None: if not PostgresQuery.is_params_sequence(vars): raise TypeError("raw queries require a sequence of parameters") diff --git a/psycopg/psycopg/rows.py b/psycopg/psycopg/rows.py index 07e0dbcaf..25c938386 100644 --- a/psycopg/psycopg/rows.py +++ b/psycopg/psycopg/rows.py @@ -4,8 +4,10 @@ psycopg row factories # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import functools -from typing import Any, Callable, Dict, List, Optional, NamedTuple, NoReturn +from typing import Any, Callable, Dict, List, NamedTuple, NoReturn from typing import TYPE_CHECKING, Protocol, Sequence, Tuple, Type from collections import namedtuple @@ -239,7 +241,7 @@ def no_result(values: Sequence[Any]) -> NoReturn: raise e.InterfaceError("the cursor doesn't have a result") -def _get_names(cursor: "BaseCursor[Any, Any]") -> Optional[List[str]]: +def _get_names(cursor: "BaseCursor[Any, Any]") -> List[str] | None: res = cursor.pgresult if not res: return None @@ -254,7 +256,7 @@ def _get_names(cursor: "BaseCursor[Any, Any]") -> Optional[List[str]]: ] -def _get_nfields(res: "PGresult") -> Optional[int]: +def _get_nfields(res: "PGresult") -> int | None: """ Return the number of columns in a result, if it returns tuples else None diff --git a/psycopg/psycopg/server_cursor.py b/psycopg/psycopg/server_cursor.py index 2f5f44739..76f5ad355 100644 --- a/psycopg/psycopg/server_cursor.py +++ b/psycopg/psycopg/server_cursor.py @@ -4,8 +4,10 @@ psycopg server-side cursor objects. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from typing import Any, AsyncIterator, List, Iterable, Iterator -from typing import Optional, TYPE_CHECKING, overload +from typing import TYPE_CHECKING, overload from warnings import warn from . import pq @@ -43,7 +45,7 @@ class ServerCursorMixin(BaseCursor[ConnectionType, Row]): def __init__( self, name: str, - scrollable: Optional[bool], + scrollable: bool | None, withhold: bool, ): self._name = name @@ -65,7 +67,7 @@ class ServerCursorMixin(BaseCursor[ConnectionType, Row]): return self._name @property - def scrollable(self) -> Optional[bool]: + def scrollable(self) -> bool | None: """ Whether the cursor is scrollable or not. @@ -82,7 +84,7 @@ class ServerCursorMixin(BaseCursor[ConnectionType, Row]): return self._withhold @property - def rownumber(self) -> Optional[int]: + def rownumber(self) -> int | None: """Index of the next row to fetch in the current result. `!None` if there is no result to fetch. @@ -97,8 +99,8 @@ class ServerCursorMixin(BaseCursor[ConnectionType, Row]): def _declare_gen( self, query: Query, - params: Optional[Params] = None, - binary: Optional[bool] = None, + params: Params | None = None, + binary: bool | None = None, ) -> PQGen[None]: """Generator implementing `ServerCursor.execute()`.""" @@ -159,7 +161,7 @@ class ServerCursorMixin(BaseCursor[ConnectionType, Row]): query = sql.SQL("CLOSE {}").format(sql.Identifier(self._name)) yield from self._conn._exec_command(query) - def _fetch_gen(self, num: Optional[int]) -> PQGen[List[Row]]: + def _fetch_gen(self, num: int | None) -> PQGen[List[Row]]: if self.closed: raise e.InterfaceError("the cursor is closed") # If we are stealing the cursor, make sure we know its shape @@ -220,7 +222,7 @@ class ServerCursor(ServerCursorMixin["Connection[Any]", Row], Cursor[Row]): connection: "Connection[Row]", name: str, *, - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ): ... @@ -231,7 +233,7 @@ class ServerCursor(ServerCursorMixin["Connection[Any]", Row], Cursor[Row]): name: str, *, row_factory: RowFactory[Row], - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ): ... @@ -240,8 +242,8 @@ class ServerCursor(ServerCursorMixin["Connection[Any]", Row], Cursor[Row]): connection: "Connection[Any]", name: str, *, - row_factory: Optional[RowFactory[Row]] = None, - scrollable: Optional[bool] = None, + row_factory: RowFactory[Row] | None = None, + scrollable: bool | None = None, withhold: bool = False, ): Cursor.__init__( @@ -271,9 +273,9 @@ class ServerCursor(ServerCursorMixin["Connection[Any]", Row], Cursor[Row]): def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - binary: Optional[bool] = None, + binary: bool | None = None, **kwargs: Any, ) -> Self: """ @@ -304,7 +306,7 @@ class ServerCursor(ServerCursorMixin["Connection[Any]", Row], Cursor[Row]): """Method not implemented for server-side cursors.""" raise e.NotSupportedError("executemany not supported on server-side cursors") - def fetchone(self) -> Optional[Row]: + def fetchone(self) -> Row | None: with self._conn.lock: recs = self._conn.wait(self._fetch_gen(1)) if recs: @@ -359,7 +361,7 @@ class AsyncServerCursor( connection: "AsyncConnection[Row]", name: str, *, - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ): ... @@ -370,7 +372,7 @@ class AsyncServerCursor( name: str, *, row_factory: AsyncRowFactory[Row], - scrollable: Optional[bool] = None, + scrollable: bool | None = None, withhold: bool = False, ): ... @@ -379,8 +381,8 @@ class AsyncServerCursor( connection: "AsyncConnection[Any]", name: str, *, - row_factory: Optional[AsyncRowFactory[Row]] = None, - scrollable: Optional[bool] = None, + row_factory: AsyncRowFactory[Row] | None = None, + scrollable: bool | None = None, withhold: bool = False, ): AsyncCursor.__init__( @@ -407,9 +409,9 @@ class AsyncServerCursor( async def execute( self, query: Query, - params: Optional[Params] = None, + params: Params | None = None, *, - binary: Optional[bool] = None, + binary: bool | None = None, **kwargs: Any, ) -> Self: if kwargs: @@ -436,7 +438,7 @@ class AsyncServerCursor( ) -> None: raise e.NotSupportedError("executemany not supported on server-side cursors") - async def fetchone(self) -> Optional[Row]: + async def fetchone(self) -> Row | None: async with self._conn.lock: recs = await self._conn.wait(self._fetch_gen(1)) if recs: diff --git a/psycopg/psycopg/sql.py b/psycopg/psycopg/sql.py index a94f77f6e..c27b6657a 100644 --- a/psycopg/psycopg/sql.py +++ b/psycopg/psycopg/sql.py @@ -4,10 +4,12 @@ SQL composition utility module # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import codecs import string from abc import ABC, abstractmethod -from typing import Any, Iterator, Iterable, List, Optional, Sequence, Union +from typing import Any, Iterator, Iterable, List, Sequence, Union from .pq import Escaping from .abc import AdaptContext @@ -17,7 +19,7 @@ from ._encodings import conn_encoding from ._transformer import Transformer -def quote(obj: Any, context: Optional[AdaptContext] = None) -> str: +def quote(obj: Any, context: AdaptContext | None = None) -> str: """ Adapt a Python object to a quoted SQL string. @@ -55,7 +57,7 @@ class Composable(ABC): return f"{self.__class__.__name__}({self._obj!r})" @abstractmethod - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: """ Return the value of the object as bytes. @@ -69,7 +71,7 @@ class Composable(ABC): """ raise NotImplementedError - def as_string(self, context: Optional[AdaptContext] = None) -> str: + def as_string(self, context: AdaptContext | None = None) -> str: """ Return the value of the object as string. @@ -130,7 +132,7 @@ class Composed(Composable): seq = [obj if isinstance(obj, Composable) else Literal(obj) for obj in seq] super().__init__(seq) - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: return b"".join(obj.as_bytes(context) for obj in self._obj) def __iter__(self) -> Iterator[Composable]: @@ -200,10 +202,10 @@ class SQL(Composable): if not isinstance(obj, str): raise TypeError(f"SQL values must be strings, got {obj!r} instead") - def as_string(self, context: Optional[AdaptContext] = None) -> str: + def as_string(self, context: AdaptContext | None = None) -> str: return self._obj - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: conn = context.connection if context else None enc = conn_encoding(conn) return self._obj.encode(enc) @@ -244,7 +246,7 @@ class SQL(Composable): """ rv: List[Composable] = [] - autonum: Optional[int] = 0 + autonum: int | None = 0 # TODO: this is probably not the right way to whitelist pre # pyre complains. Will wait for mypy to complain too to fix. pre: LiteralString @@ -362,7 +364,7 @@ class Identifier(Composable): def __repr__(self) -> str: return f"{self.__class__.__name__}({', '.join(map(repr, self._obj))})" - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: conn = context.connection if context else None if conn: esc = Escaping(conn.pgconn) @@ -400,7 +402,7 @@ class Literal(Composable): """ - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: tx = Transformer.from_context(context) return tx.as_literal(self._obj) @@ -459,11 +461,11 @@ class Placeholder(Composable): return f"{self.__class__.__name__}({', '.join(parts)})" - def as_string(self, context: Optional[AdaptContext] = None) -> str: + def as_string(self, context: AdaptContext | None = None) -> str: code = self._format.value return f"%({self._obj}){code}" if self._obj else f"%{code}" - def as_bytes(self, context: Optional[AdaptContext] = None) -> bytes: + def as_bytes(self, context: AdaptContext | None = None) -> bytes: conn = context.connection if context else None enc = conn_encoding(conn) return self.as_string(context).encode(enc) diff --git a/psycopg/psycopg/transaction.py b/psycopg/psycopg/transaction.py index 56b710445..da9f98048 100644 --- a/psycopg/psycopg/transaction.py +++ b/psycopg/psycopg/transaction.py @@ -4,10 +4,12 @@ Transaction context managers returned by Connection.transaction() # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import logging from types import TracebackType -from typing import Generic, Iterator, Optional, Type, Union, TYPE_CHECKING +from typing import Generic, Iterator, Type, Union, TYPE_CHECKING from . import pq from . import sql @@ -57,7 +59,7 @@ class BaseTransaction(Generic[ConnectionType]): def __init__( self, connection: ConnectionType, - savepoint_name: Optional[str] = None, + savepoint_name: str | None = None, force_rollback: bool = False, ): self._conn = connection @@ -69,7 +71,7 @@ class BaseTransaction(Generic[ConnectionType]): self._stack_index = -1 @property - def savepoint_name(self) -> Optional[str]: + def savepoint_name(self) -> str | None: """ The name of the savepoint; `!None` if handling the main transaction. """ @@ -101,9 +103,9 @@ class BaseTransaction(Generic[ConnectionType]): def _exit_gen( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> PQGen[bool]: if not exc_val and not self.force_rollback: yield from self._commit_gen() @@ -133,7 +135,7 @@ class BaseTransaction(Generic[ConnectionType]): for command in self._get_commit_commands(): yield from self._conn._exec_command(command) - def _rollback_gen(self, exc_val: Optional[BaseException]) -> PQGen[bool]: + def _rollback_gen(self, exc_val: BaseException | None) -> PQGen[bool]: if isinstance(exc_val, Rollback): logger.debug(f"{self._conn}: Explicit rollback from: ", exc_info=True) @@ -214,7 +216,7 @@ class BaseTransaction(Generic[ConnectionType]): self._stack_index = self._conn._num_transactions self._conn._num_transactions += 1 - def _pop_savepoint(self, action: str) -> Optional[Exception]: + def _pop_savepoint(self, action: str) -> Exception | None: """ Pop the transaction from the connection transactions stack. @@ -248,9 +250,9 @@ class Transaction(BaseTransaction["Connection[Any]"]): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> bool: if self.pgconn.status == OK: with self._conn.lock: @@ -277,9 +279,9 @@ class AsyncTransaction(BaseTransaction["AsyncConnection[Any]"]): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> bool: if self.pgconn.status == OK: async with self._conn.lock: diff --git a/psycopg/psycopg/types/array.py b/psycopg/psycopg/types/array.py index 23afc7d9f..a795c6d3f 100644 --- a/psycopg/psycopg/types/array.py +++ b/psycopg/psycopg/types/array.py @@ -4,10 +4,12 @@ Adapters for arrays # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re import struct from math import prod -from typing import Any, cast, Callable, List, Optional, Pattern, Set, Tuple, Type +from typing import Any, cast, Callable, List, Pattern, Set, Tuple, Type from .. import pq from .. import errors as e @@ -34,12 +36,12 @@ PQ_BINARY = pq.Format.BINARY class BaseListDumper(RecursiveDumper): element_oid = INVALID_OID - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): if cls is NoneType: cls = list super().__init__(cls, context) - self.sub_dumper: Optional[Dumper] = None + self.sub_dumper: Dumper | None = None if self.element_oid and context: sdclass = context.adapters.get_dumper_by_oid(self.element_oid, self.format) self.sub_dumper = sdclass(NoneType, context) @@ -153,7 +155,7 @@ class ListDumper(BaseListDumper): # backslash-escaped. _re_esc = re.compile(rb'(["\\])') - def dump(self, obj: List[Any]) -> Optional[Buffer]: + def dump(self, obj: List[Any]) -> Buffer | None: tokens: List[Buffer] = [] needs_quotes = _get_needs_quotes_regexp(self.delimiter).search @@ -187,7 +189,7 @@ class ListDumper(BaseListDumper): return b"".join(tokens) - def _dump_item(self, item: Any) -> Optional[Buffer]: + def _dump_item(self, item: Any) -> Buffer | None: if self.sub_dumper: return self.sub_dumper.dump(item) else: @@ -245,7 +247,7 @@ class ListBinaryDumper(BaseListDumper): return dumper - def dump(self, obj: List[Any]) -> Optional[Buffer]: + def dump(self, obj: List[Any]) -> Buffer | None: # Postgres won't take unknown for element oid: fall back on text sub_oid = self.sub_dumper and self.sub_dumper.oid or TEXT_OID @@ -310,7 +312,7 @@ class ArrayBinaryLoader(RecursiveLoader): return _load_binary(data, self._tx) -def register_array(info: TypeInfo, context: Optional[AdaptContext] = None) -> None: +def register_array(info: TypeInfo, context: AdaptContext | None = None) -> None: if not info.array_oid: raise ValueError(f"the type info {info} doesn't describe an array") diff --git a/psycopg/psycopg/types/bool.py b/psycopg/psycopg/types/bool.py index c05679354..8f367b80e 100644 --- a/psycopg/psycopg/types/bool.py +++ b/psycopg/psycopg/types/bool.py @@ -4,8 +4,9 @@ Adapters for booleans. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + from .. import _oids -from typing import Optional from ..pq import Format from ..abc import AdaptContext from ..adapt import Buffer, Dumper, Loader @@ -14,7 +15,7 @@ from ..adapt import Buffer, Dumper, Loader class BoolDumper(Dumper): oid = _oids.BOOL_OID - def dump(self, obj: bool) -> Optional[Buffer]: + def dump(self, obj: bool) -> Buffer | None: return b"t" if obj else b"f" def quote(self, obj: bool) -> Buffer: @@ -25,7 +26,7 @@ class BoolBinaryDumper(Dumper): format = Format.BINARY oid = _oids.BOOL_OID - def dump(self, obj: bool) -> Optional[Buffer]: + def dump(self, obj: bool) -> Buffer | None: return b"\x01" if obj else b"\x00" diff --git a/psycopg/psycopg/types/composite.py b/psycopg/psycopg/types/composite.py index 174727cfb..6dd8642c6 100644 --- a/psycopg/psycopg/types/composite.py +++ b/psycopg/psycopg/types/composite.py @@ -4,10 +4,12 @@ Support for composite types adaptation. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re import struct from collections import namedtuple -from typing import Any, Callable, cast, Dict, Iterator, List, Optional +from typing import Any, Callable, cast, Dict, Iterator, List from typing import NamedTuple, Sequence, Tuple, Type, TYPE_CHECKING from .. import pq @@ -48,7 +50,7 @@ class CompositeInfo(TypeInfo): self.field_names = field_names self.field_types = field_types # Will be set by register() if the `factory` is a type - self.python_type: Optional[type] = None + self.python_type: type | None = None @classmethod def _get_info_query(cls, conn: "BaseConnection[Any]") -> abc.Query: @@ -119,7 +121,7 @@ class TupleDumper(SequenceDumper): # Should be this, but it doesn't work # oid = _oids.RECORD_OID - def dump(self, obj: Tuple[Any, ...]) -> Optional[Buffer]: + def dump(self, obj: Tuple[Any, ...]) -> Buffer | None: return self._dump_sequence(obj, b"(", b")", b",") @@ -129,7 +131,7 @@ class TupleBinaryDumper(Dumper): # Subclasses must set this info _field_types: Tuple[int, ...] - def __init__(self, cls: type, context: Optional[abc.AdaptContext] = None): + def __init__(self, cls: type, context: abc.AdaptContext | None = None): super().__init__(cls, context) # Note: this class is not a RecursiveDumper because it would use the @@ -142,7 +144,7 @@ class TupleBinaryDumper(Dumper): nfields = len(self._field_types) self._formats = (PyFormat.from_pq(self.format),) * nfields - def dump(self, obj: Tuple[Any, ...]) -> Optional[Buffer]: + def dump(self, obj: Tuple[Any, ...]) -> Buffer | None: out = bytearray(pack_len(len(obj))) adapted = self._tx.dump_sequence(obj, self._formats) for i in range(len(obj)): @@ -158,11 +160,11 @@ class TupleBinaryDumper(Dumper): class BaseCompositeLoader(Loader): - def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): + def __init__(self, oid: int, context: abc.AdaptContext | None = None): super().__init__(oid, context) self._tx = Transformer(context) - def _parse_record(self, data: abc.Buffer) -> Iterator[Optional[bytes]]: + def _parse_record(self, data: abc.Buffer) -> Iterator[bytes | None]: """ Split a non-empty representation of a composite type into components. @@ -208,7 +210,7 @@ class RecordLoader(BaseCompositeLoader): class RecordBinaryLoader(Loader): format = pq.Format.BINARY - def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): + def __init__(self, oid: int, context: abc.AdaptContext | None = None): super().__init__(oid, context) self._ctx = context # Cache a transformer for each sequence of oid found. @@ -272,8 +274,8 @@ class CompositeBinaryLoader(RecordBinaryLoader): def register_composite( info: CompositeInfo, - context: Optional[abc.AdaptContext] = None, - factory: Optional[Callable[..., Any]] = None, + context: abc.AdaptContext | None = None, + factory: Callable[..., Any] | None = None, ) -> None: """Register the adapters to load and dump a composite type. diff --git a/psycopg/psycopg/types/datetime.py b/psycopg/psycopg/types/datetime.py index b3bfa1b8b..55f8e9259 100644 --- a/psycopg/psycopg/types/datetime.py +++ b/psycopg/psycopg/types/datetime.py @@ -4,10 +4,12 @@ Adapters for date/time types. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re import struct from datetime import date, datetime, time, timedelta, timezone -from typing import Any, Callable, cast, Optional, Tuple, TYPE_CHECKING +from typing import Any, Callable, cast, Tuple, TYPE_CHECKING from .. import _oids from ..pq import Format @@ -40,7 +42,7 @@ _py_date_min_days = date.min.toordinal() class DateDumper(Dumper): oid = _oids.DATE_OID - def dump(self, obj: date) -> Optional[Buffer]: + def dump(self, obj: date) -> Buffer | None: # NOTE: whatever the PostgreSQL DateStyle input format (DMY, MDY, YMD) # the YYYY-MM-DD is always understood correctly. return str(obj).encode() @@ -50,7 +52,7 @@ class DateBinaryDumper(Dumper): format = Format.BINARY oid = _oids.DATE_OID - def dump(self, obj: date) -> Optional[Buffer]: + def dump(self, obj: date) -> Buffer | None: days = obj.toordinal() - _pg_date_epoch_days return pack_int4(days) @@ -77,7 +79,7 @@ class _BaseTimeDumper(Dumper): class _BaseTimeTextDumper(_BaseTimeDumper): - def dump(self, obj: time) -> Optional[Buffer]: + def dump(self, obj: time) -> Buffer | None: return str(obj).encode() @@ -94,7 +96,7 @@ class TimeDumper(_BaseTimeTextDumper): class TimeTzDumper(_BaseTimeTextDumper): oid = _oids.TIMETZ_OID - def dump(self, obj: time) -> Optional[Buffer]: + def dump(self, obj: time) -> Buffer | None: self._get_offset(obj) return super().dump(obj) @@ -103,7 +105,7 @@ class TimeBinaryDumper(_BaseTimeDumper): format = Format.BINARY oid = _oids.TIME_OID - def dump(self, obj: time) -> Optional[Buffer]: + def dump(self, obj: time) -> Buffer | None: us = obj.microsecond + 1_000_000 * ( obj.second + 60 * (obj.minute + 60 * obj.hour) ) @@ -120,7 +122,7 @@ class TimeTzBinaryDumper(_BaseTimeDumper): format = Format.BINARY oid = _oids.TIMETZ_OID - def dump(self, obj: time) -> Optional[Buffer]: + def dump(self, obj: time) -> Buffer | None: us = obj.microsecond + 1_000_000 * ( obj.second + 60 * (obj.minute + 60 * obj.hour) ) @@ -142,7 +144,7 @@ class _BaseDatetimeDumper(Dumper): class _BaseDatetimeTextDumper(_BaseDatetimeDumper): - def dump(self, obj: datetime) -> Optional[Buffer]: + def dump(self, obj: datetime) -> Buffer | None: # NOTE: whatever the PostgreSQL DateStyle input format (DMY, MDY, YMD) # the YYYY-MM-DD is always understood correctly. return str(obj).encode() @@ -166,7 +168,7 @@ class DatetimeBinaryDumper(_BaseDatetimeDumper): format = Format.BINARY oid = _oids.TIMESTAMPTZ_OID - def dump(self, obj: datetime) -> Optional[Buffer]: + def dump(self, obj: datetime) -> Buffer | None: delta = obj - _pg_datetimetz_epoch micros = delta.microseconds + 1_000_000 * (86_400 * delta.days + delta.seconds) return pack_int8(micros) @@ -182,7 +184,7 @@ class DatetimeNoTzBinaryDumper(_BaseDatetimeDumper): format = Format.BINARY oid = _oids.TIMESTAMP_OID - def dump(self, obj: datetime) -> Optional[Buffer]: + def dump(self, obj: datetime) -> Buffer | None: delta = obj - _pg_datetime_epoch micros = delta.microseconds + 1_000_000 * (86_400 * delta.days + delta.seconds) return pack_int8(micros) @@ -191,14 +193,14 @@ class DatetimeNoTzBinaryDumper(_BaseDatetimeDumper): class TimedeltaDumper(Dumper): oid = _oids.INTERVAL_OID - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) if _get_intervalstyle(self.connection) == b"sql_standard": self._dump_method = self._dump_sql else: self._dump_method = self._dump_any - def dump(self, obj: timedelta) -> Optional[Buffer]: + def dump(self, obj: timedelta) -> Buffer | None: return self._dump_method(self, obj) @staticmethod @@ -222,7 +224,7 @@ class TimedeltaBinaryDumper(Dumper): format = Format.BINARY oid = _oids.INTERVAL_OID - def dump(self, obj: timedelta) -> Optional[Buffer]: + def dump(self, obj: timedelta) -> Buffer | None: micros = 1_000_000 * obj.seconds + obj.microseconds return _pack_interval(micros, obj.days, 0) @@ -232,7 +234,7 @@ class DateLoader(Loader): _ORDER_DMY = 1 _ORDER_MDY = 2 - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) ds = _get_datestyle(self.connection) if ds.startswith(b"I"): # ISO @@ -411,7 +413,7 @@ class TimestampLoader(Loader): _ORDER_PGDM = 3 _ORDER_PGMD = 4 - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) ds = _get_datestyle(self.connection) @@ -493,7 +495,7 @@ class TimestamptzLoader(Loader): """ ) - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) self._timezone = get_tzinfo(self.connection.pgconn if self.connection else None) @@ -565,7 +567,7 @@ class TimestamptzLoader(Loader): class TimestamptzBinaryLoader(Loader): format = Format.BINARY - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) self._timezone = get_tzinfo(self.connection.pgconn if self.connection else None) @@ -610,7 +612,7 @@ class IntervalLoader(Loader): re.VERBOSE, ) - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) if _get_intervalstyle(self.connection) == b"postgres": self._load_method = self._load_postgres @@ -676,7 +678,7 @@ class IntervalBinaryLoader(Loader): raise DataError(f"can't parse interval: {e}") from None -def _get_datestyle(conn: Optional["BaseConnection[Any]"]) -> bytes: +def _get_datestyle(conn: "BaseConnection[Any]" | None) -> bytes: if conn: ds = conn.pgconn.parameter_status(b"DateStyle") if ds: @@ -685,7 +687,7 @@ def _get_datestyle(conn: Optional["BaseConnection[Any]"]) -> bytes: return b"ISO, DMY" -def _get_intervalstyle(conn: Optional["BaseConnection[Any]"]) -> bytes: +def _get_intervalstyle(conn: "BaseConnection[Any]" | None) -> bytes: if conn: ints = conn.pgconn.parameter_status(b"IntervalStyle") if ints: @@ -695,7 +697,7 @@ def _get_intervalstyle(conn: Optional["BaseConnection[Any]"]) -> bytes: def _get_timestamp_load_error( - conn: Optional["BaseConnection[Any]"], data: Buffer, ex: Optional[Exception] = None + conn: "BaseConnection[Any]" | None, data: Buffer, ex: Exception | None = None ) -> Exception: s = bytes(data).decode("utf8", "replace") diff --git a/psycopg/psycopg/types/enum.py b/psycopg/psycopg/types/enum.py index 04616419d..0ae3d9517 100644 --- a/psycopg/psycopg/types/enum.py +++ b/psycopg/psycopg/types/enum.py @@ -2,8 +2,10 @@ Adapters for the enum type. """ +from __future__ import annotations + from enum import Enum -from typing import Any, Dict, Generic, Optional, Mapping, Sequence +from typing import Any, Dict, Generic, Mapping, Sequence from typing import Tuple, Type, Union, cast, TYPE_CHECKING from .. import sql @@ -46,7 +48,7 @@ class EnumInfo(TypeInfo): super().__init__(name, oid, array_oid) self.labels = labels # Will be set by register_enum() - self.enum: Optional[Type[Enum]] = None + self.enum: Type[Enum] | None = None @classmethod def _get_info_query(cls, conn: "BaseConnection[Any]") -> Query: @@ -98,7 +100,7 @@ class _BaseEnumDumper(Dumper, Generic[E]): enum: Type[E] _dump_map: EnumDumpMap[E] - def dump(self, value: E) -> Optional[Buffer]: + def dump(self, value: E) -> Buffer | None: return self._dump_map[value] @@ -107,11 +109,11 @@ class EnumDumper(Dumper): Dumper for a generic Enum class """ - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) self._encoding = conn_encoding(self.connection) - def dump(self, value: E) -> Optional[Buffer]: + def dump(self, value: E) -> Buffer | None: return value.name.encode(self._encoding) @@ -121,8 +123,8 @@ class EnumBinaryDumper(EnumDumper): def register_enum( info: EnumInfo, - context: Optional[AdaptContext] = None, - enum: Optional[Type[E]] = None, + context: AdaptContext | None = None, + enum: Type[E] | None = None, *, mapping: EnumMapping[E] = None, ) -> None: @@ -209,7 +211,7 @@ def _make_load_map( info: EnumInfo, enum: Type[E], mapping: EnumMapping[E], - context: Optional[AdaptContext], + context: AdaptContext | None, ) -> _HEnumLoadMap[E]: enc = conn_encoding(context.connection if context else None) rv = [] @@ -237,7 +239,7 @@ def _make_dump_map( info: EnumInfo, enum: Type[E], mapping: EnumMapping[E], - context: Optional[AdaptContext], + context: AdaptContext | None, ) -> _HEnumDumpMap[E]: enc = conn_encoding(context.connection if context else None) rv = [] diff --git a/psycopg/psycopg/types/hstore.py b/psycopg/psycopg/types/hstore.py index 2c52f79f1..526c0a688 100644 --- a/psycopg/psycopg/types/hstore.py +++ b/psycopg/psycopg/types/hstore.py @@ -4,8 +4,10 @@ Dict to hstore adaptation # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import re -from typing import Dict, List, Optional, Type +from typing import Dict, List, Type from .. import errors as e from .. import postgres @@ -35,11 +37,11 @@ _re_hstore = re.compile( ) -Hstore: TypeAlias = Dict[str, Optional[str]] +Hstore: TypeAlias = Dict[str, str | None] class BaseHstoreDumper(RecursiveDumper): - def dump(self, obj: Hstore) -> Optional[Buffer]: + def dump(self, obj: Hstore) -> Buffer | None: if not obj: return b"" @@ -96,7 +98,7 @@ class HstoreLoader(RecursiveLoader): return rv -def register_hstore(info: TypeInfo, context: Optional[AdaptContext] = None) -> None: +def register_hstore(info: TypeInfo, context: AdaptContext | None = None) -> None: """Register the adapters to load and dump hstore. :param info: The object with the information about the hstore type. diff --git a/psycopg/psycopg/types/json.py b/psycopg/psycopg/types/json.py index 7ac2be63f..8e2422de4 100644 --- a/psycopg/psycopg/types/json.py +++ b/psycopg/psycopg/types/json.py @@ -4,8 +4,10 @@ Adapters for JSON types. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import json -from typing import Any, Callable, Dict, Optional, Tuple, Type, Union +from typing import Any, Callable, Dict, Tuple, Type, Union from .. import abc from .. import _oids @@ -20,7 +22,7 @@ JsonLoadsFunction = Callable[[Union[str, bytes]], Any] def set_json_dumps( - dumps: JsonDumpsFunction, context: Optional[abc.AdaptContext] = None + dumps: JsonDumpsFunction, context: abc.AdaptContext | None = None ) -> None: """ Set the JSON serialisation function to store JSON objects in the database. @@ -59,7 +61,7 @@ def set_json_dumps( def set_json_loads( - loads: JsonLoadsFunction, context: Optional[abc.AdaptContext] = None + loads: JsonLoadsFunction, context: abc.AdaptContext | None = None ) -> None: """ Set the JSON parsing function to fetch JSON objects from the database. @@ -114,7 +116,7 @@ def _make_loader(base: Type[Loader], loads: JsonLoadsFunction) -> Type[Loader]: class _JsonWrapper: __slots__ = ("obj", "dumps") - def __init__(self, obj: Any, dumps: Optional[JsonDumpsFunction] = None): + def __init__(self, obj: Any, dumps: JsonDumpsFunction | None = None): self.obj = obj self.dumps = dumps @@ -138,11 +140,11 @@ class _JsonDumper(Dumper): # set_json_dumps) or by a subclass. _dumps: JsonDumpsFunction = json.dumps - def __init__(self, cls: type, context: Optional[abc.AdaptContext] = None): + def __init__(self, cls: type, context: abc.AdaptContext | None = None): super().__init__(cls, context) self.dumps = self.__class__._dumps - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: if isinstance(obj, _JsonWrapper): dumps = obj.dumps or self.dumps obj = obj.obj @@ -171,7 +173,7 @@ class JsonbBinaryDumper(_JsonDumper): format = Format.BINARY oid = _oids.JSONB_OID - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: obj_bytes = super().dump(obj) if obj_bytes is not None: return b"\x01" + obj_bytes @@ -184,7 +186,7 @@ class _JsonLoader(Loader): # set_json_loads) or by a subclass. _loads: JsonLoadsFunction = json.loads - def __init__(self, oid: int, context: Optional[abc.AdaptContext] = None): + def __init__(self, oid: int, context: abc.AdaptContext | None = None): super().__init__(oid, context) self.loads = self.__class__._loads diff --git a/psycopg/psycopg/types/multirange.py b/psycopg/psycopg/types/multirange.py index d37681009..5704c8001 100644 --- a/psycopg/psycopg/types/multirange.py +++ b/psycopg/psycopg/types/multirange.py @@ -4,9 +4,11 @@ Support for multirange types adaptation. # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + from decimal import Decimal from typing import Any, Generic, List, Iterable, MutableSequence -from typing import Optional, Type, Union, overload, TYPE_CHECKING +from typing import Type, Union, overload, TYPE_CHECKING from datetime import date, datetime from .. import sql @@ -183,9 +185,9 @@ class TimestamptzMultirange(Multirange[datetime]): class BaseMultirangeDumper(RecursiveDumper): - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) - self.sub_dumper: Optional[Dumper] = None + self.sub_dumper: Dumper | None = None self._adapt_format = PyFormat.from_pq(self.format) def get_key(self, obj: Multirange[Any], format: PyFormat) -> DumperKey: @@ -256,7 +258,7 @@ class MultirangeDumper(BaseMultirangeDumper): The dumper can upgrade to one specific for a different range type. """ - def dump(self, obj: Multirange[Any]) -> Optional[Buffer]: + def dump(self, obj: Multirange[Any]) -> Buffer | None: if not obj: return b"{}" @@ -277,7 +279,7 @@ class MultirangeDumper(BaseMultirangeDumper): class MultirangeBinaryDumper(BaseMultirangeDumper): format = Format.BINARY - def dump(self, obj: Multirange[Any]) -> Optional[Buffer]: + def dump(self, obj: Multirange[Any]) -> Buffer | None: item = self._get_item(obj) if item is not None: dump = self._tx.get_dumper(item, self._adapt_format).dump @@ -295,7 +297,7 @@ class MultirangeBinaryDumper(BaseMultirangeDumper): class BaseMultirangeLoader(RecursiveLoader, Generic[T]): subtype_oid: int - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) self._load = self._tx.get_loader(self.subtype_oid, format=self.format).load @@ -366,7 +368,7 @@ class MultirangeBinaryLoader(BaseMultirangeLoader[T]): def register_multirange( - info: MultirangeInfo, context: Optional[AdaptContext] = None + info: MultirangeInfo, context: AdaptContext | None = None ) -> None: """Register the adapters to load and dump a multirange type. diff --git a/psycopg/psycopg/types/net.py b/psycopg/psycopg/types/net.py index b8fc99204..593b272e9 100644 --- a/psycopg/psycopg/types/net.py +++ b/psycopg/psycopg/types/net.py @@ -4,7 +4,9 @@ Adapters for network types. # Copyright (C) 2020 The Psycopg Team -from typing import Callable, Optional, Type, Union, TYPE_CHECKING +from __future__ import annotations + +from typing import Callable, Type, Union, TYPE_CHECKING from .. import _oids from ..pq import Format @@ -52,14 +54,14 @@ class _LazyIpaddress: class InterfaceDumper(Dumper): oid = _oids.INET_OID - def dump(self, obj: Interface) -> Optional[Buffer]: + def dump(self, obj: Interface) -> Buffer | None: return str(obj).encode() class NetworkDumper(Dumper): oid = _oids.CIDR_OID - def dump(self, obj: Network) -> Optional[Buffer]: + def dump(self, obj: Network) -> Buffer | None: return str(obj).encode() @@ -69,7 +71,7 @@ class _AIBinaryDumper(Dumper): class AddressBinaryDumper(_AIBinaryDumper): - def dump(self, obj: Address) -> Optional[Buffer]: + def dump(self, obj: Address) -> Buffer | None: packed = obj.packed family = PGSQL_AF_INET if obj.version == 4 else PGSQL_AF_INET6 head = bytes((family, obj.max_prefixlen, 0, len(packed))) @@ -77,7 +79,7 @@ class AddressBinaryDumper(_AIBinaryDumper): class InterfaceBinaryDumper(_AIBinaryDumper): - def dump(self, obj: Interface) -> Optional[Buffer]: + def dump(self, obj: Interface) -> Buffer | None: packed = obj.packed family = PGSQL_AF_INET if obj.version == 4 else PGSQL_AF_INET6 head = bytes((family, obj.network.prefixlen, 0, len(packed))) @@ -90,11 +92,11 @@ class InetBinaryDumper(_AIBinaryDumper, _LazyIpaddress): Used when looking up by oid. """ - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) self._ensure_module() - def dump(self, obj: Union[Address, Interface]) -> Optional[Buffer]: + def dump(self, obj: Union[Address, Interface]) -> Buffer | None: packed = obj.packed family = PGSQL_AF_INET if obj.version == 4 else PGSQL_AF_INET6 if isinstance(obj, (IPv4Interface, IPv6Interface)): @@ -110,7 +112,7 @@ class NetworkBinaryDumper(Dumper): format = Format.BINARY oid = _oids.CIDR_OID - def dump(self, obj: Network) -> Optional[Buffer]: + def dump(self, obj: Network) -> Buffer | None: packed = obj.network_address.packed family = PGSQL_AF_INET if obj.version == 4 else PGSQL_AF_INET6 head = bytes((family, obj.prefixlen, 1, len(packed))) @@ -118,7 +120,7 @@ class NetworkBinaryDumper(Dumper): class _LazyIpaddressLoader(Loader, _LazyIpaddress): - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) self._ensure_module() diff --git a/psycopg/psycopg/types/none.py b/psycopg/psycopg/types/none.py index b65f6315b..1669abb8d 100644 --- a/psycopg/psycopg/types/none.py +++ b/psycopg/psycopg/types/none.py @@ -4,7 +4,7 @@ Adapters for None. # Copyright (C) 2020 The Psycopg Team -from typing import Optional +from __future__ import annotations from ..abc import AdaptContext, NoneType, Buffer from ..adapt import Dumper @@ -16,7 +16,7 @@ class NoneDumper(Dumper): quote(), so it can be used in sql composition. """ - def dump(self, obj: None) -> Optional[Buffer]: + def dump(self, obj: None) -> Buffer | None: raise NotImplementedError("NULL is passed to Postgres in other ways") def quote(self, obj: None) -> Buffer: diff --git a/psycopg/psycopg/types/numeric.py b/psycopg/psycopg/types/numeric.py index 4bfef8e2f..c3ece2991 100644 --- a/psycopg/psycopg/types/numeric.py +++ b/psycopg/psycopg/types/numeric.py @@ -4,11 +4,13 @@ Adapters for numeric types. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import sys import struct from abc import ABC, abstractmethod from math import log -from typing import Any, Callable, DefaultDict, Dict, Optional, Tuple, Union +from typing import Any, Callable, DefaultDict, Dict, Tuple, Union from typing import cast, TYPE_CHECKING from decimal import Decimal, DefaultContext, Context @@ -38,7 +40,7 @@ if TYPE_CHECKING: class _IntDumper(Dumper): - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: return str(obj).encode() def quote(self, obj: Any) -> Buffer: @@ -49,7 +51,7 @@ class _IntDumper(Dumper): class _IntOrSubclassDumper(_IntDumper): - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: t = type(obj) # Convert to int in order to dump IntEnum or numpy.integer correctly if t is not int: @@ -61,7 +63,7 @@ class _IntOrSubclassDumper(_IntDumper): class _SpecialValuesDumper(Dumper): _special: Dict[bytes, bytes] = {} - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: return str(obj).encode() def quote(self, obj: Any) -> Buffer: @@ -95,21 +97,21 @@ class FloatBinaryDumper(Dumper): format = Format.BINARY oid = _oids.FLOAT8_OID - def dump(self, obj: float) -> Optional[Buffer]: + def dump(self, obj: float) -> Buffer | None: return pack_float8(obj) class Float4BinaryDumper(FloatBinaryDumper): oid = _oids.FLOAT4_OID - def dump(self, obj: float) -> Optional[Buffer]: + def dump(self, obj: float) -> Buffer | None: return pack_float4(obj) class DecimalDumper(_SpecialValuesDumper): oid = _oids.NUMERIC_OID - def dump(self, obj: Decimal) -> Optional[Buffer]: + def dump(self, obj: Decimal) -> Buffer | None: return dump_decimal_to_text(obj) _special = { @@ -140,7 +142,7 @@ class OidDumper(_IntOrSubclassDumper): class IntDumper(Dumper): - def dump(self, obj: Any) -> Optional[Buffer]: + def dump(self, obj: Any) -> Buffer | None: raise TypeError( f"{type(self).__name__} is a dispatcher to other dumpers:" " dump() is not supposed to be called" @@ -170,21 +172,21 @@ class IntDumper(Dumper): class Int2BinaryDumper(Int2Dumper): format = Format.BINARY - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: return pack_int2(obj) class Int4BinaryDumper(Int4Dumper): format = Format.BINARY - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: return pack_int4(obj) class Int8BinaryDumper(Int8Dumper): format = Format.BINARY - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: return pack_int8(obj) @@ -196,14 +198,14 @@ BIT_PER_PGDIGIT = log(2) / log(10_000) class IntNumericBinaryDumper(IntNumericDumper): format = Format.BINARY - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: return dump_int_to_numeric_binary(obj) class OidBinaryDumper(OidDumper): format = Format.BINARY - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: return pack_uint4(obj) @@ -356,7 +358,7 @@ class DecimalBinaryDumper(Dumper): format = Format.BINARY oid = _oids.NUMERIC_OID - def dump(self, obj: Decimal) -> Optional[Buffer]: + def dump(self, obj: Decimal) -> Buffer | None: return dump_decimal_to_numeric_binary(obj) @@ -371,7 +373,7 @@ class _MixedNumericDumper(Dumper, ABC): # If numpy is available, the dumped object might be a numpy integer too int_classes: Union[type, Tuple[type, ...]] = () - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) # Verify if numpy is available. If it is, we might have to dump @@ -385,13 +387,11 @@ class _MixedNumericDumper(Dumper, ABC): _MixedNumericDumper.int_classes = int @abstractmethod - def dump( - self, obj: Union[Decimal, int, "numpy.integer[Any]"] - ) -> Optional[Buffer]: ... + def dump(self, obj: Union[Decimal, int, "numpy.integer[Any]"]) -> Buffer | None: ... class NumericDumper(_MixedNumericDumper): - def dump(self, obj: Union[Decimal, int, "numpy.integer[Any]"]) -> Optional[Buffer]: + def dump(self, obj: Union[Decimal, int, "numpy.integer[Any]"]) -> Buffer | None: if isinstance(obj, self.int_classes): return str(obj).encode() elif isinstance(obj, Decimal): @@ -405,7 +405,7 @@ class NumericDumper(_MixedNumericDumper): class NumericBinaryDumper(_MixedNumericDumper): format = Format.BINARY - def dump(self, obj: Union[Decimal, int, "numpy.integer[Any]"]) -> Optional[Buffer]: + def dump(self, obj: Union[Decimal, int, "numpy.integer[Any]"]) -> Buffer | None: if type(obj) is int: return dump_int_to_numeric_binary(obj) elif isinstance(obj, Decimal): diff --git a/psycopg/psycopg/types/range.py b/psycopg/psycopg/types/range.py index dba34139c..20b375aa2 100644 --- a/psycopg/psycopg/types/range.py +++ b/psycopg/psycopg/types/range.py @@ -4,8 +4,10 @@ Support for range types adaptation. # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations + import re -from typing import Any, Dict, Generic, List, Optional, Type, Tuple +from typing import Any, Dict, Generic, List, Type, Tuple from typing import cast, TYPE_CHECKING from decimal import Decimal from datetime import date, datetime @@ -84,8 +86,8 @@ class Range(Generic[T]): def __init__( self, - lower: Optional[T] = None, - upper: Optional[T] = None, + lower: T | None = None, + upper: T | None = None, bounds: str = "[)", empty: bool = False, ): @@ -129,12 +131,12 @@ class Range(Generic[T]): return "".join(items) @property - def lower(self) -> Optional[T]: + def lower(self) -> T | None: """The lower bound of the range. `!None` if empty or unbound.""" return self._lower @property - def upper(self) -> Optional[T]: + def upper(self) -> T | None: """The upper bound of the range. `!None` if empty or unbound.""" return self._upper @@ -285,9 +287,9 @@ class TimestamptzRange(Range[datetime]): class BaseRangeDumper(RecursiveDumper): - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) - self.sub_dumper: Optional[Dumper] = None + self.sub_dumper: Dumper | None = None self._adapt_format = PyFormat.from_pq(self.format) def get_key(self, obj: Range[Any], format: PyFormat) -> DumperKey: @@ -354,7 +356,7 @@ class RangeDumper(BaseRangeDumper): The dumper can upgrade to one specific for a different range type. """ - def dump(self, obj: Range[Any]) -> Optional[Buffer]: + def dump(self, obj: Range[Any]) -> Buffer | None: item = self._get_item(obj) if item is not None: dump = self._tx.get_dumper(item, self._adapt_format).dump @@ -401,7 +403,7 @@ _re_esc = re.compile(rb"([\\\"])") class RangeBinaryDumper(BaseRangeDumper): format = Format.BINARY - def dump(self, obj: Range[Any]) -> Optional[Buffer]: + def dump(self, obj: Range[Any]) -> Buffer | None: item = self._get_item(obj) if item is not None: dump = self._tx.get_dumper(item, self._adapt_format).dump @@ -459,7 +461,7 @@ class BaseRangeLoader(RecursiveLoader, Generic[T]): subtype_oid: int - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) self._load = self._tx.get_loader(self.subtype_oid, format=self.format).load @@ -557,7 +559,7 @@ def load_range_binary(data: Buffer, load: LoadFunc) -> Range[Any]: return Range(min, max, lb + ub) -def register_range(info: RangeInfo, context: Optional[AdaptContext] = None) -> None: +def register_range(info: RangeInfo, context: AdaptContext | None = None) -> None: """Register the adapters to load and dump a range type. :param info: The object with the information about the range to register. diff --git a/psycopg/psycopg/types/shapely.py b/psycopg/psycopg/types/shapely.py index 571445413..107ad520c 100644 --- a/psycopg/psycopg/types/shapely.py +++ b/psycopg/psycopg/types/shapely.py @@ -2,7 +2,9 @@ Adapters for PostGIS geometries """ -from typing import Optional, Type +from __future__ import annotations + +from typing import Type from .. import postgres from ..abc import AdaptContext, Buffer @@ -11,7 +13,6 @@ from ..pq import Format from .._compat import cache from .._typeinfo import TypeInfo - try: from shapely.wkb import loads, dumps from shapely.geometry.base import BaseGeometry @@ -43,16 +44,16 @@ class GeometryLoader(Loader): class BaseGeometryBinaryDumper(Dumper): format = Format.BINARY - def dump(self, obj: "BaseGeometry") -> Optional[Buffer]: + def dump(self, obj: "BaseGeometry") -> Buffer | None: return dumps(obj) # type: ignore class BaseGeometryDumper(Dumper): - def dump(self, obj: "BaseGeometry") -> Optional[Buffer]: + def dump(self, obj: "BaseGeometry") -> Buffer | None: return dumps(obj, hex=True).encode() # type: ignore -def register_shapely(info: TypeInfo, context: Optional[AdaptContext] = None) -> None: +def register_shapely(info: TypeInfo, context: AdaptContext | None = None) -> None: """Register Shapely dumper and loaders.""" # A friendly error warning instead of an AttributeError in case fetch() diff --git a/psycopg/psycopg/types/string.py b/psycopg/psycopg/types/string.py index 88548180a..2d17a94fc 100644 --- a/psycopg/psycopg/types/string.py +++ b/psycopg/psycopg/types/string.py @@ -4,7 +4,9 @@ Adapters for textual types. # Copyright (C) 2020 The Psycopg Team -from typing import Optional, Union, TYPE_CHECKING +from __future__ import annotations + +from typing import Union, TYPE_CHECKING from .. import _oids from ..pq import Format, Escaping @@ -18,7 +20,7 @@ if TYPE_CHECKING: class _BaseStrDumper(Dumper): - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) enc = conn_encoding(self.connection) self._encoding = enc if enc != "ascii" else "utf-8" @@ -33,7 +35,7 @@ class _StrBinaryDumper(_BaseStrDumper): format = Format.BINARY - def dump(self, obj: str) -> Optional[Buffer]: + def dump(self, obj: str) -> Buffer | None: # the server will raise DataError subclass if the string contains 0x00 return obj.encode(self._encoding) @@ -45,7 +47,7 @@ class _StrDumper(_BaseStrDumper): Subclasses shall specify the oids of real types (text, varchar, name...). """ - def dump(self, obj: str) -> Optional[Buffer]: + def dump(self, obj: str) -> Buffer | None: if "\x00" in obj: raise DataError("PostgreSQL text fields cannot contain NUL (0x00) bytes") else: @@ -103,7 +105,7 @@ class StrDumperUnknown(_StrDumper): class TextLoader(Loader): - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) enc = conn_encoding(self.connection) self._encoding = enc if enc != "ascii" else "" @@ -128,11 +130,11 @@ class BytesDumper(Dumper): oid = _oids.BYTEA_OID _qprefix = b"" - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): super().__init__(cls, context) self._esc = Escaping(self.connection.pgconn if self.connection else None) - def dump(self, obj: Buffer) -> Optional[Buffer]: + def dump(self, obj: Buffer) -> Buffer | None: return self._esc.escape_bytea(obj) def quote(self, obj: Buffer) -> Buffer: @@ -167,14 +169,14 @@ class BytesBinaryDumper(Dumper): format = Format.BINARY oid = _oids.BYTEA_OID - def dump(self, obj: Buffer) -> Optional[Buffer]: + def dump(self, obj: Buffer) -> Buffer | None: return obj class ByteaLoader(Loader): _escaping: "EscapingProto" - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) if not hasattr(self.__class__, "_escaping"): self.__class__._escaping = Escaping() diff --git a/psycopg/psycopg/types/uuid.py b/psycopg/psycopg/types/uuid.py index df4509ee5..fb9485d8e 100644 --- a/psycopg/psycopg/types/uuid.py +++ b/psycopg/psycopg/types/uuid.py @@ -4,7 +4,9 @@ Adapters for the UUID type. # Copyright (C) 2020 The Psycopg Team -from typing import Callable, Optional, TYPE_CHECKING +from __future__ import annotations + +from typing import Callable, TYPE_CHECKING from .. import _oids from ..pq import Format @@ -21,19 +23,19 @@ UUID: Callable[..., "uuid.UUID"] = None # type: ignore[assignment] class UUIDDumper(Dumper): oid = _oids.UUID_OID - def dump(self, obj: "uuid.UUID") -> Optional[Buffer]: + def dump(self, obj: "uuid.UUID") -> Buffer | None: return obj.hex.encode() class UUIDBinaryDumper(UUIDDumper): format = Format.BINARY - def dump(self, obj: "uuid.UUID") -> Optional[Buffer]: + def dump(self, obj: "uuid.UUID") -> Buffer | None: return obj.bytes class UUIDLoader(Loader): - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): super().__init__(oid, context) global UUID if UUID is None: diff --git a/psycopg/psycopg/waiting.py b/psycopg/psycopg/waiting.py index 1247a8dd3..0a808346e 100644 --- a/psycopg/psycopg/waiting.py +++ b/psycopg/psycopg/waiting.py @@ -8,13 +8,13 @@ These functions are designed to consume the generators returned by the # Copyright (C) 2020 The Psycopg Team +from __future__ import annotations import os import sys import select import logging import selectors -from typing import Optional from asyncio import get_event_loop, wait_for, Event, TimeoutError from selectors import DefaultSelector @@ -34,7 +34,7 @@ READY_RW = Ready.RW logger = logging.getLogger(__name__) -def wait_selector(gen: PQGen[RV], fileno: int, interval: Optional[float] = None) -> RV: +def wait_selector(gen: PQGen[RV], fileno: int, interval: float | None = None) -> RV: """ Wait for a generator using the best strategy available. @@ -68,7 +68,7 @@ def wait_selector(gen: PQGen[RV], fileno: int, interval: Optional[float] = None) return rv -def wait_conn(gen: PQGenConn[RV], interval: Optional[float] = None) -> RV: +def wait_conn(gen: PQGenConn[RV], interval: float | None = None) -> RV: """ Wait for a connection generator using the best strategy available. @@ -103,9 +103,7 @@ def wait_conn(gen: PQGenConn[RV], interval: Optional[float] = None) -> RV: return rv -async def wait_async( - gen: PQGen[RV], fileno: int, interval: Optional[float] = None -) -> RV: +async def wait_async(gen: PQGen[RV], fileno: int, interval: float | None = None) -> RV: """ Coroutine waiting for a generator to complete. @@ -166,7 +164,7 @@ async def wait_async( return rv -async def wait_conn_async(gen: PQGenConn[RV], interval: Optional[float] = None) -> RV: +async def wait_conn_async(gen: PQGenConn[RV], interval: float | None = None) -> RV: """ Coroutine waiting for a connection generator to complete. @@ -227,7 +225,7 @@ async def wait_conn_async(gen: PQGenConn[RV], interval: Optional[float] = None) # Specialised implementation of wait functions. -def wait_select(gen: PQGen[RV], fileno: int, interval: Optional[float] = None) -> RV: +def wait_select(gen: PQGen[RV], fileno: int, interval: float | None = None) -> RV: """ Wait for a generator using select where supported. @@ -272,7 +270,7 @@ else: _epoll_evmasks = {} -def wait_epoll(gen: PQGen[RV], fileno: int, interval: Optional[float] = None) -> RV: +def wait_epoll(gen: PQGen[RV], fileno: int, interval: float | None = None) -> RV: """ Wait for a generator using epoll where supported. @@ -327,7 +325,7 @@ else: _poll_evmasks = {} -def wait_poll(gen: PQGen[RV], fileno: int, interval: Optional[float] = None) -> RV: +def wait_poll(gen: PQGen[RV], fileno: int, interval: float | None = None) -> RV: """ Wait for a generator using poll where supported. diff --git a/psycopg_c/psycopg_c/_psycopg.pyi b/psycopg_c/psycopg_c/_psycopg.pyi index 7ff2994eb..50834c6b4 100644 --- a/psycopg_c/psycopg_c/_psycopg.pyi +++ b/psycopg_c/psycopg_c/_psycopg.pyi @@ -7,7 +7,7 @@ information. Will submit a bug. # Copyright (C) 2020 The Psycopg Team -from typing import Any, List, Optional, Sequence, Tuple +from typing import Any, List, Sequence, Tuple from psycopg import pq, abc, BaseConnection from psycopg.rows import Row, RowMaker @@ -16,38 +16,36 @@ from psycopg.pq.abc import PGcancelConn, PGconn, PGresult from psycopg._compat import Deque class Transformer(abc.AdaptContext): - types: Optional[Tuple[int, ...]] - formats: Optional[List[pq.Format]] - def __init__(self, context: Optional[abc.AdaptContext] = None): ... + types: Tuple[int, ...] | None + formats: List[pq.Format] | None + def __init__(self, context: abc.AdaptContext | None = None): ... @classmethod - def from_context(cls, context: Optional[abc.AdaptContext]) -> "Transformer": ... + def from_context(cls, context: abc.AdaptContext | None) -> "Transformer": ... @property - def connection(self) -> Optional[BaseConnection[Any]]: ... + def connection(self) -> BaseConnection[Any] | None: ... @property def encoding(self) -> str: ... @property def adapters(self) -> AdaptersMap: ... @property - def pgresult(self) -> Optional[PGresult]: ... + def pgresult(self) -> PGresult | None: ... def set_pgresult( self, - result: Optional["PGresult"], + result: "PGresult" | None, *, set_loaders: bool = True, - format: Optional[pq.Format] = None, + format: pq.Format | None = None, ) -> None: ... def set_dumper_types(self, types: Sequence[int], format: pq.Format) -> None: ... def set_loader_types(self, types: Sequence[int], format: pq.Format) -> None: ... def dump_sequence( self, params: Sequence[Any], formats: Sequence[PyFormat] - ) -> Sequence[Optional[abc.Buffer]]: ... + ) -> Sequence[abc.Buffer | None]: ... def as_literal(self, obj: Any) -> bytes: ... def get_dumper(self, obj: Any, format: PyFormat) -> abc.Dumper: ... def load_rows(self, row0: int, row1: int, make_row: RowMaker[Row]) -> List[Row]: ... - def load_row(self, row: int, make_row: RowMaker[Row]) -> Optional[Row]: ... - def load_sequence( - self, record: Sequence[Optional[abc.Buffer]] - ) -> Tuple[Any, ...]: ... + def load_row(self, row: int, make_row: RowMaker[Row]) -> Row | None: ... + def load_sequence(self, record: Sequence[abc.Buffer | None]) -> Tuple[Any, ...]: ... def get_loader(self, oid: int, format: pq.Format) -> abc.Loader: ... # Generators @@ -58,20 +56,20 @@ def cancel( def execute(pgconn: PGconn) -> abc.PQGen[List[PGresult]]: ... def send(pgconn: PGconn) -> abc.PQGen[None]: ... def fetch_many(pgconn: PGconn) -> abc.PQGen[List[PGresult]]: ... -def fetch(pgconn: PGconn) -> abc.PQGen[Optional[PGresult]]: ... +def fetch(pgconn: PGconn) -> abc.PQGen[PGresult | None]: ... def pipeline_communicate( pgconn: PGconn, commands: Deque[abc.PipelineCommand] ) -> abc.PQGen[List[List[PGresult]]]: ... def wait_c( - gen: abc.PQGen[abc.RV], fileno: int, interval: Optional[float] = None + gen: abc.PQGen[abc.RV], fileno: int, interval: float | None = None ) -> abc.RV: ... # Copy support def format_row_text( - row: Sequence[Any], tx: abc.Transformer, out: Optional[bytearray] = None + row: Sequence[Any], tx: abc.Transformer, out: bytearray | None = None ) -> bytearray: ... def format_row_binary( - row: Sequence[Any], tx: abc.Transformer, out: Optional[bytearray] = None + row: Sequence[Any], tx: abc.Transformer, out: bytearray | None = None ) -> bytearray: ... def parse_row_text(data: abc.Buffer, tx: abc.Transformer) -> Tuple[Any, ...]: ... def parse_row_binary(data: abc.Buffer, tx: abc.Transformer) -> Tuple[Any, ...]: ... diff --git a/psycopg_c/psycopg_c/_psycopg/adapt.pyx b/psycopg_c/psycopg_c/_psycopg/adapt.pyx index e32ef19d1..3f587d2c4 100644 --- a/psycopg_c/psycopg_c/_psycopg/adapt.pyx +++ b/psycopg_c/psycopg_c/_psycopg/adapt.pyx @@ -35,7 +35,7 @@ cdef class CDumper: oid = oids.INVALID_OID - def __cinit__(self, cls, context: Optional[AdaptContext] = None): + def __cinit__(self, cls, context: AdaptContext | None = None): self.cls = cls conn = context.connection if context is not None else None self._pgconn = conn.pgconn if conn is not None else None @@ -58,7 +58,7 @@ cdef class CDumper: """ raise NotImplementedError() - def dump(self, obj) -> Optional[Buffer]: + def dump(self, obj) -> Buffer | None: """Return the Postgres representation of *obj* as Python array of bytes""" cdef rv = PyByteArray_FromStringAndSize("", 0) cdef Py_ssize_t length = self.cdump(obj, rv, 0) @@ -148,7 +148,7 @@ cdef class CLoader: cdef public libpq.Oid oid cdef pq.PGconn _pgconn - def __cinit__(self, libpq.Oid oid, context: Optional[AdaptContext] = None): + def __cinit__(self, libpq.Oid oid, context: AdaptContext | None = None): self.oid = oid conn = context.connection if context is not None else None self._pgconn = conn.pgconn if conn is not None else None @@ -167,5 +167,5 @@ cdef class _CRecursiveLoader(CLoader): cdef Transformer _tx - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): self._tx = Transformer.from_context(context) diff --git a/psycopg_c/psycopg_c/_psycopg/generators.pyx b/psycopg_c/psycopg_c/_psycopg/generators.pyx index df06b1d1c..df210f698 100644 --- a/psycopg_c/psycopg_c/_psycopg/generators.pyx +++ b/psycopg_c/psycopg_c/_psycopg/generators.pyx @@ -198,7 +198,7 @@ def fetch_many(pq.PGconn pgconn) -> PQGen[List[PGresult]]: return results -def fetch(pq.PGconn pgconn) -> PQGen[Optional[PGresult]]: +def fetch(pq.PGconn pgconn) -> PQGen[PGresult | None]: """ Generator retrieving a single result from the database without blocking. diff --git a/psycopg_c/psycopg_c/_psycopg/transform.pyx b/psycopg_c/psycopg_c/_psycopg/transform.pyx index a4fbe498e..c9849551e 100644 --- a/psycopg_c/psycopg_c/_psycopg/transform.pyx +++ b/psycopg_c/psycopg_c/_psycopg/transform.pyx @@ -19,7 +19,7 @@ from cpython.bytes cimport PyBytes_AS_STRING from cpython.tuple cimport PyTuple_New, PyTuple_SET_ITEM from cpython.object cimport PyObject, PyObject_CallFunctionObjArgs -from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple +from typing import Any, Dict, Iterable, List, Sequence, Tuple from psycopg import errors as e from psycopg.pq import Format as PqFormat @@ -98,7 +98,7 @@ cdef class Transformer: cdef dict _oid_types - def __cinit__(self, context: Optional["AdaptContext"] = None): + def __cinit__(self, context: "AdaptContext" | None = None): if context is not None: self.adapters = context.adapters self.connection = context.connection @@ -111,7 +111,7 @@ cdef class Transformer: self._none_oid = -1 @classmethod - def from_context(cls, context: Optional["AdaptContext"]): + def from_context(cls, context: "AdaptContext" | None): """ Return a Transformer from an AdaptContext. @@ -126,7 +126,7 @@ cdef class Transformer: return self._encoding @property - def pgresult(self) -> Optional[PGresult]: + def pgresult(self) -> PGresult | None: return self._pgresult cpdef set_pgresult( @@ -493,7 +493,7 @@ cdef class Transformer: Py_DECREF(brecord) return records - def load_row(self, int row, object make_row) -> Optional[Row]: + def load_row(self, int row, object make_row) -> Row | None: if self._pgresult is None: return None @@ -537,7 +537,7 @@ cdef class Transformer: make_row, record, NULL) return record - cpdef object load_sequence(self, record: Sequence[Optional[Buffer]]): + cpdef object load_sequence(self, record: Sequence[Buffer | None]): cdef Py_ssize_t nfields = len(record) out = PyTuple_New(nfields) cdef PyObject *loader # borrowed RowLoader diff --git a/psycopg_c/psycopg_c/pq/pgconn.pyx b/psycopg_c/psycopg_c/pq/pgconn.pyx index 6117abd1e..86e6910aa 100644 --- a/psycopg_c/psycopg_c/pq/pgconn.pyx +++ b/psycopg_c/psycopg_c/pq/pgconn.pyx @@ -87,7 +87,7 @@ cdef class PGconn: self._pgconn_ptr = NULL @property - def pgconn_ptr(self) -> Optional[int]: + def pgconn_ptr(self) -> int | None: if self._pgconn_ptr: return self._pgconn_ptr else: @@ -162,7 +162,7 @@ cdef class PGconn: def transaction_status(self) -> int: return libpq.PQtransactionStatus(self._pgconn_ptr) - def parameter_status(self, const char *name) -> Optional[bytes]: + def parameter_status(self, const char *name) -> bytes | None: _ensure_pgconn(self) cdef const char *rv = libpq.PQparameterStatus(self._pgconn_ptr, name) if rv is not NULL: @@ -226,9 +226,9 @@ cdef class PGconn: def exec_params( self, const char *command, - param_values: Optional[Sequence[Optional[bytes]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, int result_format = PqFormat.TEXT, ) -> PGresult: _ensure_pgconn(self) @@ -254,9 +254,9 @@ cdef class PGconn: def send_query_params( self, const char *command, - param_values: Optional[Sequence[Optional[bytes]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, int result_format = PqFormat.TEXT, ) -> None: _ensure_pgconn(self) @@ -284,7 +284,7 @@ cdef class PGconn: self, const char *name, const char *command, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> None: _ensure_pgconn(self) @@ -310,8 +310,8 @@ cdef class PGconn: def send_query_prepared( self, const char *name, - param_values: Optional[Sequence[Optional[bytes]]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes | None] | None, + param_formats: Sequence[int] | None = None, int result_format = PqFormat.TEXT, ) -> None: _ensure_pgconn(self) @@ -339,7 +339,7 @@ cdef class PGconn: self, const char *name, const char *command, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> PGresult: _ensure_pgconn(self) @@ -363,8 +363,8 @@ cdef class PGconn: def exec_prepared( self, const char *name, - param_values: Optional[Sequence[bytes]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes] | None, + param_formats: Sequence[int] | None = None, int result_format = PqFormat.TEXT, ) -> PGresult: _ensure_pgconn(self) @@ -463,7 +463,7 @@ cdef class PGconn: f"sending close prepared failed: {error_message(self)}" ) - def get_result(self) -> Optional["PGresult"]: + def get_result(self) -> "PGresult" | None: cdef libpq.PGresult *pgresult = libpq.PQgetResult(self._pgconn_ptr) if pgresult is NULL: return None @@ -539,7 +539,7 @@ cdef class PGconn: raise e.OperationalError(f"sending copy data failed: {error_message(self)}") return rv - def put_copy_end(self, error: Optional[bytes] = None) -> int: + def put_copy_end(self, error: bytes | None = None) -> int: cdef int rv cdef const char *cerr = NULL if error is not None: @@ -710,9 +710,9 @@ cdef void notice_receiver(void *arg, const libpq.PGresult *res_ptr) with gil: cdef (Py_ssize_t, libpq.Oid *, char * const*, int *, int *) _query_params_args( - list param_values: Optional[Sequence[Optional[bytes]]], - param_types: Optional[Sequence[int]], - list param_formats: Optional[Sequence[int]], + list param_values: Sequence[bytes | None] | None, + param_types: Sequence[int] | None, + list param_formats: Sequence[int] | None, ) except *: cdef int i diff --git a/psycopg_c/psycopg_c/pq/pgresult.pyx b/psycopg_c/psycopg_c/pq/pgresult.pyx index 6df42e8e3..696d44ff1 100644 --- a/psycopg_c/psycopg_c/pq/pgresult.pyx +++ b/psycopg_c/psycopg_c/pq/pgresult.pyx @@ -36,7 +36,7 @@ cdef class PGresult: self._pgresult_ptr = NULL @property - def pgresult_ptr(self) -> Optional[int]: + def pgresult_ptr(self) -> int | None: if self._pgresult_ptr: return self._pgresult_ptr else: @@ -50,7 +50,7 @@ cdef class PGresult: def error_message(self) -> bytes: return libpq.PQresultErrorMessage(self._pgresult_ptr) - def error_field(self, int fieldcode) -> Optional[bytes]: + def error_field(self, int fieldcode) -> bytes | None: cdef char * rv = libpq.PQresultErrorField(self._pgresult_ptr, fieldcode) if rv is not NULL: return rv @@ -65,7 +65,7 @@ cdef class PGresult: def nfields(self) -> int: return libpq.PQnfields(self._pgresult_ptr) - def fname(self, int column_number) -> Optional[bytes]: + def fname(self, int column_number) -> bytes | None: cdef char *rv = libpq.PQfname(self._pgresult_ptr, column_number) if rv is not NULL: return rv @@ -94,7 +94,7 @@ cdef class PGresult: def binary_tuples(self) -> int: return libpq.PQbinaryTuples(self._pgresult_ptr) - def get_value(self, int row_number, int column_number) -> Optional[bytes]: + def get_value(self, int row_number, int column_number) -> bytes | None: cdef int crow = row_number cdef int ccol = column_number cdef int length = libpq.PQgetlength(self._pgresult_ptr, crow, ccol) @@ -117,7 +117,7 @@ cdef class PGresult: return libpq.PQparamtype(self._pgresult_ptr, param_number) @property - def command_status(self) -> Optional[bytes]: + def command_status(self) -> bytes | None: cdef char *rv = libpq.PQcmdStatus(self._pgresult_ptr) if rv is not NULL: return rv @@ -125,7 +125,7 @@ cdef class PGresult: return None @property - def command_tuples(self) -> Optional[int]: + def command_tuples(self) -> int | None: cdef char *rv = libpq.PQcmdTuples(self._pgresult_ptr) if rv is NULL: return None diff --git a/psycopg_c/psycopg_c/types/bool.pyx b/psycopg_c/psycopg_c/types/bool.pyx index 5b3a7c387..a3d8d9088 100644 --- a/psycopg_c/psycopg_c/types/bool.pyx +++ b/psycopg_c/psycopg_c/types/bool.pyx @@ -28,7 +28,7 @@ cdef class BoolDumper(CDumper): return 1 - def quote(self, obj: bool) -> Optional[Buffer]: + def quote(self, obj: bool) -> Buffer | None: if obj is True: return b"true" elif obj is False: diff --git a/psycopg_c/psycopg_c/types/datetime.pyx b/psycopg_c/psycopg_c/types/datetime.pyx index c11e7b11c..2e1f1eee3 100644 --- a/psycopg_c/psycopg_c/types/datetime.pyx +++ b/psycopg_c/psycopg_c/types/datetime.pyx @@ -310,7 +310,7 @@ cdef class TimedeltaDumper(CDumper): oid = oids.INTERVAL_OID cdef int _style - def __cinit__(self, cls, context: Optional[AdaptContext] = None): + def __cinit__(self, cls, context: AdaptContext | None = None): cdef const char *ds = _get_intervalstyle(self._pgconn) if ds[0] == b's': # sql_standard @@ -371,7 +371,7 @@ cdef class DateLoader(CLoader): format = PQ_TEXT cdef int _order - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): cdef const char *ds = _get_datestyle(self._pgconn) if ds[0] == b'I': # ISO @@ -576,7 +576,7 @@ cdef class TimestampLoader(CLoader): format = PQ_TEXT cdef int _order - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): cdef const char *ds = _get_datestyle(self._pgconn) if ds[0] == b'I': # ISO @@ -714,7 +714,7 @@ cdef class TimestampBinaryLoader(CLoader): cdef class _BaseTimestamptzLoader(CLoader): cdef object _time_zone - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): self._time_zone = _timezone_from_connection(self._pgconn) @@ -724,7 +724,7 @@ cdef class TimestamptzLoader(_BaseTimestamptzLoader): format = PQ_TEXT cdef int _order - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): cdef const char *ds = _get_datestyle(self._pgconn) if ds[0] == b'I': # ISO @@ -871,7 +871,7 @@ cdef class IntervalLoader(CLoader): format = PQ_TEXT cdef int _style - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): cdef const char *ds = _get_intervalstyle(self._pgconn) if ds[0] == b'p' and ds[8] == 0: # postgres @@ -1096,7 +1096,7 @@ cdef object _timezone_from_seconds(int sec, __cache={}): cdef object _get_timestamp_load_error( - pq.PGconn pgconn, const char *data, ex: Optional[Exception] = None + pq.PGconn pgconn, const char *data, ex: Exception | None = None ): s = bytes(data).decode("utf8", "replace") diff --git a/psycopg_c/psycopg_c/types/numeric.pyx b/psycopg_c/psycopg_c/types/numeric.pyx index f2f2c2ac1..bf1989523 100644 --- a/psycopg_c/psycopg_c/types/numeric.pyx +++ b/psycopg_c/psycopg_c/types/numeric.pyx @@ -55,7 +55,7 @@ cdef class _IntDumper(CDumper): cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1: return dump_int_to_text(obj, rv, offset) - def quote(self, obj) -> Optional[Buffer]: + def quote(self, obj) -> Buffer | None: cdef Py_ssize_t length rv = PyByteArray_FromStringAndSize("", 0) @@ -311,7 +311,7 @@ cdef class _FloatDumper(CDumper): PyMem_Free(out) return length - def quote(self, obj) -> Optional[Buffer]: + def quote(self, obj) -> Buffer | None: value = bytes(self.dump(obj)) cdef PyObject *ptr = PyDict_GetItem(_special_float, value) if ptr != NULL: @@ -417,7 +417,7 @@ cdef class DecimalDumper(CDumper): cdef Py_ssize_t cdump(self, obj, bytearray rv, Py_ssize_t offset) except -1: return dump_decimal_to_text(obj, rv, offset) - def quote(self, obj) -> Optional[Buffer]: + def quote(self, obj) -> Buffer | None: value = bytes(self.dump(obj)) cdef PyObject *ptr = PyDict_GetItem(_special_decimal, value) if ptr != NULL: @@ -524,7 +524,7 @@ cdef class _MixedNumericDumper(CDumper): oid = oids.NUMERIC_OID - def __cinit__(self, cls, context: Optional[AdaptContext] = None): + def __cinit__(self, cls, context: AdaptContext | None = None): global _int_classes if _int_classes is None: diff --git a/psycopg_c/psycopg_c/types/string.pyx b/psycopg_c/psycopg_c/types/string.pyx index 170b17b7d..f1b8b64f4 100644 --- a/psycopg_c/psycopg_c/types/string.pyx +++ b/psycopg_c/psycopg_c/types/string.pyx @@ -30,7 +30,7 @@ cdef class _BaseStrDumper(CDumper): cdef char *encoding cdef bytes _bytes_encoding # needed to keep `encoding` alive - def __cinit__(self, cls, context: Optional[AdaptContext] = None): + def __cinit__(self, cls, context: AdaptContext | None = None): self.is_utf8 = 0 self.encoding = "utf-8" @@ -138,7 +138,7 @@ cdef class _TextLoader(CLoader): cdef char *encoding cdef bytes _bytes_encoding # needed to keep `encoding` alive - def __cinit__(self, oid: int, context: Optional[AdaptContext] = None): + def __cinit__(self, oid: int, context: AdaptContext | None = None): self.is_utf8 = 0 self.encoding = "utf-8" diff --git a/psycopg_pool/psycopg_pool/_task.py b/psycopg_pool/psycopg_pool/_task.py index 9169f759c..7629d1c46 100644 --- a/psycopg_pool/psycopg_pool/_task.py +++ b/psycopg_pool/psycopg_pool/_task.py @@ -4,11 +4,13 @@ Task for Scheduler and AsyncScheduler # Copyright (C) 2023 The Psycopg Team -from typing import Any, Callable, Optional +from __future__ import annotations + +from typing import Any, Callable from dataclasses import dataclass, field @dataclass(order=True) class Task: time: float - action: Optional[Callable[[], Any]] = field(compare=False) + action: Callable[[], Any] | None = field(compare=False) diff --git a/psycopg_pool/psycopg_pool/base.py b/psycopg_pool/psycopg_pool/base.py index 5b0c83991..b2946ee0a 100644 --- a/psycopg_pool/psycopg_pool/base.py +++ b/psycopg_pool/psycopg_pool/base.py @@ -4,9 +4,11 @@ psycopg connection pool base class and functionalities. # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + from time import monotonic from random import random -from typing import Any, Dict, Optional, Tuple, TYPE_CHECKING +from typing import Any, Dict, Tuple, TYPE_CHECKING from psycopg import errors as e @@ -44,10 +46,10 @@ class BasePool: self, conninfo: str = "", *, - kwargs: Optional[Dict[str, Any]], + kwargs: Dict[str, Any] | None, min_size: int, - max_size: Optional[int], - name: Optional[str], + max_size: int | None, + name: str | None, timeout: float, max_waiting: int, max_lifetime: float, @@ -116,7 +118,7 @@ class BasePool: """`!True` if the pool is closed.""" return self._closed - def _check_size(self, min_size: int, max_size: Optional[int]) -> Tuple[int, int]: + def _check_size(self, min_size: int, max_size: int | None) -> Tuple[int, int]: if max_size is None: max_size = min_size diff --git a/psycopg_pool/psycopg_pool/null_pool.py b/psycopg_pool/psycopg_pool/null_pool.py index 0be5d6148..fd3b4f462 100644 --- a/psycopg_pool/psycopg_pool/null_pool.py +++ b/psycopg_pool/psycopg_pool/null_pool.py @@ -10,7 +10,7 @@ Psycopg null connection pool module (sync version). from __future__ import annotations import logging -from typing import Any, cast, Dict, Optional, Type +from typing import Any, cast, Dict, Type from psycopg import Connection from psycopg.pq import TransactionStatus @@ -32,20 +32,20 @@ class NullConnectionPool(_BaseNullConnectionPool, ConnectionPool[CT]): conninfo: str = "", *, connection_class: Type[CT] = cast(Type[CT], Connection), - kwargs: Optional[Dict[str, Any]] = None, + kwargs: Dict[str, Any] | None = None, min_size: int = 0, - max_size: Optional[int] = None, + max_size: int | None = None, open: bool | None = None, - configure: Optional[ConnectionCB[CT]] = None, - check: Optional[ConnectionCB[CT]] = None, - reset: Optional[ConnectionCB[CT]] = None, - name: Optional[str] = None, + configure: ConnectionCB[CT] | None = None, + check: ConnectionCB[CT] | None = None, + reset: ConnectionCB[CT] | None = None, + name: str | None = None, timeout: float = 30.0, max_waiting: int = 0, max_lifetime: float = 60 * 60.0, max_idle: float = 10 * 60.0, reconnect_timeout: float = 5 * 60.0, - reconnect_failed: Optional[ConnectFailedCB] = None, + reconnect_failed: ConnectFailedCB | None = None, num_workers: int = 3, ): # Note: min_size default value changed to 0. @@ -97,11 +97,11 @@ class NullConnectionPool(_BaseNullConnectionPool, ConnectionPool[CT]): logger.info("pool %r is ready to use", self.name) - def _get_ready_connection(self, timeout: Optional[float]) -> Optional[CT]: + def _get_ready_connection(self, timeout: float | None) -> CT | None: if timeout is not None and timeout <= 0.0: raise PoolTimeout() - conn: Optional[CT] = None + conn: CT | None = None if self.max_size == 0 or self._nconns < self.max_size: # Create a new connection for the client try: @@ -132,7 +132,7 @@ class NullConnectionPool(_BaseNullConnectionPool, ConnectionPool[CT]): self._nconns -= 1 return True - def resize(self, min_size: int, max_size: Optional[int] = None) -> None: + def resize(self, min_size: int, max_size: int | None = None) -> None: """Change the size of the pool during runtime. Only *max_size* can be changed; *min_size* must remain 0. diff --git a/psycopg_pool/psycopg_pool/null_pool_async.py b/psycopg_pool/psycopg_pool/null_pool_async.py index a9baa8e12..4cac69230 100644 --- a/psycopg_pool/psycopg_pool/null_pool_async.py +++ b/psycopg_pool/psycopg_pool/null_pool_async.py @@ -7,7 +7,7 @@ Psycopg null connection pool module (async version). from __future__ import annotations import logging -from typing import Any, cast, Dict, Optional, Type +from typing import Any, cast, Dict, Type from psycopg import AsyncConnection from psycopg.pq import TransactionStatus @@ -28,20 +28,20 @@ class AsyncNullConnectionPool(_BaseNullConnectionPool, AsyncConnectionPool[ACT]) conninfo: str = "", *, connection_class: Type[ACT] = cast(Type[ACT], AsyncConnection), - kwargs: Optional[Dict[str, Any]] = None, + kwargs: Dict[str, Any] | None = None, min_size: int = 0, # Note: min_size default value changed to 0. - max_size: Optional[int] = None, + max_size: int | None = None, open: bool | None = None, - configure: Optional[AsyncConnectionCB[ACT]] = None, - check: Optional[AsyncConnectionCB[ACT]] = None, - reset: Optional[AsyncConnectionCB[ACT]] = None, - name: Optional[str] = None, + configure: AsyncConnectionCB[ACT] | None = None, + check: AsyncConnectionCB[ACT] | None = None, + reset: AsyncConnectionCB[ACT] | None = None, + name: str | None = None, timeout: float = 30.0, max_waiting: int = 0, max_lifetime: float = 60 * 60.0, max_idle: float = 10 * 60.0, reconnect_timeout: float = 5 * 60.0, - reconnect_failed: Optional[AsyncConnectFailedCB] = None, + reconnect_failed: AsyncConnectFailedCB | None = None, num_workers: int = 3, ): super().__init__( @@ -92,11 +92,11 @@ class AsyncNullConnectionPool(_BaseNullConnectionPool, AsyncConnectionPool[ACT]) logger.info("pool %r is ready to use", self.name) - async def _get_ready_connection(self, timeout: Optional[float]) -> Optional[ACT]: + async def _get_ready_connection(self, timeout: float | None) -> ACT | None: if timeout is not None and timeout <= 0.0: raise PoolTimeout() - conn: Optional[ACT] = None + conn: ACT | None = None if self.max_size == 0 or self._nconns < self.max_size: # Create a new connection for the client try: @@ -128,7 +128,7 @@ class AsyncNullConnectionPool(_BaseNullConnectionPool, AsyncConnectionPool[ACT]) self._nconns -= 1 return True - async def resize(self, min_size: int, max_size: Optional[int] = None) -> None: + async def resize(self, min_size: int, max_size: int | None = None) -> None: """Change the size of the pool during runtime. Only *max_size* can be changed; *min_size* must remain 0. diff --git a/psycopg_pool/psycopg_pool/pool.py b/psycopg_pool/psycopg_pool/pool.py index 4a086827c..f8ae79d79 100644 --- a/psycopg_pool/psycopg_pool/pool.py +++ b/psycopg_pool/psycopg_pool/pool.py @@ -15,7 +15,7 @@ from abc import ABC, abstractmethod from time import monotonic from types import TracebackType from typing import Any, Iterator, cast, Dict, Generic, List -from typing import Optional, Type +from typing import Type from weakref import ref from contextlib import contextmanager @@ -43,20 +43,20 @@ class ConnectionPool(Generic[CT], BasePool): conninfo: str = "", *, connection_class: Type[CT] = cast(Type[CT], Connection), - kwargs: Optional[Dict[str, Any]] = None, + kwargs: Dict[str, Any] | None = None, min_size: int = 4, - max_size: Optional[int] = None, + max_size: int | None = None, open: bool | None = None, - configure: Optional[ConnectionCB[CT]] = None, - check: Optional[ConnectionCB[CT]] = None, - reset: Optional[ConnectionCB[CT]] = None, - name: Optional[str] = None, + configure: ConnectionCB[CT] | None = None, + check: ConnectionCB[CT] | None = None, + reset: ConnectionCB[CT] | None = None, + name: str | None = None, timeout: float = 30.0, max_waiting: int = 0, max_lifetime: float = 60 * 60.0, max_idle: float = 10 * 60.0, reconnect_timeout: float = 5 * 60.0, - reconnect_failed: Optional[ConnectFailedCB] = None, + reconnect_failed: ConnectFailedCB | None = None, num_workers: int = 3, ): self.connection_class = connection_class @@ -75,9 +75,9 @@ class ConnectionPool(Generic[CT], BasePool): self._waiting = Deque[WaitingClient[CT]]() # to notify that the pool is full - self._pool_full_event: Optional[Event] = None + self._pool_full_event: Event | None = None - self._sched_runner: Optional[Worker] = None + self._sched_runner: Worker | None = None self._workers: List[Worker] = [] super().__init__( @@ -153,7 +153,7 @@ class ConnectionPool(Generic[CT], BasePool): logger.info("pool %r is ready to use", self.name) @contextmanager - def connection(self, timeout: Optional[float] = None) -> Iterator[CT]: + def connection(self, timeout: float | None = None) -> Iterator[CT]: """Context manager to obtain a connection from the pool. Return the connection immediately if available, otherwise wait up to @@ -175,7 +175,7 @@ class ConnectionPool(Generic[CT], BasePool): t1 = monotonic() self._stats[self._USAGE_MS] += int(1000.0 * (t1 - t0)) - def getconn(self, timeout: Optional[float] = None) -> CT: + def getconn(self, timeout: float | None = None) -> CT: """Obtain a connection from the pool. You should preferably use `connection()`. Use this function only if @@ -262,12 +262,12 @@ class ConnectionPool(Generic[CT], BasePool): conn._pool = self return conn - def _get_ready_connection(self, timeout: Optional[float]) -> Optional[CT]: + def _get_ready_connection(self, timeout: float | None) -> CT | None: """Return a connection, if the client deserves one.""" if timeout is not None and timeout <= 0.0: raise PoolTimeout() - conn: Optional[CT] = None + conn: CT | None = None if self._pool: # Take a connection ready out of the pool conn = self._pool.popleft() @@ -467,13 +467,13 @@ class ConnectionPool(Generic[CT], BasePool): def __exit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: self.close() - def resize(self, min_size: int, max_size: Optional[int] = None) -> None: + def resize(self, min_size: int, max_size: int | None = None) -> None: """Change the size of the pool during runtime.""" min_size, max_size = self._check_size(min_size, max_size) @@ -586,7 +586,7 @@ class ConnectionPool(Generic[CT], BasePool): "task run %s failed: %s: %s", task, ex.__class__.__name__, ex ) - def _connect(self, timeout: Optional[float] = None) -> CT: + def _connect(self, timeout: float | None = None) -> CT: """Return a new connection configured for the pool.""" self._stats[self._CONNECTIONS_NUM] += 1 kwargs = self.kwargs @@ -619,7 +619,7 @@ class ConnectionPool(Generic[CT], BasePool): return conn def _add_connection( - self, attempt: Optional[AttemptWithBackoff], growing: bool = False + self, attempt: AttemptWithBackoff | None, growing: bool = False ) -> None: """Try to connect and add the connection to the pool. @@ -788,7 +788,7 @@ class ConnectionPool(Generic[CT], BasePool): conn.close() def _shrink_pool(self) -> None: - to_close: Optional[CT] = None + to_close: CT | None = None with self._lock: # Reset the min number of connections used @@ -823,8 +823,8 @@ class WaitingClient(Generic[CT]): __slots__ = ("conn", "error", "_cond") def __init__(self) -> None: - self.conn: Optional[CT] = None - self.error: Optional[BaseException] = None + self.conn: CT | None = None + self.error: BaseException | None = None # The WaitingClient behaves in a way similar to an Event, but we need # to notify reliably the flagger that the waiter has "accepted" the @@ -939,7 +939,7 @@ class AddConnection(MaintenanceTask): def __init__( self, pool: ConnectionPool[Any], - attempt: Optional[AttemptWithBackoff] = None, + attempt: AttemptWithBackoff | None = None, growing: bool = False, ): super().__init__(pool) diff --git a/psycopg_pool/psycopg_pool/pool_async.py b/psycopg_pool/psycopg_pool/pool_async.py index ac6925aed..45000b6ec 100644 --- a/psycopg_pool/psycopg_pool/pool_async.py +++ b/psycopg_pool/psycopg_pool/pool_async.py @@ -12,7 +12,7 @@ from abc import ABC, abstractmethod from time import monotonic from types import TracebackType from typing import Any, AsyncIterator, cast, Dict, Generic, List -from typing import Optional, Type +from typing import Type from weakref import ref from contextlib import asynccontextmanager @@ -42,20 +42,20 @@ class AsyncConnectionPool(Generic[ACT], BasePool): conninfo: str = "", *, connection_class: Type[ACT] = cast(Type[ACT], AsyncConnection), - kwargs: Optional[Dict[str, Any]] = None, + kwargs: Dict[str, Any] | None = None, min_size: int = 4, - max_size: Optional[int] = None, + max_size: int | None = None, open: bool | None = None, - configure: Optional[AsyncConnectionCB[ACT]] = None, - check: Optional[AsyncConnectionCB[ACT]] = None, - reset: Optional[AsyncConnectionCB[ACT]] = None, - name: Optional[str] = None, + configure: AsyncConnectionCB[ACT] | None = None, + check: AsyncConnectionCB[ACT] | None = None, + reset: AsyncConnectionCB[ACT] | None = None, + name: str | None = None, timeout: float = 30.0, max_waiting: int = 0, max_lifetime: float = 60 * 60.0, max_idle: float = 10 * 60.0, reconnect_timeout: float = 5 * 60.0, - reconnect_failed: Optional[AsyncConnectFailedCB] = None, + reconnect_failed: AsyncConnectFailedCB | None = None, num_workers: int = 3, ): self.connection_class = connection_class @@ -74,9 +74,9 @@ class AsyncConnectionPool(Generic[ACT], BasePool): self._waiting = Deque[WaitingClient[ACT]]() # to notify that the pool is full - self._pool_full_event: Optional[AEvent] = None + self._pool_full_event: AEvent | None = None - self._sched_runner: Optional[AWorker] = None + self._sched_runner: AWorker | None = None self._workers: List[AWorker] = [] super().__init__( @@ -176,7 +176,7 @@ class AsyncConnectionPool(Generic[ACT], BasePool): logger.info("pool %r is ready to use", self.name) @asynccontextmanager - async def connection(self, timeout: Optional[float] = None) -> AsyncIterator[ACT]: + async def connection(self, timeout: float | None = None) -> AsyncIterator[ACT]: """Context manager to obtain a connection from the pool. Return the connection immediately if available, otherwise wait up to @@ -198,7 +198,7 @@ class AsyncConnectionPool(Generic[ACT], BasePool): t1 = monotonic() self._stats[self._USAGE_MS] += int(1000.0 * (t1 - t0)) - async def getconn(self, timeout: Optional[float] = None) -> ACT: + async def getconn(self, timeout: float | None = None) -> ACT: """Obtain a connection from the pool. You should preferably use `connection()`. Use this function only if @@ -286,12 +286,12 @@ class AsyncConnectionPool(Generic[ACT], BasePool): conn._pool = self return conn - async def _get_ready_connection(self, timeout: Optional[float]) -> Optional[ACT]: + async def _get_ready_connection(self, timeout: float | None) -> ACT | None: """Return a connection, if the client deserves one.""" if timeout is not None and timeout <= 0.0: raise PoolTimeout() - conn: Optional[ACT] = None + conn: ACT | None = None if self._pool: # Take a connection ready out of the pool conn = self._pool.popleft() @@ -499,13 +499,13 @@ class AsyncConnectionPool(Generic[ACT], BasePool): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], + exc_type: Type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, ) -> None: await self.close() - async def resize(self, min_size: int, max_size: Optional[int] = None) -> None: + async def resize(self, min_size: int, max_size: int | None = None) -> None: """Change the size of the pool during runtime.""" min_size, max_size = self._check_size(min_size, max_size) @@ -634,7 +634,7 @@ class AsyncConnectionPool(Generic[ACT], BasePool): "task run %s failed: %s: %s", task, ex.__class__.__name__, ex ) - async def _connect(self, timeout: Optional[float] = None) -> ACT: + async def _connect(self, timeout: float | None = None) -> ACT: """Return a new connection configured for the pool.""" self._stats[self._CONNECTIONS_NUM] += 1 kwargs = self.kwargs @@ -668,7 +668,7 @@ class AsyncConnectionPool(Generic[ACT], BasePool): return conn async def _add_connection( - self, attempt: Optional[AttemptWithBackoff], growing: bool = False + self, attempt: AttemptWithBackoff | None, growing: bool = False ) -> None: """Try to connect and add the connection to the pool. @@ -844,7 +844,7 @@ class AsyncConnectionPool(Generic[ACT], BasePool): await conn.close() async def _shrink_pool(self) -> None: - to_close: Optional[ACT] = None + to_close: ACT | None = None async with self._lock: # Reset the min number of connections used @@ -880,8 +880,8 @@ class WaitingClient(Generic[ACT]): __slots__ = ("conn", "error", "_cond") def __init__(self) -> None: - self.conn: Optional[ACT] = None - self.error: Optional[BaseException] = None + self.conn: ACT | None = None + self.error: BaseException | None = None # The WaitingClient behaves in a way similar to an Event, but we need # to notify reliably the flagger that the waiter has "accepted" the @@ -995,7 +995,7 @@ class AddConnection(MaintenanceTask): def __init__( self, pool: AsyncConnectionPool[Any], - attempt: Optional[AttemptWithBackoff] = None, + attempt: AttemptWithBackoff | None = None, growing: bool = False, ): super().__init__(pool) diff --git a/psycopg_pool/psycopg_pool/sched.py b/psycopg_pool/psycopg_pool/sched.py index 40954b996..4a5181d63 100644 --- a/psycopg_pool/psycopg_pool/sched.py +++ b/psycopg_pool/psycopg_pool/sched.py @@ -15,10 +15,12 @@ Tasks are called "Task", not "Event", here, because we actually make use of # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import logging from time import monotonic from heapq import heappush, heappop -from typing import Any, Callable, List, Optional +from typing import Any, Callable, List from ._task import Task from ._acompat import Lock, Event @@ -35,7 +37,7 @@ class Scheduler: EMPTY_QUEUE_TIMEOUT = 600.0 - def enter(self, delay: float, action: Optional[Callable[[], Any]]) -> Task: + def enter(self, delay: float, action: Callable[[], Any] | None) -> Task: """Enter a new task in the queue delayed in the future. Schedule a `!None` to stop the execution. @@ -43,7 +45,7 @@ class Scheduler: time = monotonic() + delay return self.enterabs(time, action) - def enterabs(self, time: float, action: Optional[Callable[[], Any]]) -> Task: + def enterabs(self, time: float, action: Callable[[], Any] | None) -> Task: """Enter a new task in the queue at an absolute time. Schedule a `!None` to stop the execution. diff --git a/psycopg_pool/psycopg_pool/sched_async.py b/psycopg_pool/psycopg_pool/sched_async.py index c9fdbad79..d43ef1d2e 100644 --- a/psycopg_pool/psycopg_pool/sched_async.py +++ b/psycopg_pool/psycopg_pool/sched_async.py @@ -12,10 +12,12 @@ Tasks are called "Task", not "Event", here, because we actually make use of # Copyright (C) 2021 The Psycopg Team +from __future__ import annotations + import logging from time import monotonic from heapq import heappush, heappop -from typing import Any, Callable, List, Optional +from typing import Any, Callable, List from ._task import Task from ._acompat import ALock, AEvent @@ -31,7 +33,7 @@ class AsyncScheduler: EMPTY_QUEUE_TIMEOUT = 600.0 - async def enter(self, delay: float, action: Optional[Callable[[], Any]]) -> Task: + async def enter(self, delay: float, action: Callable[[], Any] | None) -> Task: """Enter a new task in the queue delayed in the future. Schedule a `!None` to stop the execution. @@ -39,7 +41,7 @@ class AsyncScheduler: time = monotonic() + delay return await self.enterabs(time, action) - async def enterabs(self, time: float, action: Optional[Callable[[], Any]]) -> Task: + async def enterabs(self, time: float, action: Callable[[], Any] | None) -> Task: """Enter a new task in the queue at an absolute time. Schedule a `!None` to stop the execution. diff --git a/tests/adapters_example.py b/tests/adapters_example.py index a184e6aa4..5c0444a50 100644 --- a/tests/adapters_example.py +++ b/tests/adapters_example.py @@ -1,4 +1,4 @@ -from typing import Optional +from __future__ import annotations from psycopg import pq from psycopg.abc import Dumper, Loader, AdaptContext, PyFormat, Buffer @@ -17,7 +17,7 @@ class MyStrDumper: format = pq.Format.TEXT oid = 25 # text - def __init__(self, cls: type, context: Optional[AdaptContext] = None): + def __init__(self, cls: type, context: AdaptContext | None = None): self._cls = cls def dump(self, obj: str) -> bytes: @@ -38,7 +38,7 @@ class MyStrDumper: class MyTextLoader: format = pq.Format.TEXT - def __init__(self, oid: int, context: Optional[AdaptContext] = None): + def __init__(self, oid: int, context: AdaptContext | None = None): pass def load(self, data: Buffer) -> str: diff --git a/tests/fix_crdb.py b/tests/fix_crdb.py index 37f7c15a8..554293f3d 100644 --- a/tests/fix_crdb.py +++ b/tests/fix_crdb.py @@ -1,4 +1,4 @@ -from typing import Optional +from __future__ import annotations import pytest @@ -53,7 +53,7 @@ def check_crdb_version(got, mark): is_crdb = CrdbConnection.is_crdb -def crdb_skip_message(reason: Optional[str]) -> str: +def crdb_skip_message(reason: str | None) -> str: msg = "" if reason: msg = reason diff --git a/tests/fix_db.py b/tests/fix_db.py index 0c3fe0c8e..cfa0ae143 100644 --- a/tests/fix_db.py +++ b/tests/fix_db.py @@ -1,10 +1,11 @@ +from __future__ import annotations + import io import os import sys import pytest import logging from contextlib import contextmanager -from typing import Optional import psycopg from psycopg import pq @@ -17,7 +18,7 @@ from .utils import check_postgres_version # Set by warm_up_database() the first time the dsn fixture is used pg_version: int -crdb_version: Optional[int] +crdb_version: int | None def pytest_addoption(parser): diff --git a/tests/fix_faker.py b/tests/fix_faker.py index e302d22e5..0256a94d5 100644 --- a/tests/fix_faker.py +++ b/tests/fix_faker.py @@ -1,10 +1,12 @@ +from __future__ import annotations + import datetime as dt import importlib import ipaddress from math import isnan from uuid import UUID from random import choice, random, randrange -from typing import Any, List, Optional, Set, Tuple, Union +from typing import Any, List, Set, Tuple, Union from decimal import Decimal from contextlib import contextmanager, asynccontextmanager @@ -42,7 +44,7 @@ class Faker: self.records = [] self._schema = None - self._types: Optional[List[type]] = None + self._types: List[type] | None = None self._types_names = None self._makers = {} self.table_name = sql.Identifier("fake_table") diff --git a/tests/scripts/pipeline-demo.py b/tests/scripts/pipeline-demo.py index 74cc04d1f..ff05f0b01 100644 --- a/tests/scripts/pipeline-demo.py +++ b/tests/scripts/pipeline-demo.py @@ -8,12 +8,14 @@ handled by execute() calls when pgconn socket is read-ready, which happens when the output buffer is full. """ +from __future__ import annotations + import argparse import asyncio import logging from contextlib import contextmanager from functools import partial -from typing import Any, Iterator, Optional, Sequence, Tuple +from typing import Any, Iterator, Sequence, Tuple from psycopg import AsyncConnection, Connection from psycopg import pq, waiting @@ -36,7 +38,7 @@ class LoggingPGconn: self._logger = logger def log_notice(result: pq.abc.PGresult) -> None: - def get_field(field: DiagnosticField) -> Optional[str]: + def get_field(field: DiagnosticField) -> str | None: value = result.error_field(field) return value.decode("utf-8", "replace") if value else None @@ -68,9 +70,9 @@ class LoggingPGconn: def send_query_params( self, command: bytes, - param_values: Optional[Sequence[Optional[bytes]]], - param_types: Optional[Sequence[int]] = None, - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes | None] | None, + param_types: Sequence[int] | None = None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: self._pgconn.send_query_params( @@ -81,8 +83,8 @@ class LoggingPGconn: def send_query_prepared( self, name: bytes, - param_values: Optional[Sequence[Optional[bytes]]], - param_formats: Optional[Sequence[int]] = None, + param_values: Sequence[bytes | None] | None, + param_formats: Sequence[int] | None = None, result_format: int = Format.TEXT, ) -> None: self._pgconn.send_query_prepared( @@ -94,12 +96,12 @@ class LoggingPGconn: self, name: bytes, command: bytes, - param_types: Optional[Sequence[int]] = None, + param_types: Sequence[int] | None = None, ) -> None: self._pgconn.send_prepare(name, command, param_types) self._logger.info("prepare %s as '%s'", command.decode(), name.decode()) - def get_result(self) -> Optional[pq.abc.PGresult]: + def get_result(self) -> pq.abc.PGresult | None: r = self._pgconn.get_result() if r is not None: self._logger.info("got %s result", pq.ExecStatus(r.status).name) diff --git a/tests/test_adapt.py b/tests/test_adapt.py index 688be1a12..aaddd0438 100644 --- a/tests/test_adapt.py +++ b/tests/test_adapt.py @@ -1,6 +1,8 @@ +from __future__ import annotations + import datetime as dt from types import ModuleType -from typing import Any, List, Optional +from typing import Any, List import pytest @@ -521,12 +523,12 @@ class MyStr(str): class StrNoneDumper(StrDumper): - def dump(self, obj: str) -> Optional[Buffer]: + def dump(self, obj: str) -> Buffer | None: return super().dump(obj) if obj else None class StrNoneBinaryDumper(StrBinaryDumper): - def dump(self, obj: str) -> Optional[Buffer]: + def dump(self, obj: str) -> Buffer | None: return super().dump(obj) if obj else None diff --git a/tests/test_typing.py b/tests/test_typing.py index efafd0bb4..76555aac3 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import os import pytest @@ -233,15 +235,15 @@ obj = {curs} [ ( "conn.cursor()", - "Optional[Tuple[Any, ...]]", + "Tuple[Any, ...] | None", ), ( "conn.cursor(row_factory=rows.dict_row)", - "Optional[Dict[str, Any]]", + "Dict[str, Any] | None", ), ( "conn.cursor(row_factory=thing_row)", - "Optional[Thing]", + "Thing | None", ), ], ) @@ -371,11 +373,11 @@ class MyCursor(psycopg.{cur_base_class}[Row]): def _test_reveal(stmts, type, mypy): - ignore = "" if type.startswith("Optional") else "# type: ignore[assignment]" + ignore = "" if type.endswith("| None") else "# type: ignore[assignment]" stmts = "\n".join(f" {line}" for line in stmts.splitlines()) src = f"""\ -from typing import Any, Callable, Dict, List, NamedTuple, Optional, Sequence +from typing import Any, Callable, Dict, List, NamedTuple, Sequence from typing import Tuple, Union import psycopg from psycopg import rows diff --git a/tests/types/test_numeric.py b/tests/types/test_numeric.py index 9a71aaebb..9af20a045 100644 --- a/tests/types/test_numeric.py +++ b/tests/types/test_numeric.py @@ -1,6 +1,7 @@ +from __future__ import annotations + import enum from math import isnan, isinf, exp -from typing import Optional from decimal import Decimal import pytest @@ -80,7 +81,7 @@ def test_int_none(conn, fmt_in): Base: type = Int8Dumper if fmt_in == PyFormat.TEXT else Int8BinaryDumper class MyDumper(Base): # type: ignore - def dump(self, obj: int) -> Optional[Buffer]: + def dump(self, obj: int) -> Buffer | None: if not obj: return None else: diff --git a/tests/typing_example.py b/tests/typing_example.py index a26ca49b4..139c92a21 100644 --- a/tests/typing_example.py +++ b/tests/typing_example.py @@ -3,7 +3,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Union +from typing import Any, Callable, Dict, Sequence, Tuple, Union from psycopg import Connection, Cursor, ServerCursor, connect, rows from psycopg import AsyncConnection, AsyncCursor, AsyncServerCursor @@ -45,12 +45,12 @@ def check_row_factory_cursor() -> None: cur1: Cursor[Any] cur1 = conn.cursor() - r1: Optional[Any] + r1: Any | None r1 = cur1.fetchone() r1 is not None cur2: Cursor[int] - r2: Optional[int] + r2: int | None with conn.cursor(row_factory=int_row_factory) as cur2: cur2.execute("select 1") r2 = cur2.fetchone() @@ -70,12 +70,12 @@ async def async_check_row_factory_cursor() -> None: cur1: AsyncCursor[Any] cur1 = conn.cursor() - r1: Optional[Any] + r1: Any | None r1 = await cur1.fetchone() r1 is not None cur2: AsyncCursor[int] - r2: Optional[int] + r2: int | None async with conn.cursor(row_factory=int_row_factory) as cur2: await cur2.execute("select 1") r2 = await cur2.fetchone() @@ -95,7 +95,7 @@ def check_row_factory_connection() -> None: """ conn1: Connection[int] cur1: Cursor[int] - r1: Optional[int] + r1: int | None conn1 = connect(row_factory=int_row_factory) cur1 = conn1.execute("select 1") r1 = cur1.fetchone() @@ -105,7 +105,7 @@ def check_row_factory_connection() -> None: conn2: Connection[Person] cur2: Cursor[Person] - r2: Optional[Person] + r2: Person | None conn2 = connect(row_factory=Person.row_factory) cur2 = conn2.execute("select * from persons") r2 = cur2.fetchone() @@ -114,7 +114,7 @@ def check_row_factory_connection() -> None: cur2.execute("select 2") cur3: Cursor[Tuple[Any, ...]] - r3: Optional[Tuple[Any, ...]] + r3: Tuple[Any, ...] | None conn3 = connect() cur3 = conn3.execute("select 3") with conn3.cursor() as cur3: @@ -129,7 +129,7 @@ async def async_check_row_factory_connection() -> None: """ conn1: AsyncConnection[int] cur1: AsyncCursor[int] - r1: Optional[int] + r1: int | None conn1 = await AsyncConnection.connect(row_factory=int_row_factory) cur1 = await conn1.execute("select 1") r1 = await cur1.fetchone() @@ -139,7 +139,7 @@ async def async_check_row_factory_connection() -> None: conn2: AsyncConnection[Person] cur2: AsyncCursor[Person] - r2: Optional[Person] + r2: Person | None conn2 = await AsyncConnection.connect(row_factory=Person.row_factory) cur2 = await conn2.execute("select * from persons") r2 = await cur2.fetchone() @@ -148,7 +148,7 @@ async def async_check_row_factory_connection() -> None: await cur2.execute("select 2") cur3: AsyncCursor[Tuple[Any, ...]] - r3: Optional[Tuple[Any, ...]] + r3: Tuple[Any, ...] | None conn3 = await AsyncConnection.connect() cur3 = await conn3.execute("select 3") async with conn3.cursor() as cur3: diff --git a/tests/utils.py b/tests/utils.py index 8eef880bd..c2a97307a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,7 +1,9 @@ +from __future__ import annotations + import re import sys import operator -from typing import Callable, Optional, Tuple +from typing import Callable, Tuple from contextlib import contextmanager @@ -70,7 +72,7 @@ class VersionCheck: self, *, skip: bool = False, - op: Optional[str] = None, + op: str | None = None, version_tuple: Tuple[int, ...] = (), whose: str = "(wanted)", postgres_rule: bool = False, @@ -105,10 +107,10 @@ class VersionCheck: skip=skip, op=op, version_tuple=version_tuple, postgres_rule=postgres_rule ) - def get_skip_message(self, version: Optional[int]) -> Optional[str]: + def get_skip_message(self, version: int | None) -> str | None: got_tuple = self._parse_int_version(version) - msg: Optional[str] = None + msg: str | None = None if self.skip: if got_tuple: if not self.version_tuple: @@ -147,7 +149,7 @@ class VersionCheck: op = getattr(operator, self._OP_NAMES[self.op]) return op(got_tuple, version_tuple) - def _parse_int_version(self, version: Optional[int]) -> Tuple[int, ...]: + def _parse_int_version(self, version: int | None) -> Tuple[int, ...]: if version is None: return () version, ver_fix = divmod(version, 100)