Skip to content

Commit 765f663

Browse files
committed
refactor(tests): tweak test_cursor to make it more similar to the async version
1 parent b1c3c19 commit 765f663

File tree

9 files changed

+276
-211
lines changed

9 files changed

+276
-211
lines changed

tests/_test_cursor.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
"""
2+
Support module for test_cursor[_async].py
3+
"""
4+
5+
import re
6+
from typing import Any, List, Match, Union
7+
8+
import pytest
9+
import psycopg
10+
from psycopg.rows import RowMaker
11+
12+
13+
@pytest.fixture(scope="session")
14+
def _execmany(svcconn):
15+
cur = svcconn.cursor()
16+
cur.execute(
17+
"""
18+
drop table if exists execmany;
19+
create table execmany (id serial primary key, num integer, data text)
20+
"""
21+
)
22+
23+
24+
@pytest.fixture(scope="function")
25+
def execmany(svcconn, _execmany):
26+
cur = svcconn.cursor()
27+
cur.execute("truncate table execmany")
28+
29+
30+
def ph(cur: Any, query: str) -> str:
31+
"""Change placeholders in a query from %s to $n if testing a raw cursor"""
32+
if not isinstance(cur, (psycopg.RawCursor, psycopg.AsyncRawCursor)):
33+
return query
34+
35+
if "%(" in query:
36+
raise pytest.skip("RawCursor only supports positional placeholders")
37+
38+
n = 1
39+
40+
def s(m: Match[str]) -> str:
41+
nonlocal n
42+
rv = f"${n}"
43+
n += 1
44+
return rv
45+
46+
return re.sub(r"(?<!%)(%[bst])", s, query)
47+
48+
49+
def my_row_factory(
50+
cursor: Union[psycopg.Cursor[List[str]], psycopg.AsyncCursor[List[str]]]
51+
) -> RowMaker[List[str]]:
52+
if cursor.description is not None:
53+
titles = [c.name for c in cursor.description]
54+
55+
def mkrow(values):
56+
return [f"{value.upper()}{title}" for title, value in zip(titles, values)]
57+
58+
return mkrow
59+
else:
60+
return psycopg.rows.no_result

tests/test_column.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import pickle
2+
3+
import pytest
4+
5+
from psycopg.postgres import types as builtins
6+
from .fix_crdb import is_crdb, crdb_encoding, crdb_time_precision
7+
8+
9+
def test_description_attribs(conn):
10+
curs = conn.cursor()
11+
curs.execute(
12+
"""select
13+
3.14::decimal(10,2) as pi,
14+
'hello'::text as hi,
15+
'2010-02-18'::date as now
16+
"""
17+
)
18+
assert len(curs.description) == 3
19+
for c in curs.description:
20+
len(c) == 7 # DBAPI happy
21+
for i, a in enumerate(
22+
"""
23+
name type_code display_size internal_size precision scale null_ok
24+
""".split()
25+
):
26+
assert c[i] == getattr(c, a)
27+
28+
# Won't fill them up
29+
assert c.null_ok is None
30+
31+
c = curs.description[0]
32+
assert c.name == "pi"
33+
assert c.type_code == builtins["numeric"].oid
34+
assert c.display_size is None
35+
assert c.internal_size is None
36+
assert c.precision == 10
37+
assert c.scale == 2
38+
39+
c = curs.description[1]
40+
assert c.name == "hi"
41+
assert c.type_code == builtins["text"].oid
42+
assert c.display_size is None
43+
assert c.internal_size is None
44+
assert c.precision is None
45+
assert c.scale is None
46+
47+
c = curs.description[2]
48+
assert c.name == "now"
49+
assert c.type_code == builtins["date"].oid
50+
assert c.display_size is None
51+
if is_crdb(conn) and conn.info.server_version < 230000:
52+
assert c.internal_size == 16
53+
else:
54+
assert c.internal_size == 4
55+
assert c.precision is None
56+
assert c.scale is None
57+
58+
59+
def test_description_slice(conn):
60+
curs = conn.cursor()
61+
curs.execute("select 1::int as a")
62+
curs.description[0][0:2] == ("a", 23)
63+
64+
65+
@pytest.mark.parametrize(
66+
"type, precision, scale, dsize, isize",
67+
[
68+
("text", None, None, None, None),
69+
("varchar", None, None, None, None),
70+
("varchar(42)", None, None, 42, None),
71+
("int4", None, None, None, 4),
72+
("numeric", None, None, None, None),
73+
("numeric(10)", 10, 0, None, None),
74+
("numeric(10, 3)", 10, 3, None, None),
75+
("time", None, None, None, 8),
76+
crdb_time_precision("time(4)", 4, None, None, 8),
77+
crdb_time_precision("time(10)", 6, None, None, 8),
78+
],
79+
)
80+
def test_details(conn, type, precision, scale, dsize, isize):
81+
cur = conn.cursor()
82+
cur.execute(f"select null::{type}")
83+
col = cur.description[0]
84+
repr(col)
85+
assert col.precision == precision
86+
assert col.scale == scale
87+
assert col.display_size == dsize
88+
assert col.internal_size == isize
89+
90+
91+
def test_pickle(conn):
92+
curs = conn.cursor()
93+
curs.execute(
94+
"""select
95+
3.14::decimal(10,2) as pi,
96+
'hello'::text as hi,
97+
'2010-02-18'::date as now
98+
"""
99+
)
100+
description = curs.description
101+
pickled = pickle.dumps(description, pickle.HIGHEST_PROTOCOL)
102+
unpickled = pickle.loads(pickled)
103+
assert [tuple(d) for d in description] == [tuple(d) for d in unpickled]
104+
105+
106+
@pytest.mark.crdb_skip("no col query")
107+
def test_no_col_query(conn):
108+
cur = conn.execute("select")
109+
assert cur.description == []
110+
assert cur.fetchall() == [()]
111+
112+
113+
def test_description_closed_connection(conn):
114+
# If we have reasons to break this test we will (e.g. we really need
115+
# the connection). In #172 it fails just by accident.
116+
cur = conn.execute("select 1::int4 as foo")
117+
conn.close()
118+
assert len(cur.description) == 1
119+
col = cur.description[0]
120+
assert col.name == "foo"
121+
assert col.type_code == 23
122+
123+
124+
def test_name_not_a_name(conn):
125+
cur = conn.cursor()
126+
(res,) = cur.execute("""select 'x' as "foo-bar" """).fetchone()
127+
assert res == "x"
128+
assert cur.description[0].name == "foo-bar"
129+
130+
131+
@pytest.mark.parametrize("encoding", ["utf8", crdb_encoding("latin9")])
132+
def test_name_encode(conn, encoding):
133+
conn.execute(f"set client_encoding to {encoding}")
134+
cur = conn.cursor()
135+
(res,) = cur.execute("""select 'x' as "\u20ac" """).fetchone()
136+
assert res == "x"
137+
assert cur.description[0].name == "\u20ac"

tests/test_connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from psycopg.conninfo import conninfo_to_dict, make_conninfo
1313

1414
from .utils import gc_collect
15-
from .test_cursor import my_row_factory
15+
from ._test_cursor import my_row_factory
1616
from .test_adapt import make_bin_dumper, make_dumper
1717

1818

tests/test_connection_async.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from psycopg.conninfo import conninfo_to_dict, make_conninfo
1111

1212
from .utils import gc_collect
13-
from .test_cursor import my_row_factory
13+
from ._test_cursor import my_row_factory
1414
from .test_connection import tx_params, tx_params_isolation, tx_values_map
1515
from .test_connection import conninfo_params_timeout
1616
from .test_connection import testctx # noqa: F401 # fixture

0 commit comments

Comments
 (0)