flake8-import-order-0.19.2/0000775000175000017500000000000015026506505015326 5ustar jriverojriveroflake8-import-order-0.19.2/README.rst0000664000175000017500000001615715026506505017027 0ustar jriverojriveroflake8-import-order =================== |Build Status| A `flake8 `__ and `Pylama `__ plugin that checks the ordering of your imports. It does not check anything else about the imports. Merely that they are grouped and ordered correctly. In general stdlib comes first, then 3rd party, then local packages, and that each group is individually alphabetized, however this depends on the style used. Flake8-Import-Order supports a number of `styles <#styles>`_ and is extensible allowing for `custom styles <#extending-styles>`_. This plugin was originally developed to match the style preferences of the `cryptography `__ project, with this style remaining the default. Warnings -------- This package adds 4 new flake8 warnings - ``I100``: Your import statements are in the wrong order. - ``I101``: The names in your from import are in the wrong order. - ``I201``: Missing newline between import groups. - ``I202``: Additional newline in a group of imports. Styles ------ The following styles are directly supported, * ``cryptography`` - see an `example `__ * ``google`` - style described in `Google Style Guidelines `__, see an `example `__ * ``smarkets`` - style as ``google`` only with `import` statements before `from X import ...` statements, see an `example `__ * ``appnexus`` - style as ``google`` only with `import` statements for packages local to your company or organisation coming after `import` statements for third-party packages, see an `example `__ * ``edited`` - see an `example `__ * ``pycharm`` - style as ``smarkets`` only with case sensitive sorting imported names * ``pep8`` - style that only enforces groups without enforcing the order within the groups You can also `add your own style <#extending-styles>`_ by extending ``Style`` class. Configuration ------------- You will want to set the ``application-import-names`` option to a comma separated list of names that should be considered local to your application. These will be used to help categorise your import statements into the correct groups. Note that relative imports are always considered local. You will want to set the ``application-package-names`` option to a comma separated list of names that should be considered local to your company or organisation, but which are obtained using some sort of package manager like Pip, Apt, or Yum. Typically, code representing the values listed in this option is located in a different repository than the code being developed. This option is only accepted in the supported ``appnexus`` or ``edited`` styles or in any style that accepts application package names. The ``application-import-names`` and ``application-package-names`` can contain namespaced packages or even exact nested module names. (This is possible with 0.16 onwards). ``import-order-style`` controls what style the plugin follows (``cryptography`` is the default). Limitations ----------- Currently these checks are limited to module scope imports only. Conditional imports in module scope will be ignored except imports under ```if TYPE_CHECKING:``` block. Classification of an imported module is achieved by checking the module against a stdlib list and then if there is no match against the ``application-import-names`` list and ``application-package-names`` if the style accepts application-package names. Only if none of these lists contain the imported module will it be classified as third party. These checks only consider an import against its previous import, rather than considering all the imports together. This means that ``I100`` errors are only raised for the latter of adjacent imports out of order. For example, .. code-block:: python import X.B import X # I100 import X.A only ``import X`` raises an ``I100`` error, yet ``import X.A`` is also out of order compared with the ``import X.B``. Imported modules are classified as stdlib if the module is in a vendored list of stdlib modules. This list is based on the latest release of Python and hence the results can be misleading. This list is also the same for all Python versions because otherwise it would be impossible to write programs that work under both Python 2 and 3 *and* pass the import order check. The ``I202`` check will consider any blank line between imports to count, even if the line is not contextually related to the imports. For example, .. code-block:: python import logging try: from logging import NullHandler except ImportError: class NullHandler(logging.Handler): """Shim for version of Python < 2.7.""" def emit(self, record): pass import sys # I202 due to the blank line before the 'def emit' will trigger a ``I202`` error despite the blank line not being contextually related. Extending styles ---------------- You can add your own style by extending ``flake8_import_order.styles.Style`` class. Here's an example: .. code-block:: python from flake8_import_order.styles import Cryptography class ReversedCryptography(Cryptography): # Note that Cryptography is a subclass of Style. @staticmethod def sorted_names(names): return reversed(Cryptography.sorted_names(names)) By default there are five import groupings or sections; future, stdlib, third party, application, and relative imports. A style can choose to accept another grouping, application-package, by setting the ``Style`` class variable ``accepts_application_package_names`` to True, e.g. .. code-block:: python class PackageNameCryptography(Cryptography): accepts_application_package_names = True To make flake8-import-order able to discover your extended style, you need to register it as ``flake8_import_order.styles`` using setuptools' `entry points `__ mechanism: .. code-block:: python # setup.py of your style package setup( name='flake8-import-order-reversed-cryptography', ..., entry_points={ 'flake8_import_order.styles': [ 'reversed = reversedcryptography:ReversedCryptography', # 'reversed' is a style name. You can pass it to # --import-order-style option # 'reversedcryptography:ReversedCryptography' is an import path # of your extended style class. ] } ) .. |Build Status| image:: https://travis-ci.org/PyCQA/flake8-import-order.svg?branch=master :target: https://travis-ci.org/PyCQA/flake8-import-order flake8-import-order-0.19.2/pyproject.toml0000664000175000017500000000105215026506505020240 0ustar jriverojrivero[tool.black] line-length = 78 target-version = ['py37'] exclude = ''' ( /( \.eggs # exclude a few common directories in the | \.git # root of the project | \.hg | \.mypy_cache | \.tox | \.venv | _build | build | dist | venv | tests\/test_cases )/ ) ''' force-exclude = ''' ( /( tests\/test_cases )/ ) ''' [tool.mypy] files = ["."] exclude = [ "^docs/", "^tests/", ] [tool.isort] profile = "black" line_length = 78 force_single_line = true skip = ["tests/test_cases/*"] flake8-import-order-0.19.2/.github/0000775000175000017500000000000015026506505016666 5ustar jriverojriveroflake8-import-order-0.19.2/.github/workflows/0000775000175000017500000000000015026506505020723 5ustar jriverojriveroflake8-import-order-0.19.2/.github/workflows/tox.yml0000664000175000017500000000263415026506505022265 0ustar jriverojriveroname: tests on: pull_request: push: branches: [main] concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true env: FORCE_COLOR: "1" TOX_TESTENV_PASSENV: "FORCE_COLOR" MIN_PYTHON_VERSION: "3.9" DEFAULT_PYTHON_VERSION: "3.13" jobs: tests: strategy: fail-fast: false matrix: os: [ubuntu-latest] # [macos-latest, ubuntu-latest, windows-latest] python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} - run: pip install --upgrade pip - run: pip install tox - run: tox -e py lint: strategy: fail-fast: false matrix: job: [pep8, release-check] runs-on: ubuntu-latest steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: 3.x - run: pip install --upgrade pip - run: pip install tox - run: tox -e ${{ matrix.job }} flake8-import-order-0.19.2/.github/workflows/publish.yml0000664000175000017500000000467215026506505023125 0ustar jriverojriveroname: Publish to PyPI on: push: tags: - "*" permissions: contents: read jobs: build: name: "Build dists" runs-on: "ubuntu-latest" environment: name: "publish" outputs: hashes: ${{ steps.hash.outputs.hashes }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 with: egress-policy: audit - name: "Checkout repository" uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" - name: "Setup Python" uses: "actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065" with: python-version: "3.x" - name: "Install dependencies" run: python -m pip install build==0.8.0 - name: "Build dists" run: | SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \ python -m build - name: "Generate hashes" id: hash run: | cd dist && echo "::set-output name=hashes::$(sha256sum * | base64 -w0)" - name: "Upload dists" uses: "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02" with: name: "dist" path: "dist/" if-no-files-found: error retention-days: 5 provenance: needs: [build] permissions: actions: read contents: write id-token: write # Needed to access the workflow's OIDC identity. uses: "slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0" with: base64-subjects: "${{ needs.build.outputs.hashes }}" upload-assets: true compile-generator: true # Workaround for https://github.com/slsa-framework/slsa-github-generator/issues/1163 publish: name: "Publish" if: startsWith(github.ref, 'refs/tags/') needs: ["build", "provenance"] permissions: contents: write id-token: write runs-on: "ubuntu-latest" steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@002fdce3c6a235733a90a27c80493a3241e56863 # v2.12.1 with: egress-policy: audit - name: "Download dists" uses: "actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093" with: name: "dist" path: "dist/" - name: "Publish dists to PyPI" uses: "pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc" flake8-import-order-0.19.2/.github/dependabot.yml0000664000175000017500000000015715026506505021521 0ustar jriverojriveroversion: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: daily flake8-import-order-0.19.2/flake8_import_order/0000775000175000017500000000000015026506505021265 5ustar jriverojriveroflake8-import-order-0.19.2/flake8_import_order/__init__.py0000664000175000017500000001210515026506505023375 0ustar jriverojriveroimport ast import sys from collections import namedtuple from enum import IntEnum if sys.version_info >= (3, 10): STDLIB_NAMES = sys.stdlib_module_names | {"__main__", "test"} else: from .stdlib_list import STDLIB_NAMES from .__about__ import __author__ from .__about__ import __copyright__ from .__about__ import __email__ from .__about__ import __license__ from .__about__ import __summary__ from .__about__ import __title__ from .__about__ import __uri__ from .__about__ import __version__ __all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", ] DEFAULT_IMPORT_ORDER_STYLE = "cryptography" ClassifiedImport = namedtuple( "ClassifiedImport", [ "type", "is_from", "modules", "names", "lineno", "end_lineno", "level", "package", "type_checking", ], ) NewLine = namedtuple("NewLine", ["lineno"]) class ImportType(IntEnum): FUTURE = 0 STDLIB = 10 THIRD_PARTY = 20 APPLICATION_PACKAGE = 30 APPLICATION = 40 APPLICATION_RELATIVE = 50 MIXED = -1 def get_package_names(name): tree = ast.parse(name) parts = [] for node in ast.walk(tree): if isinstance(node, ast.Attribute): parts.append(node.attr) if isinstance(node, ast.Name): parts.append(node.id) if not parts: return [] last_package_name = parts.pop() package_names = [last_package_name] for part in reversed(parts): last_package_name = f"{last_package_name}.{part}" package_names.append(last_package_name) return package_names def root_package_name(name): tree = ast.parse(name) for node in ast.walk(tree): if isinstance(node, ast.Name): return node.id else: return None class ImportVisitor(ast.NodeVisitor): def __init__(self, application_import_names, application_package_names): self.imports = [] self.application_import_names = frozenset(application_import_names) self.application_package_names = frozenset(application_package_names) def generic_visit(self, node): for child in ast.iter_child_nodes(node): child.parent = node return super().generic_visit(node) def visit_Import(self, node): # noqa: N802 if node.col_offset == 0 or self._type_checking_import(node): modules = [alias.name for alias in node.names] types_ = {self._classify_type(module) for module in modules} if len(types_) == 1: type_ = types_.pop() else: type_ = ImportType.MIXED classified_import = ClassifiedImport( type_, False, modules, [], node.lineno, node.end_lineno, 0, root_package_name(modules[0]), self._type_checking_import(node), ) self.imports.append(classified_import) def visit_ImportFrom(self, node): # noqa: N802 if node.col_offset == 0 or self._type_checking_import(node): module = node.module or "" if node.level > 0: type_ = ImportType.APPLICATION_RELATIVE else: type_ = self._classify_type(module) names = [alias.name for alias in node.names] classified_import = ClassifiedImport( type_, True, [module], names, node.lineno, node.end_lineno, node.level, root_package_name(module), self._type_checking_import(node), ) self.imports.append(classified_import) def _type_checking_import(self, node): return isinstance(node.parent, ast.If) and ( ( isinstance(node.parent.test, ast.Name) and node.parent.test.id == "TYPE_CHECKING" ) or ( isinstance(node.parent.test, ast.Attribute) and isinstance(node.parent.test.value, ast.Name) and node.parent.test.value.id in {"t", "typing"} and getattr(node.parent.test, "attr", "") == "TYPE_CHECKING" ) ) def _classify_type(self, module): package_names = get_package_names(module) # Walk through package names from most-specific to least-specific, # taking the first match found. for package in reversed(package_names): if package == "__future__": return ImportType.FUTURE elif package in self.application_import_names: return ImportType.APPLICATION elif package in self.application_package_names: return ImportType.APPLICATION_PACKAGE elif package in STDLIB_NAMES: return ImportType.STDLIB # Not future, stdlib or an application import. # Must be 3rd party. return ImportType.THIRD_PARTY flake8-import-order-0.19.2/flake8_import_order/stdlib_list.py0000664000175000017500000001122415026506505024153 0ustar jriverojriveroSTDLIB_NAMES = { "AL", "BaseHTTPServer", "Bastion", "Binary", "Boolean", "CGIHTTPServer", "ColorPicker", "ConfigParser", "Cookie", "DEVICE", "DocXMLRPCServer", "EasyDialogs", "FL", "FrameWork", "GL", "HTMLParser", "MacOS", "Mapping", "MimeWriter", "MiniAEFrame", "Numeric", "Queue", "SUNAUDIODEV", "ScrolledText", "Sequence", "Set", "SimpleHTTPServer", "SimpleXMLRPCServer", "SocketServer", "StringIO", "Text", "Tix", "Tkinter", "UserDict", "UserList", "UserString", "__builtin__", "__future__", "__main__", "_dummy_thread", "_thread", "_threading_local", "_winapi", "abc", "aepack", "aetools", "aetypes", "aifc", "al", "anydbm", "argparse", "array", "ast", "asynchat", "asyncio", "asyncore", "atexit", "audioop", "autoGIL", "base64", "bdb", "binascii", "binhex", "bisect", "bsddb", "builtins", "bz2", "cPickle", "cProfile", "cStringIO", "calendar", "cd", "cgi", "cgitb", "chunk", "cmath", "cmd", "code", "codecs", "codeop", "collections", "colorsys", "commands", "compileall", "concurrent", "configparser", "contextlib", "contextvars", "cookielib", "copy", "copy_reg", "copyreg", "crypt", "csv", "ctypes", "curses", "dataclasses", "datetime", "dbhash", "dbm", "decimal", "difflib", "dircache", "dis", "distutils", "dl", "doctest", "dumbdbm", "dummy_thread", "dummy_threading", "email", "encodings", "ensurepip", "enum", "errno", "faulthandler", "fcntl", "filecmp", "fileinput", "findertools", "fl", "flp", "fm", "fnmatch", "formatter", "fpectl", "fpformat", "fractions", "ftplib", "functools", "future_builtins", "gc", "gdbm", "gensuitemodule", "getopt", "getpass", "gettext", "gl", "glob", "grp", "gzip", "hashlib", "heapq", "hmac", "hotshot", "html", "htmlentitydefs", "htmllib", "http", "httplib", "ic", "imageop", "imaplib", "imgfile", "imghdr", "imp", "importlib", "imputil", "inspect", "io", "ipaddress", "itertools", "jpeg", "json", "keyword", "lib2to3", "linecache", "locale", "logging", "lzma", "macostools", "macpath", "macurl2path", "mailbox", "mailcap", "marshal", "math", "md5", "mhlib", "mimetools", "mimetypes", "mimify", "mmap", "modulefinder", "msilib", "multifile", "multiprocessing", "mutex", "netrc", "new", "nis", "nntplib", "ntpath", "nturl2path", "numbers", "operator", "optparse", "os", "os2emxpath", "ossaudiodev", "parser", "pathlib", "pdb", "pickle", "pickletools", "pipes", "pkgutil", "platform", "plistlib", "popen2", "poplib", "posix", "posixfile", "posixpath", "pprint", "profile", "pstats", "pty", "pwd", "py_compile", "pyclbr", "pydoc", "pyexpat", "queue", "quopri", "random", "re", "readline", "repr", "reprlib", "resource", "rexec", "rfc822", "rlcompleter", "robotparser", "runpy", "sched", "secrets", "select", "selectors", "sets", "sgmllib", "sha", "shelve", "shlex", "shutil", "signal", "site", "smtpd", "smtplib", "sndhdr", "socket", "socketserver", "spwd", "sqlite3", "ssl", "stat", "statistics", "statvfs", "string", "stringprep", "struct", "subprocess", "sunau", "sunaudiodev", "symbol", "symtable", "sys", "sysconfig", "syslog", "tabnanny", "tarfile", "telnetlib", "tempfile", "termios", "test", "textwrap", "thread", "threading", "time", "timeit", "tkinter", "token", "tokenize", "trace", "traceback", "tracemalloc", "ttk", "tty", "turtle", "turtledemo", "types", "typing", "unicodedata", "unittest", "urllib", "urllib2", "urlparse", "user", "uu", "uuid", "venv", "warnings", "wave", "weakref", "webbrowser", "whichdb", "winsound", "wsgiref", "xdrlib", "xml", "xmlrpc", "xmlrpclib", "zipapp", "zipfile", "zipimport", "zlib", "zoneinfo", } flake8-import-order-0.19.2/flake8_import_order/flake8_linter.py0000664000175000017500000000635115026506505024373 0ustar jriverojriveroimport optparse from flake8_import_order import __version__ from flake8_import_order.checker import DEFAULT_IMPORT_ORDER_STYLE from flake8_import_order.checker import ImportOrderChecker from flake8_import_order.styles import list_entry_points from flake8_import_order.styles import lookup_entry_point class Linter(ImportOrderChecker): name = "import-order" version = __version__ def __init__(self, tree, filename, lines=None): super().__init__(filename, tree) self.lines = lines @classmethod def add_options(cls, parser): # List of application import names. They go last. register_opt( parser, "--application-import-names", default="", action="store", type=str, help="Import names to consider as application-specific", parse_from_config=True, comma_separated_list=True, ) register_opt( parser, "--application-package-names", default="", action="store", type=str, help=( "Package names to consider as company-specific " "(used only by 'appnexus' style)" ), parse_from_config=True, comma_separated_list=True, ) register_opt( parser, "--import-order-style", default=DEFAULT_IMPORT_ORDER_STYLE, action="store", type=str, help=( "Style to follow. Available: " ", ".join(cls.list_available_styles()) ), parse_from_config=True, ) @staticmethod def list_available_styles(): entry_points = list_entry_points() return sorted(entry_point.name for entry_point in entry_points) @classmethod def parse_options(cls, options): optdict = {} names = options.application_import_names if not isinstance(names, list): names = options.application_import_names.split(",") pkg_names = options.application_package_names if not isinstance(pkg_names, list): pkg_names = options.application_package_names.split(",") style_entry_point = lookup_entry_point(options.import_order_style) optdict = dict( application_import_names=[n.strip() for n in names], application_package_names=[p.strip() for p in pkg_names], import_order_style=style_entry_point, ) cls.options = optdict def error(self, error): return ( error.lineno, 0, f"{error.code} {error.message}", Linter, ) def run(self): yield from self.check_order() def register_opt(parser, *args, **kwargs): try: # Flake8 3.x registration parser.add_option(*args, **kwargs) except (optparse.OptionError, TypeError): # Flake8 2.x registration parse_from_config = kwargs.pop("parse_from_config", False) kwargs.pop("comma_separated_list", False) kwargs.pop("normalize_paths", False) parser.add_option(*args, **kwargs) if parse_from_config: parser.config_options.append(args[-1].lstrip("-")) flake8-import-order-0.19.2/flake8_import_order/pylama_linter.py0000664000175000017500000000177215026506505024506 0ustar jriverojriverofrom pylama.lint import Linter as BaseLinter from flake8_import_order import __version__ from flake8_import_order.checker import DEFAULT_IMPORT_ORDER_STYLE from flake8_import_order.checker import ImportOrderChecker from flake8_import_order.styles import lookup_entry_point class Linter(ImportOrderChecker, BaseLinter): name = "import-order" version = __version__ def __init__(self): super().__init__(None, None) def allow(self, path): return path.endswith(".py") def error(self, error): return { "lnum": error.lineno, "col": 0, "text": error.message, "type": error.code, } def run(self, path, **meta): self.filename = path self.ast_tree = None meta.setdefault("import_order_style", DEFAULT_IMPORT_ORDER_STYLE) meta["import_order_style"] = lookup_entry_point( meta["import_order_style"] ) self.options = meta yield from self.check_order() flake8-import-order-0.19.2/flake8_import_order/__about__.py0000664000175000017500000000123015026506505023541 0ustar jriverojrivero__all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", "__maintainer__", "__maintainer_email__", ] __title__ = "flake8-import-order" __summary__ = ( "Flake8 and pylama plugin that checks the ordering of import statements." ) __uri__ = "https://github.com/PyCQA/flake8-import-order" __version__ = "0.19.2" __author__ = "Alex Stapleton" __email__ = "alexs@prol.etari.at" __maintainer__ = "Phil Jones" __maintainer_email__ = "philip.graham.jones+flake8-import@gmail.com" __license__ = "LGPLv3" __copyright__ = "Copyright 2013-2016 %s" % __author__ flake8-import-order-0.19.2/flake8_import_order/styles.py0000664000175000017500000002323415026506505023166 0ustar jriverojriveroimport importlib.metadata from collections import namedtuple from flake8_import_order import ClassifiedImport from flake8_import_order import ImportType from flake8_import_order import NewLine Error = namedtuple("Error", ["lineno", "code", "message"]) def list_entry_points(): entry_points = importlib.metadata.entry_points() if not hasattr(entry_points, "select"): return entry_points.get("flake8_import_order.styles", []) return entry_points.select(group="flake8_import_order.styles") def lookup_entry_point(name): for style in list_entry_points(): if style.name == name: return style raise LookupError(f"Unknown style {name}") class Style: accepts_application_package_names = False def __init__(self, nodes): self.nodes = nodes def check(self): previous = None previous_import = None for current in self.nodes: if isinstance(current, ClassifiedImport): yield from self._check(previous_import, previous, current) previous_import = current previous = current def _check(self, previous_import, previous, current_import): yield from self._check_I666(current_import) yield from self._check_I101(current_import) if ( previous_import is not None and not previous_import.type_checking and current_import.type_checking ): yield from self._check_I300(previous_import, current_import) previous_import = None if previous_import is not None: yield from self._check_I100(previous_import, current_import) yield from self._check_I201( previous_import, previous, current_import ) yield from self._check_I202( previous_import, previous, current_import ) def _check_I666(self, current_import): # noqa: N802 if current_import.type == ImportType.MIXED: yield Error( current_import.lineno, "I666", "Import statement mixes groups", ) def _check_I101(self, current_import): # noqa: N802 correct_names = self.sorted_names(current_import.names) if correct_names != current_import.names: corrected = ", ".join(correct_names) yield Error( current_import.lineno, "I101", "Imported names are in the wrong order. " "Should be {}".format(corrected), ) def _check_I300(self, previous_import, current_import): # noqa: N802 if current_import.lineno - previous_import.end_lineno != 3: yield Error( current_import.lineno, "I300", "TYPE_CHECKING block should have one newline above.", ) def _check_I100(self, previous_import, current_import): # noqa: N802 previous_key = self.import_key(previous_import) current_key = self.import_key(current_import) if previous_key > current_key: message = ( "Import statements are in the wrong order. " "'{}' should be before '{}'" ).format( self._explain_import(current_import), self._explain_import(previous_import), ) same_section = self.same_section( previous_import, current_import, ) if not same_section: message = f"{message} and in a different group." yield Error(current_import.lineno, "I100", message) def _check_I201( # noqa: N802 self, previous_import, previous, current_import ): same_section = self.same_section(previous_import, current_import) has_newline = isinstance(previous, NewLine) if not same_section and not has_newline: yield Error( current_import.lineno, "I201", "Missing newline between import groups. {}".format( self._explain_grouping( current_import, previous_import, ) ), ) def _check_I202( # noqa: N802 self, previous_import, previous, current_import ): same_section = self.same_section(previous_import, current_import) has_newline = isinstance(previous, NewLine) if same_section and has_newline: yield Error( current_import.lineno, "I202", "Additional newline in a group of imports. {}".format( self._explain_grouping( current_import, previous_import, ) ), ) @staticmethod def sorted_names(names): return names @staticmethod def import_key(import_): return (import_.type,) @staticmethod def same_section(previous, current): same_type = current.type == previous.type both_first = {previous.type, current.type} <= { ImportType.APPLICATION, ImportType.APPLICATION_RELATIVE, } return same_type or both_first @staticmethod def _explain_import(import_): if import_.is_from: return "from {}{} import {}".format( import_.level * ".", ", ".join(import_.modules), ", ".join(import_.names), ) else: return "import {}".format(", ".join(import_.modules)) @staticmethod def _explain_grouping(current_import, previous_import): return ( "'{}' is identified as {} and " "'{}' is identified as {}." ).format( Style._explain_import(current_import), current_import.type.name.title().replace("_", " "), Style._explain_import(previous_import), previous_import.type.name.title().replace("_", " "), ) class PEP8(Style): pass class Google(Style): @staticmethod def sorted_names(names): return sorted(names, key=Google.name_key) @staticmethod def name_key(name): return (name.lower(), name) @staticmethod def import_key(import_): modules = [Google.name_key(module) for module in import_.modules] names = [Google.name_key(name) for name in import_.names] return (import_.type, import_.level, modules, names) class AppNexus(Google): accepts_application_package_names = True class Smarkets(Style): @staticmethod def sorted_names(names): return sorted(names, key=Smarkets.name_key) @staticmethod def name_key(name): return (name.lower(), name) @staticmethod def import_key(import_): modules = [Smarkets.name_key(module) for module in import_.modules] names = [Smarkets.name_key(name) for name in import_.names] return (import_.type, import_.is_from, import_.level, modules, names) class Edited(Smarkets): accepts_application_package_names = True def _check_I202( # noqa: N802 self, previous_import, previous, current_import ): same_section = self.same_section(previous_import, current_import) has_newline = isinstance(previous, NewLine) optional_split = ( current_import.is_from and not previous_import.is_from ) if same_section and has_newline and not optional_split: yield Error( current_import.lineno, "I202", "Additional newline in a group of imports. {}".format( self._explain_grouping( current_import, previous_import, ) ), ) @staticmethod def same_section(previous, current): return current.type == previous.type class PyCharm(Smarkets): @staticmethod def sorted_names(names): return sorted(names) @staticmethod def import_key(import_): return ( import_.type, import_.is_from, import_.level, import_.modules, import_.names, ) class ISort(PyCharm): @staticmethod def name_key(name): # Group by CONSTANT, Class, func. group = 0 if name.isupper() else 2 if name.islower() else 1 return (group, name) @staticmethod def sorted_names(names): return sorted(names, key=ISort.name_key) class Cryptography(Style): @staticmethod def sorted_names(names): return sorted(names) @staticmethod def import_key(import_): if import_.type in {ImportType.THIRD_PARTY, ImportType.APPLICATION}: return ( import_.type, import_.package, import_.is_from, import_.level, import_.modules, import_.names, ) else: return ( import_.type, "", import_.is_from, import_.level, import_.modules, import_.names, ) @staticmethod def same_section(previous, current): app_or_third = current.type in { ImportType.THIRD_PARTY, ImportType.APPLICATION, } same_type = current.type == previous.type both_relative = ( previous.type == current.type == ImportType.APPLICATION_RELATIVE ) same_package = previous.package == current.package return (not app_or_third and same_type or both_relative) or ( app_or_third and same_package ) flake8-import-order-0.19.2/flake8_import_order/checker.py0000664000175000017500000000637515026506505023256 0ustar jriverojriveroimport ast import re from itertools import chain import pycodestyle from flake8_import_order import ImportVisitor from flake8_import_order import NewLine from flake8_import_order.styles import lookup_entry_point DEFAULT_IMPORT_ORDER_STYLE = "cryptography" NOQA_INLINE_REGEXP = re.compile( # We're looking for items that look like this: # ``# noqa`` # ``# noqa: E123`` # ``# noqa: E123,W451,F921`` # ``# NoQA: E123,W451,F921`` # ``# NOQA: E123,W451,F921`` # We do not care about the ``: `` that follows ``noqa`` # We do not care about the casing of ``noqa`` # We want a comma-separated list of errors r"# noqa(?:: (?P([A-Z][0-9]+(?:[,\s]+)?)+))?", re.IGNORECASE, ) COMMA_SEPARATED_LIST_RE = re.compile(r"[,\s]") BLANK_LINE_RE = re.compile(r"\s*\n") class ImportOrderChecker: visitor_class = ImportVisitor options = None def __init__(self, filename, tree): self.tree = tree self.filename = filename self.lines = None def load_file(self): if self.filename in ("stdin", "-", None): self.filename = "stdin" self.lines = pycodestyle.stdin_get_value().splitlines(True) else: self.lines = pycodestyle.readlines(self.filename) if self.tree is None: self.tree = ast.parse("".join(self.lines)) def error(self, error): return error def check_order(self): if not self.tree or not self.lines: self.load_file() try: style_entry_point = self.options["import_order_style"] except KeyError: style_entry_point = lookup_entry_point(DEFAULT_IMPORT_ORDER_STYLE) style_cls = style_entry_point.load() if style_cls.accepts_application_package_names: visitor = self.visitor_class( self.options.get("application_import_names", []), self.options.get("application_package_names", []), ) else: visitor = self.visitor_class( self.options.get("application_import_names", []), [], ) visitor.visit(self.tree) newlines = [ NewLine(lineno) # Lines are ordinal, no zero line for lineno, line in enumerate(self.lines, start=1) if BLANK_LINE_RE.match(line) ] # Replace the below with heapq merge, when Python2 is dropped. combined = sorted( chain(newlines, visitor.imports), key=lambda element: element.lineno, ) style = style_cls(combined) for error in style.check(): if not self.error_is_ignored(error): yield self.error(error) def error_is_ignored(self, error): noqa_match = NOQA_INLINE_REGEXP.search(self.lines[error.lineno - 1]) if noqa_match is None: return False codes_str = noqa_match.group("codes") if codes_str is None: return True codes = parse_comma_separated_list(codes_str) if error.code in codes: return True return False def parse_comma_separated_list(value): value = COMMA_SEPARATED_LIST_RE.split(value) item_gen = (item.strip() for item in value) return {item for item in item_gen if item} flake8-import-order-0.19.2/setup.py0000664000175000017500000000010515026506505017034 0ustar jriverojriverofrom setuptools import setup if __name__ == "__main__": setup() flake8-import-order-0.19.2/tox.ini0000664000175000017500000000137615026506505016650 0ustar jriverojrivero[tox] envlist = py3{9,10,11,12,13},pypy3,pep8,release-check [testenv] deps = flake8 pytest pytest-cov commands = pytest --cov --capture=no --strict-markers {posargs} [testenv:pep8] deps = flake8 flake8-import-order pep8-naming commands = flake8 flake8_import_order/ tests/ [testenv:setuppy] deps = docutils Pygments commands = python setup.py check \ --metadata \ --restructuredtext [testenv:manifest] deps = check-manifest commands = check-manifest [testenv:release-check] deps = build twine commands = python -m build twine check dist/* [testenv:release] deps = build twine commands = python -m build twine check dist/* twine upload {posargs:--skip-existing} dist/* flake8-import-order-0.19.2/setup.cfg0000664000175000017500000000376515026506505017162 0ustar jriverojrivero[metadata] name = flake8_import_order version = attr: flake8_import_order.__about__.__version__ description = Flake8 and pylama plugin that checks the ordering of import statements. long_description = file: README.rst long_description_content_type = text/x-rst url = https://github.com/PyCQA/flake8-import-order author = Alex Stapleton author_email = alexs@prol.etari.at maintainer = Phil Jones maintainer_email = philip.graham.jones+flake8-import@gmail.com license = LGPLv3 classifiers = Development Status :: 4 - Beta Framework :: Flake8 Intended Audience :: Developers Operating System :: OS Independent Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries :: Python Modules Topic :: Software Development :: Quality Assurance [options] packages = find: py_modules = flake8_import_order install_requires = pycodestyle setuptools python_requires > = 3.9 tests_require = pytest flake8 pycodestyle pylama zip_safe = False [options.packages.find] exclude = tests tests.* [options.entry_points] flake8.extension = I = flake8_import_order.flake8_linter:Linter flake8_import_order.styles = cryptography = flake8_import_order.styles:Cryptography google = flake8_import_order.styles:Google isort = flake8_import_order.styles:ISort pep8 = flake8_import_order.styles:PEP8 smarkets = flake8_import_order.styles:Smarkets appnexus = flake8_import_order.styles:AppNexus edited = flake8_import_order.styles:Edited pycharm = flake8_import_order.styles:PyCharm pylama.linter = import_order = flake8_import_order.pylama_linter:Linter [check-manifest] ignore = tox.ini [flake8] exclude = .tox,*.egg,tests/test_cases/ select = E,W,F,N,I ignore = W503 application-import-names = flake8_import_order,tests max-line-length = 88 max-complexity = 10 [coverage:run] source = flake8_import_order [coverage:report] show_missing = True flake8-import-order-0.19.2/COPYING0000664000175000017500000001674315026506505016374 0ustar jriverojrivero GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. flake8-import-order-0.19.2/CHANGELOG.rst0000664000175000017500000001315315026506505017352 0ustar jriverojrivero0.19.2 2025-06-24 ----------------- * Fix ``AttributeError`` in code for checking the order of imports in a ``TYPE_CHECKING`` block. * Yank 0.19.0 from PyPI to avoid aforementioned users on older versions of Python accidentally receiving 0.19.0 0.19.1 2025-06-20 ----------------- * Fix bug in code for checking the order of imports in a ``TYPE_CHECKING`` block. * Fix ``python_requires`` missing from 0.19.0 to allow folks using vulnerable and unmaintained versions of Python to continue running 0.18.x with pip selecting the correct version for them. 0.19.0 2025-06-12 ----------------- * Drop deprecated Python versions 3.7 and 3.8 * Drop unsupported pypy3.9 * Use ``sys.stdlib_module_names`` where available to determine whether a module is in the standard library or not. * Replace usage of ``pkg_resources`` from setuptools with ``importlib.metadata``. * Move package to PyCQA PyPI organization * Add support for checking ordering in a ``TYPE_CHECKING`` block. This will support the following variants: * ``if TYPE_CHECKING:`` (where there was a prior ``from typing import TYPE_CHECKING``). * ``if t.TYPE_CHECKING:`` (where there was a prior ``import typing as t``). * ``if typing.TYPE_CHECKING:`` 0.18.2 2022-11-26 ----------------- * Add ``zoneinfo`` to list of standard library modules * Fix registering of options with Flake8 >= 6.0 0.18.1 2019-03-04 ----------------- * Fix case-sensitive related I100 errors for the pycharm style * Fix noqa regexp 0.18 2018-07-08 --------------- * Add new Python 3.7 modules to the stdlib list, and support 3.7. 0.17.1 2018-03-05 ----------------- * Rebuild of 0.17 with the latest setuptools to fix an enum34 dependency bug. 0.17 2018-02-11 --------------- * Add all Python3 modules to stdlib list (should be no more missing modules). * Clarify the error messages (more context). * Allow styles to override specific checks. * Correct the edited style to match the actual edited style guide. * Add pycharm style, to match the pycharm auto formatter. 0.16 2017-11-26 --------------- * Change spacing determination to consider only blank newlines as a space. This adds NewLine nodes to the checker and hence could break custom styles (that use the nodes directly). This also drops the asttokens dependency as it is no longer required. * Understand the existence of namespaced packages, thereby allowing different namespaced packages to be defined as local or third party. 0.15 2017-11-06 --------------- * Drop Python 3.3 support, as Python 3.3 is beyond it's end of lfe. * Correct the flake8 entrypoint to report all ``I`` errors, this may result in ``I2XX`` errors being reported that were absent previously. * Support in-line ``# noqa`` comments specifying only the error codes to be ignored, e.g., ``# noqa: I101``. * Accept only ``# noqa`` directives on the line reporting the error, see limitations. 0.14.3 2017-11-01 ----------------- * Bug fix, allow for noqa directives to work with I202. 0.14.2 2017-10-30 ----------------- * Bug fix, ensure the plugin is invoked by flake8. 0.14.1 2017-10-27 ----------------- * Bug fix, cope with multi-line imports when considering I202. 0.14 2017-10-24 --------------- * Fixed I201 error raising for cryptography style. * Added I202 error when there is an additional newline in a section of imports. * Added ``ntpath`` and ``os2emxpath`` to stdlib list. 0.13 2017-07-29 --------------- * Added ``secrets`` to stdlib list. * Allow for any style to use application-package grouping. 0.12 2017-02-11 --------------- * Added new Edited style, this is equivalent to the Smarkets style except that values specified in the ``application-package-names`` option must be imported after third-party import statements * Added ability to extend a style using an entrypoint. * Fix ambiguous I100 error, now lists correct packages. 0.11 2016-11-09 --------------- * Enforce lexicographic ordering for Google, Smarkets and AppNexus styles. This may introduce warnings not present in previous releases relating to case sensitivity. * Fix I100 case sensitivity for ungrouped imports, again enforcing lexicographic ordering. 0.10 2016-10-16 --------------- * Added new AppNexus style, this is equivalent to the google style except that values specified in the `application-package-names` option must be imported after third-party import statements * Fixed ungrouped ordering bug whereby I100 wasn't triggered. 0.9.2 2016-08-05 ---------------- * Fix error when checking from stdin using flake8 3.0. 0.9.1 2016-07-27 ---------------- * Fix case sensitivity bug for Google and Smarkets style. 0.9 2016-07-26 -------------- * Drop pep8 requirement and replace with pycodestyle. * Support Flake8 3.0 (alongside Flake8 2.X). * Drop Python2.6 compatibility. * Fixed a bug where intermixed 1st and 3rd party imports cause an error with the PEP8 style. * Fixed a bug whereby the I101 recommended ordering wasn't a valid ordering in the cryptography style. 0.8 --- * Added profile, cProfile, pstats and typing to stdlib list. * Added new PEP8 style, that enforces grouping of importes but allows any ordering within the groups. 0.7 --- * Added new Smarkets style, this is equivalent to the google style except that any `import X` statements must come before any `from X import y` statements. 0.6.2 ----- * Fixed a bug where I101 messages were not suggesting the correct order in the default style. The output message now outputs a message that matches the selected style. 0.6.1 ----- * Fixed a bug where I101 messages were not suggesting the correct order. * Extended test harness to be able to check error messages as well as codes. flake8-import-order-0.19.2/MANIFEST.in0000664000175000017500000000022315026506505017061 0ustar jriverojriveroinclude .pre-commit-config.yaml include CHANGELOG.rst include COPYING include README.md recursive-include tests * recursive-exclude tests *.py[co] flake8-import-order-0.19.2/.gitignore0000664000175000017500000000010315026506505017310 0ustar jriverojrivero*.pyc *.pyo __pycache__ *.egg-info *~ .coverage .tox/ build/ dist/ flake8-import-order-0.19.2/tests/0000775000175000017500000000000015026506505016470 5ustar jriverojriveroflake8-import-order-0.19.2/tests/__init__.py0000664000175000017500000000000015026506505020567 0ustar jriverojriveroflake8-import-order-0.19.2/tests/test_stdlib.py0000664000175000017500000000143015026506505021360 0ustar jriverojriveroimport ast # isort: off import pycodestyle import pytest # isort: on from flake8_import_order import STDLIB_NAMES from flake8_import_order.checker import ImportOrderChecker def _load_test_cases(): test_cases = [] for name in STDLIB_NAMES: if not name.startswith("__"): test_cases.append(name) return test_cases def _checker(data): pycodestyle.stdin_get_value = lambda: data tree = ast.parse(data) checker = ImportOrderChecker(None, tree) checker.options = {} return checker @pytest.mark.parametrize("import_name", _load_test_cases()) def test_styles(import_name): data = f"import {import_name}\nimport zoneinfo\n" checker = _checker(data) codes = [error.code for error in checker.check_order()] assert codes == [] flake8-import-order-0.19.2/tests/test_style_cases.py0000664000175000017500000000437015026506505022423 0ustar jriverojriveroimport ast import glob import os import re import sys import pytest from flake8_import_order.checker import ImportOrderChecker from flake8_import_order.styles import lookup_entry_point ERROR_RX = re.compile("# ((I[0-9]{3} ?)+) ?.*$") def _extract_expected_errors(data): lines = data.splitlines() expected_codes = [] for line in lines: match = ERROR_RX.search(line) if match is not None: codes = match.group(1).split() expected_codes.extend(codes) return expected_codes def _load_test_cases(): base_path = os.path.dirname(__file__) test_cases = [] test_case_path = os.path.join(base_path, "test_cases") wildcard_path = os.path.join(test_case_path, "*.py") for filename in glob.glob(wildcard_path): # The namespace.py test only works with Python3 if filename.endswith("namespace.py") and sys.version_info.major < 3: continue fullpath = os.path.join(test_case_path, filename) with open(fullpath) as file_: data = file_.read() styles = data.splitlines()[0].lstrip("#").strip().split() codes = _extract_expected_errors(data) tree = ast.parse(data, fullpath) for style_name in styles: style_entry_point = lookup_entry_point(style_name) test_cases.append((filename, tree, style_entry_point, codes)) return test_cases def _checker(filename, tree, style_entry_point): options = { "application_import_names": [ "flake8_import_order", "namespace.package_b", "tests", ], "application_package_names": ["localpackage"], "import_order_style": style_entry_point, } checker = ImportOrderChecker(filename, tree) checker.options = options return checker @pytest.mark.parametrize( "filename, tree, style, expected_codes", _load_test_cases(), ) def test_styles(filename, tree, style, expected_codes): checker = _checker(filename, tree, style) codes = [ (filename, error.lineno, error.code) for error in checker.check_order() ] assert [i[2] for i in codes] == expected_codes, codes def test_unknown_style(): with pytest.raises(LookupError): lookup_entry_point("Unknown") flake8-import-order-0.19.2/tests/test_cases/0000775000175000017500000000000015026506505020625 5ustar jriverojriveroflake8-import-order-0.19.2/tests/test_cases/complete_cryptography_alt1.py0000664000175000017500000000255515026506505026552 0ustar jriverojrivero# cryptography import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if t.TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/wrong_from_import_order.py0000664000175000017500000000033515026506505026144 0ustar jriverojrivero# appnexus edited google smarkets from A import a, A # I101 from B import b, A # I101 from C import b, a # I101 if TYPE_CHECKING: from A import a, A # I101 from B import b, A # I101 from C import b, a # I101 flake8-import-order-0.19.2/tests/test_cases/bug_212_regression.py0000664000175000017500000006605215026506505024611 0ustar jriverojrivero# google # testing/plugin/pytestplugin.py # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors # # # This module is part of SQLAlchemy and is released under # the MIT License: https://www.opensource.org/licenses/mit-license.php # mypy: ignore-errors from __future__ import annotations import argparse import collections from functools import update_wrapper import inspect import itertools import operator import os import re import sys from typing import TYPE_CHECKING import uuid import pytest try: # installed by bootstrap.py if not TYPE_CHECKING: import sqla_plugin_base as plugin_base except ImportError: # assume we're a package, use traditional import from . import plugin_base def pytest_addoption(parser): group = parser.getgroup("sqlalchemy") def make_option(name, **kw): callback_ = kw.pop("callback", None) if callback_: class CallableAction(argparse.Action): def __call__( self, parser, namespace, values, option_string=None ): callback_(option_string, values, parser) kw["action"] = CallableAction zeroarg_callback = kw.pop("zeroarg_callback", None) if zeroarg_callback: class CallableAction(argparse.Action): def __init__( self, option_strings, dest, default=False, required=False, help=None, # noqa ): super().__init__( option_strings=option_strings, dest=dest, nargs=0, const=True, default=default, required=required, help=help, ) def __call__( self, parser, namespace, values, option_string=None ): zeroarg_callback(option_string, values, parser) kw["action"] = CallableAction group.addoption(name, **kw) plugin_base.setup_options(make_option) def pytest_configure(config: pytest.Config): plugin_base.read_config(config.rootpath) if plugin_base.exclude_tags or plugin_base.include_tags: new_expr = " and ".join( list(plugin_base.include_tags) + [f"not {tag}" for tag in plugin_base.exclude_tags] ) if config.option.markexpr: config.option.markexpr += f" and {new_expr}" else: config.option.markexpr = new_expr if config.pluginmanager.hasplugin("xdist"): config.pluginmanager.register(XDistHooks()) if hasattr(config, "workerinput"): plugin_base.restore_important_follower_config(config.workerinput) plugin_base.configure_follower(config.workerinput["follower_ident"]) else: if config.option.write_idents and os.path.exists( config.option.write_idents ): os.remove(config.option.write_idents) plugin_base.pre_begin(config.option) plugin_base.set_coverage_flag( bool(getattr(config.option, "cov_source", False)) ) plugin_base.set_fixture_functions(PytestFixtureFunctions) if config.option.dump_pyannotate: global DUMP_PYANNOTATE DUMP_PYANNOTATE = True DUMP_PYANNOTATE = False @pytest.fixture(autouse=True) def collect_types_fixture(): if DUMP_PYANNOTATE: from pyannotate_runtime import collect_types collect_types.start() yield if DUMP_PYANNOTATE: collect_types.stop() def _log_sqlalchemy_info(session): import sqlalchemy from sqlalchemy import __version__ from sqlalchemy.util import has_compiled_ext from sqlalchemy.util._has_cython import _CYEXTENSION_MSG greet = "sqlalchemy installation" site = "no user site" if sys.flags.no_user_site else "user site loaded" msgs = [ f"SQLAlchemy {__version__} ({site})", f"Path: {sqlalchemy.__file__}", ] if has_compiled_ext(): from sqlalchemy.engine import _util_cy msgs.append(f"compiled extension enabled, e.g. {_util_cy.__file__} ") else: msgs.append(f"compiled extension not enabled; {_CYEXTENSION_MSG}") pm = session.config.pluginmanager.get_plugin("terminalreporter") if pm: pm.write_sep("=", greet) for m in msgs: pm.write_line(m) else: # fancy pants reporter not found, fallback to plain print print("=" * 25, greet, "=" * 25) for m in msgs: print(m) def pytest_sessionstart(session): from sqlalchemy.testing import asyncio _log_sqlalchemy_info(session) asyncio._assume_async(plugin_base.post_begin) def pytest_sessionfinish(session): from sqlalchemy.testing import asyncio asyncio._maybe_async_provisioning(plugin_base.final_process_cleanup) if session.config.option.dump_pyannotate: from pyannotate_runtime import collect_types collect_types.dump_stats(session.config.option.dump_pyannotate) def pytest_unconfigure(config): from sqlalchemy.testing import asyncio asyncio._shutdown() def pytest_collection_finish(session): if session.config.option.dump_pyannotate: from pyannotate_runtime import collect_types lib_sqlalchemy = os.path.abspath("lib/sqlalchemy") def _filter(filename): filename = os.path.normpath(os.path.abspath(filename)) if "lib/sqlalchemy" not in os.path.commonpath( [filename, lib_sqlalchemy] ): return None if "testing" in filename: return None return filename collect_types.init_types_collection(filter_filename=_filter) class XDistHooks: def pytest_configure_node(self, node): from sqlalchemy.testing import provision from sqlalchemy.testing import asyncio # the master for each node fills workerinput dictionary # which pytest-xdist will transfer to the subprocess plugin_base.memoize_important_follower_config(node.workerinput) node.workerinput["follower_ident"] = ( "test_%s" % uuid.uuid4().hex[0:12] ) asyncio._maybe_async_provisioning( provision.create_follower_db, node.workerinput["follower_ident"] ) def pytest_testnodedown(self, node, error): from sqlalchemy.testing import provision from sqlalchemy.testing import asyncio asyncio._maybe_async_provisioning( provision.drop_follower_db, node.workerinput["follower_ident"] ) def pytest_collection_modifyitems(session, config, items): # look for all those classes that specify __backend__ and # expand them out into per-database test cases. # this is much easier to do within pytest_pycollect_makeitem, however # pytest is iterating through cls.__dict__ as makeitem is # called which causes a "dictionary changed size" error on py3k. # I'd submit a pullreq for them to turn it into a list first, but # it's to suit the rather odd use case here which is that we are adding # new classes to a module on the fly. from sqlalchemy.testing import asyncio rebuilt_items = collections.defaultdict( lambda: collections.defaultdict(list) ) items[:] = [ item for item in items if item.getparent(pytest.Class) is not None and not item.getparent(pytest.Class).name.startswith("_") ] test_classes = {item.getparent(pytest.Class) for item in items} def collect(element): for inst_or_fn in element.collect(): if isinstance(inst_or_fn, pytest.Collector): yield from collect(inst_or_fn) else: yield inst_or_fn def setup_test_classes(): for test_class in test_classes: # transfer legacy __backend__ and __sparse_backend__ symbols # to be markers if getattr(test_class.cls, "__backend__", False) or getattr( test_class.cls, "__only_on__", False ): add_markers = {"backend"} elif getattr(test_class.cls, "__sparse_backend__", False): add_markers = {"sparse_backend"} else: add_markers = frozenset() existing_markers = { mark.name for mark in test_class.iter_markers() } add_markers = add_markers - existing_markers all_markers = existing_markers.union(add_markers) for marker in add_markers: test_class.add_marker(marker) for sub_cls in plugin_base.generate_sub_tests( test_class.cls, test_class.module, all_markers ): if sub_cls is not test_class.cls: per_cls_dict = rebuilt_items[test_class.cls] module = test_class.getparent(pytest.Module) new_cls = pytest.Class.from_parent( name=sub_cls.__name__, parent=module ) for marker in add_markers: new_cls.add_marker(marker) for fn in collect(new_cls): per_cls_dict[fn.name].append(fn) # class requirements will sometimes need to access the DB to check # capabilities, so need to do this for async asyncio._maybe_async_provisioning(setup_test_classes) newitems = [] for item in items: cls_ = item.cls if cls_ in rebuilt_items: newitems.extend(rebuilt_items[cls_][item.name]) else: newitems.append(item) # seems like the functions attached to a test class aren't sorted already? # is that true and why's that? (when using unittest, they're sorted) items[:] = sorted( newitems, key=lambda item: ( item.getparent(pytest.Module).name, item.getparent(pytest.Class).name, item.name, ), ) def pytest_pycollect_makeitem(collector, name, obj): if inspect.isclass(obj) and plugin_base.want_class(name, obj): from sqlalchemy.testing import config if config.any_async: obj = _apply_maybe_async(obj) return [ pytest.Class.from_parent( name=parametrize_cls.__name__, parent=collector ) for parametrize_cls in _parametrize_cls(collector.module, obj) ] elif ( inspect.isfunction(obj) and collector.cls is not None and plugin_base.want_method(collector.cls, obj) ): # None means, fall back to default logic, which includes # method-level parametrize return None else: # empty list means skip this item return [] def _is_wrapped_coroutine_function(fn): while hasattr(fn, "__wrapped__"): fn = fn.__wrapped__ return inspect.iscoroutinefunction(fn) def _apply_maybe_async(obj, recurse=True): from sqlalchemy.testing import asyncio for name, value in vars(obj).items(): if ( (callable(value) or isinstance(value, classmethod)) and not getattr(value, "_maybe_async_applied", False) and (name.startswith("test_")) and not _is_wrapped_coroutine_function(value) ): is_classmethod = False if isinstance(value, classmethod): value = value.__func__ is_classmethod = True @_pytest_fn_decorator def make_async(fn, *args, **kwargs): return asyncio._maybe_async(fn, *args, **kwargs) do_async = make_async(value) if is_classmethod: do_async = classmethod(do_async) do_async._maybe_async_applied = True setattr(obj, name, do_async) if recurse: for cls in obj.mro()[1:]: if cls != object: _apply_maybe_async(cls, False) return obj def _parametrize_cls(module, cls): """implement a class-based version of pytest parametrize.""" if "_sa_parametrize" not in cls.__dict__: return [cls] _sa_parametrize = cls._sa_parametrize classes = [] for full_param_set in itertools.product( *[params for argname, params in _sa_parametrize] ): cls_variables = {} for argname, param in zip( [_sa_param[0] for _sa_param in _sa_parametrize], full_param_set ): if not argname: raise TypeError("need argnames for class-based combinations") argname_split = re.split(r",\s*", argname) for arg, val in zip(argname_split, param.values): cls_variables[arg] = val parametrized_name = "_".join( re.sub(r"\W", "", token) for param in full_param_set for token in param.id.split("-") ) name = "%s_%s" % (cls.__name__, parametrized_name) newcls = type.__new__(type, name, (cls,), cls_variables) setattr(module, name, newcls) classes.append(newcls) return classes _current_class = None def pytest_runtest_setup(item): from sqlalchemy.testing import asyncio # pytest_runtest_setup runs *before* pytest fixtures with scope="class". # plugin_base.start_test_class_outside_fixtures may opt to raise SkipTest # for the whole class and has to run things that are across all current # databases, so we run this outside of the pytest fixture system altogether # and ensure asyncio greenlet if any engines are async global _current_class if isinstance(item, pytest.Function) and _current_class is None: asyncio._maybe_async_provisioning( plugin_base.start_test_class_outside_fixtures, item.cls, ) _current_class = item.getparent(pytest.Class) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_teardown(item, nextitem): # runs inside of pytest function fixture scope # after test function runs from sqlalchemy.testing import asyncio asyncio._maybe_async(plugin_base.after_test, item) yield # this is now after all the fixture teardown have run, the class can be # finalized. Since pytest v7 this finalizer can no longer be added in # pytest_runtest_setup since the class has not yet been setup at that # time. # See https://github.com/pytest-dev/pytest/issues/9343 global _current_class, _current_report if _current_class is not None and ( # last test or a new class nextitem is None or nextitem.getparent(pytest.Class) is not _current_class ): _current_class = None try: asyncio._maybe_async_provisioning( plugin_base.stop_test_class_outside_fixtures, item.cls ) except Exception as e: # in case of an exception during teardown attach the original # error to the exception message, otherwise it will get lost if _current_report.failed: if not e.args: e.args = ( "__Original test failure__:\n" + _current_report.longreprtext, ) elif e.args[-1] and isinstance(e.args[-1], str): args = list(e.args) args[-1] += ( "\n__Original test failure__:\n" + _current_report.longreprtext ) e.args = tuple(args) else: e.args += ( "__Original test failure__", _current_report.longreprtext, ) raise finally: _current_report = None def pytest_runtest_call(item): # runs inside of pytest function fixture scope # before test function runs from sqlalchemy.testing import asyncio asyncio._maybe_async( plugin_base.before_test, item, item.module.__name__, item.cls, item.name, ) _current_report = None def pytest_runtest_logreport(report): global _current_report if report.when == "call": _current_report = report @pytest.fixture(scope="class") def setup_class_methods(request): from sqlalchemy.testing import asyncio cls = request.cls if hasattr(cls, "setup_test_class"): asyncio._maybe_async(cls.setup_test_class) yield if hasattr(cls, "teardown_test_class"): asyncio._maybe_async(cls.teardown_test_class) asyncio._maybe_async(plugin_base.stop_test_class, cls) @pytest.fixture(scope="function") def setup_test_methods(request): from sqlalchemy.testing import asyncio # called for each test self = request.instance # before this fixture runs: # 1. function level "autouse" fixtures under py3k (examples: TablesTest # define tables / data, MappedTest define tables / mappers / data) # 2. was for p2k. no longer applies # 3. run outer xdist-style setup if hasattr(self, "setup_test"): asyncio._maybe_async(self.setup_test) # alembic test suite is using setUp and tearDown # xdist methods; support these in the test suite # for the near term if hasattr(self, "setUp"): asyncio._maybe_async(self.setUp) # inside the yield: # 4. function level fixtures defined on test functions themselves, # e.g. "connection", "metadata" run next # 5. pytest hook pytest_runtest_call then runs # 6. test itself runs yield # yield finishes: # 7. function level fixtures defined on test functions # themselves, e.g. "connection" rolls back the transaction, "metadata" # emits drop all # 8. pytest hook pytest_runtest_teardown hook runs, this is associated # with fixtures close all sessions, provisioning.stop_test_class(), # engines.testing_reaper -> ensure all connection pool connections # are returned, engines created by testing_engine that aren't the # config engine are disposed asyncio._maybe_async(plugin_base.after_test_fixtures, self) # 10. run xdist-style teardown if hasattr(self, "tearDown"): asyncio._maybe_async(self.tearDown) if hasattr(self, "teardown_test"): asyncio._maybe_async(self.teardown_test) # 11. was for p2k. no longer applies # 12. function level "autouse" fixtures under py3k (examples: TablesTest / # MappedTest delete table data, possibly drop tables and clear mappers # depending on the flags defined by the test class) def _pytest_fn_decorator(target): """Port of langhelpers.decorator with pytest-specific tricks.""" from sqlalchemy.util.langhelpers import format_argspec_plus from sqlalchemy.util.compat import inspect_getfullargspec def _exec_code_in_env(code, env, fn_name): # note this is affected by "from __future__ import annotations" at # the top; exec'ed code will use non-evaluated annotations # which allows us to be more flexible with code rendering # in format_argpsec_plus() exec(code, env) return env[fn_name] def decorate(fn, add_positional_parameters=()): spec = inspect_getfullargspec(fn) if add_positional_parameters: spec.args.extend(add_positional_parameters) metadata = dict( __target_fn="__target_fn", __orig_fn="__orig_fn", name=fn.__name__ ) metadata.update(format_argspec_plus(spec, grouped=False)) code = ( """\ def %(name)s%(grouped_args)s: return %(__target_fn)s(%(__orig_fn)s, %(apply_kw)s) """ % metadata ) decorated = _exec_code_in_env( code, {"__target_fn": target, "__orig_fn": fn}, fn.__name__ ) if not add_positional_parameters: decorated.__defaults__ = getattr(fn, "__func__", fn).__defaults__ decorated.__wrapped__ = fn return update_wrapper(decorated, fn) else: # this is the pytest hacky part. don't do a full update wrapper # because pytest is really being sneaky about finding the args # for the wrapped function decorated.__module__ = fn.__module__ decorated.__name__ = fn.__name__ if hasattr(fn, "pytestmark"): decorated.pytestmark = fn.pytestmark return decorated return decorate class PytestFixtureFunctions(plugin_base.FixtureFunctions): def skip_test_exception(self, *arg, **kw): return pytest.skip.Exception(*arg, **kw) @property def add_to_marker(self): return pytest.mark def mark_base_test_class(self): return pytest.mark.usefixtures( "setup_class_methods", "setup_test_methods" ) _combination_id_fns = { "i": lambda obj: obj, "r": repr, "s": str, "n": lambda obj: ( obj.__name__ if hasattr(obj, "__name__") else type(obj).__name__ ), } def combinations(self, *arg_sets, **kw): """Facade for pytest.mark.parametrize. Automatically derives argument names from the callable which in our case is always a method on a class with positional arguments. ids for parameter sets are derived using an optional template. """ from sqlalchemy.testing import exclusions if len(arg_sets) == 1 and hasattr(arg_sets[0], "__next__"): arg_sets = list(arg_sets[0]) argnames = kw.pop("argnames", None) def _filter_exclusions(args): result = [] gathered_exclusions = [] for a in args: if isinstance(a, exclusions.compound): gathered_exclusions.append(a) else: result.append(a) return result, gathered_exclusions id_ = kw.pop("id_", None) tobuild_pytest_params = [] has_exclusions = False if id_: _combination_id_fns = self._combination_id_fns # because itemgetter is not consistent for one argument vs. # multiple, make it multiple in all cases and use a slice # to omit the first argument _arg_getter = operator.itemgetter( 0, *[ idx for idx, char in enumerate(id_) if char in ("n", "r", "s", "a") ], ) fns = [ (operator.itemgetter(idx), _combination_id_fns[char]) for idx, char in enumerate(id_) if char in _combination_id_fns ] for arg in arg_sets: if not isinstance(arg, tuple): arg = (arg,) fn_params, param_exclusions = _filter_exclusions(arg) parameters = _arg_getter(fn_params)[1:] if param_exclusions: has_exclusions = True tobuild_pytest_params.append( ( parameters, param_exclusions, "-".join( comb_fn(getter(arg)) for getter, comb_fn in fns ), ) ) else: for arg in arg_sets: if not isinstance(arg, tuple): arg = (arg,) fn_params, param_exclusions = _filter_exclusions(arg) if param_exclusions: has_exclusions = True tobuild_pytest_params.append( (fn_params, param_exclusions, None) ) pytest_params = [] for parameters, param_exclusions, id_ in tobuild_pytest_params: if has_exclusions: parameters += (param_exclusions,) param = pytest.param(*parameters, id=id_) pytest_params.append(param) def decorate(fn): if inspect.isclass(fn): if has_exclusions: raise NotImplementedError( "exclusions not supported for class level combinations" ) if "_sa_parametrize" not in fn.__dict__: fn._sa_parametrize = [] fn._sa_parametrize.append((argnames, pytest_params)) return fn else: _fn_argnames = inspect.getfullargspec(fn).args[1:] if argnames is None: _argnames = _fn_argnames else: _argnames = re.split(r", *", argnames) if has_exclusions: existing_exl = sum( 1 for n in _fn_argnames if n.startswith("_exclusions") ) current_exclusion_name = f"_exclusions_{existing_exl}" _argnames += [current_exclusion_name] @_pytest_fn_decorator def check_exclusions(fn, *args, **kw): _exclusions = args[-1] if _exclusions: exlu = exclusions.compound().add(*_exclusions) fn = exlu(fn) return fn(*args[:-1], **kw) fn = check_exclusions( fn, add_positional_parameters=(current_exclusion_name,), ) return pytest.mark.parametrize(_argnames, pytest_params)(fn) return decorate def param_ident(self, *parameters): ident = parameters[0] return pytest.param(*parameters[1:], id=ident) def fixture(self, *arg, **kw): from sqlalchemy.testing import config from sqlalchemy.testing import asyncio # wrapping pytest.fixture function. determine if # decorator was called as @fixture or @fixture(). if len(arg) > 0 and callable(arg[0]): # was called as @fixture(), we have the function to wrap. fn = arg[0] arg = arg[1:] else: # was called as @fixture, don't have the function yet. fn = None # create a pytest.fixture marker. because the fn is not being # passed, this is always a pytest.FixtureFunctionMarker() # object (or whatever pytest is calling it when you read this) # that is waiting for a function. fixture = pytest.fixture(*arg, **kw) # now apply wrappers to the function, including fixture itself def wrap(fn): if config.any_async: fn = asyncio._maybe_async_wrapper(fn) # other wrappers may be added here # now apply FixtureFunctionMarker fn = fixture(fn) return fn if fn: return wrap(fn) else: return wrap def get_current_test_name(self): return os.environ.get("PYTEST_CURRENT_TEST") def async_test(self, fn): from sqlalchemy.testing import asyncio @_pytest_fn_decorator def decorate(fn, *args, **kwargs): asyncio._run_coroutine_function(fn, *args, **kwargs) return decorate(fn) flake8-import-order-0.19.2/tests/test_cases/missing_newline.py0000664000175000017500000000047415026506505024376 0ustar jriverojrivero# smarkets import ast # This comment should not prevent the I201 below, it is not a newline. import X # I201 import flake8_import_order # I201 if TYPE_CHECKING: import ast # I300 # This comment should not prevent the I201 below, it is not a newline. import X # I201 import flake8_import_order # I201 flake8-import-order-0.19.2/tests/test_cases/wrong_relative_order.py0000664000175000017500000000023215026506505025416 0ustar jriverojrivero# appnexus cryptography edited google smarkets from .. import A from . import B # I100 if TYPE_CHECKING: from .. import A from . import B # I100 flake8-import-order-0.19.2/tests/test_cases/namespace.py0000664000175000017500000000041315026506505023131 0ustar jriverojrivero# appnexus edited google smarkets import namespace import namespace.package_a import flake8_import_order import namespace.package_b if TYPE_CHECKING: import namespace import namespace.package_a import flake8_import_order import namespace.package_b flake8-import-order-0.19.2/tests/test_cases/complete_cryptography_alt0.py0000664000175000017500000000256215026506505026547 0ustar jriverojrivero# cryptography import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if typing.TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_appnexus_alt0.py0000664000175000017500000000223615026506505025655 0ustar jriverojrivero# appnexus import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if typing.TYPE_CHECKING: import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/wrong_import_order_cryptography.py0000664000175000017500000000016615026506505027736 0ustar jriverojrivero# cryptography import A import a import B # I100 if TYPE_CHECKING: import A import a import B # I100 flake8-import-order-0.19.2/tests/test_cases/missing_newline_cryptography.py0000664000175000017500000000021015026506505027175 0ustar jriverojrivero# cryptography import flake8_import_order import tests # I201 if TYPE_CHECKING: import flake8_import_order import tests # I201 flake8-import-order-0.19.2/tests/test_cases/additional_newline_edited.py0000664000175000017500000000131615026506505026347 0ustar jriverojrivero# edited import ast # This comment should not trigger a I202 (not a newline) import os import signal # I202 import X from X import B, b, \ C, d from Y import A # I202 from Y import ( B, b, C, d, ) from Z import A import flake8_import_order import tests # I202 from . import A from . import B # I202 if TYPE_CHECKING: import ast # This comment should not trigger a I202 (not a newline) import os import signal # I202 import X from X import B, b, \ C, d from Y import A # I202 from Y import ( B, b, C, d, ) from Z import A import flake8_import_order import tests # I202 from . import A from . import B # I202 flake8-import-order-0.19.2/tests/test_cases/additional_newline_cryptography.py0000664000175000017500000000052415026506505027644 0ustar jriverojrivero# cryptography import ast import signal # I202 import X import Y import flake8_import_order import tests from . import A from . import B # I202 if TYPE_CHECKING: import ast import signal # I202 import X import Y import flake8_import_order import tests from . import A from . import B # I202 flake8-import-order-0.19.2/tests/test_cases/additional_newline.py0000664000175000017500000000144715026506505025036 0ustar jriverojrivero# appnexus google pep8 smarkets import ast # This comment should not trigger a I202 (not a newline) import os import signal # I202 import X from X import B, b, \ C, d from Y import A # I202 from Y import ( B, b, C, d, ) from Z import A import flake8_import_order import tests # I202 from . import A from . import B # I202 from .Z import ( A, B, C, D, E, F, G, ) if TYPE_CHECKING: import ast # This comment should not trigger a I202 (not a newline) import os import signal # I202 import X from X import B, b, \ C, d from Y import A # I202 from Y import ( B, b, C, d, ) from Z import A import flake8_import_order import tests # I202 from . import A from . import B # I202 flake8-import-order-0.19.2/tests/test_cases/wrong_section.py0000664000175000017500000000034215026506505024056 0ustar jriverojrivero# cryptography edited google pep8 smarkets import ast import flake8_import_order # I100 import A import os # I100 if TYPE_CHECKING: import ast import flake8_import_order # I100 import A import os # I100 flake8-import-order-0.19.2/tests/test_cases/wrong_section_appnexus.py0000664000175000017500000000037115026506505026003 0ustar jriverojrivero# appnexus edited import ast import flake8_import_order # I201 I100 import localpackage # I201 I100 import X # I201 if TYPE_CHECKING: import ast import flake8_import_order # I201 I100 import localpackage # I201 I100 import X # I201 flake8-import-order-0.19.2/tests/test_cases/complete_cryptography.py0000664000175000017500000000255315026506505025627 0ustar jriverojrivero# cryptography import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X from X import * from X import A from X import B, C, D import Y from Y import * from Y import A from Y import B, C, D import Z from Z import A from Z.A import A from Z.A.B import A import localpackage import flake8_import_order from flake8_import_order import * from flake8_import_order import A from flake8_import_order import B import tests from tests import A from tests import B from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_edited_alt0.py0000664000175000017500000000230715026506505025247 0ustar jriverojrivero# edited import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if t.TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_pep8.py0000664000175000017500000000212615026506505023744 0ustar jriverojrivero# pep8 import sys from os import path import os from functools import * import ast import localpackage import X from X import A, d from X import * import Z from Z import A from Z.A.B import A from Z.A import A import Y from Y import * from Y import B from Y import A, C, D import flake8_import_order from flake8_import_order import * from . import B from . import A from .B import B from .A import A from .. import B from .. import A from ..B import B from ..A import A if TYPE_CHECKING: import sys from os import path import os from functools import * import ast import localpackage import X from X import A, d from X import * import Z from Z import A from Z.A.B import A from Z.A import A import Y from Y import * from Y import B from Y import A, C, D import flake8_import_order from flake8_import_order import * from . import B from . import A from .B import B from .A import A from .. import B from .. import A from ..B import B from ..A import A if not TYPE_CHECKING: from ..c import c flake8-import-order-0.19.2/tests/test_cases/complete_pycharm.py0000664000175000017500000000220015026506505024524 0ustar jriverojrivero# pycharm import ast import os import sys from functools import * from os import path import X import Y import Z import localpackage from X import * from X import A from X import B, C, b, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X import Y import Z import localpackage from X import * from X import A from X import B, C, b, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_smarkets.py0000664000175000017500000000220115026506505024713 0ustar jriverojrivero# smarkets import ast import os import sys from functools import * from os import path import localpackage import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if TYPE_CHECKING: import ast import os import sys from functools import * from os import path import localpackage import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_edited.py0000664000175000017500000000230515026506505024325 0ustar jriverojrivero# edited import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/wrong_from_import_same.py0000664000175000017500000000021415026506505025752 0ustar jriverojrivero# cryptography from os import system from os import path # I100 if TYPE_CHECKING: from os import system from os import path # I100 flake8-import-order-0.19.2/tests/test_cases/complete_google.py0000664000175000017500000000231315026506505024342 0ustar jriverojrivero# google import ast from functools import * import os from os import path import sys import localpackage import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B from ..Z import ( A, B, C, D, E, F, G, H, ) if TYPE_CHECKING: import ast from functools import * import os from os import path import sys import localpackage import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/mixed_groups.py0000664000175000017500000000023515026506505023704 0ustar jriverojrivero# appnexus cryptography edited google pep8 smarkets import ast, X, flake_import_order # I666 if TYPE_CHECKING: import ast, X, flake_import_order # I666 flake8-import-order-0.19.2/tests/test_cases/complete_appnexus_alt1.py0000664000175000017500000000223115026506505025651 0ustar jriverojrivero# appnexus import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if t.TYPE_CHECKING: import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_edited_alt1.py0000664000175000017500000000230715026506505025250 0ustar jriverojrivero# edited import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if t.TYPE_CHECKING: import ast import os import sys from functools import * from os import path import X import Y import Z from X import * from X import A from X import B, b, C, d from Y import * from Y import A from Y import B, C, D from Y import e from Z import A from Z.A import A from Z.A.B import A import localpackage from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/complete_appnexus.py0000664000175000017500000000222715026506505024735 0ustar jriverojrivero# appnexus import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B if TYPE_CHECKING: import ast from functools import * import os from os import path import sys import X from X import * from X import A from X import B, b, C, d import Y from Y import * from Y import A from Y import B, C, D from Y import e import Z from Z import A from Z.A import A from Z.A.B import A from localpackage import A, b import flake8_import_order from flake8_import_order import * from . import A from . import B from .A import A from .B import B from .. import A from .. import B from ..A import A from ..B import B flake8-import-order-0.19.2/tests/test_cases/stdlib_shadowing.py0000664000175000017500000000033415026506505024523 0ustar jriverojrivero# appnexus cryptography google pep8 smarkets from .filesystem import FilesystemStorage from os import path # I100 I201 if TYPE_CHECKING: from .filesystem import FilesystemStorage from os import path # I100 I201 flake8-import-order-0.19.2/tests/test_cases/wrong_from_import_order_cryptography.py0000664000175000017500000000032415026506505030755 0ustar jriverojrivero# cryptography from A import a, A # I101 from B import A, a, B # I101 from C import b, a # I101 if TYPE_CHECKING: from A import a, A # I101 from B import A, a, B # I101 from C import b, a # I101 flake8-import-order-0.19.2/tests/test_cases/noqa.py0000664000175000017500000000054315026506505022137 0ustar jriverojrivero# appnexus cryptography edited google pep8 smarkets import ast import sys # noqa: I202 import os # noqa import unittest import X # noqa from . import B, C, A # I201 # noqa: I101 if TYPE_CHECKING: import ast import sys # noqa: I202 import os # noqa import unittest import X # noqa from . import B, C, A # I201 # noqa: I101 flake8-import-order-0.19.2/tests/test_cases/wrong_import_order.py0000664000175000017500000000015715026506505025123 0ustar jriverojrivero# appnexus edited google smarkets import a import A # I100 if TYPE_CHECKING: import a import A # I100 flake8-import-order-0.19.2/tests/test_pylama_linter.py0000664000175000017500000000075415026506505022747 0ustar jriverojriveroimport pytest @pytest.mark.skip("pylama seems unmaintained") def test_linter(tmpdir): from flake8_import_order.pylama_linter import Linter file_ = tmpdir.join("flake8_import_order.py") file_.write("import ast\nimport flake8_import_order\n") options = { "application-import-names": ["flake8_import_order"], } checker = Linter() assert checker.allow(str(file_)) for error in checker.run(str(file_), **options): assert error["type"] == "I201" flake8-import-order-0.19.2/tests/test_flake8_linter.py0000664000175000017500000000265415026506505022637 0ustar jriverojriveroimport ast import pycodestyle from flake8_import_order.flake8_linter import Linter from flake8_import_order.styles import Google def test_parsing(): style = "google" import_names = ["flake8_import_order", "tests"] package_names = ["local_package"] argv = [ "--application-import-names={}".format(",".join(import_names)), f"--import-order-style={style}", "--application-package-names={}".format(",".join(package_names)), ] parser = pycodestyle.get_parser("", "") Linter.add_options(parser) options, args = parser.parse_args(argv) Linter.parse_options(options) assert Linter.options["import_order_style"].name == style assert Linter.options["import_order_style"].load() is Google assert Linter.options["application_import_names"] == import_names assert Linter.options["application_package_names"] == package_names def test_linter(): argv = ["--application-import-names=flake8_import_order"] parser = pycodestyle.get_parser("", "") Linter.add_options(parser) options, args = parser.parse_args(argv) Linter.parse_options(options) data = "import ast\nimport flake8_import_order\n" pycodestyle.stdin_get_value = lambda: data tree = ast.parse(data) checker = Linter(tree, None) for lineno, col_offset, msg, instance in checker.run(): assert msg.startswith( "I201 Missing newline between import groups.", ) flake8-import-order-0.19.2/.pre-commit-config.yaml0000664000175000017500000000322415026506505021610 0ustar jriverojrivero# To enable this pre-commit hook run: # `pip install pre-commit` or `brew install pre-commit` # Then run `pre-commit install` exclude: 'tests\/test_cases' # Learn more about this config here: https://pre-commit.com/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-added-large-files - id: check-toml types: [toml] - id: check-yaml types: [yaml] - id: end-of-file-fixer types: [text] stages: [pre-commit, pre-push, manual] - id: trailing-whitespace types: [text] stages: [pre-commit, pre-push, manual] - id: detect-private-key - id: mixed-line-ending - repo: https://github.com/codespell-project/codespell rev: v2.4.1 hooks: - id: codespell additional_dependencies: - tomli - repo: https://github.com/asottile/pyupgrade rev: v3.20.0 hooks: - id: pyupgrade args: [--py39-plus] - repo: https://github.com/PyCQA/isort rev: 6.0.1 hooks: - id: isort - repo: https://github.com/psf/black rev: 25.1.0 hooks: - id: black # - repo: https://github.com/pre-commit/mirrors-mypy # rev: v1.16.0 # hooks: # - id: mypy # additional_dependencies: # - types-python-dateutil # - types-requests # exclude: ^(docs/|tests/) # - repo: https://github.com/jorisroovers/gitlint # rev: v0.19.1 # hooks: # - id: gitlint # - repo: https://github.com/asottile/setup-cfg-fmt rev: v2.8.0 hooks: - id: setup-cfg-fmt args: [--min-py3-version, '3.9'] - repo: https://github.com/gitleaks/gitleaks rev: v8.27.2 hooks: - id: gitleaks