diff --git a/CHANGELOG.md b/CHANGELOG.md index 4060f54b..1f75c850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ * Exit with status 0 upon keyboard interrupt. +### Removed + +* Python 3.8 is no longer supported. + ## pg\_activity 3.5.1 - 2024-04-03 ### Fixed diff --git a/pgactivity/activities.py b/pgactivity/activities.py index 6cc596f0..e7127e10 100644 --- a/pgactivity/activities.py +++ b/pgactivity/activities.py @@ -3,13 +3,13 @@ import builtins import os import time +from collections.abc import Sequence from typing import TypeVar from warnings import catch_warnings, simplefilter import attr import psutil -from .compat import Sequence from .types import ( BlockingProcess, IOCounter, diff --git a/pgactivity/cli.py b/pgactivity/cli.py index 2a97e6b3..d3642982 100755 --- a/pgactivity/cli.py +++ b/pgactivity/cli.py @@ -47,14 +47,13 @@ def configure_logger(debug_file: str | None = None) -> StringIO: def flag(p: Any, spec: str, *, dest: str, feature: str) -> None: assert not spec.startswith("--no-") and spec.startswith("--"), spec - if sys.version_info < (3, 9): - spec = f"--no-{spec[2:]}" - action = "store_false" - help = f"Disable {feature}." - else: - action = argparse.BooleanOptionalAction - help = f"Enable/disable {feature}." - p.add_argument(spec, dest=dest, help=help, action=action, default=None) + p.add_argument( + spec, + dest=dest, + help=f"Enable/disable {feature}.", + action=argparse.BooleanOptionalAction, + default=None, + ) def get_parser() -> argparse.ArgumentParser: diff --git a/pgactivity/compat.py b/pgactivity/compat.py index 2b7f2f20..d615664c 100644 --- a/pgactivity/compat.py +++ b/pgactivity/compat.py @@ -1,8 +1,6 @@ from __future__ import annotations -import importlib.resources import operator -import sys from importlib.metadata import version from typing import Any @@ -10,50 +8,6 @@ import attr.validators import blessed -__all__ = [ - "Callable", - "Dict", - "Iterable", - "Iterator", - "Mapping", - "MutableSet", - "Sequence", -] - -if sys.version_info >= (3, 9): - from collections.abc import ( - Callable, - Iterable, - Iterator, - Mapping, - MutableSet, - Sequence, - ) - - Dict = dict -else: - from typing import Callable, Dict, Iterable, Iterator, Mapping, MutableSet, Sequence - -if sys.version_info >= (3, 9): - - def read_resource(pkgname: str, dirname: str, *args: str) -> str | None: - resource = importlib.resources.files(pkgname).joinpath(dirname) - for arg in args: - resource = resource.joinpath(arg) - if resource.is_file(): - return resource.read_text() - return None - -else: - - def read_resource(pkgname: str, dirname: str, *args: str) -> str | None: - with importlib.resources.path(pkgname, dirname) as dirp: - f = dirp.joinpath(*args) - if f.is_file(): - return f.read_text() - return None - - ATTR_VERSION = tuple(int(x) for x in version("attrs").split(".", 2)[:2]) BLESSED_VERSION = tuple(int(x) for x in version("blessed").split(".", 2)[:2]) diff --git a/pgactivity/config.py b/pgactivity/config.py index 4d5e81bd..f329af78 100644 --- a/pgactivity/config.py +++ b/pgactivity/config.py @@ -2,6 +2,7 @@ import configparser import enum +import importlib.resources import io import os from collections.abc import ItemsView @@ -11,7 +12,16 @@ import attr from attr import validators -from .compat import gt, read_resource +from .compat import gt + + +def read_resource(pkgname: str, dirname: str, *args: str) -> str | None: + resource = importlib.resources.files(pkgname).joinpath(dirname) + for arg in args: + resource = resource.joinpath(arg) + if resource.is_file(): + return resource.read_text() + return None class ConfigurationError(Exception): diff --git a/pgactivity/pg.py b/pgactivity/pg.py index 5b1a0c92..3c82a82d 100644 --- a/pgactivity/pg.py +++ b/pgactivity/pg.py @@ -2,10 +2,9 @@ import logging import os +from collections.abc import Callable, Sequence from typing import Any, TypeVar, overload -from .compat import Callable, Dict, Sequence - Row = TypeVar("Row") try: @@ -29,7 +28,7 @@ __version__ = psycopg.__version__ - Connection = psycopg.Connection[Dict[str, Any]] + Connection = psycopg.Connection[dict[str, Any]] class BytesLoader(Loader): def load(self, data: Buffer) -> bytes: diff --git a/pgactivity/queries/__init__.py b/pgactivity/queries/__init__.py index 63be06a0..bb629db7 100644 --- a/pgactivity/queries/__init__.py +++ b/pgactivity/queries/__init__.py @@ -4,7 +4,7 @@ here = pathlib.Path(__file__).parent -@functools.lru_cache(maxsize=None) +@functools.cache def get(name: str) -> str: path = here / f"{name}.sql" with path.open() as f: diff --git a/pgactivity/types.py b/pgactivity/types.py index 28119057..8f6bd2ff 100644 --- a/pgactivity/types.py +++ b/pgactivity/types.py @@ -2,16 +2,16 @@ import enum import functools +from collections.abc import Callable, Iterable, Iterator, Mapping, MutableSet, Sequence from datetime import timedelta from ipaddress import IPv4Address, IPv6Address -from typing import Any, Tuple, TypeVar, Union, overload +from typing import Any, TypeVar, Union, overload import attr import psutil from attr import validators from . import colors, compat, pg, utils -from .compat import Callable, Iterable, Iterator, Mapping, MutableSet, Sequence from .config import Configuration, Flag, HeaderSection, UISection @@ -1186,7 +1186,7 @@ def copy_focused_query_to_clipboard(self) -> str: ActivityStats = Union[ Iterable[WaitingProcess], Iterable[RunningProcess], - Tuple[Iterable[WaitingProcess], SystemInfo], - Tuple[Iterable[BlockingProcess], SystemInfo], - Tuple[Iterable[LocalRunningProcess], SystemInfo], + tuple[Iterable[WaitingProcess], SystemInfo], + tuple[Iterable[BlockingProcess], SystemInfo], + tuple[Iterable[LocalRunningProcess], SystemInfo], ] diff --git a/pgactivity/ui.py b/pgactivity/ui.py index 520926a0..0cd68d9f 100644 --- a/pgactivity/ui.py +++ b/pgactivity/ui.py @@ -3,7 +3,7 @@ import time from argparse import Namespace from functools import partial -from typing import List, cast +from typing import cast import attr from blessed import Terminal @@ -216,7 +216,7 @@ def main( if is_local: # TODO: Use this logic in waiting and blocking cases. local_pg_procs, io_read, io_write = activities.ps_complete( - cast(List[types.RunningProcess], pg_procs.items), + cast(list[types.RunningProcess], pg_procs.items), sys_procs, fs_blocksize, ) diff --git a/pgactivity/utils.py b/pgactivity/utils.py index 4127d242..1ebc2fe4 100644 --- a/pgactivity/utils.py +++ b/pgactivity/utils.py @@ -4,8 +4,9 @@ import functools import re import sys +from collections.abc import Iterable, Mapping from datetime import datetime, timedelta, timezone -from typing import IO, Any, Iterable, Mapping +from typing import IO, Any import attr import humanize diff --git a/pgactivity/views.py b/pgactivity/views.py index 454f477e..b8720afc 100644 --- a/pgactivity/views.py +++ b/pgactivity/views.py @@ -3,6 +3,7 @@ import functools import inspect import itertools +from collections.abc import Callable, Iterable, Iterator, Sequence from textwrap import TextWrapper, dedent from typing import Any, Literal @@ -10,7 +11,7 @@ from . import colors, utils from .activities import sorted as sorted_processes -from .compat import Callable, Iterable, Iterator, Sequence, link +from .compat import link from .keys import BINDINGS, EXIT_KEY from .keys import HELP as HELP_KEY from .keys import ( diff --git a/pyproject.toml b/pyproject.toml index 8f34b4c5..c20dec4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Command line tool for PostgreSQL server activity monitoring." readme = "README.md" license = { text = "PostgreSQL" } -requires-python = ">=3.8" +requires-python = ">=3.9" authors = [ { name = "Julien Tachoires", email = "julmon@gmail.com" }, { name = "Benoit Lobréau", email = "benoit.lobreau@dalibo.com" }, diff --git a/tests/test_cli_help_py38.txt b/tests/test_cli_help_py38.txt deleted file mode 100644 index 48a3c46e..00000000 --- a/tests/test_cli_help_py38.txt +++ /dev/null @@ -1,86 +0,0 @@ ->>> import sys ->>> import pytest ->>> if sys.version_info >= (3, 9): -... pytest.skip("only applies for Python version < 3.9") -... - ->>> from pgactivity import cli ->>> parser = cli.get_parser() ->>> parser.print_help() -usage: pytest [options] [connection string] - -htop like application for PostgreSQL server activity monitoring. - -Configuration: - -P PROFILE, --profile PROFILE - Configuration profile matching a PROFILE.conf file in - ${XDG_CONFIG_HOME:~/.config}/pg_activity/ or - /etc/pg_activity/, or a built-in profile. - -Options: - --blocksize BLOCKSIZE - Filesystem blocksize (default: 4096). - --rds Enable support for AWS RDS (implies --no-tempfiles and - filters out the rdsadmin database from space - calculation). - --output FILEPATH Store running queries as CSV. - --no-db-size Disable total size of DB. - --no-tempfiles Disable tempfile count and size. - --no-walreceiver Disable walreceiver checks. - -w, --wrap-query Wrap query column instead of truncating. - --duration-mode DURATION_MODE - Duration mode. Values: 1-QUERY(default), - 2-TRANSACTION, 3-BACKEND. - --min-duration SECONDS - Don't display queries with smaller than specified - duration (in seconds). - --filter FIELD:REGEX Filter activities with a (case insensitive) regular - expression applied on selected fields. Known fields - are: dbname. - --debug-file DEBUG_FILE - Enable debug and write it to DEBUG_FILE. - --version show program's version number and exit. - --help Show this help message and exit. - -Connection Options: - connection string A valid connection string to the database, e.g.: - 'host=HOSTNAME port=PORT user=USER dbname=DBNAME'. - -h HOSTNAME, --host HOSTNAME - Database server host or socket directory. - -p PORT, --port PORT Database server port. - -U USERNAME, --username USERNAME - Database user name. - -d DBNAME, --dbname DBNAME - Database name to connect to. - -Process table display options: - These options may be used hide some columns from the processes table. - - --no-pid Disable PID. - --no-database Disable DATABASE. - --no-user Disable USER. - --no-client Disable CLIENT. - --no-cpu Disable CPU%. - --no-mem Disable MEM%. - --no-read Disable READ/s. - --no-write Disable WRITE/s. - --no-time Disable TIME+. - --no-wait Disable W. - --no-app-name Disable APP. - -Header display options: - --no-inst-info Hide instance information. - --no-sys-info Hide system information. - --no-proc-info Hide workers process information. - -Other display options: - --hide-queries-in-logs - Disable log_min_duration_statements and - log_min_duration_sample for pg_activity. - --refresh REFRESH Refresh rate. Values: 0.5, 1, 2, 3, 4, 5 (default: 2). - -The connection string can be in the form of a list of Key/Value parameters or -an URI as described in the PostgreSQL documentation. The parsing is delegated -to the libpq: different versions of the client library may support different -formats or parameters (for example, connection URIs are only supported from -libpq 9.2).