From 47a6a51258a4272d44c7af866031c4da4afa9e8a Mon Sep 17 00:00:00 2001 From: Xavier Mitault Date: Sun, 6 Nov 2022 19:35:16 +0100 Subject: [PATCH] Add parser of gitignore --- norma2/config/config.py | 3 ++ norma2/config/config_class.py | 16 ++++++- norma2/config/from_gitignore.py | 17 +++++++ norma2/main.py | 7 ++- norma2/parser/get_files.py | 24 +++++++--- pdm.lock | 80 ++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 7 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 norma2/config/from_gitignore.py diff --git a/norma2/config/config.py b/norma2/config/config.py index 404e3c7..fdcc765 100644 --- a/norma2/config/config.py +++ b/norma2/config/config.py @@ -4,6 +4,7 @@ from norma2.config.config_class import Config from norma2.config.from_cmdline import from_cmdline +from norma2.config.from_gitignore import from_gitignore from norma2.config.from_json import from_json @@ -13,4 +14,6 @@ def get_config(console: Console, argv: Optional[List[str]] = None) -> Config: conf = conf + conf_cmdline conf_json = from_json(console) conf = conf + conf_json + conf_gitignore = from_gitignore(console) + conf = conf + conf_gitignore return conf diff --git a/norma2/config/config_class.py b/norma2/config/config_class.py index 8eb0744..d939f4e 100644 --- a/norma2/config/config_class.py +++ b/norma2/config/config_class.py @@ -1,9 +1,12 @@ import json from argparse import Namespace -from typing import Any, Union +from contextlib import suppress +from typing import Any, Callable, Optional, Union from rich.console import Console +GITIGNORE_MATCHES_TYPE = Optional[Callable[[str], bool]] + class __OutputFormat: __choices = { @@ -115,6 +118,8 @@ class __Defaults: explain_error = "" list_errors = False install_completion = False + folder_exclude = [".git"] + file_ext_exclude = [] _options = [ "operators_plugin", "preview", @@ -132,10 +137,14 @@ class __Defaults: "explain_error", "list_errors", "install_completion", + "folder_exclude", + "file_ext_exclude", ] class Config(__Defaults): + gitignore_matches: GITIGNORE_MATCHES_TYPE = None + def __init__(self, console: Console) -> None: super().__init__() self.console: Console = console @@ -163,6 +172,11 @@ def __add__(self, other): self.__add_one(other, attr, conf) except Exception: self.console.print_exception() + with suppress(Exception): + if self.gitignore_matches is None: + conf.gitignore_matches = other.gitignore_matches + else: + conf.gitignore_matches = self.gitignore_matches return conf def __str__(self): diff --git a/norma2/config/from_gitignore.py b/norma2/config/from_gitignore.py new file mode 100644 index 0000000..bdf739f --- /dev/null +++ b/norma2/config/from_gitignore.py @@ -0,0 +1,17 @@ +import os + +from igittigitt import IgnoreParser +from rich.console import Console + +from norma2.config.config_class import Config + + +def from_gitignore(console: Console, conf_path: str = ".") -> Config: + full_path = os.path.join(conf_path, ".gitignore") + full_path = os.path.abspath(full_path) + parser = IgnoreParser() + parser.parse_rule_file(full_path) + matches = parser.match + conf = Config(console) + conf.gitignore_matches = matches + return conf diff --git a/norma2/main.py b/norma2/main.py index dc5228e..0b410b1 100644 --- a/norma2/main.py +++ b/norma2/main.py @@ -102,7 +102,12 @@ def main(config: Config) -> int: config.console.print(f"[blue]Check: {folder}", justify="center") list_all_err: List[List[_TemplateNormError]] = [] try: - files_to_check = get_files.get_all_files(folder, [], []) + files_to_check = get_files.get_all_files( + folder, + config.folder_exclude, + config.file_ext_exclude, + config.gitignore_matches, + ) except Exception: config.console.print(":warning: [red]An Error Occured") config.console.print_exception() diff --git a/norma2/parser/get_files.py b/norma2/parser/get_files.py index d2309e3..04b87f2 100644 --- a/norma2/parser/get_files.py +++ b/norma2/parser/get_files.py @@ -1,23 +1,34 @@ import os from typing import List +from norma2.config.config_class import GITIGNORE_MATCHES_TYPE -def _can_add_file(file: str, exts: List[str], folder_out: List[str]): + +def _can_add_file( + file: str, + full_file: str, + exts: List[str], + folder_out: List[str], + gitignore_matches: GITIGNORE_MATCHES_TYPE, +): for ext in exts: if file.endswith(ext): return False - splited = file.split(os.path.sep) + splited = full_file.split(os.path.sep) for dirr in splited: for fold in folder_out: if dirr == fold: return False - return True + if gitignore_matches is None: + return True + return not gitignore_matches(full_file) def get_all_files( folder_or_file_path: str, folder_exclude: List[str], file_ext_exclude: List[str], + gitignore_matches: GITIGNORE_MATCHES_TYPE, ) -> List[str]: if os.path.isfile(folder_or_file_path): return [folder_or_file_path] @@ -25,8 +36,11 @@ def get_all_files( res = [] for root, _, files in os.walk(folder_or_file_path): for file in files: - if not _can_add_file(file, file_ext_exclude, folder_exclude): + full_file = os.path.join(root, file) + if not _can_add_file( + file, full_file, file_ext_exclude, folder_exclude, gitignore_matches + ): continue - res.append(os.path.join(root, file)) + res.append(full_file) return res raise os.error(f"Invalid path: {folder_or_file_path}") diff --git a/pdm.lock b/pdm.lock index feb4c16..dfeb9de 100644 --- a/pdm.lock +++ b/pdm.lock @@ -1,3 +1,9 @@ +[[package]] +name = "attrs" +version = "22.1.0" +requires_python = ">=3.5" +summary = "Classes Without Boilerplate" + [[package]] name = "black" version = "22.10.0" @@ -11,12 +17,28 @@ dependencies = [ "tomli>=1.1.0; python_full_version < \"3.11.0a7\"", ] +[[package]] +name = "bracex" +version = "2.3.post1" +requires_python = ">=3.7" +summary = "Bash style brace expander." + [[package]] name = "cfgv" version = "3.3.1" requires_python = ">=3.6.1" summary = "Validate configuration and produce human readable error messages." +[[package]] +name = "cli-exit-tools" +version = "1.2.3.2" +requires_python = ">=3.6.0" +summary = "functions to exit an cli application properly" +dependencies = [ + "click", + "lib-detect-testenv", +] + [[package]] name = "click" version = "8.1.3" @@ -71,6 +93,19 @@ version = "2.5.8" requires_python = ">=3.7" summary = "File identification library for Python" +[[package]] +name = "igittigitt" +version = "2.1.2" +requires_python = ">=3.6.0" +summary = "A spec-compliant gitignore parser for Python" +dependencies = [ + "attrs", + "cli-exit-tools", + "click", + "lib-detect-testenv", + "wcmatch", +] + [[package]] name = "isort" version = "5.10.1" @@ -86,6 +121,16 @@ dependencies = [ "MarkupSafe>=2.0", ] +[[package]] +name = "lib-detect-testenv" +version = "2.0.2.2" +requires_python = ">=3.6.0" +summary = "detects if pytest or doctest or pyrunner on pycharm is running" +dependencies = [ + "cli-exit-tools", + "click", +] + [[package]] name = "markdown" version = "3.4.1" @@ -297,11 +342,24 @@ dependencies = [ "platformdirs<3,>=2.4", ] +[[package]] +name = "wcmatch" +version = "8.4.1" +requires_python = ">=3.7" +summary = "Wildcard/glob file name matcher." +dependencies = [ + "bracex>=2.1.1", +] + [metadata] lock_version = "4.0" -content_hash = "sha256:ed115150166a82ba53d1406b667ecac3ebec22bbac630ea93e6d309d18d95185" +content_hash = "sha256:8b8a6b415031b18b1d69751de34c38b58cab2706abddd05e0242742b4835409f" [metadata.files] +"attrs 22.1.0" = [ + {url = "https://files.pythonhosted.org/packages/1a/cb/c4ffeb41e7137b23755a45e1bfec9cbb76ecf51874c6f1d113984ecaa32c/attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, + {url = "https://files.pythonhosted.org/packages/f2/bc/d817287d1aa01878af07c19505fafd1165cd6a119e9d0821ca1d1c20312d/attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, +] "black 22.10.0" = [ {url = "https://files.pythonhosted.org/packages/2c/11/f2737cd3b458d91401801e83a014e87c63e8904dc063200f77826c352f54/black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"}, {url = "https://files.pythonhosted.org/packages/3d/c5/b3ab9b563f35fb284d37ab2b14acaed9a27d8cdea9c31364766eb54946a7/black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"}, @@ -325,10 +383,18 @@ content_hash = "sha256:ed115150166a82ba53d1406b667ecac3ebec22bbac630ea93e6d309d1 {url = "https://files.pythonhosted.org/packages/f2/23/f4278377cabf882298b4766e977fd04377f288d1ccef706953076a1e0598/black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"}, {url = "https://files.pythonhosted.org/packages/ff/ce/22281871536b3d79474fd44d48dad48f7cbc5c3982bddf6a7495e7079d00/black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"}, ] +"bracex 2.3.post1" = [ + {url = "https://files.pythonhosted.org/packages/26/f5/7c60fb31c9aea37b3424e4206f9f6ed23c1ee0a717fa31749d10665a4eef/bracex-2.3.post1-py3-none-any.whl", hash = "sha256:351b7f20d56fb9ea91f9b9e9e7664db466eb234188c175fd943f8f755c807e73"}, + {url = "https://files.pythonhosted.org/packages/b3/96/d53e290ddf6215cfb24f93449a1835eff566f79a1f332cf046a978df0c9e/bracex-2.3.post1.tar.gz", hash = "sha256:e7b23fc8b2cd06d3dec0692baabecb249dda94e06a617901ff03a6c56fd71693"}, +] "cfgv 3.3.1" = [ {url = "https://files.pythonhosted.org/packages/6d/82/0a0ebd35bae9981dea55c06f8e6aaf44a49171ad798795c72c6f64cba4c2/cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {url = "https://files.pythonhosted.org/packages/c4/bf/d0d622b660d414a47dc7f0d303791a627663f554345b21250e39e7acb48b/cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] +"cli-exit-tools 1.2.3.2" = [ + {url = "https://files.pythonhosted.org/packages/27/ce/8ffc01c0bf8dbe07894846c214eac61f7d8b53ff0dbf920463c5bbba6887/cli_exit_tools-1.2.3.2-py3-none-any.whl", hash = "sha256:6268d51e6ac400fc2f349be2c81c1e7bb9cb3ff94e4058fa5220620c0aadae9f"}, + {url = "https://files.pythonhosted.org/packages/5d/4d/27fcad9f189a53f85e4c23f16444890b3df483469c96c258acd7dcc1e079/cli_exit_tools-1.2.3.2.tar.gz", hash = "sha256:60e798a219c5e3bf42ca309575afdbae22808b55bcf030554a61ea21c68fc206"}, +] "click 8.1.3" = [ {url = "https://files.pythonhosted.org/packages/59/87/84326af34517fca8c58418d148f2403df25303e02736832403587318e9e8/click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, {url = "https://files.pythonhosted.org/packages/c2/f1/df59e28c642d583f7dacffb1e0965d0e00b218e0186d7858ac5233dce840/click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, @@ -361,6 +427,10 @@ content_hash = "sha256:ed115150166a82ba53d1406b667ecac3ebec22bbac630ea93e6d309d1 {url = "https://files.pythonhosted.org/packages/1b/66/c826b47a47d805f09092b8f5a4dd241aebc50bb3b190b9e2e3d44e3f6f55/identify-2.5.8-py2.py3-none-any.whl", hash = "sha256:48b7925fe122720088aeb7a6c34f17b27e706b72c61070f27fe3789094233440"}, {url = "https://files.pythonhosted.org/packages/67/e1/869d7b8df41a3ac2a3c74a2a4ba401df468044dccc489b8937aad40d148e/identify-2.5.8.tar.gz", hash = "sha256:7a214a10313b9489a0d61467db2856ae8d0b8306fc923e03a9effa53d8aedc58"}, ] +"igittigitt 2.1.2" = [ + {url = "https://files.pythonhosted.org/packages/6a/72/f4b2bf951364d577c5bf98f9ae40503484a6dbf84a946e9db6f58d9c9553/igittigitt-2.1.2.tar.gz", hash = "sha256:e9830e43704343fc561ebcbb73633d64277f52364bd58c4df8d6e8eb6ef2a07e"}, + {url = "https://files.pythonhosted.org/packages/b3/49/ef4495cb8f1ec2feff29fca24790af6da13f45feb03f108fc21e797c511c/igittigitt-2.1.2-py3-none-any.whl", hash = "sha256:6ce86ebbd6a3bdb4ceba5171216c21f40400c8816575332b4320aa63d86f7e5a"}, +] "isort 5.10.1" = [ {url = "https://files.pythonhosted.org/packages/ab/e9/964cb0b2eedd80c92f5172f1f8ae0443781a9d461c1372a3ce5762489593/isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, {url = "https://files.pythonhosted.org/packages/b8/5b/f18e227df38b94b4ee30d2502fd531bebac23946a2497e5595067a561274/isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, @@ -369,6 +439,10 @@ content_hash = "sha256:ed115150166a82ba53d1406b667ecac3ebec22bbac630ea93e6d309d1 {url = "https://files.pythonhosted.org/packages/7a/ff/75c28576a1d900e87eb6335b063fab47a8ef3c8b4d88524c4bf78f670cce/Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, {url = "https://files.pythonhosted.org/packages/bc/c3/f068337a370801f372f2f8f6bad74a5c140f6fda3d9de154052708dd3c65/Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, ] +"lib-detect-testenv 2.0.2.2" = [ + {url = "https://files.pythonhosted.org/packages/8e/3f/6bb3ee83061ae52317887671b7eb48190a20f696e89f52bfae12481a3345/lib_detect_testenv-2.0.2.2-py3-none-any.whl", hash = "sha256:e2d63e1013b0b408f4366e62ecb3fbb3977e407561c3fc8c75b8701f9a263cc2"}, + {url = "https://files.pythonhosted.org/packages/a7/09/ac7c5eb3f357f94e3cebe7ee733d71c917ce37c8cbaea2b687496f94f89b/lib_detect_testenv-2.0.2.2.tar.gz", hash = "sha256:d8c4d5d725250ab7bff2a5bd4a9f628171e0a5372094f8e34d0e76281d4fca6d"}, +] "markdown 3.4.1" = [ {url = "https://files.pythonhosted.org/packages/85/7e/133e943e97a943d2f1d8bae0c5060f8ac50e6691754eb9dbe036b047a9bb/Markdown-3.4.1.tar.gz", hash = "sha256:3b809086bb6efad416156e00a0da66fe47618a5d6918dd688f53f40c8e4cfeff"}, {url = "https://files.pythonhosted.org/packages/86/be/ad281f7a3686b38dd8a307fa33210cdf2130404dfef668a37a4166d737ca/Markdown-3.4.1-py3-none-any.whl", hash = "sha256:08fb8465cffd03d10b9dd34a5c3fea908e20391a2a90b88d66362cb05beed186"}, @@ -705,3 +779,7 @@ content_hash = "sha256:ed115150166a82ba53d1406b667ecac3ebec22bbac630ea93e6d309d1 {url = "https://files.pythonhosted.org/packages/b4/27/b71df0a723d879baa0af1ad897b2498ad78f284ae668b4420092e44c05fa/virtualenv-20.16.6.tar.gz", hash = "sha256:530b850b523c6449406dfba859d6345e48ef19b8439606c5d74d7d3c9e14d76e"}, {url = "https://files.pythonhosted.org/packages/ef/e0/1295d8a0b34f71a81fdf0f09c1ef658ae6d611240829c3c39fb2b6b80967/virtualenv-20.16.6-py3-none-any.whl", hash = "sha256:186ca84254abcbde98180fd17092f9628c5fe742273c02724972a1d8a2035108"}, ] +"wcmatch 8.4.1" = [ + {url = "https://files.pythonhosted.org/packages/b4/a7/1875f68c4e39f9c68f5ba3aaf80ed1853903e5119941d514789fbc51a80d/wcmatch-8.4.1-py3-none-any.whl", hash = "sha256:3476cd107aba7b25ba1d59406938a47dc7eec6cfd0ad09ff77193f21a964dee7"}, + {url = "https://files.pythonhosted.org/packages/b7/94/5dd083fc972655f6689587c3af705aabc8b8e781bacdf22d6d2282fe6142/wcmatch-8.4.1.tar.gz", hash = "sha256:b1f042a899ea4c458b7321da1b5e3331e3e0ec781583434de1301946ceadb943"}, +] diff --git a/pyproject.toml b/pyproject.toml index bcbc098..8e6acab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ dependencies = [ "rich>=12.6.0", "rich-argparse>=0.3.1", "shtab>=1.5.7", + "igittigitt>=2.1.2", ] requires-python = ">=3.10" readme = "README.md"