]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
pq objects defined as proto rather than stub
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 19 Apr 2020 00:00:05 +0000 (12:00 +1200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 19 Apr 2020 00:00:05 +0000 (12:00 +1200)
12 files changed:
psycopg3/adapt.py
psycopg3/connection.py
psycopg3/cursor.py
psycopg3/errors.py
psycopg3/generators.py
psycopg3/pq/__init__.py
psycopg3/pq/__init__.pyi [deleted file]
psycopg3/pq/misc.py
psycopg3/pq/pq_ctypes.py
psycopg3/pq/pq_cython.pyx
psycopg3/pq/proto.py [new file with mode: 0644]
setup.py

index 73ecba12165373efdbfd1298b12a2b504d19fac6..538cff3ae9e6dc4890bed07283d3fd1ee796a333 100644 (file)
@@ -230,11 +230,11 @@ class Transformer:
         self._loaders_maps.append(Loader.globals)
 
     @property
-    def pgresult(self) -> Optional[pq.PGresult]:
+    def pgresult(self) -> Optional[pq.proto.PGresult]:
         return self._pgresult
 
     @pgresult.setter
-    def pgresult(self, result: Optional[pq.PGresult]) -> None:
+    def pgresult(self, result: Optional[pq.proto.PGresult]) -> None:
         self._pgresult = result
         rc = self._row_loaders = []
 
index 09791ac3a112682271d5aad5af64dfe75ce9a3c0..e7570062e770952e0f14aff756e9b9c3da597caa 100644 (file)
@@ -49,7 +49,7 @@ class BaseConnection:
     ConnStatus = pq.ConnStatus
     TransactionStatus = pq.TransactionStatus
 
-    def __init__(self, pgconn: pq.PGconn):
+    def __init__(self, pgconn: pq.proto.PGconn):
         self.pgconn = pgconn
         self.cursor_factory = cursor.BaseCursor
         self.dumpers: DumpersMap = {}
@@ -117,7 +117,7 @@ class Connection(BaseConnection):
 
     cursor_factory: Type[cursor.Cursor]
 
-    def __init__(self, pgconn: pq.PGconn):
+    def __init__(self, pgconn: pq.proto.PGconn):
         super().__init__(pgconn)
         self.lock = threading.Lock()
         self.cursor_factory = cursor.Cursor
@@ -188,7 +188,7 @@ class AsyncConnection(BaseConnection):
 
     cursor_factory: Type[cursor.AsyncCursor]
 
-    def __init__(self, pgconn: pq.PGconn):
+    def __init__(self, pgconn: pq.proto.PGconn):
         super().__init__(pgconn)
         self.lock = asyncio.Lock()
         self.cursor_factory = cursor.AsyncCursor
index e07ec0d2c1b37596ac4af755626ece96c8a34c95..b9dc38b4197576ba296adc33f967f6035691289d 100644 (file)
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
 
 class Column(Sequence[Any]):
     def __init__(
-        self, pgresult: pq.PGresult, index: int, codec: codecs.CodecInfo
+        self, pgresult: pq.proto.PGresult, index: int, codec: codecs.CodecInfo
     ):
         self._pgresult = pgresult
         self._index = index
@@ -72,7 +72,7 @@ class BaseCursor:
         self._closed = False
 
     def _reset(self) -> None:
-        self._results: List[pq.PGresult] = []
+        self._results: List[pq.proto.PGresult] = []
         self.pgresult = None
         self._pos = 0
         self._iresult = 0
@@ -90,11 +90,11 @@ class BaseCursor:
             return None
 
     @property
-    def pgresult(self) -> Optional[pq.PGresult]:
+    def pgresult(self) -> Optional[pq.proto.PGresult]:
         return self._pgresult
 
     @pgresult.setter
-    def pgresult(self, result: Optional[pq.PGresult]) -> None:
+    def pgresult(self, result: Optional[pq.proto.PGresult]) -> None:
         self._pgresult = result
         if result is not None:
             if self._transformer is not None:
@@ -169,7 +169,7 @@ class BaseCursor:
             else:
                 self.connection.pgconn.send_query(pgq.query)
 
-    def _execute_results(self, results: Sequence[pq.PGresult]) -> None:
+    def _execute_results(self, results: Sequence[pq.proto.PGresult]) -> None:
         """
         Implement part of execute() after waiting common to sync and async
         """
index a18f3c8bfb9ba86e5ad4b24b246218d559a50e93..cd95e2957931e56678f4514ea931ad735a8127a6 100644 (file)
@@ -18,10 +18,8 @@ DBAPI-defined Exceptions are defined in the following hierarchy::
 
 # Copyright (C) 2020 The Psycopg Team
 
-from typing import Any, Optional, Sequence, Type, TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from psycopg3.pq import PGresult  # noqa
+from typing import Any, Optional, Sequence, Type
+from psycopg3.pq.proto import PGresult
 
 
 class Warning(Exception):
@@ -38,7 +36,7 @@ class Error(Exception):
     """
 
     def __init__(
-        self, *args: Sequence[Any], pgresult: Optional["PGresult"] = None
+        self, *args: Sequence[Any], pgresult: Optional[PGresult] = None
     ):
         super().__init__(*args)
         self.pgresult = pgresult
@@ -112,7 +110,7 @@ def class_for_state(sqlstate: bytes) -> Type[Error]:
     return DatabaseError
 
 
-def error_from_result(result: "PGresult") -> Error:
+def error_from_result(result: PGresult) -> Error:
     from psycopg3 import pq
 
     state = result.error_field(pq.DiagnosticField.SQLSTATE) or b""
index 54d69d290f6aaba1473b332b9cfae1350a64bcce..14f5ba9118bda68f4ba61ca1d0483a943f530727 100644 (file)
@@ -29,7 +29,7 @@ PQGen = Generator[Tuple[int, Wait], Ready, RV]
 logger = logging.getLogger(__name__)
 
 
-def connect(conninfo: str) -> PQGen[pq.PGconn]:
+def connect(conninfo: str) -> PQGen[pq.proto.PGconn]:
     """
     Generator to create a database connection without blocking.
 
@@ -61,7 +61,7 @@ def connect(conninfo: str) -> PQGen[pq.PGconn]:
     return conn
 
 
-def execute(pgconn: pq.PGconn) -> PQGen[List[pq.PGresult]]:
+def execute(pgconn: pq.proto.PGconn) -> PQGen[List[pq.proto.PGresult]]:
     """
     Generator sending a query and returning results without blocking.
 
@@ -77,7 +77,7 @@ def execute(pgconn: pq.PGconn) -> PQGen[List[pq.PGresult]]:
     return rv
 
 
-def send(pgconn: pq.PGconn) -> PQGen[None]:
+def send(pgconn: pq.proto.PGconn) -> PQGen[None]:
     """
     Generator to send a query to the server without blocking.
 
@@ -99,7 +99,7 @@ def send(pgconn: pq.PGconn) -> PQGen[None]:
         continue
 
 
-def fetch(pgconn: pq.PGconn) -> PQGen[List[pq.PGresult]]:
+def fetch(pgconn: pq.proto.PGconn) -> PQGen[List[pq.proto.PGresult]]:
     """
     Generator retrieving results from the database without blocking.
 
@@ -110,7 +110,7 @@ def fetch(pgconn: pq.PGconn) -> PQGen[List[pq.PGresult]]:
     or error).
     """
     S = pq.ExecStatus
-    results: List[pq.PGresult] = []
+    results: List[pq.proto.PGresult] = []
     while 1:
         pgconn.consume_input()
         if pgconn.is_busy():
index c5d7a714d9bc3a6ffdef20d487449ef40edbd5c5..cbb90557a0411b1102e27d2968ed94bc3f4ad11b 100644 (file)
@@ -11,7 +11,7 @@ implementation-dependant but all the implementations share the same interface.
 
 import os
 import logging
-from types import ModuleType
+from typing import Callable, Type
 
 from .enums import (
     ConnStatus,
@@ -23,20 +23,31 @@ from .enums import (
     Format,
 )
 from .encodings import py_codecs
-from .misc import error_message, ConninfoOption
+from .misc import error_message, ConninfoOption, PQerror
+from . import proto
 
 logger = logging.getLogger(__name__)
 
+__impl__: str
+version: Callable[[], int]
+PGconn: Type[proto.PGconn]
+PGresult: Type[proto.PGresult]
+Conninfo: Type[proto.Conninfo]
+Escaping: Type[proto.Escaping]
 
-def import_libpq() -> ModuleType:
+
+def import_from_libpq() -> None:
     """
-    Find the best libpw wrapper available.
+    Import pq objects implementation from the best libpw wrapper available.
     """
+    global __impl__, version, PGconn, PGresult, Conninfo, Escaping
+
     impl = os.environ.get("PSYCOPG3_IMPL", "").lower()
 
     if not impl or impl == "c":
         try:
-            from . import pq_cython
+            # TODO: extension module not recognised by mypy?
+            from . import pq_cython  # type: ignore
         except Exception as e:
             if not impl:
                 logger.debug(f"C pq wrapper not available: %s", e)
@@ -45,7 +56,13 @@ def import_libpq() -> ModuleType:
                     f"requested pq implementation '{impl}' not available"
                 ) from e
         else:
-            return pq_cython
+            __impl__ = pq_cython.__impl__
+            version = pq_cython.version
+            PGconn = pq_cython.PGconn
+            PGresult = pq_cython.PGresult
+            Conninfo = pq_cython.Conninfo
+            Escaping = pq_cython.Escaping
+            return
 
     if not impl or impl == "ctypes":
         try:
@@ -58,7 +75,13 @@ def import_libpq() -> ModuleType:
                     f"requested pq implementation '{impl}' not available"
                 ) from e
         else:
-            return pq_ctypes
+            __impl__ = pq_ctypes.__impl__
+            version = pq_ctypes.version
+            PGconn = pq_ctypes.PGconn
+            PGresult = pq_ctypes.PGresult
+            Conninfo = pq_ctypes.Conninfo
+            Escaping = pq_ctypes.Escaping
+            return
 
     if impl:
         raise ImportError(f"requested pq impementation '{impl}' unknown")
@@ -66,15 +89,7 @@ def import_libpq() -> ModuleType:
         raise ImportError(f"no pq wrapper available")
 
 
-pq_module = import_libpq()
-
-__impl__ = pq_module.__impl__
-version = pq_module.version
-PGconn = pq_module.PGconn
-PGresult = pq_module.PGresult
-PQerror = pq_module.PQerror
-Conninfo = pq_module.Conninfo
-Escaping = pq_module.Escaping
+import_from_libpq()
 
 __all__ = (
     "ConnStatus",
diff --git a/psycopg3/pq/__init__.pyi b/psycopg3/pq/__init__.pyi
deleted file mode 100644 (file)
index 008a0ac..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-"""
-Public interface for the psycopg3.pq module
-
-This is provided as a stub because the implementation can be different.
-"""
-
-# Copyright (C) 2020 The Psycopg Team
-
-from typing import Any, List, Optional, Sequence
-
-from .enums import (
-    ConnStatus,
-    PollingStatus,
-    ExecStatus,
-    TransactionStatus,
-    Ping,
-    DiagnosticField,
-    Format,
-)
-from .misc import error_message, ConninfoOption
-from .encodings import py_codecs
-from ..errors import OperationalError
-
-def version() -> int: ...
-
-class PQerror(OperationalError): ...
-
-class PGconn:
-    def __init__(self, pgconn_ptr: Any): ...
-    def __del__(self) -> None: ...
-    @classmethod
-    def connect(cls, conninfo: bytes) -> "PGconn": ...
-    @classmethod
-    def connect_start(cls, conninfo: bytes) -> "PGconn": ...
-    def connect_poll(self) -> PollingStatus: ...
-    def finish(self) -> None: ...
-    @property
-    def info(self) -> List["ConninfoOption"]: ...
-    def reset(self) -> None: ...
-    def reset_start(self) -> None: ...
-    def reset_poll(self) -> PollingStatus: ...
-    @classmethod
-    def ping(self, conninfo: bytes) -> Ping: ...
-    @property
-    def db(self) -> bytes: ...
-    @property
-    def user(self) -> bytes: ...
-    @property
-    def password(self) -> bytes: ...
-    @property
-    def host(self) -> bytes: ...
-    @property
-    def hostaddr(self) -> bytes: ...
-    @property
-    def port(self) -> bytes: ...
-    @property
-    def tty(self) -> bytes: ...
-    @property
-    def options(self) -> bytes: ...
-    @property
-    def status(self) -> ConnStatus: ...
-    @property
-    def transaction_status(self) -> TransactionStatus: ...
-    def parameter_status(self, name: bytes) -> Optional[bytes]: ...
-    @property
-    def error_message(self) -> bytes: ...
-    @property
-    def protocol_version(self) -> int: ...
-    @property
-    def server_version(self) -> int: ...
-    @property
-    def socket(self) -> int: ...
-    @property
-    def backend_pid(self) -> int: ...
-    @property
-    def needs_password(self) -> bool: ...
-    @property
-    def used_password(self) -> bool: ...
-    @property
-    def ssl_in_use(self) -> bool: ...
-    def exec_(self, command: bytes) -> "PGresult": ...
-    def send_query(self, command: bytes) -> None: ...
-    def exec_params(
-        self,
-        command: bytes,
-        param_values: Optional[Sequence[Optional[bytes]]],
-        param_types: Optional[Sequence[int]] = None,
-        param_formats: Optional[Sequence[Format]] = None,
-        result_format: Format = Format.TEXT,
-    ) -> "PGresult": ...
-    def send_query_params(
-        self,
-        command: bytes,
-        param_values: Optional[Sequence[Optional[bytes]]],
-        param_types: Optional[Sequence[int]] = None,
-        param_formats: Optional[Sequence[Format]] = None,
-        result_format: Format = Format.TEXT,
-    ) -> None: ...
-    def send_prepare(
-        self,
-        name: bytes,
-        command: bytes,
-        param_types: Optional[Sequence[int]] = None,
-    ) -> None: ...
-    def send_query_prepared(
-        self,
-        name: bytes,
-        param_values: Optional[Sequence[Optional[bytes]]],
-        param_formats: Optional[Sequence[Format]] = None,
-        result_format: Format = Format.TEXT,
-    ) -> None: ...
-    def prepare(
-        self,
-        name: bytes,
-        command: bytes,
-        param_types: Optional[Sequence[int]] = None,
-    ) -> "PGresult": ...
-    def exec_prepared(
-        self,
-        name: bytes,
-        param_values: Optional[Sequence[bytes]],
-        param_formats: Optional[Sequence[int]] = None,
-        result_format: int = 0,
-    ) -> "PGresult": ...
-    def describe_prepared(self, name: bytes) -> "PGresult": ...
-    def describe_portal(self, name: bytes) -> "PGresult": ...
-    def get_result(self) -> Optional["PGresult"]: ...
-    def consume_input(self) -> None: ...
-    def is_busy(self) -> int: ...
-    @property
-    def nonblocking(self) -> int: ...
-    @nonblocking.setter
-    def nonblocking(self, arg: int) -> None: ...
-    def flush(self) -> int: ...
-    def make_empty_result(self, exec_status: ExecStatus) -> "PGresult": ...
-
-class PGresult:
-    def __init__(self, pgresult_ptr: Any): ...
-    def __del__(self) -> None: ...
-    def clear(self) -> None: ...
-    @property
-    def status(self) -> ExecStatus: ...
-    @property
-    def error_message(self) -> bytes: ...
-    def error_field(self, fieldcode: DiagnosticField) -> Optional[bytes]: ...
-    @property
-    def ntuples(self) -> int: ...
-    @property
-    def nfields(self) -> int: ...
-    def fname(self, column_number: int) -> Optional[bytes]: ...
-    def ftable(self, column_number: int) -> int: ...
-    def ftablecol(self, column_number: int) -> int: ...
-    def fformat(self, column_number: int) -> Format: ...
-    def ftype(self, column_number: int) -> int: ...
-    def fmod(self, column_number: int) -> int: ...
-    def fsize(self, column_number: int) -> int: ...
-    @property
-    def binary_tuples(self) -> Format: ...
-    def get_value(
-        self, row_number: int, column_number: int
-    ) -> Optional[bytes]: ...
-    @property
-    def nparams(self) -> int: ...
-    def param_type(self, param_number: int) -> int: ...
-    @property
-    def command_status(self) -> Optional[bytes]: ...
-    @property
-    def command_tuples(self) -> Optional[int]: ...
-    @property
-    def oid_value(self) -> int: ...
-
-class Conninfo:
-    @classmethod
-    def get_defaults(cls) -> List[ConninfoOption]: ...
-    @classmethod
-    def parse(cls, conninfo: bytes) -> List[ConninfoOption]: ...
-    @classmethod
-    def _options_from_array(
-        cls, opts: Sequence[Any]
-    ) -> List[ConninfoOption]: ...
-
-class Escaping:
-    def __init__(self, conn: Optional[PGconn] = None): ...
-    def escape_bytea(self, data: bytes) -> bytes: ...
-    def unescape_bytea(self, data: bytes) -> bytes: ...
-
-__all__ = (
-    "ConnStatus",
-    "PollingStatus",
-    "TransactionStatus",
-    "ExecStatus",
-    "Ping",
-    "DiagnosticField",
-    "Format",
-    "PGconn",
-    "Conninfo",
-    "PQerror",
-    "error_message",
-    "ConninfoOption",
-    "py_codecs",
-    "version",
-)
index d2dd78329feef0f678f8f7d1a236f728cd7bcf7c..ca0a9fdd40c111e3ff89ed9679235b3782b630d2 100644 (file)
@@ -5,10 +5,15 @@ Various functionalities to make easier to work with the libpq.
 # Copyright (C) 2020 The Psycopg Team
 
 from collections import namedtuple
-from typing import TYPE_CHECKING, Union
+from typing import cast, Union
 
-if TYPE_CHECKING:
-    from psycopg3.pq import PGconn, PGresult  # noqa
+from ..errors import OperationalError
+from .enums import DiagnosticField
+from .proto import PGconn, PGresult
+
+
+class PQerror(OperationalError):
+    pass
 
 
 ConninfoOption = namedtuple(
@@ -16,25 +21,18 @@ ConninfoOption = namedtuple(
 )
 
 
-def error_message(obj: Union["PGconn", "PGresult"]) -> str:
+def error_message(obj: Union[PGconn, PGresult]) -> str:
     """
     Return an error message from a PGconn or PGresult.
 
     The return value is a str (unlike pq data which is usually bytes).
     """
-    from psycopg3 import pq
-
     bmsg: bytes
 
-    if isinstance(obj, pq.PGconn):
-        bmsg = obj.error_message
-
-        # strip severity and whitespaces
-        if bmsg:
-            bmsg = bmsg.splitlines()[0].split(b":", 1)[-1].strip()
+    if hasattr(obj, "error_field"):
+        obj = cast(PGresult, obj)
 
-    elif isinstance(obj, pq.PGresult):
-        bmsg = obj.error_field(pq.DiagnosticField.MESSAGE_PRIMARY) or b""
+        bmsg = obj.error_field(DiagnosticField.MESSAGE_PRIMARY) or b""
         if not bmsg:
             bmsg = obj.error_message
 
@@ -42,6 +40,14 @@ def error_message(obj: Union["PGconn", "PGresult"]) -> str:
             if bmsg:
                 bmsg = bmsg.splitlines()[0].split(b":", 1)[-1].strip()
 
+    elif hasattr(obj, "error_message"):
+        # obj is a PGconn
+        bmsg = obj.error_message
+
+        # strip severity and whitespaces
+        if bmsg:
+            bmsg = bmsg.splitlines()[0].split(b":", 1)[-1].strip()
+
     else:
         raise TypeError(
             f"PGconn or PGresult expected, got {type(obj).__name__}"
index e7683626b152b7ac94e9175836efc1863d7d0f8a..8a7fa3881ce9648c8ac7a39f779b2558ad3b8c9e 100644 (file)
@@ -22,9 +22,8 @@ from .enums import (
     DiagnosticField,
     Format,
 )
-from .misc import error_message, ConninfoOption
+from .misc import error_message, ConninfoOption, PQerror
 from . import _pq_ctypes as impl
-from ..errors import OperationalError
 
 if TYPE_CHECKING:
     from psycopg3 import pq  # noqa
@@ -36,10 +35,6 @@ def version() -> int:
     return impl.PQlibVersion()
 
 
-class PQerror(OperationalError):
-    pass
-
-
 class PGconn:
     __slots__ = ("pgconn_ptr",)
 
@@ -201,10 +196,7 @@ class PGconn:
             raise TypeError(f"bytes expected, got {type(command)} instead")
         self._ensure_pgconn()
         if not impl.PQsendQuery(self.pgconn_ptr, command):
-            raise PQerror(
-                "sending query failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
-            )
+            raise PQerror(f"sending query failed: {error_message(self)}")
 
     def exec_params(
         self,
@@ -237,8 +229,7 @@ class PGconn:
         self._ensure_pgconn()
         if not impl.PQsendQueryParams(*args):
             raise PQerror(
-                "sending query and params failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
+                f"sending query and params failed: {error_message(self)}"
             )
 
     def send_prepare(
@@ -260,8 +251,7 @@ class PGconn:
             self.pgconn_ptr, name, command, nparams, atypes
         ):
             raise PQerror(
-                "sending query and params failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
+                f"sending query and params failed: {error_message(self)}"
             )
 
     def send_query_prepared(
@@ -281,8 +271,7 @@ class PGconn:
         self._ensure_pgconn()
         if not impl.PQsendQueryPrepared(*args):
             raise PQerror(
-                "sending prepared query failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
+                f"sending prepared query failed: {error_message(self)}"
             )
 
     def _query_params_args(
@@ -431,10 +420,7 @@ class PGconn:
 
     def consume_input(self) -> None:
         if 1 != impl.PQconsumeInput(self.pgconn_ptr):
-            raise PQerror(
-                "consuming input failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
-            )
+            raise PQerror(f"consuming input failed: {error_message(self)}")
 
     def is_busy(self) -> int:
         return impl.PQisBusy(self.pgconn_ptr)
@@ -446,17 +432,12 @@ class PGconn:
     @nonblocking.setter
     def nonblocking(self, arg: int) -> None:
         if 0 > impl.PQsetnonblocking(self.pgconn_ptr, arg):
-            raise PQerror(
-                f"setting nonblocking failed:"
-                f" {error_message(t_cast('pq.PGconn', self))}"
-            )
+            raise PQerror(f"setting nonblocking failed: {error_message(self)}")
 
     def flush(self) -> int:
         rv: int = impl.PQflush(self.pgconn_ptr)
         if rv < 0:
-            raise PQerror(
-                f"flushing failed:{error_message(t_cast('pq.PGconn', self))}"
-            )
+            raise PQerror(f"flushing failed: {error_message(self)}")
         return rv
 
     def make_empty_result(self, exec_status: ExecStatus) -> "PGresult":
index f8dd197817f8b21b31af7a81aca41d1847617eef..83b81a49d0c33435d4169fd49bae23ac25f11902 100644 (file)
@@ -9,7 +9,7 @@ from cpython.mem cimport PyMem_Malloc, PyMem_Free
 from psycopg3.pq cimport libpq as impl
 from psycopg3.errors import OperationalError
 
-from .misc import error_message, ConninfoOption
+from .misc import error_message, ConninfoOption, PQerror
 from .enums import (
     ConnStatus,
     PollingStatus,
@@ -24,10 +24,6 @@ from .enums import (
 __impl__ = 'c'
 
 
-class PQerror(OperationalError):
-    pass
-
-
 def version():
     return impl.PQlibVersion()
 
@@ -110,9 +106,9 @@ cdef class PGconn:
     def host(self) -> bytes:
         return self._call_bytes(impl.PQhost)
 
-    @property
-    def hostaddr(self) -> bytes:
-    #     return self._call_bytes(impl.PQhostaddr)
+    @property
+    def hostaddr(self) -> bytes:
+        return b'TODO'
 
     @property
     def port(self) -> bytes:
diff --git a/psycopg3/pq/proto.py b/psycopg3/pq/proto.py
new file mode 100644 (file)
index 0000000..36ef88b
--- /dev/null
@@ -0,0 +1,312 @@
+from typing import Any, List, Optional, Sequence, TYPE_CHECKING
+from typing_extensions import Protocol
+
+from .enums import (
+    ConnStatus,
+    PollingStatus,
+    ExecStatus,
+    TransactionStatus,
+    Ping,
+    DiagnosticField,
+    Format,
+)
+
+if TYPE_CHECKING:
+    from .misc import ConninfoOption  # noqa
+
+
+class PGconn(Protocol):
+    @classmethod
+    def connect(cls, conninfo: bytes) -> "PGconn":
+        ...
+
+    @classmethod
+    def connect_start(cls, conninfo: bytes) -> "PGconn":
+        ...
+
+    def connect_poll(self) -> PollingStatus:
+        ...
+
+    def finish(self) -> None:
+        ...
+
+    @property
+    def info(self) -> List["ConninfoOption"]:
+        ...
+
+    def reset(self) -> None:
+        ...
+
+    def reset_start(self) -> None:
+        ...
+
+    def reset_poll(self) -> PollingStatus:
+        ...
+
+    @classmethod
+    def ping(self, conninfo: bytes) -> Ping:
+        ...
+
+    @property
+    def db(self) -> bytes:
+        ...
+
+    @property
+    def user(self) -> bytes:
+        ...
+
+    @property
+    def password(self) -> bytes:
+        ...
+
+    @property
+    def host(self) -> bytes:
+        ...
+
+    @property
+    def hostaddr(self) -> bytes:
+        ...
+
+    @property
+    def port(self) -> bytes:
+        ...
+
+    @property
+    def tty(self) -> bytes:
+        ...
+
+    @property
+    def options(self) -> bytes:
+        ...
+
+    @property
+    def status(self) -> ConnStatus:
+        ...
+
+    @property
+    def transaction_status(self) -> TransactionStatus:
+        ...
+
+    def parameter_status(self, name: bytes) -> Optional[bytes]:
+        ...
+
+    @property
+    def error_message(self) -> bytes:
+        ...
+
+    @property
+    def protocol_version(self) -> int:
+        ...
+
+    @property
+    def server_version(self) -> int:
+        ...
+
+    @property
+    def socket(self) -> int:
+        ...
+
+    @property
+    def backend_pid(self) -> int:
+        ...
+
+    @property
+    def needs_password(self) -> bool:
+        ...
+
+    @property
+    def used_password(self) -> bool:
+        ...
+
+    @property
+    def ssl_in_use(self) -> bool:
+        ...
+
+    def exec_(self, command: bytes) -> "PGresult":
+        ...
+
+    def send_query(self, command: bytes) -> None:
+        ...
+
+    def exec_params(
+        self,
+        command: bytes,
+        param_values: Optional[Sequence[Optional[bytes]]],
+        param_types: Optional[Sequence[int]] = None,
+        param_formats: Optional[Sequence[Format]] = None,
+        result_format: Format = Format.TEXT,
+    ) -> "PGresult":
+        ...
+
+    def send_query_params(
+        self,
+        command: bytes,
+        param_values: Optional[Sequence[Optional[bytes]]],
+        param_types: Optional[Sequence[int]] = None,
+        param_formats: Optional[Sequence[Format]] = None,
+        result_format: Format = Format.TEXT,
+    ) -> None:
+        ...
+
+    def send_prepare(
+        self,
+        name: bytes,
+        command: bytes,
+        param_types: Optional[Sequence[int]] = None,
+    ) -> None:
+        ...
+
+    def send_query_prepared(
+        self,
+        name: bytes,
+        param_values: Optional[Sequence[Optional[bytes]]],
+        param_formats: Optional[Sequence[Format]] = None,
+        result_format: Format = Format.TEXT,
+    ) -> None:
+        ...
+
+    def prepare(
+        self,
+        name: bytes,
+        command: bytes,
+        param_types: Optional[Sequence[int]] = None,
+    ) -> "PGresult":
+        ...
+
+    def exec_prepared(
+        self,
+        name: bytes,
+        param_values: Optional[Sequence[bytes]],
+        param_formats: Optional[Sequence[int]] = None,
+        result_format: int = 0,
+    ) -> "PGresult":
+        ...
+
+    def describe_prepared(self, name: bytes) -> "PGresult":
+        ...
+
+    def describe_portal(self, name: bytes) -> "PGresult":
+        ...
+
+    def get_result(self) -> Optional["PGresult"]:
+        ...
+
+    def consume_input(self) -> None:
+        ...
+
+    def is_busy(self) -> int:
+        ...
+
+    @property
+    def nonblocking(self) -> int:
+        ...
+
+    @nonblocking.setter
+    def nonblocking(self, arg: int) -> None:
+        ...
+
+    def flush(self) -> int:
+        ...
+
+    def make_empty_result(self, exec_status: ExecStatus) -> "PGresult":
+        ...
+
+
+class PGresult(Protocol):
+    def clear(self) -> None:
+        ...
+
+    @property
+    def status(self) -> ExecStatus:
+        ...
+
+    @property
+    def error_message(self) -> bytes:
+        ...
+
+    def error_field(self, fieldcode: DiagnosticField) -> Optional[bytes]:
+        ...
+
+    @property
+    def ntuples(self) -> int:
+        ...
+
+    @property
+    def nfields(self) -> int:
+        ...
+
+    def fname(self, column_number: int) -> Optional[bytes]:
+        ...
+
+    def ftable(self, column_number: int) -> int:
+        ...
+
+    def ftablecol(self, column_number: int) -> int:
+        ...
+
+    def fformat(self, column_number: int) -> Format:
+        ...
+
+    def ftype(self, column_number: int) -> int:
+        ...
+
+    def fmod(self, column_number: int) -> int:
+        ...
+
+    def fsize(self, column_number: int) -> int:
+        ...
+
+    @property
+    def binary_tuples(self) -> Format:
+        ...
+
+    def get_value(
+        self, row_number: int, column_number: int
+    ) -> Optional[bytes]:
+        ...
+
+    @property
+    def nparams(self) -> int:
+        ...
+
+    def param_type(self, param_number: int) -> int:
+        ...
+
+    @property
+    def command_status(self) -> Optional[bytes]:
+        ...
+
+    @property
+    def command_tuples(self) -> Optional[int]:
+        ...
+
+    @property
+    def oid_value(self) -> int:
+        ...
+
+
+class Conninfo(Protocol):
+    @classmethod
+    def get_defaults(cls) -> List["ConninfoOption"]:
+        ...
+
+    @classmethod
+    def parse(cls, conninfo: bytes) -> List["ConninfoOption"]:
+        ...
+
+    @classmethod
+    def _options_from_array(
+        cls, opts: Sequence[Any]
+    ) -> List["ConninfoOption"]:
+        ...
+
+
+class Escaping(Protocol):
+    def __init__(self, conn: Optional[PGconn] = None):
+        ...
+
+    def escape_bytea(self, data: bytes) -> bytes:
+        ...
+
+    def unescape_bytea(self, data: bytes) -> bytes:
+        ...
index 85ec9a6ae2f832df2299354ac29f4b7562690c83..ed8ef71a350a7c4eebee5a671a63eebaedf38342 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -95,7 +95,7 @@ setup(
     packages=find_packages(exclude=["tests"]),
     classifiers=[x for x in classifiers.split("\n") if x],
     setup_requires=["Cython"],
-    install_requires=["Cython"],
+    install_requires=["Cython", "typing_extensions"],
     zip_safe=False,
     version=version,
     project_urls={