diff options
author | pommicket <pommicket@gmail.com> | 2025-09-23 02:18:01 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-23 02:18:24 -0400 |
commit | 0ece68fb8a7fd80f4497eaaa0464e0e31980455d (patch) | |
tree | 1625a0e69af7f711ee84d9860812104b66a08d75 /pom_parser | |
parent | b5cdae01c4186f7b52ca1966267a8c735756a891 (diff) |
Start documentation
Diffstat (limited to 'pom_parser')
-rw-r--r-- | pom_parser/__init__.py | 73 |
1 files changed, 66 insertions, 7 deletions
diff --git a/pom_parser/__init__.py b/pom_parser/__init__.py index c179158..e904d95 100644 --- a/pom_parser/__init__.py +++ b/pom_parser/__init__.py @@ -2,6 +2,22 @@ import io from typing import Optional, Any, Iterable, Iterator class Error(ValueError): + r'''!An error raised by pom_parser. + +Attributes +---------- +- `next: Optional[Error]` - + Next error (used when there are multiple errors in a file) +- `message: str` - + Error message as a string. Note that this does not include + file/line information, or all errors in a list, so you most + likely want to use str(error) instead. +- `file: str` - + File name where error occurred. +- `line: int` - + Line number where error occurred. +''' + next: Optional['Error'] message: str file: str @@ -27,6 +43,22 @@ class Error(ValueError): return l[0] class Item: + r'''! +An item (key-value pair) in a POM configuration. + +Attributes +---------- +- `key: str` - + The key. +- `value: str` - + The value. +- `file: str` - + File name where item was defined. +- `line: int` - + Line number where item was defined. +- `read: bool` - + Has this item been accessed by a \ref pom_parser.Configuration `get_*` method? +''' key: str value: str file: str @@ -113,20 +145,34 @@ class Item: return list_ class Configuration: + '''!A POM configuration.''' _items: dict[str, Item] + _section_locations: dict[str, tuple[str, int]] def __repr__(self) -> str: result = [] for item in self._items.values(): result.append(f'{item.key}: {repr(item.value)}') return '\n'.join(result) + def _init(self, items: dict[str, Item]) -> None: + self._items = items + self._section_locations = {} + for item in self._items.values(): + for i in range(len(item.key)): + if item.key[i] != '.': + continue + section = item.key[:i] + if section not in self._section_locations \ + or self._section_locations[section][1] > item.line: + self._section_locations[section] = (item.file, item.line) + def has(self, key: str) -> bool: return key in self._items def location(self, key: str) -> Optional[tuple[str, int]]: item = self._items.get(key) if item is None: - return item + return self._section_locations.get(key, None) return (item.file, item.line) def get(self, key: str, default: Optional[str] = None) -> Optional[str]: @@ -143,7 +189,8 @@ class Configuration: item.read = True uint = item._parse_uint() if uint is None: - raise item._error(f'Value {repr(item.value)} for {item.key} is not a valid (non-negative) integer.') + raise item._error(f'Value {repr(item.value)} for {item.key} is ' + 'not a valid (non-negative) integer.') return uint def get_int(self, key: str, default: Optional[int] = None) -> Optional[int]: @@ -173,7 +220,8 @@ class Configuration: item.read = True boolv = item._parse_bool() if boolv is None: - raise item._error(f'Value {repr(item.value)} for {item.key} is invalid (want on/off/yes/no/true/false)') + raise item._error(f'Value {repr(item.value)} for {item.key} is ' + 'invalid (want on/off/yes/no/true/false)') return boolv def get_list(self, key: str, default: Optional[list[str]] = None) -> Optional[list[str]]: @@ -203,9 +251,20 @@ class Configuration: item_copy = copy.copy(item) section_items[item.key[len(name_dot):]] = item_copy conf = Configuration() - conf._items = section_items + conf._init(section_items) + return conf + + def merge(self, other: 'Configuration') -> 'Configuration': + import copy + new_items = {key: copy.copy(item) for key, item in other._items.items()} + for key, item in self._items: + if key not in new_items: + new_items[key] = copy.copy(item) + conf = Configuration() + conf._init(new_items) return conf + def _parse_hex_digit(d: Optional[str]) -> Optional[int]: if d in list('0123456789'): return ord(d) - ord('0') @@ -215,7 +274,7 @@ def _parse_hex_digit(d: Optional[str]) -> Optional[int]: return ord(d) - ord('A') + 10 return None -class _Parser: +class __Parser: line_number: int filename: str current_section: str @@ -383,13 +442,13 @@ class _Parser: return True def load_file(filename: str, file: io.BufferedIOBase) -> Configuration: - parser = _Parser(filename, file) + parser = __Parser(filename, file) while parser._parse_line(): pass if parser.errors: raise Error._from_list(parser.errors) conf = Configuration() - conf._items = parser.items + conf._init(parser.items) return conf def load_string(filename: str, string: str) -> Configuration: |