From: Daniele Varrazzo Date: Sun, 11 Jul 2021 02:27:20 +0000 (+0200) Subject: Allow adapter protocols tests to run on Python 3.6 X-Git-Tag: 3.0.dev1~28^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cfd0499835b7698d27efb1e80397afb3fdf8a9a;p=thirdparty%2Fpsycopg.git Allow adapter protocols tests to run on Python 3.6 --- diff --git a/tests/adapters_example.py b/tests/adapters_example.py new file mode 100644 index 000000000..57ff38091 --- /dev/null +++ b/tests/adapters_example.py @@ -0,0 +1,51 @@ +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 diff --git a/tests/test_adapt.py b/tests/test_adapt.py index 9a5f1b689..3f9d93c5b 100644 --- a/tests/test_adapt.py +++ b/tests/test_adapt.py @@ -108,7 +108,7 @@ def test_subclass_dumper(conn): 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"]) @@ -121,7 +121,7 @@ def test_dumper_protocol(conn): 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") diff --git a/tests/test_typing.py b/tests/test_typing.py index 365ae542b..3ef420673 100644 --- a/tests/test_typing.py +++ b/tests/test_typing.py @@ -6,9 +6,20 @@ import pytest @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 diff --git a/tests/typing_example.py b/tests/typing_example.py index 6746125fa..b0d4c3e61 100644 --- a/tests/typing_example.py +++ b/tests/typing_example.py @@ -3,11 +3,9 @@ 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]: @@ -87,50 +85,3 @@ def check_row_factory_connection() -> None: 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