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

Add reset_contextvars and update bind_contextvars #339

Merged
merged 1 commit into from
Sep 19, 2021
Merged

Conversation

jab
Copy link
Contributor

@jab jab commented Jul 26, 2021

Update structlog.contextvars.bind_contextvars() to return a Mapping[str, contextvar.Token] corresponding to the results
of setting the requested contextvars.

This return value is suitable for use with the newly-added structlog.contextvars.reset_contextvars() function, which resets the requested contextvars to their previous values using the given Tokens.

Pull Request Check List

This is just a friendly reminder about the most common mistakes. Please make sure that you tick all boxes. But please read our contribution guide at least once, it will save you unnecessary review cycles!

If an item doesn't apply to your pull request, check it anyway to make it apparent that there's nothing left to do.

  • Added tests for changed code.
  • Updated documentation for changed code.
    • New functions/classes have to be added to docs/api.rst by hand.
    • Changed/added classes/methods/functions have appropriate versionadded, versionchanged, or deprecated directives. Find the appropriate next version in our __init__.py file.
  • Documentation in .rst files is written using semantic newlines.
  • Changes (and possible deprecations) are documented in the changelog.

If you have any questions to any of the points above, just submit and ask! This checklist is here to help you, not to deter you from contributing!

@hynek
Copy link
Owner

hynek commented Jul 27, 2021

I’m gonna have to ask you to write some narrative docs for contextvars.rst so I have a clue what this is about. 😅

i have to admit that my understanding of contextvars is rather superficial.

@jab jab force-pushed the reset branch 3 times, most recently from 70fa295 to 4aa209b Compare August 2, 2021 00:27
@jab
Copy link
Contributor Author

jab commented Aug 2, 2021

Done in the latest revision. Is it any clearer now?

@@ -98,16 +98,22 @@ def clear_contextvars() -> None:
k.set(Ellipsis)


def bind_contextvars(**kw: Any) -> None:
"""
def bind_contextvars(**kw: Any) -> Mapping[str, contextvars.Token]:
Copy link
Contributor Author

@jab jab Aug 2, 2021

Choose a reason for hiding this comment

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

I think for some reason, Sphinx is getting confused trying to resolve the occurrences of contextvars.Token in these typehints and docstrings, causing a spurious warning (which is getting turned into a spurious error).

I noticed the old-(pre-Sphinx-1.0)-format intersphinx_mapping was being used, so I tried updating it to the up-to-date format, but that wasn't enough to appease Sphinx. 🤷

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Meant to say, I also tried several other things (e.g. adding from contextvars import Token and changing the typehint to just Token, and when that didn't work, tried spelling these references a bunch of other ways), but nothing I tried worked, and I gave up after the better part of an hour. I wonder if it's confusing Sphinx that both structlog and the standard library have modules named contextvars?

Copy link
Owner

Choose a reason for hiding this comment

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

Unfortunately Sphinx and types is hit and miss. :(

If you can't make it work, add it to

structlog/docs/conf.py

Lines 69 to 81 in b39a720

nitpick_ignore = [
("py:class", "BinaryIO"),
("py:class", "ILogObserver"),
("py:class", "PlainFileObserver"),
("py:class", "TLLogger"),
("py:class", "TextIO"),
("py:class", "traceback"),
("py:class", "structlog._base.BoundLoggerBase"),
("py:class", "structlog.dev._Styles"),
("py:class", "structlog.types.EventDict"),
("py:obj", "sync_bl"),
("py:obj", "entries"),
]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Done in the latest revision. (After rebasing on top of latest main, I also had to add an entry for bare "TLLogger", to ignore a new warning I was getting that is unrelated to my changes.)

Copy link
Owner

@hynek hynek left a comment

Choose a reason for hiding this comment

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

Just a few stylistic changes.

Other than that, it needs a changelog entry. I would suggest some high-level description telling why ppl should be excited?

Maybe something like:

``structlog.contextvars.bind_contextvars()`` now returns a mapping of keys to ``contextvars.Token``\ s that allow to reset values using the new ``structlog.contextvars.reset_contextvars()``.

Comment on lines 67 to 72
If e.g. your request handler calls a helper function that needs to
temporarily override some contextvars before restoring them back to
their original values, you can use the :class:`~contextvars.contextvars.Token`\s
returned by :func:`~structlog.contextvars.bind_contextvars` along with
:func:`~structlog.contextvars.reset_contextvars` to accomplish this
(much like how :meth:`contextvars.ContextVar.reset` works):
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
If e.g. your request handler calls a helper function that needs to
temporarily override some contextvars before restoring them back to
their original values, you can use the :class:`~contextvars.contextvars.Token`\s
returned by :func:`~structlog.contextvars.bind_contextvars` along with
:func:`~structlog.contextvars.reset_contextvars` to accomplish this
(much like how :meth:`contextvars.ContextVar.reset` works):
If e.g. your request handler calls a helper function that needs to temporarily override some contextvars before restoring them back to their original values, you can use the :class:`~contextvars.contextvars.Token`\s returned by :func:`~structlog.contextvars.bind_contextvars` along with :func:`~structlog.contextvars.reset_contextvars` to accomplish this (much like how :meth:`contextvars.ContextVar.reset` works):

We use semantic newlines.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

(Though, as a fan of semantic line breaks myself, I was surprised to get this suggestion. I think adding additional line breaks at appropriate positions within a single sentence is actually encouraged, not forbidden. See the example at https://sembr.org starting with "We can further clarify the source text by adding a line break after the clause". This makes reading and editing docs (and diffs) easier in editors that are not set to soft-wrap long lines, as well as pagers and other tools.)

@@ -116,7 +122,20 @@ def bind_contextvars(**kw: Any) -> None:
var = contextvars.ContextVar(structlog_k, default=Ellipsis)
_CONTEXT_VARS[structlog_k] = var

var.set(v)
rv[k] = var.set(v)
return rv
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
return rv
return rv

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Comment on lines 74 to 78
bind_contextvars(a=1)
assert {"a": 1} == get_contextvars()
await event_loop.create_task(nested_coro())
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
bind_contextvars(a=1)
assert {"a": 1} == get_contextvars()
await event_loop.create_task(nested_coro())
bind_contextvars(a=1)
assert {"a": 1} == get_contextvars()
await event_loop.create_task(nested_coro())

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Comment on lines 80 to 87
assert {"a": 2, "b": 3} == get_contextvars()
reset_contextvars(**tokens)
assert {"a": 1} == get_contextvars()
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
assert {"a": 2, "b": 3} == get_contextvars()
reset_contextvars(**tokens)
assert {"a": 1} == get_contextvars()
assert {"a": 2, "b": 3} == get_contextvars()
reset_contextvars(**tokens)
assert {"a": 1} == get_contextvars()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Copy link
Contributor Author

@jab jab 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 reviewing, @hynek! I believe I've addressed all your requested changes.

Comment on lines 67 to 72
If e.g. your request handler calls a helper function that needs to
temporarily override some contextvars before restoring them back to
their original values, you can use the :class:`~contextvars.contextvars.Token`\s
returned by :func:`~structlog.contextvars.bind_contextvars` along with
:func:`~structlog.contextvars.reset_contextvars` to accomplish this
(much like how :meth:`contextvars.ContextVar.reset` works):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

(Though, as a fan of semantic line breaks myself, I was surprised to get this suggestion. I think adding additional line breaks at appropriate positions within a single sentence is actually encouraged, not forbidden. See the example at https://sembr.org starting with "We can further clarify the source text by adding a line break after the clause". This makes reading and editing docs (and diffs) easier in editors that are not set to soft-wrap long lines, as well as pagers and other tools.)

@@ -98,16 +98,22 @@ def clear_contextvars() -> None:
k.set(Ellipsis)


def bind_contextvars(**kw: Any) -> None:
"""
def bind_contextvars(**kw: Any) -> Mapping[str, contextvars.Token]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Done in the latest revision. (After rebasing on top of latest main, I also had to add an entry for bare "TLLogger", to ignore a new warning I was getting that is unrelated to my changes.)

@@ -116,7 +122,20 @@ def bind_contextvars(**kw: Any) -> None:
var = contextvars.ContextVar(structlog_k, default=Ellipsis)
_CONTEXT_VARS[structlog_k] = var

var.set(v)
rv[k] = var.set(v)
return rv
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Comment on lines 74 to 78
bind_contextvars(a=1)
assert {"a": 1} == get_contextvars()
await event_loop.create_task(nested_coro())
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Comment on lines 80 to 87
assert {"a": 2, "b": 3} == get_contextvars()
reset_contextvars(**tokens)
assert {"a": 1} == get_contextvars()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in the latest revision.

Copy link
Owner

@hynek hynek left a comment

Choose a reason for hiding this comment

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

mypy is failing:

src/structlog/contextvars.py:101: error: Missing type parameters for generic type "Token"
src/structlog/contextvars.py:130: error: Missing type parameters for generic type "Token"

I suspect (vacationing rn) you have to set it to Token[Any]?

@jab
Copy link
Contributor Author

jab commented Sep 19, 2021

@hynek should be fixed now! Thanks for catching that, and sorry I missed that when testing locally. (Each time I update this PR, the CI checks don't run until you approve running them, since this is my first structlog PR. If there were some way for you to pre-approve running the checks for future updates of this PR, I would have noticed and fixed that by the time you took your next look. Not sure if that's possible?)

Hope you've been having a great vacation!

@hynek
Copy link
Owner

hynek commented Sep 19, 2021

Sorry terse because phone:

  • any reason why running tox Locally doesn’t work for you?
  • I think we’ll have to go for a string annotation :(
  • I’m not aware of a way to pre-approve users. If you find anything lmk. But it’ll be moot once a single PR by you is merged.

structlog.contextvars.bind_contextvars() now returns a
Mapping[str, contextvar.Token] corresponding to the results
of setting the requested contextvars.

This return value is suitable for use with the newly-added
structlog.contextvars.reset_contextvars() function, which resets the
requested contextvars to their previous values using the given Tokens.
@jab
Copy link
Contributor Author

jab commented Sep 19, 2021

tox is now succeeding for me locally as of the latest revision. I think 3.6, 3.7, 3.8, 3.9, and docs should all succeed in CI once they run next.

Not sure what's going on with the required codecov status checks. Please let me know if there's anything else you need me to do about those. Thanks for reviewing!

@hynek
Copy link
Owner

hynek commented Sep 19, 2021

Quickly approved before leaving. As long as builders are failing, you can ignore codecov, because of fast-failing alone (you can see that some builders don’t even start).

@jab
Copy link
Contributor Author

jab commented Sep 19, 2021

All checks have passed

🎉

@hynek hynek merged commit 3a08abd into hynek:main Sep 19, 2021
@hynek
Copy link
Owner

hynek commented Sep 19, 2021

Thanks for your perseverance!

@jab jab deleted the reset branch September 19, 2021 17:09
@jab
Copy link
Contributor Author

jab commented Sep 19, 2021

Thanks for merging! And for structlog and your other great work!

@hynek
Copy link
Owner

hynek commented Nov 12, 2021

JFTR, I've run into contextvars.Token problems myself and it's actually a bug in CPython's docs!

So I've fixed it today: python/cpython#29533

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.

2 participants