]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Drop Connection.client_encoding attribute
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 14 May 2021 22:24:24 +0000 (00:24 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 26 Sep 2021 22:08:43 +0000 (00:08 +0200)
Expose it as `Connection.info.encoding`, but use pgconn_encoding()
internally.

17 files changed:
docs/api/connections.rst
docs/basic/adapt.rst
psycopg/psycopg/_column.py
psycopg/psycopg/_encodings.py
psycopg/psycopg/_queries.py
psycopg/psycopg/connection.py
psycopg/psycopg/connection_async.py
psycopg/psycopg/conninfo.py
psycopg/psycopg/copy.py
psycopg/psycopg/cursor.py
psycopg/psycopg/server_cursor.py
psycopg/psycopg/sql.py
psycopg/psycopg/types/string.py
tests/fix_db.py
tests/test_connection.py
tests/test_connection_async.py
tests/test_conninfo.py

index 19d3ae2c5d66e6ae80565c6211b92b695040cc83..8c9e6a379b7d867d129bef78c333a9863c9a1401 100644 (file)
@@ -225,34 +225,6 @@ The `!Connection` class
 
     .. autoattribute:: info
 
-    .. autoattribute:: client_encoding
-
-        The value returned is always normalized to the Python codec
-        `~codecs.CodecInfo.name`::
-
-            conn.client_encoding = 'latin9'
-            conn.client_encoding
-            'iso8859-15'
-
-        and it reflects the current connection property, even if it is set
-        outside Python::
-
-            conn.execute("SET client_encoding TO LATIN1")
-            conn.client_encoding
-            'iso8859-1'
-
-        A few PostgreSQL encodings are not available in Python and cannot be
-        selected (currently ``EUC_TW``, ``MULE_INTERNAL``). The PostgreSQL
-        ``SQL_ASCII`` encoding has the special meaning of "no encoding": see
-        :ref:`adapt-string` for details.
-
-        .. seealso::
-
-            The `PostgreSQL supported encodings`__.
-
-            .. __: https://www.postgresql.org/docs/current/multibyte.html
-
-
     .. autoattribute:: prepare_threshold
 
         See :ref:`prepared-statements` for details.
@@ -433,6 +405,26 @@ Connection support objects
         ``standard_conforming_strings``... See :pq:`PQparameterStatus()` for
         all the available parameters.
 
+    .. autoattribute:: encoding
+
+        The value returned is always normalized to the Python codec
+        `~codecs.CodecInfo.name`::
+
+            conn.execute("SET client_encoding TO LATIN9")
+            conn.info.encoding
+            'iso8859-15'
+
+        A few PostgreSQL encodings are not available in Python and cannot be
+        selected (currently ``EUC_TW``, ``MULE_INTERNAL``). The PostgreSQL
+        ``SQL_ASCII`` encoding has the special meaning of "no encoding": see
+        :ref:`adapt-string` for details.
+
+        .. seealso::
+
+            The `PostgreSQL supported encodings`__.
+
+            .. __: https://www.postgresql.org/docs/current/multibyte.html
+
 
 .. rubric:: Objects involved in :ref:`transactions`
 
index 99b3a2a89fd9d798fdf697fea3550d7f12e9ad61..024f226926c98acadb77e24ea4fdc10b852ab6c9 100644 (file)
@@ -102,11 +102,11 @@ such as :sql:`text` and :sql:`varchar` are converted back to Python `!str`:
     'Crème Brûlée at 4.99€'
 
 PostgreSQL databases `have an encoding`__, and `the session has an encoding`__
-too, exposed in the `Connection.client_encoding` attribute. If your database
-and connection are in UTF-8 encoding you will likely have no problem,
-otherwise you will have to make sure that your application only deals with the
-non-ASCII chars that the database can handle; failing to do so may result in
-encoding/decoding errors:
+too, exposed in the `!Connection.info.`\ `~ConnectionInfo.encoding`
+attribute. If your database and connection are in UTF-8 encoding you will
+likely have no problem, otherwise you will have to make sure that your
+application only deals with the non-ASCII chars that the database can handle;
+failing to do so may result in encoding/decoding errors:
 
 .. __: https://www.postgresql.org/docs/current/sql-createdatabase.html
 .. __: https://www.postgresql.org/docs/current/multibyte.html
@@ -114,17 +114,17 @@ encoding/decoding errors:
 .. code:: python
 
     # The encoding is set at connection time according to the db configuration
-    conn.client_encoding
+    conn.info.encoding
     'utf-8'
 
     # The Latin-9 encoding can manage some European accented letters
     # and the Euro symbol
-    conn.client_encoding = 'latin9'
+    conn.execute("SET client_encoding TO LATIN9")
     conn.execute("SELECT entry FROM menu WHERE id = 1").fetchone()[0]
     'Crème Brûlée at 4.99€'
 
     # The Latin-1 encoding doesn't have a representation for the Euro symbol
-    conn.client_encoding = 'latin1'
+    conn.execute("SET client_encoding TO LATIN1")
     conn.execute("SELECT entry FROM menu WHERE id = 1").fetchone()[0]
     # Traceback (most recent call last)
     # ...
@@ -132,13 +132,12 @@ encoding/decoding errors:
     # in encoding "UTF8" has no equivalent in encoding "LATIN1"
 
 In rare cases you may have strings with unexpected encodings in the database.
-Using the ``SQL_ASCII`` client encoding (or setting
-`~Connection.client_encoding` ``= "ascii"``) will disable decoding of the data
+Using the ``SQL_ASCII`` client encoding  will disable decoding of the data
 coming from the database, which will be returned as `bytes`:
 
 .. code:: python
 
-    conn.client_encoding = "ascii"
+    conn.execute("SET client_encoding TO SQL_ASCII")
     conn.execute("SELECT entry FROM menu WHERE id = 1").fetchone()[0]
     b'Cr\xc3\xa8me Br\xc3\xbbl\xc3\xa9e at 4.99\xe2\x82\xac'
 
index 69d7c8dcc7f4a475922feae44ae1cb3f3547a11d..60773f9de86d713ac4f73efa5ed4d985b6f4147c 100644 (file)
@@ -8,6 +8,7 @@ from typing import Any, NamedTuple, Optional, Sequence, TYPE_CHECKING
 from operator import attrgetter
 
 from . import errors as e
+from ._encodings import pgconn_encoding
 
 if TYPE_CHECKING:
     from .cursor import BaseCursor
@@ -31,7 +32,7 @@ class Column(Sequence[Any]):
         if not fname:
             raise e.InterfaceError(f"no name available for column {index}")
 
-        self._name = fname.decode(cursor.connection.client_encoding)
+        self._name = fname.decode(pgconn_encoding(cursor._pgconn))
 
         self._data = ColumnData(
             ftype=res.ftype(index),
index 5d92ed4d4aaa2231580906a35010dd682f1b10fd..705e1cadfa5248cd92fd59af1297e930264e7bbb 100644 (file)
@@ -5,10 +5,13 @@ Mappings between PostgreSQL and Python encodings.
 # Copyright (C) 2020-2021 The Psycopg Team
 
 import codecs
-from typing import Dict, Union
+from typing import Dict, Union, TYPE_CHECKING
 
 from .errors import NotSupportedError
 
+if TYPE_CHECKING:
+    from .pq.abc import PGconn
+
 _py_codecs = {
     "BIG5": "big5",
     "EUC_CN": "gb2312",
@@ -63,6 +66,11 @@ py_codecs.update((k.encode(), v) for k, v in _py_codecs.items())
 pg_codecs = {v: k.encode() for k, v in _py_codecs.items()}
 
 
+def pgconn_encoding(pgconn: "PGconn") -> str:
+    pgenc = pgconn.parameter_status(b"client_encoding") or b"UTF8"
+    return pg2pyenc(pgenc)
+
+
 def py2pgenc(name: str) -> bytes:
     """Convert a Python encoding name to PostgreSQL encoding name.
 
index 2c1f72dcc043b23404c7d890091704a2aa0fcb8a..a974d599cfc6422e78e745974116725e95de51f7 100644 (file)
@@ -14,6 +14,7 @@ from . import errors as e
 from .sql import Composable
 from .abc import Buffer, Query, Params
 from ._enums import PyFormat
+from ._encodings import pgconn_encoding
 
 if TYPE_CHECKING:
     from .abc import Transformer
@@ -53,7 +54,7 @@ class PostgresQuery:
 
         conn = transformer.connection
         if conn:
-            self._encoding = conn.client_encoding
+            self._encoding = pgconn_encoding(conn.pgconn)
 
     def convert(self, query: Query, vars: Optional[Params]) -> None:
         """
index 8e89250fca58c4f41e9c3a6e5ee6f227809e3761..b24d5e3076e7b7435815e522d1e6f0c25f95b899 100644 (file)
@@ -30,7 +30,7 @@ from .cursor import Cursor
 from ._cmodule import _psycopg
 from .conninfo import make_conninfo, conninfo_to_dict, ConnectionInfo
 from .generators import notifies
-from ._encodings import pg2pyenc
+from ._encodings import pgconn_encoding
 from ._preparing import PrepareManager
 from .transaction import Transaction
 from .server_cursor import ServerCursor
@@ -259,12 +259,6 @@ class BaseConnection(Generic[Row]):
                     f"{TransactionStatus(status).name}"
                 )
 
-    @property
-    def client_encoding(self) -> str:
-        """The Python codec name of the connection's client encoding."""
-        pgenc = self.pgconn.parameter_status(b"client_encoding") or b"UTF8"
-        return pg2pyenc(pgenc)
-
     @property
     def info(self) -> ConnectionInfo:
         """A `ConnectionInfo` attribute to inspect connection properties."""
@@ -313,7 +307,7 @@ class BaseConnection(Generic[Row]):
         if not (self and self._notice_handler):
             return
 
-        diag = e.Diagnostic(res, self.client_encoding)
+        diag = e.Diagnostic(res, pgconn_encoding(self.pgconn))
         for cb in self._notice_handlers:
             try:
                 cb(diag)
@@ -342,7 +336,7 @@ class BaseConnection(Generic[Row]):
         if not (self and self._notify_handlers):
             return
 
-        enc = self.client_encoding
+        enc = pgconn_encoding(self.pgconn)
         n = Notify(pgn.relname.decode(enc), pgn.extra.decode(enc), pgn.be_pid)
         for cb in self._notify_handlers:
             cb(n)
@@ -419,7 +413,7 @@ class BaseConnection(Generic[Row]):
             )
 
         if isinstance(command, str):
-            command = command.encode(self.client_encoding)
+            command = command.encode(pgconn_encoding(self.pgconn))
         elif isinstance(command, Composable):
             command = command.as_bytes(self)
 
@@ -434,7 +428,7 @@ class BaseConnection(Generic[Row]):
         if result.status not in (ExecStatus.COMMAND_OK, ExecStatus.TUPLES_OK):
             if result.status == ExecStatus.FATAL_ERROR:
                 raise e.error_from_result(
-                    result, encoding=self.client_encoding
+                    result, encoding=pgconn_encoding(self.pgconn)
                 )
             else:
                 raise e.InterfaceError(
@@ -754,7 +748,7 @@ class Connection(BaseConnection[Row]):
         while 1:
             with self.lock:
                 ns = self.wait(notifies(self.pgconn))
-            enc = self.client_encoding
+            enc = pgconn_encoding(self.pgconn)
             for pgn in ns:
                 n = Notify(
                     pgn.relname.decode(enc), pgn.extra.decode(enc), pgn.be_pid
index 7f6b3a3af3a15eb2fc4772b113a4bed7c6a0515e..015adf8eef4e3a936694b239684241d44adda2f1 100644 (file)
@@ -20,6 +20,7 @@ from .adapt import AdaptersMap
 from ._enums import IsolationLevel
 from ._compat import asynccontextmanager
 from .conninfo import make_conninfo, conninfo_to_dict
+from ._encodings import pgconn_encoding
 from .connection import BaseConnection, CursorRow, Notify
 from .generators import notifies
 from .transaction import AsyncTransaction
@@ -268,7 +269,7 @@ class AsyncConnection(BaseConnection[Row]):
         while 1:
             async with self.lock:
                 ns = await self.wait(notifies(self.pgconn))
-            enc = self.client_encoding
+            enc = pgconn_encoding(self.pgconn)
             for pgn in ns:
                 n = Notify(
                     pgn.relname.decode(enc), pgn.extra.decode(enc), pgn.be_pid
index af924c7ef20ece2881c658927b961fffc5e5647b..129b054559ff40b3d88a252057707e8b151cf947 100644 (file)
@@ -12,7 +12,7 @@ from datetime import tzinfo
 from . import pq
 from . import errors as e
 from ._tz import get_tzinfo
-from ._encodings import pg2pyenc
+from ._encodings import pgconn_encoding
 
 
 def make_conninfo(conninfo: str = "", **kwargs: Any) -> str:
@@ -171,7 +171,7 @@ class ConnectionInfo:
         `~Connection.connect()` or from environment variables. The password
         is never returned (you can read it using the `password` attribute).
         """
-        pyenc = self._pyenc
+        pyenc = self.encoding
 
         # Get the known defaults to avoid reporting them
         defaults = {
@@ -222,8 +222,8 @@ class ConnectionInfo:
 
         Return `None` is the parameter is unknown.
         """
-        res = self.pgconn.parameter_status(param_name.encode(self._pyenc))
-        return res.decode(self._pyenc) if res is not None else None
+        res = self.pgconn.parameter_status(param_name.encode(self.encoding))
+        return res.decode(self.encoding) if res is not None else None
 
     @property
     def server_version(self) -> int:
@@ -259,11 +259,11 @@ class ConnectionInfo:
         """The Python timezone info of the connection's timezone."""
         return get_tzinfo(self.pgconn)
 
+    @property
+    def encoding(self) -> str:
+        """The Python codec name of the connection's client encoding."""
+        return pgconn_encoding(self.pgconn)
+
     def _get_pgconn_attr(self, name: str) -> str:
         value: bytes = getattr(self.pgconn, name)
-        return value.decode(self._pyenc)
-
-    @property
-    def _pyenc(self) -> str:
-        pgenc = self.pgconn.parameter_status(b"client_encoding") or b"UTF8"
-        return pg2pyenc(pgenc)
+        return value.decode(self.encoding)
index b26041a084a69519d05ef13c277a9d1c9005e76d..572b9699d61850d52db9937ac2ad415892a888e7 100644 (file)
@@ -21,6 +21,7 @@ from .abc import ConnectionType, PQGen, Transformer
 from .adapt import PyFormat
 from ._compat import create_task
 from ._cmodule import _psycopg
+from ._encodings import pgconn_encoding
 from .generators import copy_from, copy_to, copy_end
 
 if TYPE_CHECKING:
@@ -66,7 +67,7 @@ class BaseCopy(Generic[ConnectionType]):
 
         if self._pgresult.binary_tuples == pq.Format.TEXT:
             self.formatter = TextFormatter(
-                tx, encoding=self.connection.client_encoding
+                tx, encoding=pgconn_encoding(self._pgconn)
             )
         else:
             self.formatter = BinaryFormatter(tx)
@@ -149,7 +150,7 @@ class BaseCopy(Generic[ConnectionType]):
         bmsg: Optional[bytes]
         if exc:
             msg = f"error from Python: {type(exc).__qualname__} - {exc}"
-            bmsg = msg.encode(self.connection.client_encoding, "replace")
+            bmsg = msg.encode(pgconn_encoding(self._pgconn), "replace")
         else:
             bmsg = None
 
index 20e35cbe8bc9d7c2d3d80a44d1b960bda68b806f..58deeaba3de23428d0885fac648ea1a29b87ec59 100644 (file)
@@ -22,6 +22,7 @@ from .rows import Row, RowMaker, RowFactory
 from ._column import Column
 from ._cmodule import _psycopg
 from ._queries import PostgresQuery
+from ._encodings import pgconn_encoding
 from ._preparing import Prepare
 
 if TYPE_CHECKING:
@@ -53,6 +54,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
 
     _tx: "Transformer"
     _make_row: RowMaker[Row]
+    _pgconn: "PGconn"
 
     def __init__(self, connection: ConnectionType):
         self._conn = connection
@@ -238,7 +240,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
             (result,) = yield from execute(self._pgconn)
             if result.status == ExecStatus.FATAL_ERROR:
                 raise e.error_from_result(
-                    result, encoding=self._conn.client_encoding
+                    result, encoding=pgconn_encoding(self._pgconn)
                 )
             self._send_query_prepared(name, pgq, binary=binary)
 
@@ -412,7 +414,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
         badstats = statuses.difference(self._status_ok)
         if results[-1].status == ExecStatus.FATAL_ERROR:
             raise e.error_from_result(
-                results[-1], encoding=self._conn.client_encoding
+                results[-1], encoding=pgconn_encoding(self._pgconn)
             )
         elif statuses.intersection(self._status_copy):
             raise e.ProgrammingError(
@@ -457,7 +459,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
             return
         elif status == ExecStatus.FATAL_ERROR:
             raise e.error_from_result(
-                result, encoding=self._conn.client_encoding
+                result, encoding=pgconn_encoding(self._pgconn)
             )
         else:
             raise e.ProgrammingError(
index d74ee321a0832a3d87454385caacbab9e1a63008..1485b43ffd18c828e55751ac2f713677089a54a6 100644 (file)
@@ -15,6 +15,7 @@ from .abc import ConnectionType, Query, Params, PQGen
 from .rows import Row, RowFactory, AsyncRowFactory
 from .cursor import AnyCursor, BaseCursor, Cursor, execute
 from .cursor_async import AsyncCursor
+from ._encodings import pgconn_encoding
 
 if TYPE_CHECKING:
     from .connection import Connection
@@ -83,7 +84,7 @@ class ServerCursorHelper(Generic[ConnectionType, Row]):
     ) -> PQGen[None]:
         conn = cur._conn
         conn.pgconn.send_describe_portal(
-            self.name.encode(conn.client_encoding)
+            self.name.encode(pgconn_encoding(conn.pgconn))
         )
         results = yield from execute(conn.pgconn)
         cur._execute_results(results, format=self.format)
@@ -158,7 +159,7 @@ class ServerCursorHelper(Generic[ConnectionType, Row]):
     ) -> sql.Composable:
 
         if isinstance(query, bytes):
-            query = query.decode(cur._conn.client_encoding)
+            query = query.decode(pgconn_encoding(cur._conn.pgconn))
         if not isinstance(query, sql.Composable):
             query = sql.SQL(query)
 
index fa2e3b1a24cd952cc05ed6fff0423ce966e4e1b1..f715aab0451804e3126d9a43d6e5f34e22e7a392 100644 (file)
@@ -12,6 +12,7 @@ from typing import Any, Iterator, List, Optional, Sequence, Union
 from .pq import Escaping
 from .abc import AdaptContext
 from .adapt import Transformer, PyFormat
+from ._encodings import pgconn_encoding
 
 
 def quote(obj: Any, context: Optional[AdaptContext] = None) -> str:
@@ -75,7 +76,7 @@ class Composable(ABC):
 
         """
         conn = context.connection if context else None
-        enc = conn.client_encoding if conn else "utf-8"
+        enc = pgconn_encoding(conn.pgconn) if conn else "utf-8"
         b = self.as_bytes(context)
         if isinstance(b, bytes):
             return b.decode(enc)
@@ -207,7 +208,7 @@ class SQL(Composable):
         if context:
             conn = context.connection
             if conn:
-                enc = conn.client_encoding
+                enc = pgconn_encoding(conn.pgconn)
         return self._obj.encode(enc)
 
     def format(self, *args: Any, **kwargs: Any) -> Composed:
@@ -366,7 +367,7 @@ class Identifier(Composable):
         if not conn:
             raise ValueError("a connection is necessary for Identifier")
         esc = Escaping(conn.pgconn)
-        enc = conn.client_encoding
+        enc = pgconn_encoding(conn.pgconn)
         escs = [esc.escape_identifier(s.encode(enc)) for s in self._obj]
         return b".".join(escs)
 
@@ -451,7 +452,7 @@ class Placeholder(Composable):
 
     def as_bytes(self, context: Optional[AdaptContext]) -> bytes:
         conn = context.connection if context else None
-        enc = conn.client_encoding if conn else "utf-8"
+        enc = pgconn_encoding(conn.pgconn) if conn else "utf-8"
         return self.as_string(context).encode(enc)
 
 
index 9f8deb079b9ed4da68f0ee9aab70f853e5f2271f..7dcc98baf69578b137daa549c604571883fd83f3 100644 (file)
@@ -11,6 +11,7 @@ from ..pq import Format, Escaping
 from ..abc import AdaptContext
 from ..adapt import Buffer, Dumper, Loader
 from ..errors import DataError
+from .._encodings import pgconn_encoding
 
 if TYPE_CHECKING:
     from ..pq.abc import Escaping as EscapingProto
@@ -25,7 +26,7 @@ class _BaseStrDumper(Dumper):
 
         conn = self.connection
         if conn:
-            enc = conn.client_encoding
+            enc = pgconn_encoding(conn.pgconn)
             if enc != "ascii":
                 self._encoding = enc
 
@@ -85,7 +86,7 @@ class TextLoader(Loader):
         super().__init__(oid, context)
         conn = self.connection
         if conn:
-            enc = conn.client_encoding
+            enc = pgconn_encoding(conn.pgconn)
             self._encoding = enc if enc != "ascii" else ""
 
     def load(self, data: Buffer) -> Union[bytes, str]:
index 3cba37b5905c758fa47a7c4fc22d890c4ecd86d1..c63e283dd3c3732843f3774b6c56f6928a499edf 100644 (file)
@@ -112,7 +112,7 @@ def patch_exec(conn, monkeypatch):
     def _exec_command(command, *args, **kwargs):
         cmdcopy = command
         if isinstance(cmdcopy, bytes):
-            cmdcopy = cmdcopy.decode(conn.client_encoding)
+            cmdcopy = cmdcopy.decode(conn.info.encoding)
         elif isinstance(cmdcopy, sql.Composable):
             cmdcopy = cmdcopy.as_string(conn)
 
index f6324d8e22b378ae1b70940a9b0a4eab5444e08b..b75866e915a20f8c42eda5f750bff87f330f39e3 100644 (file)
@@ -7,7 +7,6 @@ import weakref
 from threading import Thread
 
 import psycopg
-from psycopg._encodings import pg2pyenc
 from psycopg import Connection, Notify
 from psycopg.rows import tuple_row
 from psycopg.errors import UndefinedTable
@@ -299,52 +298,6 @@ def test_autocommit_unknown(conn):
     assert not conn.autocommit
 
 
-def test_get_encoding(conn):
-    (enc,) = conn.cursor().execute("show client_encoding").fetchone()
-    assert conn.client_encoding == pg2pyenc(enc)
-
-
-@pytest.mark.parametrize(
-    "enc, out, codec",
-    [
-        ("utf8", "UTF8", "utf-8"),
-        ("utf-8", "UTF8", "utf-8"),
-        ("utf_8", "UTF8", "utf-8"),
-        ("eucjp", "EUC_JP", "euc_jp"),
-        ("euc-jp", "EUC_JP", "euc_jp"),
-        ("latin9", "LATIN9", "iso8859-15"),
-    ],
-)
-def test_normalize_encoding(conn, enc, out, codec):
-    conn.execute("select set_config('client_encoding', %s, false)", [enc])
-    assert conn.pgconn.parameter_status(b"client_encoding").decode() == out
-    assert conn.client_encoding == codec
-
-
-@pytest.mark.parametrize(
-    "enc, out, codec",
-    [
-        ("utf8", "UTF8", "utf-8"),
-        ("utf-8", "UTF8", "utf-8"),
-        ("utf_8", "UTF8", "utf-8"),
-        ("eucjp", "EUC_JP", "euc_jp"),
-        ("euc-jp", "EUC_JP", "euc_jp"),
-    ],
-)
-def test_encoding_env_var(dsn, monkeypatch, enc, out, codec):
-    monkeypatch.setenv("PGCLIENTENCODING", enc)
-    conn = psycopg.connect(dsn)
-    assert conn.pgconn.parameter_status(b"client_encoding").decode() == out
-    assert conn.client_encoding == codec
-
-
-def test_set_encoding_unsupported(conn):
-    cur = conn.cursor()
-    cur.execute("set client_encoding to EUC_TW")
-    with pytest.raises(psycopg.NotSupportedError):
-        cur.execute("select 'x'")
-
-
 @pytest.mark.parametrize(
     "args, kwargs, want",
     [
index 4ae40fe63bbdefd30c3e48f8ed968b8e5357e54f..64b30a928f9ad4da2e1f4924df3fcb14ba3a58d4 100644 (file)
@@ -10,7 +10,6 @@ from psycopg import AsyncConnection, Notify
 from psycopg.rows import tuple_row
 from psycopg.errors import UndefinedTable
 from psycopg.conninfo import conninfo_to_dict, make_conninfo
-from psycopg._encodings import pg2pyenc
 
 from .utils import gc_collect
 from .test_cursor import my_row_factory
@@ -310,56 +309,6 @@ async def test_autocommit_unknown(aconn):
     assert not aconn.autocommit
 
 
-async def test_get_encoding(aconn):
-    cur = aconn.cursor()
-    await cur.execute("show client_encoding")
-    (enc,) = await cur.fetchone()
-    assert aconn.client_encoding == pg2pyenc(enc)
-
-
-@pytest.mark.parametrize(
-    "enc, out, codec",
-    [
-        ("utf8", "UTF8", "utf-8"),
-        ("utf-8", "UTF8", "utf-8"),
-        ("utf_8", "UTF8", "utf-8"),
-        ("eucjp", "EUC_JP", "euc_jp"),
-        ("euc-jp", "EUC_JP", "euc_jp"),
-        ("latin9", "LATIN9", "iso8859-15"),
-    ],
-)
-async def test_normalize_encoding(aconn, enc, out, codec):
-    await aconn.execute(
-        "select set_config('client_encoding', %s, false)", [enc]
-    )
-    assert aconn.pgconn.parameter_status(b"client_encoding").decode() == out
-    assert aconn.client_encoding == codec
-
-
-@pytest.mark.parametrize(
-    "enc, out, codec",
-    [
-        ("utf8", "UTF8", "utf-8"),
-        ("utf-8", "UTF8", "utf-8"),
-        ("utf_8", "UTF8", "utf-8"),
-        ("eucjp", "EUC_JP", "euc_jp"),
-        ("euc-jp", "EUC_JP", "euc_jp"),
-    ],
-)
-async def test_encoding_env_var(dsn, monkeypatch, enc, out, codec):
-    monkeypatch.setenv("PGCLIENTENCODING", enc)
-    aconn = await psycopg.AsyncConnection.connect(dsn)
-    assert aconn.pgconn.parameter_status(b"client_encoding").decode() == out
-    assert aconn.client_encoding == codec
-
-
-async def test_set_encoding_unsupported(aconn):
-    cur = aconn.cursor()
-    await cur.execute("set client_encoding to EUC_TW")
-    with pytest.raises(psycopg.NotSupportedError):
-        await cur.execute("select 'x'")
-
-
 @pytest.mark.parametrize(
     "args, kwargs, want",
     [
index 8410aad2f322eb3431a30894d702ef18ee5a47a0..c210a3b375a264d06f59d3f9884e9bca55e550c3 100644 (file)
@@ -6,6 +6,7 @@ import pytest
 import psycopg
 from psycopg import ProgrammingError
 from psycopg.conninfo import make_conninfo, conninfo_to_dict, ConnectionInfo
+from psycopg._encodings import pg2pyenc
 
 snowman = "\u2603"
 
@@ -245,3 +246,45 @@ class TestConnectionInfo:
         conn.info.timezone
         assert len(caplog.records) == 2
         assert "FOOBAAR0" in caplog.records[1].message
+
+    def test_encoding(self, conn):
+        enc = conn.execute("show client_encoding").fetchone()[0]
+        assert conn.info.encoding == pg2pyenc(enc)
+
+    @pytest.mark.parametrize(
+        "enc, out, codec",
+        [
+            ("utf8", "UTF8", "utf-8"),
+            ("utf-8", "UTF8", "utf-8"),
+            ("utf_8", "UTF8", "utf-8"),
+            ("eucjp", "EUC_JP", "euc_jp"),
+            ("euc-jp", "EUC_JP", "euc_jp"),
+            ("latin9", "LATIN9", "iso8859-15"),
+        ],
+    )
+    def test_normalize_encoding(self, conn, enc, out, codec):
+        conn.execute("select set_config('client_encoding', %s, false)", [enc])
+        assert conn.info.parameter_status("client_encoding") == out
+        assert conn.info.encoding == codec
+
+    @pytest.mark.parametrize(
+        "enc, out, codec",
+        [
+            ("utf8", "UTF8", "utf-8"),
+            ("utf-8", "UTF8", "utf-8"),
+            ("utf_8", "UTF8", "utf-8"),
+            ("eucjp", "EUC_JP", "euc_jp"),
+            ("euc-jp", "EUC_JP", "euc_jp"),
+        ],
+    )
+    def test_encoding_env_var(self, dsn, monkeypatch, enc, out, codec):
+        monkeypatch.setenv("PGCLIENTENCODING", enc)
+        conn = psycopg.connect(dsn)
+        assert conn.info.parameter_status("client_encoding") == out
+        assert conn.info.encoding == codec
+
+    def test_set_encoding_unsupported(self, conn):
+        cur = conn.cursor()
+        cur.execute("set client_encoding to EUC_TW")
+        with pytest.raises(psycopg.NotSupportedError):
+            cur.execute("select 'x'")