summaryrefslogtreecommitdiff
path: root/pom_parser
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-23 02:18:01 -0400
committerpommicket <pommicket@gmail.com>2025-09-23 02:18:24 -0400
commit0ece68fb8a7fd80f4497eaaa0464e0e31980455d (patch)
tree1625a0e69af7f711ee84d9860812104b66a08d75 /pom_parser
parentb5cdae01c4186f7b52ca1966267a8c735756a891 (diff)
Start documentation
Diffstat (limited to 'pom_parser')
-rw-r--r--pom_parser/__init__.py73
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: