-
Notifications
You must be signed in to change notification settings - Fork 260
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
Fixed issue #314 - include information about view/serializer in warnings #322
Fixed issue #314 - include information about view/serializer in warnings #322
Conversation
…in warnings. Two techniques for doing this: 1. If we already have a view/viewset object in our local scope, we can add it into the message 2. For other deeply nested places from which we call `error()` or `warn()`, we instead use a stack of traces that get added as prefices to the message (rather than having to pass this context information through many layers of code). This also fixes a set of consistency issues with warning/error messages: - Always start message with `{serializer/view name}: ` if possible. This means that method 1 and 2 above produce the same format of message. - First sentence shouldn't have a capital letter (the message is printed after colon from the prefix) - Subsequent sentences should have a capital letter.
Codecov Report
@@ Coverage Diff @@
## master #322 +/- ##
==========================================
+ Coverage 98.22% 98.24% +0.02%
==========================================
Files 53 54 +1
Lines 4729 4788 +59
==========================================
+ Hits 4645 4704 +59
Misses 84 84
Continue to review full report at Codecov.
|
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 @spookylukey, i love this this change and some people already asked for it. thank you very much!
the majority of comments is in regard to a single point. by adding the context manager to the generator too, we can remove the test hack and manual string prefix hacks. the view trace would be consistently added to everything. what do you think?
Due to the addition of prefixes into the message, this change can result in more messages being printed, because there are more "unique" messages.
i think the benefits of traceability significantly outweighs the added noise. it's worth it imho.
f'is untyped and obtaining queryset from {self.view.__class__} failed. ' | ||
f'consider adding a type to the path (e.g. <int:{variable}>) or annotating ' | ||
f'the parameter type with @extend_schema. defaulting to "string".' | ||
f'{self.view.__class__.__name__}: could not derive type of path parameter ' |
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.
prefix can be dropped again (see other comment)
f'could not derive type of path parameter "{variable}" because ' | ||
f'model "{model}" did contain no such field. consider annotating ' | ||
f'parameter with @extend_schema. defaulting to "string".' | ||
f'{self.view.__class__.__name__}: could not derive type of path ' |
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.
prefix can be dropped again (see other comment)
@@ -835,15 +836,15 @@ def _get_serializer(self): | |||
return view.serializer_class | |||
else: | |||
error( | |||
f'Unable to guess serializer for {view.__class__.__name__}. This is graceful ' | |||
f'{view.__class__.__name__}: unable to guess serializer. This is graceful ' |
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.
prefix can be dropped again (see other comment)
f'failed to obtain model through view\'s queryset due to raised exception. ' | ||
f'prevent this either by setting "queryset = Model.objects.none()" on the view, ' | ||
f'having an empty fallback in get_queryset() or by using @extend_schema. ' | ||
f'{view.__class__.__name__}: failed to obtain model through view\'s queryset due to ' |
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.
prefix can be dropped again (see other comment)
schema = generator.get_schema(request=None, public=True) | ||
validate_schema(schema) # make sure generated schemas are always valid | ||
return schema | ||
with add_trace_message(trace_message): |
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.
usage of the context manager and manual traces can be dropped in favor of the other solution. this also keeps the tests closer to reality
del self.registry[component] | ||
return ResolvedComponent(None, None) # sentinel | ||
return component | ||
with add_trace_message(serializer.__class__.__name__): |
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.
my OCD complains here due to increasing indent and obfuscating the git blame, but what can you do if that is the proper thing to do.
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.
Yep, me too, but I couldn't see a way around it. This consideration was in fact one of the reasons why I didn't want to put add_trace_message
round the big block in the generators.py
file. However I found in the end I only needed to put in around one line there. See #327
closed in favor of #327 |
I've used two techniques for doing this:
If we already have a view/viewset/serailizer object in our local scope, we can add its name into the warning/error message.
For other deeply nested places from which we call
error()
orwarn()
, we instead use a stack of traces that get added as prefixes to the message. Although this uses a global variable, it uses a fairly safe context manager pattern for doing so (as long as drf_spectacular doesn't go multi-threaded for the schema generation, which seems unlikely), and overall this seems much cleaner and more usable than having to pass this context information through many layers of code.This PR also fixes a set of consistency issues with warning/error messages:
{serializer/view name}:
if possible. This means that method 1 and 2 above produce the same format of message.I've added tests for quite a few cases that are improved, but not every possible combination because there are too many. On the current project I'm using drf_spectacular for, which has 500+ errors/warnings generated, this is a big boost in the consistency and usefulness of the error messages. I now have a list that looks like:
etc. This makes it massively easier to track down the errors.
In some cases it is possible to get
MyViewSet: MySerializer: ...
and evenMySerializer: MyNestedSerializer: ...
, but usually there is just a single prefixed name.I think in most cases this is about ideal. We probably don't want to include module names as well - in most cases this would add unnecessary noise.
Due to the addition of prefixes into the message, this change can result in more messages being printed, because there are more "unique" messages:
It's very difficult to distinguish between these two situations, overall I think this part of the change is neutral/positive.
Thanks for your time and consideration of this PR!