Skip to main content

Tab (or Spaces) indentation style checker for flake

Project description

Tab (and Spaces) Style Checker for flake8 (flake8-tabs)

PyPILicenseDownload StatisticsStandard-readme compliant

Like tabs? So do I!

This module provides a smart indentation checking module for the awesome flake8 style checker suite, featuring pycodestyle, pyflakes and mccabe by default as well as a minimalist plugin architecture.

When enabled flake8-tabs will disable pycodestyle's “tabs_or_spaces”, “continued_indentation”, “tabs_obsolete” and “trailing_whitespace” checkers by default. Replacing them by its own more flexible checkers, that allow for the “tabs for indentation, spaces for alignment” paradgime to be applied to your source code – or simply getting a better indentation checker for your spaces based source code.

Table of Contents

Install

For interactive use (flake8 command) install using pip 19.1+:

$ pip install flake8-tabs

Or, if flake8 was installed via pipx add it that program's virtual environment instead:

$ pipx inject flake8 flake8-tabs

For adding flake8-tabs to your regular linting checks simply ensure that is mentioned in your development requirements.txt, tox configuration or wherever your linting dependencies are stored in your project. There is no way in flake8 to enforce that a certain linter is run when invoking the flake8 command, but you can use flake8 --help to view the list of plugins detected and active (last line of output).

There should be no issues when using this plugin with flake8 forks like flake9 or flakehell, but this has not been tested. Feedback welcome!

Usage

By default installing flake8-tabs will only enable the blank line indentation checker. To enable flake8-tabs for your project proper, you will need to set the use-flake8-tabs option mentioned below to true.

All of the following options are specified using flake8's configuration system. See the relevant sections on invoking and configuring flake8 for details on how and where these options may be used.

Configuration options

Some of the below options will attempt to use read their defaults from the repository's .editorconfig file if one is found. Adding an EditorConfig file to your project is highly recommended as it will ensure that many code editors will automatically set their editing preferences based on your project's expectations. By using these values by default flake8-tabs saves you from duplicating this configuration between that file and your linter.

All other option defaults, as well as all ultimate defaults tend to reflect recommendations from PEP-8.

Note: In the following, indenting refers to the practice of adding new levels of indentation (usually using tab key whether you're using tabs or not) on a separate line, while aligning refers to the practice of adding spacing in front of the elements on the following line to make them visually match the elements on the previous line. Indentation may refer to any of the above.

use-flake8-tabs

  • Default: false
  • Allowed values: true, false

Enable flake8-tabs for indentation checking and, by default, disable the all conflicting checkers from pycodestyle (see next option). Without setting this to true, or passing --use-flake8-tabs on the command-line, only flake8-tabs's blank line indentation checking will be enabled. While this flag is disabled it is guaranteed no errors will be reported where pycodestyle will not report one as well. While further checks may be enabled at some point that do not require use of this flag, they will not ever break this guarantee.

This way you can selectively disable errors from pycodestyle and incrementally depend on the smarter checkers in flake8-tabs.

use-pycodestyle-indent

  • Default: false if use-flake8-tabs is true otherwise true
  • Allowed values: true, false

If false indentation style checks from pycodestyle will be disabled, see the description of the previous option for details.

indent-style

  • Default: indent_style property of .editorconfig or "keep" if unset / not found
  • Allowed values: "tab", "space", "keep"

The indentation style to expect in newly indented code blocks. By default flake8-tabs will simply analyze what is already there and only ensure that no inconsistent mixture of both tabs and spaces in the same code block happens. This allows different files, and even different code blocks in the same file, to use different indentation styles; generally you'll want to pick one and stick with it.

tab-width

  • Default: indent_size property of .editorconfig or 4 if unset / not found
  • Allowed values: Any integer >= 1

The expected size of each tab in spaces. This is not really specific to this plugin, but used to properly calculate the required additional spaced indentation when indenting within an aligned section.

Internally this value is also used to monkeypatch flake8's pretty printing function to expand tabs to this number of spaces instead of its hard-coded default of 8 spaces per tab.

blank-lines-indent

  • Default depends on trim_trailing_whitespace property of .editorconfig:
    • "never" if true
    • "always" if false
    • "maybe" if unset / not found
  • Allowed values: "maybe", "always", "never"

Whether to allow properly aligned indentation in blank lines. The default value will allow both aligned indentation and no indentation. "always" will require blank lines to contain indentation, "never" will prohibit it.

By properly aligned indentation we mean indentation that has the same value as the indentation of the next block of source code:

# This is OK (matching indent):
def main():
	# … snip …
	do_something()
	
	do_something_else()
	# … snip …

# This is not OK (unmatching indent):
def main():
	while True:
		# … snip …
		do_something()
	
		do_something_else()
		# … snip …

# This is by default OK as well (unindented):
def main():
	while True:
		# … snip …
		do_something()

		do_something_else()
		# … snip …

Note that this checker does not ensure that the blank line indentation style is consistent within a document when set to "maybe", so the following will pass by default but may not be what you want:

def main():
	while True:
		# … snip …
		do_something()

		do_something_else()
		
		do_more()
		# … snip …

Please open an issue if ensuring consistent per-document blank line indentation is something you need.

continuation-style

  • Default: "both"
  • Allowed values: "aligned", "hanging", "both"

By default flake8-tabs allows lines following the initial line belonging to the same statement (“continuation lines”) to either align to the innermost opened bracket of the preceeding line or use 1–2 levels of indenting (see the next set of options) depending on whether the previous line ended with an opening bracket or not:

# Example for aligned indentation (OK with continuation-style=aligned|both):
def foo_bar(x: int,
␣␣␣␣␣␣␣␣␣␣␣␣y: int) -> str:  # The “y” matches up with the “x” above
	return f"{x}, {y}"

# Same code with hanging indentation (OK with continuation-style=hanging|both):
def foo_bar(  # This line ends with an opening bracket
		x: int,
		y: int,
) -> str:
	return f"{x}, {y}"

This option can be used to restrict the allowed continuation style to only one of the above (PEP-8 allows both with semantics very similar to the ones mentioned above). In particular, setting continuation-style to "hanging" will ensure that flake8-tabs will never allow a line to contain both spaces and tabs at the same time.

Indenting Tab Counts

When using hanging indentation on a continuation line, the following options may be used to configurate the number of tabs expected on the continuation line. These options are not very useful if continuation-style is set to "aligned".

(Note that a tab here may also refer to an equivalent number of spaces based on the configured tab width.)

indent-tabs-call
  • Default: 1
  • Allowed values: Any integer >= 1

The number of tabs to add when adding the first level of indentation using indenting within a function or method call:

# Example with: indent-tabs-call=3
x = long_function_name(
			{  # First level gets 3 levels of indenting
				"name": "value"  # Next level is indented as usual
			},
			param2, param3
)

Usually you should leave this at 1 (PEP-8) but some teams may prefer a value of 2 to function calls more easily distinguishable from blocks.

indent-tabs-def
  • Default: 2
  • Allowed values: Any integer >= 1

The number of tabs to add when adding the first level of indentation using indenting within a class of method definition:

# Example with the default of: indent-tabs-def=2
def main(
		param1, param2, param3,
		param4):
	initialize_something()
)

Notice in the example above how an indent level of 1 would make the elements of the parameter list hard to distingish from the first statement. Hence PEP-8 recommends either indenting twice or using alignment instead.

indent-tabs-expr
  • Default: 1
  • Allowed values: Any integer >= 1

The number of tabs to add when adding the first level of indentation using indenting within any other kind of construct (such as a tuple, set, dict, …).

Reported Error Codes

All error codes were borrowed from pycodestyle. You can detect that they were generated by flake8-tabs by them being followed by the word (flake8-tabs) as well as the extra T added after the initial severity letter.

Error Code Meaning
ET101 Mixed block of tabs and spaces detected where there as a least one tab following a space
ET121, ET122, ET123, ET126, ET127, ET128 Identation did not match what was expected (somewhat configurable)
WT291 Extranous whitespace detected on end of line
WT293 Whitespace that was not aligned with the surrounding source detected (configurable)

Also note that, due to the way we disable the relevant checkers from pycodestyle the following error codes do not have an equivalent in this plugin: E124, E125, E129, E131, E133.

Alternatives

  • flake8-expandtab replaces all tabs in the input document with a configurable number of spaces before it is passed to pycodestyle
    • Comment: While its code is a lot simpler, this approach does not prevent inappropriate mixing of tabs and spaces on the same line. It will silently allow documents that will look broken when viewed with a different tab width setting and will even fail to detect documents that are invalid Python due to unparsable mixed spaces/tabs usage.

      That said, it was the primary inspiration for developing this checker in the first place.

Contributing

As usual, contributions to the project are welcome!

Please report issues and feature requests at https://gitlab.com/ntninja/flake8-tabs/-/issues/ (requires GitLab.com account) after verifying that it hasn't already been reported. In cases where you believe that flake8-tabs is incorrectly reporting a violation or should report a violation that it is currently not, please include a minimum affected code sample and any reported violations you are currently seeing.

If you cannot or don't want to create a GitLab.com account, feel free to instead contact me via email at alexander-f8t@ninetailed.ninja instead. However, please use GitLab instead if you can as it helps me keep things organized.

Code and documentation contributions are of course also welcome. If your new to forking and merge requests on GitLab this short guide should come in handy. If you need help with anything, feel free to open an issue and I'll try to guide you through the process if needed.

As with reporting issues you may also contact me via email with patches if using GitLab.com is not possible for you. Again, using GitLab.com is strongly preferred however.

Setting up a local development environment

flake8-tabs uses PDM for its Python project management needs. Please refer to its installation instructions before continuting. (Using the pipx based installation method on Linux is highly recommended: As of March 2021, the virtualenv preloading packages shipped with some distros may cause weird version-conflict related errors in the author's experience.)

After installing PDM, prepare the development environment using:

$ pdm install --dev

Your locally modified version of flake8-tabs may now be invoked using pdm run:

$ pdm run flake8 […]

You can also run your modified version from another directory (such as a project of yours where flake8-tabs exhibits some bad behaviour) using the -p option of pdm run:

~/path/to/your/project$ pdm run -p ~/path/to/flake8-tabs flake8 […]

License

This project is licensed under the GNU Lesser General Public License version 3.0 or (at your option) any later version.

For the full license, please consult the LICENSE.md file distributed along with this project.

Copyright owners are:

  • 2017–2021 Alexander Schlarb
  • 2020 Peter Linss

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flake8-tabs-2.3.2.tar.gz (28.7 kB view hashes)

Uploaded Source

Built Distribution

flake8_tabs-2.3.2-py3-none-any.whl (25.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page