[Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
Having survived four rounds in the boxing ring at python-ideas, PEP
572 is now ready to enter the arena of python-dev. I'll let the
proposal speak for itself. Be aware that the reference implementation
currently has a few test failures, which I'm still working on, but to
my knowledge nothing will prevent the proposal itself from being
successfully implemented.

For those who have seen the most recent iteration on -ideas, the only
actual change to the core proposal is that chaining is fully supported
now.

Formatted version:
https://www.python.org/dev/peps/pep-0572/

ChrisA

PEP: 572
Title: Assignment Expressions
Author: Chris Angelico 
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 28-Feb-2018
Python-Version: 3.8
Post-History: 28-Feb-2018, 02-Mar-2018, 23-Mar-2018, 04-Apr-2018, 17-Apr-2018


Abstract


This is a proposal for creating a way to assign to names within an expression.
Additionally, the precise scope of comprehensions is adjusted, to maintain
consistency and follow expectations.


Rationale
=

Naming the result of an expression is an important part of programming,
allowing a descriptive name to be used in place of a longer expression,
and permitting reuse.  Currently, this feature is available only in
statement form, making it unavailable in list comprehensions and other
expression contexts.  Merely introducing a way to assign as an expression
would create bizarre edge cases around comprehensions, though, and to avoid
the worst of the confusions, we change the definition of comprehensions,
causing some edge cases to be interpreted differently, but maintaining the
existing behaviour in the majority of situations.


Syntax and semantics


In any context where arbitrary Python expressions can be used, a **named
expression** can appear. This is of the form ``target := expr`` where
``expr`` is any valid Python expression, and ``target`` is any valid
assignment target.

The value of such a named expression is the same as the incorporated
expression, with the additional side-effect that the target is assigned
that value::

# Handle a matched regex
if (match := pattern.search(data)) is not None:
...

# A more explicit alternative to the 2-arg form of iter() invocation
while (value := read_next_item()) is not None:
...

# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]


Differences from regular assignment statements
--

Most importantly, since ``:=`` is an expression, it can be used in contexts
where statements are illegal, including lambda functions and comprehensions.

An assignment statement can assign to multiple targets, left-to-right::

x = y = z = 0

The equivalent assignment expression is parsed as separate binary operators,
and is therefore processed right-to-left, as if it were spelled thus::

assert 0 == (x := (y := (z := 0)))

Augmented assignment is not supported in expression form::

>>> x +:= 1
  File "", line 1
x +:= 1
^
SyntaxError: invalid syntax

Otherwise, the semantics of assignment are identical in statement and
expression forms.


Alterations to comprehensions
-

The current behaviour of list/set/dict comprehensions and generator
expressions has some edge cases that would behave strangely if an assignment
expression were to be used. Therefore the proposed semantics are changed,
removing the current edge cases, and instead altering their behaviour *only*
in a class scope.

As of Python 3.7, the outermost iterable of any comprehension is evaluated
in the surrounding context, and then passed as an argument to the implicit
function that evaluates the comprehension.

Under this proposal, the entire body of the comprehension is evaluated in
its implicit function. Names not assigned to within the comprehension are
located in the surrounding scopes, as with normal lookups. As one special
case, a comprehension at class scope will **eagerly bind** any name which
is already defined in the class scope.

A list comprehension can be unrolled into an equivalent function. With
Python 3.7 semantics::

numbers = [x + y for x in range(3) for y in range(4)]
# Is approximately equivalent to
def (iterator):
result = []
for x in iterator:
for y in range(4):
result.append(x + y)
return result
numbers = (iter(range(3)))

Under the new semantics, this would instead be equivalent to::

def ():
result = []
for x in range(3):
for y in range(4):
result.append(x + y)
return result
numbers = ()

When a class scope is involved, a naive transformation into a function would
prevent name lookups (as the function would behave like a method)::

class X:
names = 

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Jerdonek
On Tue, Apr 17, 2018 at 12:46 AM, Chris Angelico  wrote:
>
> Having survived four rounds in the boxing ring at python-ideas, PEP
> 572 is now ready to enter the arena of python-dev. I'll let the
> proposal speak for itself. Be aware that the reference implementation
> currently has a few test failures, which I'm still working on, but to
> my knowledge nothing will prevent the proposal itself from being
> successfully implemented.

Very interesting / exciting, thanks!

> Augmented assignment is not supported in expression form::
>
> >>> x +:= 1
>   File "", line 1
> x +:= 1
> ^
> SyntaxError: invalid syntax

Can you include in the PEP a brief rationale for not accepting this
form? In particular, is the intent never to support it, or is the
intent to expressly allow adding it at a later date (e.g. after
getting experience with the simpler form, etc)?

--Chris
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
On Tue, Apr 17, 2018 at 6:26 PM, Chris Jerdonek
 wrote:
> On Tue, Apr 17, 2018 at 12:46 AM, Chris Angelico  wrote:
>>
>> Having survived four rounds in the boxing ring at python-ideas, PEP
>> 572 is now ready to enter the arena of python-dev. I'll let the
>> proposal speak for itself. Be aware that the reference implementation
>> currently has a few test failures, which I'm still working on, but to
>> my knowledge nothing will prevent the proposal itself from being
>> successfully implemented.
>
> Very interesting / exciting, thanks!
>
>> Augmented assignment is not supported in expression form::
>>
>> >>> x +:= 1
>>   File "", line 1
>> x +:= 1
>> ^
>> SyntaxError: invalid syntax
>
> Can you include in the PEP a brief rationale for not accepting this
> form? In particular, is the intent never to support it, or is the
> intent to expressly allow adding it at a later date (e.g. after
> getting experience with the simpler form, etc)?

Sure. There are a few reasons; and Imaybe the best place to explain
them is in the rejecteds section.

Augmented assignment is currently all of these:

augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
'<<=' | '>>=' | '**=' | '//=')

I'm actually not sure whether the augmented-assignment-expression
operators should be "+:=" or ":+=", but either way, it'd be another
thirteen tokens, some of which would be *four* character tokens.
That's an awful lot of extra tokens. There's a lot less value in
chaining on top of a "+=" than a simple assignment, and it doesn't
seem right to have tons of code to support that. (Consider how much
extra code there is to support async functions, and then imagine that
you have a similarly large amount of effort for something that's of
far lesser significance.)

Another reason is that it can be confusing (to a human, not to the
parser) that it would be the result of the augmented assignment, not
the original expression, that would carry through. With regular
assignment (whether it's to a simple name or to a
subscript/attribute), removing the "target :=" part will leave you
with the same value - the value of "x := 1" is 1. With augmented, the
only logical way to do it would be to re-capture the left operand.
Consider:

x = 5
print(x +:= 2)

Logically, this has to set x to 7, then print 7. But in more
complicated situations, it can get confusing more easily than direct
assignment does. Consider, for instance, augmented addition on a list,
or similar.

It could potentially be added later, but I'm not working hard to keep
the option open or anything. Basically it's just "outside the scope of
this document".

ChrisA
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread David Mertz
Strongly agree with Nick that only simple name targets should be permitted
(at least initially). NONE of the motivating cases use more complex
targets, and allowing them encourages obscurity and code golf.

On Tue, Apr 17, 2018, 8:20 AM Nick Coghlan  wrote:

> On 17 April 2018 at 17:46, Chris Angelico  wrote:
> > Syntax and semantics
> > 
> >
> > In any context where arbitrary Python expressions can be used, a **named
> > expression** can appear. This is of the form ``target := expr`` where
> > ``expr`` is any valid Python expression, and ``target`` is any valid
> > assignment target.
>
> The "assignment expressions should be restricted to names only"
> subthread from python-ideas finally crystallised for me (thanks in
> part to your own comment that 'With regular assignment (whether it's
> to a simple name or to a subscript/attribute), removing the "target
> :=" part will leave you with the same value - the value of "x := 1" is
> 1.'), and I now have a concrete argument for why I think we want to
> restrict the assignment targets to names only: all complex assignment
> targets create inherent ambiguity around the type of the expression
> result, and exactly which operations are performed as part of the
> assignment.
>
> Initially I thought the problem was specific to tuple unpacking
> syntax, but attempting to explain why subscript assignment and
> attribute assignments were OK made me realise that they're actually
> even worse off (since they can execute arbitrary code on both setting
> and retrieval, whereas tuple unpacking only iterates over iterables).
>
> Tackling those in order...
>
> Tuple unpacking:
>
> What's the result type for "a, b, c := range(3)"? Is it a range()
> object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
> 3)" or "(a, b, range(3))"?
> Once you have your answer, what about "a, b, c := iter(range(3))"
> or "a, b, *c := range(10)"?
>
> Whichever answers we chose would be surprising at least some of the
> time, so it seems simplest to disallow such ambiguous constructs, such
> that the only possible interpretation is as "(a, b, range(3))"
>
> Subscript assignment:
>
> What's the final value of "result" in "seq = list(); result =
> (seq[:] := range(3))"? Is it "range(3)"? Or is it "[1, 2, 3]"?
> As for tuple unpacking, does your preferred answer change for the
> case of "seq[:] := iter(range(3))"?
>
> More generally, if I write  "container[k] := value", does only
> "type(container).__setitem__" get called, or does
> "type(container).__getitem__" get called as well?
>
> Again, this seems inherently ambiguous to me, and hence best avoided
> (at least for now), such that the result is always unambiguously
> "range(3)".
>
> Attribute assignment:
>
> If I write  "obj.attr := value", does only "type(obj).__setattr__"
> get called, or does "type(obj).__getattribute__" get called as well?
>
> While I can't think of a simple obviously ambiguous example using
> builtins or the standard library, result ambiguity exists even for the
> attribute access case, since type or value coercion may occur either
> when setting the attribute, or when retrieving it, so it makes a
> difference as to whether a reference to the right hand side is passed
> through directly as the assignment expression result, or if the
> attribute is stored and then retrieved again.
>
> If all these constructs are prohibited, then a simple design principle
> serves to explain both their absence and the absence of the augmented
> assignment variants: "allowing the more complex forms of assignment as
> expressions makes the order of operations (as well as exactly which
> operations are executed) inherently ambiguous".
>
> That ambiguity generally doesn't exist with simple name bindings (I'm
> excluding execution namespaces with exotic binding behaviour from
> consideration here, as the consequences of trying to work with those
> are clearly on the folks defining and using them).
>
> > The value of such a named expression is the same as the incorporated
> > expression, with the additional side-effect that the target is assigned
> > that value::
> >
> > # Handle a matched regex
> > if (match := pattern.search(data)) is not None:
> > ...
> >
> > # A more explicit alternative to the 2-arg form of iter() invocation
> > while (value := read_next_item()) is not None:
> > ...
> >
> > # Share a subexpression between a comprehension filter clause and
> its output
> > filtered_data = [y for x in data if (y := f(x)) is not None]
>
> [snip]
>
> > Style guide recommendations
> > ===
> >
> > As this adds another way to spell some of the same effects as can
> already be
> > done, it is worth noting a few broad recommendations. These could be
> included
> > in PEP 8 and/or other style guides.
> >
> > 1. If either assignment statements or assignment expressions can be
> >used, 

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Nick Coghlan
On 17 April 2018 at 17:46, Chris Angelico  wrote:
> Syntax and semantics
> 
>
> In any context where arbitrary Python expressions can be used, a **named
> expression** can appear. This is of the form ``target := expr`` where
> ``expr`` is any valid Python expression, and ``target`` is any valid
> assignment target.

The "assignment expressions should be restricted to names only"
subthread from python-ideas finally crystallised for me (thanks in
part to your own comment that 'With regular assignment (whether it's
to a simple name or to a subscript/attribute), removing the "target
:=" part will leave you with the same value - the value of "x := 1" is
1.'), and I now have a concrete argument for why I think we want to
restrict the assignment targets to names only: all complex assignment
targets create inherent ambiguity around the type of the expression
result, and exactly which operations are performed as part of the
assignment.

Initially I thought the problem was specific to tuple unpacking
syntax, but attempting to explain why subscript assignment and
attribute assignments were OK made me realise that they're actually
even worse off (since they can execute arbitrary code on both setting
and retrieval, whereas tuple unpacking only iterates over iterables).

Tackling those in order...

Tuple unpacking:

What's the result type for "a, b, c := range(3)"? Is it a range()
object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
3)" or "(a, b, range(3))"?
Once you have your answer, what about "a, b, c := iter(range(3))"
or "a, b, *c := range(10)"?

Whichever answers we chose would be surprising at least some of the
time, so it seems simplest to disallow such ambiguous constructs, such
that the only possible interpretation is as "(a, b, range(3))"

Subscript assignment:

What's the final value of "result" in "seq = list(); result =
(seq[:] := range(3))"? Is it "range(3)"? Or is it "[1, 2, 3]"?
As for tuple unpacking, does your preferred answer change for the
case of "seq[:] := iter(range(3))"?

More generally, if I write  "container[k] := value", does only
"type(container).__setitem__" get called, or does
"type(container).__getitem__" get called as well?

Again, this seems inherently ambiguous to me, and hence best avoided
(at least for now), such that the result is always unambiguously
"range(3)".

Attribute assignment:

If I write  "obj.attr := value", does only "type(obj).__setattr__"
get called, or does "type(obj).__getattribute__" get called as well?

While I can't think of a simple obviously ambiguous example using
builtins or the standard library, result ambiguity exists even for the
attribute access case, since type or value coercion may occur either
when setting the attribute, or when retrieving it, so it makes a
difference as to whether a reference to the right hand side is passed
through directly as the assignment expression result, or if the
attribute is stored and then retrieved again.

If all these constructs are prohibited, then a simple design principle
serves to explain both their absence and the absence of the augmented
assignment variants: "allowing the more complex forms of assignment as
expressions makes the order of operations (as well as exactly which
operations are executed) inherently ambiguous".

That ambiguity generally doesn't exist with simple name bindings (I'm
excluding execution namespaces with exotic binding behaviour from
consideration here, as the consequences of trying to work with those
are clearly on the folks defining and using them).

> The value of such a named expression is the same as the incorporated
> expression, with the additional side-effect that the target is assigned
> that value::
>
> # Handle a matched regex
> if (match := pattern.search(data)) is not None:
> ...
>
> # A more explicit alternative to the 2-arg form of iter() invocation
> while (value := read_next_item()) is not None:
> ...
>
> # Share a subexpression between a comprehension filter clause and its 
> output
> filtered_data = [y for x in data if (y := f(x)) is not None]

[snip]

> Style guide recommendations
> ===
>
> As this adds another way to spell some of the same effects as can already be
> done, it is worth noting a few broad recommendations. These could be included
> in PEP 8 and/or other style guides.
>
> 1. If either assignment statements or assignment expressions can be
>used, prefer statements; they are a clear declaration of intent.
>
> 2. If using assignment expressions would lead to ambiguity about
>execution order, restructure it to use statements instead.
>
> 3. Chaining multiple assignment expressions should generally be avoided.
>More than one assignment per expression can detract from readability.

Given the many different uses for ":" identified on python-ideas, I'm
inclined to suggest making these proposed style guidelines more

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Paul Moore
On 17 April 2018 at 14:01, David Mertz  wrote:
> Strongly agree with Nick that only simple name targets should be permitted
> (at least initially). NONE of the motivating cases use more complex targets,
> and allowing them encourages obscurity and code golf.

I also agree. Originally I would have said why not allow them, it's a
potentially useful generalisation. But Nick's examples pretty clearly
demonstrate that there are a lot of unclear edge cases involved, and
even though "prevent people writing ugly code" is explicitly stated as
a non-goal in the PEP, that doesn't mean it's OK to allow an obvious
bug magnet with no clear use cases.

Paul
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Ethan Furman

On 04/17/2018 12:46 AM, Chris Angelico wrote:


PEP: 572
Title: Assignment Expressions
Author: Chris Angelico 


+1

Thanks for all the hard work, Chris!  Hoping this PEP finally breaks your 
streak!  ;)

--
~Ethan~

___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
On Tue, Apr 17, 2018 at 10:17 PM, Nick Coghlan  wrote:
> Initially I thought the problem was specific to tuple unpacking
> syntax, but attempting to explain why subscript assignment and
> attribute assignments were OK made me realise that they're actually
> even worse off (since they can execute arbitrary code on both setting
> and retrieval, whereas tuple unpacking only iterates over iterables).
>
> Tackling those in order...
>
> Tuple unpacking:
>
> What's the result type for "a, b, c := range(3)"? Is it a range()
> object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
> 3)" or "(a, b, range(3))"?
> Once you have your answer, what about "a, b, c := iter(range(3))"
> or "a, b, *c := range(10)"?

This is one that I didn't originally think about, and when I first
tried it out a couple of weeks ago, I decided against mentioning it
either way, because I've no idea what _should_ be done. But here's
what my reference implementation does:

>>> x = (a,b,c := range(3))
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'a' is not defined

In other words, it's being parsed as:

x = (a, b, (c := range(3)))

Forcing the interpretation does work:

>>> x = ((a,b,c) := range(3))

And then the value of the expression is the same object that just got
assigned (or in this case, unpacked):

>>> x
range(0, 3)

That's true even if it's an exhausted iterator:

>>> x = ((a,b,c) := iter(range(3)))
>>> x

>>> next(x)
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

The way it works is that the RHS gets evaluated, then that gets put on
ice for a moment, and the assignment done with a copy of it. (In the
CPython reference implementation, "put on ice" is done with DUP_TOP.)
So the value that got assigned - even if that assignment involved
unpacking a sequence - is still passed through the assignment and out
the other side.

>>> dis.dis("x = ((a,b,c) := range(3))")
### evaluate RHS
  1   0 LOAD_NAME0 (range)
  2 LOAD_CONST   0 (3)
  4 CALL_FUNCTION1
### grab another reference to the range
  6 DUP_TOP
### do the assignment
  8 UNPACK_SEQUENCE  3
 10 STORE_NAME   1 (a)
 12 STORE_NAME   2 (b)
 14 STORE_NAME   3 (c)
### carry on with the rest of the expression
 16 STORE_NAME   4 (x)
 18 LOAD_CONST   1 (None)
 20 RETURN_VALUE

> Whichever answers we chose would be surprising at least some of the
> time, so it seems simplest to disallow such ambiguous constructs, such
> that the only possible interpretation is as "(a, b, range(3))"

Yeah, that's what happens. Tuple display is defined as "test , test"
and a 'test' can be 'target := value', so each element of a tuple can
have an assignment expression in it. If you actually want to unpack
inside an assignment expression, you need parentheses.

> Subscript assignment:
>
> What's the final value of "result" in "seq = list(); result =
> (seq[:] := range(3))"? Is it "range(3)"? Or is it "[1, 2, 3]"?
> As for tuple unpacking, does your preferred answer change for the
> case of "seq[:] := iter(range(3))"?

It's range(3), and no, my preferred answer doesn't change. It'll
probably never be useful to unpack an iterator in an assignment
expression (since you'll always get an exhausted iterator at the end
of it), but I'm sure there'll be uses for unpacking iterables.

> More generally, if I write  "container[k] := value", does only
> "type(container).__setitem__" get called, or does
> "type(container).__getitem__" get called as well?

Only setitem. If you like, imagine the := operator as a tee junction:
you "tap off" the pipe and snag it with assignment, and also keep
using it just as if the assignment hadn't been there.

> Again, this seems inherently ambiguous to me, and hence best avoided
> (at least for now), such that the result is always unambiguously
> "range(3)".
>
> Attribute assignment:
>
> If I write  "obj.attr := value", does only "type(obj).__setattr__"
> get called, or does "type(obj).__getattribute__" get called as well?

I didn't change anything about how assignment actually works, so I
would expect it to be exactly the same semantics as
statement-assignment has. Let's test.

>>> class X:
...   @property
...   def spam(self): return 42
...   @spam.setter
...   def spam(self, val): print("Setting spam to", val)
...
>>> x = X()
>>> dis.dis("(x.spam := 7)")
  1   0 LOAD_CONST   0 (7)
  2 DUP_TOP
  4 LOAD_NAME0 (x)
  6 STORE_ATTR   1 (spam)
  8 RETURN_VALUE
>>> (x.spam := 7)
Setting spam to 7
7

Looks good to me. If I had to choose semantics, I don't think this
would be a bad choice; and for something that derived naturally from a
basic "let's just copy in 

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Ethan Furman

On 04/17/2018 07:01 AM, Chris Angelico wrote:

On Tue, Apr 17, 2018 at 10:17 PM, Nick Coghlan wrote:



That ambiguity generally doesn't exist with simple name bindings (I'm
excluding execution namespaces with exotic binding behaviour from
consideration here, as the consequences of trying to work with those
are clearly on the folks defining and using them).


The cool thing about the simple and naive code is that even those
should work. I don't have an example ready for demo, but I fully
expect that it would 'just work' the exact same way; the namespace
would never be retrieved from, only set to.

Hmm. I don't know what the consequences would be on class namespace
with a non-vanilla dict. Probably functionally identical. But there
might be some extremely weird cases if the namespace dict accepts
setitem and then raises KeyError for that key.


If you want to play with a non-standard (okay, wierd) class namespaces, you can try using the assignment expression in 
an Enum class.


--
~Ethan~

___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Ethan Furman

On 04/17/2018 06:26 AM, Paul Moore wrote:


I should also point out that I remain -0 on this proposal (I'd already
said this on python-ideas, but I should probably repeat it here). For
me, the use cases are mostly marginal, and the major disadvantage is
in having two forms of assignment. Explaining to a beginner why we use
a := b in an expression, but a = b in a statement is going to be a
challenge.


I don't see the challenge:  They are different because '=' came first, but using '=' in an expression is a common source 
of bugs, so there we use ':=' instead.


--
~Ethan~
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Steve Dower
Agree with Paul. The PEP is well thought out and well presented, but I really 
don’t think we need this in Python (and I say this as someone who uses it 
regularly in C/C#).

-1 on the idea; no disrespect intended toward to people who did a lot of work 
on it.

Top-posted from my Windows phone

From: Paul Moore
Sent: Tuesday, April 17, 2018 6:31
To: David Mertz
Cc: Nick Coghlan; Python-Dev
Subject: Re: [Python-Dev] PEP 572: Assignment Expressions

On 17 April 2018 at 14:07, Paul Moore  wrote:
> On 17 April 2018 at 14:01, David Mertz  wrote:
>> Strongly agree with Nick that only simple name targets should be permitted
>> (at least initially). NONE of the motivating cases use more complex targets,
>> and allowing them encourages obscurity and code golf.
>
> I also agree. Originally I would have said why not allow them, it's a
> potentially useful generalisation. But Nick's examples pretty clearly
> demonstrate that there are a lot of unclear edge cases involved, and
> even though "prevent people writing ugly code" is explicitly stated as
> a non-goal in the PEP, that doesn't mean it's OK to allow an obvious
> bug magnet with no clear use cases.

I should also point out that I remain -0 on this proposal (I'd already
said this on python-ideas, but I should probably repeat it here). For
me, the use cases are mostly marginal, and the major disadvantage is
in having two forms of assignment. Explaining to a beginner why we use
a := b in an expression, but a = b in a statement is going to be a
challenge.

The fact that the PEP needs a section covering all the style guide
warnings we feel are needed seems like it's a warning bell, too.

Paul
___
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/steve.dower%40python.org

___
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


[Python-Dev] Basic test to validate win10 install?

2018-04-17 Thread Martin Gainty
Martin Gainty has shared a OneDrive file with you. To view it, click the link 
below.



[https://r1.res.office365.com/owa/prem/images/dc-generic_20.png]

errors 3.lst





i ran python test suite here and attaching output for your purview..

displaying output from stderr


Python36>python -m test
== CPython 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit 
(AMD64)]
== Windows-10-10.0.16299-SP0 little-endian
== cwd: .\test_python_6296
== CPU count: 4
== encodings: locale=cp1252, FS=utf-8
Run tests sequentially
0:00:00 [  1/406] test_grammar
0:00:00 [  2/406] test_opcodes
0:00:00 [  3/406] test_dict
0:00:00 [  4/406] test_builtin
0:00:00 [  5/406] test_exceptions
0:00:02 [  6/406] test_types
0:00:02 [  7/406] test_unittest
0:00:04 [  8/406] test_doctest
0:05:41 [  9/406] test_doctest2 -- test_doctest passed in 5 min 37 sec
0:05:41 [ 10/406] test_support
0:05:41 [ 11/406] test___all__
0:05:57 [ 12/406] test___future__
0:07:30 [ 13/406] test__locale -- test___future__ passed in 1 min 34 sec
0:07:30 [ 14/406] test__opcode
0:07:31 [ 15/406] test__osx_support
0:07:31 [ 16/406] test_abc
0:07:31 [ 17/406] test_abstract_numbers
0:07:31 [ 18/406] test_aifc
0:07:31 [ 19/406] test_argparse
0:07:33 [ 20/406] test_array
0:07:37 [ 21/406] test_asdl_parser
test_asdl_parser skipped -- test irrelevant for an installed Python
0:07:37 [ 22/406] test_ast -- test_asdl_parser skipped
0:07:39 [ 23/406] test_asyncgen
0:08:01 [ 24/406] test_asynchat
0:08:03 [ 25/406] test_asyncio
0:08:53 [ 26/406] test_asyncore -- test_asyncio passed in 50 sec
0:11:38 [ 27/406] test_atexit -- test_asyncore passed in 2 min 45 sec
0:11:39 [ 28/406] test_audioop
0:11:39 [ 29/406] test_augassign
0:11:39 [ 30/406] test_base64
0:11:40 [ 31/406] test_baseexception
0:11:40 [ 32/406] test_bigaddrspace
0:11:40 [ 33/406] test_bigmem
0:11:40 [ 34/406] test_binascii
0:11:40 [ 35/406] test_binhex
0:11:40 [ 36/406] test_binop
0:11:40 [ 37/406] test_bisect
0:11:40 [ 38/406] test_bool
0:11:40 [ 39/406] test_buffer
0:11:47 [ 40/406] test_bufio
0:11:52 [ 41/406] test_bytes
0:11:54 [ 42/406] test_bz2
0:12:01 [ 43/406] test_calendar
0:12:05 [ 44/406] test_call
0:12:05 [ 45/406] test_capi
0:12:09 [ 46/406] test_cgi
0:12:09 [ 47/406] test_cgitb
0:12:10 [ 48/406] test_charmapcodec
0:12:10 [ 49/406] test_class
0:12:10 [ 50/406] test_cmath
0:12:10 [ 51/406] test_cmd
0:12:10 [ 52/406] test_cmd_line
0:12:14 [ 53/406] test_cmd_line_script
0:12:20 [ 54/406] test_code
0:12:20 [ 55/406] test_code_module
0:12:20 [ 56/406] test_codeccallbacks
0:12:20 [ 57/406] test_codecencodings_cn
0:12:21 [ 58/406] test_codecencodings_hk
0:12:21 [ 59/406] test_codecencodings_iso2022
0:12:21 [ 60/406] test_codecencodings_jp
0:12:22 [ 61/406] test_codecencodings_kr
0:12:22 [ 62/406] test_codecencodings_tw
0:12:22 [ 63/406] test_codecmaps_cn
0:12:24 [ 64/406] test_codecmaps_hk
0:12:25 [ 65/406] test_codecmaps_jp
0:12:28 [ 66/406] test_codecmaps_kr
0:12:30 [ 67/406] test_codecmaps_tw
0:12:31 [ 68/406] test_codecs
0:12:33 [ 69/406] test_codeop
0:12:33 [ 70/406] test_collections
0:12:34 [ 71/406] test_colorsys
0:12:34 [ 72/406] test_compare
0:12:34 [ 73/406] test_compile
0:12:34 [ 74/406] test_compileall
0:12:44 [ 75/406] test_complex
0:12:44 [ 76/406] test_concurrent_futures
0:14:00 [ 77/406] test_configparser -- test_concurrent_futures passed in 1 min 
16 sec
0:14:01 [ 78/406] test_contains
0:14:01 [ 79/406] test_contextlib
0:14:01 [ 80/406] test_copy
0:14:02 [ 81/406] test_copyreg
0:14:02 [ 82/406] test_coroutines
0:14:02 [ 83/406] test_cprofile
0:14:03 [ 84/406] test_crashers
0:14:03 [ 85/406] test_crypt
test_crypt skipped -- No module named '_crypt'
0:14:03 [ 86/406] test_csv -- test_crypt skipped
0:14:03 [ 87/406] test_ctypes
0:14:05 [ 88/406] test_curses
test_curses skipped -- Use of the 'curses' resource not enabled
0:14:05 [ 89/406] test_datetime -- test_curses skipped (resource denied)
0:14:06 [ 90/406] test_dbm
0:14:06 [ 91/406] test_dbm_dumb
0:14:07 [ 92/406] test_dbm_gnu
test_dbm_gnu skipped -- No module named '_gdbm'
0:14:07 [ 93/406] test_dbm_ndbm -- test_dbm_gnu skipped
test_dbm_ndbm skipped -- No module named '_dbm'
0:14:07 [ 94/406] test_decimal -- test_dbm_ndbm skipped
0:14:14 [ 95/406] test_decorators
0:14:14 [ 96/406] test_defaultdict
0:14:14 [ 97/406] test_deque
0:14:17 [ 98/406] test_descr
0:14:19 [ 99/406] test_descrtut
0:14:19 [100/406] test_devpoll
test_devpoll skipped -- test works only on Solaris OS family
0:14:19 [101/406] test_dict_version -- test_devpoll skipped
0:14:19 [102/406] test_dictcomps
0:14:19 [103/406] test_dictviews
0:14:20 [104/406] test_difflib
0:14:20 [105/406] test_dis
0:14:21 [106/406] test_distutils

.\test_python_6296>exit 1

.\test_python_6296>exit 0
Warning -- files was modified by test_distutils
  Before: []
  After:  ['_configtest.c']
test test_distutils failed -- multiple errors 

Re: [Python-Dev] Basic test to validate win10 install?

2018-04-17 Thread Zachary Ware
Hi Martin,

On Mon, Apr 16, 2018 at 5:00 PM, Martin Gainty  wrote:
> I was told the python -m test are for devs only to test "auxiliary
> functions"
> do you have a "basic test" i can use to validate Python36 has installed
> properly on Win10?

The fact that the tests run at all show that everything installed
properly.  The three failures that you see don't look serious, and are
probably only failing due to differences in the development and
installation environments on Windows.  One of these days I hope to
have a bulidbot that automatically installs and tests Python on every
commit on Windows, but we're not there yet.  Until then, investigating
these failures and contributing patches to fix them could be a great
way to get involved in CPython development!

Regards,
-- 
Zach
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Eric Fahlgren
On Tue, Apr 17, 2018 at 2:23 AM, Chris Angelico  wrote:

> Augmented assignment is currently all of these:
>
> augassign: ('+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' |
> '<<=' | '>>=' | '**=' | '//=')
>
> I'm actually not sure whether the augmented-assignment-expression
> operators should be "+:=" or ":+=", but either way, it'd be another
> thirteen tokens, some of which would be *four* character tokens.
>

​Or simply rework the augmented assignment's semantics to become expression
operators without any syntactic changes.  Since there's no bug magnet
arising in the usual context where '=' and '==' get confused:

> if x += 1 < 2:
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Glenn Linderman

On 4/17/2018 12:46 AM, Chris Angelico wrote:

 gen = (x for x in rage(10)) # NameError
 gen = (x for x in 10) # TypeError (not iterable)
 gen = (x for x in range(1/0)) # Exception raised during evaluation

This brings such generator expressions in line with a simple translation to
function form::

 def ():
 for x in rage(10):
 yield x
 gen = () # No exception yet
 tng = next(gen) # NameError

Detecting these errors more quickly is nontrivial. It is, however, the exact
same problem as generator functions currently suffer from, and this proposal
brings the genexp in line with the most natural longhand form.


Open questions
==

Can the outermost iterable still be evaluated early?


As of Python 3.7, the outermost iterable in a genexp is evaluated early, and
the result passed to the implicit function as an argument.  With PEP 572, this
would no longer be the case. Can we still, somehow, evaluate it before moving
on? One possible implementation would be::

 gen = (x for x in rage(10))
 # translates to
 def ():
 iterable = iter(rage(10))
 yield None
 for x in iterable:
 yield x
 gen = ()
 next(gen)



I think "rage" is supposed to be "range" in four places in the quoted block.
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Jeroen Demeyer

On 2018-04-18 02:13, Chris Angelico wrote:

I'm much happier promoting a full-featured assignment expression than
something that can only be used in a limited set of situations. Is
there reason to believe that extensions to the := operator might take
it in a different direction? If not, there's very little to lose by
permitting any assignment target, and then letting style guides frown
on it if they like.


This is a very good argument: why artificially restrict the operator?

This reminds me of the artificial restriction of decorator syntax (why 
is @foo()() not legal?). There was never a rationale given for that and 
now we are stuck with it.


___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Paul Moore
On 17 April 2018 at 14:07, Paul Moore  wrote:
> On 17 April 2018 at 14:01, David Mertz  wrote:
>> Strongly agree with Nick that only simple name targets should be permitted
>> (at least initially). NONE of the motivating cases use more complex targets,
>> and allowing them encourages obscurity and code golf.
>
> I also agree. Originally I would have said why not allow them, it's a
> potentially useful generalisation. But Nick's examples pretty clearly
> demonstrate that there are a lot of unclear edge cases involved, and
> even though "prevent people writing ugly code" is explicitly stated as
> a non-goal in the PEP, that doesn't mean it's OK to allow an obvious
> bug magnet with no clear use cases.

I should also point out that I remain -0 on this proposal (I'd already
said this on python-ideas, but I should probably repeat it here). For
me, the use cases are mostly marginal, and the major disadvantage is
in having two forms of assignment. Explaining to a beginner why we use
a := b in an expression, but a = b in a statement is going to be a
challenge.

The fact that the PEP needs a section covering all the style guide
warnings we feel are needed seems like it's a warning bell, too.

Paul
___
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


Re: [Python-Dev] Basic test to validate win10 install?

2018-04-17 Thread Terry Reedy

On 4/16/2018 6:00 PM, Martin Gainty wrote:

I was told the python -m test are for devs only to test "auxiliary 
functions"


People say all sorts of nonsense.  I don't know what 'auxiliary 
function' would mean in this context.  The Windows installer optionally 
installs the tests so that users can run them.


do you have a "basic test" i can use to validate Python36 has installed 
properly on Win10?


If clicking the Python 3.6 Python 3.6 Start Menu entry or entering 
'python' or in particular 'py -3.6' at a command prompt brings up the 
3.6 interactive interpreter, the installation is 'proper' unless a 
particular file has an error.


For a recent StackOverflow question, a strange error in lib.idlelib.rpc 
was fixed by re-installing (whereas the IDLE editor worked), so I 
suspect a disk error.  I wonder if the installer could do a better job 
of verifying the download and then the installation of each file.


--
Terry Jan Reedy


___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
On Wed, Apr 18, 2018 at 5:28 AM, Tim Peters  wrote:
> I'll channel that Guido would be happiest if this rule were followed:
>
> Given an assignment statement using "=", the meaning is the same if
> "=" is replaced with ":=".

That's broadly the intention. At the moment, there are two exceptions:

1) Augmented assignment isn't a thing
2) Chained assignment isn't a thing, which means that the assignments
operate right-to-left
2a) Assignment

> In particular, the expression at the far right is evaluated once, and
> - in case of chained assignments - is applied in turn to each target
> left-to-right.

I'll toy with this and see if I can implement it sanely. If so,
that'll eliminate one more distinction.

> Otherwise the semantics of "=" and ":=" can be very different indeed.

TBH, the common cases won't actually be much affected. You give this example:

k := container[k] := value

but that's not going to be more common. What I'm more likely to see is
something like this:

k, container[k] = new_key(), new_value()

which can instead be written:

container[k := new_key()] = new_value()

and is, IMO, clearer that way.

> So, then, e.g., and assuming the rule above always applies:
>
> [Nick]
>> Tuple unpacking:
>>
>> What's the result type for "a, b, c := range(3)"? Is it a range()
>> object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
>> 3)" or "(a, b, range(3))"?
>
> It's the range object range(3).  Same as in:
>
> x = a, b, c = range(3)
>
> `x` is bound to the range object range(3).

At the moment, "x = a, b, c := range(3)" will set c to range(3), then
build a tuple of that with the existing values of a and b. You can,
however, parenthesize the (a, b, c) part, and then it'll behave as you
say.

>> Whichever answers we chose would be surprising at least some of the
>> time, so it seems simplest to disallow such ambiguous constructs, such
>> that the only possible interpretation is as "(a, b, range(3))"
>
> That's why Guido would be happiest with the rule at the top.  "The
> answers" can already be surprising at times with current assignment
> statements, but they are well defined.  It would be mondo bonkers to
> make up entirely different subtle answers ;-)

Wholeheartedly agreed.

ChrisA
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Tim Peters
I'll channel that Guido would be happiest if this rule were followed:

Given an assignment statement using "=", the meaning is the same if
"=" is replaced with ":=".

In particular, the expression at the far right is evaluated once, and
- in case of chained assignments - is applied in turn to each target
left-to-right.

Otherwise the semantics of "=" and ":=" can be very different indeed.

So, then, e.g., and assuming the rule above always applies:

[Nick]
> Tuple unpacking:
>
> What's the result type for "a, b, c := range(3)"? Is it a range()
> object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
> 3)" or "(a, b, range(3))"?

It's the range object range(3).  Same as in:

x = a, b, c = range(3)

`x` is bound to the range object range(3).


> Once you have your answer, what about "a, b, c := iter(range(3))"

A range_iterator object, same as what `x` is bound to in:

x = a, b, c = iter(range(3))

However, `list(x)` then returns an empty list, because iter(range(3))
was evaluated only once, and the iterator was run to exhaustion when
unpacking it for the `a, b, c` target.

> or "a, b, *c := range(10)"?

The range object range(10).


> Whichever answers we chose would be surprising at least some of the
> time, so it seems simplest to disallow such ambiguous constructs, such
> that the only possible interpretation is as "(a, b, range(3))"

That's why Guido would be happiest with the rule at the top.  "The
answers" can already be surprising at times with current assignment
statements, but they are well defined.  It would be mondo bonkers to
make up entirely different subtle answers ;-)


> Subscript assignment:
>
> What's the final value of "result" in "seq = list(); result =
> (seq[:] := range(3))"? Is it "range(3)"? Or is it "[1, 2, 3]"?

As above, it's range(3).


> As for tuple unpacking, does your preferred answer change for the
> case of "seq[:] := iter(range(3))"?

As above, a range_iterator object, but one that's already been run to
exhaustion.


> More generally, if I write  "container[k] := value", does only
> "type(container).__setitem__" get called, or does
> "type(container).__getitem__" get called as well?

The rule at the top implies __setitem_ is called once, and __getitem__
not at all.  The value of the assignment is the object  `value` was
bound to at the start, regardless of how tricky __setitem__ may be.
And in

k := container[k] := value

`k` is bound to `value` before `container[k]` is evaluated.  Why?
Because that's how assignment _statements_ have always worked.


> Attribute assignment:
>
> If I write  "obj.attr := value", does only "type(obj).__setattr__"
> get called, or does "type(obj).__getattribute__" get called as well?

As above, only __setattr__.


> While I can't think of a simple obviously ambiguous example using
> builtins or the standard library, result ambiguity exists even for the
> attribute access case, since type or value coercion may occur either
> when setting the attribute, or when retrieving it, so it makes a
> difference as to whether a reference to the right hand side is passed
> through directly as the assignment expression result, or if the
> attribute is stored and then retrieved again.

This is already defined for assignment statements.  While the PEP
doesn't say "and the same for assignment expressions", my guess is
that it won't be accepted unless it does.

Or, indeed, the target is limited to a name.  But Guido wasn't keen on that.

In short, I think the PEP's chance of acceptance increases the _more_
assignment expressions act like assignment statements, not the less,
and is highest if they act exactly the same (except for returning a
value; e.g., while

>>> a = 3

at a shell displays nothing,

>>> a := 3

should display 3).
___
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


Re: [Python-Dev] Basic test to validate win10 install?

2018-04-17 Thread Martin Gainty

 Please feel free to reply to
python-dev@python.org rather than me personally to take this back to
the list.

On Tue, Apr 17, 2018 at 1:20 PM, Martin Gainty  wrote:
> I'll need a few specifics before i wandering into "dll hell"
>
> .NetFramework version?
> RT Library version? in the old days we used to call this msvcrt.dll
>
> msvc or mingw compiler?
> if msvc which version msvc?
> if mingw which version msvc ?

Have a look at https://devguide.python.org/ to get started
Python Developer’s Guide — Python Developer's 
Guide
devguide.python.org
Contributing¶. We encourage everyone to contribute to Python and that’s why we 
have put up this developer’s guide. If you still have questions after reviewing 
the material in this guide, then the Core Python Mentorship group is available 
to help guide new contributors through the process.



contributing to CPython, and in particular
https://devguide.python.org/setup/#windows for getting set up on
Windows.

To answer your questions in brief, though: we use the latest MSVC
compiler available with Visual Studio 2017, which links to the
"universal CRT" and avoids most of the old headaches of
incompatibilities between the runtimes used by different compiler
versions.

You may also be interested in the core-mentorship mailing list,
details about which can be found at
https://mail.python.org/mm3/mailman3/lists/core-mentorship.python.org/

Regards,
--
Zach
___
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


[Python-Dev] https://bugs.python.org/issue33127 breaks pip / easy_install / pipenv etc in corporate networks on MS Windows using self-signed certificates

2018-04-17 Thread Oleg Sivokon
It is common practice in corporate networks that connect MS Windows machines to 
redirect all (encrypted included) traffic through company's router.  For this 
purpose routers are usually configured to act as a CA.  However, the 
certificate issued by such "CA" will of course not be found in the certificates 
distributed with LibreSSL (how would they even know?).  MS Windows networking, 
however, has a way to configure these policies.

Prior to this issue, Python relied on the OS libraries to implement TLS 
protocol, so the overall setup worked transparently for users.  Since 3.6.5, 
however, this is no longer possible (requires alteration of certificates 
distributed with Python).

I'm asking that this be made configurable / possible to disable using simple 
means, perhaps an environment variable / registry key or similar.

PS. I still cannot register to the bug tracker (never received a confirmation 
email), this is why you are reading this email.

- Best.

Oleg
This communication and all information contained in or attached to it is 
confidential, intended solely for the addressee, may be legally privileged and 
is the intellectual property of one of the companies of NEX Group plc ("NEX") 
or third parties. If you are not the intended addressee or receive this message 
in error, please immediately delete all copies of it and notify the sender. We 
have taken precautions to minimise the risk of transmitting software viruses, 
but we advise you to carry out your own virus checks on any attachments. We do 
not accept liability for any loss or damage caused by software viruses. NEX 
reserves the right to monitor all communications. We do not accept any legal 
responsibility for the content of communications, and no communication shall be 
considered legally binding. Furthermore, if the content of this communication 
is personal or unconnected with our business, we accept no liability or 
responsibility for it. NEX Group plc is a public limited company regi
 stered in England and Wales under number 10013770 and certain of its 
affiliates are authorised and regulated by regulatory authorities. For further 
regulatory information please see www.NEX.com.
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Yury Selivanov
Hi Chris,

Thank you for working on this PEP!  Inline assignments is a long
requested feature and this seems to be the first serious attempt
at adding it.

That said I'm very -1 on the idea.


1. I switch between Python / JavaScript / C frequently (although
I code in Python 70% of my time.)  C and JS have inline
assignments but I don't find myself using them often.

JavaScript has inline assignments and they are useful to get the
match object after applying a regex. You use the same example
in your PEP.  But in my experience, this is the only common pattern
in JavaScript.  I don't see people using inline assignments for
anything else, at least it's not a common pattern.

C is low-level and has no exceptions. It uses function return values
to signal if there was an error or not. It's a popular pattern to call
a function from an 'if' statement like this: "if ((int ret = func()))" to
save a line of code.  If we ignore this particular pattern, we see that
inline assignment isn't used that often.

In your PEP you use comprehensions and regex match object to
show how inline assignment can simplify the code. In my experience,
comprehensions that are a little more complex than
"(f(x) for x in something)" are always better being rewritten to an
expanded form.  I don't find "stuff = [[y := f(x), x/y] for x in range(5)]"
very readable, and yes, I think that the simple expanded version
of this comprehension is better.

Using inline assignments in "while" statements is neat, but how
often do we use "while" statements?


2. We all try to follow the Python zen when we are designing new
language features.  With the exception of string formatting Python
follows the "There should be one-- and preferably only one --
obvious way to do it." line.  Your PEP, in my personal opinion,
goes agains this one, and also a few other lines.


I simply don't see a very compelling use case to have two forms
of assignment in Python.  It does complicate the grammar by adding
a new operator, it invites people to write more complex code, and it
has only a couple good use cases.


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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Paul Moore
On 17 April 2018 at 16:12, Ethan Furman  wrote:
> On 04/17/2018 06:26 AM, Paul Moore wrote:
>
>> I should also point out that I remain -0 on this proposal (I'd already
>> said this on python-ideas, but I should probably repeat it here). For
>> me, the use cases are mostly marginal, and the major disadvantage is
>> in having two forms of assignment. Explaining to a beginner why we use
>> a := b in an expression, but a = b in a statement is going to be a
>> challenge.
>
>
> I don't see the challenge:  They are different because '=' came first, but
> using '=' in an expression is a common source of bugs, so there we use ':='
> instead.

I fully expect the question "so if I want to assign to a variable, why
shouldn't I just use x := 12 (as a statement)?" I don't have a good
answer for that. And if I can't come up with a convincing reply to
that, the next question will likely be "so why does = exist at all?"

If we want to change Python so that assignments are expressions, and
assignment statements only remain for backward compatibility, then
fine - we should propose that (I'd probably be against it, BTW, but
I'd reserve judgement until I saw a proper proposal) but this PEP
would need a lot of changes if it were to go down that route. This
half-way house of having both seems like it will just confuse people.

Paul
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Larry Hastings



On 04/17/2018 06:55 AM, Steve Dower wrote:


Agree with Paul. The PEP is well thought out and well presented, but I 
really don’t think we need this in Python (and I say this as someone 
who uses it regularly in C/C#).


-1 on the idea; no disrespect intended toward to people who did a lot 
of work on it.




Well put!  I feel the same, and yes that includes being -1 on the proposal.

Best wishes,


//arry/
___
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


Re: [Python-Dev] Translating sample programs in documentation

2018-04-17 Thread Xuan Wu
Hi Erik, thanks a lot for the support. As mentioned in the thread in 
Doc-Sig list, IMO it can be great showcase to use identifiers in native 
languages, which PEP 3131 was designed for initially, while even now 
many beginners don't even know it's possible.


Besides, thanks to the references from that thread, it seems some change 
in Sphinx config is needed before the programs can be translated. Help 
or pointers are greatly appreciated, as I'm brand new to the 
documentation system. I've setup translation system locally and got a 
testing Chinese version up and running, but haven't got far on Sphinx 
config.


Last but not least, please let me know if it is OK to continue this 
topic here, especially the technical details.


Best wishes,

Xuan.


On 4/16/18 5:35 AM, Erik Bray wrote:

On Mon, Apr 16, 2018 at 4:49 AM, Shell Xu  wrote:

Well, I'm not sure weather or not this is what you're looking for, but pep-8
(https://www.python.org/dev/peps/pep-0008/) suggest like this:

For Python 3.0 and beyond, the following policy is prescribed for the
standard library (see PEP 3131): All identifiers in the Python standard
library MUST use ASCII-only identifiers, and SHOULD use English words
wherever feasible (in many cases, abbreviations and technical terms are used
which aren't English). In addition, string literals and comments must also
be in ASCII. The only exceptions are (a) test cases testing the non-ASCII
features, and (b) names of authors. Authors whose names are not based on the
Latin alphabet (latin-1, ISO/IEC 8859-1 character set) MUST provide a
transliteration of their names in this character set.

So, I guess translate symbols to Chinese are not gonna help reader to figure
out what kind of code should they writing...


That only applies to the Python stdlib itself.  It's a feature that
Python allows unicode identifiers, and there's nothing about that
against PEP-8 for sure.

I think it's a great idea; I'm not sure how it works out technically
in terms of providing .po files for .rst documentation or if there's
some better mechanism for that...

Best,
E



On Mon, Apr 16, 2018 at 12:41 AM, Xuan Wu
 wrote:

Excuse me if this was discussed before, but in French and Japanese
translations, all the sample programs seem to have identifiers in English
still. According to "PEP 545 -- Python Documentation Translations", as I
understand .po files are used for translations. May I ask if there's
technical restrictions causing translations being only applied to the text
parts?

For example, here's the first sample program in 4.2:


# Measure some strings:

... words = ['cat', 'window', 'defenestrate']

for w in words:

... print(w, len(w))
...
cat 3
window 6
defenestrate 12

Here's a possible translation in Chinese:


# 丈量一些字符串

... 词表 = ['猫', '窗户', '丢出窗户']

for 词 in 词表:

... print(词, len(词))
...
猫 1
窗户 2
丢出窗户 4

As you may notice the strings differ in size if they are translated
directly. Obviously that does add extra burden to review the new sample
programs to assure effectiveness and readability.
Any suggestion or comments are welcome.


Thanks,
Xuan.

___
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/shell909090%40gmail.com




--
彼節者有間,而刀刃者無厚;以無厚入有間,恢恢乎其於游刃必有餘地矣。
blog: http://shell909090.org/
twitter: @shell909090
about.me: http://about.me/shell909090

___
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/erik.m.bray%40gmail.com



___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Terry Reedy

On 4/17/2018 1:03 PM, Yury Selivanov wrote:


Using inline assignments in "while" statements is neat, but how
often do we use "while" statements?


Beginners commonly write game and entry loops and commonly stumble over 
the need to write an infinite loop and a half.  The evidence is on both 
python-list and StackOverflow.  Reducing 4 lines to 1 here reduces 
complexity and, I think, increases clarity.



2. We all try to follow the Python zen when we are designing new
language features.  With the exception of string formatting Python
follows the "There should be one-- and preferably only one --
obvious way to do it." line.


There is currently no way, not one way, to bind a name to an expression 
for use within the same statement.  I recommended to Chris that he 
remove from the PEP everything but this.  No other targets.  No 
chaining. (And no redefining class-scope comprehensions.)  This would 
remove all but the unavoidable overlap and reduce the added complexity 
of what people could write.



I simply don't see a very compelling use case to have two forms
of assignment in Python.


We already have more than two ;-).


--
Terry Jan Reedy

___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Tim Peters
[Tim]
>> I'll channel that Guido would be happiest if this rule were followed:
>>
>> Given an assignment statement using "=", the meaning is the same if
>> "=" is replaced with ":=".

[Chris]
> That's broadly the intention. At the moment, there are two exceptions:
>
> 1) Augmented assignment isn't a thing

Doesn't have to be :-)  "Augmented assignment statement" is already a
different thing than "assignment statement" (for example, in an
augmented assignment statement, there is no chaining, and the sole
target can' t be, e.g., a slice or any form of unpacking syntax).


> 2) Chained assignment isn't a thing, which means that the assignments
> operate right-to-left

>> In particular, the expression at the far right is evaluated once, and
>> - in case of chained assignments - is applied in turn to each target
>> left-to-right.

> I'll toy with this and see if I can implement it sanely. If so,
> that'll eliminate one more distinction.

>> Otherwise the semantics of "=" and ":=" can be very different indeed.

> TBH, the common cases won't actually be much affected.

Or at all!  That's not the point here, though:  if making assignment
expressions work as exactly like assignment statements as possible is
what's needed for the PEP to pass, it's the _annoying_ cases that have
to be looked at.

Personally, after considerable staring at my own code, I would be
perfectly happy to settle for assignment expressions no fancier than

identifier ":=" expression

That alone covers over 99% of the cases I'd be tempted to use the new
feature at all, and then gobs of general-case assignment-statement
difficulties go away, including the "right-to-left or left-to-right?"
distinction (there's no way to tell which order bindings happen in `x
:= y := z := 3` short of staring at the generated code).

But so far I haven't gotten the impression that Guido is fond of that.
He should be, though ;-)


> You give this example:
>
> k := container[k] := value
>
> but that's not going to be more common. What I'm more likely to see is
> something like this:

Not about what's common, but about the full range of what's possible to express.

...

[Nick]
>>> Tuple unpacking:
>>>
>>> What's the result type for "a, b, c := range(3)"? Is it a range()
>>> object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2,
>>> 3)" or "(a, b, range(3))"?

>> It's the range object range(3).  Same as in:
>>
>> x = a, b, c = range(3)
>>
>> `x` is bound to the range object range(3).

> At the moment, "x = a, b, c := range(3)" will set c to range(3), then
> build a tuple of that with the existing values of a and b. You can,
> however, parenthesize the (a, b, c) part, and then it'll behave as you
> say.

Which would be really annoying to "repair".


>>> Whichever answers we chose would be surprising at least some of the
>>> time, so it seems simplest to disallow such ambiguous constructs, such
>>> that the only possible interpretation is as "(a, b, range(3))"

>> That's why Guido would be happiest with the rule at the top.  "The
>> answers" can already be surprising at times with current assignment
>> statements, but they are well defined.  It would be mondo bonkers to
>> make up entirely different subtle answers ;-)

> Wholeheartedly agreed.

I'd like Guido to chime in again, because I'm pretty sure he won't
accept what's currently on the table.  There are two plausible ways to
repair that:

1. Continue down the road of making assignment expressions "exactly
like" assignment statements in their full generality.

2. Back off and limit assignment expressions to what appears to be the
overwhelmingly most common case motivated by looking at real code (as
opposed to constructing examples to illustrate pitfalls &
obscurities):

identifier ":=" expression
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Terry Reedy

On 4/17/2018 3:46 AM, Chris Angelico wrote:


Abstract


This is a proposal for creating a way to assign to names within an expression.


I started at -something as this is nice but not necessary.  I migrated 
to +something for the specific, limited proposal you wrote above: 
expressions of the form "name := expression".



Additionally, the precise scope of comprehensions is adjusted, to maintain
consistency and follow expectations.


We fiddled with comprehension scopes, and broke some code, in 3.0.  I 
oppose doing so again.  People expect their 3.x code to continue working 
in future versions.  Breaking that expectation should require 
deprecation for at least 2 versions.



Rationale
=

Naming the result of an expression is an important part of programming,
allowing a descriptive name to be used in place of a longer expression,
and permitting reuse.


Right.  In other words, "name := expression".


Merely introducing a way to assign as an expression
would create bizarre edge cases around comprehensions, though, and to avoid
the worst of the confusions, we change the definition of comprehensions,
causing some edge cases to be interpreted differently, but maintaining the
existing behaviour in the majority of situations.


If it is really true that introducing 'name := expression' requires such 
a side-effect, then I might oppose it.




Syntax and semantics


In any context where arbitrary Python expressions can be used, a **named
expression** can appear. This is of the form ``target := expr`` where
``expr`` is any valid Python expression, and ``target`` is any valid
assignment target.


This generalization is different from what you said in the abstract and 
rationale.  No rationale is given.  After reading Nick's examination of 
the generalization, and your response, -1.



The value of such a named expression is the same as the incorporated
expression, with the additional side-effect that the target is assigned
that value::


As someone else noted, you only use names as targets, thus providing no 
rationale for anything else.



 # Handle a matched regex
 if (match := pattern.search(data)) is not None:
 ...

 # A more explicit alternative to the 2-arg form of iter() invocation
 while (value := read_next_item()) is not None:
 ...


To me, being able to name and test expressions fits with Python names 
not being typed.  To me, usage such as the above is the justification 
for the limited proposal.



 # Share a subexpression between a comprehension filter clause and its 
output
 filtered_data = [y for x in data if (y := f(x)) is not None]


And this is secondary.


Differences from regular assignment statements
--

Most importantly, since ``:=`` is an expression, it can be used in contexts
where statements are illegal, including lambda functions and comprehensions.

An assignment statement can assign to multiple targets, left-to-right::

 x = y = z = 0


This is a bad example as there is very seldom a reason to assign 
multiple names, as opposed to multiple targets.  Here is a typical real 
example.


self.x = x = expression
# Use local x in the rest of the method.

In "x = y = 0", x and y likely represent two *different* concepts 
(variables) that happen to be initialized with the same value.  One 
could instead write "x,y = 0,0".



The equivalent assignment expression


should be a syntax error.


is parsed as separate binary operators,


':=' is not a binary operator, any more than '=' is, as names, and 
targets in general, are not objects.  Neither fetch and operate on the 
current value, if any, of the name or target.  Therefore neither has an 
'associativity'.



and is therefore processed right-to-left, as if it were spelled thus::

 assert 0 == (x := (y := (z := 0)))


Parentheses should be required, to maintain the syntax "name := expression".


Augmented assignment is not supported in expression form::

 >>> x +:= 1
   File "", line 1
 x +:= 1
 ^
 SyntaxError: invalid syntax


I would have expected :+=, but agree with the omission.


Otherwise, the semantics of assignment are identical in statement and
expression forms.


Mostly replacing '=' with ':=' is a different proposal and a different 
goal than naming expressions within an expression for reuse (primarily) 
within the expression (including compound expressions).


Proposing an un-augmentable, un-chainable, name_only := expression 
expression would not be duplicating assignment statements.



Alterations to comprehensions
-

The current behaviour of list/set/dict comprehensions and generator
expressions has some edge cases that would behave strangely if an assignment
expression were to be used.


You have not shown this.  Your examples do not involve assignment 
expressions, and adding them should make no difference.  Changing the 
scoping 

Re: [Python-Dev] https://bugs.python.org/issue33127 breaks pip / easy_install / pipenv etc in corporate networks on MS Windows using self-signed certificates

2018-04-17 Thread Steve Dower

On 17Apr2018 0246, Oleg Sivokon wrote:

It is common practice in corporate networks that connect MS Windows machines to redirect 
all (encrypted included) traffic through company's router.  For this purpose routers are 
usually configured to act as a CA.  However, the certificate issued by such 
"CA" will of course not be found in the certificates distributed with LibreSSL 
(how would they even know?).  MS Windows networking, however, has a way to configure 
these policies.

Prior to this issue, Python relied on the OS libraries to implement TLS 
protocol, so the overall setup worked transparently for users.  Since 3.6.5, 
however, this is no longer possible (requires alteration of certificates 
distributed with Python).


If you are referring to Python on Windows, this was never true. We've 
always relied on OpenSSL and at best will read locally installed 
certificates (and by default, most certificates are not locally 
installed). This should not have changed recently, and certainly not 
with the bug you reference.



I'm asking that this be made configurable / possible to disable using simple 
means, perhaps an environment variable / registry key or similar.


I'm not clear on what you're asking for. The only thing we can disable 
is reading OS certificates into OpenSSL, and that would be the opposite 
of what you are having trouble with.


Perhaps this is an issue with pip more specifically than Python?


PS. I still cannot register to the bug tracker (never received a confirmation 
email), this is why you are reading this email.


I would guess it ended up in a junk mail folder, though that may be 
controlled by your organization rather than anywhere you can get to it. 
Perhaps using an alternate email address will be easiest?


Cheers,
Steve
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Eric Snow
On Tue, Apr 17, 2018 at 7:55 AM, Steve Dower  wrote:
> Agree with Paul. The PEP is well thought out and well presented, but I
> really don’t think we need this in Python (and I say this as someone who
> uses it regularly in C/C#).
>
> -1 on the idea; no disrespect intended toward to people who did a lot of
> work on it.

Same here.  I'm more interested in having anonymous blocks (i.e.
scopes), along the lines of PEP 3150, though it's currently #2 on my
wish list. :)

-eric
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Steven D'Aprano
On Wed, Apr 18, 2018 at 10:13:58AM +1000, Chris Angelico wrote:

[regarding comprehensions]

> The changes here are only to edge and corner cases, other than as they
> specifically relate to assignment expressions. The current behaviour
> is intended to "do the right thing" according to people's
> expectations, and it largely does so; those cases are not changing.
> For list comprehensions at global or function scope, the ONLY case
> that can change (to my knowledge) is where you reuse a variable name:
> 
> [t for t in t.__parameters__ if t not in tvars]
> 
> This works in 3.7 but will fail easily and noisily (UnboundLocalError)
> with PEP 572.

That's a major semantic change, and the code you show is no better or 
worse than:

t = ...
result = []
for t in t.parameters:
if t not in tvars:
result.append(t)


which is allowed. I think you need a better justification for breaking 
it than merely the opinion:

> IMO this is a poor way to write a loop,

Reusing names is permitted in Python. If you're going to break code, 
that surely needs a future import or deprecation period. As you say 
yourself, you've already found one example in the standard library that 
will break.


> and the fact
> that it "happened to work" is on par with code that depended on dict
> iteration order in Python 3.2 and earlier.

I don't think that's justified. As far as I can tell, the fact that it 
works is not a mere accident of implementation but a consequence of the 
promised semantics of comprehensions and Python's scoping rules.

If that's not the case, I think you need to justify exactly why it isn't 
guaranteed.


> Yes, the implementation is
> well defined, but since you can achieve exactly the same thing by
> picking a different variable name, it's better to be clear.

Ah, but the aim of the PEP is not to prohibit ugly or unclear code.


> Note that the second of the open questions would actually return this
> to current behaviour, by importing the name 't' into the local scope.

Indeed. Maybe this needs to stop being an open question and become a 
settled question.

 
> The biggest semantic change is to the way names are looked up at class
> scope. Currently, the behaviour is somewhat bizarre unless you think
> in terms of unrolling a loop *as a function*; there is no way to
> reference names from the current scope, and you will instead ignore
> the surrounding class and "reach out" into the next scope outwards
> (probably global scope).
> 
> Out of all the code in the stdlib, the *only* one that needed changing
> was in Lib/typing.py, where the above comprehension was found. (Not
> counting a couple of unit tests whose specific job is to verify this
> behaviour.)

If there are tests which intentionally verify this behaviour, that 
really hurts your position that the behaviour is an accident of 
implementation. It sounds like the behaviour is intended and required.


> The potential for breakage is extremely low. Non-zero, but
> far lower than the cost of introducing a new keyword, for instance,
> which is done without deprecation cycles.

Which new keywords are you thinking of? The most recent new keywords I 
can think of were "True/False", "as" and "with".

- True, False became keywords in 3.x during the "breaking code 
  is allowed" 2 -> 3 transition;

- "as" became a keyword in 2.6 following a deprecation period in 2.5:

py> as = 1
:1: Warning: 'as' will become a reserved keyword in Python 2.6

- and "with" needed a __future__ import.

Have I missed any?


-- 
Steve
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Tim Peters
[Guido, makes peace with `identifier := expression`]
> ...
> I am fine with this, it certainly seems the easiest to implement, with the
> fewest corner cases, and the easiest restriction to explain.
>
> (I was thinking there would be a use case for basic tuple unpacking, like
> seen a lot in for-loop, but the only examples I tried to come up with were
> pretty sub-optimal, so I don't worry about that any more.)

Chris's pain threshold appears to be higher than ours ;-)

So I would really like to see if anyone has plausibly realistic uses
for fancier forms of assignment expression.

I have plenty of code that does stuff like this:

while True:
x, y = func_returning_tuple()
if y is None:
break
...

Maybe it's just that I'm used to it, but I find that very easy to
understand now.  If we had fancy assignment expressions, my first
thought was I could write it like so instead:

while ((x, y) := func_returning_tuple()) and y is not None:
...

and pray that I put in enough parens to get the intended meaning.

And maybe it's just that I'm _not_ used to that, but I do find it
harder to understand.  Contributing factor:  I don't really want "and"
there - what the context requires is really more like C's comma
operator (take only the last value from a sequence of expressions).
As is, I'm relying on that a 2-tuple is truthy regardless of its
content (so that `and` always goes on to evaluate its RHS).

And, for some reason, I find this even worse:

while ((x, y) := func_returning_tuple())[1] is not None:
...

The rub there:  I gave `y` a name but can't use it in the test?!


And those are the same kinds of headaches I saw over & over in my own
"fancier" code:  stuff that's already perfectly clear would become
more obscure instead.

Tuple unpacking works great in for-loops because the only effect there
is to give names to the tuple components, none of which are needed
_in_ the `for` statement itself.  But in a `while" or `if` statement,
I would typically _also_ want to use the names _in_ the `while` or
`if` tests.  But, as in C, that's what the comma operator is for, not
the assignment operator.

while (s = function_returning_struct_with_x_and_y_members(), s.y != NULL) {
...
}

In contrast, ,many plausible uses I saw for `identifier := expression`
in a `while` or `if` statement would have been improvements, and most
of the rest neutral:  I'm still wondering whether this one is better
or worse ;-):

def newton(f, fprime, x):
import math
while not math.isclose((next_x := x - f(x) / fprime(x)), x):
x = next_x
return next_x
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
On Wed, Apr 18, 2018 at 11:20 AM, Steven D'Aprano  wrote:
> On Wed, Apr 18, 2018 at 10:13:58AM +1000, Chris Angelico wrote:
>
> [regarding comprehensions]
>
>> The changes here are only to edge and corner cases, other than as they
>> specifically relate to assignment expressions. The current behaviour
>> is intended to "do the right thing" according to people's
>> expectations, and it largely does so; those cases are not changing.
>> For list comprehensions at global or function scope, the ONLY case
>> that can change (to my knowledge) is where you reuse a variable name:
>>
>> [t for t in t.__parameters__ if t not in tvars]
>>
>> This works in 3.7 but will fail easily and noisily (UnboundLocalError)
>> with PEP 572.
>
> That's a major semantic change, and the code you show is no better or
> worse than:
>
> t = ...
> result = []
> for t in t.parameters:
> if t not in tvars:
> result.append(t)
>
>
> which is allowed. I think you need a better justification for breaking
> it than merely the opinion:
>
>> IMO this is a poor way to write a loop,

Ah but that isn't what the list comp is equivalent to. If you want to
claim that "for t in t.parameters" is legal, you first have to assume
that you're overwriting t, not shadowing it. In the list comp as it is
today, the "for t in" part is inside an implicit nested function, but
the "t.parameters" part is outside that function.

Try this instead:

t = ...
def listcomp():
result = []
for t in t.parameters:
if t not in tvars:
result.append(t)
return result
listcomp()

Except that it isn't that either, because the scope isn't quite that
clean. It actually involves a function parameter, and the iterator is
fetched before it's passed as a parameter, and then NOT fetched inside
the loop. So you actually can't write perfectly equivalent longhand.

PEP 572 will *reduce* the edge cases and complexity.

>> Note that the second of the open questions would actually return this
>> to current behaviour, by importing the name 't' into the local scope.
>
> Indeed. Maybe this needs to stop being an open question and become a
> settled question.

Okay. The question is open if you wish to answer it. Are you happy
with the extra complexity that this would entail? Discuss.

>> The biggest semantic change is to the way names are looked up at class
>> scope. Currently, the behaviour is somewhat bizarre unless you think
>> in terms of unrolling a loop *as a function*; there is no way to
>> reference names from the current scope, and you will instead ignore
>> the surrounding class and "reach out" into the next scope outwards
>> (probably global scope).
>>
>> Out of all the code in the stdlib, the *only* one that needed changing
>> was in Lib/typing.py, where the above comprehension was found. (Not
>> counting a couple of unit tests whose specific job is to verify this
>> behaviour.)
>
> If there are tests which intentionally verify this behaviour, that
> really hurts your position that the behaviour is an accident of
> implementation. It sounds like the behaviour is intended and required.

These changes also broke some tests of disassembly, which quote the
exact bytecode created for specific pieces of code. Does that mean
that we can't change anything? They're specifically verifying
(asserting) the behaviour that currently exists.

I've never tried to claim that this is going to have no impact. Of
course it will change things. Otherwise why have a PEP?

>> The potential for breakage is extremely low. Non-zero, but
>> far lower than the cost of introducing a new keyword, for instance,
>> which is done without deprecation cycles.
>
> Which new keywords are you thinking of? The most recent new keywords I
> can think of were "True/False", "as" and "with".

async, await? They became "soft keywords" and then full keywords, but
that's not exactly a deprecation period.

rosuav@sikorsky:~$ python3.5 -c "await = 1; print(await)"
1
rosuav@sikorsky:~$ python3.6 -c "await = 1; print(await)"
1
rosuav@sikorsky:~$ python3.7 -c "await = 1; print(await)"
  File "", line 1
await = 1; print(await)
  ^
SyntaxError: invalid syntax

The shift from 3.6 to 3.7 breaks any code that uses 'async' or 'await'
as an identifier. And that kind of breakage CAN happen in a minor
release. Otherwise, it would be virtually impossible to improve
anything in the language.

PEP 572 will make changes. The result of these changes will be fewer
complex or unintuitive interactions between different features in
Python, most notably comprehensions/genexps and class scope. It also
makes the transformation from list comp to external function more
accurate and easier to understand. For normal usage, the net result
will be the same, but the differences are visible if you actually
probe for them.

ChrisA
___
Python-Dev mailing list
Python-Dev@python.org

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Rob Cliffe via Python-Dev



On 17/04/2018 15:01, Chris Angelico wrote:

On Tue, Apr 17, 2018 at 10:17 PM, Nick Coghlan  wrote:


Style guide recommendations
===

As this adds another way to spell some of the same effects as can already be
done, it is worth noting a few broad recommendations. These could be included
in PEP 8 and/or other style guides.

1. If either assignment statements or assignment expressions can be
used, prefer statements; they are a clear declaration of intent.

2. If using assignment expressions would lead to ambiguity about
execution order, restructure it to use statements instead.

3. Chaining multiple assignment expressions should generally be avoided.
More than one assignment per expression can detract from readability.

Given the many different uses for ":" identified on python-ideas, I'm
inclined to suggest making these proposed style guidelines more
prescriptive (at least initially) by either:

1. Listing out specific approved unambiguous use cases (i.e. if
statement conditions, while loop conditions, list comprehensions,
generation expressions)
2. Making the 3rd admonition more general by advising against using
":" for more than one purpose in the same expression (i.e. don't
combine assignment expressions with slicing syntax, lambda
expressions, function headers, variable annotations, dict or set
displays, dict or set comprehensions)

I'm actually dubious about the third point as it stands.
I'm more than dubious - I disagree with Nick on this point.  It is 
already possible to have multiple uses of ":" in an expression; surely 
we wouldn't advise that such existing code should be changed, in cases 
where it is arises naturally and is genuinely useful.

Best wishes
Rob Cliffe
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Chris Angelico
On Wed, Apr 18, 2018 at 7:53 AM, Terry Reedy  wrote:
> On 4/17/2018 3:46 AM, Chris Angelico wrote:
>
>> Abstract
>> 
>>
>> This is a proposal for creating a way to assign to names within an
>> expression.
>
>
> I started at -something as this is nice but not necessary.  I migrated to
> +something for the specific, limited proposal you wrote above: expressions
> of the form "name := expression".
>
>> Additionally, the precise scope of comprehensions is adjusted, to maintain
>> consistency and follow expectations.
>
>
> We fiddled with comprehension scopes, and broke some code, in 3.0.  I oppose
> doing so again.  People expect their 3.x code to continue working in future
> versions.  Breaking that expectation should require deprecation for at least
> 2 versions.

The changes here are only to edge and corner cases, other than as they
specifically relate to assignment expressions. The current behaviour
is intended to "do the right thing" according to people's
expectations, and it largely does so; those cases are not changing.
For list comprehensions at global or function scope, the ONLY case
that can change (to my knowledge) is where you reuse a variable name:

[t for t in t.__parameters__ if t not in tvars]

This works in 3.7 but will fail easily and noisily (UnboundLocalError)
with PEP 572. IMO this is a poor way to write a loop, and the fact
that it "happened to work" is on par with code that depended on dict
iteration order in Python 3.2 and earlier. Yes, the implementation is
well defined, but since you can achieve exactly the same thing by
picking a different variable name, it's better to be clear.

Note that the second of the open questions would actually return this
to current behaviour, by importing the name 't' into the local scope.

The biggest semantic change is to the way names are looked up at class
scope. Currently, the behaviour is somewhat bizarre unless you think
in terms of unrolling a loop *as a function*; there is no way to
reference names from the current scope, and you will instead ignore
the surrounding class and "reach out" into the next scope outwards
(probably global scope).

Out of all the code in the stdlib, the *only* one that needed changing
was in Lib/typing.py, where the above comprehension was found. (Not
counting a couple of unit tests whose specific job is to verify this
behaviour.) The potential for breakage is extremely low. Non-zero, but
far lower than the cost of introducing a new keyword, for instance,
which is done without deprecation cycles.

>> Merely introducing a way to assign as an expression
>> would create bizarre edge cases around comprehensions, though, and to
>> avoid
>> the worst of the confusions, we change the definition of comprehensions,
>> causing some edge cases to be interpreted differently, but maintaining the
>> existing behaviour in the majority of situations.
>
>
> If it is really true that introducing 'name := expression' requires such a
> side-effect, then I might oppose it.

It's that comprehensions/genexps are currently bizarre, only people
don't usually notice it because there aren't many ways to recognize
the situation. Introducing assignment expressions will make the
existing weirdnesses more visible.

>> Syntax and semantics
>> 
>>
>> In any context where arbitrary Python expressions can be used, a **named
>> expression** can appear. This is of the form ``target := expr`` where
>> ``expr`` is any valid Python expression, and ``target`` is any valid
>> assignment target.
>
>
> This generalization is different from what you said in the abstract and
> rationale.  No rationale is given.  After reading Nick's examination of the
> generalization, and your response, -1.

Without trying it or looking up any reference documentation, can you
tell me whether these statements are legal?

with open(f) as self.file: pass

try: pass
except Exception as self.exc: pass

The rationale for assignment to arbitrary targets is the same as for
assigning to names: it's useful to be able to assign as an expression.

>> Differences from regular assignment statements
>> --
>>
>> Most importantly, since ``:=`` is an expression, it can be used in
>> contexts
>> where statements are illegal, including lambda functions and
>> comprehensions.
>>
>> An assignment statement can assign to multiple targets, left-to-right::
>>
>>  x = y = z = 0
>
>
> This is a bad example as there is very seldom a reason to assign multiple
> names, as opposed to multiple targets.  Here is a typical real example.
>
> self.x = x = expression
> # Use local x in the rest of the method.
>
> In "x = y = 0", x and y likely represent two *different* concepts
> (variables) that happen to be initialized with the same value.  One could
> instead write "x,y = 0,0".

Personally, if I need to quickly set a bunch of things to zero or
None, I'll use chained assignment. But sure. If you want to, you can

Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread MRAB

On 2018-04-17 22:53, Terry Reedy wrote:

On 4/17/2018 3:46 AM, Chris Angelico wrote:

[snip]



Augmented assignment is not supported in expression form::

 >>> x +:= 1
   File "", line 1
 x +:= 1
 ^
 SyntaxError: invalid syntax


I would have expected :+=, but agree with the omission.

x = x op 1 is abbreviated to x op= 1, so x := x op 1 would be 
abbreviated to x op:= 1. That's what's used in the Icon language.


[snip]
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Guido van Rossum
On Tue, Apr 17, 2018 at 3:23 PM, Tim Peters  wrote:

> [Tim]
> >> I'll channel that Guido would be happiest if this rule were followed:
> >>
> >> Given an assignment statement using "=", the meaning is the same if
> >> "=" is replaced with ":=".
>

Thanks for channeling me. :=)

I'd like Guido to chime in again, because I'm pretty sure he won't
> accept what's currently on the table.  There are two plausible ways to
> repair that:
>
> 1. Continue down the road of making assignment expressions "exactly
> like" assignment statements in their full generality.
>
> 2. Back off and limit assignment expressions to what appears to be the
> overwhelmingly most common case motivated by looking at real code (as
> opposed to constructing examples to illustrate pitfalls &
> obscurities):
>
> identifier ":=" expression
>

I haven't had the time to follow this thread in detail; fortunately I don't
have to because of Tim's excellent channeling.

I am fine with this, it certainly seems the easiest to implement, with the
fewest corner cases, and the easiest restriction to explain.

(I was thinking there would be a use case for basic tuple unpacking, like
seen a lot in for-loop, but the only examples I tried to come up with were
pretty sub-optimal, so I don't worry about that any more.)

-- 
--Guido van Rossum (python.org/~guido)
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Eric Fahlgren
On Tue, Apr 17, 2018 at 6:20 PM, Steven D'Aprano 
wrote:

> If there are tests which intentionally verify this behaviour, that
> really hurts your position that the behaviour is an accident of
> implementation. It sounds like the behaviour is intended and required.
>

​It is nonetheless bizarre and unexpected behavior.

>>> prefix = 'global'
>>> [prefix+c for c in 'abc']
['globala', 'globalb', 'globalc']

>>> def func():
... prefix = 'local'
... print([prefix+c for c in 'abc'])
>>> func()
['locala', 'localb', 'localc']

>>> class klass:
... prefix = 'classy'
... items = [prefix+c for c in 'abc']
>>> print(klass.items)
['globala', 'globalb', 'globalc']​

In Python 2, that last one would produce 'classya' and friends, due to the
"broken" comprehension scope.
___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Greg Ewing

Paul Moore wrote:

the next question will likely be "so why does = exist at all?"


And if we decide to make ':=' the official assigment operator and
deprectate '=', the next question will be "Why do we have '=='
instead of '='?"

--
Greg

___
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


Re: [Python-Dev] PEP 572: Assignment Expressions

2018-04-17 Thread Tim Peters
[Paul Moore]
>> the next question will likely be "so why does = exist at all?"

[Greg Ewing ]
> And if we decide to make ':=' the official assigment operator and
> deprectate '=', the next question will be "Why do we have '=='
> instead of '='?"

Which would be a fine question!  In Python's very early days, it
didn't have "==" at all:  plain "=" was used for both assignment and
equality testing.

>From the HISTORY file:

"""
New features in 0.9.6:
...
- '==' is now the only equality operator; "../demo/scripts/eqfix.py" is
  a script that fixes old Python modules
"""

That script crawled a source tree and replaced instances of "=" used
for equality testing with the new-fangled "==".  We can obviously do
something similar to replace instances of "=" used for assignment when
that's removed, and I'm sure nobody will complain about that either
;-)
___
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