diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78a8e55..6389a71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - name: Run checks run: | - python3 precommit.py + python3 continuous_integration/precommit.py - name: Upload Coverage run: coveralls --service=github diff --git a/README.rst b/README.rst index 08c2b74..0ded01c 100644 --- a/README.rst +++ b/README.rst @@ -100,7 +100,7 @@ Development .. code-block:: bash - python precommit.py + python continuous_integration/precommit.py Versioning ========== diff --git a/continuous_integration/__init__.py b/continuous_integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/check_help_in_readme.py b/continuous_integration/check_help_in_readme.py similarity index 83% rename from check_help_in_readme.py rename to continuous_integration/check_help_in_readme.py index dc1489a..04e3c01 100644 --- a/check_help_in_readme.py +++ b/continuous_integration/check_help_in_readme.py @@ -50,14 +50,14 @@ def parse_readme(lines: List[str]) -> Tuple[List[Block], List[str]]: mtch = HELP_STARTS_RE.match(lines[i]) if mtch: command = mtch.group("command") - help_ends = ".. Help ends: {}".format(command) + help_ends = f".. Help ends: {command}" try: end_index = lines.index(help_ends, i) except ValueError: end_index = -1 if end_index == -1: - return [], ["Could not find the end marker {!r}".format(help_ends)] + return [], [f"Could not find the end marker {help_ends!r}"] blocks.append( Block(command=command, start_line_idx=i + 1, end_line_idx=end_index) @@ -84,18 +84,18 @@ def capture_output_lines(command: str) -> List[str]: f"The python interpreter could not be found: {command_parts[0]}" ) - proc = subprocess.Popen( + with subprocess.Popen( command_parts, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding="utf-8", - ) - output, err = proc.communicate() - if err: - raise RuntimeError( - f"The command {command!r} failed with exit code {proc.returncode} and " - f"stderr:\n{err}" - ) + ) as proc: + output, err = proc.communicate() + if err: + raise RuntimeError( + f"The command {command!r} failed with exit code {proc.returncode} and " + f"stderr:\n{err}" + ) return output.splitlines() @@ -126,16 +126,16 @@ def diff(got_lines: List[str], expected_lines: List[str]) -> Optional[str]: result.append("Expected:") for i, line in enumerate(expected_lines): if i >= len(got_lines) or line != got_lines[i]: - print("DIFF: {:2d}: {!r}".format(i, line)) + print(f"DIFF: {i:2d}: {line!r}") else: - print("OK : {:2d}: {!r}".format(i, line)) + print(f"OK : {i:2d}: {line!r}") result.append("Got:") for i, line in enumerate(got_lines): if i >= len(expected_lines) or line != expected_lines[i]: - print("DIFF: {:2d}: {!r}".format(i, line)) + print(f"DIFF: {i:2d}: {line!r}") else: - print("OK : {:2d}: {!r}".format(i, line)) + print(f"OK : {i:2d}: {line!r}") return "\n".join(result) @@ -152,20 +152,22 @@ def main() -> int: args = parser.parse_args() overwrite = bool(args.overwrite) - this_dir = pathlib.Path(os.path.realpath(__file__)).parent - pth = this_dir / "README.rst" + repo_root = pathlib.Path(os.path.realpath(__file__)).parent.parent + pth = repo_root / "README.rst" text = pth.read_text(encoding="utf-8") lines = text.splitlines() blocks, errors = parse_readme(lines=lines) if errors: - print("One or more errors in {}:".format(pth), file=sys.stderr) + print(f"One or more errors in {pth}:", file=sys.stderr) for error in errors: print(error, file=sys.stderr) return -1 + assert blocks is not None + if len(blocks) == 0: return 0 @@ -185,6 +187,7 @@ def main() -> int: result.extend(code_block_lines) previous_block = block + assert previous_block is not None result.extend(lines[previous_block.end_line_idx :]) result.append("") # new line at the end of file @@ -198,9 +201,11 @@ def main() -> int: expected_lines = lines[block.start_line_idx : block.end_line_idx] expected_lines = [line.rstrip() for line in expected_lines] - error = diff(got_lines=code_block_lines, expected_lines=expected_lines) - if error: - print(error, file=sys.stderr) + maybe_error = diff( + got_lines=code_block_lines, expected_lines=expected_lines + ) + if maybe_error: + print(maybe_error, file=sys.stderr) return -1 return 0 diff --git a/check_version_consistent.py b/continuous_integration/check_version_consistent.py similarity index 100% rename from check_version_consistent.py rename to continuous_integration/check_version_consistent.py diff --git a/precommit.py b/continuous_integration/precommit.py similarity index 87% rename from precommit.py rename to continuous_integration/precommit.py index 92031a7..77fa56a 100644 --- a/precommit.py +++ b/continuous_integration/precommit.py @@ -7,6 +7,8 @@ import subprocess import sys +# pylint: disable=missing-docstring + class Step(enum.Enum): BLACK = "black" @@ -60,21 +62,20 @@ def main() -> int: selects = ( [Step(value) for value in args.select] if args.select is not None - else [value for value in Step] + else [value for value in Step] # pylint: disable=unnecessary-comprehension ) skips = [Step(value) for value in args.skip] if args.skip is not None else [] - repo_root = pathlib.Path(__file__).parent + repo_root = pathlib.Path(__file__).parent.parent if Step.BLACK in selects and Step.BLACK not in skips: print("Black'ing...") # fmt: off black_targets = [ "abnf_to_regexp", - "precommit.py", - "check_version_consistent.py", - "check_help_in_readme.py", - "dev_scripts" + "continuous_integration", + "dev_scripts", + "tests" ] # fmt: on @@ -93,7 +94,12 @@ def main() -> int: if Step.MYPY in selects and Step.MYPY not in skips: print("Mypy'ing...") # fmt: off - mypy_targets = ["abnf_to_regexp", "tests", "dev_scripts"] + mypy_targets = [ + "abnf_to_regexp", + "tests", + "dev_scripts", + "continuous_integration" + ] subprocess.check_call( [ sys.executable, @@ -108,13 +114,17 @@ def main() -> int: if Step.PYLINT in selects and Step.PYLINT not in skips: # fmt: off print("Pylint'ing...") - pylint_targets = ["abnf_to_regexp", "tests", "dev_scripts"] + pylint_targets = [ + "abnf_to_regexp", + "tests", + "dev_scripts", + "continuous_integration" + ] subprocess.check_call( [ sys.executable, "-m", - "pylint", - "--rcfile=pylint.rc" + "pylint" ] + pylint_targets, cwd=str(repo_root) ) # fmt: on @@ -166,7 +176,9 @@ def main() -> int: "Checking that the version is consistent between " "abnf_to_regexp/__init__.py and pyproject.toml ..." ) - subprocess.check_call([sys.executable, "check_version_consistent.py"]) + subprocess.check_call( + [sys.executable, "continuous_integration/check_version_consistent.py"] + ) else: print( "Skipped checking that the versions in abnf_to_regexp/__init__.py " @@ -175,7 +187,7 @@ def main() -> int: if Step.CHECK_HELP_IN_README in selects and Step.CHECK_HELP_IN_README not in skips: if sys.version_info < (3, 10): - cmd = [sys.executable, "check_help_in_readme.py"] + cmd = [sys.executable, "continuous_integration/check_help_in_readme.py"] if overwrite: cmd.append("--overwrite") diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 691e938..0000000 --- a/mypy.ini +++ /dev/null @@ -1,10 +0,0 @@ -[mypy] - -[mypy-abnf] -ignore_missing_imports = True - -[mypy-regex] -ignore_missing_imports = True - -[mypy-sortedcontainers] -ignore_missing_imports = True diff --git a/pylint.rc b/pylint.rc deleted file mode 100644 index e05a2f8..0000000 --- a/pylint.rc +++ /dev/null @@ -1,11 +0,0 @@ -[TYPECHECK] -ignored-modules = numpy -ignored-classes = numpy,PurePath -generated-members=bottle\.request\.forms\.decode,bottle\.request\.query\.decode - -[FORMAT] -max-line-length=120 - -[MESSAGES CONTROL] -disable=too-few-public-methods,len-as-condition,duplicate-code,no-else-raise,no-else-return,too-many-locals,too-many-branches,too-many-nested-blocks,too-many-return-statements,unsubscriptable-object,not-an-iterable,broad-except,too-many-statements,protected-access,unnecessary-pass,use-dict-literal - diff --git a/pyproject.toml b/pyproject.toml index 30dbd70..5fc56f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,4 +54,41 @@ packages = ["abnf_to_regexp"] abnf_to_regexp = ["py.typed"] [tool.setuptools.exclude-package-data] -"*" = ["tests*"] \ No newline at end of file +"*" = ["tests*"] + +[tool.pylint.format] +max-line-length = 120 + +[tool.pylint."messages control"] +disable = [ + "too-few-public-methods", + "len-as-condition", + "duplicate-code", + "no-else-raise", + "no-else-return", + "too-many-locals", + "too-many-branches", + "too-many-nested-blocks", + "too-many-return-statements", + "unsubscriptable-object", + "not-an-iterable", + "broad-except", + "too-many-statements", + "protected-access", + "unnecessary-pass", + "use-dict-literal", +] + +[tool.mypy] + +[[tool.mypy.overrides]] +module = "abnf" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "regex" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "sortedcontainers" +ignore_missing_imports = true \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6d58b48..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -icontract>=2.5.1,<3 -regex==2021.4.4 -abnf>=2.0,<2.3.0 -sortedcontainers>=2,<3 diff --git a/tests/test_abnf_transformation.py b/tests/test_abnf_transformation.py index 83ab38d..eff5c00 100644 --- a/tests/test_abnf_transformation.py +++ b/tests/test_abnf_transformation.py @@ -43,10 +43,8 @@ def test_wider_than_gamuth(self) -> None: ) def test_misses_between_ranges(self) -> None: - for (_, end1), (start2, _) in ( - abnf_to_regexp.abnf_transformation.pairwise( - abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES - ) + for (_, end1), (start2, _) in abnf_to_regexp.abnf_transformation.pairwise( + abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES ): if start2 - end1 <= 2: continue @@ -58,15 +56,15 @@ def test_misses_between_ranges(self) -> None: ) def test_hits_with_start_and_end_between_ranges(self) -> None: - for (start1, end1), (start2, end2) in ( - abnf_to_regexp.abnf_transformation.pairwise( - abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES - ) + for (start1, end1), ( + start2, + end2, + ) in abnf_to_regexp.abnf_transformation.pairwise( + abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES ): self.assertTrue( abnf_to_regexp.abnf_transformation._range_overlaps_with_a_letter_range( - start_ord=min(start1 + 1, end1), - end_ord=min(start2 + 1, end2) + start_ord=min(start1 + 1, end1), end_ord=min(start2 + 1, end2) ) ) @@ -74,23 +72,19 @@ def test_hit_with_point_ranges(self) -> None: for start, end in abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES: self.assertTrue( abnf_to_regexp.abnf_transformation._range_overlaps_with_a_letter_range( - start_ord=start, - end_ord=start + start_ord=start, end_ord=start ) ) self.assertTrue( abnf_to_regexp.abnf_transformation._range_overlaps_with_a_letter_range( - start_ord=end, - end_ord=end + start_ord=end, end_ord=end ) ) def test_misses_between_ranges_with_point_range(self) -> None: - for (_, end1), (start2, _) in ( - abnf_to_regexp.abnf_transformation.pairwise( - abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES - ) + for (_, end1), (start2, _) in abnf_to_regexp.abnf_transformation.pairwise( + abnf_to_regexp.abnf_transformation._LETTER_ORD_RANGES ): if start2 - end1 <= 1: continue @@ -104,8 +98,7 @@ def test_misses_between_ranges_with_point_range(self) -> None: def test_with_explicit_case(self) -> None: self.assertTrue( abnf_to_regexp.abnf_transformation._range_overlaps_with_a_letter_range( - start_ord=ord('b'), - end_ord=ord('d') + start_ord=ord("b"), end_ord=ord("d") ) ) diff --git a/tests/test_main.py b/tests/test_main.py index c679288..5dd776c 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -14,8 +14,8 @@ def represent_diff(expected_text: str, got_text: str) -> Optional[str]: """Generate a diff message with +-30 characters from the first difference.""" width = max(len(expected_text), len(got_text)) - expected_padded = expected_text.ljust(width, '\0') - got_padded = got_text.ljust(width, '\0') + expected_padded = expected_text.ljust(width, "\0") + got_padded = got_text.ljust(width, "\0") first_diff_index = -1 for i, (expected, got) in enumerate(zip(expected_padded, got_padded)): @@ -28,12 +28,12 @@ def represent_diff(expected_text: str, got_text: str) -> Optional[str]: result = io.StringIO() - result.write('Offset: expected vs got\n') + result.write("Offset: expected vs got\n") for i in range(max(0, first_diff_index - 30), min(width, first_diff_index + 30)): - expected = repr(expected_text[i]) if i < len(expected_text) else 'N/A' - got = repr(got_text[i]) if i < len(got_text) else 'N/A' + expected = repr(expected_text[i]) if i < len(expected_text) else "N/A" + got = repr(got_text[i]) if i < len(got_text) else "N/A" - result.write(f'{i + 1:4d}: {expected} vs {got}') + result.write(f"{i + 1:4d}: {expected} vs {got}") if i == first_diff_index: result.write(" <<< first difference here <<<") @@ -61,10 +61,10 @@ def test_single_regexp(self) -> None: params=abnf_to_regexp.main.Params( input_path=grammar_pth, output_path=None, - fmt=abnf_to_regexp.main.Format.SINGLE_REGEXP + fmt=abnf_to_regexp.main.Format.SINGLE_REGEXP, ), stdout=stdout, - stderr=stderr + stderr=stderr, ) expected_err_pth = case_dir / "expected.err" @@ -74,11 +74,11 @@ def test_single_regexp(self) -> None: record = False if record: - expected_err_pth.write_text(stderr.getvalue(), encoding='utf-8') - expected_out_pth.write_text(stdout.getvalue(), encoding='utf-8') + expected_err_pth.write_text(stderr.getvalue(), encoding="utf-8") + expected_out_pth.write_text(stdout.getvalue(), encoding="utf-8") - expected_err = expected_err_pth.read_text(encoding='utf-8') - expected_out = expected_out_pth.read_text(encoding='utf-8') + expected_err = expected_err_pth.read_text(encoding="utf-8") + expected_out = expected_out_pth.read_text(encoding="utf-8") diff = represent_diff(expected_err, stderr.getvalue()) if diff: @@ -86,13 +86,15 @@ def test_single_regexp(self) -> None: f"Expected and obtained STDERR differ on {case_dir}. " f"Expected error:\n{expected_err!r}\n\n" f"Got error:\n{stderr.getvalue()!r}\n\n" - f"The diff was:\n{diff}") + f"The diff was:\n{diff}" + ) diff = represent_diff(expected_out, stdout.getvalue()) if diff: raise AssertionError( f"Expected and obtained STDOUT differ on {case_dir}. " - f"The diff was:\n{diff}") + f"The diff was:\n{diff}" + ) if not stderr.getvalue(): abnf_re_str = stdout.getvalue().strip() @@ -120,12 +122,14 @@ def test_single_regexp(self) -> None: self.assertRegex(example, abnf_re) for counter_example_pth in sorted( - case_dir.glob("counter_example*.txt")): + case_dir.glob("counter_example*.txt") + ): counter_example = counter_example_pth.read_text() self.assertIsNone( abnf_re.match(counter_example), f"Expected the counter-example not to match " - f"for {grammar_pth}: {counter_example_pth}") + f"for {grammar_pth}: {counter_example_pth}", + ) def test_python_nested(self) -> None: this_dir = pathlib.Path(os.path.realpath(__file__)).parent @@ -144,10 +148,10 @@ def test_python_nested(self) -> None: params=abnf_to_regexp.main.Params( input_path=grammar_pth, output_path=None, - fmt=abnf_to_regexp.main.Format.PYTHON_NESTED + fmt=abnf_to_regexp.main.Format.PYTHON_NESTED, ), stdout=stdout, - stderr=stderr + stderr=stderr, ) expected_err_pth = case_dir / "expected.err" @@ -157,11 +161,11 @@ def test_python_nested(self) -> None: record = False if record: - expected_err_pth.write_text(stderr.getvalue(), encoding='utf-8') - expected_out_pth.write_text(stdout.getvalue(), encoding='utf-8') + expected_err_pth.write_text(stderr.getvalue(), encoding="utf-8") + expected_out_pth.write_text(stdout.getvalue(), encoding="utf-8") - expected_err = expected_err_pth.read_text(encoding='utf-8') - expected_out = expected_out_pth.read_text(encoding='utf-8') + expected_err = expected_err_pth.read_text(encoding="utf-8") + expected_out = expected_out_pth.read_text(encoding="utf-8") diff = represent_diff(expected_err, stderr.getvalue()) if diff: @@ -169,23 +173,25 @@ def test_python_nested(self) -> None: f"Expected and obtained STDERR differ on {case_dir}. " f"Expected error:\n{expected_err!r}\n\n" f"Got error:\n{stderr.getvalue()!r}\n\n" - f"The diff was:\n{diff}") + f"The diff was:\n{diff}" + ) diff = represent_diff(expected_out, stdout.getvalue()) if diff: raise AssertionError( f"Expected and obtained STDOUT differ on {case_dir}. " - f"The diff was:\n{diff}") + f"The diff was:\n{diff}" + ) if not stderr.getvalue(): code = stdout.getvalue().strip() try: - compile(code, "", mode='exec') + compile(code, "", mode="exec") except Exception as exception: raise AssertionError( - f"Failed to compile code as in {expected_out_pth}:\n" - f"{code}" + f"Failed to compile code as in {expected_out_pth}:\n" f"{code}" ) from exception + if __name__ == "__main__": unittest.main() diff --git a/tests/test_representation.py b/tests/test_representation.py index c9892d0..06f78ef 100644 --- a/tests/test_representation.py +++ b/tests/test_representation.py @@ -6,7 +6,9 @@ # noinspection PyPep8Naming -class Test_escape_for_character_class(unittest.TestCase): # pylint: disable=invalid-name +class Test_escape_for_character_class( + unittest.TestCase +): # pylint: disable=invalid-name def test_empty_string(self) -> None: self.assertEqual("", escape_for_character_class(""))