예제 #1
0
    def test_no_observation(self, context: Context) -> None:
        """Test adding information about no justification added."""
        state = State(
            score=0.0,
            resolved_dependencies={
                "flask": ("flask", "0.12", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
        )

        context.project.pipfile.packages.packages.pop("tensorflow")

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "flask", "0.12", "https://pypi.org/simple"
        ).and_return(["222"]).once()

        pypi = Source("https://pypi.org/simple")
        pv_pandas_locked = PackageVersion(name="flask", version="==0.12", index=pypi, develop=False)

        context.should_receive("get_package_version").with_args(
            ("flask", "0.12", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_pandas_locked).once()

        context.dependents = {
            "flask": {("flask", "0.12", "https://pypi.org/simple"): set()},
        }

        product = Product.from_final_state(context=context, state=state)
        expected = {
            "advised_manifest_changes": [],
            "advised_runtime_environment": None,
            "dependency_graph": {"edges": [], "nodes": ["flask"]},
            "justification": [
                {
                    "type": "INFO",
                    "message": "No issues spotted for this stack based on Thoth's database",
                    "link": jl("no_observations"),
                }
            ],
            "project": {
                "constraints": [],
                "requirements": {
                    "dev-packages": {},
                    "packages": {"flask": "*"},
                    "requires": {"python_version": "3.6"},
                    "source": [
                        {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                        {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                    ],
                    "thoth": {
                        "allow_prereleases": {},
                        "disable_index_adjustment": False,
                    },
                },
                "requirements_locked": {
                    "_meta": {
                        "hash": {"sha256": "2e49395dfa87159358e581bd22e656c27c0dab04894d1b137a14f85bb387ea51"},
                        "pipfile-spec": 6,
                        "requires": {"python_version": "3.6"},
                        "sources": [
                            {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                            {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                        ],
                    },
                    "default": {
                        "flask": {"hashes": ["sha256:222"], "index": "pypi-org-simple", "version": "==0.12"},
                    },
                    "develop": {},
                },
                "runtime_environment": {
                    "base_image": None,
                    "cuda_version": None,
                    "cudnn_version": None,
                    "hardware": {"cpu_family": None, "cpu_model": None, "gpu_model": None},
                    "name": None,
                    "operating_system": {"name": None, "version": None},
                    "labels": None,
                    "platform": None,
                    "mkl_version": None,
                    "openmpi_version": None,
                    "openblas_version": None,
                    "python_version": None,
                    "recommendation_type": None,
                },
            },
            "score": 0.0,
        }

        assert product.to_dict() == expected
예제 #2
0
    def test_environment_markers_direct_dependency(self, context: Context) -> None:
        """Test handling environment markers for direct dependencies."""
        state = State(
            score=0.0,
            resolved_dependencies={
                "numpy": ("numpy", "1.0.0", "https://pypi.org/simple"),
                "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
            justification=[{"type": "INFO", "message": "Foo bar", "link": "https://thoth-station.ninja"}],
        )

        pypi = Source("https://pypi.org/simple")

        # Let's assume tensorflow is our direct dependency with an environment marker set. It sets also an environment
        # marker for numpy.
        context.project.pipfile.packages.packages.pop("tensorflow", None)
        tf_package_version_direct = PackageVersion(
            name="tensorflow", version=">1.0.0", index=pypi, develop=False, markers="python_version >= '3.6'"
        )
        context.project.pipfile.add_package_version(tf_package_version_direct)
        # Just to make sure numpy is not in the direct dependency listing.
        context.project.pipfile.packages.packages.pop("numpy", None)

        pv_numpy_locked = PackageVersion(name="numpy", version="==1.0.0", index=pypi, develop=False)
        pv_tensorflow_locked = PackageVersion(name="tensorflow", version="==2.0.0", index=pypi, develop=False)

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            *pv_numpy_locked.to_tuple()
        ).and_return(["000"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            *pv_tensorflow_locked.to_tuple()
        ).and_return(["111"]).once()

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).once()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).once()

        context.dependents = {
            "numpy": {
                ("numpy", "1.0.0", "https://pypi.org/simple"): {
                    (
                        ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    )
                }
            },
            "tensorflow": {("tensorflow", "2.0.0", "https://pypi.org/simple"): set()},
        }

        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
            marker_evaluation_result=True,
        ).and_return("python_version >= '3.7'").once()

        product = Product.from_final_state(context=context, state=state)
        expected = {
            "advised_manifest_changes": [],
            "advised_runtime_environment": None,
            "justification": [
                {
                    "link": "https://thoth-station.ninja",
                    "message": "Foo bar",
                    "type": "INFO",
                }
            ],
            "dependency_graph": {"edges": [], "nodes": ["numpy", "tensorflow"]},
            "project": {
                "constraints": [],
                "requirements": {
                    "dev-packages": {},
                    "packages": {
                        "flask": "*",
                        "tensorflow": {
                            "index": "pypi-org-simple",
                            "markers": "python_version >= '3.6'",
                            "version": ">1.0.0",
                        },
                    },
                    "requires": {"python_version": "3.6"},
                    "source": [
                        {
                            "name": "pypi",
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                        },
                        {
                            "name": "pypi-org-simple",
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                        },
                    ],
                    "thoth": {
                        "allow_prereleases": {},
                        "disable_index_adjustment": False,
                    },
                },
                "requirements_locked": {
                    "_meta": {
                        "hash": {"sha256": "382cd84046246d08cf670a07c61628958dec76e05d2473bf936866fc54619f9a"},
                        "pipfile-spec": 6,
                        "requires": {"python_version": "3.6"},
                        "sources": [
                            {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                            {
                                "name": "pypi-org-simple",
                                "url": "https://pypi.org/simple",
                                "verify_ssl": True,
                            },
                        ],
                    },
                    "default": {
                        "numpy": {
                            "hashes": ["sha256:000"],
                            "index": "pypi-org-simple",
                            "markers": "python_version >= '3.7'",
                            "version": "==1.0.0",
                        },
                        "tensorflow": {
                            "hashes": ["sha256:111"],
                            "index": "pypi-org-simple",
                            "markers": "python_version >= '3.6'",
                            "version": "==2.0.0",
                        },
                    },
                    "develop": {},
                },
                "runtime_environment": {
                    "base_image": None,
                    "cuda_version": None,
                    "cudnn_version": None,
                    "hardware": {"cpu_family": None, "cpu_model": None, "gpu_model": None},
                    "name": None,
                    "operating_system": {"name": None, "version": None},
                    "openblas_version": None,
                    "labels": None,
                    "openmpi_version": None,
                    "mkl_version": None,
                    "python_version": None,
                    "recommendation_type": None,
                    "platform": None,
                },
            },
            "score": 0.0,
        }

        assert product.to_dict() == expected
예제 #3
0
    def test_environment_markers_shared(self, context: Context) -> None:
        """Test handling of environment markers when multiple dependencies share one."""
        state = State(
            score=0.0,
            resolved_dependencies={
                "pandas": ("pandas", "1.0.0", "https://pypi.org/simple"),
                "numpy": ("numpy", "1.0.0", "https://pypi.org/simple"),
                "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
            justification=[{"type": "INFO", "message": "Foo bar", "link": "https://thoth-station.ninja"}],
        )

        # Make sure tested packages are not direct dependencies. In such cases the behaviour is
        # different, see other tests.
        context.project.pipfile.packages.packages.pop("pandas", None)
        context.project.pipfile.packages.packages.pop("numpy", None)
        context.project.pipfile.packages.packages.pop("tensorflow", None)

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "numpy", "1.0.0", "https://pypi.org/simple"
        ).and_return(["000"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "tensorflow", "2.0.0", "https://pypi.org/simple"
        ).and_return(["111"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "pandas", "1.0.0", "https://pypi.org/simple"
        ).and_return(["222"]).once()

        pypi = Source("https://pypi.org/simple")
        pv_numpy_locked = PackageVersion(name="numpy", version="==1.0.0", index=pypi, develop=False)
        pv_tensorflow_locked = PackageVersion(name="tensorflow", version="==2.0.0", index=pypi, develop=False)
        pv_pandas_locked = PackageVersion(name="pandas", version="==1.0.0", index=pypi, develop=False)

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).once()

        context.should_receive("get_package_version").with_args(
            ("pandas", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_pandas_locked).once()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).once()

        context.dependents = {
            "numpy": {
                ("numpy", "1.0.0", "https://pypi.org/simple"): [  # set to list for reproducible runs.
                    (
                        ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    ),
                    (
                        ("pandas", "1.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    ),
                ]
            },
            "tensorflow": {("tensorflow", "2.0.0", "https://pypi.org/simple"): set()},
            "pandas": {("pandas", "1.0.0", "https://pypi.org/simple"): set()},
        }

        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
            marker_evaluation_result=True,
        ).and_return("python_version >= '3.8'").once()

        context.graph.should_receive("get_python_environment_marker").with_args(
            "pandas",
            "1.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
            marker_evaluation_result=True,
        ).and_return(None).once()

        product = Product.from_final_state(context=context, state=state)
        expected = {
            "advised_manifest_changes": [],
            "advised_runtime_environment": None,
            "dependency_graph": {"edges": [], "nodes": ["pandas", "numpy", "tensorflow"]},
            "justification": [
                {
                    "link": "https://thoth-station.ninja",
                    "message": "Foo bar",
                    "type": "INFO",
                }
            ],
            "project": {
                "constraints": [],
                "requirements": {
                    "dev-packages": {},
                    "packages": {"flask": "*"},
                    "requires": {"python_version": "3.6"},
                    "source": [
                        {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                        {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                    ],
                    "thoth": {
                        "allow_prereleases": {},
                        "disable_index_adjustment": False,
                    },
                },
                "requirements_locked": {
                    "_meta": {
                        "hash": {"sha256": "2e49395dfa87159358e581bd22e656c27c0dab04894d1b137a14f85bb387ea51"},
                        "pipfile-spec": 6,
                        "requires": {"python_version": "3.6"},
                        "sources": [
                            {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                            {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                        ],
                    },
                    "default": {
                        "numpy": {"hashes": ["sha256:000"], "index": "pypi-org-simple", "version": "==1.0.0"},
                        "pandas": {"hashes": ["sha256:222"], "index": "pypi-org-simple", "version": "==1.0.0"},
                        "tensorflow": {"hashes": ["sha256:111"], "index": "pypi-org-simple", "version": "==2.0.0"},
                    },
                    "develop": {},
                },
                "runtime_environment": {
                    "base_image": None,
                    "cuda_version": None,
                    "cudnn_version": None,
                    "hardware": {"cpu_family": None, "cpu_model": None, "gpu_model": None},
                    "name": None,
                    "operating_system": {"name": None, "version": None},
                    "labels": None,
                    "platform": None,
                    "mkl_version": None,
                    "openmpi_version": None,
                    "openblas_version": None,
                    "python_version": None,
                    "recommendation_type": None,
                },
            },
            "score": 0.0,
        }

        assert product.to_dict() == expected
예제 #4
0
    def test_from_final_state(self, context: Context) -> None:
        """Test instantiating product from a final state."""
        state = State(
            score=0.5,
            resolved_dependencies={
                "daiquiri": ("daiquiri", "1.6.0", "https://pypi.org/simple"),
                "numpy": ("numpy", "1.17.4", "https://pypi.org/simple"),
                "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
            advised_runtime_environment=RuntimeEnvironment.from_dict({"python_version": "3.6"}),
        )
        state.add_justification(self.JUSTIFICATION_SAMPLE_1)

        pypi = Source("https://pypi.org/simple")

        pv_daiquiri_locked = PackageVersion(name="daiquiri", version="==1.6.0", index=pypi, develop=False)
        pv_numpy_locked = PackageVersion(name="numpy", version="==1.17.4", index=pypi, develop=False)
        pv_tensorflow_locked = PackageVersion(name="tensorflow", version="==2.0.0", index=pypi, develop=False)

        context.should_receive("get_package_version").with_args(
            ("daiquiri", "1.6.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_daiquiri_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "daiquiri", "1.6.0", "https://pypi.org/simple"
        ).and_return(["000"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.17.4", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "numpy", "1.17.4", "https://pypi.org/simple"
        ).and_return(["111"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "tensorflow", "2.0.0", "https://pypi.org/simple"
        ).and_return(["222"]).ordered()

        pv_daiquiri = PackageVersion(name="daiquiri", version="*", index=pypi, develop=False)
        pv_tensorflow = PackageVersion(name="tensorflow", version=">=2.0.0", index=pypi, develop=False)

        project = flexmock(
            pipfile=Pipfile.from_string(self._PIPFILE),
            runtime_environment=RuntimeEnvironment.from_dict({"operating_system": {"name": "rhel"}}),
        )
        project.should_receive("iter_dependencies").with_args(with_devel=True).and_return(
            [pv_daiquiri, pv_tensorflow]
        ).once()

        context.project = project
        context.dependencies = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {("numpy", "1.17.4", "https://pypi.org/simple"): set()},
            "tensorflow": {
                ("tensorflow", "2.0.0", "https://pypi.org/simple"): {("numpy", "1.17.4", "https://pypi.org/simple")}
            },
        }
        context.dependents = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {
                ("numpy", "1.17.4", "https://pypi.org/simple"): {
                    (
                        ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    )
                }
            },
            "tensorflow": {("tensorflow", "2.0.0", "https://pypi.org/simple"): set()},
        }
        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.17.4",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
            marker_evaluation_result=True,
        ).and_return("python_version >= '3.7'").once()

        assert "THOTH_ADVISER_METADATA" not in os.environ
        metadata_justification = {"thoth.adviser": {"justification": [{"bar": "baz"}]}}
        os.environ["THOTH_ADVISER_METADATA"] = json.dumps(metadata_justification)
        try:
            product = Product.from_final_state(state=state, context=context)
        finally:
            os.environ.pop("THOTH_ADVISER_METADATA")

        assert product.score == state.score
        assert product.justification == list(
            chain(metadata_justification["thoth.adviser"]["justification"], state.justification)
        )
        assert product.advised_runtime_environment == state.advised_runtime_environment
        assert product.project.to_dict() == {
            "constraints": [],
            "requirements": {
                "packages": {
                    "daiquiri": {"index": "pypi-org-simple", "version": "*"},
                    "tensorflow": {"index": "pypi-org-simple", "version": ">=2.0.0"},
                },
                "dev-packages": {},
                "requires": {"python_version": "3.7"},
                "source": [
                    {
                        "url": "https://pypi.org/simple",
                        "verify_ssl": True,
                        "name": "pypi-org",
                    },
                    {
                        "url": "https://pypi.org/simple",
                        "verify_ssl": True,
                        "name": "pypi-org-simple",
                    },
                ],
                "thoth": {
                    "allow_prereleases": {"black": True},
                },
            },
            "requirements_locked": {
                "_meta": {
                    "sources": [
                        {"name": "pypi-org", "url": "https://pypi.org/simple", "verify_ssl": True},
                        {
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                            "name": "pypi-org-simple",
                        },
                    ],
                    "requires": {"python_version": "3.7"},
                    "hash": {"sha256": "6cc8365e799b949fb6cc564cea2d8e0e8a782ab676a006e65abbe14621b93381"},
                    "pipfile-spec": 6,
                },
                "default": {
                    "daiquiri": {
                        "version": "==1.6.0",
                        "hashes": ["sha256:000"],
                        "index": "pypi-org-simple",
                    },
                    "numpy": {
                        "version": "==1.17.4",
                        "hashes": ["sha256:111"],
                        "index": "pypi-org-simple",
                        "markers": "python_version >= '3.7'",
                    },
                    "tensorflow": {
                        "version": "==2.0.0",
                        "hashes": ["sha256:222"],
                        "index": "pypi-org-simple",
                    },
                },
                "develop": {},
            },
            "runtime_environment": {
                "hardware": {"cpu_family": None, "cpu_model": None, "gpu_model": None},
                "operating_system": {"name": "rhel", "version": None},
                "python_version": None,
                "cuda_version": None,
                "labels": None,
                "cudnn_version": None,
                "name": None,
                "platform": None,
                "base_image": None,
                "mkl_version": None,
                "openblas_version": None,
                "openmpi_version": None,
                "recommendation_type": None,
            },
        }
예제 #5
0
    def test_from_final_state(self, context: Context) -> None:
        """Test instantiating product from a final state."""
        state = State(
            score=0.5,
            resolved_dependencies=OrderedDict(
                {
                    "daiquiri": ("daiquiri", "1.6.0", "https://pypi.org/simple"),
                    "numpy": ("numpy", "1.17.4", "https://pypi.org/simple"),
                    "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                }
            ),
            unresolved_dependencies=OrderedDict(),
            advised_runtime_environment=RuntimeEnvironment.from_dict(
                {"python_version": "3.6"}
            ),
        )
        state.add_justification([{"foo": "bar"}])

        pypi = Source("https://pypi.org/simple")

        pv_daiquiri_locked = PackageVersion(
            name="daiquiri", version="==1.6.0", index=pypi, develop=False
        )
        pv_numpy_locked = PackageVersion(
            name="numpy", version="==1.17.4", index=pypi, develop=False
        )
        pv_tensorflow_locked = PackageVersion(
            name="tensorflow", version="==2.0.0", index=pypi, develop=False
        )

        context.should_receive("get_package_version").with_args(
            ("daiquiri", "1.6.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_daiquiri_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "daiquiri", "1.6.0", "https://pypi.org/simple"
        ).and_return(["000"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.17.4", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "numpy", "1.17.4", "https://pypi.org/simple"
        ).and_return(["111"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).ordered()
        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "tensorflow", "2.0.0", "https://pypi.org/simple"
        ).and_return(["222"]).ordered()

        pv_daiquiri = PackageVersion(
            name="daiquiri", version="*", index=pypi, develop=False
        )
        pv_tensorflow = PackageVersion(
            name="tensorflow", version=">=2.0.0", index=pypi, develop=False
        )

        project = flexmock()
        project.should_receive("iter_dependencies").with_args(
            with_devel=True
        ).and_return([pv_daiquiri, pv_tensorflow]).once()

        context.project = project
        context.dependencies = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {
                ("numpy", "1.17.4", "https://pypi.org/simple"): set()
            },
            "tensorflow": {
                ("tensorflow", "2.0.0", "https://pypi.org/simple"): {
                    ("numpy", "1.17.4", "https://pypi.org/simple")
                }
            },
        }
        context.dependents = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {
                ("numpy", "1.17.4", "https://pypi.org/simple"): {
                    (("tensorflow", "2.0.0", "https://pypi.org/simple"), "fedora", "31", "3.7")
                }
            },
            "tensorflow": {
                ("tensorflow", "2.0.0", "https://pypi.org/simple"): set()
            },
        }
        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.17.4",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
        ).and_return("python_version >= '3.7'").once()
        product = Product.from_final_state(state=state, context=context)

        assert product.score == state.score
        assert product.justification == state.justification
        assert product.advised_runtime_environment == state.advised_runtime_environment
        assert product.project.to_dict() == {
            "requirements": {
                "packages": {
                    "daiquiri": {"index": "pypi-org", "version": "*"},
                    "tensorflow": {"index": "pypi-org", "version": ">=2.0.0"},
                },
                "dev-packages": {},
                "source": [
                    {
                        "url": "https://pypi.org/simple",
                        "verify_ssl": True,
                        "name": "pypi-org",
                    }
                ],
            },
            "requirements_locked": {
                "_meta": {
                    "sources": [
                        {
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                            "name": "pypi-org",
                        }
                    ],
                    "requires": {},
                    "hash": {
                        "sha256": "c3a2f42932b6e5cd30f5664b11eda605f5fbd672f1b88729561d0d3edd10b5d9"
                    },
                    "pipfile-spec": 6,
                },
                "default": {
                    "daiquiri": {
                        "version": "==1.6.0",
                        "hashes": ["sha256:000"],
                        "index": "pypi-org",
                    },
                    "numpy": {
                        "version": "==1.17.4",
                        "hashes": ["sha256:111"],
                        "index": "pypi-org",
                        "markers": "python_version >= '3.7'",
                    },
                    "tensorflow": {
                        "version": "==2.0.0",
                        "hashes": ["sha256:222"],
                        "index": "pypi-org",
                    },
                },
                "develop": {},
            },
            "runtime_environment": {
                "hardware": {"cpu_family": None, "cpu_model": None},
                "operating_system": {"name": None, "version": None},
                "python_version": None,
                "cuda_version": None,
                "name": None,
            },
        }
예제 #6
0
    def test_environment_markers(self, context: Context) -> None:
        """Test handling of environment markers across multiple runs."""
        state = State(
            score=0.0,
            resolved_dependencies=OrderedDict(
                {
                    "numpy": ("numpy", "1.0.0", "https://pypi.org/simple"),
                    "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                }
            ),
            unresolved_dependencies=OrderedDict(),
        )

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "numpy", "1.0.0", "https://pypi.org/simple"
        ).and_return(["000"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "tensorflow", "2.0.0", "https://pypi.org/simple"
        ).and_return(["111"]).once()

        pypi = Source("https://pypi.org/simple")
        pv_numpy_locked = PackageVersion(
            name="numpy", version="==1.0.0", index=pypi, develop=False
        )
        pv_tensorflow_locked = PackageVersion(
            name="tensorflow", version="==2.0.0", index=pypi, develop=False
        )

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).twice()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).twice()

        context.dependents = {
            "numpy": {
                ("numpy", "1.0.0", "https://pypi.org/simple"): {
                    (
                        ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    )
                }
            },
            "tensorflow": {("tensorflow", "2.0.0", "https://pypi.org/simple"): set()},
        }

        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
        ).and_return("python_version >= '3.7'").and_return(
            "python_version >= '3' or 1"
        ).twice()

        product = Product.from_final_state(context=context, state=state)
        expected = {
            "advised_runtime_environment": None,
            "justification": [],
            "project": {
                "requirements": {
                    "dev-packages": {},
                    "packages": {"flask": "*", "tensorflow": "==1.9.0"},
                    "requires": {"python_version": "3.6"},
                    "source": [
                        {
                            "name": "pypi",
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                        },
                        {
                            "name": "pypi-org",
                            "url": "https://pypi.org/simple",
                            "verify_ssl": True,
                        },
                    ],
                },
                "requirements_locked": {
                    "_meta": {
                        "hash": {
                            "sha256": "e55b6bbaba9467f1629c34e7a4180a6a2d82df37e02e762866e7aac27ced0f99"
                        },
                        "pipfile-spec": 6,
                        "requires": {"python_version": "3.6"},
                        "sources": [
                            {
                                "name": "pypi",
                                "url": "https://pypi.org/simple",
                                "verify_ssl": True,
                            },
                            {
                                "name": "pypi-org",
                                "url": "https://pypi.org/simple",
                                "verify_ssl": True,
                            },
                        ],
                    },
                    "default": {
                        "numpy": {
                            "hashes": ["sha256:000"],
                            "index": "pypi-org",
                            "markers": "python_version >= '3.7'",
                            "version": "==1.0.0",
                        },
                        "tensorflow": {
                            "hashes": ["sha256:111"],
                            "index": "pypi-org",
                            "version": "==2.0.0",
                        },
                    },
                    "develop": {},
                },
                "runtime_environment": {
                    "cuda_version": None,
                    "hardware": {"cpu_family": None, "cpu_model": None},
                    "name": None,
                    "operating_system": {"name": None, "version": None},
                    "python_version": None,
                    "platform": None,
                },
            },
            "score": 0.0,
        }

        assert product.to_dict() == expected

        # Markers should not intersect.
        product = Product.from_final_state(context=context, state=state)
        expected["project"]["requirements_locked"]["default"]["numpy"][
            "markers"
        ] = "python_version >= '3' or 1"
        assert product.to_dict() == expected
예제 #7
0
    def test_from_final_state(self, context: Context) -> None:
        """Test instantiating product from a final state."""
        state = State(
            score=0.5,
            resolved_dependencies={
                "daiquiri": ("daiquiri", "1.6.0", "https://pypi.org/simple"),
                "numpy": ("numpy", "1.17.4", "https://pypi.org/simple"),
                "tensorflow":
                ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
            advised_runtime_environment=RuntimeEnvironment.from_dict(
                {"python_version": "3.6"}),
        )
        state.add_justification(self.JUSTIFICATION_SAMPLE_1)

        pypi = Source("https://pypi.org/simple")

        pv_daiquiri_locked = PackageVersion(name="daiquiri",
                                            version="==1.6.0",
                                            index=pypi,
                                            develop=False)
        pv_numpy_locked = PackageVersion(name="numpy",
                                         version="==1.17.4",
                                         index=pypi,
                                         develop=False)
        pv_tensorflow_locked = PackageVersion(name="tensorflow",
                                              version="==2.0.0",
                                              index=pypi,
                                              develop=False)

        context.should_receive("get_package_version").with_args(
            ("daiquiri", "1.6.0", "https://pypi.org/simple"),
            graceful=False).and_return(pv_daiquiri_locked).ordered()
        context.graph.should_receive(
            "get_python_package_hashes_sha256").with_args(
                "daiquiri", "1.6.0",
                "https://pypi.org/simple").and_return(["000"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.17.4", "https://pypi.org/simple"),
            graceful=False).and_return(pv_numpy_locked).ordered()
        context.graph.should_receive(
            "get_python_package_hashes_sha256").with_args(
                "numpy", "1.17.4",
                "https://pypi.org/simple").and_return(["111"]).ordered()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            graceful=False).and_return(pv_tensorflow_locked).ordered()
        context.graph.should_receive(
            "get_python_package_hashes_sha256").with_args(
                "tensorflow", "2.0.0",
                "https://pypi.org/simple").and_return(["222"]).ordered()

        pv_daiquiri = PackageVersion(name="daiquiri",
                                     version="*",
                                     index=pypi,
                                     develop=False)
        pv_tensorflow = PackageVersion(name="tensorflow",
                                       version=">=2.0.0",
                                       index=pypi,
                                       develop=False)

        project = flexmock(
            pipfile=Pipfile.from_string(self._PIPFILE),
            runtime_environment=RuntimeEnvironment.from_dict(
                {"operating_system": {
                    "name": "rhel"
                }}),
        )
        project.should_receive("iter_dependencies").with_args(
            with_devel=True).and_return([pv_daiquiri, pv_tensorflow]).once()

        context.project = project
        context.dependencies = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {
                ("numpy", "1.17.4", "https://pypi.org/simple"): set()
            },
            "tensorflow": {
                ("tensorflow", "2.0.0", "https://pypi.org/simple"):
                {("numpy", "1.17.4", "https://pypi.org/simple")}
            },
        }
        context.dependents = {
            "daiquiri": {
                ("daiquiri", "1.6.0", "https://pypi.org/simple"): set(),
            },
            "numpy": {
                ("numpy", "1.17.4", "https://pypi.org/simple"): {(
                    ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                    "fedora",
                    "31",
                    "3.7",
                )}
            },
            "tensorflow": {
                ("tensorflow", "2.0.0", "https://pypi.org/simple"): set()
            },
        }
        context.graph.should_receive(
            "get_python_environment_marker").with_args(
                "tensorflow",
                "2.0.0",
                "https://pypi.org/simple",
                dependency_name="numpy",
                dependency_version="1.17.4",
                os_name="fedora",
                os_version="31",
                python_version="3.7",
            ).and_return("python_version >= '3.7'").once()
        product = Product.from_final_state(state=state, context=context)

        assert product.score == state.score
        assert product.justification == state.justification
        assert product.advised_runtime_environment == state.advised_runtime_environment
        assert product.project.to_dict() == {
            "requirements": {
                "packages": {
                    "daiquiri": {
                        "index": "pypi-org",
                        "version": "*"
                    },
                    "tensorflow": {
                        "index": "pypi-org",
                        "version": ">=2.0.0"
                    },
                },
                "dev-packages": {},
                "requires": {
                    "python_version": "3.7"
                },
                "source": [{
                    "url": "https://pypi.org/simple",
                    "verify_ssl": True,
                    "name": "pypi-org",
                }],
            },
            "requirements_locked": {
                "_meta": {
                    "sources": [{
                        "url": "https://pypi.org/simple",
                        "verify_ssl": True,
                        "name": "pypi-org",
                    }],
                    "requires": {
                        "python_version": "3.7"
                    },
                    "hash": {
                        "sha256":
                        "f08689732b596fd705a45bbf9ec44c3995b17a1aa6392c46500aeb736c4d4e88"
                    },
                    "pipfile-spec":
                    6,
                },
                "default": {
                    "daiquiri": {
                        "version": "==1.6.0",
                        "hashes": ["sha256:000"],
                        "index": "pypi-org",
                    },
                    "numpy": {
                        "version": "==1.17.4",
                        "hashes": ["sha256:111"],
                        "index": "pypi-org",
                        "markers": "python_version >= '3.7'",
                    },
                    "tensorflow": {
                        "version": "==2.0.0",
                        "hashes": ["sha256:222"],
                        "index": "pypi-org",
                    },
                },
                "develop": {},
            },
            "runtime_environment": {
                "hardware": {
                    "cpu_family": None,
                    "cpu_model": None
                },
                "operating_system": {
                    "name": "rhel",
                    "version": None
                },
                "python_version": None,
                "cuda_version": None,
                "name": None,
                "platform": None,
            },
        }
예제 #8
0
    def test_environment_markers_shared(self, context: Context) -> None:
        """Test handling of environment markers when multiple dependencies share one."""
        state = State(
            score=0.0,
            resolved_dependencies={
                "pandas": ("pandas", "1.0.0", "https://pypi.org/simple"),
                "numpy": ("numpy", "1.0.0", "https://pypi.org/simple"),
                "tensorflow": ("tensorflow", "2.0.0", "https://pypi.org/simple"),
            },
            unresolved_dependencies={},
        )

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "numpy", "1.0.0", "https://pypi.org/simple"
        ).and_return(["000"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "tensorflow", "2.0.0", "https://pypi.org/simple"
        ).and_return(["111"]).once()

        context.graph.should_receive("get_python_package_hashes_sha256").with_args(
            "pandas", "1.0.0", "https://pypi.org/simple"
        ).and_return(["222"]).once()

        pypi = Source("https://pypi.org/simple")
        pv_numpy_locked = PackageVersion(name="numpy", version="==1.0.0", index=pypi, develop=False)
        pv_tensorflow_locked = PackageVersion(name="tensorflow", version="==2.0.0", index=pypi, develop=False)
        pv_pandas_locked = PackageVersion(name="pandas", version="==1.0.0", index=pypi, develop=False)

        context.should_receive("get_package_version").with_args(
            ("numpy", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_numpy_locked).once()

        context.should_receive("get_package_version").with_args(
            ("pandas", "1.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_pandas_locked).once()

        context.should_receive("get_package_version").with_args(
            ("tensorflow", "2.0.0", "https://pypi.org/simple"), graceful=False
        ).and_return(pv_tensorflow_locked).once()

        context.dependents = {
            "numpy": {
                ("numpy", "1.0.0", "https://pypi.org/simple"): [  # set to list for reproducible runs.
                    (
                        ("tensorflow", "2.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    ),
                    (
                        ("pandas", "1.0.0", "https://pypi.org/simple"),
                        "fedora",
                        "31",
                        "3.7",
                    ),
                ]
            },
            "tensorflow": {("tensorflow", "2.0.0", "https://pypi.org/simple"): set()},
            "pandas": {("pandas", "1.0.0", "https://pypi.org/simple"): set()},
        }

        context.graph.should_receive("get_python_environment_marker").with_args(
            "tensorflow",
            "2.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
        ).and_return("python_version >= '3.8'").once()

        context.graph.should_receive("get_python_environment_marker").with_args(
            "pandas",
            "1.0.0",
            "https://pypi.org/simple",
            dependency_name="numpy",
            dependency_version="1.0.0",
            os_name="fedora",
            os_version="31",
            python_version="3.7",
        ).and_return(None).once()

        product = Product.from_final_state(context=context, state=state)
        expected = {
            "advised_manifest_changes": [],
            "advised_runtime_environment": None,
            "justification": [],
            "project": {
                "constraints": [],
                "requirements": {
                    "dev-packages": {},
                    "packages": {"flask": "*", "tensorflow": "==1.9.0"},
                    "requires": {"python_version": "3.6"},
                    "source": [
                        {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                        {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                    ],
                    "thoth": {
                        "allow_prereleases": {},
                        "disable_index_adjustment": False,
                    },
                },
                "requirements_locked": {
                    "_meta": {
                        "hash": {"sha256": "4628b328465fa6946ca9abf9c3576fb502436d0a40300d798058677de0f6128a"},
                        "pipfile-spec": 6,
                        "requires": {"python_version": "3.6"},
                        "sources": [
                            {"name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": True},
                            {"name": "pypi-org-simple", "url": "https://pypi.org/simple", "verify_ssl": True},
                        ],
                    },
                    "default": {
                        "numpy": {"hashes": ["sha256:000"], "index": "pypi-org-simple", "version": "==1.0.0"},
                        "pandas": {"hashes": ["sha256:222"], "index": "pypi-org-simple", "version": "==1.0.0"},
                        "tensorflow": {"hashes": ["sha256:111"], "index": "pypi-org-simple", "version": "==2.0.0"},
                    },
                    "develop": {},
                },
                "runtime_environment": {
                    "base_image": None,
                    "cuda_version": None,
                    "cudnn_version": None,
                    "hardware": {"cpu_family": None, "cpu_model": None, "gpu_model": None},
                    "name": None,
                    "operating_system": {"name": None, "version": None},
                    "platform": None,
                    "mkl_version": None,
                    "openmpi_version": None,
                    "openblas_version": None,
                    "python_version": None,
                    "recommendation_type": None,
                },
            },
            "score": 0.0,
        }

        assert product.to_dict() == expected