Skip to content

Commit 30eb826

Browse files
committed
feat: recursively look for lock files (elementsinteractive#332)
1 parent 809b7f1 commit 30eb826

4 files changed

Lines changed: 36 additions & 19 deletions

File tree

src/twyn/cli.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def entry_point() -> None:
4747
@click.option(
4848
"--dependency-file",
4949
type=str,
50+
multiple=True,
5051
help=(
5152
"Dependency file to analyze. By default, twyn will search in the current directory "
5253
"for supported files, but this option will override that behavior."
@@ -112,7 +113,7 @@ def entry_point() -> None:
112113
)
113114
def run( # noqa: C901
114115
config: str,
115-
dependency_file: Optional[str],
116+
dependency_file: tuple[str],
116117
dependency: tuple[str],
117118
selector_method: str,
118119
v: bool,
@@ -133,15 +134,16 @@ def run( # noqa: C901
133134
"Only one of --dependency or --dependency-file can be set at a time.", ctx=click.get_current_context()
134135
)
135136

136-
if dependency_file and not any(dependency_file.endswith(key) for key in DEPENDENCY_FILE_MAPPING):
137-
raise click.UsageError("Dependency file name not supported.", ctx=click.get_current_context())
137+
for dep_file in dependency_file:
138+
if dep_file and not any(dep_file.endswith(key) for key in DEPENDENCY_FILE_MAPPING):
139+
raise click.UsageError(f"Dependency file name {dep_file} not supported.", ctx=click.get_current_context())
138140

139141
try:
140142
possible_typos = check_dependencies(
141143
selector_method=selector_method,
142144
dependencies=set(dependency) or None,
143145
config_file=config,
144-
dependency_file=dependency_file,
146+
dependency_file=set(dependency_file) or None,
145147
use_cache=not no_cache if no_cache is not None else no_cache,
146148
show_progress_bar=False if json else not no_track,
147149
load_config_from_file=True,

src/twyn/config/config_handler.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
class TwynConfiguration:
3333
"""Fully resolved configuration for Twyn."""
3434

35-
dependency_file: Optional[str]
35+
dependency_file: Optional[list[str]]
3636
selector_method: str
3737
allowlist: set[str]
3838
source: Optional[str]
@@ -45,7 +45,7 @@ class TwynConfiguration:
4545
class ReadTwynConfiguration:
4646
"""Configuration for twyn as set by the user. It may have None values."""
4747

48-
dependency_file: Optional[str] = None
48+
dependency_file: Optional[list[str]] = None
4949
selector_method: Optional[str] = None
5050
allowlist: set[str] = field(default_factory=set)
5151
source: Optional[str] = None
@@ -63,7 +63,7 @@ def __init__(self, file_handler: Optional[FileHandler] = None) -> None:
6363
def resolve_config(
6464
self,
6565
selector_method: Optional[str] = None,
66-
dependency_file: Optional[str] = None,
66+
dependency_files: Optional[list[str]] = None,
6767
use_cache: Optional[bool] = None,
6868
package_ecosystem: Optional[PackageEcosystems] = None,
6969
recursive: Optional[bool] = None,
@@ -107,7 +107,7 @@ def resolve_config(
107107
final_recursive = DEFAULT_RECURSIVE
108108

109109
return TwynConfiguration(
110-
dependency_file=dependency_file or read_config.dependency_file,
110+
dependency_file=dependency_files or read_config.dependency_file,
111111
selector_method=final_selector_method,
112112
allowlist=read_config.allowlist,
113113
source=read_config.source,
@@ -147,6 +147,8 @@ def _get_read_config(self, toml: TOMLDocument) -> ReadTwynConfiguration:
147147
allowlist=set(twyn_config_data.get("allowlist", set())),
148148
source=twyn_config_data.get("source"),
149149
use_cache=twyn_config_data.get("use_cache"),
150+
package_ecosystem=twyn_config_data.get("package_ecosystem"),
151+
recursive=twyn_config_data.get("recursive"),
150152
)
151153

152154
def _write_config(self, toml: TOMLDocument, config: ReadTwynConfiguration) -> None:

src/twyn/main.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
def check_dependencies(
3838
selector_method: Union[SelectorMethod, None] = None,
3939
config_file: Optional[str] = None,
40-
dependency_file: Optional[str] = None,
40+
dependency_file: Optional[list[str]] = None,
4141
dependencies: Optional[set[str]] = None,
4242
use_cache: Optional[bool] = True,
4343
show_progress_bar: bool = False,
@@ -68,7 +68,7 @@ def check_dependencies(
6868
load_config_from_file=load_config_from_file,
6969
config_file=config_file,
7070
selector_method=selector_method,
71-
dependency_file=dependency_file,
71+
dependency_files=dependency_file,
7272
use_cache=use_cache,
7373
package_ecosystem=package_ecosystem,
7474
recursive=recursive,
@@ -104,7 +104,7 @@ def check_dependencies(
104104
maybe_cache_handler=maybe_cache_handler,
105105
allowlist=config.allowlist,
106106
show_progress_bar=show_progress_bar,
107-
dependency_file=config.dependency_file,
107+
dependency_files=config.dependency_file,
108108
)
109109

110110

@@ -153,7 +153,7 @@ def _analyze_packages_from_source(
153153
allowlist: set[str],
154154
selector_method: SelectorMethod,
155155
show_progress_bar: bool,
156-
dependency_file: Optional[str],
156+
dependency_files: Optional[list[str]],
157157
source: Optional[str],
158158
maybe_cache_handler: Optional[CacheHandler],
159159
) -> TyposquatCheckResults:
@@ -180,11 +180,24 @@ def _analyze_packages_from_source(
180180
top_package_reference, trusted_packages, parser.parse(), allowlist, show_progress_bar, parser.file_path
181181
)
182182

183-
if analyzed_dependencies:
184-
results.append(
185-
TyposquatCheckResultFromSource(source=str(parser.file_path), errors=analyzed_dependencies)
183+
packages_from_source = top_package_reference.get_packages()
184+
trusted_packages = TrustedPackages(
185+
names=packages_from_source,
186+
algorithm=EditDistance(),
187+
selector=selector_method,
188+
threshold_class=SimilarityThreshold,
189+
)
190+
results: list[TyposquatCheckResultFromSource] = []
191+
for parser in parsers:
192+
analyzed_dependencies = _analyze_dependencies(
193+
top_package_reference, trusted_packages, parser.parse(), allowlist, show_progress_bar
186194
)
187-
typos_by_file.results += results
195+
196+
if analyzed_dependencies:
197+
results.append(
198+
TyposquatCheckResultFromSource(source=str(parser.file_path), errors=analyzed_dependencies)
199+
)
200+
typos_by_file.results += results
188201

189202
return typos_by_file
190203

@@ -274,7 +287,7 @@ def _get_config(
274287
load_config_from_file: bool,
275288
config_file: Optional[str],
276289
selector_method: Union[SelectorMethod, None],
277-
dependency_file: Optional[str],
290+
dependency_files: Optional[list[str]],
278291
use_cache: Optional[bool],
279292
package_ecosystem: Optional[PackageEcosystems],
280293
recursive: Optional[bool],
@@ -286,7 +299,7 @@ def _get_config(
286299
config_file_handler = None
287300
return ConfigHandler(config_file_handler).resolve_config(
288301
selector_method=selector_method,
289-
dependency_file=dependency_file,
302+
dependency_files=dependency_files,
290303
use_cache=use_cache,
291304
package_ecosystem=package_ecosystem,
292305
recursive=recursive,

tests/main/test_main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def test_options_priorities_assignation(
114114
with patch.object(handler, "_read_toml", return_value=parse(dumps({"tool": {"twyn": file_config}}))):
115115
resolved = handler.resolve_config(
116116
selector_method=cli_config.get("selector_method"),
117-
dependency_file=cli_config.get("dependency_file"),
117+
dependency_files=cli_config.get("dependency_file"),
118118
use_cache=cli_config.get("use_cache"),
119119
)
120120

0 commit comments

Comments
 (0)