[Python-Dev] PEP 567 pre v3 (original) (raw)

Yury Selivanov yselivanov.ml at gmail.com
Tue Jan 9 06:41:07 EST 2018


On Tue, Jan 9, 2018 at 11:02 AM, Nathaniel Smith <njs at pobox.com> wrote:

On Mon, Jan 8, 2018 at 11:34 AM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:

1. Proposal: ContextVar has default set to None.

From the typing point of view that would mean that if a context variable is declared without an explicit default, its type would be Optional. E.g. say we have a hypothetical web framework that allows to access the current request object through a context variable: _requestvar: ContextVar[Optional[Request]] = _ ContextVar('currentrequest') When we need to get the current request object, we would write: request: Optional[Request] = requestvar.get() And we'd also need to explicitly handle when 'request' is set to None. Of course we could create requestvar with its default set to some "InvalidRequest" object, but that would complicate things. It would be easier to just state that the framework always sets the current request and it's a bug if it's not set. Therefore, in my opinion, it's better to keep the current behaviour: if a context variable was created without a default value, ContextVar.get() can raise a LookupError. All the different behaviors here can work, so I don't want to make a huge deal about this. But the current behavior is bugging me, and I don't think anyone has brought up the reason why, so here goes :-). Right now, the set of valid states for a ContextVar are: it can hold any Python object, or it can be undefined. However, the only way it can be in the "undefined" state is in a new Context where it has never had a value; once it leaves the undefined state, it can never return to it.

Is "undefined" a state when a context variable doesn't have a default and isn't yet set? If so, why can't it be returned back to the "undefined" state? That's why we have the 'reset' method:

c = ContextVar('c') c.get() # LookupError

t = c.set(42) c.get() # 42 c.reset(t)

c.get() # LookupError

I don't like how context variables are defined in Option 1 and Option 2. I view ContextVars as keys in some global context mapping--akin to Python variables. Similar to how we have a NameError for variables, we have a LookupError for context variables. When we write a variable name, Python looks it up in locals and globals. When we call ContextVar.get(), Python will look up that context variable in the current Context. I don't think we should try to classify ContextVar objects as containers or something capable of holding a value on their own.

Even when you have a "del some_var" statement, you are only guaranteed to remove the "some_var" name from the innermost scope. This is similar to what ContextVar.unset() will do in PEP 568, by removing the variable only from the head of the chain.

So the sole purpose of ContextVar.default is to make ContextVar.get() convenient. Context objects don't know about ContextVar.default, and ContextVars don't know about values they are mapped to in some Context object.

In any case, at this point I think that the best option is to simply drop the "default" parameter from the ContextVar constructor. This would leave us with only one default in ContextVar.get() method:

c.get()   # Will raise a LookupError if 'c' is not set
c.get('python')  # Will return 'python' if 'c' is not set

I also now see how having two different 'default' values: one defined when a ContextVar is created, and one can be passed to ContextVar.get() is confusing.

But I'd be -1 on making all ContextVars have a None default (effectively have a "ContextVar.get(default=None)" signature. This would be a very loose semantics in my opinion.

Yury



More information about the Python-Dev mailing list