pre-commit-hooks/.venv/lib/python3.10/site-packages/iniconfig/__init__.py
2024-04-13 00:00:20 +00:00

214 lines
5.5 KiB
Python

""" brain-dead simple parser for ini-style files.
(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
"""
from __future__ import annotations
import os
from typing import Callable
from typing import cast
from typing import Iterator
from typing import Mapping
from typing import NamedTuple
from typing import NoReturn
from typing import Optional
from typing import overload
from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
if TYPE_CHECKING:
from typing import Final
__all__ = ['IniConfig', 'ParseError', 'COMMENTCHARS', 'iscommentline']
from .exceptions import ParseError
from . import _parse
from ._parse import COMMENTCHARS, iscommentline
_D = TypeVar('_D')
_T = TypeVar('_T')
class SectionWrapper:
config: Final[IniConfig]
name: Final[str]
def __init__(self, config: IniConfig, name: str) -> None:
self.config = config
self.name = name
def lineof(self, name: str) -> int | None:
return self.config.lineof(self.name, name)
@overload
def get(self, key: str) -> str | None:
...
@overload
def get(
self,
key: str,
convert: Callable[[str], _T],
) -> _T | None:
...
@overload
def get(
self,
key: str,
default: None,
convert: Callable[[str], _T],
) -> _T | None:
...
@overload
def get(self, key: str, default: _D, convert: None = None) -> str | _D:
...
@overload
def get(
self,
key: str,
default: _D,
convert: Callable[[str], _T],
) -> _T | _D:
...
# TODO: investigate possible mypy bug wrt matching the passed over data
def get( # type: ignore [misc]
self,
key: str,
default: _D | None = None,
convert: Callable[[str], _T] | None = None,
) -> _D | _T | str | None:
return self.config.get(self.name, key, convert=convert, default=default)
def __getitem__(self, key: str) -> str:
return self.config.sections[self.name][key]
def __iter__(self) -> Iterator[str]:
section: Mapping[str, str] = self.config.sections.get(self.name, {})
def lineof(key: str) -> int:
return self.config.lineof(self.name, key) # type: ignore[return-value]
yield from sorted(section, key=lineof)
def items(self) -> Iterator[tuple[str, str]]:
for name in self:
yield name, self[name]
class IniConfig:
path: Final[str]
sections: Final[Mapping[str, Mapping[str, str]]]
def __init__(
self,
path: str | os.PathLike[str],
data: str | None = None,
encoding: str = 'utf-8',
) -> None:
self.path = os.fspath(path)
if data is None:
with open(self.path, encoding=encoding) as fp:
data = fp.read()
tokens = _parse.parse_lines(self.path, data.splitlines(True))
self._sources = {}
sections_data: dict[str, dict[str, str]]
self.sections = sections_data = {}
for lineno, section, name, value in tokens:
if section is None:
raise ParseError(self.path, lineno, 'no section header defined')
self._sources[section, name] = lineno
if name is None:
if section in self.sections:
raise ParseError(
self.path, lineno, f'duplicate section {section!r}',
)
sections_data[section] = {}
else:
if name in self.sections[section]:
raise ParseError(self.path, lineno, f'duplicate name {name!r}')
assert value is not None
sections_data[section][name] = value
def lineof(self, section: str, name: str | None = None) -> int | None:
lineno = self._sources.get((section, name))
return None if lineno is None else lineno + 1
@overload
def get(
self,
section: str,
name: str,
) -> str | None:
...
@overload
def get(
self,
section: str,
name: str,
convert: Callable[[str], _T],
) -> _T | None:
...
@overload
def get(
self,
section: str,
name: str,
default: None,
convert: Callable[[str], _T],
) -> _T | None:
...
@overload
def get(
self, section: str, name: str, default: _D, convert: None = None,
) -> str | _D:
...
@overload
def get(
self,
section: str,
name: str,
default: _D,
convert: Callable[[str], _T],
) -> _T | _D:
...
def get( # type: ignore
self,
section: str,
name: str,
default: _D | None = None,
convert: Callable[[str], _T] | None = None,
) -> _D | _T | str | None:
try:
value: str = self.sections[section][name]
except KeyError:
return default
else:
if convert is not None:
return convert(value)
else:
return value
def __getitem__(self, name: str) -> SectionWrapper:
if name not in self.sections:
raise KeyError(name)
return SectionWrapper(self, name)
def __iter__(self) -> Iterator[SectionWrapper]:
for name in sorted(self.sections, key=self.lineof): # type: ignore
yield SectionWrapper(self, name)
def __contains__(self, arg: str) -> bool:
return arg in self.sections