Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ Contributors:
* Diego
* Chris (ChrisJr404)
* Pieter Ouwerkerk (pouwerkerk)
* jackwalkerlabs

Creator:
--------
Expand Down
4 changes: 4 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Features:

Bug fixes:
----------
* [breaking change] Passwords in service files (usually
``~/.pg_service.conf``) are now read as they appear, as ``libpq`` and
``psql`` do. Previously, hashes, percent signs and other values had to be
quoted. They must not be quoted anymore.
* Fix special commands being broken while explain mode (F5) is on. Every input
was prefixed with ``EXPLAIN (...)`` and sent to the server as SQL, including
backslash commands and the bare words ``exit``/``quit``, so ``\q``, ``\d``,
Expand Down
13 changes: 6 additions & 7 deletions pgcli/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from zoneinfo import ZoneInfoNotFoundError
from configobj import ConfigObj, ParseError
from configparser import ConfigParser

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to drop the configobj dependency. pgcli still uses it to parse its main configuration file, but we could probably replace that by configparser as well. There may be incompatibilities/differences between the two, they would have to be listed first (maybe we can ignore them, maybe we cant'). Are you willing to give it a try? (That should be in a separate pull request.)

from pgspecial.namedqueries import NamedQueries
from .config import skip_initial_comment

Expand Down Expand Up @@ -2251,14 +2251,13 @@ def parse_service_info(service):
return None, service_file
with open(service_file, newline="") as f:
skipped_lines = skip_initial_comment(f)
try:
service_file_config = ConfigObj(f)
except ParseError as err:
err.line_number += skipped_lines
raise err
# libpq treats values literally: hashes, commas, quotes and percent
# signs are part of the value.
service_file_config = ConfigParser(interpolation=None, delimiters=("=",), comment_prefixes=("#",))
service_file_config.read_file(itertools.chain(itertools.repeat("\n", skipped_lines), f), source=service_file)
Comment thread
dbaty marked this conversation as resolved.
if service not in service_file_config:
return None, service_file
service_conf = service_file_config.get(service)
service_conf = service_file_config[service]
Comment thread
dbaty marked this conversation as resolved.
return service_conf, service_file


Expand Down
13 changes: 13 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
get_connect_timeout,
get_editor,
notify_callback,
parse_service_info,
PGCli,
OutputSettings,
COLOR_CODE_REGEX,
Expand Down Expand Up @@ -615,6 +616,18 @@ def test_quoted_db_uri(tmpdir):
mock_connect.assert_called_with(database="testdb[", host="baz.com", user="bar^", passwd="]foo")


@pytest.mark.parametrize("password", ["abc#def", "#leading", "abc #def", "abc%def", "a,b", '"quoted"', "'quoted'"])
def test_pg_service_password_is_literal(tmp_path, monkeypatch, password):
service_file = tmp_path / "pg_service.conf"
service_file.write_text(f"# comment\n[myservice]\npassword={password}\n")
monkeypatch.setenv("PGSERVICEFILE", str(service_file))

config, filename = parse_service_info("myservice")

assert filename == str(service_file)
assert config["password"] == password


def test_pg_service_file(tmpdir):
with mock.patch.object(PGCli, "connect") as mock_connect:
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
Expand Down