Skip to content

Commit 405398b

Browse files
committed
Sort the reqs, add keyring.alt.
1 parent dba4f63 commit 405398b

File tree

9 files changed

+60
-94
lines changed

9 files changed

+60
-94
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,5 @@ target/
7272
venv/
7373

7474
.ropeproject/
75+
uv.lock
7576

CONTRIBUTING.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,6 @@ in the ``tests`` directory. An example::
158158
First, install the requirements for testing:
159159

160160
::
161-
$ uv pip install ".[sshtunnel]"
162161
$ uv pip install ".[dev]"
163162

164163
Ensure that the database user has permissions to create and drop test databases

pgcli/packages/sqlcompletion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,10 +530,10 @@ def _suggest_expression(token_v, stmt):
530530
)
531531

532532

533-
def identifies(id, ref):
533+
def identifies(table_id, ref):
534534
"""Returns true if string `id` matches TableReference `ref`"""
535535

536-
return id == ref.alias or id == ref.name or (ref.schema and (id == ref.schema + "." + ref.name))
536+
return table_id == ref.alias or table_id == ref.name or (ref.schema and (table_id == ref.schema + "." + ref.name))
537537

538538

539539
def _allow_join_condition(statement):

pgcli/pgbuffer.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ def _is_complete(sql):
2525
def safe_multi_line_mode(pgcli):
2626
@Condition
2727
def cond():
28-
_logger.debug(
29-
'Multi-line mode state: "%s" / "%s"', pgcli.multi_line, pgcli.multiline_mode
30-
)
28+
_logger.debug('Multi-line mode state: "%s" / "%s"', pgcli.multi_line, pgcli.multiline_mode)
3129
return pgcli.multi_line and (pgcli.multiline_mode == "safe")
3230

3331
return cond
@@ -48,7 +46,13 @@ def cond():
4846
text = doc.text.strip()
4947

5048
return (
51-
text.startswith("\\") or text.endswith((r"\e", r"\G")) or _is_complete(text) or text == "exit" or text == "quit" or text == ":q" or text == "" # Just a plain enter without any text
49+
text.startswith("\\")
50+
or text.endswith((r"\e", r"\G"))
51+
or _is_complete(text)
52+
or text == "exit"
53+
or text == "quit"
54+
or text == ":q"
55+
or text == "" # Just a plain enter without any text
5256
)
5357

5458
return cond

pgcli/pgcompleter.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def Candidate(completion, prio=None, meta=None, synonyms=None, prio2=None, displ
5454
# Used to strip trailing '::some_type' from default-value expressions
5555
arg_default_type_strip_regex = re.compile(r"::[\w\.]+(\[\])?$")
5656

57+
5758
def normalize_ref(ref):
5859
return ref if ref[0] == '"' else '"' + ref.lower() + '"'
5960

@@ -493,8 +494,10 @@ def get_column_matches(self, suggestion, word_before_cursor):
493494
"if_more_than_one_table": len(tables) > 1,
494495
}[self.qualify_columns]
495496
)
497+
496498
def qualify(col, tbl):
497-
return ((tbl + "." + self.case(col)) if do_qualify else self.case(col))
499+
return (tbl + "." + self.case(col)) if do_qualify else self.case(col)
500+
498501
_logger.debug("Completion column scope: %r", tables)
499502
scoped_cols = self.populate_scoped_cols(tables, suggestion.local_tables)
500503

@@ -515,12 +518,12 @@ def flat_cols():
515518
if lastword == "*":
516519
if suggestion.context == "insert":
517520

518-
def filter(col):
521+
def _filter(col):
519522
if not col.has_default:
520523
return True
521524
return not any(p.match(col.default) for p in self.insert_col_skip_patterns)
522525

523-
scoped_cols = {t: [col for col in cols if filter(col)] for t, cols in scoped_cols.items()}
526+
scoped_cols = {t: [col for col in cols if _filter(col)] for t, cols in scoped_cols.items()}
524527
if self.asterisk_column_order == "alphabetic":
525528
for cols in scoped_cols.values():
526529
cols.sort(key=operator.attrgetter("name"))

pyproject.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,18 @@ pgcli = "pgcli.main:cli"
5252
keyring = ["keyring >= 12.2.0"]
5353
sshtunnel = ["sshtunnel >= 0.4.0"]
5454
dev = [
55-
"pytest>=2.7.0",
56-
"tox>=1.9.2",
55+
# "build<0.10.0",
5756
"behave>=1.2.4",
58-
"pexpect==3.3; platform_system != 'Windows'",
59-
"pre-commit>=1.16.0",
6057
"coverage>=5.0.4",
6158
"codecov>=1.5.1",
6259
"docutils>=0.13.1",
60+
"keyrings.alt>=3.1",
61+
"pexpect==3.3; platform_system != 'Windows'",
62+
"pre-commit>=1.16.0",
63+
"pytest>=2.7.0",
6364
"ruff>=0.11.7",
6465
"sshtunnel>=0.4.0",
65-
"build<0.10.0",
66+
"tox>=1.9.2",
6667
]
6768

6869
[build-system]
@@ -107,9 +108,14 @@ ignore = [
107108
'E114', # indentation-with-invalid-multiple-comment
108109
'E117', # over-indented
109110
'W191', # tab-indentation
111+
'E741', # ambiguous-variable-name
110112
# TODO
111113
'PIE796', # todo enableme Enum contains duplicate value
112114
]
115+
exclude = [
116+
'pgcli/magic.py',
117+
'pgcli/pyev.py',
118+
]
113119

114120
[tool.ruff.lint.isort]
115121
force-sort-within-sections = true
@@ -124,7 +130,6 @@ preview = true
124130
quote-style = 'preserve'
125131
exclude = [
126132
'build',
127-
'pgcli/magic.py',
128133
]
129134

130135
[tool.pytest.ini_options]

tests/features/db_utils.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
from psycopg import connect
22

33

4-
def create_db(
5-
hostname="localhost", username=None, password=None, dbname=None, port=None
6-
):
4+
def create_db(hostname="localhost", username=None, password=None, dbname=None, port=None):
75
"""Create test database.
86
97
:param hostname: string
@@ -36,9 +34,7 @@ def create_cn(hostname, password, username, dbname, port):
3634
:param dbname: string
3735
:return: psycopg2.connection
3836
"""
39-
cn = connect(
40-
host=hostname, user=username, dbname=dbname, password=password, port=port
41-
)
37+
cn = connect(host=hostname, user=username, dbname=dbname, password=password, port=port)
4238

4339
print(f"Created connection: {cn.info.get_parameters()}.")
4440
return cn
@@ -49,7 +45,7 @@ def pgbouncer_available(hostname="localhost", password=None, username="postgres"
4945
try:
5046
cn = create_cn(hostname, password, username, "pgbouncer", 6432)
5147
return True
52-
except:
48+
except Exception:
5349
print("Pgbouncer is not available.")
5450
finally:
5551
if cn:

tests/test_main.py

Lines changed: 28 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,7 @@ def test_obfuscate_process_password():
6161

6262
def test_format_output():
6363
settings = OutputSettings(table_format="psql", dcmlfmt="d", floatfmt="g")
64-
results = format_output(
65-
"Title", [("abc", "def")], ["head1", "head2"], "test status", settings
66-
)
64+
results = format_output("Title", [("abc", "def")], ["head1", "head2"], "test status", settings)
6765
expected = [
6866
"Title",
6967
"+-------+-------+",
@@ -128,9 +126,7 @@ def test_no_column_date_formats():
128126

129127

130128
def test_format_output_truncate_on():
131-
settings = OutputSettings(
132-
table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=10
133-
)
129+
settings = OutputSettings(table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=10)
134130
results = format_output(
135131
None,
136132
[("first field value", "second field value")],
@@ -149,9 +145,7 @@ def test_format_output_truncate_on():
149145

150146

151147
def test_format_output_truncate_off():
152-
settings = OutputSettings(
153-
table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=None
154-
)
148+
settings = OutputSettings(table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=None)
155149
long_field_value = ("first field " * 100).strip()
156150
results = format_output(None, [(long_field_value,)], ["head1"], None, settings)
157151
lines = list(results)
@@ -207,12 +201,8 @@ def test_format_array_output_expanded(executor):
207201

208202

209203
def test_format_output_auto_expand():
210-
settings = OutputSettings(
211-
table_format="psql", dcmlfmt="d", floatfmt="g", max_width=100
212-
)
213-
table_results = format_output(
214-
"Title", [("abc", "def")], ["head1", "head2"], "test status", settings
215-
)
204+
settings = OutputSettings(table_format="psql", dcmlfmt="d", floatfmt="g", max_width=100)
205+
table_results = format_output("Title", [("abc", "def")], ["head1", "head2"], "test status", settings)
216206
table = [
217207
"Title",
218208
"+-------+-------+",
@@ -269,18 +259,18 @@ def test_format_output_auto_expand():
269259
def pset_pager_mocks():
270260
cli = PGCli()
271261
cli.watch_command = None
272-
with mock.patch("pgcli.main.click.echo") as mock_echo, mock.patch(
273-
"pgcli.main.click.echo_via_pager"
274-
) as mock_echo_via_pager, mock.patch.object(cli, "prompt_app") as mock_app:
262+
with (
263+
mock.patch("pgcli.main.click.echo") as mock_echo,
264+
mock.patch("pgcli.main.click.echo_via_pager") as mock_echo_via_pager,
265+
mock.patch.object(cli, "prompt_app") as mock_app,
266+
):
275267
yield cli, mock_echo, mock_echo_via_pager, mock_app
276268

277269

278270
@pytest.mark.parametrize("term_height,term_width,text", test_data, ids=test_ids)
279271
def test_pset_pager_off(term_height, term_width, text, pset_pager_mocks):
280272
cli, mock_echo, mock_echo_via_pager, mock_cli = pset_pager_mocks
281-
mock_cli.output.get_size.return_value = termsize(
282-
rows=term_height, columns=term_width
283-
)
273+
mock_cli.output.get_size.return_value = termsize(rows=term_height, columns=term_width)
284274

285275
with mock.patch.object(cli.pgspecial, "pager_config", PAGER_OFF):
286276
cli.echo_via_pager(text)
@@ -292,9 +282,7 @@ def test_pset_pager_off(term_height, term_width, text, pset_pager_mocks):
292282
@pytest.mark.parametrize("term_height,term_width,text", test_data, ids=test_ids)
293283
def test_pset_pager_always(term_height, term_width, text, pset_pager_mocks):
294284
cli, mock_echo, mock_echo_via_pager, mock_cli = pset_pager_mocks
295-
mock_cli.output.get_size.return_value = termsize(
296-
rows=term_height, columns=term_width
297-
)
285+
mock_cli.output.get_size.return_value = termsize(rows=term_height, columns=term_width)
298286

299287
with mock.patch.object(cli.pgspecial, "pager_config", PAGER_ALWAYS):
300288
cli.echo_via_pager(text)
@@ -306,14 +294,10 @@ def test_pset_pager_always(term_height, term_width, text, pset_pager_mocks):
306294
pager_on_test_data = [l + (r,) for l, r in zip(test_data, use_pager_when_on)]
307295

308296

309-
@pytest.mark.parametrize(
310-
"term_height,term_width,text,use_pager", pager_on_test_data, ids=test_ids
311-
)
297+
@pytest.mark.parametrize("term_height,term_width,text,use_pager", pager_on_test_data, ids=test_ids)
312298
def test_pset_pager_on(term_height, term_width, text, use_pager, pset_pager_mocks):
313299
cli, mock_echo, mock_echo_via_pager, mock_cli = pset_pager_mocks
314-
mock_cli.output.get_size.return_value = termsize(
315-
rows=term_height, columns=term_width
316-
)
300+
mock_cli.output.get_size.return_value = termsize(rows=term_height, columns=term_width)
317301

318302
with mock.patch.object(cli.pgspecial, "pager_config", PAGER_LONG_OUTPUT):
319303
cli.echo_via_pager(text)
@@ -330,7 +314,7 @@ def test_pset_pager_on(term_height, term_width, text, use_pager, pset_pager_mock
330314
"text,expected_length",
331315
[
332316
(
333-
"22200K .......\u001b[0m\u001b[91m... .......... ...\u001b[0m\u001b[91m.\u001b[0m\u001b[91m...... .........\u001b[0m\u001b[91m.\u001b[0m\u001b[91m \u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m...... 50% 28.6K 12m55s",
317+
"22200K .......\u001b[0m\u001b[91m... .......... ...\u001b[0m\u001b[91m.\u001b[0m\u001b[91m...... .........\u001b[0m\u001b[91m.\u001b[0m\u001b[91m \u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m.\u001b[0m\u001b[91m...... 50% 28.6K 12m55s", # noqa: E501
334318
78,
335319
),
336320
("=\u001b[m=", 2),
@@ -405,34 +389,24 @@ def test_logfile_unwriteable_file(executor):
405389
cli = PGCli(pgexecute=executor)
406390
statement = r"\log-file forbidden.log"
407391
with mock.patch("builtins.open") as mock_open:
408-
mock_open.side_effect = PermissionError(
409-
"[Errno 13] Permission denied: 'forbidden.log'"
410-
)
392+
mock_open.side_effect = PermissionError("[Errno 13] Permission denied: 'forbidden.log'")
411393
result = run(executor, statement, pgspecial=cli.pgspecial)
412-
assert result == [
413-
"[Errno 13] Permission denied: 'forbidden.log'\nLogfile capture disabled"
414-
]
394+
assert result == ["[Errno 13] Permission denied: 'forbidden.log'\nLogfile capture disabled"]
415395

416396

417397
@dbtest
418398
def test_watch_works(executor):
419399
cli = PGCli(pgexecute=executor)
420400

421-
def run_with_watch(
422-
query, target_call_count=1, expected_output="", expected_timing=None
423-
):
401+
def run_with_watch(query, target_call_count=1, expected_output="", expected_timing=None):
424402
"""
425403
:param query: Input to the CLI
426404
:param target_call_count: Number of times the user lets the command run before Ctrl-C
427405
:param expected_output: Substring expected to be found for each executed query
428406
:param expected_timing: value `time.sleep` expected to be called with on every invocation
429407
"""
430-
with mock.patch.object(cli, "echo_via_pager") as mock_echo, mock.patch(
431-
"pgcli.main.sleep"
432-
) as mock_sleep:
433-
mock_sleep.side_effect = [None] * (target_call_count - 1) + [
434-
KeyboardInterrupt
435-
]
408+
with mock.patch.object(cli, "echo_via_pager") as mock_echo, mock.patch("pgcli.main.sleep") as mock_sleep:
409+
mock_sleep.side_effect = [None] * (target_call_count - 1) + [KeyboardInterrupt]
436410
cli.handle_watch_command(query)
437411
# Validate that sleep was called with the right timing
438412
for i in range(target_call_count - 1):
@@ -446,16 +420,11 @@ def run_with_watch(
446420
with mock.patch("pgcli.main.click.secho") as mock_secho:
447421
cli.handle_watch_command(r"\watch 2")
448422
mock_secho.assert_called()
449-
assert (
450-
r"\watch cannot be used with an empty query"
451-
in mock_secho.call_args_list[0][0][0]
452-
)
423+
assert r"\watch cannot be used with an empty query" in mock_secho.call_args_list[0][0][0]
453424

454425
# Usage 1: Run a query and then re-run it with \watch across two prompts.
455426
run_with_watch("SELECT 111", expected_output="111")
456-
run_with_watch(
457-
"\\watch 10", target_call_count=2, expected_output="111", expected_timing=10
458-
)
427+
run_with_watch("\\watch 10", target_call_count=2, expected_output="111", expected_timing=10)
459428

460429
# Usage 2: Run a query and \watch via the same prompt.
461430
run_with_watch(
@@ -466,9 +435,7 @@ def run_with_watch(
466435
)
467436

468437
# Usage 3: Re-run the last watched command with a new timing
469-
run_with_watch(
470-
"\\watch 5", target_call_count=4, expected_output="222", expected_timing=5
471-
)
438+
run_with_watch("\\watch 5", target_call_count=4, expected_output="222", expected_timing=5)
472439

473440

474441
def test_missing_rc_dir(tmpdir):
@@ -482,9 +449,7 @@ def test_quoted_db_uri(tmpdir):
482449
with mock.patch.object(PGCli, "connect") as mock_connect:
483450
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
484451
cli.connect_uri("postgres://bar%5E:%[email protected]/testdb%5B")
485-
mock_connect.assert_called_with(
486-
database="testdb[", host="baz.com", user="bar^", passwd="]foo"
487-
)
452+
mock_connect.assert_called_with(database="testdb[", host="baz.com", user="bar^", passwd="]foo")
488453

489454

490455
def test_pg_service_file(tmpdir):
@@ -544,8 +509,7 @@ def test_ssl_db_uri(tmpdir):
544509
with mock.patch.object(PGCli, "connect") as mock_connect:
545510
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
546511
cli.connect_uri(
547-
"postgres://bar%5E:%[email protected]/testdb%5B?"
548-
"sslmode=verify-full&sslcert=m%79.pem&sslkey=my-key.pem&sslrootcert=c%61.pem"
512+
"postgres://bar%5E:%[email protected]/testdb%5B?sslmode=verify-full&sslcert=m%79.pem&sslkey=my-key.pem&sslrootcert=c%61.pem"
549513
)
550514
mock_connect.assert_called_with(
551515
database="testdb[",
@@ -563,17 +527,13 @@ def test_port_db_uri(tmpdir):
563527
with mock.patch.object(PGCli, "connect") as mock_connect:
564528
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
565529
cli.connect_uri("postgres://bar:[email protected]:2543/testdb")
566-
mock_connect.assert_called_with(
567-
database="testdb", host="baz.com", user="bar", passwd="foo", port="2543"
568-
)
530+
mock_connect.assert_called_with(database="testdb", host="baz.com", user="bar", passwd="foo", port="2543")
569531

570532

571533
def test_multihost_db_uri(tmpdir):
572534
with mock.patch.object(PGCli, "connect") as mock_connect:
573535
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
574-
cli.connect_uri(
575-
"postgres://bar:[email protected]:2543,baz2.com:2543,baz3.com:2543/testdb"
576-
)
536+
cli.connect_uri("postgres://bar:[email protected]:2543,baz2.com:2543,baz3.com:2543/testdb")
577537
mock_connect.assert_called_with(
578538
database="testdb",
579539
host="baz1.com,baz2.com,baz3.com",
@@ -588,9 +548,7 @@ def test_application_name_db_uri(tmpdir):
588548
mock_pgexecute.return_value = None
589549
cli = PGCli(pgclirc_file=str(tmpdir.join("rcfile")))
590550
cli.connect_uri("postgres://[email protected]/?application_name=cow")
591-
mock_pgexecute.assert_called_with(
592-
"bar", "bar", "", "baz.com", "", "", notify_callback, application_name="cow"
593-
)
551+
mock_pgexecute.assert_called_with("bar", "bar", "", "baz.com", "", "", notify_callback, application_name="cow")
594552

595553

596554
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)