From 940ba3e2c9364844565038c5c569ebc2e1a0202d Mon Sep 17 00:00:00 2001 From: Matt Shin Date: Tue, 17 Dec 2019 12:53:20 +0000 Subject: [PATCH] Directory restructure for PyPI readiness (#1) * Directory restructure for PyPI readiness Add meta files for PyPI readiness. Move projects into `src/` to model after PyPA sample project. Move script to `src/stylist/__main__.py`. Add basic Github action to run linter and test. * Happiness for flake8 and pytest Remove Python 2 compat. Minor syntax update to satisfy flake8 and pytest. * Test all supported versions Also include coverage. * Fix pytest coverage syntax Use package name instead of src. Fail if coverage less than 75%. * Remove unnecessary __main__ test Already in __main__.py module! * Update description for package * src -> source This is preferred by the reviewer. --- .github/workflows/pythonpackage.yml | 31 +++++++++++ MANIFEST.in | 8 +++ setup.cfg | 2 + setup.py | 53 +++++++++++++++++++ {lib-python => source}/stylist/__init__.py | 0 bin/stylist => source/stylist/__main__.py | 11 ++-- {lib-python => source}/stylist/engine.py | 2 - {lib-python => source}/stylist/issue.py | 1 - {lib-python => source}/stylist/rule.py | 12 ++--- {lib-python => source}/stylist/source.py | 11 ++-- {lib-python => source}/stylist/style.py | 1 - .../stylist/tests => tests}/engine_test.py | 1 - .../stylist/tests => tests}/issue_test.py | 1 - .../stylist/tests => tests}/rule_test.py | 26 ++++----- .../stylist/tests => tests}/source_test.py | 11 ++-- .../stylist/tests => tests}/style_test.py | 1 - 16 files changed, 124 insertions(+), 48 deletions(-) create mode 100644 .github/workflows/pythonpackage.yml create mode 100644 MANIFEST.in create mode 100644 setup.cfg create mode 100644 setup.py rename {lib-python => source}/stylist/__init__.py (100%) rename bin/stylist => source/stylist/__main__.py (97%) rename {lib-python => source}/stylist/engine.py (95%) rename {lib-python => source}/stylist/issue.py (94%) rename {lib-python => source}/stylist/rule.py (96%) rename {lib-python => source}/stylist/source.py (98%) rename {lib-python => source}/stylist/style.py (96%) rename {lib-python/stylist/tests => tests}/engine_test.py (95%) rename {lib-python/stylist/tests => tests}/issue_test.py (90%) rename {lib-python/stylist/tests => tests}/rule_test.py (96%) rename {lib-python/stylist/tests => tests}/source_test.py (97%) rename {lib-python/stylist/tests => tests}/style_test.py (98%) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml new file mode 100644 index 0000000..3299f52 --- /dev/null +++ b/.github/workflows/pythonpackage.yml @@ -0,0 +1,31 @@ +name: Python package + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 3 + matrix: + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v1 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install + run: | + python3 -m pip install --upgrade pip + pip install -e . + - name: Lint with flake8 + run: | + pip install -e .[dev] + flake8 . --count --show-source --statistics + - name: Test with pytest + run: | + pip install .[test] + pytest --cov stylist --cov-fail-under=75 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..1f03165 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +# Include the README +include *.md + +# Include the license file +include LICENSE + +# Include the documentation files +recursive-include documentation * diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..8183238 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +license_files = LICENSE diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..46a29d8 --- /dev/null +++ b/setup.py @@ -0,0 +1,53 @@ +"""Setup Stylist for distribution on PyPI.""" + +import os +from setuptools import setup, find_packages + +# Get the long description from the README file +with open( + os.path.join(os.path.dirname(__file__), 'README.md'), + encoding='utf-8', +) as handle: + LONG_DESCRIPTION = handle.read() + +setup( + name='stylist', + # TODO: + # For a discussion on single-sourcing the version across setup.py and the + # project code, see + # https://packaging.python.org/en/latest/single_source_version.html + version='0.1', + description=( + 'Extensible code style checker' + ' currently supporting Fortran, PSyclone DSL, etc' + ), + long_description=LONG_DESCRIPTION, + long_description_content_type='text/markdown', + url='https://github.com/MetOffice/stylist', + author='Met Office', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Quality Assurance', + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + ], + keywords='linter fortran psyclone', + package_dir={'': 'source'}, + packages=find_packages(where='source'), + python_requires='>=3.6, <4', + install_requires=['fparser'], + extras_require={ + 'dev': ['check-manifest', 'flake8'], + 'test': ['pytest', 'pytest-cov'], + }, + entry_points={ + 'console_scripts': ['stylist=stylist.__main__:main'], + }, + project_urls={ + 'Bug Reports': 'https://github.com/MetOffice/stylist/issues', + 'Source': 'https://github.com/MetOffice/stylist/', + }, +) diff --git a/lib-python/stylist/__init__.py b/source/stylist/__init__.py similarity index 100% rename from lib-python/stylist/__init__.py rename to source/stylist/__init__.py diff --git a/bin/stylist b/source/stylist/__main__.py similarity index 97% rename from bin/stylist rename to source/stylist/__main__.py index 94d703c..13f41b5 100755 --- a/bin/stylist +++ b/source/stylist/__main__.py @@ -8,8 +8,6 @@ Tool for checking code style. ''' -from __future__ import absolute_import, division, print_function - import argparse import logging import os.path @@ -22,7 +20,7 @@ from stylist.style import LFRicStyle -def cli(): +def parse_cli(): ''' Parse the command line. Returns a dictionary of arguments. ''' @@ -82,7 +80,7 @@ def _add_extensions(additional_extensions): *preproc_objects) -def main(arguments): +def process(arguments): ''' Examines files for style compliance. @@ -126,5 +124,6 @@ def main(arguments): sys.exit(1) -if __name__ == '__main__': - main(cli()) +def main(): + '''Main entry point.''' + return process(parse_cli()) diff --git a/lib-python/stylist/engine.py b/source/stylist/engine.py similarity index 95% rename from lib-python/stylist/engine.py rename to source/stylist/engine.py index 9353733..b96e06f 100644 --- a/lib-python/stylist/engine.py +++ b/source/stylist/engine.py @@ -7,8 +7,6 @@ ''' Core of the Fortran style checking tool. ''' -from __future__ import absolute_import, division, print_function - import logging import stylist.source diff --git a/lib-python/stylist/issue.py b/source/stylist/issue.py similarity index 94% rename from lib-python/stylist/issue.py rename to source/stylist/issue.py index 1f49a13..9ea0085 100644 --- a/lib-python/stylist/issue.py +++ b/source/stylist/issue.py @@ -7,7 +7,6 @@ ''' Issues found in the source. ''' -from __future__ import absolute_import, division, print_function class Issue(object): diff --git a/lib-python/stylist/rule.py b/source/stylist/rule.py similarity index 96% rename from lib-python/stylist/rule.py rename to source/stylist/rule.py index 9b644c7..835102f 100644 --- a/lib-python/stylist/rule.py +++ b/source/stylist/rule.py @@ -7,12 +7,10 @@ ''' A collection of style rules. ''' -from __future__ import absolute_import, division, print_function from abc import ABCMeta, abstractmethod import logging import re -from six import add_metaclass import fparser.two.Fortran2003 import fparser.common.readfortran @@ -20,8 +18,7 @@ from stylist.source import FortranSource -@add_metaclass(ABCMeta) -class Rule(object): +class Rule(object, metaclass=ABCMeta): # pylint: disable=too-few-public-methods ''' Abstract parent of all rules. @@ -113,7 +110,9 @@ class TrailingWhitespace(Rule): def examine(self, subject): ''' - Examines the text for white space at the end of lines. This includes empty lines. + Examines the text for white space at the end of lines. + + This includes empty lines. :param subject: File contents as Source object. :return: List of issues or empty list. ''' @@ -131,8 +130,7 @@ def examine(self, subject): return issues -@add_metaclass(ABCMeta) -class FortranRule(Rule): +class FortranRule(Rule, metaclass=ABCMeta): ''' Parent for style rules pertaining to Fortran source. ''' diff --git a/lib-python/stylist/source.py b/source/stylist/source.py similarity index 98% rename from lib-python/stylist/source.py rename to source/stylist/source.py index 71b4e8f..3700219 100644 --- a/lib-python/stylist/source.py +++ b/source/stylist/source.py @@ -7,12 +7,10 @@ ''' Manages source code in various flavours. ''' -from __future__ import absolute_import, division, print_function from abc import ABCMeta, abstractmethod import re import os.path -from six import add_metaclass import fparser.common.readfortran as readfortran import fparser.two.Fortran2003 @@ -20,8 +18,7 @@ from fparser.two.utils import FparserException -@add_metaclass(ABCMeta) -class SourceText(object): +class SourceText(object, metaclass=ABCMeta): # pylint: disable=too-few-public-methods ''' Handles source code at the text level. Makes use of the decorator pattern @@ -70,8 +67,7 @@ def get_text(self): return self._source_string -@add_metaclass(ABCMeta) -class TextProcessor(SourceText): +class TextProcessor(SourceText, metaclass=ABCMeta): # pylint: disable=too-few-public-methods, abstract-method ''' Preprocessor decorators inherit from this. @@ -148,8 +144,7 @@ def get_text(self): return text -@add_metaclass(ABCMeta) -class SourceTree(object): +class SourceTree(object, metaclass=ABCMeta): ''' Abstract parent of all actual language source files. ''' diff --git a/lib-python/stylist/style.py b/source/stylist/style.py similarity index 96% rename from lib-python/stylist/style.py rename to source/stylist/style.py index 6e4aaf9..4d0e98b 100644 --- a/lib-python/stylist/style.py +++ b/source/stylist/style.py @@ -7,7 +7,6 @@ ''' Classes relating to styles made up of rules. ''' -from __future__ import absolute_import, division, print_function from abc import ABCMeta import logging diff --git a/lib-python/stylist/tests/engine_test.py b/tests/engine_test.py similarity index 95% rename from lib-python/stylist/tests/engine_test.py rename to tests/engine_test.py index 21491ed..1d68ccc 100644 --- a/lib-python/stylist/tests/engine_test.py +++ b/tests/engine_test.py @@ -7,7 +7,6 @@ ''' Ensures the 'engine' module functions as expected. ''' -from __future__ import absolute_import, division, print_function import tempfile diff --git a/lib-python/stylist/tests/issue_test.py b/tests/issue_test.py similarity index 90% rename from lib-python/stylist/tests/issue_test.py rename to tests/issue_test.py index 9ad5a06..d05ed82 100644 --- a/lib-python/stylist/tests/issue_test.py +++ b/tests/issue_test.py @@ -7,7 +7,6 @@ ''' Ensures the Issue object functions as expected. ''' -from __future__ import absolute_import, division, print_function import stylist.issue diff --git a/lib-python/stylist/tests/rule_test.py b/tests/rule_test.py similarity index 96% rename from lib-python/stylist/tests/rule_test.py rename to tests/rule_test.py index 1d81d66..e548795 100644 --- a/lib-python/stylist/tests/rule_test.py +++ b/tests/rule_test.py @@ -7,7 +7,6 @@ ''' Tests of the stylist.rule module. ''' -from __future__ import absolute_import, division, print_function import fparser.two.Fortran2003 import stylist.rule @@ -121,7 +120,7 @@ def test_simple(self, simple_source): write( output_unit, '("Hello ", A)' ) 'world' end program some_trailing_whitespace -''' +''' # noqa: W291, W293 _PF_TWS = '''module trailing_whitespace_in_unit_tests @@ -146,7 +145,8 @@ def test_simple(self, simple_source): end subroutine test_thing -end module trailing_whitespace_in_unit_tests''' +end module trailing_whitespace_in_unit_tests''' # noqa: W291, W293 + class TestTrailingWhitespace(object): ''' @@ -172,9 +172,11 @@ def test_examples(self, example_source): reader = SourceStringReader(example_source[0]) source = FortranSource(reader) issues = unit_under_test.examine(source) - assert [str(issue) for issue in issues] \ - == [str(eln) + ': Found trailing white space' - for eln in example_source[1]] + assert ( + [str(issue) for issue in issues] + == [str(eln) + ': Found trailing white space' + for eln in example_source[1]] + ) class TestMissingImplicit(object): @@ -392,7 +394,7 @@ def unit_type(self, request): ''' Parameter fixture giving program unit types. ''' - #pylint: disable=no-self-use + # pylint: disable=no-self-use yield request.param @pytest.fixture(scope='class', @@ -409,8 +411,8 @@ def ignorance(self, request): [('missing_mod', [])], [('present_mod', ['stuff'])], [('multi_mod', ['one', 'two'])], - [('missing_mod', []),('present_mod', ['stuff'])], - [('present_mod', ['stuff']),('missing_mod',[])]]) + [('missing_mod', []), ('present_mod', ['stuff'])], + [('present_mod', ['stuff']), ('missing_mod', [])]]) def unit_usage(self, request): ''' Parameter fixture giving permutations of "use" statements. @@ -423,8 +425,8 @@ def unit_usage(self, request): [('missing_mod', [])], [('present_mod', ['stuff'])], [('multi_mod', ['one', 'two'])], - [('missing_mod', []),('present_mod', ['stuff'])], - [('present_mod', ['stuff']),('missing_mod',[])]]) + [('missing_mod', []), ('present_mod', ['stuff'])], + [('present_mod', ['stuff']), ('missing_mod', [])]]) def procedure_usage(self, request): ''' Parameter fixture giving permutations of "use" statements. @@ -437,7 +439,7 @@ def test_use(self, unit_type, unit_usage, procedure_usage, ignorance): Checks that the rule reports missing "use" clauses correctly. ''' # pylint: disable=no-self-use - def prepare( params ): + def prepare(params): usage = [] expectations = [] for details in params: diff --git a/lib-python/stylist/tests/source_test.py b/tests/source_test.py similarity index 97% rename from lib-python/stylist/tests/source_test.py rename to tests/source_test.py index 605ded6..6a0fc19 100644 --- a/lib-python/stylist/tests/source_test.py +++ b/tests/source_test.py @@ -7,11 +7,6 @@ ''' Checks source code management classes. ''' -from __future__ import absolute_import, division, print_function - -import os.path -import shutil -import tempfile import fparser.two.Fortran2003 import pytest @@ -258,10 +253,10 @@ def test_find_all(self): unit_under_test = FortranSource(reader) wanted = fparser.two.Fortran2003.Module_Subprogram result = unit_under_test.find_all(wanted) - assert str(result.next().content[0].items[1]) == 'one' - assert str(result.next().content[0].items[1]) == 'two' + assert str(next(result).content[0].items[1]) == 'one' + assert str(next(result).content[0].items[1]) == 'two' with pytest.raises(StopIteration): - result.next() + next(result) class TestCSource(object): diff --git a/lib-python/stylist/tests/style_test.py b/tests/style_test.py similarity index 98% rename from lib-python/stylist/tests/style_test.py rename to tests/style_test.py index b24f2f0..7ba4d6e 100644 --- a/lib-python/stylist/tests/style_test.py +++ b/tests/style_test.py @@ -7,7 +7,6 @@ ''' Ensures the 'style' module functions as expected. ''' -from __future__ import absolute_import, division, print_function import stylist.rule from stylist.source import FortranSource, SourceStringReader