Source code for pymapadmin.local


from __future__ import annotations

import os
from argparse import Action, ArgumentParser, Namespace
from collections.abc import Sequence
from functools import partial
from pathlib import Path
from typing import Any, Final

from tempfile import gettempdir

__all__ = ['LocalFile', 'config_file', 'token_file', 'socket_file']


class _AddAction(Action):

    def __init__(self, local_file: LocalFile,
                 *args: Any, **kwargs: Any) -> None:
        kwargs['metavar'] = 'PATH'
        super().__init__(*args, **kwargs)
        self._local_file = local_file

    def __call__(self, parser: ArgumentParser, namespace: Namespace,
                 values: Any, option_string: str | None = None) -> None:
        setattr(namespace, self.dest, values)
        self._local_file.add(values)


[docs] class LocalFile: """Defines a file that is kept on the local filesystem but could be in a number of different places. Args: envvar: The environment variable used to custom the file path. filename: The default filename. """ def __init__(self, envvar: str, filename: str) -> None: super().__init__() self.envvar: Final = envvar self.filename: Final = filename self._custom: list[Path] = [] @property def add_action(self) -> type[Action]: """Use as an ``action=`` in :mod:`argparse` to add command-line arguments as custom paths. """ return partial(_AddAction, self) # type: ignore @property def custom(self) -> Sequence[Path]: """The custom paths added by :meth:`.add`, followed by any path specified by environment variable. """ try: envvar_val = os.environ[self.envvar] except KeyError: return self._custom else: return self._custom + [Path(envvar_val).expanduser()] @property def _config_home(self) -> Path: try: config_home = os.environ['XDG_CONFIG_HOME'] except KeyError: return Path(Path.home(), '.config') else: return Path(config_home).expanduser() @property def _temp_path(self) -> Path: return Path(gettempdir(), 'pymap', self.filename) @property def _home_path(self) -> Path: return Path(self._config_home, 'pymap', self.filename)
[docs] def add(self, *custom: None | str | Path) -> None: """Append the *custom* paths to :attr:`.custom`. Args: custom: The custom paths to the file, e.g. from config or command-line. """ new_custom = [*self._custom, *(Path(path).expanduser() for path in custom if path)] self._custom = new_custom
[docs] def get_home(self, *, mkdir: bool = False) -> Path: """Returns *filename* inside ``~/.config/pymap/``. If :attr:`.custom` is not empty, its first value is returned instead. Args: mkdir: Whether any intermediate directories should be created. """ try: path = self.custom[0] except IndexError: path = self._home_path if mkdir: path.parent.mkdir(mode=0o700, exist_ok=True) return path
[docs] def get_temp(self, *, mkdir: bool = False) -> Path: """Returns *filename* inside the ``pymap/`` subdirectory of :func:`~tempfile.gettempdir`. If :attr:`.custom` is not empty, its first value is returned instead. Args: mkdir: Whether any intermediate directories should be created. """ try: path = self.custom[0] except IndexError: path = self._temp_path if mkdir: path.parent.mkdir(mode=0o700, exist_ok=True) return path
[docs] def get_all(self) -> Sequence[Path]: """Return all the paths that may contain the file.""" return list(self.custom) + [self._home_path, self._temp_path]
[docs] def find(self) -> Path | None: """Return the :class:`~pathlib.Path` of an existing file, if one exists. The order of searched paths looks like this: 1. The :attr:`.custom` paths. 2. A path defined in the *envvar* environment variable. 3. *filename* in ``~/.config/pymap/``. 4. *filename* in the ``pymap`` subdirectory of :func:`tempfile.gettempdir`. """ all_paths = self.get_all() return next((path for path in all_paths if path.exists()), None)
#: The config file for default command-line arguments. config_file = LocalFile('PYMAP_ADMIN_CONFIG', 'pymap-admin.conf') #: The token file for pymap-admin authentication. token_file = LocalFile('PYMAP_ADMIN_TOKEN_FILE', 'pymap-admin.token') #: The socket file for connecting to a running pymap server. socket_file = LocalFile('PYMAP_ADMIN_SOCKET', 'pymap-admin.sock')