-
Notifications
You must be signed in to change notification settings - Fork 624
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
Separate no-op from interfaces #311
Conversation
Signed-off-by: Alex Boten <aboten@lightstep.com>
Codecov Report
@@ Coverage Diff @@
## master #311 +/- ##
==========================================
- Coverage 85.76% 84.91% -0.85%
==========================================
Files 33 38 +5
Lines 1672 1883 +211
Branches 182 217 +35
==========================================
+ Hits 1434 1599 +165
- Misses 190 219 +29
- Partials 48 65 +17
Continue to review full report at Codecov.
|
Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
Updated Tracer and Span to be ABCs as well. Added a try block to the loader to default to the DefaultTracer when any other implementation fails to load https://github.com/open-telemetry/opentelemetry-python/pull/311/files#diff-97345450e765fa40d32664356325e06cR601-R606 |
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.
Changes LGTM, and I think this is the right approach for the APIs in general. I have a few questions about default values.
# pylint: disable=unused-argument,no-self-use | ||
return INVALID_SPAN | ||
|
||
@contextmanager # type: ignore |
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.
What's the right solution for context manager types? I see we've been adding # type: ignore
to these since (at least) #92, but couldn't find a discussion about this.
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.
This is tracked in #219. The best solution I know is still #198 (comment).
@@ -525,7 +598,12 @@ def tracer() -> Tracer: | |||
|
|||
if _TRACER is None: | |||
# pylint:disable=protected-access | |||
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY) | |||
try: | |||
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY) # type: ignore |
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.
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY) # type: ignore | |
_TRACER = loader._load_impl(DefaultTracer, _TRACER_FACTORY) # type: ignore |
Would this do the same thing as the try/catch?
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.
Using a DefaultTracer
would mean any implementation would be extending DefaultTracer
rather than the Tracer
interface, which means we would lose the value of using abstract methods to signal what methods are expected to be implemented.
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 think this try:
catch should really be moved to the loader. Probably some reflection will be required (or a class attribute DEFAULT_IMPLEMENTATION_CLASS
linking the two (alternatively INTERFACE_CLASS
in the other direction)
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.
But if I were a vendor, I would definitely inherit from DefaultTracer instead of Tracer because I don't want my code to crash the customer's app if they start calling a new method that my Tracer doesn't (yet) implement. Which was also one argument by me (and I think @carlosalberto agreed) against this ABC/Default split in #66 (#66 (comment) to be exact).
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 think that's a really good argument that almost convinces me against ABCs. I'm +1 to doing that work in this PR, or going back to switching to no-ops once we have all of this standardized.
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.
Yeah, definitely same feeling about vendors ending up inheriting from DefaultTracer
.
@@ -49,7 +49,7 @@ def setUp(self): | |||
|
|||
def test_get_default(self): | |||
tracer = trace.tracer() | |||
self.assertIs(type(tracer), trace.Tracer) | |||
self.assertIs(type(tracer), trace.DefaultTracer) |
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.
@Oberon00 might want to check that this is testing the right thing still. It seems like DummyTracer
should still extend Tracer
here, but that seems to rely on instantiating Tracer
s.
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.
Alternatively here I can implement the abstract methods in DummyTracer
and that should work as well
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 think implementing the abstract methods would be worth it here.
Co-Authored-By: Chris Kleinknecht <libc@google.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
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, let's see if @Oberon00 has any comments.
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 have no strong opinion (I weakly prefer not splitting ABC/Default but I won't have time to argue in the near future)
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.
Thanks for the work here! Standardizing our policy would be great.
@@ -525,7 +598,12 @@ def tracer() -> Tracer: | |||
|
|||
if _TRACER is None: | |||
# pylint:disable=protected-access | |||
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY) | |||
try: | |||
_TRACER = loader._load_impl(Tracer, _TRACER_FACTORY) # type: ignore |
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 think that's a really good argument that almost convinces me against ABCs. I'm +1 to doing that work in this PR, or going back to switching to no-ops once we have all of this standardized.
Signed-off-by: Alex Boten <aboten@lightstep.com>
Note that in #66 we decided that we do not want to use ABCs. It seems that we have revised our decision here and in #166, but I want to make sure that everyone is aware of that. Please make sure you understand the reasons for the decision from #66 and that you still want to do this. I'm removing the "please merge" label again for now (ofc you are still free to merge but it isn't something that should be merged without consideration). |
# pylint: disable=no-self-use | ||
return DefaultMetric() | ||
|
||
|
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 think get_label_set
will be needed too.
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.
Fixed, i'd forgotten to make get_label_set
abstract.
Updated TracerSource to also use ABC, created the DefaultTracerSource no-op implementation. Signed-off-by: Alex Boten <aboten@lightstep.com>
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.
One question about abstract method implementations, and it looks like we still need an abstract get_label_set
, but otherwise it LGTM!
@@ -211,6 +212,7 @@ def record_batch( | |||
corresponding value to record for that metric. | |||
""" | |||
|
|||
@abc.abstractmethod |
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.
What are your thoughts on abstract methods having implementations? I see how it could be useful to have a safe default implementation that the user has to explicitly call with super
, but I see that DefaultMeter
has its own implementation.
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.
For now, I'll remove the default implementation from the interfaces. I don't have a strong preference either ways, but it's less changes to remove the few that were left behind than to add an implementation for all abstract methods.
@@ -175,12 +178,14 @@ def get_context(self) -> "SpanContext": | |||
# pylint: disable=no-self-use | |||
return INVALID_SPAN_CONTEXT |
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.
Same question about implementation here.
|
||
|
||
class Tracer: | ||
class DefaultTracerSource(TracerSource): | ||
"""The default TracerSource, used when no TracerSource implementation is available. |
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 default TracerSource, used when no TracerSource implementation is available. | |
"""The default TracerSource, used when no implementation is available. |
or
"""The default TracerSource, used when no implementation is
available.
Signed-off-by: Alex Boten <aboten@lightstep.com>
closes open-telemetry#311 Signed-off-by: Olivier Albertini <olivier.albertini@montreal.ca>
Fixes #66
Started on this today. Providing a no-op for Meter is easy. Separating the Tracer interface from a no-op implementation is a bit more tricky. It requires changes to the loader as the following code tries to load an implementation of a Tracer, and if it fails it tries to load the Tracer interface itself which won't work with an ABC:
opentelemetry-python/opentelemetry-api/src/opentelemetry/util/loader.py
Lines 162 to 172 in d3bb228
Signed-off-by: Alex Boten aboten@lightstep.com