prettier/plugin-python

Non-injective token to AST mappings

taion opened this issue · 4 comments

taion commented

There are a number of cases in Python where the token-to-AST mapping is very clearly non-injective, where multiple different-looking statements are syntactically equivalent. We should think about how to handle these; perhaps this will require looking at the actual tokens.

Usually only one use case is common, but mangling tokens might be a bit much.

1. elif (#7)

if foo:
    foo()
elif bar:
    bar()


if foo:
    foo()
else:
    if bar:
        bar()

2. Nested with in Python 2 (#15)

with a, b:
    pass


with a:
    with b:
        pass

3. as in except in Python 2

try:
    foo()
except A as a:
    pass


try:
    foo()
except A, a:
    pass

4. Empty parent class

class Foo:
    pass


class Foo():
    pass
  1. I'd say let's leave like it is now (merging the ifs), if that's going to be a problem we can change that in the future :)
  2. I'd like to merge the with statements if possible
  3. Let's use as, since works for both python 2 and 3 :)
  4. I'd prefer to have no parenthesis
taion commented

The comma syntax in except is a Python 2.5 thing so probably it's dead.

The nested with statements do arise in real code, though: https://github.com/tensorflow/models/blob/1887a5fe187c8ab9f623cd91b74fc8432ce76d6f/research/slim/nets/mobilenet_v1.py#L199-L200

Part of the issue is that there's not a good way to break with statements to multiple lines. Neither of the following work:

with (
    Foo() as foo,
    Bar() as bar,
):
    pass

with (
    Foo() as foo
), (
    Bar() as bar
):
    pass

The closest is something like:

with \
        Foo() as foo, \
        Bar() as bar:
    pass

But this is less than pretty.

taion commented

Makes you wish they didn't drop contextlib.nested in Python 3. ExitStack can give the same semantics but it's more annoying to use.

taion commented

YAPF's output here is good for a giggle, though:

# In:
with \
    Foo() as foo, \
    Foo() as foo_2, \
    Bar(
        long_argument,
        long_argument,
        long_argument,
        long_argument,
        long_argument,
    ) as bar, \
    Baz(some_arg) as baz, \
    Baz(some_arg) as baz_2 \
:
    pass

# Out:
with Foo() as foo, Foo() as foo_2, Bar(
        long_argument,
        long_argument,
        long_argument,
        long_argument,
        long_argument,
) as bar, Baz(some_arg) as baz, Baz(some_arg) as baz_2:
    pass