Esempio n. 1
0
    def test_invalid_elements(self, path, hash_, size, caused_by):
        with pytest.raises(InvalidRecordEntry) as exc_info:
            RecordEntry.from_elements(path, hash_, size)

        assert exc_info.value.elements == (path, hash_, size)
        for word in caused_by:
            assert word in str(exc_info.value)
Esempio n. 2
0
    def write_to_fs(
        self,
        scheme: Scheme,
        path: str,
        stream: BinaryIO,
        is_executable: bool,
    ) -> RecordEntry:
        """Write contents of ``stream`` to the correct location on the filesystem.

        :param scheme: scheme to write the file in (like "purelib", "platlib" etc).
        :param path: path within that scheme
        :param stream: contents of the file
        :param is_executable: whether the file should be made executable

        - Ensures that an existing file is not being overwritten.
        - Hashes the written content, to determine the entry in the ``RECORD`` file.
        """
        target_path = self._path_with_destdir(scheme, path)
        if os.path.exists(target_path):
            message = f"File already exists: {target_path}"
            raise FileExistsError(message)

        parent_folder = os.path.dirname(target_path)
        if not os.path.exists(parent_folder):
            os.makedirs(parent_folder)

        with open(target_path, "wb") as f:
            hash_, size = copyfileobj_with_hashing(stream, f,
                                                   self.hash_algorithm)

        if is_executable:
            make_file_executable(target_path)

        return RecordEntry(path, Hash(self.hash_algorithm, hash_), size)
Esempio n. 3
0
    def test_string_representation_with_prefix(self, scheme, elements, data,
                                               passes_validation):
        record = RecordEntry.from_elements(*elements)

        expected_string_value = "prefix/" + ",".join(
            [(str(elem) if elem is not None else "") for elem in elements])
        assert record.to_line("prefix/") == expected_string_value.encode()
Esempio n. 4
0
    def test_populates_attributes_correctly(self, scheme, elements, data,
                                            passes_validation):
        path, hash_string, size = elements

        record = RecordEntry.from_elements(path, hash_string, size)

        assert record.path == path
        assert record.size == size

        if record.hash_ is not None:
            assert isinstance(record.hash_, Hash)
            assert record.hash_.name == "sha256"
            assert record.hash_.value == hash_string[len("sha256="):]
Esempio n. 5
0
 def finalize_installation(
     self,
     scheme: Scheme,
     record_file_path: str | Path,
     records: Iterable[tuple[Scheme, RecordEntry]],
 ) -> None:
     if self.symlink_to:
         # Create symlinks to the cached location
         for relpath in _create_symlinks_recursively(
                 self.symlink_to, self.scheme_dict[scheme]):
             records = itertools.chain(
                 records,
                 [(scheme,
                   RecordEntry(relpath.replace("\\", "/"), None, None))],
             )
     return super().finalize_installation(scheme, record_file_path, records)
Esempio n. 6
0
    def test_equality(self):
        record = RecordEntry.from_elements(
            "file.py",
            "sha256=AVTFPZpEKzuHr7OvQZmhaU3LvwKz06AJw8mT\\_pNh2yI",
            "3144",
        )
        record_same = RecordEntry.from_elements(
            "file.py",
            "sha256=AVTFPZpEKzuHr7OvQZmhaU3LvwKz06AJw8mT\\_pNh2yI",
            "3144",
        )
        record_different_name = RecordEntry.from_elements(
            "file2.py",
            "sha256=AVTFPZpEKzuHr7OvQZmhaU3LvwKz06AJw8mT\\_pNh2yI",
            "3144",
        )
        record_different_hash_name = RecordEntry.from_elements(
            "file.py",
            "md5=AVTFPZpEKzuHr7OvQZmhaU3LvwKz06AJw8mT\\_pNh2yI",
            "3144",
        )
        record_different_hash_value = RecordEntry.from_elements(
            "file.py",
            "sha256=qwertyuiodfdsflkgshdlkjghrefawrwerwffsdfflk29",
            "3144",
        )
        record_different_size = RecordEntry.from_elements(
            "file.py",
            "sha256=AVTFPZpEKzuHr7OvQZmhaU3LvwKz06AJw8mT\\_pNh2yI",
            "10",
        )

        assert record == record_same

        assert record != "random string"
        assert record != record_different_name
        assert record != record_different_hash_name
        assert record != record_different_hash_value
        assert record != record_different_size

        # Ensure equality is based on current state
        record_same.hash_ = None
        assert record != record_same
Esempio n. 7
0
def _install_wheel(
    wheel: str,
    interpreter: str,
    script_kind: str,
    scheme_dict: dict[str, str],
    excludes: Callable[[Scheme, str], bool] | None = None,
    additional_files: list[tuple[Scheme | None, str, BinaryIO]] | None = None,
    additional_metadata: dict[str, bytes] | None = None,
) -> str:
    """A lower level installation method that is copied from installer
    but is controlled by extra parameters.

    Return the .dist-info path
    """
    destination = InstallDestination(
        scheme_dict,
        interpreter=interpreter,
        script_kind=script_kind,
    )

    with WheelFile.open(wheel) as source:
        root_scheme = _process_WHEEL_file(source)
        destination.root_scheme = root_scheme

        # RECORD handling
        record_file_path = os.path.join(source.dist_info_dir, "RECORD")
        written_records = []

        # console-scripts and gui-scripts are copied anyway.
        if "entry_points.txt" in source.dist_info_filenames:
            entrypoints_text = source.read_dist_info("entry_points.txt")
            for name, module, attr, section in parse_entrypoints(
                    entrypoints_text):
                record = destination.write_script(
                    name=name,
                    module=module,
                    attr=attr,
                    section=section,
                )
                written_records.append(record)

        for record_elements, stream in source.get_contents():
            source_record = RecordEntry.from_elements(*record_elements)
            path = source_record.path
            if os.path.normcase(path) == os.path.normcase(record_file_path):
                continue
            # Figure out where to write this file.
            scheme, destination_path = _determine_scheme(
                path=path,
                source=source,
                root_scheme=root_scheme,
            )
            if excludes is not None and excludes(scheme, path):
                continue
            record = destination.write_file(
                scheme=scheme,
                path=destination_path,
                stream=stream,
            )
            # add executable bit if necessary
            target_path = os.path.join(scheme_dict[scheme], destination_path)
            file_mode = stat.S_IMODE(
                source._zipfile.getinfo(path).external_attr >> 16)
            if file_mode & 0o111:
                new_mode = os.stat(target_path).st_mode
                new_mode |= (new_mode & 0o444) >> 2
                os.chmod(target_path, new_mode)
            written_records.append(record)

        # Write additional files
        if additional_files:
            for scheme, path, stream in additional_files:
                record = destination.write_file(
                    scheme=scheme or root_scheme,
                    path=path,
                    stream=stream,
                )
            written_records.append(record)

        # Write all the installation-specific metadata
        metadata = {
            "INSTALLER": f"installer {__version__}".encode(),
        }
        if additional_metadata:
            metadata.update(additional_metadata)
        for filename, contents in metadata.items():
            path = os.path.join(source.dist_info_dir, filename)

            with io.BytesIO(contents) as other_stream:
                record = destination.write_file(
                    scheme=root_scheme,
                    path=path,
                    stream=other_stream,
                )
            written_records.append(record)

        written_records.append(RecordEntry(record_file_path, None, None))
        destination.finalize_installation(
            scheme=root_scheme,
            record_file_path=record_file_path,
            records=written_records,
        )
        return os.path.join(scheme_dict[root_scheme], source.dist_info_dir)
Esempio n. 8
0
    def test_finalize_write_record(self, destination):
        records = [
            (
                "data",
                destination.write_file(
                    "data",
                    "my_data1.bin",
                    io.BytesIO(b"my data 1"),
                    is_executable=False,
                ),
            ),
            (
                "data",
                destination.write_file(
                    "data",
                    "my_data2.bin",
                    io.BytesIO(b"my data 2"),
                    is_executable=False,
                ),
            ),
            (
                "data",
                destination.write_file(
                    "data",
                    "my_data3.bin",
                    io.BytesIO(b"my data 3"),
                    is_executable=False,
                ),
            ),
            (
                "scripts",
                destination.write_file(
                    "scripts",
                    "my_script",
                    io.BytesIO(b"my script"),
                    is_executable=True,
                ),
            ),
            (
                "scripts",
                destination.write_file(
                    "scripts",
                    "my_script2",
                    io.BytesIO(b"#!python\nmy script"),
                    is_executable=False,
                ),
            ),
            (
                "scripts",
                destination.write_script("my_entrypoint", "my_module",
                                         "my_function", "console"),
            ),
            ("purelib", RecordEntry("RECORD", None, None)),
        ]

        destination.finalize_installation("purelib", "RECORD", records)
        file_path = os.path.join(destination.scheme_dict["purelib"], "RECORD")

        with open(file_path, "rb") as f:
            data = f.read()

        assert data == (
            b"../data/my_data1.bin,sha256=355d00f8ce0e3eea93b078de0fa5ad87ff94aaba40000772a6572eb2d159f2ce,9\n"
            b"../data/my_data2.bin,sha256=94fed5f2858baa0c9709b74048d88f76c5288333d466186dffb17c4f96c2dde4,9\n"
            b"../data/my_data3.bin,sha256=d7c92baeebb582bd35c7e58cffd0a14804a81efd267d1015ebe0766ddf6cc69a,9\n"
            b"../scripts/my_script,sha256=33ad1f5af51230990fb70d9aa54be3596c0e72744f715cbfccee3ee25a47d3ca,9\n"
            b"../scripts/my_script2,sha256=93dffdf7b9136d36109bb11714b7255592f59b637df2b53dd105f8e9778cbe36,22\n"
            b"../scripts/my_entrypoint,sha256=fe9ffd9f099e21ea0c05f4346a486bd4a6ca9f795a0f2760d09edccb416ce892,216\n"
            b"RECORD,,\n")
Esempio n. 9
0
    def test_calls_destination_correctly(self, mock_destination):
        # Create a fake wheel
        source = FakeWheelSource(
            distribution="fancy",
            version="1.0.0",
            regular_files={
                "fancy/__init__.py":
                b"""\
                    def main():
                        print("I'm a fancy package")
                """,
                "fancy/__main__.py":
                b"""\
                    if __name__ == "__main__":
                        from . import main
                        main()
                """,
            },
            dist_info_files={
                "top_level.txt":
                b"""\
                    fancy
                """,
                "entry_points.txt":
                b"""\
                    [console_scripts]
                    fancy = fancy:main

                    [gui_scripts]
                    fancy-gui = fancy:main
                """,
                "WHEEL":
                b"""\
                    Wheel-Version: 1.0
                    Generator: magic (1.0.0)
                    Root-Is-Purelib: true
                    Tag: py3-none-any
                """,
                "METADATA":
                b"""\
                    Metadata-Version: 2.1
                    Name: fancy
                    Version: 1.0.0
                    Summary: A fancy package
                    Author: Agendaless Consulting
                    Author-email: [email protected]
                    License: MIT
                    Keywords: fancy amazing
                    Platform: UNKNOWN
                    Classifier: Intended Audience :: Developers
                """,
            },
        )

        install(
            source=source,
            destination=mock_destination,
            additional_metadata={
                "fun_file.txt": b"this should be in dist-info!",
            },
        )

        mock_destination.assert_has_calls([
            mock.call.write_script(
                name="fancy",
                module="fancy",
                attr="main",
                section="console",
            ),
            mock.call.write_script(
                name="fancy-gui",
                module="fancy",
                attr="main",
                section="gui",
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy/__init__.py",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy/__main__.py",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy-1.0.0.dist-info/METADATA",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy-1.0.0.dist-info/WHEEL",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy-1.0.0.dist-info/entry_points.txt",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy-1.0.0.dist-info/top_level.txt",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.write_file(
                scheme="purelib",
                path="fancy-1.0.0.dist-info/fun_file.txt",
                stream=mock.ANY,
                is_executable=False,
            ),
            mock.call.finalize_installation(
                scheme="purelib",
                record_file_path="fancy-1.0.0.dist-info/RECORD",
                records=[
                    ("scripts", ("fancy", "fancy", "main", "console")),
                    ("scripts", ("fancy-gui", "fancy", "main", "gui")),
                    ("purelib", ("fancy/__init__.py", "purelib", 0)),
                    ("purelib", ("fancy/__main__.py", "purelib", 0)),
                    ("purelib", ("fancy-1.0.0.dist-info/METADATA", "purelib",
                                 0)),
                    ("purelib", ("fancy-1.0.0.dist-info/WHEEL", "purelib", 0)),
                    (
                        "purelib",
                        ("fancy-1.0.0.dist-info/entry_points.txt", "purelib",
                         0),
                    ),
                    (
                        "purelib",
                        ("fancy-1.0.0.dist-info/top_level.txt", "purelib", 0),
                    ),
                    (
                        "purelib",
                        ("fancy-1.0.0.dist-info/fun_file.txt", "purelib", 0),
                    ),
                    (
                        "purelib",
                        RecordEntry("fancy-1.0.0.dist-info/RECORD", None,
                                    None),
                    ),
                ],
            ),
        ])
Esempio n. 10
0
def install(
    source: WheelSource,
    destination: WheelDestination,
    additional_metadata: Dict[str, bytes],
) -> None:
    """Install wheel described by ``source`` into ``destination``.

    :param source: wheel to install.
    :param destination: where to write the wheel.
    :param additional_metadata: additional metadata files to generate, usually
                                generated by the installer.

    """
    root_scheme = _process_WHEEL_file(source)

    # RECORD handling
    record_file_path = posixpath.join(source.dist_info_dir, "RECORD")
    written_records = []

    # Write the entry_points based scripts.
    if "entry_points.txt" in source.dist_info_filenames:
        entrypoints_text = source.read_dist_info("entry_points.txt")
        for name, module, attr, section in parse_entrypoints(entrypoints_text):
            record = destination.write_script(
                name=name,
                module=module,
                attr=attr,
                section=section,
            )
            written_records.append((Scheme("scripts"), record))

    # Write all the files from the wheel.
    for record_elements, stream, is_executable in source.get_contents():
        source_record = RecordEntry.from_elements(*record_elements)
        path = source_record.path
        # Skip the RECORD, which is written at the end, based on this info.
        if path == record_file_path:
            continue

        # Figure out where to write this file.
        scheme, destination_path = _determine_scheme(
            path=path,
            source=source,
            root_scheme=root_scheme,
        )
        record = destination.write_file(
            scheme=scheme,
            path=destination_path,
            stream=stream,
            is_executable=is_executable,
        )
        written_records.append((scheme, record))

    # Write all the installation-specific metadata
    for filename, contents in additional_metadata.items():
        path = posixpath.join(source.dist_info_dir, filename)

        with BytesIO(contents) as other_stream:
            record = destination.write_file(
                scheme=root_scheme,
                path=path,
                stream=other_stream,
                is_executable=is_executable,
            )
        written_records.append((root_scheme, record))

    written_records.append(
        (root_scheme, RecordEntry(record_file_path, None, None)))
    destination.finalize_installation(
        scheme=root_scheme,
        record_file_path=record_file_path,
        records=written_records,
    )
Esempio n. 11
0
 def test_validation(self, scheme, elements, data, passes_validation):
     record = RecordEntry.from_elements(*elements)
     assert record.validate(data) == passes_validation
Esempio n. 12
0
 def test_valid_elements(self, path, hash_, size):
     RecordEntry.from_elements(path, hash_, size)