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 | |
parent | b5cdae01c4186f7b52ca1966267a8c735756a891 (diff) |
Start documentation
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Doxyfile | 25 | ||||
-rw-r--r-- | examples/read_conf.py | 2 | ||||
-rw-r--r-- | pom_parser/__init__.py | 73 |
4 files changed, 93 insertions, 8 deletions
@@ -1,3 +1,4 @@ dist venv *.pyc +doc diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..6babbdb --- /dev/null +++ b/Doxyfile @@ -0,0 +1,25 @@ +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = pom_parser +PROJECT_BRIEF = "Parser for the POM configuration language" +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +MARKDOWN_SUPPORT = YES +INPUT_ENCODING = UTF-8 +INPUT = pom_parser/__init__.py +RECURSIVE = NO +GENERATE_HTML = YES +HTML_OUTPUT = . +HTML_FILE_EXTENSION = .html +GENERATE_LATEX = NO +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +MULTILINE_CPP_IS_BRIEF = YES +AUTOLINK_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = YES +EXTRACT_PRIVATE = NO +COLLABORATION_GRAPH = NO +WARN_IF_UNDOCUMENTED = YES +QUIET = YES +INCLUDE_GRAPH = NO +WARN_AS_ERROR = FAIL_ON_WARNINGS diff --git a/examples/read_conf.py b/examples/read_conf.py index 936157e..bbee83b 100644 --- a/examples/read_conf.py +++ b/examples/read_conf.py @@ -9,6 +9,6 @@ import pom_parser try: filename = 'examples/conf.pom' if len(sys.argv) < 2 else sys.argv[1] conf = pom_parser.load_path(filename) - print(conf.get_list('file-extensions.C',['a'])) + print(conf.location('file-extensions')) except pom_parser.Error as e: print('Parse error:', str(e), sep = '\n') 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: |