PyCQA / pycodestyle

Simple Python style checker in one Python file
https://pycodestyle.pycqa.org
Other
5.03k stars 753 forks source link

E127: method chaining #898

Open fgotzens opened 4 years ago

fgotzens commented 4 years ago

I use method chaining in pandas very often, for example

import pandas as pd
df = (pd.read_csv('very/long/path/to/file/abcdefghijklmnopqrstuvwxyz.csv',
                  index_col='bar', encoding='utf-8')
        .drop(labels='baz', axis=1))

in which I indent the lines according to the first dot . after pd.

Now sometimes, this works: working

but also sometimes it doesn't, for example when the first part exceeds into a new line: not_working

Any idea what's going wrong here?

I am using Spyder 4.0.0 | Python 3.7.3 64-bit | Windows 10

FichteFoll commented 4 years ago

PEP 8 doesn't have anything regarding this style, so it can't serve as a reference. pycodestyle currently expects the expression to proceed at the indent level of the opening paren.

fgotzens commented 4 years ago

@FichteFoll Hm, okay, thank you for your reply!

I just checked again and found this kind of indentation is the recommended style of using method chaing as shown in this tutorial linked from the official pandas website.

I don't really see how to stick to the E127 rule and write readable code at the same time, since you will probably agree that this consumes too much space:

df = (pd
      .read_csv('very/long/path/to/file/abcdefghijklmnopqrstuvwxyz.csv',
                  index_col='bar', encoding='utf-8')
      .drop(labels='baz', axis=1))

and this is more than ugly:

df = (pd.read_csv('very/long/path/to/file/abcdefghijklmnopqrstuvwxyz.csv',
                  index_col='bar', encoding='utf-8')
      .drop(labels='baz', axis=1))

So how do you think we should proceed here?

chaoflow commented 4 years ago

@fgotzens instead of the outer parentheses, you could use backslashes to escape the line breaks:

df = pd.read_csv('very/long/path/to/file/abcdefghijklmnopqrstuvwxyz.csv',
                  index_col='bar', encoding='utf-8')\
        .drop(labels='baz', axis=1)
fgotzens commented 4 years ago

@chaoflow Thanks for the hint! However that does not really solve to problem, as it triggers E126 in the line starting with .drop(labels...) and claiming it to be over-indented.

chaoflow commented 4 years ago

@fgotzens I see the problem. We work around this by using pylint as well and disabling E126 for flake8, see https://gitlab.com/ternaris/marv-robotics/blob/master/setup.cfg

sigmavirus24 commented 4 years ago

The backslash is a hideous way to work around this problem, in my opinion

chaoflow commented 4 years ago

@sigmavirus24 An indisputable case would be:

foo = bar.func(obj.method()
               .next_method())

versus over-idented within parentheses

foo = bar.func(obj.method()
                  .next_method())

At least emacs with python-mode will auto-align on the former and using the tab key it won't settle for more indentation (the latter), which means to support the latter, there is at least one more tool to be convinced.

Given that so far I use:

foo = bar.func(obj
               .method()
               .next_method())

or

foo = bar.func(obj.method()
               .next_method())

or, if the xyz is needed anyway:

xyz = obj.method()\
         .next_method()
foo = bar.func(xyz)

My conclusion: pycodestyle should at least support the backslash use case and it would be great if the over-idented within parentheses use case would be supported as well, though there would be at least one editor to be taught to make use of it.

As already mentioned, pep8 says nothing about dotted continuation lines within parentheses.