Git Product home page Git Product logo

flake8-tidy-imports's Introduction

flake8-tidy-imports

https://img.shields.io/github/actions/workflow/status/adamchainz/flake8-tidy-imports/main.yml.svg?branch=main&style=for-the-badge https://img.shields.io/pypi/v/flake8-tidy-imports.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

A flake8 plugin that helps you write tidier imports.


Linting a Django project? Check out my book Boost Your Django DX which covers Flake8 and many other code quality tools.


Requirements

Python 3.8 to 3.13 supported.

Installation

First, install with pip:

python -m pip install flake8-tidy-imports

Second, if you define Flake8’s select setting, add the I25 prefix to it. Otherwise, the plugin should be active by default.

Options

banned-modules

Config for rule I251 (below). Should contain a map where each line is a banned import string, followed by '=', then the message to use when encountering that import.

There is also a special directive to ban a preselected list of removed/moved modules between Python 2 and Python 3, recommending replacements from six where possible. It can be turned on by adding {python2to3} to the list of banned-modules.

For example in setup.cfg:

[flake8]
banned-modules =
  mock = Use unittest.mock.
  {python2to3}

Note that despite the name, you can ban imported objects too, since the syntax is the same. For example:

[flake8]
banned-modules =
  decimal.Decimal = Use ints and floats only.

Entries containing * are treated as wildcards matching zero or more path components. For example:

  • example.yellow.* matches example.yellow, example.yellow.truck, example.yellow.truck.driving etc.
  • example.*.truck matches example.truck, example.yellow.truck, example.red.truck, example.big.red.truck, etc.

ban-relative-imports

Controls rule I252 (below). Accepts two values:

  • parents - bans imports from parent modules (and grandparents, etc.), i.e. with more than one ..
  • true - bans all relative imports.

For example:

[flake8]
ban-relative-imports = parents

(If you want to ban absolute imports, you can put your project's modules in banned-modules.)

Rules

Note: Before version 4.0.0, the rule codes were numbered 50 lower, e.g. I250 was I200. They were changed in Issue #106 due to conflict with flake8-import-order.

I250: Unnecessary import alias

Complains about unnecessary import aliasing of three forms:

  • import foo as foo -> import foo
  • import foo.bar as bar -> from foo import bar
  • from foo import bar as bar -> from foo import bar

The message includes the suggested rewrite (which may not always be correct), for example:

$ flake8 file.py
file.py:1:1: I250 Unnecessary import alias - rewrite as 'from foo import bar'.

Such aliases can be automatically fixed by isort if you activate its remove_redundant_aliases option.

I251: Banned import <import> used.

Complains about use of banned imports. By default there are no imports banned - you should configure them with banned-modules as described above in 'Options'.

The message includes a user-defined part that comes from the configuration. For example:

$ flake8 file.py
file.py:1:1: I251 Banned import 'mock' used - use unittest.mock instead.

I252: Relative imports <from parent modules> are banned.

Complains about use of relative imports:

  • from . import foo (sibling import)
  • from .bar import foo (sibling import)
  • from .. import foo (parent import)

Controlled by the ban-relative-imports configuration option.

Absolute imports, or relative imports from siblings, are recommended by PEP8:

Absolute imports are recommended, as they are usually more readable and tend to be better behaved...

import mypkg.sibling
from mypkg import sibling
from mypkg.sibling import example

However, explicit relative imports are an acceptable alternative to absolute imports...

from . import sibling
from .sibling import example

See also

For more advanced control of imports in your project, try import-linter.

flake8-tidy-imports's People

Contributors

adamchainz avatar bsvetchine avatar chriselion avatar dependabot[bot] avatar dirn avatar graingert avatar lucaswiman avatar mxr avatar pre-commit-ci[bot] avatar proofit404 avatar the-compiler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

flake8-tidy-imports's Issues

Errors are bizrre for non-module import

Many frameworks and libraries deprecate/remove individual classes or methods within a module. For example, django.core.management.NoArgsCommand was removed in Django 1.10, but the django.core.management module still exists.

While flake8-tidy-imports works correctly for these cases, the error message is somewhat confusing:

I201 Banned module 'django.core.management.NoArgsCommand' imported - Use django.core.management.BaseCommand instead

But NoArgsCommand isn't a module, it's a class! I propose changing the message from "Banned module" to "Banned Import" so the error message is not incorrect in both the module and the method/class cases.

This is a fairly pedantic issue, so feel free to close as "wontfix" if you don't want to be bothered. If you're amenable to the change, I'd be happy to implement it. :-)

BTW, thanks for writing this library! It seems to be quite useful for helping with framework upgrades and migrating to Python 3.

Require minimal local imports for siblings

Description

I'd like to be able to require that immediate sibling imports are always relative (and in their minimal form).

For example, given a project structure like:

$ tree mypkg/
mypkg/
├── child
│   ├── __init__.py
│   └── utils.py
├── __init__.py
├── main.py
└── sibling.py

Then in main.py:

from mypkg import sibling  # bad
from mypkg.sibling import A  # bad

from . import sibling  # good
from .sibling import A  # good

Not sure what the rule should be for child as the project I want to use this in is a Django project where we don't tend to expose things via __init__.py. Probably we'd treat child/__init__.py as if it were a child.py given that the imports look the same.

This miight have the same requirement around knowing the project root that #394 does, though there are plenty of other relative spellings which could be prohibited without that.

Ban absolute imports

First, awesome tool. I am adding it to my list of flask plugins :)

However, I guess we have different opinions. I would like an option to explicitly ban absolute imports.

I find that people accidentally create circular dependencies that are difficult to resolve much more often when there are absolute imports. Also, it gives people the idea that they can run python files as scripts, which i want to stop my co-developers from doing in favor of them using python -m ...

However, I wouldn't want to ban absolute imports in my tests/ directory, which should be able to run without knowing anything about how my package is written or structured in the src/ directory.

Ban relative imports only when through the root of the package

Description

I have a (Django) project where we ban relative imports which go through the root of the project, however mostly allow them otherwise.
Would you be open to a PR which extends the ban-relative-imports behaviour with a root value to ban these?

For example given a project like:

$ tree mypkg/
mypkg/
├── left
│   ├── child
│   │   ├── __init__.py
│   │   └── utils.py
│   ├── __init__.py
│   ├── main.py
│   └── sibling_left.py
└── right
    ├── __init__.py
    ├── main.py
    └── sibling_right.py

Then for imports in mypkg/left/main.py:

from .sibling_left import A  # ok
from .child.utils import B  # ok
from ..right import X  # not ok
from ..right.main import Y  # not ok

Or for imports in mypkg/left/child/utils.py:

from ..sibling_left import A  # ok
from ...right import X  # not ok
from ...right.main import Y  # not ok

Currently we have a project-local linter implementation which does this, though it relies on a hard-coded path to know the project root. I can see that knowing the project root automatically might be a stumbling block for generalising this, so suggestions towards that would be welcome!

Add a `grandparents` option to `ban-relative-imports`

Description

This would ban relative imports with more than two dots (eg from ...a.b import c), just like parents does, but for grandparents.
I personally like relative imports, but with three dots it becomes unreadable.

If this is accepted, I would be interested in submitting a PR for this.

Error TypeError: object of type 'NoneType' has no len() in version 1.0.4

Hi, After the update to version 1.0.4 i received several the same errors:

Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self.run()
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "/home/white/projects/quark/.tox/pep8/local/lib/python2.7/site-packages/flake8/checker.py", line 666, in run_checks_from_queue
checker.run_checks(results_queue, statistics_queue)
File "/home/white/projects/quark/.tox/pep8/local/lib/python2.7/site-packages/flake8/checker.py", line 606, in run_checks
self.run_ast_checks()
File "/home/white/projects/quark/.tox/pep8/local/lib/python2.7/site-packages/flake8/checker.py", line 517, in run_ast_checks
for (line_number, offset, text, check) in runner:
File "/home/white/projects/quark/.tox/pep8/local/lib/python2.7/site-packages/flake8_tidy_imports.py", line 61, in run
for err in getattr(self, 'rule
{}'.format(rule))(node):
File "/home/white/projects/quark/.tox/pep8/local/lib/python2.7/site-packages/flake8_tidy_imports.py", line 113, in rule_I201
module_names.sort(key=len, reverse=True)
TypeError: object of type 'NoneType' has no len()

Do you have any idea how to fix this?

Add deferred-only option

Description

A key technique to make Python projects start faster is deferring imports. I'd like to add a setting to flake8-tidy-imports that lists imports that must be deferred. An error would be raised for such imports outside of function bodies, except under if TYPE_CHECKING clauses.

flake8 v3 do not consider banned-modules in configuration file

Using flake8 v3, option --banned-modules works correctly when passed in argument to the command line :

flake8 --banned-modules ...

However, banned-module option is not recognized when configured in the flake8 configuration file (setup.cfg, tox.ini, or .flake8)

In the flake8 log, I have the following lines :
flake8.options.manager MainProcess 421 DEBUG Registered option "Option(None, --banned-modules, action=store, default=, dest=banned_modules, type=None, callback=None, help=A map of modules to ban to the error messages to display in the error., callback=None, callback_args=None, callback_kwargs=None, metavar=None)". flake8.options.manager MainProcess 422 DEBUG Removing ['I20'] from the default ignore list flake8.options.manager MainProcess 423 DEBUG Attempted to remove I20 from default ignore but it was not a member of the list. flake8.options.manager MainProcess 423 DEBUG Extending default select list with ['I20'] flake8.plugins.manager MainProcess 424 DEBUG Registering options from plugin "F" on OptionManager <flake8.options.manager.OptionManager object at 0x7f6e15db93d0> flake8.options.manager MainProcess 425 DEBUG Registered option "Option(None, --builtins, action=None, default=None, dest=builtins, type=None, callback=None, help=define more built-ins, comma separated, callback=None, callback_args=None, callback_kwargs=None, metavar=None)". flake8.options.manager MainProcess 425 DEBUG Registered option "Option(None, --doctests, action=store_true, default=False, dest=doctests, type=None, callback=None, help=check syntax of the doctests, callback=None, callback_args=None, callback_kwargs=None, metavar=None)". flake8.options.manager MainProcess 426 DEBUG Registered option "Option(None, --include-in-doctest, action=None, default=, dest=include_in_doctest, type=string, callback=None, help=Run doctests only on these files, callback=None, callback_args=None, callback_kwargs=None, metavar=None)". flake8.options.manager MainProcess 426 DEBUG Registered option "Option(None, --exclude-from-doctest, action=None, default=, dest=exclude_from_doctest, type=string, callback=None, help=Skip these files when running doctests, callback=None, callback_args=None, callback_kwargs=None, metavar=None)". flake8.options.manager MainProcess 427 DEBUG Removing ['F'] from the default ignore list flake8.options.manager MainProcess 428 DEBUG Attempted to remove F from default ignore but it was not a member of the list. flake8.options.manager MainProcess 428 DEBUG Extending default select list with ['F'] flake8.options.config MainProcess 429 DEBUG Ignoring user and locally found configuration files. Reading only configuration from "setup.cfg" specified via --config by the user flake8.options.config MainProcess 433 DEBUG Found cli configuration files: ['setup.cfg'] flake8.options.config MainProcess 433 DEBUG Parsing CLI configuration files. flake8.options.config MainProcess 434 DEBUG Option "banned-modules" is not registered. Ignoring.

Allow `*` to match part of a module or package name

Description

Hi 👋

I'd like to ban all packages listed in https://youtype.github.io/boto3_stubs_docs/#packages. That's 337 packages as of the time I created this feature request...

It would be great if I could just define:

banned-modules =
    mypy_boto3_*.* = This is a dev dependency only

So mypy_boto3_* would match any package name starting with mypy_boto3_.

Other ideas: Maybe * could be even used in any part of the name, e.g. foo*bar, *baz. Or multiple * could be allowed too, e.g. *f*o*. Also, since only * is used as wildcard, other glob characters like ? or [] would not be considered as wildcard.

I'll be happy to submit a PR if you agree.

option --ban-relative-imports: 'const' must not be supplied for action 'store'

Python Version

3.7.9

Package Version

4.4.0

Description

flake8 fails with:

  File "lib/python3.7/site-packages/flake8_tidy_imports/__init__.py", line 48, in add_options
    help="Ban relative imports, from parental modules or in all cases.",
...
optparse.OptionError: option --ban-relative-imports: 'const' must not be supplied for action 'store'

This library doesn't seems be working for banned-modules

Python Version

3.9.0

flake8 Version

3.8.3

Package Version

No response

Description

Tried setting up the module. I've configured the following - .flake8 and .pre-commit-config.yaml

.flake8

[flake8]
max-line-length = 80
max-complexity = 18
select = B,C,E,F,W,T4,B9,I125
ignore = B008, B903, B904, C901, E203, E266, E501, F401, F403, F541, W503
ban-relative-imports = true
banned-modules =
    json = use utils.msgspec
    elasticsearch = dont
require-plugins =
    flake8-implicit-str-concat
    flake8-bugbear
    flake8-tidy-imports==4.10.0

and .pre-commit-config-.yaml

repos:
  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort
  - repo: https://github.com/psf/black
    rev: 23.1.0
    hooks:
      - id: black
  - repo: https://github.com/PyCQA/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        additional_dependencies:
          - flake8-bugbear
          - flake8-implicit-str-concat
          - flake8-tidy-imports==4.10.0
  - repo: https://github.com/PyCQA/autoflake
    rev: v2.1.1
    hooks:
      - id: autoflake
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: fix-byte-order-marker
      - id: trailing-whitespace
      - id: end-of-file-fixer
        exclude: (\.txt)$

When I try to import json in a file and commit, it commits easily and pushed. The expected behaviour should have been that if json library is imported - it should not commit (as per above details in .flake8) and raise error.

ban-wildcard-imports?

Description

Great project!

I was wondering if there would be any interest in adding the ability to bad wildcard imports.

So for example:
from my_module import * would be disallowed, but from my_module import my_function would be allowed.

Reason is that this may help with finding usages, and preventing name conflicts in repos.

PEP8:

Wildcard imports (from import *) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. There is one defensible use case for a wildcard import, which is to republish an internal interface as part of a public API (for example, overwriting a pure Python implementation of an interface with the definitions from an optional accelerator module and exactly which definitions will be overwritten isn’t known in advance).

https://peps.python.org/pep-0008/#imports

  • based on this, maybe an option for allowing wildcards in __import__.py files can be an option too.

  • maybe with an optional whitelist, so you can allow certain packages like typing (personally wouldn't use this, but seems to be common)

Ban import for file pattern

Description

Thanks for your library!

For me and my command it will be nice to have possibility to import some local files in a certain way.

For example, I have

controllers/
- __init__.py
- controller1.py
- controller2.py

In __init__.py i write

from .controller1 import ControllerOne
from .controller2 import ControllerTwo

And inside controller1.py i want to use ControllerTwo.

So, if i write in controller1.py

from controllers import ControllerTwo

it will be a circular import, but if I write something like

from controllers.controller2 import ControllerTwo 

It will work.

So, I want to restrict in some files import some modules "in short way" to avoid circular import. But outside controllers - any files can import any controller in short way

Is it possible with your plugin to do somethibg like it? Could you consider such functionality for yourself?

Thanks for any reply!

Ban relative imports?

Hi, thank you for the useful package!

I wonder if it would be desirable to introduce the third rule to the plugin?

What do you think about the possibility of forbidding relative imports?

For example,

# bad
from .foo import Bar
# good
from quiz.foo import Bar

Have a good day 🎉

Best regards,
Artem.

I252 does not allow for fine-tuning

Python Version

3.9.0

Package Version

latest

Description

It's more of a feature request than an issue :

Currently I252 can only be a boolean

ban-relative-imports = True

As stated by the pep documentation using relative import, is acceptable when dealing with complex package layouts.

from .sibling import example
# is more readable than
from my.project.sub.module.sibling import example

But abusing the relative import by searching into a parent module makes it less readable

from ....parent import example
# is less readable than
from my.project.parent import example

So the idea would be to have an option to specify the maximum level of relativeness that is allowed in the project
Something like

max-relative-import = 1
# This would pass
from .sibling import example

# This would fail
from ..parent import example 

(I'm not good at naming things so any idea is welcome !)

I can make a PR if needed :)

Support alias import

Description

Hi Adam,

Can we support alias import something like this ?

# Don't
import datetime
# Do
import datetime as dt

In my projects, some of my modules have name conflict with 3rd-party package so I want to leverage alias imports to avoid name confusion.

Thanks.

Ban relative imports, not working

I put

from . import test

in one of my source files that is being checked.

However flake8 --ban-relative-imports does not pick up on it.

I cannot create a minimal example, because minimal examples work.

When I create another problem in the same file flake8 does pick up on it. Please assist me in finding the problem.

Ability to ban certain modules from being imported

As we're migrating to Python 3, it would be nice to enforce that only new versions of certain modules are imported, e.g. instead of importing mock, people import unittest.mock (via backports.unittest_mock : https://github.com/jaraco/backports.unittest_mock ).

Would like a setting to specify banned imports plus a message as to why they're banned, e.g.

banned_modules = mock: "Use unittest.mock instead"
                 urlparse: "Use six.moves.urllib.parse instead"

I250 rewrite suggestions are bad

They don't work with multi-part imports e.g. from foo import bar as bar, baz as baz appears as two errors with two suggestions from foo import bar and from foo import baz. We should instead just fix the single line.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.