diff --git a/AUTHORS b/AUTHORS index 40d7f58..4bbaba2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -137,10 +137,7 @@ Contributors: * Chris Rose (offbyone/offby1) * Mathieu Dupuy (deronnax) * Chris Novakovic - * Max Smolin (maximsmol) * Josh Lynch (josh-lynch) - * Fabio (3ximus) - * Doug Harris (dougharris) Creator: -------- diff --git a/changelog.rst b/changelog.rst index dcf886a..0b219ea 100644 --- a/changelog.rst +++ b/changelog.rst @@ -1,10 +1,3 @@ -4.3.0 (2025-03-22) -================== - -Features --------- -* The session time zone setting is set to the system time zone by default - 4.2.0 (2025-03-06) ================== @@ -12,8 +5,6 @@ Features -------- * Add a `--ping` command line option; allows pgcli to replace `pg_isready` * Changed the packaging metadata from setup.py to pyproject.toml -* Add bash completion for services defined in the service file `~/.pg_service.conf` -* Added support for per-column date/time formatting using `column_date_formats` in config Bug fixes: ---------- diff --git a/debian/changelog b/debian/changelog index 82e3da3..9ff7998 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,3 @@ -pgcli (4.3.0-1) sid; urgency=medium - - * Merging upstream version 4.3.0. - - -- Daniel Baumann Mon, 24 Mar 2025 15:47:46 +0100 - pgcli (4.2.0-2) sid; urgency=medium * Using codename instead of suitename in changelog. diff --git a/pgcli-completion.bash b/pgcli-completion.bash index 620563d..3549b56 100644 --- a/pgcli-completion.bash +++ b/pgcli-completion.bash @@ -3,9 +3,9 @@ _pg_databases() # -w was introduced in 8.4, https://launchpad.net/bugs/164772 # "Access privileges" in output may contain linefeeds, hence the NF > 1 COMPREPLY=( $( compgen -W "$( psql -AtqwlF $'\t' 2>/dev/null | \ - awk 'NF > 1 { print $1 }' )" -- "$cur" ) ) + awk 'NF > 1 { print $1 }' )" -- "$cur" ) ) } - + _pg_users() { # -w was introduced in 8.4, https://launchpad.net/bugs/164772 @@ -13,23 +13,12 @@ _pg_users() template1 2>/dev/null )" -- "$cur" ) ) [[ ${#COMPREPLY[@]} -eq 0 ]] && COMPREPLY=( $( compgen -u -- "$cur" ) ) } - -_pg_services() -{ - # return list of available services - local services - if [[ -f "$HOME/.pg_service.conf" ]]; then - services=$(grep -oP '(?<=^\[).*?(?=\])' "$HOME/.pg_service.conf") - fi - local suffix="${cur#*=}" - COMPREPLY=( $(compgen -W "$services" -- "$suffix") ) -} - + _pgcli() { local cur prev words cword _init_completion -s || return - + case $prev in -h|--host) _known_hosts_real "$cur" @@ -50,27 +39,23 @@ _pgcli() esac case "$cur" in - service=*) - _pg_services - return 0 - ;; - --*) - # return list of available options - COMPREPLY=( $( compgen -W '--host --port --user --password --no-password - --single-connection --version --dbname --pgclirc --dsn - --row-limit --help' -- "$cur" ) ) - [[ $COMPREPLY == *= ]] && compopt -o nospace - return 0 - ;; - -) - # only complete long options - compopt -o nospace - COMPREPLY=( -- ) - return 0 - ;; - *) + --*) + # return list of available options + COMPREPLY=( $( compgen -W '--host --port --user --password --no-password + --single-connection --version --dbname --pgclirc --dsn + --row-limit --help' -- "$cur" ) ) + [[ $COMPREPLY == *= ]] && compopt -o nospace + return 0 + ;; + -) + # only complete long options + compopt -o nospace + COMPREPLY=( -- ) + return 0 + ;; + *) # return list of available databases - _pg_databases + _pg_databases esac -} && +} && complete -F _pgcli pgcli diff --git a/pgcli/__init__.py b/pgcli/__init__.py index 111dc91..0fd7811 100644 --- a/pgcli/__init__.py +++ b/pgcli/__init__.py @@ -1 +1 @@ -__version__ = "4.3.0" +__version__ = "4.2.0" diff --git a/pgcli/main.py b/pgcli/main.py index 0765efb..d4c6dbf 100644 --- a/pgcli/main.py +++ b/pgcli/main.py @@ -1,4 +1,3 @@ -from zoneinfo import ZoneInfoNotFoundError from configobj import ConfigObj, ParseError from pgspecial.namedqueries import NamedQueries from .config import skip_initial_comment @@ -20,15 +19,10 @@ from time import time, sleep from typing import Optional from cli_helpers.tabular_output import TabularOutputFormatter -from cli_helpers.tabular_output.preprocessors import ( - align_decimals, - format_numbers, - format_timestamps, -) +from cli_helpers.tabular_output.preprocessors import align_decimals, format_numbers from cli_helpers.utils import strip_ansi from .explain_output_formatter import ExplainOutputFormatter import click -import tzlocal try: import setproctitle @@ -117,13 +111,12 @@ MetaQuery.__new__.__defaults__ = ("", False, 0, 0, False, False, False, False) OutputSettings = namedtuple( "OutputSettings", - "table_format dcmlfmt floatfmt column_date_formats missingval expanded max_width case_function style_output max_field_width", + "table_format dcmlfmt floatfmt missingval expanded max_width case_function style_output max_field_width", ) OutputSettings.__new__.__defaults__ = ( None, None, None, - None, "", False, None, @@ -271,7 +264,6 @@ class PGCli: self.on_error = c["main"]["on_error"].upper() self.decimal_format = c["data_formats"]["decimal"] self.float_format = c["data_formats"]["float"] - self.column_date_formats = c["column_date_formats"] auth.keyring_initialize(c["main"].as_bool("keyring"), logger=self.logger) self.show_bottom_toolbar = c["main"].as_bool("show_bottom_toolbar") @@ -1187,7 +1179,6 @@ class PGCli: table_format=self.table_format, dcmlfmt=self.decimal_format, floatfmt=self.float_format, - column_date_formats=self.column_date_formats, missingval=self.null_string, expanded=expanded, max_width=max_width, @@ -1602,9 +1593,9 @@ def cli( if list_databases or ping_database: database = "postgres" - cfg = load_config(pgclirc, config_full_path) if dsn != "": try: + cfg = load_config(pgclirc, config_full_path) dsn_config = cfg["alias_dsn"][dsn] except KeyError: click.secho( @@ -1633,55 +1624,6 @@ def cli( else: pgcli.connect(database, host, user, port) - if "use_local_timezone" not in cfg["main"] or cfg["main"].as_bool( - "use_local_timezone" - ): - server_tz = pgcli.pgexecute.get_timezone() - - def echo_error(msg: str): - click.secho( - "Failed to determine the local time zone", - err=True, - fg="yellow", - ) - click.secho( - msg, - err=True, - fg="yellow", - ) - click.secho( - f"Continuing with the default time zone as preset by the server ({server_tz})", - err=True, - fg="yellow", - ) - click.secho( - "Set `use_local_timezone = False` in the config to avoid trying to override the server time zone\n", - err=True, - dim=True, - ) - - local_tz = None - try: - local_tz = tzlocal.get_localzone_name() - - if local_tz is None: - echo_error("No local time zone configuration found\n") - else: - click.secho( - f"Using local time zone {local_tz} (server uses {server_tz})", - fg="green", - ) - click.secho( - "Use `set time zone ` to override, or set `use_local_timezone = False` in the config", - dim=True, - ) - - pgcli.pgexecute.set_timezone(local_tz) - except ZoneInfoNotFoundError as e: - # e.args[0] is the pre-formatted message which includes a list - # of conflicting sources - echo_error(e.args[0]) - if list_databases: cur, headers, status = pgcli.pgexecute.full_databases() @@ -1888,7 +1830,6 @@ def format_output(title, cur, headers, status, settings, explain_mode=False): "missing_value": settings.missingval, "integer_format": settings.dcmlfmt, "float_format": settings.floatfmt, - "column_date_formats": settings.column_date_formats, "preprocessors": (format_numbers, format_arrays), "disable_numparse": True, "preserve_whitespace": True, @@ -1898,9 +1839,6 @@ def format_output(title, cur, headers, status, settings, explain_mode=False): if not settings.floatfmt: output_kwargs["preprocessors"] = (align_decimals,) - if settings.column_date_formats: - output_kwargs["preprocessors"] += (format_timestamps,) - if table_format == "csv": # The default CSV dialect is "excel" which is not handling newline values correctly # Nevertheless, we want to keep on using "excel" on Windows since it uses '\r\n' diff --git a/pgcli/pgclirc b/pgcli/pgclirc index be10610..dd8b15f 100644 --- a/pgcli/pgclirc +++ b/pgcli/pgclirc @@ -191,10 +191,6 @@ enable_pager = True # Use keyring to automatically save and load password in a secure manner keyring = True -# Automatically set the session time zone to the local time zone -# If unset, uses the server's time zone, which is the Postgres default -use_local_timezone = True - # Custom colors for the completion menu, toolbar, etc. [colors] completion-menu.completion.current = 'bg:#ffffff #000000' @@ -244,8 +240,3 @@ output.null = "#808080" [data_formats] decimal = "" float = "" - -# Per column formats for date/timestamp columns -[column_date_formats] -# use strftime format, e.g. -# created = "%Y-%m-%d" diff --git a/pgcli/pgexecute.py b/pgcli/pgexecute.py index 4e7c637..e091757 100644 --- a/pgcli/pgexecute.py +++ b/pgcli/pgexecute.py @@ -881,16 +881,3 @@ class PGExecute: def explain_prefix(self): return "EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) " - - def get_timezone(self) -> str: - query = psycopg.sql.SQL("show time zone") - with self.conn.cursor() as cur: - cur.execute(query) - return cur.fetchone()[0] - - def set_timezone(self, timezone: str): - query = psycopg.sql.SQL("set time zone {}").format( - psycopg.sql.Identifier(timezone) - ) - with self.conn.cursor() as cur: - cur.execute(query) diff --git a/pyproject.toml b/pyproject.toml index 6c73602..d714282 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,13 +34,12 @@ dependencies = [ "psycopg-binary >= 3.0.14; sys_platform == 'win32'", "sqlparse >=0.3.0,<0.6", "configobj >= 5.0.6", - "cli_helpers[styles] >= 2.4.0", + "cli_helpers[styles] >= 2.2.1", # setproctitle is used to mask the password when running `ps` in command line. # But this is not necessary in Windows since the password is never shown in the # task manager. Also setproctitle is a hard dependency to install in Windows, # so we'll only install it if we're not in Windows. "setproctitle >= 1.1.9; sys_platform != 'win32' and 'CYGWIN' not in sys_platform", - "tzlocal >= 5.2", ] dynamic = ["version"] diff --git a/release.py b/release.py index ad6a957..42a72a9 100644 --- a/release.py +++ b/release.py @@ -2,10 +2,10 @@ """A script to publish a release of pgcli to PyPI.""" import io +from optparse import OptionParser import re import subprocess import sys -from optparse import OptionParser import click @@ -66,8 +66,7 @@ def create_git_tag(tag_name): def create_distribution_files(): - run_step("rm", "-rf", "dist/") - run_step("python", "-m", "build") + run_step("python", "setup.py", "clean", "--all", "sdist", "bdist_wheel") def upload_distribution_files(): @@ -92,11 +91,11 @@ if __name__ == "__main__": if DEBUG: subprocess.check_output = lambda x: x - # checks = [ - # "Have you updated the AUTHORS file?", - # "Have you updated the `Usage` section of the README?", - # ] - # checklist(checks) + checks = [ + "Have you updated the AUTHORS file?", + "Have you updated the `Usage` section of the README?", + ] + checklist(checks) ver = version("pgcli/__init__.py") print("Releasing Version:", ver) diff --git a/tests/test_main.py b/tests/test_main.py index 102ebcd..3683d49 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -76,57 +76,6 @@ def test_format_output(): assert list(results) == expected -def test_column_date_formats(): - settings = OutputSettings( - table_format="psql", - column_date_formats={ - "date_col": "%Y-%m-%d", - "datetime_col": "%I:%M:%S %m/%d/%y", - }, - ) - data = [ - ("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"), - ("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"), - ] - headers = ["name", "date_col", "datetime_col", "unchanged_col"] - - results = format_output("Title", data, headers, "test status", settings) - expected = [ - "Title", - "+-------+------------+-------------------+---------------------+", - "| name | date_col | datetime_col | unchanged_col |", - "|-------+------------+-------------------+---------------------|", - "| name1 | 2024-12-13 | 07:32:22 12/13/24 | 2024-12-13T20:32:22 |", - "| name2 | 2025-02-13 | 02:32:22 02/13/25 | 2025-02-13T02:32:22 |", - "+-------+------------+-------------------+---------------------+", - "test status", - ] - assert list(results) == expected - - -def test_no_column_date_formats(): - """Test that not setting any column date formats returns unaltered datetime columns""" - settings = OutputSettings(table_format="psql") - data = [ - ("name1", "2024-12-13T18:32:22", "2024-12-13T19:32:22", "2024-12-13T20:32:22"), - ("name2", "2025-02-13T02:32:22", "2025-02-13T02:32:22", "2025-02-13T02:32:22"), - ] - headers = ["name", "date_col", "datetime_col", "unchanged_col"] - - results = format_output("Title", data, headers, "test status", settings) - expected = [ - "Title", - "+-------+---------------------+---------------------+---------------------+", - "| name | date_col | datetime_col | unchanged_col |", - "|-------+---------------------+---------------------+---------------------|", - "| name1 | 2024-12-13T18:32:22 | 2024-12-13T19:32:22 | 2024-12-13T20:32:22 |", - "| name2 | 2025-02-13T02:32:22 | 2025-02-13T02:32:22 | 2025-02-13T02:32:22 |", - "+-------+---------------------+---------------------+---------------------+", - "test status", - ] - assert list(results) == expected - - def test_format_output_truncate_on(): settings = OutputSettings( table_format="psql", dcmlfmt="d", floatfmt="g", max_field_width=10