pax_global_header00006660000000000000000000000064143404716730014523gustar00rootroot0000000000000052 comment=4147a5f991122182dd8ee250adcea36eba0d36b7 flake8-import-order-0.18.2/000077500000000000000000000000001434047167300154065ustar00rootroot00000000000000flake8-import-order-0.18.2/.gitignore000066400000000000000000000001031434047167300173700ustar00rootroot00000000000000*.pyc *.pyo __pycache__ *.egg-info *~ .coverage .tox/ build/ dist/ flake8-import-order-0.18.2/.travis.yml000066400000000000000000000004211434047167300175140ustar00rootroot00000000000000language: python matrix: include: - python: 2.7 dist: trusty - python: 3.5 dist: trusty - python: 3.6 dist: trusty - python: 3.7 dist: xenial - python: 3.8 dist: xenial install: - pip install tox-travis script: - tox flake8-import-order-0.18.2/CHANGELOG.rst000066400000000000000000000106101434047167300174250ustar00rootroot000000000000000.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 existance of namespaced packages, thereby allowing different namespaced pacakages 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 specifing 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` statments. 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.18.2/COPYING000066400000000000000000000167431434047167300164540ustar00rootroot00000000000000 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.18.2/MANIFEST.in000066400000000000000000000001631434047167300171440ustar00rootroot00000000000000include CHANGELOG.rst include COPYING include README.md recursive-include tests * recursive-exclude tests *.py[co] flake8-import-order-0.18.2/README.rst000066400000000000000000000160751434047167300171060ustar00rootroot00000000000000flake8-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 also be ignored. 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.18.2/flake8_import_order/000077500000000000000000000000001434047167300213455ustar00rootroot00000000000000flake8-import-order-0.18.2/flake8_import_order/__about__.py000066400000000000000000000012761434047167300236330ustar00rootroot00000000000000from __future__ import absolute_import, division, print_function __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.18.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.18.2/flake8_import_order/__init__.py000066400000000000000000000072451434047167300234660ustar00rootroot00000000000000import ast from collections import namedtuple from enum import IntEnum from flake8_import_order.__about__ import ( __author__, __copyright__, __email__, __license__, __summary__, __title__, __uri__, __version__, ) from flake8_import_order.stdlib_list import STDLIB_NAMES __all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", ] DEFAULT_IMPORT_ORDER_STYLE = 'cryptography' ClassifiedImport = namedtuple( 'ClassifiedImport', ['type', 'is_from', 'modules', 'names', 'lineno', 'level', 'package'], ) 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 = '%s.%s' % (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 visit_Import(self, node): # noqa: N802 if node.col_offset == 0: 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, 0, root_package_name(modules[0]), ) self.imports.append(classified_import) def visit_ImportFrom(self, node): # noqa: N802 if node.col_offset == 0: 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.level, root_package_name(module), ) self.imports.append(classified_import) 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.18.2/flake8_import_order/checker.py000066400000000000000000000063451434047167300233330ustar00rootroot00000000000000import ast import re from itertools import chain import pycodestyle from flake8_import_order import ImportVisitor, 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(object): 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.18.2/flake8_import_order/flake8_linter.py000066400000000000000000000063121434047167300244500ustar00rootroot00000000000000from __future__ import absolute_import import optparse from flake8_import_order import __version__ from flake8_import_order.checker import ( DEFAULT_IMPORT_ORDER_STYLE, ImportOrderChecker, ) from flake8_import_order.styles import list_entry_points, lookup_entry_point class Linter(ImportOrderChecker): name = "import-order" version = __version__ def __init__(self, tree, filename, lines=None): super(Linter, self).__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, "{0} {1}".format(error.code, error.message), Linter, ) def run(self): for error in self.check_order(): yield error 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.18.2/flake8_import_order/pylama_linter.py000066400000000000000000000020531434047167300245570ustar00rootroot00000000000000from __future__ import absolute_import from pylama.lint import Linter as BaseLinter from flake8_import_order import __version__ from flake8_import_order.checker import ( DEFAULT_IMPORT_ORDER_STYLE, ImportOrderChecker, ) from flake8_import_order.styles import lookup_entry_point class Linter(ImportOrderChecker, BaseLinter): name = "import-order" version = __version__ def __init__(self): super(Linter, self).__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 for error in self.check_order(): yield error flake8-import-order-0.18.2/flake8_import_order/stdlib_list.py000066400000000000000000000112241434047167300242330ustar00rootroot00000000000000STDLIB_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.18.2/flake8_import_order/styles.py000066400000000000000000000211161434047167300232430ustar00rootroot00000000000000from collections import namedtuple from pkg_resources import iter_entry_points from flake8_import_order import ClassifiedImport, ImportType, NewLine Error = namedtuple('Error', ['lineno', 'code', 'message']) def list_entry_points(): return iter_entry_points('flake8_import_order.styles') def lookup_entry_point(name): try: return next(iter_entry_points('flake8_import_order.styles', name=name)) except StopIteration: raise LookupError('Unknown style {}'.format(name)) class Style(object): 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): for error in self._check(previous_import, previous, current): yield error previous_import = current previous = current def _check(self, previous_import, previous, current_import): for error in self._check_I666(current_import): yield error for error in self._check_I101(current_import): yield error if previous_import is not None: for error in self._check_I100(previous_import, current_import): yield error for error in self._check_I201( previous_import, previous, current_import, ): yield error for error in self._check_I202( previous_import, previous, current_import, ): yield error 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 {0}".format(corrected), ) 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. " "'{0}' should be before '{1}'" ).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 = "{0} and in a different group.".format(message) yield Error(current_import.lineno, 'I100', message) def _check_I201(self, previous_import, previous, current_import): # noqa: N802,E501 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(self, previous_import, previous, current_import): # noqa: N802,E501 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 ( "'{0}' is identified as {1} and " "'{2}' is identified as {3}." ).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(self, previous_import, previous, current_import): # noqa: N802,E501 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 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.18.2/setup.cfg000066400000000000000000000004071434047167300172300ustar00rootroot00000000000000[wheel] universal = 1 [check-manifest] ignore = tox.ini [flake8] exclude = .tox,*.egg, tests/test_cases/ select = E,W,F,N,I application-import-names = flake8_import_order,tests [coverage:run] source = flake8_import_order [coverage:report] show_missing = True flake8-import-order-0.18.2/setup.py000066400000000000000000000044611434047167300171250ustar00rootroot00000000000000import os from setuptools import setup, find_packages base_dir = os.path.dirname(__file__) about = {} with open(os.path.join(base_dir, "flake8_import_order", "__about__.py")) as f: exec(f.read(), about) with open(os.path.join(base_dir, "README.rst")) as f: long_description = f.read() setup( name=about["__title__"], version=about["__version__"], description=about["__summary__"], long_description=long_description, license=about["__license__"], url=about["__uri__"], author=about["__author__"], author_email=about["__email__"], maintainer=about['__maintainer__'], maintainer_email=about['__maintainer_email__'], packages=find_packages(exclude=["tests", "tests.*"]), zip_safe=False, install_requires=[ "enum34 ; python_version <= '2.7'", "pycodestyle", "setuptools", ], tests_require=[ "pytest", "flake8", "pycodestyle", "pylama" ], py_modules=['flake8_import_order'], entry_points={ 'flake8_import_order.styles': [ 'cryptography = flake8_import_order.styles:Cryptography', 'google = flake8_import_order.styles:Google', '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', ], 'flake8.extension': [ 'I = flake8_import_order.flake8_linter:Linter', ], 'pylama.linter': [ 'import_order = flake8_import_order.pylama_linter:Linter' ] }, classifiers=[ "Framework :: Flake8", "Intended Audience :: Developers", "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", ( "License :: OSI Approved :: " "GNU Lesser General Public License v3 (LGPLv3)" ), "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", "Operating System :: OS Independent" ] ) flake8-import-order-0.18.2/tests/000077500000000000000000000000001434047167300165505ustar00rootroot00000000000000flake8-import-order-0.18.2/tests/__init__.py000066400000000000000000000000001434047167300206470ustar00rootroot00000000000000flake8-import-order-0.18.2/tests/test_cases/000077500000000000000000000000001434047167300207055ustar00rootroot00000000000000flake8-import-order-0.18.2/tests/test_cases/additional_newline.py000066400000000000000000000005261434047167300251130ustar00rootroot00000000000000# 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 flake8-import-order-0.18.2/tests/test_cases/additional_newline_cryptography.py000066400000000000000000000002301434047167300277160ustar00rootroot00000000000000# cryptography 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.18.2/tests/test_cases/additional_newline_edited.py000066400000000000000000000005001434047167300264210ustar00rootroot00000000000000# 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 flake8-import-order-0.18.2/tests/test_cases/complete_appnexus.py000066400000000000000000000011051434047167300250070ustar00rootroot00000000000000# appnexus from __future__ import absolute_import import ast from functools import * import os from os import path import StringIO 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.18.2/tests/test_cases/complete_cryptography.py000066400000000000000000000012311434047167300256770ustar00rootroot00000000000000# cryptography from __future__ import absolute_import 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.18.2/tests/test_cases/complete_edited.py000066400000000000000000000011311434047167300244010ustar00rootroot00000000000000# edited from __future__ import absolute_import import ast import os import StringIO 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.18.2/tests/test_cases/complete_google.py000066400000000000000000000010701434047167300244210ustar00rootroot00000000000000# google from __future__ import absolute_import import ast from functools import * import os from os import path import StringIO 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.18.2/tests/test_cases/complete_pep8.py000066400000000000000000000010001434047167300240120ustar00rootroot00000000000000# pep8 from __future__ import absolute_import 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 flake8-import-order-0.18.2/tests/test_cases/complete_pycharm.py000066400000000000000000000010711434047167300246110ustar00rootroot00000000000000# pycharm from __future__ import absolute_import import StringIO 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.18.2/tests/test_cases/complete_smarkets.py000066400000000000000000000010721434047167300250000ustar00rootroot00000000000000# smarkets from __future__ import absolute_import import ast import os import StringIO 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.18.2/tests/test_cases/missing_newline.py000066400000000000000000000002701434047167300244500ustar00rootroot00000000000000# appnexus cryptography edited google pep8 smarkets import ast # 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.18.2/tests/test_cases/missing_newline_cryptography.py000066400000000000000000000000761434047167300272670ustar00rootroot00000000000000# cryptography import flake8_import_order import tests # I201 flake8-import-order-0.18.2/tests/test_cases/mixed_groups.py000066400000000000000000000001351434047167300237630ustar00rootroot00000000000000# appnexus cryptography edited google pep8 smarkets import ast, X, flake_import_order # I666 flake8-import-order-0.18.2/tests/test_cases/namespace.py000066400000000000000000000002051434047167300232100ustar00rootroot00000000000000# appnexus edited google smarkets import namespace import namespace.package_a import flake8_import_order import namespace.package_b flake8-import-order-0.18.2/tests/test_cases/noqa.py000066400000000000000000000002661434047167300222210ustar00rootroot00000000000000# 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 flake8-import-order-0.18.2/tests/test_cases/stdlib_shadowing.py000066400000000000000000000001671434047167300246070ustar00rootroot00000000000000# appnexus cryptography google pep8 smarkets from .filesystem import FilesystemStorage from os import path # I100 I201 flake8-import-order-0.18.2/tests/test_cases/wrong_from_import_order.py000066400000000000000000000001601434047167300262200ustar00rootroot00000000000000# appnexus edited google smarkets from A import a, A # I101 from B import b, A # I101 from C import b, a # I101 flake8-import-order-0.18.2/tests/test_cases/wrong_from_import_order_cryptography.py000066400000000000000000000001421434047167300310330ustar00rootroot00000000000000# cryptography from A import a, A # I101 from B import A, a, B # I101 from C import b, a # I101 flake8-import-order-0.18.2/tests/test_cases/wrong_from_import_same.py000066400000000000000000000001001434047167300260240ustar00rootroot00000000000000# cryptography from os import system from os import path # I100 flake8-import-order-0.18.2/tests/test_cases/wrong_import_order.py000066400000000000000000000000731434047167300252000ustar00rootroot00000000000000# appnexus edited google smarkets import a import A # I100 flake8-import-order-0.18.2/tests/test_cases/wrong_import_order_cryptography.py000066400000000000000000000000631434047167300300120ustar00rootroot00000000000000# cryptography import A import a import B # I100 flake8-import-order-0.18.2/tests/test_cases/wrong_relative_order.py000066400000000000000000000001271434047167300255010ustar00rootroot00000000000000# appnexus cryptography edited google smarkets from .. import A from . import B # I100 flake8-import-order-0.18.2/tests/test_cases/wrong_section.py000066400000000000000000000001651434047167300241410ustar00rootroot00000000000000# cryptography edited google pep8 smarkets import ast import flake8_import_order # I100 import A import os # I100 flake8-import-order-0.18.2/tests/test_cases/wrong_section_appnexus.py000066400000000000000000000001641434047167300260630ustar00rootroot00000000000000# appnexus edited import ast import flake8_import_order # I201 I100 import localpackage # I201 I100 import X # I201 flake8-import-order-0.18.2/tests/test_flake8_linter.py000066400000000000000000000026641434047167300227200ustar00rootroot00000000000000import 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)), "--import-order-style={}".format(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.18.2/tests/test_pylama_linter.py000066400000000000000000000006521434047167300230240ustar00rootroot00000000000000from flake8_import_order.pylama_linter import Linter def test_linter(tmpdir): 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.18.2/tests/test_stdlib.py000066400000000000000000000014061434047167300214430ustar00rootroot00000000000000import ast import pycodestyle import pytest 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 = "import {}\nimport zoneinfo\n".format(import_name) checker = _checker(data) codes = [error.code for error in checker.check_order()] assert codes == [] flake8-import-order-0.18.2/tests/test_style_cases.py000066400000000000000000000042311434047167300224770ustar00rootroot00000000000000import 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 = [error.code for error in checker.check_order()] assert codes == expected_codes def test_unknown_style(): with pytest.raises(LookupError): lookup_entry_point('Unknown') flake8-import-order-0.18.2/tox.ini000066400000000000000000000016771434047167300167340ustar00rootroot00000000000000[tox] envlist = {py27,py34,py35,py36,py37}-flake8{2,3},pypy,pep8,py3pep8,setuppy,manifest [testenv] deps = pytest pytest-cov flake82: flake8<3.0 flake83: flake8>3.0 pylama pycodestyle>=2.0 pyflakes>=1.3.0 commands = pytest --cov --capture=no --strict {posargs} [testenv:pep8] deps = flake8 pep8-naming flake8-import-order commands = flake8 flake8_import_order/ tests/ [testenv:py3pep8] basepython = python3.7 deps = flake8 pep8-naming flake8-import-order commands = flake8 flake8_import_order/ tests/ [testenv:setuppy] basepython = python3.7 deps = docutils Pygments commands = python setup.py check \ --metadata \ --restructuredtext \ --strict [testenv:manifest] deps = check-manifest commands = check-manifest [testenv:release] deps = build twine commands = python -m build twine check dist/* twine upload {posargs:--skip-existing} dist/*