Skip to content
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

Merged
merged 13 commits into from
Jan 15, 2020
Merged

Conversation

codeboten
Copy link
Contributor

@codeboten codeboten commented Nov 28, 2019

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:

def _load_impl(
api_type: Type[_T], factory: Optional[Callable[[Type[_T]], Optional[_T]]]
) -> _T:
"""Tries to load a configured implementation, if unsuccessful, returns a
fast no-op implemenation that is always available.
"""
result = _try_load_configured_impl(api_type, factory)
if result is None:
return api_type()
return result

Signed-off-by: Alex Boten [email protected]

Signed-off-by: Alex Boten <[email protected]>
@codeboten codeboten added the WIP Work in progress label Nov 28, 2019
@codeboten codeboten changed the title make Meter an ABC Separate no-op from interfaces Nov 28, 2019
@codecov-io
Copy link

codecov-io commented Nov 28, 2019

Codecov Report

Merging #311 into master will decrease coverage by 0.84%.
The diff coverage is 79.5%.

Impacted file tree graph

@@            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
Impacted Files Coverage Δ
opentelemetry-api/src/opentelemetry/util/loader.py 81.25% <ø> (ø) ⬆️
...etry-sdk/src/opentelemetry/sdk/metrics/__init__.py 99.05% <ø> (ø) ⬆️
...ntelemetry-api/src/opentelemetry/trace/sampling.py 85.1% <ø> (ø) ⬆️
...sdk/src/opentelemetry/sdk/trace/export/__init__.py 86.79% <0%> (ø) ⬆️
...try-ext-wsgi/src/opentelemetry/ext/wsgi/version.py 100% <100%> (ø) ⬆️
...pentelemetry-sdk/src/opentelemetry/sdk/__init__.py 100% <100%> (ø) ⬆️
...-ext-flask/src/opentelemetry/ext/flask/__init__.py 88.88% <100%> (ø)
...elemetry-api/src/opentelemetry/metrics/__init__.py 87.93% <100%> (+1.96%) ⬆️
.../src/opentelemetry/ext/opentracing_shim/version.py 100% <100%> (ø)
...app/src/opentelemetry_example_app/flask_example.py 100% <100%> (ø) ⬆️
... and 14 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 607ff4b...bcaaf6b. Read the comment docs.

@codeboten
Copy link
Contributor Author

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

@codeboten codeboten marked this pull request as ready for review December 3, 2019 15:56
@codeboten codeboten removed the WIP Work in progress label Dec 3, 2019
Copy link
Member

@c24t c24t left a 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.

opentelemetry-api/src/opentelemetry/trace/__init__.py Outdated Show resolved Hide resolved
opentelemetry-api/src/opentelemetry/trace/__init__.py Outdated Show resolved Hide resolved
# pylint: disable=unused-argument,no-self-use
return INVALID_SPAN

@contextmanager # type: ignore
Copy link
Member

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.

Copy link
Member

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_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?

Copy link
Contributor Author

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.

Copy link
Member

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)

Copy link
Member

@Oberon00 Oberon00 Dec 6, 2019

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).

Copy link
Member

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.

Copy link
Contributor

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)
Copy link
Member

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 Tracers.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Member

@c24t c24t left a 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.

Copy link
Member

@Oberon00 Oberon00 left a 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)

@Oberon00 Oberon00 mentioned this pull request Dec 6, 2019
Copy link
Member

@toumorokoshi toumorokoshi left a 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
Copy link
Member

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.

@toumorokoshi toumorokoshi added the PR:please merge This PR is ready to be merged by a Maintainer (has enough valid approvals, successful build, etc.) label Dec 9, 2019
@Oberon00
Copy link
Member

Oberon00 commented Dec 9, 2019

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).

@Oberon00 Oberon00 removed the PR:please merge This PR is ready to be merged by a Maintainer (has enough valid approvals, successful build, etc.) label Dec 9, 2019
# pylint: disable=no-self-use
return DefaultMetric()


Copy link
Contributor

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.

Copy link
Contributor Author

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.

@a-feld a-feld removed their request for review December 10, 2019 00:54
Updated TracerSource to also use ABC, created the DefaultTracerSource no-op implementation.

Signed-off-by: Alex Boten <[email protected]>
@codeboten codeboten requested a review from a team January 9, 2020 18:52
Copy link
Member

@c24t c24t left a 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
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""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.

@c24t c24t merged commit ada53ff into open-telemetry:master Jan 15, 2020
@GoPavel GoPavel mentioned this pull request Jan 30, 2020
@codeboten codeboten deleted the issue_166 branch October 9, 2020 18:50
srikanthccv pushed a commit to srikanthccv/opentelemetry-python that referenced this pull request Nov 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider using ABC
7 participants