-
Notifications
You must be signed in to change notification settings - Fork 287
qol: exception formatting #2715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
✅ Deploy Preview for dlt-hub-docs canceled.
|
b09a52b
to
f48df5a
Compare
094ce2f
to
a39b802
Compare
I briefly looked at the failed tests and except:
|
That's a fun one. it seems that the code doesn't if isinstance(data, DltSource):
if len(data.selected_resources.keys()) == 1:
return list(data.selected_resources.values())[0]
else:
# no `raise` keyword
ValueError(
"You are trying to use an adapter on a `DltSource` with multiple resources. You can"
" only use adapters on: pure data, a `DltResouce` or a `DltSource` with a single"
" `DltResource`."
) Good reminder for writing unit tests: |
@@ -23,8 +23,6 @@ class ConfigurationValueError(ConfigurationException, ValueError): | |||
class ContainerException(DltException): | |||
"""base exception for all exceptions related to injectable container""" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
learning question: why not pass?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pass
keyword is not needed since there's a docstring.
details
These are valid ways to define a "empty" class or function in Python (an "empty" function returns None
):
# with a pass
class MyClass:
pass
def my_fn():
pass
# with a docstring
class MyClass:
"""This is my class"""
def my_fn():
"""This is my function"""
# with ellipsis
class MyClass:
...
def my_fn():
...
# ellipsis is typically used inline
class MyClass: ...
def my_fn(): ...
Those are all equivalent and can be combined. Personally, I find pass
to be the most ambiguous because it can be used for other purposes in loops.
"Empty" classes and functions are mostly used in typing stubs and @typing.overload
to define type signatures.
# dlt.common.destination.dataset
class SupportsReadableRelation:
# ...
@overload
def __getitem__(self, column: str) -> Self: ...
@overload
def __getitem__(self, columns: Sequence[str]) -> Self: ...
def __getitem__(self, columns: Union[str, Sequence[str]]) -> Self:
"""Returns a new relation with the given columns selected.
Args:
columns (Union[str, Sequence[str]]): The columns to select.
Returns:
Self: The relation with the columns selected.
"""
raise NotImplementedError("`__getitem__()` method is not supported for this relation")
pivoted_rows = np.asarray(rows, dtype="object", order="K").T | ||
return { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: nice fix! apparently 'k' didn't do anything, but maybe it then defaulted to K...
was there a no test for this? maybe we should consider adding it, if it's easy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, I completely missed this. Good catch!
The numpy docs version 2.2 (latest)
and 1.26
indicate that order="K"
is the default value. Then, I think the change from "k"
(did nothing and defaulted to "K"
) to "K"
is safe
dlt/common/schema/utils.py
Outdated
@@ -440,7 +441,7 @@ def merge_columns( | |||
* incomplete columns in `columns_a` that got completed in `columns_b` are removed to preserve order | |||
""" | |||
if columns_partial is False: | |||
raise NotImplementedError("columns_partial must be False for merge_columns") | |||
raise NotImplementedError("`columns_partial` must be `False` for `merge_columns`") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: I don't think the error message is helpful here, as columns_partial being false is exactly what raises the error here, isn't it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not a problem this pr created , but it could fix it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure. I think it's legacy code from a refactoring. The developer didn't want to change the function signature by removing columns_partial
(this would break downstream code), but instead added an explicit exception with a more useful error message.
Tip: when using git or GitHub, you can use git blame
to blame who made the changes :). It points to specific commit where you'll find a useful commit message or PR hopefully.
@@ -114,8 +114,8 @@ def _sync_locks(self) -> t.List[str]: | |||
output.append(name) | |||
if not output: | |||
raise RuntimeError( | |||
f"When syncing locks for path {self.path} and lock {self.lock_path} no lock file" | |||
" was found" | |||
f"Lock syncing failed. No lock file found for path `{self.path}` and lock" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: I like this format much better. is there a wor for it?
in emails or conversations, I call it frontloading
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but "frontloading" is a good name for it! I don't have a rigorous process, but I try to write exceptions:
- what's wrong in the fewest words possible. Here, it would be even better to raise a
LockSyncingError
- explain why the error is raised
- include hints to start debugging
- make it explicit when you interpolate a variable
- keep long variables at the end (e.g.,
self.path
)
Didn't want to rewrite all exceptions, but this one was hiding the key message at the very end "no lock file was found"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey thierry,
what a ride. Reading nothing but exception messages is kinda fun. never did that before.
I found only two or three places that really need changes imo (prefixed my comment with issue:
, the rest is just a bunch of small things to adjust like missing backticks or ' that failed to get replaced.
I corrected or marked many of them, not all, but I think the codebase will be more unified for sure after this PR. (achieving 100% similarity wouldnt be a wise use of our time for sure)
the other main issue is that you probably need to adjust a couple more test-messages to match the new messages.
I'm on vacation until next week, so I won't mark this as changes requested to no tblock this from being merged becasue I am slow to respond, but my requested changes would be: the issues i marked and make the tests pass. then its green from me.
e0f6450
to
79eb192
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! tests are passing
* first pass formatting raise statements * added custom exception * updated Exception classes * linting & formatting * fixed tests * fix imports * capitalize argument to please mypy * add raise keyword * applied review comments * passing tests * fixed test * fixed broken check logic * format * fixes bigquery test --------- Co-authored-by: Marcin Rudolf <[email protected]>
Description
The majority of exceptions use f-strings and interpolate variables into the exception message. However, they don't highlight when values are interpolated nor name the entities. This makes some user-facing message confusing.
Example
Changes
{variable_name=:}
for clearer exception messages. This also simplifies maintenance when renaming argsdlt.common.exceptions
theTypeErrorWithKnownTypes
andValueErrorWithKnownValues
to streamline a two common error messages