diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 1a8a31c..40fe808 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -2,7 +2,7 @@ name: Question or consultation about: Ask anything about this project title: '' -labels: guestion +labels: question assignees: pomponchik --- diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7d54f80..c03f651 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -44,7 +44,7 @@ jobs: - name: Run mypy shell: bash - run: mypy dirstree + run: mypy --strict dirstree - name: Run mypy for tests shell: bash diff --git a/.ruff.toml b/.ruff.toml deleted file mode 100644 index 353bb74..0000000 --- a/.ruff.toml +++ /dev/null @@ -1,4 +0,0 @@ -lint.ignore = ['E501', 'E712'] - -[format] -quote-style = "single" diff --git a/README.md b/README.md index 82ba52f..03a6ce7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -![logo](https://raw.githubusercontent.com/pomponchik/dirstree/develop/docs/assets/logo_1.svg) +
+ [![Downloads](https://static.pepy.tech/badge/dirstree/month)](https://pepy.tech/project/dirstree) [![Downloads](https://static.pepy.tech/badge/dirstree)](https://pepy.tech/project/dirstree) @@ -12,6 +13,11 @@ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/pomponchik/dirstree) +
+ +![logo](https://raw.githubusercontent.com/pomponchik/dirstree/develop/docs/assets/logo_1.svg) + + There are many libraries for traversing directories. You can also do this using the standard library. This particular library is a bit different in that: - ⚗️ Filtering by file extensions, text patterns in [`.gitignore` format](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#_ignoring), and using custom callables. diff --git a/dirstree/__init__.py b/dirstree/__init__.py index ef8a58e..860b041 100644 --- a/dirstree/__init__.py +++ b/dirstree/__init__.py @@ -1,2 +1,4 @@ -from dirstree.crawlers.crawler import Crawler as Crawler # noqa: F401 -from dirstree.crawlers.python_crawler import PythonCrawler as PythonCrawler # noqa: F401 +from dirstree.crawlers.crawler import Crawler as Crawler +from dirstree.crawlers.python_crawler import ( + PythonCrawler as PythonCrawler, +) diff --git a/dirstree/crawlers/abstract.py b/dirstree/crawlers/abstract.py index 8aa3372..3e8638b 100644 --- a/dirstree/crawlers/abstract.py +++ b/dirstree/crawlers/abstract.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod -from typing import Generator from pathlib import Path +from typing import Generator from cantok import AbstractToken, DefaultToken @@ -13,10 +13,10 @@ def __add__(self, other: 'AbstractCrawler') -> 'AbstractCrawler': if not isinstance(other, AbstractCrawler): raise TypeError(f"Cannot add {type(self).__name__} and {type(other).__name__}.") - from dirstree.crawlers.group import CrawlersGroup + from dirstree.crawlers.group import CrawlersGroup # noqa: PLC0415 return CrawlersGroup([self, other]) @abstractmethod - def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: + def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: # noqa: B008 ... # pragma: no cover diff --git a/dirstree/crawlers/crawler.py b/dirstree/crawlers/crawler.py index a8259b8..18090f6 100644 --- a/dirstree/crawlers/crawler.py +++ b/dirstree/crawlers/crawler.py @@ -1,9 +1,10 @@ -from typing import List, Dict, Optional, Union, Collection, Generator, Callable, Any from pathlib import Path +from typing import Any, Callable, Collection, Dict, Generator, List, Optional, Union import pathspec -from printo import descript_data_object, not_none from cantok import AbstractToken, DefaultToken +from printo import descript_data_object, not_none +from sigmatch import PossibleCallMatcher from dirstree.crawlers.abstract import AbstractCrawler @@ -30,15 +31,17 @@ def __init__( *paths: Union[str, Path], extensions: Optional[Collection[str]] = None, exclude: Optional[List[str]] = None, - filter: Optional[Callable[[Path], bool]] = None, - token: AbstractToken = DefaultToken(), + filter: Optional[Callable[[Path], bool]] = None, # noqa: A002 + token: AbstractToken = DefaultToken(), # noqa: B008 ) -> None: if extensions is not None: for extension in extensions: if not extension.startswith('.'): raise ValueError( - f'The line with the file extension must start with a dot. You have passed: "{extension}".' + f'The line with the file extension must start with a dot. You have passed: "{extension}".', ) + if filter is not None: + PossibleCallMatcher('.').match(filter, raise_exception=True) self.paths = paths self.extensions = extensions @@ -69,7 +72,7 @@ def __repr__(self) -> str: filters=filters, # type: ignore[arg-type] ) - def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: + def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: # noqa: B008 token = token + self.token excludes_spec = pathspec.PathSpec.from_lines('gitwildmatch', self.exclude) diff --git a/dirstree/crawlers/group.py b/dirstree/crawlers/group.py index c6d92cd..d599a23 100644 --- a/dirstree/crawlers/group.py +++ b/dirstree/crawlers/group.py @@ -1,5 +1,5 @@ -from typing import List, Generator from pathlib import Path +from typing import Generator, List from cantok import AbstractToken, DefaultToken from printo import descript_data_object @@ -11,14 +11,14 @@ class CrawlersGroup(AbstractCrawler): def __init__(self, crawlers: List[AbstractCrawler]) -> None: self.crawlers = crawlers - def __repr__(self): + def __repr__(self) -> str: return descript_data_object( type(self).__name__, (self.crawlers,), {}, ) - def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: + def go(self, token: AbstractToken = DefaultToken()) -> Generator[Path, None, None]: # noqa: B008 memory = set() for crawler in self.crawlers: diff --git a/dirstree/crawlers/python_crawler.py b/dirstree/crawlers/python_crawler.py index 09ececd..d3b895e 100644 --- a/dirstree/crawlers/python_crawler.py +++ b/dirstree/crawlers/python_crawler.py @@ -1,5 +1,5 @@ -from typing import List, Optional, Union, Callable from pathlib import Path +from typing import Callable, List, Optional, Union from cantok import AbstractToken, DefaultToken @@ -11,12 +11,12 @@ def __init__( self, *paths: Union[str, Path], exclude: Optional[List[str]] = None, - filter: Optional[Callable[[Path], bool]] = None, - token: AbstractToken = DefaultToken(), + filter: Optional[Callable[[Path], bool]] = None, # noqa: A002 + token: AbstractToken = DefaultToken(), # noqa: B008 ) -> None: super().__init__( - *paths, extensions=('.py',), exclude=exclude, filter=filter, token=token + *paths, extensions=('.py',), exclude=exclude, filter=filter, token=token, ) self.addictional_repr_filters = { - 'extensions': lambda x: False, + 'extensions': lambda x: False, # noqa: ARG005 } diff --git a/pyproject.toml b/pyproject.toml index 96cef65..121c8c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "dirstree" -version = "0.0.3" +version = "0.0.4" authors = [{ name = "Evgeniy Blinov", email = "zheni-b@yandex.ru" }] description = 'Another library for iterating through the contents of a directory' readme = "README.md" requires-python = ">=3.8" -dependencies = ['pathspec==0.12.1', 'printo==0.0.8', 'cantok==0.0.32'] +dependencies = ['pathspec==0.12.1', 'printo>=0.0.10', 'cantok==0.0.32', 'sigmatch>=0.0.6'] classifiers = [ "Operating System :: OS Independent", @@ -25,6 +25,8 @@ classifiers = [ 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: Free Threading', + 'Programming Language :: Python :: Free Threading :: 3 - Stable', 'License :: OSI Approved :: MIT License', 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries', @@ -41,6 +43,11 @@ runner = "pytest" [tool.pytest.ini_options] markers = ["mypy_testing"] +[tool.ruff] +lint.ignore = ['E501', 'E712', 'PTH123', 'PTH118', 'PLR2004', 'PTH107', 'SIM105', 'SIM102', 'RET503', 'PLR0912', 'C901'] +lint.select = ["ERA001", "YTT", "ASYNC", "BLE", "B", "A", "COM", "INP", "PIE", "T20", "PT", "RSE", "RET", "SIM", "SLOT", "TID252", "ARG", "PTH", "I", "C90", "N", "E", "W", "D201", "D202", "D419", "F", "PL", "PLE", "PLR", "PLW", "RUF", "TRY201", "TRY400", "TRY401"] +format.quote-style = "single" + [project.urls] 'Source' = 'https://github.com/pomponchik/dirstree' 'Tracker' = 'https://github.com/pomponchik/dirstree/issues' diff --git a/requirements_dev.txt b/requirements_dev.txt index d097412..6105320 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -4,6 +4,6 @@ build==1.2.2.post1 twine==6.1.0 mypy==1.14.1 pytest-mypy-testing==0.1.3 -ruff==0.9.9 +ruff==0.14.6 mutmut==3.2.3 -full_match==0.0.2 +full_match==0.0.3 diff --git a/tests/test_crawler.py b/tests/test_crawler.py index 3403472..da83183 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -1,15 +1,16 @@ import os from pathlib import Path -from typing import Union, Type +from typing import Type, Union import pytest -import full_match -from cantok import ConditionToken, SimpleToken, DefaultToken +from cantok import ConditionToken, DefaultToken, SimpleToken +from full_match import match +from sigmatch.errors import SignatureMismatchError from dirstree import Crawler, PythonCrawler -def custom_filter(path: Path) -> bool: +def custom_filter(path: Path) -> bool: # noqa: ARG001 return True @@ -22,10 +23,10 @@ def test_crawl_test_directory_with_default_extensions( os.path.join('tests', 'test_files', 'walk_it', '__init__.py'), os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt', ), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), os.path.join('tests', 'test_files', 'walk_it', 'nested_folder', '__init__.py'), ] @@ -44,7 +45,7 @@ def test_crawl_test_directory_with_txt_extension( assert [str(x) for x in crawler] == [ os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt', ), ] @@ -56,7 +57,7 @@ def test_crawl_test_directory_with_py_extension(crawl_directory_path: Union[str, os.path.join('tests', 'test_files', 'walk_it', '__init__.py'), os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), os.path.join('tests', 'test_files', 'walk_it', 'nested_folder', '__init__.py'), ] @@ -76,7 +77,7 @@ def test_crawl_test_directory_with_exclude_with_py_extension( assert [str(x) for x in crawler] == [ os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), ] @@ -89,10 +90,10 @@ def test_crawl_test_directory_with_exclude_patterns_without_extensions( expected_paths = [ os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt', ), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), ] real_paths = [str(x) for x in crawler] @@ -107,18 +108,18 @@ def test_crawl_test_directory_with_exclude_patterns_and_extensions( crawl_directory_path: Union[str, Path], ): crawler = Crawler( - crawl_directory_path, extensions=['.txt'], exclude=['__init__.py'] + crawl_directory_path, extensions=['.txt'], exclude=['__init__.py'], ) assert [str(x) for x in crawler] == [ os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'non_python_file.txt', ), ] @pytest.mark.parametrize( - ['crawler', 'expected_repr'], + ('crawler', 'expected_repr'), [ (Crawler('.'), "Crawler('.')"), (Crawler('usr/bin'), "Crawler('usr/bin')"), @@ -126,7 +127,7 @@ def test_crawl_test_directory_with_exclude_patterns_and_extensions( (Crawler('.', exclude=['*.py'], extensions=['.py']), "Crawler('.', extensions=['.py'], exclude=['*.py'])"), (Crawler('.', exclude=['*.py']), "Crawler('.', exclude=['*.py'])"), (Crawler('.', filter=custom_filter), "Crawler('.', filter=custom_filter)"), - (Crawler('.', filter=lambda x: True), "Crawler('.', filter=λ)"), + (Crawler('.', filter=lambda x: True), "Crawler('.', filter=λ)"), # noqa: ARG005 (Crawler('.', token=ConditionToken(lambda: True)), "Crawler('.', token=ConditionToken(λ))"), (Crawler('../dirstree') + Crawler('../cantok'), "CrawlersGroup([Crawler('../dirstree'), Crawler('../cantok')])"), (Crawler('../dirstree') + PythonCrawler('../cantok'), "CrawlersGroup([Crawler('../dirstree'), PythonCrawler('../cantok')])"), @@ -137,11 +138,11 @@ def test_repr(crawler: Crawler, expected_repr: str): @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_iter(factory: Type[Crawler]): crawler = factory('.') @@ -150,11 +151,11 @@ def test_iter(factory: Type[Crawler]): @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_crawl_repeat(factory: Type[Crawler]): crawler = factory('.') @@ -163,117 +164,109 @@ def test_crawl_repeat(factory: Type[Crawler]): @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_filter_first(factory: Type[Crawler]): index = 0 - def filter(path) -> bool: + def empty_filter(path) -> bool: # noqa: ARG001 nonlocal index - if index == 0: - result = False - else: - result = True + result = index != 0 index += 1 return result - assert list(factory('.'))[1:] == list(factory('.', filter=filter)) + assert list(factory('.'))[1:] == list(factory('.', filter=empty_filter)) @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_argument_of_filter_is_path_object(crawl_directory_path: Union[str, Path], factory: Type[Crawler]): collector = [] - def filter(path): + def empty_filter(path): collector.append(path) return True - crawler = factory(crawl_directory_path, filter=filter) + crawler = factory(crawl_directory_path, filter=empty_filter) assert list(crawler) == collector @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) @pytest.mark.parametrize( - ['n'], + 'n', [ - (0,), - (1,), - (2,), - (3,), + 0, + 1, + 2, + 3, ], ) def test_cancel_after_n_iteranions(crawl_directory_path: Union[str, Path], n: int, factory: Type[Crawler]): index = 0 - def filter(path: Path) -> bool: + def empty_filter(path: Path) -> bool: # noqa: ARG001 nonlocal index index += 1 return True def condition() -> bool: - if index == n: - result = True - else: - result = False - - return result + return index == n token = ConditionToken(condition) - crawler = factory(crawl_directory_path, token=token, filter=filter) + crawler = factory(crawl_directory_path, token=token, filter=empty_filter) assert list(factory(crawl_directory_path))[:n] == list(crawler) @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_cancelled_token(crawl_directory_path: Union[str, Path], factory: Type[Crawler]): assert list(factory(crawl_directory_path, token=SimpleToken(cancelled=True))) == [] @pytest.mark.parametrize( - ['factory'], + 'factory', [ - (Crawler,), - (PythonCrawler,), - ] + Crawler, + PythonCrawler, + ], ) def test_default_token(crawl_directory_path: Union[str, Path], factory: Type[Crawler]): assert list(factory(crawl_directory_path, token=DefaultToken())) == list( - factory(crawl_directory_path) + factory(crawl_directory_path), ) def test_pass_not_starting_with_dot_extension(crawl_directory_path: Union[str, Path]): with pytest.raises( ValueError, - match=full_match( # type: ignore[operator] - 'The line with the file extension must start with a dot. You have passed: "txt".' + match=match( # type: ignore[operator] + 'The line with the file extension must start with a dot. You have passed: "txt".', ), ): Crawler(crawl_directory_path, extensions=['txt']) @@ -316,16 +309,21 @@ def test_sum_usual_crawler_and_python_crawler(): def test_try_to_sum_with_not_crawler(): - with pytest.raises(TypeError, match=full_match("Cannot add Crawler and int.")): + with pytest.raises(TypeError, match=match("Cannot add Crawler and int.")): Crawler('.') + 1 - with pytest.raises(TypeError, match=full_match("Cannot add Crawler and str.")): + with pytest.raises(TypeError, match=match("Cannot add Crawler and str.")): Crawler('.') + 'kek' def test_crawl_two_folders(crawl_directory_path: Union[str, Path], second_crawl_directory_path: Union[str, Path]): - list(Crawler(crawl_directory_path, second_crawl_directory_path)) == list(Crawler(crawl_directory_path)) + list(Crawler(second_crawl_directory_path)) + assert list(Crawler(crawl_directory_path, second_crawl_directory_path)) == list(Crawler(crawl_directory_path)) + list(Crawler(second_crawl_directory_path)) def test_crawl_without_path(): assert list(Crawler()) == [] + + +def test_check_filter_signature(): + with pytest.raises(SignatureMismatchError, match=match('The signature of the callable object does not match the expected one.')): + Crawler(filter=lambda: None) diff --git a/tests/test_files/walk_it/simple_code.py b/tests/test_files/walk_it/simple_code.py index 8d2f097..26f6297 100644 --- a/tests/test_files/walk_it/simple_code.py +++ b/tests/test_files/walk_it/simple_code.py @@ -1 +1 @@ -1 + 1 +1 + 1 # noqa: B018 diff --git a/tests/test_files/walk_it_2/simple_code.py b/tests/test_files/walk_it_2/simple_code.py index 8d2f097..26f6297 100644 --- a/tests/test_files/walk_it_2/simple_code.py +++ b/tests/test_files/walk_it_2/simple_code.py @@ -1 +1 @@ -1 + 1 +1 + 1 # noqa: B018 diff --git a/tests/test_python_crawler.py b/tests/test_python_crawler.py index 9aa2238..7ea4eaf 100644 --- a/tests/test_python_crawler.py +++ b/tests/test_python_crawler.py @@ -1,15 +1,17 @@ import os -from typing import Union -from pathlib import Path from inspect import signature +from pathlib import Path +from typing import Union import pytest from cantok import ConditionToken +from full_match import match +from sigmatch.errors import SignatureMismatchError from dirstree import Crawler, PythonCrawler -def custom_filter(path: Path) -> bool: +def custom_filter(path: Path) -> bool: # noqa: ARG001 return True @@ -29,7 +31,7 @@ def test_crawl_python_files_in_test_directory( os.path.join('tests', 'test_files', 'walk_it', '__init__.py'), os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), os.path.join('tests', 'test_files', 'walk_it', 'nested_folder', '__init__.py'), ] @@ -43,7 +45,7 @@ def test_crawl_python_files_in_test_directory( def test_python_crawler_is_same_as_crawler_with_python_extension(crawl_directory_path): assert list(PythonCrawler(crawl_directory_path)) == list( - Crawler(crawl_directory_path, extensions=['.py']) + Crawler(crawl_directory_path, extensions=['.py']), ) @@ -60,19 +62,19 @@ def test_crawl_test_directory_with_exclude_inits( assert [str(x) for x in crawler] == [ os.path.join('tests', 'test_files', 'walk_it', 'simple_code.py'), os.path.join( - 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py' + 'tests', 'test_files', 'walk_it', 'nested_folder', 'python_file.py', ), ] @pytest.mark.parametrize( - ['crawler', 'expected_repr'], + ('crawler', 'expected_repr'), [ (PythonCrawler('.'), "PythonCrawler('.')"), (PythonCrawler('usr/bin'), "PythonCrawler('usr/bin')"), (PythonCrawler('.', exclude=['*.py']), "PythonCrawler('.', exclude=['*.py'])"), (PythonCrawler('.', filter=custom_filter), "PythonCrawler('.', filter=custom_filter)"), - (PythonCrawler('.', filter=lambda x: True), "PythonCrawler('.', filter=λ)"), + (PythonCrawler('.', filter=lambda x: True), "PythonCrawler('.', filter=λ)"), # noqa: ARG005 (PythonCrawler('.', token=ConditionToken(lambda: True)), "PythonCrawler('.', token=ConditionToken(λ))"), (PythonCrawler('../dirstree') + PythonCrawler('../cantok'), "CrawlersGroup([PythonCrawler('../dirstree'), PythonCrawler('../cantok')])"), ], @@ -90,8 +92,13 @@ def test_sum_of_same_python_crawlers_for_current_directory(): def test_crawl_two_folders(crawl_directory_path: Union[str, Path], second_crawl_directory_path: Union[str, Path]): - list(PythonCrawler(crawl_directory_path, second_crawl_directory_path)) == list(PythonCrawler(crawl_directory_path)) + list(PythonCrawler(second_crawl_directory_path)) + assert list(PythonCrawler(crawl_directory_path, second_crawl_directory_path)) == list(PythonCrawler(crawl_directory_path)) + list(PythonCrawler(second_crawl_directory_path)) def test_crawl_without_path(): assert list(PythonCrawler()) == [] + + +def test_check_filter_signature(): + with pytest.raises(SignatureMismatchError, match=match('The signature of the callable object does not match the expected one.')): + PythonCrawler(filter=lambda: None)