コード例 #1
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_module_install_version(
    tmpdir: py.path.local,
    snippetcompiler_clean,
    modules_v2_dir: str,
    dev: bool,
) -> None:
    """
    Make sure that the module install results in a module instance with the appropriate version information.
    :param dev: whether to add a dev tag to the version
    """
    module_name: str = "minimalv2module"
    module_path: str = os.path.join(str(tmpdir), module_name)
    module_version: version.Version = version.Version(
        "1.2.3") if not dev else version.Version("1.2.3.dev0")

    # set up simple project and activate the snippetcompiler venv
    project: module.Project = snippetcompiler_clean.setup_for_snippet("")

    # install module
    module_from_template(
        os.path.join(modules_v2_dir, module_name),
        module_path,
        new_version=module_version,
    )
    os.chdir(project.path)
    ModuleTool().install(editable=True, path=module_path)

    # check version
    mod: module.Module = ModuleTool().get_module(module_name)
    assert mod.version == module_version
コード例 #2
0
ファイル: test_add.py プロジェクト: inmanta/inmanta-core
def test_module_add_v2_module_to_v2_module(tmpdir: py.path.local, monkeypatch, modules_v2_dir: str) -> None:
    """
    Add a V2 module to a V2 module using the `inmanta module add` command.
    """
    # Create module to execute `inmanta module add` command on
    module_dir = os.path.join(tmpdir, "test")
    module_from_template(source_dir=os.path.join(modules_v2_dir, "elaboratev2module"), dest_dir=module_dir)
    monkeypatch.chdir(module_dir)

    def _assert_module_requirements(expected_requirements: List[str]) -> None:
        module_v2 = ModuleV2(project=None, path=module_dir)
        assert sorted(module_v2.metadata.install_requires) == sorted(expected_requirements)
        assert not os.path.exists(os.path.join(module_dir, "requirements.txt"))

    _assert_module_requirements(expected_requirements=[])
    installed_packages = process_env.get_installed_packages()

    name_dependent_module = "a_module"
    ModuleTool().add(module_req="a_module", v2=True, override=False)
    _assert_module_requirements(expected_requirements=[ModuleV2Source.get_package_name_for(name_dependent_module)])

    with pytest.raises(CLIException, match="A dependency on the given module was already defined"):
        ModuleTool().add(module_req=f"{name_dependent_module}==1.1.1", v2=True, override=False)
    _assert_module_requirements(expected_requirements=[ModuleV2Source.get_package_name_for(name_dependent_module)])

    ModuleTool().add(module_req=f"{name_dependent_module}==1.1.1", v2=True, override=True)
    _assert_module_requirements(expected_requirements=[f"{ModuleV2Source.get_package_name_for(name_dependent_module)}==1.1.1"])

    # Ensure no new packages were installed as a side-effect of `inmanta modules add`
    assert process_env.get_installed_packages() == installed_packages
コード例 #3
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_3322_module_install_deep_data_files(tmpdir: py.path.local,
                                             snippetcompiler_clean,
                                             modules_v2_dir: str) -> None:
    """
    Verify that module installation includes data files regardless of depth in the directory structure.
    """
    # set up module directory
    module_name: str = "minimalv2module"
    module_path: str = str(tmpdir.join(module_name))
    module_from_template(
        os.path.join(modules_v2_dir, module_name),
        module_path,
    )
    deep_model_file_rel: str = os.path.join(
        "model",
        *(str(i) for i in range(10)),
        "mymod.cf",
    )
    os.makedirs(os.path.join(module_path,
                             os.path.dirname(deep_model_file_rel)))
    open(os.path.join(module_path, deep_model_file_rel), "w").close()

    # set up simple project and activate snippetcompiler venv
    snippetcompiler_clean.setup_for_snippet("")

    # install module: non-editable mode
    ModuleTool().install(editable=False, path=module_path)

    assert os.path.exists(
        os.path.join(
            env.process_env.site_packages_dir,
            const.PLUGINS_PACKAGE,
            module_name,
            deep_model_file_rel,
        ))
コード例 #4
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_project_install_preinstalled(
    local_module_package_index: str,
    snippetcompiler_clean,
    tmpdir: py.path.local,
    modules_v2_dir: str,
    editable: bool,
) -> None:
    """
    Verify that `inmanta project install` does not override preinstalled modules.
    """
    module_name: str = "minimalv2module"
    fq_mod_name: str = "inmanta_plugins.minimalv2module"

    assert env.process_env.get_module_file(fq_mod_name) is None

    # activate snippetcompiler venv
    snippetcompiler_clean.setup_for_snippet("")

    # preinstall older version of module
    module_path: str = os.path.join(str(tmpdir), module_name)
    new_version = version.Version("1.2.3.dev0")
    module_from_template(os.path.join(modules_v2_dir, module_name),
                         module_path,
                         new_version=new_version,
                         install=True,
                         editable=editable)

    def assert_module_install() -> None:
        module_info: Optional[Tuple[Optional[str],
                                    Loader]] = env.process_env.get_module_file(
                                        fq_mod_name)
        env_module_file, module_loader = module_info
        assert not isinstance(module_loader, loader.PluginModuleLoader)
        assert env_module_file is not None
        install_path: str = module_path if editable else env.process_env.site_packages_dir
        assert env_module_file == os.path.join(install_path,
                                               *fq_mod_name.split("."),
                                               "__init__.py")
        assert (env.process_env.get_installed_packages(
            only_editable=editable).get(
                f"{module.ModuleV2.PKG_NAME_PREFIX}{module_name}",
                None) == new_version)

    assert_module_install()

    # set up project and modules
    project: module.Project = snippetcompiler_clean.setup_for_snippet(
        f"import {module_name}",
        autostd=False,
        python_package_sources=[local_module_package_index])

    os.chdir(project.path)
    # autostd=True reports std as an import for any module, thus requiring it to be v2 because v2 can not depend on v1
    module.Project.get().autostd = False
    ProjectTool().execute("install", [])
    assert_module_install()
コード例 #5
0
ファイル: test_modules.py プロジェクト: inmanta/inmanta-core
 def test_non_matching_name_v2_module(self) -> None:
     """
     Make sure the warning regarding directory name does not trigger for v2 modules, as it is not relevant there.
     """
     template_dir: str = os.path.join(
         os.path.dirname(os.path.abspath(__file__)), "data", "modules_v2",
         "minimalv2module")
     with tempfile.TemporaryDirectory() as tmpdir:
         mod_dir: str = os.path.join(tmpdir, "not-the-module-name")
         module_from_template(template_dir, mod_dir)
         module.ModuleV2(project=module.DummyProject(), path=mod_dir)
         self.handler.flush()
         assert self.stream.getvalue().strip() == ""
コード例 #6
0
ファイル: test_add.py プロジェクト: inmanta/inmanta-core
def test_module_add_v2_module_to_project(
    tmpdir: py.path.local, snippetcompiler_clean, local_module_package_index: str, modules_v2_dir: str
) -> None:
    """
    Add a V2 module to an inmanta project using the `inmanta module add` command.
    """
    # Ensure that versions 1.1.1 and 1.2.0 exist in the pip index.
    # Version 1.2.3 is present in the pip index exported by local_module_package_index.
    pip_index = PipIndex(artifact_dir=os.path.join(str(tmpdir), "pip-index"))
    for version in ["1.1.1", "1.2.0"]:
        module_from_template(
            source_dir=os.path.join(modules_v2_dir, "elaboratev2module"),
            dest_dir=os.path.join(tmpdir, f"elaboratev2module-v{version}"),
            new_version=Version(version),
            publish_index=pip_index,
        )

    # Create project
    project: Project = snippetcompiler_clean.setup_for_snippet(
        snippet="", autostd=False, python_package_sources=[local_module_package_index, pip_index.url]
    )

    requirements_txt_file = os.path.join(project.path, "requirements.txt")

    def _assert_project_state(pkg_name: str, expected_version: Version, project_requires_constraint: str) -> None:
        installed_packages = process_env.get_installed_packages()
        assert pkg_name in installed_packages
        assert installed_packages[pkg_name] == expected_version
        with open(project.get_metadata_file_path(), "r", encoding="utf-8") as fd:
            project_metadata = ProjectMetadata.parse(fd)
            assert not project_metadata.requires
        with open(requirements_txt_file, "r", encoding="utf-8") as fd:
            assert fd.read().strip() == ModuleV2Source.get_package_name_for(project_requires_constraint)

    module_name = "elaboratev2module"
    pkg_name = "inmanta-module-elaboratev2module"
    assert pkg_name not in process_env.get_installed_packages().keys()

    version_constraint = f"{module_name}==1.2.3"
    ModuleTool().add(module_req=version_constraint, v2=True, override=False)
    _assert_project_state(pkg_name=pkg_name, expected_version=Version("1.2.3"), project_requires_constraint=version_constraint)

    new_version_constraint = f"{module_name}==1.2.0"
    with pytest.raises(CLIException, match="A dependency on the given module was already defined"):
        ModuleTool().add(module_req=new_version_constraint, v2=True, override=False)
    _assert_project_state(pkg_name=pkg_name, expected_version=Version("1.2.3"), project_requires_constraint=version_constraint)

    ModuleTool().add(module_req=new_version_constraint, v2=True, override=True)
    _assert_project_state(
        pkg_name=pkg_name, expected_version=Version("1.2.0"), project_requires_constraint=new_version_constraint
    )
コード例 #7
0
ファイル: test_add.py プロジェクト: inmanta/inmanta-core
def test_module_add_v2_wrong_name_error(tmpdir: py.path.local, monkeypatch, modules_v2_dir: str) -> None:
    """
    Test the error messages of v2 modules when adding with a wrong name. (issue #3556)
    """
    # Create module to execute `inmanta module add` command on
    module_dir = os.path.join(tmpdir, "test")
    module_from_template(source_dir=os.path.join(modules_v2_dir, "elaboratev2module"), dest_dir=module_dir)
    monkeypatch.chdir(module_dir)

    with pytest.raises(ValueError, match="Invalid Inmanta module requirement: Inmanta module names use '_', not '-'."):
        ModuleTool().add(module_req="a-module", v2=True, override=False)

    with pytest.raises(
        ValueError, match="Invalid Inmanta module requirement: Use the Inmanta module name instead of the Python package name"
    ):
        ModuleTool().add(module_req="inmanta-module-a-module", v2=True, override=False)
コード例 #8
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_3322_module_install_preinstall_cleanup(tmpdir: py.path.local,
                                                snippetcompiler_clean,
                                                modules_v2_dir: str) -> None:
    """
    Verify that installing a module from source cleans up any old installation's data files.
    """
    # set up module directory
    module_name: str = "minimalv2module"
    module_path: str = str(tmpdir.join(module_name))
    module_from_template(
        os.path.join(modules_v2_dir, module_name),
        module_path,
        new_version=version.Version("1.0.0"),
    )
    model_file_rel: str = os.path.join("model", "mymod.cf")
    model_file_source_path: str = os.path.join(module_path, model_file_rel)
    assert not os.path.exists(model_file_source_path)
    open(model_file_source_path, "w").close()

    def model_file_installed() -> bool:
        return os.path.exists(
            os.path.join(
                env.process_env.site_packages_dir,
                const.PLUGINS_PACKAGE,
                module_name,
                model_file_rel,
            ))

    # set up simple project and activate snippetcompiler venv
    snippetcompiler_clean.setup_for_snippet("")

    # install module: non-editable mode
    ModuleTool().install(editable=False, path=module_path)
    assert model_file_installed()

    # remove model file and reinstall
    os.remove(model_file_source_path)
    module_from_template(
        module_path,
        new_version=version.Version("2.0.0"),
        in_place=True,
    )
    ModuleTool().install(editable=False, path=module_path)
    assert not model_file_installed()
コード例 #9
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_module_install_reinstall(
    tmpdir: py.path.local,
    snippetcompiler_clean,
    modules_v2_dir,
) -> None:
    """
    Verify that reinstalling a module from source installs any changes to model and Python files if the version is bumped.
    """
    module_name: str = "minimalv2module"
    module_path: str = str(tmpdir.join(module_name))
    module_from_template(
        os.path.join(modules_v2_dir, module_name),
        dest_dir=module_path,
        new_version=version.Version("1.0.0"),
    )

    # set up simple project and activate snippetcompiler venv
    snippetcompiler_clean.setup_for_snippet("")

    def new_files_exist() -> Iterator[bool]:
        return (
            os.path.exists(
                os.path.join(env.process_env.site_packages_dir,
                             const.PLUGINS_PACKAGE, module_name, rel_path))
            for rel_path in [os.path.join("model", "newmod.cf"), "newmod.py"])

    # install module
    ModuleTool().install(editable=False, path=module_path)

    assert not any(new_files_exist())

    # make some changes to the source and install again
    model_dir: str = os.path.join(module_path, "model")
    os.makedirs(model_dir, exist_ok=True)
    open(os.path.join(model_dir, "newmod.cf"), "w").close()
    open(
        os.path.join(module_path, const.PLUGINS_PACKAGE, module_name,
                     "newmod.py"), "w").close()
    module_from_template(module_path,
                         new_version=version.Version("2.0.0"),
                         in_place=True)
    ModuleTool().install(editable=False, path=module_path)

    assert all(new_files_exist())
コード例 #10
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_project_install_with_install_mode(
        tmpdir: py.path.local, modules_v2_dir: str, snippetcompiler_clean,
        install_mode: Optional[str]) -> None:
    """
    Test whether the `inmanta module install` command takes into account the `install_mode` configured on the inmanta project.
    """
    index: PipIndex = PipIndex(
        artifact_dir=os.path.join(str(tmpdir), ".custom-index"))

    module_template_path: str = os.path.join(modules_v2_dir,
                                             "elaboratev2module")
    module_name: str = "mod"
    package_name: str = module.ModuleV2Source.get_package_name_for(module_name)
    for module_version in ["1.0.0", "1.0.1.dev0"]:
        module_from_template(
            module_template_path,
            os.path.join(str(tmpdir), f"mod-{module_version}"),
            new_name=module_name,
            new_version=version.Version(module_version),
            publish_index=index,
        )

    # set up project
    snippetcompiler_clean.setup_for_snippet(
        f"import {module_name}",
        autostd=False,
        python_package_sources=[index.url],
        python_requires=[Requirement.parse(package_name)],
        install_mode=install_mode,
    )

    os.chdir(module.Project.get().path)
    ProjectTool().execute("install", [])

    if install_mode is None or install_mode == InstallMode.release:
        expected_version = version.Version("1.0.0")
    else:
        expected_version = version.Version("1.0.1.dev0")
    installed_packages: Dict[
        str, version.Version] = env.process_env.get_installed_packages()
    assert package_name in installed_packages
    assert installed_packages[package_name] == expected_version
コード例 #11
0
ファイル: test_add.py プロジェクト: inmanta/inmanta-core
def test_module_add_preinstalled(tmpdir: py.path.local, modules_v2_dir: str, snippetcompiler_clean, caplog) -> None:
    """
    Verify that `inmanta module add` respects preinstalled modules when they're compatible and logs a warning when they're
    not.
    """
    module_name: str = "mymodule"
    pip_index = PipIndex(artifact_dir=str(tmpdir.join("pip-index")))
    snippetcompiler_clean.setup_for_snippet(snippet="", autostd=False, python_package_sources=[pip_index.url])

    # preinstall 1.0.0, don't publish to index
    module_from_template(
        os.path.join(modules_v2_dir, "minimalv2module"),
        str(tmpdir.join(module_name, "1.0.0")),
        new_name=module_name,
        new_version=Version("1.0.0"),
        install=True,
    )
    # publish 1.1.0 and 2.0.0 to index
    module_from_template(
        os.path.join(modules_v2_dir, "minimalv2module"),
        str(tmpdir.join(module_name, "1.1.0")),
        new_name=module_name,
        new_version=Version("1.1.0"),
        install=False,
        publish_index=pip_index,
    )
    module_from_template(
        os.path.join(modules_v2_dir, "minimalv2module"),
        str(tmpdir.join(module_name, "2.0.0")),
        new_name=module_name,
        new_version=Version("2.0.0"),
        install=False,
        publish_index=pip_index,
    )

    # verify that compatible constraint does not reinstall or update
    ModuleTool().add(module_req=f"{module_name}~=1.0", v2=True, override=True)
    caplog.clear()
    with caplog.at_level(logging.WARNING):
        assert ModuleTool().get_module(module_name).version == Version("1.0.0")
        assert "does not match constraint" not in caplog.text

    # verify that incompatible constraint does reinstall and logs a warning
    with caplog.at_level(logging.WARNING):
        ModuleTool().add(module_req=f"{module_name}~=2.0", v2=True, override=True)
        assert (
            f"Currently installed {module_name}-1.0.0 does not match constraint ~=2.0: updating to compatible version."
            in caplog.messages
        )
    assert ModuleTool().get_module(module_name).version == Version("2.0.0")
コード例 #12
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_moduletool_list(capsys, tmpdir: py.path.local,
                         local_module_package_index: str,
                         snippetcompiler_clean, modules_v2_dir: str) -> None:
    """
    Verify that `inmanta module list` correctly lists all installed modules, both v1 and v2.
    """
    # set up venv
    snippetcompiler_clean.setup_for_snippet("", autostd=False)

    module_template_path: str = os.path.join(modules_v2_dir, "minimalv2module")
    module_from_template(
        module_template_path,
        str(tmpdir.join("custom_mod_one")),
        new_name="custom_mod_one",
        new_version=version.Version("1.0.0"),
        install=True,
        editable=False,
    )
    module_from_template(
        module_template_path,
        str(tmpdir.join("custom_mod_two")),
        new_name="custom_mod_two",
        new_version=version.Version("1.0.0"),
        new_content_init_cf="import custom_mod_one",
        new_requirements=[
            module.InmantaModuleRequirement.parse("custom_mod_one~=1.0")
        ],
        install=True,
        editable=True,
    )

    # set up project with a v1 and a v2 module
    project: module.Project = snippetcompiler_clean.setup_for_snippet(
        """
import std
import custom_mod_one
import custom_mod_two
        """.strip(),
        python_package_sources=[local_module_package_index],
        project_requires=[
            module.InmantaModuleRequirement.parse("std~=2.0,<2.1.11"),
            module.InmantaModuleRequirement.parse("custom_mod_one>0"),
        ],
        python_requires=[
            module.ModuleV2Source.get_python_package_requirement(
                module.InmantaModuleRequirement.parse("custom_mod_one<999")),
        ],
        install_mode=InstallMode.release,
        autostd=False,
    )

    capsys.readouterr()
    ModuleTool().list()
    out, err = capsys.readouterr()
    assert (out.strip() == """
+----------------+------+----------+----------------+----------------+---------+
|      Name      | Type | Editable |   Installed    |  Expected in   | Matches |
|                |      |          |    version     |    project     |         |
+================+======+==========+================+================+=========+
| custom_mod_one | v2   | no       | 1.0.0          | >0,<999,~=1.0  | yes     |
| custom_mod_two | v2   | yes      | 1.0.0          | *              | yes     |
| std            | v1   | yes      | 2.1.10         | 2.1.10         | yes     |
+----------------+------+----------+----------------+----------------+---------+
    """.strip())

    # install incompatible version for custom_mod_one
    module_from_template(
        str(tmpdir.join("custom_mod_one")),
        new_version=version.Version("2.0.0"),
        install=True,
        editable=False,
        in_place=True,
    )
    project.invalidate_state("custom_mod_one")
    capsys.readouterr()
    ModuleTool().list()
    out, err = capsys.readouterr()
    assert (out.strip() == """
+----------------+------+----------+----------------+----------------+---------+
|      Name      | Type | Editable |   Installed    |  Expected in   | Matches |
|                |      |          |    version     |    project     |         |
+================+======+==========+================+================+=========+
| custom_mod_one | v2   | no       | 2.0.0          | >0,<999,~=1.0  | no      |
| custom_mod_two | v2   | yes      | 1.0.0          | *              | yes     |
| std            | v1   | yes      | 2.1.10         | 2.1.10         | yes     |
+----------------+------+----------+----------------+----------------+---------+
    """.strip())
コード例 #13
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_project_install_incompatible_dependencies(
    caplog,
    snippetcompiler_clean,
    tmpdir: py.path.local,
    modules_dir: str,
    modules_v2_dir: str,
) -> None:
    """
    Verify that introducing version incompatibilities in the Python environment results in the appropriate exception and
    warnings.
    """
    index: PipIndex = PipIndex(
        artifact_dir=os.path.join(str(tmpdir), ".custom-index"))

    # prepare v2 modules
    v2_template_path: str = os.path.join(modules_v2_dir, "minimalv2module")
    v2mod1: module.ModuleV2Metadata = module_from_template(
        v2_template_path,
        os.path.join(str(tmpdir), "v2mod1"),
        new_name="v2mod1",
        new_requirements=[Requirement.parse("more-itertools~=7.0")],
        publish_index=index,
    )
    v2mod2: module.ModuleV2Metadata = module_from_template(
        v2_template_path,
        os.path.join(str(tmpdir), "v2mod2"),
        new_name="v2mod2",
        new_requirements=[Requirement.parse("more-itertools~=8.0")],
        publish_index=index,
    )

    # set up project
    snippetcompiler_clean.setup_for_snippet(
        f"""
        import {module.ModuleV2.get_name_from_metadata(v2mod1)}
        import {module.ModuleV2.get_name_from_metadata(v2mod2)}
        """,
        autostd=False,
        install_project=False,
        python_package_sources=[index.url, "https://pypi.org/simple"],
        python_requires=[
            Requirement.parse(
                module.ModuleV2Source.get_package_name_for(
                    module.ModuleV2.get_name_from_metadata(metadata)))
            for metadata in [v2mod1, v2mod2]
        ],
    )

    # install project
    os.chdir(module.Project.get().path)
    with pytest.raises(
            CompilerException,
            match=
        ("Not all installed modules are compatible: requirements conflicts were found. Please resolve any conflicts before"
         " attempting another compile. Run `pip check` to check for any incompatibilities."
         ),
    ):
        ProjectTool().execute("install", [])

    assert any(
        re.match(
            "Incompatibility between constraint more-itertools~=[78].0 and installed version [78]\\..*",
            rec.message) is not None for rec in caplog.records)
コード例 #14
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_project_install_incompatible_versions(
    caplog,
    snippetcompiler_clean,
    tmpdir: py.path.local,
    modules_dir: str,
    modules_v2_dir: str,
    autostd: bool,
) -> None:
    """
    Verify that introducing module version incompatibilities results in the appropriate exception and warnings.
    Make sure this works both for autostd (no explicit import) and for standard modules.
    """
    v2_mod_name: str = "std" if autostd else "v2mod"

    # declare conflicting module parameters
    current_version: version.Version = version.Version("1.0.0")
    req_v1_on_v1: module.InmantaModuleRequirement = module.InmantaModuleRequirement.parse(
        "v1mod1>42")
    req_v1_on_v2: module.InmantaModuleRequirement = module.InmantaModuleRequirement.parse(
        f"{v2_mod_name}>10000")

    # prepare v1 modules
    v1_modules_path: str = os.path.join(str(tmpdir), "libs")
    v1mod1_path: str = os.path.join(v1_modules_path, "v1mod1")
    shutil.copytree(os.path.join(modules_dir, "minimalv1module"), v1mod1_path)
    with open(os.path.join(v1mod1_path, module.ModuleV1.MODULE_FILE),
              "r+") as fh:
        config: Dict[str, object] = yaml.safe_load(fh)
        config["name"] = "v1mod1"
        config["version"] = str(current_version)
        fh.seek(0)
        yaml.dump(config, fh)
    v1mod2_path: str = os.path.join(v1_modules_path, "v1mod2")
    shutil.copytree(os.path.join(modules_dir, "minimalv1module"), v1mod2_path)
    with open(os.path.join(v1mod2_path, module.ModuleV1.MODULE_FILE),
              "r+") as fh:
        config: Dict[str, object] = yaml.safe_load(fh)
        config["name"] = "v1mod2"
        config["requires"] = [str(req_v1_on_v2), str(req_v1_on_v1)]
        fh.seek(0)
        yaml.dump(config, fh)

    # prepare v2 module
    index: PipIndex = PipIndex(
        artifact_dir=os.path.join(str(tmpdir), ".custom-index"))
    module_from_template(
        os.path.join(modules_v2_dir, "minimalv2module"),
        os.path.join(str(tmpdir), v2_mod_name),
        new_version=current_version,
        new_name=v2_mod_name,
        publish_index=index,
    )

    # set up project
    snippetcompiler_clean.setup_for_snippet(
        """
        import v1mod2
        import v1mod1
        %s
        """ % (f"import {v2_mod_name}" if not autostd else ""),
        autostd=autostd,
        install_project=False,
        add_to_module_path=[v1_modules_path],
        python_package_sources=[index.url],
        python_requires=[
            Requirement.parse(
                module.ModuleV2Source.get_package_name_for(v2_mod_name))
        ],
    )

    # install project
    os.chdir(module.Project.get().path)
    with pytest.raises(
            CompilerException,
            match=
            "Not all module dependencies have been met. Run `inmanta modules update` to resolve this."
    ):
        ProjectTool().execute("install", [])

    log_messages: Set[str] = {rec.message for rec in caplog.records}
    expected: Set[str] = {
        f"requirement {req_v1_on_v1} on module v1mod1 not fulfilled, now at version {current_version}",
        f"requirement {req_v1_on_v2} on module {v2_mod_name} not fulfilled, now at version {current_version}",
    }
    assert expected.issubset(log_messages)
コード例 #15
0
ファイル: test_basics.py プロジェクト: inmanta/inmanta-core
def test_modules_v2_compile(tmpdir: str, snippetcompiler_clean,
                            modules_dir: str, modules_v2_dir: str) -> None:
    # activate compiler venv and install std module (#3297)
    snippetcompiler_clean.setup_for_snippet("", install_project=True)

    v1_template_path: str = os.path.join(modules_dir, "minimalv1module")
    v2_template_path: str = os.path.join(modules_v2_dir, "minimalv2module")
    libs_dir: str = os.path.join(str(tmpdir), "libs")

    # main module, used as proxy to load test module's model and plugins
    main_module: str = "main"
    # module under test
    test_module: str = "test_module"

    def test_module_plugin_contents(value: int) -> str:
        """
        Returns the contents of the plugin file for the test module.
        """
        return f"""
from inmanta.plugins import plugin


VALUE: str = {value}

@plugin
def get_value() -> "int":
    return VALUE
        """.strip()

    # The model for the test module, regardless of value
    test_module_model: str = f"value = {test_module}::get_value()"

    # install main module (make it v1 so there are no restrictions on what it can depend on)
    v1_module_from_template(
        v1_template_path,
        os.path.join(libs_dir, f"{main_module}"),
        new_name=main_module,
        new_content_init_cf=f"""
import {test_module}

# test variable import
test_module_value = {test_module}::value
# test plugin call
test_module_value = {test_module}::get_value()
# test intermodule Python imports
test_module_value = {main_module}::get_test_module_value()
        """.strip(),
        new_content_init_py=f"""
from inmanta.plugins import plugin
from {const.PLUGINS_PACKAGE}.{test_module} import VALUE


@plugin
def get_test_module_value() -> "int":
    return VALUE
        """.strip(),
    )

    def verify_compile(expected_value: int) -> None:
        """
        Verify compilation by importing main module and checking its variable's value.
        """
        snippetcompiler_clean.setup_for_snippet(
            f"""
import {main_module}

# make sure imported variable has expected value (these statements will produce compiler error if it doesn't)
value = {main_module}::test_module_value
value = {expected_value}
            """.strip(),
            add_to_module_path=[libs_dir],
            # set autostd=False because no v2 std exists at the time of writing
            autostd=False,
            install_project=False,
        )
        compiler.do_compile()

    # install test module as v1 and verify compile
    v1_module_from_template(
        v1_template_path,
        os.path.join(libs_dir, test_module),
        new_name=test_module,
        new_content_init_cf=test_module_model,
        new_content_init_py=test_module_plugin_contents(1),
    )
    verify_compile(1)

    # install module as v2 (on top of v1) and verify compile
    v2_module_path: str = os.path.join(str(tmpdir), test_module)
    module_from_template(
        v2_template_path,
        v2_module_path,
        new_name=test_module,
        new_content_init_cf=test_module_model,
        new_content_init_py=test_module_plugin_contents(2),
        install=True,
        editable=True,  # ! this is editable for the next test step
    )
    verify_compile(2)

    # verify editable mode for plugins
    with open(
            os.path.join(v2_module_path, const.PLUGINS_PACKAGE, test_module,
                         "__init__.py"), "w") as fh:
        fh.write(test_module_plugin_contents(3))
    verify_compile(3)

    # verify editable mode for model
    with open(os.path.join(v2_module_path, "model", "_init.cf"), "w") as fh:
        fh.write("value = 4")
    # can't just verify_compile(4) because the plugin is still at 3 and promoting it to 4 would not test changes in the model
    with pytest.raises(DoubleSetException) as excinfo:
        verify_compile(3)
    assert excinfo.value.newvalue == 4
コード例 #16
0
def test_load_import_based_v2_module(
    local_module_package_index: str,
    snippetcompiler_clean,
    modules_dir: str,
    modules_v2_dir: str,
    tmpdir: py.path.local,
    v1: bool,
    explicit_dependency: bool,
) -> None:
    """
    A module needs to explicitly list its v2 dependencies in order to be able to load them. Import-based loading is not
    allowed.
    """
    main_module_name: str = "mymodule"
    dependency_module_name: str = "minimalv2module"
    index: PipIndex = PipIndex(os.path.join(str(tmpdir), ".my-index"))
    libs_dir: str = os.path.join(str(tmpdir), "libs")
    os.makedirs(libs_dir)

    model: str = f"import {dependency_module_name}"
    requirements: List[InmantaModuleRequirement] = ([
        InmantaModuleRequirement.parse(dependency_module_name)
    ] if explicit_dependency else [])

    if v1:
        v1_module_from_template(
            os.path.join(modules_dir, "minimalv1module"),
            os.path.join(libs_dir, main_module_name),
            new_name=main_module_name,
            new_content_init_cf=model,
            new_requirements=requirements,
        )
    else:
        module_from_template(
            os.path.join(modules_v2_dir, "minimalv2module"),
            os.path.join(str(tmpdir), main_module_name),
            new_name=main_module_name,
            new_content_init_cf=model,
            new_requirements=requirements,
            install=False,
            publish_index=index,
        )

    project: Project = snippetcompiler_clean.setup_for_snippet(
        f"import {main_module_name}",
        autostd=False,
        install_project=False,
        add_to_module_path=[libs_dir],
        python_package_sources=[local_module_package_index, index.url],
        # make sure that even listing the requirement in project.yml does not suffice
        project_requires=[
            InmantaModuleRequirement.parse(dependency_module_name)
        ],
        python_requires=[] if v1 else [
            Requirement.parse(
                ModuleV2Source.get_package_name_for(main_module_name))
        ],
    )

    if explicit_dependency:
        # assert that it doesn't raise an error with explicit requirements set
        project.load_module_recursive(install=True)
    else:
        with pytest.raises(
                ModuleLoadingException,
                match=f"Failed to load module {dependency_module_name}"):
            project.load_module_recursive(install=True)
コード例 #17
0
ファイル: test_install.py プロジェクト: inmanta/inmanta-core
def test_project_install_modules_cache_invalid(
    caplog,
    local_module_package_index: str,
    snippetcompiler_clean,
    tmpdir: py.path.local,
    modules_dir: str,
    modules_v2_dir: str,
) -> None:
    """
    Verify that introducing invalidities in the modules cache results in the appropriate exception and warnings.

    - preinstall old (v1 or v2) version of {dependency_module}
    - install project with {main_module} that depends on {dependency_module}>={v2_version}
    """
    main_module: str = "main_module"
    dependency_module: str = "minimalv1module"
    fq_mod_name: str = "inmanta_plugins.minimalv1module"
    index: PipIndex = PipIndex(
        artifact_dir=os.path.join(str(tmpdir), ".custom-index"))
    libs_dir: str = os.path.join(str(tmpdir), "libs")
    os.makedirs(libs_dir)

    assert env.process_env.get_module_file(fq_mod_name) is None

    # prepare most recent v2 module
    v2_template_path: str = os.path.join(str(tmpdir), dependency_module)
    v1: module.ModuleV1 = module.ModuleV1(project=DummyProject(autostd=False),
                                          path=os.path.join(
                                              modules_dir, dependency_module))
    v2_version: version.Version = version.Version(
        str(v1.version.major + 1) + ".0.0")
    ModuleConverter(v1).convert(output_directory=v2_template_path)
    module_from_template(v2_template_path,
                         os.path.join(str(tmpdir), dependency_module,
                                      "stable"),
                         new_version=v2_version,
                         publish_index=index)

    # prepare main module that depends on stable v2 version of first module
    module_from_template(
        v2_template_path,
        os.path.join(str(tmpdir), main_module),
        new_name=main_module,
        new_content_init_cf=f"import {dependency_module}",
        # requires stable version, not currently installed dev version
        new_requirements=[
            module.InmantaModuleRequirement.parse(
                f"{dependency_module}>={v2_version}")
        ],
        install=False,
        publish_index=index,
    )

    # preinstall module
    # set up project, including activation of venv before installing the module
    snippetcompiler_clean.setup_for_snippet("", install_project=False)
    # install older v2 module
    module_from_template(
        v2_template_path,
        os.path.join(str(tmpdir), dependency_module, "dev"),
        new_version=version.Version(f"{v2_version}.dev0"),
        install=True,
    )

    # set up project for installation
    project: module.Project = snippetcompiler_clean.setup_for_snippet(
        f"import {main_module}",
        autostd=False,
        install_project=False,
        add_to_module_path=[libs_dir],
        python_package_sources=[index.url, local_module_package_index],
        # make sure main module gets installed, pulling in newest version of dependency module
        python_requires=[
            Requirement.parse(
                module.ModuleV2Source.get_package_name_for(main_module))
        ],
    )

    # populate project.modules[dependency_module] to force the error conditions in this simplified example
    project.get_module(dependency_module, allow_v1=True)

    os.chdir(project.path)
    with pytest.raises(
            CompilerException,
            match=
        ("Not all modules were loaded correctly as a result of transitive dependencies."
         " A recompile should load them correctly."),
    ):
        ProjectTool().execute("install", [])

    message: str = (
        f"Compiler has loaded module {dependency_module}=={v2_version}.dev0 but {dependency_module}=={v2_version} has"
        " later been installed as a side effect.")

    assert message in (rec.message for rec in caplog.records)
コード例 #18
0
def test_module_update_with_v2_module(
    tmpdir: py.path.local,
    modules_v2_dir: str,
    snippetcompiler_clean,
    modules_repo: str,
    corrupt_module: bool,
    install_mode: InstallMode,
) -> None:
    """
    Assert that the `inmanta module update` command works correctly when executed on a project with a V2 module.

    :param corrupt_module: Whether the module to be updated contains a syntax error or not.

    Dependency graph:

        -> Inmanta project
            -> module1 (v2) -> module2 (v2)
            -> mod11 (v1)
    """
    template_module_name = "elaboratev2module"
    template_module_dir = os.path.join(modules_v2_dir, template_module_name)

    pip_index = PipIndex(artifact_dir=os.path.join(str(tmpdir), "pip-index"))

    def assert_version_installed(module_name: str, version: str) -> None:
        package_name = ModuleV2Source.get_package_name_for(module_name)
        installed_packages: Dict[
            str, Version] = process_env.get_installed_packages()
        assert package_name in installed_packages
        assert str(installed_packages[package_name]) == version

    for module_name, versions in {
            "module2": ["2.0.1", "2.1.0", "2.2.0", "2.2.1.dev0", "3.0.1"],
            "module1": ["1.2.4", "1.2.5"],
    }.items():
        # Module1 only has patch updates and Module2 has minor and major updates.
        # This ensure that the test covers the different types of version bumps.
        for current_version in versions:
            module_dir = os.path.join(tmpdir,
                                      f"{module_name}-v{current_version}")
            module_from_template(
                source_dir=template_module_dir,
                dest_dir=module_dir,
                new_version=Version(current_version),
                new_name=module_name,
                new_requirements=[
                    InmantaModuleRequirement(
                        Requirement.parse("module2<3.0.0"))
                ] if module_name == "module1" else None,
                install=False,
                publish_index=pip_index,
                new_content_init_cf="import module2"
                if module_name == "module1" else None,
            )
    patched_module_dir = os.path.join(tmpdir, "module1-v1.2.3")
    module_from_template(
        source_dir=template_module_dir,
        dest_dir=patched_module_dir,
        new_version=Version("1.2.3"),
        new_name="module1",
        # Add a dependency on module2, without setting an explicit version constraint. Later version of module1
        # do set a version constraint on the dependency on module2. This way it is verified whether the module update
        # command takes into account the version constraints set in a new version of a module.
        new_requirements=[
            InmantaModuleRequirement(Requirement.parse("module2"))
        ],
        install=False,
        publish_index=pip_index,
        new_content_init_cf="entity"
        if corrupt_module else None,  # Introduce syntax error in the module
    )

    module_path = os.path.join(tmpdir, "modulepath")
    os.mkdir(module_path)
    mod11_dir = clone_repo(modules_repo, "mod11", module_path, tag="3.2.1")

    snippetcompiler_clean.setup_for_snippet(
        # Don't import module2 in the inmanta project. An import for module2 is present in module1 instead.
        # This tests whether the module update command takes into account all transitive dependencies.
        snippet="""
        import module1
        import mod11
        """,
        autostd=False,
        install_v2_modules=[
            LocalPackagePath(path=os.path.join(tmpdir, "module2-v2.0.1")),
            LocalPackagePath(path=patched_module_dir),
        ],
        add_to_module_path=[module_path],
        python_package_sources=[pip_index.url],
        project_requires=[
            InmantaModuleRequirement.parse("module1<1.2.5"),
            InmantaModuleRequirement.parse("mod11<4.2.0"),
        ],
        install_mode=install_mode,
        install_project=False,
    )

    assert_version_installed(module_name="module1", version="1.2.3")
    assert_version_installed(module_name="module2", version="2.0.1")
    assert ModuleV1(project=None, path=mod11_dir).version == Version("3.2.1")
    ModuleTool().update()
    assert_version_installed(module_name="module1", version="1.2.4")
    assert_version_installed(module_name="module2",
                             version="2.2.0" if install_mode
                             == InstallMode.release else "2.2.1.dev0")
    assert ModuleV1(project=None, path=mod11_dir).version == Version("4.1.2")