diff --git a/pyproject.toml b/pyproject.toml index 682a3f3a..508e9c2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,6 +137,7 @@ source = ["src"] omit = [ "src/npe2/manifest/contributions/_keybindings.py", "src/npe2/manifest/menus.py", + "src/npe2/__main__.py", "src/npe2/manifest/package_metadata.py", # due to all of the isolated sub-environments and sub-processes, # it's really hard to get coverage on the setuptools plugin. diff --git a/src/npe2/__main__.py b/src/npe2/__main__.py new file mode 100644 index 00000000..7aaff027 --- /dev/null +++ b/src/npe2/__main__.py @@ -0,0 +1,4 @@ +from npe2.cli import main + +if __name__ == "__main__": + main() diff --git a/src/npe2/_inspection/_from_npe1.py b/src/npe2/_inspection/_from_npe1.py index 48622824..8a83a9d0 100644 --- a/src/npe2/_inspection/_from_npe1.py +++ b/src/npe2/_inspection/_from_npe1.py @@ -517,7 +517,13 @@ def get_top_module_path(package_name, top_module: Optional[str] = None) -> Path: top_module = top_mods[0] path = Path(dist.locate_file(top_module)) - assert path.is_dir() + if not path.is_dir() and dist.files: + for f_path in dist.files: + if "__editable__" in f_path.name: + path = Path(f_path.read_text().strip()) / top_module + break + + assert path.is_dir(), f"Could not find top level module {top_module} using {path}" return path diff --git a/tests/conftest.py b/tests/conftest.py index ed669df2..4da1e607 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -172,6 +172,53 @@ def _from_name(name): (npe1_repo / "setup.py").unlink() +@pytest.fixture +def mock_npe1_pm_with_plugin_editable(npe1_repo, npe1_plugin_module, tmp_path): + """Mocks a fully installed local repository""" + from npe2._inspection._from_npe1 import plugin_packages + + dist_path = tmp_path / "npe1-plugin-0.0.1.dist-info" + shutil.copytree(npe1_repo / "npe1-plugin-0.0.1.dist-info", dist_path) + + record_path = dist_path / "RECORD" + + record_content = record_path.read_text().splitlines() + record_content.pop(-1) + record_content.append("__editable__.npe1-plugin-0.0.1.pth") + + with record_path.open("w") as f: + f.write("\n".join(record_content)) + + with open(tmp_path / "__editable__.npe1-plugin-0.0.1.pth", "w") as f: + f.write(str(npe1_repo)) + + mock_dist = metadata.PathDistribution(dist_path) + + def _dists(): + return [mock_dist] + + def _from_name(name): + if name == "npe1-plugin": + return mock_dist + raise metadata.PackageNotFoundError(name) + + setup_cfg = npe1_repo / "setup.cfg" + new_manifest = npe1_repo / "npe1_module" / "napari.yaml" + with patch.object(metadata, "distributions", new=_dists): + with patch.object(metadata.Distribution, "from_name", new=_from_name): + cfg = setup_cfg.read_text() + plugin_packages.cache_clear() + try: + yield mock_npe1_pm + finally: + plugin_packages.cache_clear() + setup_cfg.write_text(cfg) + if new_manifest.exists(): + new_manifest.unlink() + if (npe1_repo / "setup.py").exists(): + (npe1_repo / "setup.py").unlink() + + @pytest.fixture(autouse=True) def mock_cache(tmp_path, monkeypatch): with monkeypatch.context() as m: diff --git a/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD b/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD new file mode 100644 index 00000000..f0f82af7 --- /dev/null +++ b/tests/npe1-plugin/npe1-plugin-0.0.1.dist-info/RECORD @@ -0,0 +1,5 @@ +npe1-plugin-0.0.1.dist-info/RECORD,, +npe1-plugin-0.0.1.dist-info/METADATA,, +npe1-plugin-0.0.1.dist-info/top_level.txt,, +npe1-plugin-0.0.1.dist-info/entry_points.txt,, +../npe1_module/__init__.py,, diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 499c1f6f..4f179dc5 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -68,6 +68,30 @@ def test_conversion_from_package(npe1_repo, mock_npe1_pm_with_plugin): assert "Is this package already converted?" in str(e.value) +@pytest.mark.filterwarnings("ignore:Failed to convert napari_provide_sample_data") +@pytest.mark.filterwarnings("ignore:Error converting function") +@pytest.mark.filterwarnings("ignore:Error converting dock widget") +def test_conversion_from_package_editable(npe1_repo, mock_npe1_pm_with_plugin_editable): + setup_cfg = npe1_repo / "setup.cfg" + before = setup_cfg.read_text() + convert_repository(npe1_repo, dry_run=True) + assert setup_cfg.read_text() == before + assert not (npe1_repo / "npe1_module" / "napari.yaml").exists() + convert_repository(npe1_repo, dry_run=False) + new_setup = setup_cfg.read_text() + assert new_setup != before + assert ( + "[options.entry_points]\n" + "napari.manifest = \n npe1-plugin = npe1_module:napari.yaml" + ) in new_setup + assert "[options.package_data]\nnpe1_module = napari.yaml" in new_setup + assert (npe1_repo / "npe1_module" / "napari.yaml").is_file() + + with pytest.raises(ValueError) as e: + convert_repository(npe1_repo) + assert "Is this package already converted?" in str(e.value) + + def _assert_expected_errors(record: pytest.WarningsRecorder): assert len(record) == 4 msg = str(record[0].message)