Skip to content

Commit

Permalink
feat: Replace pendulum by home-made duration-to-words function
Browse files Browse the repository at this point in the history
`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: sdispater/pendulum#784
  • Loading branch information
dbaty committed Feb 19, 2024
1 parent 96eb37f commit 82690ad
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 7 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,6 @@ jobs:
psql -h localhost -U postgres -p 6432 pgbouncer -c 'show help'
- name: Install beta version of pendulum
run: pip install pendulum==3.0.0b1
if: matrix.python-version == '3.12'

- name: Install requirements
run: |
pip install -U pip setuptools
Expand Down
1 change: 1 addition & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Bug fixes:
* Fix display of "short host" in prompt (with `\h`) for IPv4 addresses ([issue 964](https://github.com/dbcli/pgcli/issues/964)).
* Fix backwards display of NOTICEs from a Function ([issue 1443](https://github.com/dbcli/pgcli/issues/1443))
* Fix psycopg errors when installing on Windows. ([issue 1413](https://https://github.com/dbcli/pgcli/issues/1413))
* Fix installation of `pgcli` on 32-bit architectures ([issue1451](https://github.com/dbcli/pgcli/issues/1451))

==================
4.0.1 (2023-10-30)
Expand Down
27 changes: 25 additions & 2 deletions pgcli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,9 +800,9 @@ def execute_command(self, text, handle_closed_connection=True):
"Time: %0.03fs (%s), executed in: %0.03fs (%s)"
% (
query.total_time,
pendulum.Duration(seconds=query.total_time).in_words(),
duration_in_words(query.total_time),
query.execution_time,
pendulum.Duration(seconds=query.execution_time).in_words(),
duration_in_words(query.execution_time),
)
)
else:
Expand Down Expand Up @@ -1735,5 +1735,28 @@ def parse_service_info(service):
return service_conf, service_file


def duration_in_words(duration_in_seconds: float) -> str:
if not duration_in_seconds:
return "0 seconds"
components = []
hours, remainder = divmod(duration_in_seconds, 3600)
if hours > 1:
components.append(f"{hours} hours")
elif hours == 1:
components.append("1 hour")
minutes, seconds = divmod(remainder, 60)
if minutes > 1:
components.append(f"{minutes} minutes")
elif minutes == 1:
components.append("1 minute")
if seconds > 1:
components.append(f"{int(seconds)} seconds")
elif seconds == 1:
components.append("1 second")
elif seconds:
components.append(f"{round(seconds, 3)} second")
return " ".join(components)


if __name__ == "__main__":
cli()
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"psycopg-binary >= 3.0.14; sys_platform == 'win32'",
"sqlparse >=0.3.0,<0.5",
"configobj >= 5.0.6",
"pendulum>=2.1.0",
"cli_helpers[styles] >= 2.2.1",
]

Expand Down
25 changes: 25 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from pgcli.main import (
obfuscate_process_password,
duration_in_words,
format_output,
PGCli,
OutputSettings,
Expand Down Expand Up @@ -488,3 +489,27 @@ def test_application_name_db_uri(tmpdir):
mock_pgexecute.assert_called_with(
"bar", "bar", "", "baz.com", "", "", application_name="cow"
)


@pytest.mark.parametrize(
"duration_in_seconds,words",
[
(0, "0 seconds"),
(0.2, "0.2 second"),
(0.0009, "0.001 second"),
(0.0005, "0.001 second"),
(0.0004, "0.0 second"), # not perfect, but will do
(1, "1 second"),
(2, "2 seconds"),
(3.4, "3 seconds"),
(60, "1 minute"),
(61, "1 minute 1 second"),
(123, "2 minutes 3 seconds"),
(3600, "1 hour"),
(7235, "2 hours 35 seconds"),
(9005, "2 hours 30 minutes 5 seconds"),
(86401, "24 hours 1 second"),
],
)
def test_duration_in_words(duration_in_seconds, words):
assert duration_in_words(duration_in_seconds) == words

0 comments on commit 82690ad

Please sign in to comment.