On Tue, Jan 9, 2018 at 11:02 AM, Nathaniel Smith <n...@pobox.com> wrote:
> On Mon, Jan 8, 2018 at 11:34 AM, Yury Selivanov <yselivanov...@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:
>>
>>   request_var: ContextVar[Optional[Request]] = \
>>       ContextVar('current_request')
>>
>> When we need to get the current request object, we would write:
>>
>>   request: Optional[Request] = request_var.get()
>>
>> And we'd also need to explicitly handle when 'request' is set to None.
>> Of course we could create request_var 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
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to