From 17f8b8ae3334066816737a959c640dd1bfe7d49a Mon Sep 17 00:00:00 2001 From: Denis Laxalde Date: Mon, 19 Feb 2024 16:19:04 +0100 Subject: [PATCH] Add support for builtin profiles (WIP) TODO: - complement the "minimal.conf" profile - add more builtin profiles (e.g. a "narrow.conf", to limit the number of columns)? - add a compat layer around importlib.resources API - add tests --- CHANGELOG.md | 1 + README.md | 4 ++++ pgactivity/compat.py | 19 ++++++++++++++++++- pgactivity/config.py | 21 ++++++++++++++++++++- pgactivity/profiles/minimal.conf | 2 ++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 pgactivity/profiles/minimal.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index f19b73b6..a3945222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ `${XDG_CONFIG_HOME:~/.config}/pg_activity/.conf` or `/etc/pg_activity/.conf` as selected from the command line through `--profile `. + A few built-in profiles are distributed with pg\_activity. ### Changed diff --git a/README.md b/README.md index a39fb8c8..7ab8de0c 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,10 @@ files located at `${XDG_CONFIG_HOME:~/.config}/pg_activity/.conf` or `--profile ` command-line option. The format of these files is the same as the main configuration file. +`pg_activity` ships with a few built-in profiles: + +- `minimal`, providing a minimal user interface with most non-essential + information hidden [INI format]: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure diff --git a/pgactivity/compat.py b/pgactivity/compat.py index a15048a4..7f35e82a 100644 --- a/pgactivity/compat.py +++ b/pgactivity/compat.py @@ -1,6 +1,7 @@ +import importlib.resources import operator import sys -from typing import Any, Dict +from typing import Any, Dict, Optional import attr import attr.validators @@ -11,6 +12,22 @@ else: from importlib.metadata import version +if sys.version_info >= (3, 11): + + def read_resource(pkgname: str, dirname: str, *args: str) -> Optional[str]: + 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) -> Optional[str]: + raise NotImplementedError + + 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 5542bfc1..0c214185 100644 --- a/pgactivity/config.py +++ b/pgactivity/config.py @@ -1,5 +1,6 @@ import configparser import enum +import io import os from pathlib import Path from typing import IO, Any, Dict, List, Optional, Type, TypeVar @@ -7,7 +8,7 @@ import attr from attr import validators -from .compat import gt +from .compat import gt, read_resource class ConfigurationError(Exception): @@ -188,6 +189,19 @@ def from_config_section(cls: Type[_T], section: configparser.SectionProxy) -> _T return cls(**values) +@attr.s(auto_attribs=True, frozen=True, slots=True) +class BuiltinProfile: + name: str + content: IO[str] + + @classmethod + def get(cls, name: str) -> Optional["BuiltinProfile"]: + content = read_resource("pgactivity", "profiles", f"{name}.conf") + if content is not None: + return BuiltinProfile(name, io.StringIO(content)) + return None + + USER_CONFIG_HOME = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) ETC = Path("/etc") @@ -282,4 +296,9 @@ def lookup( if fpath.exists(): with fpath.open() as f: return cls.parse(f, str(fpath)) + + builtin_profile = BuiltinProfile.get(profile) + if builtin_profile is not None: + return cls.parse(builtin_profile.content, builtin_profile.name) + raise FileNotFoundError(f"profile {profile!r} not found") diff --git a/pgactivity/profiles/minimal.conf b/pgactivity/profiles/minimal.conf new file mode 100644 index 00000000..fe28866a --- /dev/null +++ b/pgactivity/profiles/minimal.conf @@ -0,0 +1,2 @@ +[database] +hidden = yes