plugin-python icon indicating copy to clipboard operation
plugin-python copied to clipboard

Non-injective token to AST mappings

Open taion opened this issue 7 years ago • 4 comments

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 (https://github.com/prettier/prettier-python/pull/7)

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


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

2. Nested with in Python 2 (https://github.com/prettier/prettier-python/pull/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

taion avatar Jan 14 '18 20:01 taion

  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

patrick91 avatar Jan 14 '18 20:01 patrick91

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 avatar Jan 14 '18 21:01 taion

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 avatar Jan 14 '18 21:01 taion

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

taion avatar Jan 14 '18 21:01 taion