Source code for outgoing.util

from __future__ import annotations
from abc import ABC, abstractmethod
import os
from pathlib import Path
from types import TracebackType
from typing import Optional, TypeVar, Union
from pydantic import BaseModel, PrivateAttr

AnyPath = Union[str, bytes, "os.PathLike[str]", "os.PathLike[bytes]"]

OC = TypeVar("OC", bound="OpenClosable")


[docs]class OpenClosable(ABC, BaseModel): """ An abstract base class for creating reentrant_ context managers. A concrete subclass must define ``open()`` and ``close()`` methods; `OpenClosable` will then define ``__enter__`` and ``__exit__`` methods that keep track of the depth of nested ``with`` statements, calling ``open()`` and ``close()`` only when entering & exiting the outermost ``with``. .. _reentrant: https://docs.python.org/3/library/contextlib.html #reentrant-cms """ _context_depth: int = PrivateAttr(0)
[docs] @abstractmethod def open(self) -> None: ...
[docs] @abstractmethod def close(self) -> None: ...
def __enter__(self: OC) -> OC: if self._context_depth == 0: self.open() self._context_depth += 1 return self def __exit__( self, _exc_type: Optional[type[BaseException]], _exc_val: Optional[BaseException], _exc_tb: Optional[TracebackType], ) -> None: self._context_depth -= 1 if self._context_depth == 0: self.close()
[docs]def resolve_path(path: AnyPath, basepath: Optional[AnyPath] = None) -> Path: """ Convert a path to a `pathlib.Path` instance and resolve it using the same rules for as paths in ``outgoing`` configuration files: expand tildes by calling `Path.expanduser()`, prepend the parent directory of ``basepath`` (usually the value of ``configpath``) to the path if given, and then resolve the resulting path to make it absolute. :param path path: the path to resolve :param path basepath: an optional path to resolve ``path`` relative to :rtype: pathlib.Path """ p = Path(os.fsdecode(path)).expanduser() if basepath is not None: p = Path(os.fsdecode(basepath)).parent / p return p.resolve()