Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add contextmanager for Context #215

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

Merged
merged 8 commits into from
Oct 17, 2019
Merged

Add contextmanager for Context #215

merged 8 commits into from
Oct 17, 2019

Conversation

reyang
Copy link
Member

@reyang reyang commented Oct 11, 2019

Resolves #183.

Now it is much simpler to restore context:

from opentelemetry.context import Context

Context.a = 'a'
print(Context)
Context.b = 'b'
print(Context)
with Context.use(a='foo', b='bar'):
    print(Context)
    with Context.use(a='baz', c='baz'):
        print(Context)
print(Context)

You will get:

AsyncRuntimeContext({'a': 'a'})
AsyncRuntimeContext({'a': 'a', 'b': 'b'})
AsyncRuntimeContext({'a': 'foo', 'b': 'bar'})
AsyncRuntimeContext({'a': 'baz', 'b': 'bar', 'c': 'baz'})
AsyncRuntimeContext({'a': 'a', 'b': 'b', 'c': None})

@@ -99,6 +100,17 @@ def __getitem__(self, name: str) -> "object":
def __setitem__(self, name: str, value: "object") -> None:
self.__setattr__(name, value)

@contextmanager # type: ignore
def __call__(
self, **kwargs: typing.Dict[str, "object"]

Choose a reason for hiding this comment

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

Are the quotes around object really needed?

@mauriciovasquezbernal
Copy link
Member

Overall it looks good to me. I just have a question that's is not strictly related to this PR, please consider the following code:

from opentelemetry.context import Context

Context.a = 'a'
print(Context)
with Context(a='foo', b='bar'):
    print(Context)
print(Context)

It prints:

AsyncRuntimeContext({'a': 'a'})
AsyncRuntimeContext({'a': 'foo', 'b': 'bar'})
AsyncRuntimeContext({'a': 'a', 'b': None})

Is that correct to print 'b': None outside the context? Should we worry about the number of slots?

@reyang
Copy link
Member Author

reyang commented Oct 11, 2019

Is that correct to print 'b': None outside the context? Should we worry about the number of slots?

Good question! The current Context design is to treat non-existing value and None the same way.

Removing a slot would be a nice-to-have thing, however it comes with high cost of introducing a contention on the hot path.

Regarding the number of slots and memory consumption, we probably should be explicit that Context is similar like TLS (thread local storage), which should be properly planned instead of being used for random stuff. Thoughts?

@toumorokoshi
Copy link
Member

Overall it looks good to me. I just have a question that's is not strictly related to this PR, please consider the following code:

from opentelemetry.context import Context

Context.a = 'a'
print(Context)
with Context(a='foo', b='bar'):
    print(Context)
print(Context)

It prints:

AsyncRuntimeContext({'a': 'a'})
AsyncRuntimeContext({'a': 'foo', 'b': 'bar'})
AsyncRuntimeContext({'a': 'a', 'b': None})

Is that correct to print 'b': None outside the context? Should we worry about the number of slots?

I think that's undesired behavior. It will not have an effect in many cases I imagine, but it will definitely be confusing for the use case where None is a possible real value rather than an indicator of an absence of one.

@toumorokoshi
Copy link
Member

Sorry, just read the reply. Something you could also do is have a stacked object proxy that recurses upward, and push / pop off the stack. This would enable appropriate values for when something is found / not found, at the cost off lookup.

I guess maybe alternatively it would be fine if the caveat was documented somewhere like the docstring.

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 as Context(...) or Context.use(...). But I don't think I understand your comment about contention. Why is del self[key] significantly worse than self[key] = None?

@c24t
Copy link
Member

c24t commented Oct 14, 2019

Poking around, ContextVar supports very fast get/set/delete via this special-purpose black-magic data structure.

I think we've talked about it before, but if we're willing to drop support for other kinds of backing context we may get a much faster and simpler API by using ContextVar directly.

@Oberon00
Copy link
Member

I think with a bit more careful implementation and maybe some API changes, we could at least have our implementation have little to no overhead over ContextVar if ContextVar is used internally.

@reyang
Copy link
Member Author

reyang commented Oct 16, 2019

LGTM as Context(...) or Context.use(...). But I don't think I understand your comment about contention. Why is del self[key] significantly worse than self[key] = None?

We have a potential contention in get and set, so we don't ask for explicit slot registration (e.g. slot = ContextVar('var', default='foobar').
The optimization was to have a small lock with assumption that slot is only going to be registered, but not unregistered. In this case, if people call register_slot (directly or indirectly via get/set) in some initialization code, there is no contention at all in the hot path.

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.

After renaming __call__ -> use, I'll approve. 👌

@@ -99,6 +100,17 @@ def __getitem__(self, name: str) -> "object":
def __setitem__(self, name: str, value: "object") -> None:
self.__setattr__(name, value)

@contextmanager # type: ignore
def __call__(
self, **kwargs: typing.Dict[str, "object"]
Copy link
Member

Choose a reason for hiding this comment

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

In other words:

Suggested change
self, **kwargs: typing.Dict[str, "object"]
self, **kwargs: typing.Dict[str, object]

@carlosalberto
Copy link
Contributor

It looks great overall, going in the right direction.

@reyang reyang merged commit 5c63be7 into master Oct 17, 2019
@reyang reyang deleted the context branch October 17, 2019 15:13
c24t pushed a commit to c24t/opentelemetry-python that referenced this pull request Oct 21, 2019
srikanthccv pushed a commit to srikanthccv/opentelemetry-python that referenced this pull request Nov 1, 2020
* feat: add grpc plugin

* fix: remove test typo

* fix: nyc missing from test

* fix: linting and NodeTracer export

* chore: rename grpc test file

* test: add error code tests

* refactor: logger is given to plugin

* fix(grpc): using logger before defined

* refactor: move private statics to internal file

* test: add withSpan OK test

* refactor: follow plugin naming pattern

* refactor(tests): replace ProxyTracer

* refactor(grpc): move helper funcs to utils

* refactor(tests): use new memory exporter helper
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 ContextManager for Context
6 participants