--- /dev/null
+from typing import Optional, Tuple, Union
+
+from psycopg import pq
+from psycopg.proto import Dumper, Loader, AdaptContext, PyFormat, Buffer
+
+
+def f() -> None:
+ d: Dumper = MyStrDumper(str, None)
+ assert d.dump("abc") == b"abcabc"
+ assert d.quote("abc") == b"'abcabc'"
+
+ lo: Loader = MyTextLoader(0, None)
+ assert lo.load(b"abc") == "abcabc"
+
+
+class MyStrDumper:
+ format = pq.Format.TEXT
+
+ def __init__(self, cls: type, context: Optional[AdaptContext] = None):
+ self._cls = cls
+ self.oid = 25 # text
+
+ def dump(self, obj: str) -> bytes:
+ return (obj * 2).encode("utf-8")
+
+ def quote(self, obj: str) -> bytes:
+ value = self.dump(obj)
+ esc = pq.Escaping()
+ return b"'%s'" % esc.escape_string(value.replace(b"h", b"q"))
+
+ def get_key(self, obj: str, format: PyFormat) -> type:
+ return self._cls
+
+ def upgrade(self, obj: str, format: PyFormat) -> "MyStrDumper":
+ return self
+
+
+class MyTextLoader:
+ format = pq.Format.TEXT
+
+ def __init__(self, oid: int, context: Optional[AdaptContext] = None):
+ pass
+
+ def load(self, data: Buffer) -> str:
+ return (bytes(data) * 2).decode("utf-8")
+
+
+# This should be the definition of psycopg.adapt.DumperKey, but mypy doesn't
+# support recursive types. When it will, this statement will give an error
+# (unused type: ignore) so we can fix our definition.
+_DumperKey = Union[type, Tuple[Union[type, "_DumperKey"]]] # type: ignore
def test_dumper_protocol(conn):
# This class doesn't inherit from adapt.Dumper but passes a mypy check
- from .typing_example import MyStrDumper
+ from .adapters_example import MyStrDumper
conn.adapters.register_dumper(str, MyStrDumper)
cur = conn.execute("select %s", ["hello"])
def test_loader_protocol(conn):
# This class doesn't inherit from adapt.Loader but passes a mypy check
- from .typing_example import MyTextLoader
+ from .adapters_example import MyTextLoader
conn.adapters.register_loader("text", MyTextLoader)
cur = conn.execute("select 'hello'::text")
@pytest.mark.slow
-@pytest.mark.skipif(sys.version_info < (3, 7), reason="no future annotations")
-def test_typing_example(mypy):
- cp = mypy.run("tests/typing_example.py")
+@pytest.mark.parametrize(
+ "filename",
+ [
+ "tests/adapters_example.py",
+ pytest.param(
+ "tests/typing_example.py",
+ marks=pytest.mark.skipif(
+ sys.version_info < (3, 7), reason="no future annotations"
+ ),
+ ),
+ ],
+)
+def test_typing_example(mypy, filename):
+ cp = mypy.run(filename)
errors = cp.stdout.decode("utf8", "replace").splitlines()
assert not errors
assert cp.returncode == 0
from __future__ import annotations
from dataclasses import dataclass
-from typing import Any, Callable, Optional, Sequence, Tuple, Union
+from typing import Any, Callable, Optional, Sequence, Tuple
from psycopg import AnyCursor, Connection, Cursor, ServerCursor, connect
-from psycopg import pq
-from psycopg.proto import Dumper, Loader, AdaptContext, PyFormat, Buffer
def int_row_factory(cursor: AnyCursor[int]) -> Callable[[Sequence[int]], int]:
cur3.execute("select 42")
r3 = cur3.fetchone()
r3 and len(r3)
-
-
-def f() -> None:
- d: Dumper = MyStrDumper(str, None)
- assert d.dump("abc") == b"abcabc"
- assert d.quote("abc") == b"'abcabc'"
-
- lo: Loader = MyTextLoader(0, None)
- assert lo.load(b"abc") == "abcabc"
-
-
-class MyStrDumper:
- format = pq.Format.TEXT
-
- def __init__(self, cls: type, context: Optional[AdaptContext] = None):
- self._cls = cls
- self.oid = 25 # text
-
- def dump(self, obj: str) -> bytes:
- return (obj * 2).encode("utf-8")
-
- def quote(self, obj: str) -> bytes:
- value = self.dump(obj)
- esc = pq.Escaping()
- return b"'%s'" % esc.escape_string(value.replace(b"h", b"q"))
-
- def get_key(self, obj: str, format: PyFormat) -> type:
- return self._cls
-
- def upgrade(self, obj: str, format: PyFormat) -> "MyStrDumper":
- return self
-
-
-class MyTextLoader:
- format = pq.Format.TEXT
-
- def __init__(self, oid: int, context: Optional[AdaptContext] = None):
- pass
-
- def load(self, data: Buffer) -> str:
- return (bytes(data) * 2).decode("utf-8")
-
-
-# This should be the definition of psycopg.adapt.DumperKey, but mypy doesn't
-# support recursive types. When it will, this statement will give an error
-# (unused type: ignore) so we can fix our definition.
-_DumperKey = Union[type, Tuple[Union[type, "_DumperKey"]]] # type: ignore