Skip to content

Commit

Permalink
feat: check if a text file contains lines (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreoliwa authored Jul 13, 2020
1 parent 6d2df4f commit 3173bf7
Show file tree
Hide file tree
Showing 31 changed files with 371 additions and 175 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v2.6.2
rev: v2.7.0
hooks:
- id: pyupgrade
- repo: https://github.com/python/black
Expand All @@ -27,7 +27,7 @@ repos:
hooks:
- id: seed-isort-config
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
rev: v5.0.9
hooks:
- id: isort
- repo: https://github.com/pre-commit/pygrep-hooks
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ always-run:
.PHONY: always-run

pre-commit .cache/make/long-pre-commit: .pre-commit-config.yaml .pre-commit-hooks.yaml # Update and install pre-commit hooks
# TODO: isort 5.0.0 is apparently broken, so we can't autoupdate for now
# pre-commit autoupdate
@# Uncomment the lines below to autoupdate all repos except a few filtered out with egrep
# yq -r '.repos[].repo' .pre-commit-config.yaml | egrep -v -e '^local' -e mirrors-isort | \
# sed -E -e 's/http/--repo http/g' | xargs pre-commit autoupdate
pre-commit autoupdate
pre-commit install --install-hooks
pre-commit install --hook-type commit-msg
pre-commit gc
Expand Down
4 changes: 2 additions & 2 deletions docs/defaults.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ Content of `styles/isort.toml <https://raw.githubusercontent.com/andreoliwa/nitp
hooks:
- id: seed-isort-config
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.21
rev: v5.0.9
hooks:
- id: isort
"""
Expand Down Expand Up @@ -266,7 +266,7 @@ Content of `styles/pre-commit/general.toml <https://raw.githubusercontent.com/an
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v2.6.2
rev: v2.7.0
hooks:
- id: pyupgrade
"""
Expand Down
46 changes: 17 additions & 29 deletions docs/generate_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,8 @@
from slugify import slugify
from sortedcontainers import SortedDict

from nitpick.app import NitpickApp
from nitpick.constants import RAW_GITHUB_CONTENT_BASE_URL
from nitpick.generic import get_subclasses
from nitpick.plugins.base import BaseFile
from nitpick.plugins.json import JSONFile
from nitpick.plugins.pre_commit import PreCommitFile
from nitpick.plugins.pyproject_toml import PyProjectTomlFile
from nitpick.plugins.setup_cfg import SetupCfgFile

style_mapping = SortedDict(
{
Expand All @@ -47,7 +42,7 @@
"python37.toml": "Python 3.7",
}
)
file_classes = [PyProjectTomlFile, SetupCfgFile, PreCommitFile, JSONFile]
app = NitpickApp.create_app()

divider = ".. auto-generated-from-here"
docs_dir = Path(__file__).parent.absolute() # type: Path
Expand Down Expand Up @@ -122,9 +117,9 @@ def generate_defaults_rst():
sys.exit(1)


def generate_config_files_rst():
"""Generate config_files.rst with the docstrings from BaseFile classes."""
rst_file = docs_dir / "config_files.rst" # type: Path
def generate_plugins_rst():
"""Generate plugins.rst with the docstrings from :py:class:`nitpick.plugins.base.NitpickPlugin` classes."""
rst_file = docs_dir / "plugins.rst" # type: Path

template = """
.. _{link}:
Expand All @@ -136,40 +131,33 @@ def generate_config_files_rst():
"""
clean_template = dedent(template).strip()
blocks = []
for file_class in file_classes:
header = file_class.file_name

# Sort order: classes with fixed file names first, then alphabetically by class name
for plugin_class in sorted(
app.plugin_manager.hook.plugin_class(), key=lambda c: "0" if c.file_name else "1" + c.__name__
):
header = plugin_class.file_name
if not header:
# module_name = file_class.__module__
module = import_module(file_class.__module__)
module = import_module(plugin_class.__module__)
header = module.__doc__.strip(" .")

stripped_lines = [line.strip() for line in file_class.__doc__.split("\n")]
# Padding with any char except space (it doesn't work)
indented_doc = "xxxx" + plugin_class.__doc__
stripped_lines = [line[4:] for line in indented_doc.split("\n")]

blocks.append("")
blocks.append(
clean_template.format(
link=slugify(file_class.__name__),
link=slugify(plugin_class.__name__),
header=header,
dashes="-" * len(header),
description="\n".join(stripped_lines).strip(),
)
)
write_rst(rst_file, blocks)

existing = set(get_subclasses(BaseFile))
documented = set(file_classes)
something_missing = existing - documented
for missing_class in something_missing:
click.secho(
"ERROR: Add missing base file {} to the 'file_classes' var in '{}'.".format(
missing_class.__name__, __file__
),
fg="red",
)
if something_missing:
sys.exit(1)


if __name__ == "__main__":
generate_defaults_rst()
generate_config_files_rst()
generate_plugins_rst()
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Useful if you maintain multiple projects and want to use the same configs in all
auto_detection
tool_nitpick_section
nitpick_section
config_files
plugins
defaults
troubleshooting
contributing
Expand Down
2 changes: 1 addition & 1 deletion docs/nitpick_section.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,5 @@ If a key/value pair appears in more than one sub-style, it will be overridden; t
[nitpick.JSONFile]
------------------

Configure the list of filenames that should be checked by the :py:class:`nitpick.plugins.json.JSONFile` class.
Configure the list of filenames that should be checked by the :py:class:`nitpick.plugins.json.JSONPlugin` class.
See :ref:`the default package.json style <default-package-json>` for an example of usage.
49 changes: 33 additions & 16 deletions docs/config_files.rst → docs/plugins.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
.. include:: targets.rst
.. _config_files:
.. _plugins:

Configuration files
===================
Plugins
=======

Below are the currently supported configuration files.
Below are the currently included plugins.

.. auto-generated-from-here
.. _pyprojecttomlfile:
.. _setupcfgplugin:

setup.cfg
---------

Checker for the `setup.cfg <https://docs.python.org/3/distutils/configfile.html>`_ config file.

Example: :ref:`flake8 configuration <default-flake8>`.

.. _pyprojecttomlplugin:

pyproject.toml
--------------
Expand All @@ -20,16 +29,7 @@ See also `PEP 518 <https://www.python.org/dev/peps/pep-0518/>`_.
Example: :ref:`the Python 3.7 default <default-python-3-7>`.
There are many other examples in :ref:`defaults`.

.. _setupcfgfile:

setup.cfg
---------

Checker for the `setup.cfg <https://docs.python.org/3/distutils/configfile.html>`_ config file.

Example: :ref:`flake8 configuration <default-flake8>`.

.. _precommitfile:
.. _precommitplugin:

.pre-commit-config.yaml
-----------------------
Expand All @@ -38,7 +38,7 @@ Checker for the `.pre-commit-config.yaml <https://pre-commit.com/#pre-commit-con

Example: :ref:`the default pre-commit hooks <default-pre-commit-hooks>`.

.. _jsonfile:
.. _jsonplugin:

JSON files
----------
Expand All @@ -52,3 +52,20 @@ Example: :ref:`the default config for package.json <default-package-json>`.

If a JSON file is configured on ``[nitpick.JSONFile] file_names``, then a configuration for it should exist.
Otherwise, a style validation error will be raised.

.. _textplugin:

Text files
----------

Checker for text files.

To check if ``some.txt`` file contains the lines ``abc`` and ``def`` (in any order):

.. code-block:: toml
[["some.txt".contains]]
line = "abc"
[["some.txt".contains]]
line = "def"
1 change: 1 addition & 0 deletions docs/source/nitpick.plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ Submodules
nitpick.plugins.pre_commit
nitpick.plugins.pyproject_toml
nitpick.plugins.setup_cfg
nitpick.plugins.text
7 changes: 7 additions & 0 deletions docs/source/nitpick.plugins.text.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
nitpick.plugins.text module
===========================

.. automodule:: nitpick.plugins.text
:members:
:undoc-members:
:show-inheritance:
18 changes: 9 additions & 9 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ classifiers = [
NIP = "nitpick.flake8:NitpickExtension"

[tool.poetry.plugins.nitpick]
text = "nitpick.plugins.text"
json = "nitpick.plugins.json"
pre_commit = "nitpick.plugins.pre_commit"
setup_cfg = "nitpick.plugins.setup_cfg"
Expand Down
10 changes: 5 additions & 5 deletions src/nitpick/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def create_app(cls, offline=False) -> "NitpickApp":
"""Create a single application."""
# pylint: disable=import-outside-toplevel
from nitpick.config import Config # pylint: disable=redefined-outer-name
from nitpick.plugins.base import BaseFile
from nitpick.plugins.base import NitpickPlugin

app = cls()
cls._current_app = app
Expand All @@ -63,7 +63,7 @@ def create_app(cls, offline=False) -> "NitpickApp":
app.main_python_file = app.find_main_python_file()
app.config = Config()
app.plugin_manager = app.load_plugins()
BaseFile.load_fixed_dynamic_classes()
NitpickPlugin.load_fixed_dynamic_classes()
except (NoRootDir, NoPythonFile) as err:
app.init_errors.append(err)

Expand All @@ -89,8 +89,8 @@ def find_root_dir() -> Path:
Start from the current working dir.
"""
# pylint: disable=import-outside-toplevel
from nitpick.plugins.pyproject_toml import PyProjectTomlFile
from nitpick.plugins.setup_cfg import SetupCfgFile
from nitpick.plugins.pyproject_toml import PyProjectTomlPlugin
from nitpick.plugins.setup_cfg import SetupCfgPlugin

root_dirs = set() # type: Set[Path]
seen = set() # type: Set[Path]
Expand All @@ -101,7 +101,7 @@ def find_root_dir() -> Path:
starting_dir = Path(starting_file).parent.absolute()
while True:
project_files = climb_directory_tree(
starting_dir, ROOT_FILES + (PyProjectTomlFile.file_name, SetupCfgFile.file_name)
starting_dir, ROOT_FILES + (PyProjectTomlPlugin.file_name, SetupCfgPlugin.file_name)
)
if project_files and project_files & seen:
break
Expand Down
13 changes: 7 additions & 6 deletions src/nitpick/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@
TOOL_NITPICK,
TOOL_NITPICK_JMEX,
)
from nitpick.formats import TomlFormat
from nitpick.formats import TOMLFormat
from nitpick.generic import search_dict, version_to_tuple
from nitpick.mixin import NitpickMixin
from nitpick.plugins.pyproject_toml import PyProjectTomlFile
from nitpick.plugins.pyproject_toml import PyProjectTomlPlugin
from nitpick.schemas import ToolNitpickSectionSchema, flatten_marshmallow_errors
from nitpick.style import Style
from nitpick.typedefs import YieldFlake8Error

if TYPE_CHECKING:
from pathlib import Path

from nitpick.typedefs import JsonDict, StrOrList

LOGGER = logging.getLogger(__name__)
Expand All @@ -32,22 +33,22 @@ class Config(NitpickMixin): # pylint: disable=too-many-instance-attributes

def __init__(self) -> None:

self.pyproject_toml = None # type: Optional[TomlFormat]
self.pyproject_toml = None # type: Optional[TOMLFormat]
self.tool_nitpick_dict = {} # type: JsonDict
self.style_dict = {} # type: JsonDict
self.nitpick_section = {} # type: JsonDict
self.nitpick_files_section = {} # type: JsonDict

def validate_pyproject_tool_nitpick(self) -> bool:
"""Validate the ``pyroject.toml``'s ``[tool.nitpick]`` section against a Marshmallow schema."""
pyproject_path = NitpickApp.current().root_dir / PyProjectTomlFile.file_name # type: Path
pyproject_path = NitpickApp.current().root_dir / PyProjectTomlPlugin.file_name # type: Path
if pyproject_path.exists():
self.pyproject_toml = TomlFormat(path=pyproject_path)
self.pyproject_toml = TOMLFormat(path=pyproject_path)
self.tool_nitpick_dict = search_dict(TOOL_NITPICK_JMEX, self.pyproject_toml.as_data, {})
pyproject_errors = ToolNitpickSectionSchema().validate(self.tool_nitpick_dict)
if pyproject_errors:
NitpickApp.current().add_style_error(
PyProjectTomlFile.file_name,
PyProjectTomlPlugin.file_name,
"Invalid data in [{}]:".format(TOOL_NITPICK),
flatten_marshmallow_errors(pyproject_errors),
)
Expand Down
Loading

0 comments on commit 3173bf7

Please sign in to comment.