Skip to content

Commit 9f114c4

Browse files
committed
feat: Replace pendulum by home-made duration-to-words function
`pgcli` uses Pendulum to display the query execution time in words: > select pg_sleep(62) +----------+ | pg_sleep | |----------| | | +----------+ SELECT 1 Time: 62.066s (1 minute 2 seconds), executed in: 62.063s (1 minute 2 seconds) Pendulum 3 (which has been released in December 2023 and is now written in Rust) does not build on 32-bit architectures [1]. As such, installing `pgcli` on such architectures fails. We could pin Pendulum to version 2 (which was written in Python and builds "everywhere"), but requiring a whole library and its own dependencies for such a small feature seems unwarranted. This commit thus removes the requirement on Pendulum and replaces it by a simple "duration-to-words" function. Fixes #1451. [1] Upstream issue: python-pendulum/pendulum#784
1 parent 96eb37f commit 9f114c4

File tree

5 files changed

+52
-8
lines changed

5 files changed

+52
-8
lines changed

.github/workflows/ci.yml

-4
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ jobs:
6767
6868
psql -h localhost -U postgres -p 6432 pgbouncer -c 'show help'
6969
70-
- name: Install beta version of pendulum
71-
run: pip install pendulum==3.0.0b1
72-
if: matrix.python-version == '3.12'
73-
7470
- name: Install requirements
7571
run: |
7672
pip install -U pip setuptools

changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Bug fixes:
1111
* Fix display of "short host" in prompt (with `\h`) for IPv4 addresses ([issue 964](https://github.com/dbcli/pgcli/issues/964)).
1212
* Fix backwards display of NOTICEs from a Function ([issue 1443](https://github.com/dbcli/pgcli/issues/1443))
1313
* Fix psycopg errors when installing on Windows. ([issue 1413](https://https://github.com/dbcli/pgcli/issues/1413))
14+
* Use a home-made function to display query duration instead of relying on a third-party library (the general behaviour does not change), which fixes the installation of `pgcli` on 32-bit architectures ([issue 1451](https://github.com/dbcli/pgcli/issues/1451))
1415

1516
==================
1617
4.0.1 (2023-10-30)

pgcli/main.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import threading
1212
import shutil
1313
import functools
14-
import pendulum
1514
import datetime as dt
1615
import itertools
1716
import platform
@@ -800,9 +799,9 @@ def execute_command(self, text, handle_closed_connection=True):
800799
"Time: %0.03fs (%s), executed in: %0.03fs (%s)"
801800
% (
802801
query.total_time,
803-
pendulum.Duration(seconds=query.total_time).in_words(),
802+
duration_in_words(query.total_time),
804803
query.execution_time,
805-
pendulum.Duration(seconds=query.execution_time).in_words(),
804+
duration_in_words(query.execution_time),
806805
)
807806
)
808807
else:
@@ -1735,5 +1734,28 @@ def parse_service_info(service):
17351734
return service_conf, service_file
17361735

17371736

1737+
def duration_in_words(duration_in_seconds: float) -> str:
1738+
if not duration_in_seconds:
1739+
return "0 seconds"
1740+
components = []
1741+
hours, remainder = divmod(duration_in_seconds, 3600)
1742+
if hours > 1:
1743+
components.append(f"{hours} hours")
1744+
elif hours == 1:
1745+
components.append("1 hour")
1746+
minutes, seconds = divmod(remainder, 60)
1747+
if minutes > 1:
1748+
components.append(f"{minutes} minutes")
1749+
elif minutes == 1:
1750+
components.append("1 minute")
1751+
if seconds >= 2:
1752+
components.append(f"{int(seconds)} seconds")
1753+
elif seconds >= 1:
1754+
components.append("1 second")
1755+
elif seconds:
1756+
components.append(f"{round(seconds, 3)} second")
1757+
return " ".join(components)
1758+
1759+
17381760
if __name__ == "__main__":
17391761
cli()

setup.py

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"psycopg-binary >= 3.0.14; sys_platform == 'win32'",
1717
"sqlparse >=0.3.0,<0.5",
1818
"configobj >= 5.0.6",
19-
"pendulum>=2.1.0",
2019
"cli_helpers[styles] >= 2.2.1",
2120
]
2221

tests/test_main.py

+26
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from pgcli.main import (
1313
obfuscate_process_password,
14+
duration_in_words,
1415
format_output,
1516
PGCli,
1617
OutputSettings,
@@ -488,3 +489,28 @@ def test_application_name_db_uri(tmpdir):
488489
mock_pgexecute.assert_called_with(
489490
"bar", "bar", "", "baz.com", "", "", application_name="cow"
490491
)
492+
493+
494+
@pytest.mark.parametrize(
495+
"duration_in_seconds,words",
496+
[
497+
(0, "0 seconds"),
498+
(0.0009, "0.001 second"),
499+
(0.0005, "0.001 second"),
500+
(0.0004, "0.0 second"), # not perfect, but will do
501+
(0.2, "0.2 second"),
502+
(1, "1 second"),
503+
(1.4, "1 second"),
504+
(2, "2 seconds"),
505+
(3.4, "3 seconds"),
506+
(60, "1 minute"),
507+
(61, "1 minute 1 second"),
508+
(123, "2 minutes 3 seconds"),
509+
(3600, "1 hour"),
510+
(7235, "2 hours 35 seconds"),
511+
(9005, "2 hours 30 minutes 5 seconds"),
512+
(86401, "24 hours 1 second"),
513+
],
514+
)
515+
def test_duration_in_words(duration_in_seconds, words):
516+
assert duration_in_words(duration_in_seconds) == words

0 commit comments

Comments
 (0)