Enable safe fallback when loading single values - #147
Conversation
| with Path(fpath).open('rt', encoding=encoding or self.encoding) as fp: | ||
| return self.load(fp) | ||
|
|
||
| def loadv(self, string: str) -> typing.Any: |
There was a problem hiding this comment.
Not sure this implementation is entirely kosher, thoughts welcome 🤔
This combined with calling it where singular values are expected does actually fairly transparently solve the issue.
There was a problem hiding this comment.
This relies on the formats doing the proper translating of all possible relevant parsing errors back to ValueError, and there not being any extraneous ValueErrors.
I hate to bring this up, but could this be a good place for a custom exception type?
There was a problem hiding this comment.
The only ones not supporting the fallback themselves are JSON and TOML, both using a fairly sane exception hierachy where a parsing failure will raise something that quacks ValueError 😎 Not sure a custom type will add anything there.
There was a problem hiding this comment.
You are right, I was led astray by the complex Exception hierarchy of PyYAML (see for example yaml/pyyaml#750). If the parsers that need it raise ValueErrors, that this should work.
It feels a bit ad-hoc though. If exception types are a part of the Format API, should it be documented so people adding a format can make sure it throws ValueErrors in the cases covered by this fallback?
There was a problem hiding this comment.
Ah yes, documentation of the assumptions here makes sense, let's at least make that clear.
- document the role of
ValueErrorinFormat.loadv
|
Comparing median (lower is better) metric of benchmarks between this PR's target (562ed1c) and the HEAD of this PR (4d54ee9):
(This comment will be updated on subsequent pushes) |
|
|
||
| suffix: str = '' #: the default file path suffix for a configuration file of this Format | ||
| encoding: str = 'utf-8' #: the default text encoding for reading from binary I/O | ||
| value_fallback: Callable[[str], typing.Any] = str #: the fallback 'factory' for unparseable single values |
There was a problem hiding this comment.
Changing this needs a test
|
|
||
| suffix: str = '' #: the default file path suffix for a configuration file of this Format | ||
| encoding: str = 'utf-8' #: the default text encoding for reading from binary I/O | ||
| value_fallback: Callable[[str], typing.Any] = str #: the fallback 'factory' for unparseable single values |
There was a problem hiding this comment.
Alternatively, we could so something like Format(strict=False), where the fallback type is always str. Less explicit, maybe easier to understand?
There was a problem hiding this comment.
Realistically, what do we expect this callable to be/do? Are there many more sane options than str? Do we expect the behavior to change per format?
I'm thinking this might be too much flexibility for our needs.
There was a problem hiding this comment.
Agreed, str is the one and only sane option I can think of at the moment, really. So that actually doesn't need to be parametrized. The behaviour wouldn't need to change per format, for so far as the formats behave the same way (as in: YAML doesn't really need these hoops as it's (too?) flexible in itself).
In hindsight, strict might not be a great fit either though, which begs the question: should this feature (if we go through with it) even get a switch or be enabled by default?
|
Based on the current functionality and the current implementation, I think we have two (not trivially reconcilable) requirements:
The issues arise from the fact that 1 is different from 2 for non-yaml formats. Possible solutions include:
Solution 3 can be very surprising, and not in a good way, if there's an actual syntax error in the document! Why am i getting an error in format X instead of Y? Solution 4 is complex and error prone. Do not recommend. Realistically we're left with 1, 2 and 5. Solution 1 clearly didn't work well since we're in this PR considering this change, and 5 would dump one of the main reasons to use confidence over bare env vars. So we're stuck with some variation of 2, it seems! |
Yeah, that's the current state of things without this PR (see quoting stuff in the test code for citatio.
Correct, details still under consideration, but yes.
100% agreed that this is a terrible idea, particularly with requirement 2 in mind; having to switch syntax is surprising and cumbersome.
Feels like the wrong kind of magic and indeed, also complex and error prone. Let's not :)
That's actually not really the hard one, I think. The common use case is putting a string in an env var, and even that already breaks in the current state of things. Fixing that while still enabling other simple values like numbers or booleans would still require something along the lines of this PR. The second requirement is almost a side effect of supporting non-string simple values.
That's exactly my train of thought too, so thanks for arriving at that same point 👯♂️ Aside from agreeing, I'm still on the fence whether this should be enabled by default, as the most surprising things this will be doing is silently eating syntax errors in environment variables. Should someone put a full document into an env var and make a syntax mistake in it, it will become a long string without any loud errors at load time. Another way this could be parametrized is |
Fixes #143