mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
* Enable PEP604 Ruff rules * Fix harcoded signature in test * Hack CLVMStreamable test with note to fast follow
593 lines
21 KiB
Python
593 lines
21 KiB
Python
from __future__ import annotations
|
|
|
|
import textwrap
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import click
|
|
import pytest
|
|
import yaml
|
|
from click.testing import CliRunner
|
|
|
|
from chia.util.virtual_project_analysis import (
|
|
Annotation,
|
|
ChiaFile,
|
|
Config,
|
|
DirectoryParameters,
|
|
File,
|
|
Package,
|
|
build_dependency_graph,
|
|
build_virtual_dependency_graph,
|
|
cli,
|
|
config,
|
|
find_cycles,
|
|
parse_file_or_package,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"file_string, expected, annotated",
|
|
[
|
|
("# Package: example\n# Some other comment", "example", True),
|
|
("# Some comment\n# Some other comment", "chia-blockchain", False),
|
|
],
|
|
)
|
|
def test_parse_annotation(file_string: str, expected: str, annotated: bool) -> None:
|
|
"""
|
|
Test that parse returns an Annotation instance for a valid annotation or
|
|
raises ValueError for an invalid one.
|
|
"""
|
|
annotation = Annotation.parse(file_string)
|
|
assert isinstance(annotation, Annotation)
|
|
assert annotation.package == expected
|
|
assert annotation.is_annotated == annotated
|
|
|
|
|
|
# Temporary directory fixture to create test files
|
|
@pytest.fixture
|
|
def create_test_file(tmp_path: Path) -> Callable[[str, str], Path]:
|
|
def _create_test_file(name: str, content: str) -> Path:
|
|
file_path = tmp_path / name
|
|
file_path.write_text(content, encoding="utf-8")
|
|
return file_path
|
|
|
|
return _create_test_file
|
|
|
|
|
|
def test_parse_with_annotation(create_test_file: Callable[[str, str], Path]) -> None:
|
|
"""Test parsing a file that contains a valid annotation."""
|
|
file_content = "# Package: test_package\n# Some other comment"
|
|
test_file = create_test_file("annotated_file.txt", file_content)
|
|
|
|
parsed_file = ChiaFile.parse(test_file)
|
|
|
|
assert parsed_file.path == test_file
|
|
assert isinstance(parsed_file.annotations, Annotation)
|
|
assert parsed_file.annotations.package == "test_package"
|
|
|
|
|
|
def test_parse_without_annotation(create_test_file: Callable[[str, str], Path]) -> None:
|
|
"""Test parsing a file that does not contain any annotations."""
|
|
file_content = "# Some comment\n# Some other comment"
|
|
test_file = create_test_file("non_annotated_file.txt", file_content)
|
|
|
|
parsed_file = ChiaFile.parse(test_file)
|
|
|
|
assert parsed_file.path == test_file
|
|
assert not parsed_file.annotations.is_annotated
|
|
|
|
|
|
# This test is optional and can be adapted based on expected behavior for non-existent files
|
|
def test_parse_nonexistent_file() -> None:
|
|
"""Test attempting to parse a non-existent file."""
|
|
with pytest.raises(FileNotFoundError):
|
|
ChiaFile.parse(Path("/path/to/nonexistent/file.txt"))
|
|
|
|
|
|
# Helper function to create a non-empty Python file
|
|
def create_python_file(dir_path: Path, name: str, content: str) -> Path:
|
|
file_path = dir_path / name
|
|
file_path.write_text(content, encoding="utf-8")
|
|
return file_path
|
|
|
|
|
|
# Helper function to create an empty Python file
|
|
def create_empty_python_file(dir_path: Path, name: str) -> Path:
|
|
file_path = dir_path / name
|
|
file_path.touch()
|
|
return file_path
|
|
|
|
|
|
def test_gather_non_empty_python_files(tmp_path: Path) -> None:
|
|
# Set up directory structure
|
|
dir_path = tmp_path / "test_dir"
|
|
dir_path.mkdir()
|
|
excluded_dir = tmp_path / "excluded_dir"
|
|
excluded_dir.mkdir()
|
|
|
|
# Create test files
|
|
non_empty_file = create_python_file(dir_path, "non_empty.py", "print('Hello World')")
|
|
create_empty_python_file(dir_path, "empty.py")
|
|
create_python_file(excluded_dir, "excluded.py", "print('Hello World')")
|
|
|
|
# Initialize DirectoryParameters with excluded paths
|
|
dir_params = DirectoryParameters(dir_path=dir_path, excluded_paths=[excluded_dir])
|
|
|
|
# Perform the test
|
|
python_files = dir_params.gather_non_empty_python_files()
|
|
|
|
# Assertions
|
|
assert len(python_files) == 1 # Only one non-empty Python file should be found
|
|
assert python_files[0].path == non_empty_file # The path of the gathered file should match the non-empty file
|
|
|
|
|
|
def test_gather_with_nested_directories_and_exclusions(tmp_path: Path) -> None:
|
|
# Set up directory structure
|
|
base_dir = tmp_path / "base_dir"
|
|
base_dir.mkdir()
|
|
nested_dir = base_dir / "nested_dir"
|
|
nested_dir.mkdir()
|
|
excluded_dir = base_dir / "excluded_dir"
|
|
excluded_dir.mkdir()
|
|
|
|
# Create test files
|
|
nested_file = create_python_file(nested_dir, "nested.py", "print('Hello World')")
|
|
create_empty_python_file(nested_dir, "nested_empty.py")
|
|
create_python_file(excluded_dir, "excluded.py", "print('Hello World')")
|
|
|
|
# Initialize DirectoryParameters without excluded paths
|
|
dir_params = DirectoryParameters(dir_path=base_dir, excluded_paths=[excluded_dir])
|
|
|
|
# Perform the test
|
|
python_files = dir_params.gather_non_empty_python_files()
|
|
|
|
# Assertions
|
|
assert len(python_files) == 1 # Only the non-empty Python file in the nested directory should be found
|
|
assert python_files[0].path == nested_file # The path of the gathered file should match the nested non-empty file
|
|
|
|
|
|
def test_find_missing_annotations(tmp_path: Path) -> None:
|
|
# Set up directory structure
|
|
dir_path = tmp_path / "test_dir"
|
|
dir_path.mkdir()
|
|
|
|
# Create test files
|
|
create_python_file(dir_path, "non_empty.py", "print('Hello World')")
|
|
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["find_missing_annotations", "--directory", str(dir_path)])
|
|
assert result.output == f"{dir_path / 'non_empty.py'}\n"
|
|
|
|
# Rewrite file to have annotation
|
|
create_python_file(dir_path, "non_empty.py", "# Package: misc\n")
|
|
|
|
# Run the command again with no results
|
|
result = runner.invoke(cli, ["find_missing_annotations", "--directory", str(dir_path)])
|
|
assert result.output == ""
|
|
|
|
|
|
def test_parse_file_or_package() -> None:
|
|
assert parse_file_or_package("example.py") == File(Path("example.py"))
|
|
assert parse_file_or_package("example.py (extra info)") == File(Path("example.py"))
|
|
assert parse_file_or_package("(package_name)") == Package("package_name")
|
|
assert parse_file_or_package("package_name") == Package("package_name")
|
|
assert parse_file_or_package("package_name(") == Package("package_name(")
|
|
assert parse_file_or_package("(package_name") == Package("(package_name")
|
|
assert parse_file_or_package("package_name)") == Package("package_name)")
|
|
|
|
|
|
@pytest.fixture
|
|
def chia_package_structure(tmp_path: Path) -> Path:
|
|
base_dir = tmp_path / "chia_project"
|
|
base_dir.mkdir()
|
|
chia_dir = base_dir / "chia"
|
|
chia_dir.mkdir()
|
|
|
|
# Create some files within the chia package
|
|
create_python_file(chia_dir, "module1.py", "def func1(): pass")
|
|
create_python_file(chia_dir, "module2.py", "def func2(): pass\nfrom chia.module1 import func1")
|
|
create_python_file(chia_dir, "module3.py", "def func3(): pass\nimport chia.module2")
|
|
|
|
return chia_dir
|
|
|
|
|
|
def test_build_dependency_graph(chia_package_structure: Path) -> None:
|
|
chia_dir = chia_package_structure
|
|
dir_params = DirectoryParameters(dir_path=chia_dir)
|
|
graph = build_dependency_graph(dir_params)
|
|
assert chia_dir / "module1.py" in graph
|
|
assert chia_dir / "module2.py" in graph
|
|
assert chia_dir / "module3.py" in graph
|
|
assert chia_dir / "module1.py" in graph[chia_dir / "module2.py"]
|
|
assert chia_dir / "module2.py" in graph[chia_dir / "module3.py"]
|
|
|
|
|
|
def test_print_dependency_graph(chia_package_structure: Path) -> None:
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["print_dependency_graph", "--directory", str(chia_package_structure)])
|
|
assert "module1.py" in result.output
|
|
assert "module2.py" in result.output
|
|
assert "module3.py" in result.output
|
|
|
|
|
|
# Mock the build_dependency_graph function to control its output
|
|
def mock_build_dependency_graph(dir_params: DirectoryParameters) -> dict[Path, list[Path]]:
|
|
return {
|
|
Path("/path/to/package1/module1.py"): [
|
|
Path("/path/to/package2/module2.py"),
|
|
Path("/path/to/package3/module3.py"),
|
|
],
|
|
Path("/path/to/package2/module2.py"): [],
|
|
Path("/path/to/package3/module3.py"): [Path("/path/to/package2/module2.py")],
|
|
}
|
|
|
|
|
|
# Helper function to simulate ChiaFile.parse for testing
|
|
def mock_chia_file_parse(path: Path) -> ChiaFile:
|
|
annotations_map = {
|
|
Path("/path/to/package1/module1.py"): Annotation("Package1", True),
|
|
Path("/path/to/package2/module2.py"): Annotation("Package2", True),
|
|
Path("/path/to/package3/module3.py"): Annotation("Package3", True),
|
|
}
|
|
return ChiaFile(path=Path(path), annotations=annotations_map.get(path, Annotation("chia-blockchain", False)))
|
|
|
|
|
|
@pytest.fixture
|
|
def prepare_mocks(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr("chia.util.virtual_project_analysis.build_dependency_graph", mock_build_dependency_graph)
|
|
monkeypatch.setattr("chia.util.virtual_project_analysis.ChiaFile.parse", mock_chia_file_parse)
|
|
|
|
|
|
def test_build_virtual_dependency_graph(prepare_mocks: None) -> None:
|
|
dir_params = DirectoryParameters(dir_path=Path("/path/to/package1"))
|
|
virtual_graph = build_virtual_dependency_graph(dir_params)
|
|
|
|
assert "Package2" in virtual_graph["Package1"]
|
|
assert "Package3" in virtual_graph["Package1"]
|
|
assert virtual_graph["Package2"] == []
|
|
assert "Package2" in virtual_graph["Package3"]
|
|
|
|
|
|
def test_print_virtual_dependency_graph(tmp_path: Path) -> None:
|
|
chia_dir = tmp_path / "chia"
|
|
chia_dir.mkdir()
|
|
|
|
# Create some files within the chia package
|
|
create_python_file(chia_dir, "module1.py", "# Package: one\ndef func1(): pass")
|
|
create_python_file(chia_dir, "module2.py", "# Package: two\ndef func2(): pass\nfrom chia.module1 import func1")
|
|
create_python_file(chia_dir, "module3.py", "# Package: three\ndef func3(): pass\nimport chia.module2")
|
|
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["print_virtual_dependency_graph", "--directory", str(chia_dir)])
|
|
assert "one" in result.output
|
|
assert "two" in result.output
|
|
assert "three" in result.output
|
|
|
|
|
|
# Helper function to simulate ChiaFile.parse for testing
|
|
def mock_chia_file_parse2(path: Path) -> ChiaFile:
|
|
annotations_map = {
|
|
Path("/path/to/package1/module1.py"): Annotation("Package1", True),
|
|
Path("/path/to/package2/module2.py"): Annotation("Package2", True),
|
|
Path("/path/to/package3/module3.py"): Annotation("Package1", True),
|
|
}
|
|
return ChiaFile(path=Path(path), annotations=annotations_map.get(path, Annotation("chia-blockchain", False)))
|
|
|
|
|
|
@pytest.fixture
|
|
def prepare_mocks2(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setattr("chia.util.virtual_project_analysis.ChiaFile.parse", mock_chia_file_parse2)
|
|
|
|
|
|
def test_cycle_detection(prepare_mocks2: None) -> None:
|
|
# Example graph with a simple cycle
|
|
graph: dict[Path, list[Path]] = {
|
|
Path("/path/to/package1/module1.py"): [Path("/path/to/package2/module2.py")],
|
|
Path("/path/to/package2/module2.py"): [Path("/path/to/package3/module3.py")], # Cycle here
|
|
Path("/path/to/package3/module3.py"): [],
|
|
}
|
|
cycles = find_cycles(
|
|
graph,
|
|
build_virtual_dependency_graph(DirectoryParameters(dir_path=Path("path")), existing_graph=graph),
|
|
excluded_paths=[],
|
|
ignore_cycles_in=[],
|
|
ignore_specific_files=[],
|
|
ignore_specific_edges=[],
|
|
)
|
|
# \path\to\package1\module1.py (Package1) -> \path\to\package2\module2.py (Package2) -> (Package1)
|
|
# \path\to\package2\module2.py (Package2) -> \path\to\package3\module3.py (Package1) -> (Package2)
|
|
assert len(cycles) == 2
|
|
|
|
|
|
def test_print_cycles(tmp_path: Path) -> None:
|
|
chia_dir = tmp_path / "chia"
|
|
chia_dir.mkdir()
|
|
|
|
# Create some files within the chia package
|
|
create_python_file(chia_dir, "module1.py", "# Package: one\ndef func1(): pass\nfrom chia.module2 import func2")
|
|
create_python_file(chia_dir, "module2.py", "# Package: two\ndef func2(): pass\nfrom chia.module3 import func3")
|
|
create_python_file(chia_dir, "module3.py", "# Package: one\ndef func3(): pass\n")
|
|
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["print_cycles", "--directory", str(chia_dir)])
|
|
assert "module1.py (one) -> " in result.output
|
|
|
|
|
|
def test_check_config(tmp_path: Path) -> None:
|
|
chia_dir = tmp_path / "chia"
|
|
chia_dir.mkdir()
|
|
|
|
# Create some files within the chia package
|
|
create_python_file(
|
|
chia_dir,
|
|
"module1.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: one
|
|
def func1(): pass
|
|
from chia.module2 import func2
|
|
"""
|
|
),
|
|
)
|
|
create_python_file(
|
|
chia_dir,
|
|
"module1b.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: one
|
|
def func1b(): pass
|
|
"""
|
|
),
|
|
)
|
|
create_python_file(
|
|
chia_dir,
|
|
"module2.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: two
|
|
def func2(): pass
|
|
from chia.module3 import func3
|
|
from chia.module1b import func1b
|
|
"""
|
|
),
|
|
)
|
|
create_python_file(
|
|
chia_dir,
|
|
"module3.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: three
|
|
def func3(): pass
|
|
"""
|
|
),
|
|
)
|
|
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"check_config",
|
|
"--directory",
|
|
str(chia_dir),
|
|
"--ignore-cycles-in",
|
|
"three",
|
|
"--ignore-specific-file",
|
|
str(chia_dir / "module3.py"),
|
|
"--ignore-specific-edge",
|
|
str(chia_dir / "module2.py") + " -> " + str(chia_dir / "module3.py"),
|
|
"--ignore-specific-edge",
|
|
str(chia_dir / "module2.py") + " -> " + str(chia_dir / "module1b.py"),
|
|
],
|
|
)
|
|
assert " module three ignored but no cycles were found" in result.output
|
|
assert f" file {chia_dir / 'module3.py'!s} ignored but no cycles were found" in result.output
|
|
assert (
|
|
f"edge {str(chia_dir / 'module2.py') + ' -> ' + str(chia_dir / 'module3.py')} ignored but no cycles were found"
|
|
in result.output
|
|
)
|
|
assert (
|
|
f"edge {str(chia_dir / 'module2.py') + ' -> ' + str(chia_dir / 'module1b.py')} ignored but no cycles were found"
|
|
not in result.output
|
|
)
|
|
|
|
|
|
def test_excluded_paths_handling(prepare_mocks2: None) -> None:
|
|
# Graph where module2.py is excluded
|
|
graph = {
|
|
Path("/path/to/package1/module1.py"): [Path("/path/to/package2/module2.py")],
|
|
Path("/path/to/package2/module2.py"): [Path("/path/to/package1/module1.py")],
|
|
}
|
|
cycles = find_cycles(
|
|
graph,
|
|
build_virtual_dependency_graph(DirectoryParameters(dir_path=Path("path")), existing_graph=graph),
|
|
excluded_paths=[Path("/path/to/package2/module2.py")],
|
|
ignore_cycles_in=[],
|
|
ignore_specific_files=[],
|
|
ignore_specific_edges=[],
|
|
)
|
|
assert len(cycles) == 0 # No cycles due to exclusion
|
|
|
|
|
|
def test_ignore_cycles_in_specific_packages(prepare_mocks2: None) -> None:
|
|
graph: dict[Path, list[Path]] = {
|
|
Path("/path/to/package1/module1.py"): [Path("/path/to/package2/module2.py")],
|
|
Path("/path/to/package2/module2.py"): [Path("/path/to/package3/module3.py")],
|
|
Path("/path/to/package3/module3.py"): [],
|
|
}
|
|
# Assuming module1.py and module3.py belong to Package1, which is ignored
|
|
cycles = find_cycles(
|
|
graph,
|
|
build_virtual_dependency_graph(DirectoryParameters(dir_path=Path("path")), existing_graph=graph),
|
|
excluded_paths=[],
|
|
ignore_cycles_in=["Package1"],
|
|
ignore_specific_files=[],
|
|
ignore_specific_edges=[],
|
|
)
|
|
assert len(cycles) == 1 # Cycles in Package1 are ignored
|
|
|
|
|
|
def test_ignore_cycles_with_specific_edges(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
def _mock_chia_file_parse(path: Path) -> ChiaFile:
|
|
annotations_map = {
|
|
Path("/path/to/package1/module1a.py"): Annotation("Package1", True),
|
|
Path("/path/to/package2/module2.py"): Annotation("Package2", True),
|
|
Path("/path/to/package3/module3.py"): Annotation("Package3", True),
|
|
Path("/path/to/package1/module1b.py"): Annotation("Package1", True),
|
|
}
|
|
return ChiaFile(path=Path(path), annotations=annotations_map.get(path, Annotation("chia-blockchain", False)))
|
|
|
|
monkeypatch.setattr("chia.util.virtual_project_analysis.ChiaFile.parse", _mock_chia_file_parse)
|
|
|
|
graph = {
|
|
Path("/path/to/package1/module1a.py"): [Path("/path/to/package2/module2.py")],
|
|
Path("/path/to/package2/module2.py"): [Path("/path/to/package3/module3.py")],
|
|
Path("/path/to/package3/module3.py"): [Path("/path/to/package1/module1b.py")],
|
|
Path("/path/to/package1/module1b.py"): [],
|
|
}
|
|
virtual_graph = build_virtual_dependency_graph(DirectoryParameters(dir_path=Path("path")), existing_graph=graph)
|
|
cycles = find_cycles(
|
|
graph,
|
|
virtual_graph,
|
|
excluded_paths=[],
|
|
ignore_cycles_in=[],
|
|
ignore_specific_files=[],
|
|
ignore_specific_edges=[
|
|
(File(Path("/path/to/package3/module3.py")), File(Path("/path/to/package2/module2.py"))),
|
|
(Package("Package3"), Package("Package2")),
|
|
],
|
|
)
|
|
assert len(cycles) == 0
|
|
|
|
|
|
def test_ignore_cycles_with_specific_files(prepare_mocks2: None) -> None:
|
|
graph = {
|
|
Path("/path/to/package1/module1.py"): [Path("/path/to/package2/module2.py")],
|
|
Path("/path/to/package2/module2.py"): [Path("/path/to/package3/module3.py")], # Cycle here
|
|
}
|
|
cycles = find_cycles(
|
|
graph,
|
|
build_virtual_dependency_graph(DirectoryParameters(dir_path=Path("path")), existing_graph=graph),
|
|
excluded_paths=[],
|
|
ignore_cycles_in=[],
|
|
ignore_specific_files=[Path("/path/to/package1/module1.py"), Path("/path/to/package2/module2.py")],
|
|
ignore_specific_edges=[],
|
|
)
|
|
assert len(cycles) == 0
|
|
|
|
|
|
# Sample function to use with the decorator for testing
|
|
@click.command("blah")
|
|
@config
|
|
def sample_function(config: Config) -> None:
|
|
print(config)
|
|
|
|
|
|
# Helper function to create a temporary YAML configuration file
|
|
@pytest.fixture
|
|
def create_yaml_config(tmp_path: Path) -> Callable[[dict[str, Any]], Path]:
|
|
def _create_yaml_config(content: dict[str, Any]) -> Path:
|
|
path = tmp_path / "config.yaml"
|
|
with open(path, "w") as f:
|
|
yaml.dump(content, f)
|
|
return path
|
|
|
|
return _create_yaml_config
|
|
|
|
|
|
def test_config_with_yaml(create_yaml_config: Callable[[dict[str, Any]], Path]) -> None:
|
|
# Create a temporary YAML configuration file
|
|
yaml_config = {
|
|
"exclude_paths": ["path/to/exclude"],
|
|
"ignore": {
|
|
"packages": ["ignored.package"],
|
|
"files": ["ignored_file.py"],
|
|
"edges": ["ignored_parent -> ignored_child"],
|
|
},
|
|
}
|
|
config_path = create_yaml_config(yaml_config)
|
|
|
|
runner = CliRunner()
|
|
|
|
# Invoke the CLI with the --config option
|
|
result = runner.invoke(sample_function, ["--directory", ".", "--config", str(config_path)])
|
|
|
|
# Check if the command ran successfully
|
|
assert result.exit_code == 0
|
|
|
|
# Verify the config object created by the decorator
|
|
config = result.output
|
|
path_type = config_path.__class__.__name__
|
|
assert config == (
|
|
"Config("
|
|
"directory_parameters=DirectoryParameters("
|
|
f"dir_path={path_type}('.'), "
|
|
f"excluded_paths=[{path_type}('path/to/exclude')]"
|
|
"), "
|
|
"ignore_cycles_in=['ignored.package'], "
|
|
f"ignore_specific_files=[{path_type}('ignored_file.py')], "
|
|
f"ignore_specific_edges=[(Package(name='ignored_child', is_file=False), "
|
|
f"Package(name='ignored_parent', is_file=False))]"
|
|
")\n"
|
|
)
|
|
|
|
|
|
def test_parse_edges(tmp_path: Path) -> None:
|
|
chia_dir = tmp_path / "chia"
|
|
chia_dir.mkdir()
|
|
|
|
# Create some files within the chia package
|
|
create_python_file(
|
|
chia_dir,
|
|
"module1.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: one
|
|
def func1(): pass
|
|
from chia.module2 import func2
|
|
from chia.module3 import func3
|
|
"""
|
|
),
|
|
)
|
|
create_python_file(
|
|
chia_dir,
|
|
"module2.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: two
|
|
def func2(): pass
|
|
"""
|
|
),
|
|
)
|
|
create_python_file(
|
|
chia_dir,
|
|
"module3.py",
|
|
textwrap.dedent(
|
|
"""
|
|
# Package: three
|
|
def func3(): pass
|
|
"""
|
|
),
|
|
)
|
|
|
|
# Run the command
|
|
runner = CliRunner()
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"print_edges",
|
|
"--directory",
|
|
str(chia_dir),
|
|
"--dependent-package",
|
|
"one",
|
|
"--provider-package",
|
|
"two",
|
|
],
|
|
)
|
|
assert result.output.strip() == f"{chia_dir / 'module1.py'!s} (one) -> {chia_dir / 'module2.py'!s} (two)"
|