Skip to content

Commit 14e3a3d

Browse files
committed
Use file streams instead of file paths
Using a file stream for the config parsers means we can separate concerns more and have the parsers only responsible for parsing a file stream, as opposed to opening and file and then parsing it.
1 parent 158ebce commit 14e3a3d

File tree

13 files changed

+212
-338
lines changed

13 files changed

+212
-338
lines changed

src/maison/config_parser.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
class Parser(typing.Protocol):
1414
"""Defines the interface for a `Parser` class that's used to parse a config."""
1515

16-
def parse_config(self, file_path: pathlib.Path) -> typedefs.ConfigValues:
17-
"""Parse a config file.
16+
def parse_config(self, file: typing.BinaryIO) -> typedefs.ConfigValues:
17+
"""Parse a config.
1818
1919
Args:
20-
file_path: the path to the config file
20+
file: the binary stream of the config file
2121
2222
Returns:
23-
the config values
23+
the parsed config
2424
"""
2525
...
2626

@@ -42,18 +42,22 @@ def register_parser(
4242
key = (suffix, stem)
4343
self._parsers[key] = parser
4444

45-
def parse_config(self, file_path: pathlib.Path) -> typedefs.ConfigValues:
45+
def parse_config(
46+
self,
47+
file_path: pathlib.Path,
48+
file: typing.BinaryIO,
49+
) -> typedefs.ConfigValues:
4650
"""See `Parser.parse_config`."""
4751
key: ParserDictKey
4852

4953
# First try (suffix, stem)
5054
key = (file_path.suffix, file_path.stem)
5155
if key in self._parsers:
52-
return self._parsers[key].parse_config(file_path)
56+
return self._parsers[key].parse_config(file)
5357

5458
# Then fallback to (suffix, None)
5559
key = (file_path.suffix, None)
5660
if key in self._parsers:
57-
return self._parsers[key].parse_config(file_path)
61+
return self._parsers[key].parse_config(file)
5862

5963
raise errors.UnsupportedConfigError(f"No parser registered for {file_path}")

src/maison/parsers/ini.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""A parser for .ini files."""
22

33
import configparser
4-
import pathlib
4+
import io
5+
import typing
56

67
from maison import typedefs
78

@@ -12,8 +13,12 @@ class IniParser:
1213
Implements the `Parser` protocol
1314
"""
1415

15-
def parse_config(self, file_path: pathlib.Path) -> typedefs.ConfigValues:
16+
def parse_config(self, file: typing.BinaryIO) -> typedefs.ConfigValues:
1617
"""See the Parser.parse_config method."""
1718
config = configparser.ConfigParser()
18-
_ = config.read(file_path)
19+
text_io = io.TextIOWrapper(file, encoding="utf-8")
20+
try:
21+
config.read_file(text_io)
22+
except UnicodeDecodeError:
23+
return {}
1924
return {section: dict(config.items(section)) for section in config.sections()}

src/maison/parsers/toml.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""A parser for .toml files."""
22

3-
import pathlib
43
import sys
54

65

@@ -36,11 +35,10 @@ def __init__(self, section_key: typing.Optional[tuple[str, ...]] = None) -> None
3635
"""
3736
self.section_key = section_key or ()
3837

39-
def parse_config(self, file_path: pathlib.Path) -> typedefs.ConfigValues:
38+
def parse_config(self, file: typing.BinaryIO) -> typedefs.ConfigValues:
4039
"""See the Parser.parse_config method."""
4140
try:
42-
with file_path.open(mode="rb") as fd:
43-
values = dict(tomllib.load(fd))
41+
values = dict(tomllib.load(file))
4442
except (FileNotFoundError, tomllib.TOMLDecodeError):
4543
return {}
4644

src/maison/protocols.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,16 @@ def open_file(self, path: pathlib.Path) -> typing.BinaryIO:
6161
class ConfigParser(typing.Protocol):
6262
"""Defines the interface for a class that parses a config."""
6363

64-
def parse_config(self, file_path: pathlib.Path) -> typedefs.ConfigValues:
64+
def parse_config(
65+
self,
66+
file_path: pathlib.Path,
67+
file: typing.BinaryIO,
68+
) -> typedefs.ConfigValues:
6569
"""Parse a config.
6670
6771
Args:
6872
file_path: the path to a config file.
73+
file: the binary I/O stream of the file.
6974
7075
Returns:
7176
the parsed config

src/maison/service.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ def get_config_values(
6767
config_values: typedefs.ConfigValues = {}
6868

6969
for path in config_file_paths:
70-
parsed_config = self.config_parser.parse_config(path)
70+
file = self.filesystem.open_file(path=path)
71+
parsed_config = self.config_parser.parse_config(file_path=path, file=file)
7172
config_values = utils.deep_merge(config_values, parsed_config)
7273

7374
if not merge_configs:

tests/integration_tests/parsers/test_ini.py

Lines changed: 0 additions & 90 deletions
This file was deleted.

tests/integration_tests/parsers/test_pyproject.py

Lines changed: 0 additions & 99 deletions
This file was deleted.

0 commit comments

Comments
 (0)