I had a similar recent need, with a bit more on top of it, and solved it
with this slightly insane library. (Alas, I haven't figured out a good way
to make it act as a true subtype of UnderlyingType yet)

import contextvars
from typing import Any, Generic, TypeVar

UnderlyingType = TypeVar('UnderlyingType')


class DuckTypeContextVar(Generic[UnderlyingType]):
    """A DuckTypeContextVar takes contextvars.ContextVar to the next level:
It duck-type emulates
    the underlying variable. This means you can do something like

        sys.stdout = DuckTypeContextVar('stdout', sys.stdout)
        token = sys.stdout.setLocalValue(myFile)
        ....
        sys.stdout.resetLocalValue(token)
    """

    def __init__(self, name: str, value: UnderlyingType) -> None:
        self._value = contextvars.ContextVar(name, default=value)

    def getLocalValue(self) -> UnderlyingType:
        return self._value.get()

    def setLocalValue(self, value: UnderlyingType) -> contextvars.Token:
        return self._value.set(value)

    def resetLocalValue(self, token: contextvars.Token) -> None:
        self._value.reset(token)

    def __getattr__(self, attr: str) -> Any:
        return getattr(self._value, attr)

On Wed, Jun 5, 2019 at 6:14 PM Yury Selivanov <yselivanov...@gmail.com>
wrote:

> On Wed, Jun 5, 2019 at 6:22 PM Christoph Groth <christ...@grothesque.org>
> wrote:
> [..]
> > I'm aware of this possibility, however it is not suitable for the use
> > case that I have in mind (configuration variables), because it requires
> > too much code for each variable.
> >
> > Of course one could wrap your example inside a factory function, but
> > that's unwieldy as well, since it requires keeping track of one context
> > manager for each context variable:
>
> I suggest you to open an issue on bugs.python.org to implement support
> for context manager protocol for contextvars.Token.  I'm not opposed
> to the idea.  Keep in mind that Python 3.8 is already in a feature
> freeze mode, so the earliest we can get this is Python 3.9.
>
> > By the way, contexts being implemented as immutable dictionaries implies
> > one copy of a dictionary per call to the set method.  That in turn means
> > that using hundreds of context variables (a large library could easily
> > have that many configuration variables) might not be a good idea.  Is
> > this correct?
>
> The contextvars module uses a special dictionary implementation (read
> more on that in PEP 567/550) with its ".set()" operation only slightly
> slower than updating a standard Python dict. Setting/resetting N
> context variables is about 1.5x slower than mutating a Python dict N
> times.  In short, it's a fast operation by Python standards.
>
> >
> > A final, unrelated comment: I find the use of the word "context" in the
> > standard library for both context managers and context variables (and
> > related to them "contexts") needlessly confusing.  I suggest clearly
> > explaining their independence at the top of the documentation of either
> > module.
>
> Pull requests to improve the docs are always welcome!
>
> Yury
> Python-Ideas mailing list -- python-dev(a)python.org
> To unsubscribe send an email to python-ideas-leave(a)python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>
Python-Ideas mailing list -- python-dev(a)python.org
To unsubscribe send an email to python-ideas-leave(a)python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/

Reply via email to