diff --git a/README.rst b/README.rst index a8620f4..815b9d4 100644 --- a/README.rst +++ b/README.rst @@ -56,6 +56,9 @@ the code being developed. This option is only supported if using the * ``edited`` - style as ``smarkets`` only with `import` statements for packages local to your company or organisation coming after `import` statements for third-party packages, see an `example `__ * ``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. + Limitations ----------- @@ -82,5 +85,45 @@ 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. +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)) + +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.png?branch=master :target: https://travis-ci.org/PyCQA/flake8-import-order diff --git a/flake8_import_order/checker.py b/flake8_import_order/checker.py index adefc0c..e6895e2 100644 --- a/flake8_import_order/checker.py +++ b/flake8_import_order/checker.py @@ -1,11 +1,10 @@ import ast +import pkg_resources + import pycodestyle from flake8_import_order import ImportVisitor -from flake8_import_order.styles import ( - AppNexus, Cryptography, Edited, Google, PEP8, Smarkets, -) DEFAULT_IMPORT_ORDER_STYLE = 'cryptography' @@ -59,20 +58,18 @@ def check_order(self): if not pycodestyle.noqa(self.lines[import_.lineno - 1]): imports.append(import_) - if style_option == 'cryptography': - style = Cryptography(imports) - elif style_option == 'google': - style = Google(imports) - elif style_option == 'pep8': - style = PEP8(imports) - elif style_option == 'smarkets': - style = Smarkets(imports) - elif style_option == 'appnexus': - style = AppNexus(imports) - elif style_option == 'edited': - style = Edited(imports) - else: + try: + style_entry_point = next( + pkg_resources.iter_entry_points( + 'flake8_import_order.styles', + name=style_option + ) + ) + except StopIteration: raise AssertionError("Unknown style {}".format(style_option)) + else: + style_cls = style_entry_point.load() + style = style_cls(imports) for error in style.check(): yield self.error(error) diff --git a/flake8_import_order/flake8_linter.py b/flake8_import_order/flake8_linter.py index c606ce8..4abf4b7 100644 --- a/flake8_import_order/flake8_linter.py +++ b/flake8_import_order/flake8_linter.py @@ -2,6 +2,8 @@ import optparse +import pkg_resources + from flake8_import_order import __version__ from flake8_import_order.checker import ( DEFAULT_IMPORT_ORDER_STYLE, ImportOrderChecker, @@ -46,11 +48,19 @@ def add_options(cls, parser): default=DEFAULT_IMPORT_ORDER_STYLE, action="store", type="string", - help=("Style to follow. Available: " - "cryptography, google, smarkets, appnexus, pep8"), + help=("Style to follow. Available: " + + ", ".join(cls.list_available_styles())), parse_from_config=True, ) + @staticmethod + def list_available_styles(): + entry_points = pkg_resources.iter_entry_points( + 'flake8_import_order.styles' + ) + for entry_point in entry_points: + yield entry_point.name + @classmethod def parse_options(cls, options): optdict = {} diff --git a/setup.py b/setup.py index e7d6405..7febe7a 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,8 @@ zip_safe=False, install_requires=[ - "pycodestyle" + "pycodestyle", + "setuptools", ], tests_require=[ @@ -41,6 +42,14 @@ 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', + ], 'flake8.extension': [ 'I10 = flake8_import_order.flake8_linter:Linter', ],