-
Notifications
You must be signed in to change notification settings - Fork 658
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
Add Link and Event classes #130
Add Link and Event classes #130
Conversation
Put all actions of the same member together so it is easier to update the tests.
368e2cc
to
4c0fb58
Compare
@@ -70,6 +70,47 @@ | |||
ParentSpan = typing.Optional[typing.Union["Span", "SpanContext"]] | |||
|
|||
|
|||
class Link: |
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.
Since these classes are so simple, why did you not keep using namedtuple
?
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.
IIRC the idea is that users can extend these classes (hence the lazy prefix). And personally I'd prefer to keep them as very simple classes (otherwise, I'd totally be up for named tuples ;) )
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.
Even if those classes are very simple, I think having a proper class allows more flexibility, for instance as mentioned by @carlosalberto, when users want to extend those.
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.
namedtuples can be extended, they are full classes. In fact, a common pattern, that's even mentioned in the docs of namedtuple is something like:
class MyDataCls(namedtuple('MyDataCls', 'foo bar')):
def baz(self):
return self.foo + self.bar
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.
namedtuples can be extended
Sure, but IHMO it's still better to have this as simple classes, as it's clearer for users who want to extend it, instead of having to go read the namedtuple
documentation ;)
(If we had this classes as private or SDK specific, I'd be also more open to have them as namedtuple
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.
@Oberon00 what's the benefit of using namedtuples here? If the goal is to avoid boilerplate we might want to use the attrs library. The docs include some reasons not to use namedtuples for data classes. One big reason is that subclasses of namedtuples have a MRO that looks very different from plain old classes.
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.
OK, these reasons sound valid enough. I retract my objections then 😃 I think since the goal of the API is to be a low level building block we should prefer a bit of boilerplate code over an additional dependency.
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.
Agreed, I think there's a stronger reason for using attrs in the SDK than the API, but we may not want it at all.
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
"""A link to a `Span`.""" | ||
|
||
def __init__( | ||
self, context: "SpanContext", attributes: types.Attributes = {} |
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.
self, context: "SpanContext", attributes: types.Attributes = {} | |
self, context: "SpanContext", attributes: types.Attributes = None |
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 used that because mypy was complaining, but then lint complained about this one. I had to change types.Attributes to typing.Optional[typing.Dict[str, AttributeValue]]
and it worked.
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.
As to why mypy/lint complain: https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
Make Link a class, also implement addLazyLink
Make Event a class and implement add_lazy_event
4c0fb58
to
a339e7d
Compare
def __init__( | ||
self, name: str, timestamp: int, attributes: types.Attributes = None | ||
) -> None: | ||
self._name = 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.
A general question - do we expect this to be implemented in the API package or the SDK?
I think it makes sense to have it the API package, and I don't expect people to override this in the SDK.
Just to put the question here to see if someone has a different understanding.
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'd +1 for keeping it in the API.
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.
By keeping it on the API we are providing a reference implementation and also allowing users to extend it if needed.
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 it makes sense to have it the API package
Agreed, that's how we do it in Java too, btw.
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.
Nice!
"""A link to a `Span`.""" | ||
|
||
def __init__( | ||
self, context: "SpanContext", attributes: types.Attributes = None |
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.
should attributes be typing.Optional? since it defaults to none.
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 directly changed the type of types.Attributes
to be Attributes = typing.Optional[typing.Dict[str, AttributeValue]]
def __init__( | ||
self, name: str, timestamp: int, attributes: types.Attributes = None | ||
) -> None: | ||
self._name = 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.
I'd +1 for keeping it in the API.
`SpanContext` passed as argument. | ||
""" | ||
|
||
def add_lazy_link(self, link: "Link") -> None: |
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 would almost argue that add_link should just always take a link object. Is it that much harder to do:
span.add_link(context, [attribute_1])
instead of
span.add_link(Link(context, [attribute_1]))
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, it seems a bit redundant as of now, though I'd vote for having only span.add_link(context, [attribute_1])
. I don't think I fully grasp the design of these "lazy" method yet, I can't see what's lazy about them.
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 have a strong opinion on any of both options, I just followed the specs: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#add-links, there the two methods are required add_xxx
that receives the parameters and add_lazy_xxx
that receives an object.
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.
We could optionally have only the add_link(link: Link)
method, to reduce API surface - but we won't easily be able in the future to add an overload that takes a SpanContext
+ attributes, because of the name (add_nonlazy_link()
?)
For historical reference: we added these as in Java this is a usual thing to add there (overload with different parameters, that is). We could drop this for Python if we feel like that, I feel.
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, but we could benefit from a better policy on which classes get implemented in the API vs. SDK. So far we've only implemented the classes needed to let applications pass along the trace context without taking a dependency on the SDK.
self._attributes = attributes | ||
self._timestamp = timestamp | ||
|
||
@property |
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.
Making these properties in the API means subclasses can't make them regular attributes. Is that intentional?
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.
Well, I could say it is, subclasses would have to re-implement this property if needed.
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 they could never choose to make these regular attributes. That is, you can always replace attributes with properties in a subclass, but not vice-versa.
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.
Ah, good point with the replacing! But if the derived class defines a read-only property, it would break the base class' __init__
.
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 am not totally sure I am following this conversation.
I want to go back a step first, the specifications states that Link class SHOULD be immutable, that's the reason I decided to use properties. (Maybe I should use slots too). Do you think there is a better way to make it an immutable type?
* Refactor: rename tracer to node-tracer * style: fix typo
This PR creates classes for
Link
andEvent
, also implements theadd_lazy_{event,link}
methods.Having a proper class would make it easier to extend in the future. Those classes are implemented directly in the API as they are general enough and different implementations could reuse them.
Solves #76
Solves #77