Skip to content
Snippets Groups Projects
Verified Commit 0527d470 authored by Antoine R. Dumont's avatar Antoine R. Dumont
Browse files

Push options from cli to the Nar class

That will ease testing. The cli becomes a pass-through.

This also bootstraps tests reusing existing test cases from before (using the nix
binary).

Refs. swh/meta#4979
parent 8d2f162a
No related branches found
No related tags found
No related merge requests found
......@@ -8,7 +8,6 @@ import io
import os
from pathlib import Path
import stat
from typing import Callable
import click
......@@ -84,9 +83,33 @@ class Nar:
"""
def __init__(self, exclude_vcs: bool, updater: Callable, debug: bool = False):
def __init__(
self,
hash_algo: str,
format_output: str = "hex",
exclude_vcs: bool = False,
debug: bool = False,
):
self.updater = hashlib.sha256() if hash_algo == "sha256" else hashlib.sha1()
format_output = format_output.lower()
self.exclude_vcs = exclude_vcs
self.updater_fn = updater
def identity(hsh: str) -> str:
return hsh
def convert_b64(hsh: str) -> str:
return base64.b64encode(bytes.fromhex(hsh)).decode().lower()
def convert_b32(hsh: str) -> str:
return base64.b32encode(bytes.fromhex(hsh)).decode().lower()
convert_fn = {
"hex": identity,
"base64": convert_b64,
"base32": convert_b32,
}
self.convert_fn = convert_fn[format_output]
self.__debug = debug
self.__indent = 0
......@@ -114,14 +137,14 @@ class Nar:
raise ValueError("not string nor file")
blen = length.to_bytes(8, byteorder="little") # 64-bit little endian
self.updater_fn(blen)
self.updater.update(blen)
# first part of 'pad'
if isinstance(thing, str):
self.updater_fn(byte_sequence)
self.updater.update(byte_sequence)
elif isinstance(thing, io.BufferedReader):
for chunk in iter(lambda: thing.read(CHUNK_SIZE), b""):
self.updater_fn(chunk)
self.updater.update(chunk)
# second part of 'pad
m = length % 8
......@@ -130,7 +153,7 @@ class Nar:
else:
offset = 8 - m
boffset = bytearray(offset)
self.updater_fn(boffset)
self.updater.update(boffset)
def _filter_and_serialize(self, fso: Path) -> None:
"""On the first level of the main tree, we may have to skip some paths (e.g.
......@@ -167,7 +190,7 @@ class Nar:
if os.access(fso, os.X_OK):
self.str_(["executable", ""])
self.str_("contents")
with fso.open("rb") as f:
with open(str(fso), "rb") as f:
self.str_(f)
elif stat.S_ISLNK(mode):
......@@ -205,7 +228,10 @@ class Nar:
else []
)
self._serialize(fso, first_level=True)
return
def result(self):
"""Compute the result in the required format."""
return self.convert_fn(self.updater.hexdigest())
@swh_cli_group.command(name="nar", context_settings=CONTEXT_SETTINGS)
......@@ -225,25 +251,6 @@ class Nar:
@click.option("--debug/--no-debug", default=lambda: os.environ.get("DEBUG", False))
def cli(exclude_vcs, directory, hash_algo, format_output, debug):
"""Compute NAR hashes on a directory."""
h = hashlib.sha256() if hash_algo == "sha256" else hashlib.sha1()
updater = h.update
format_output = format_output.lower()
def identity(hsh):
return hsh
def convert_b64(hsh: str):
return base64.b64encode(bytes.fromhex(hsh)).decode().lower()
def convert_b32(hsh: str):
return base64.b32encode(bytes.fromhex(hsh)).decode().lower()
convert_fn = {
"hex": identity,
"base64": convert_b64,
"base32": convert_b32,
}
nar = Nar(exclude_vcs, updater, debug=debug)
nar = Nar(hash_algo, format_output, exclude_vcs, debug=debug)
nar.serialize(directory)
print(convert_fn[format_output](h.hexdigest()))
print(nar.result())
# Copyright (C) 2023 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
from pathlib import Path
from swh.core.tarball import uncompress
from swh.loader.core.nar import Nar
def test_nar_tarball(tmpdir, tarball_with_nar_hashes):
tarball_path, nar_hashes = tarball_with_nar_hashes
directory_path = Path(tmpdir)
directory_path.mkdir(parents=True, exist_ok=True)
uncompress(str(tarball_path), dest=str(directory_path))
path_on_disk = next(directory_path.iterdir())
for hash_algo, expected_hash_value in nar_hashes.items():
nar = Nar(hash_algo=hash_algo)
nar.serialize(path_on_disk)
assert nar.result() == nar_hashes[hash_algo]
def test_nar_content(content_with_nar_hashes):
content_path, nar_hashes = content_with_nar_hashes
for hash_algo, expected_hash_value in nar_hashes.items():
nar = Nar(hash_algo=hash_algo)
nar.serialize(content_path)
assert nar.result() == expected_hash_value
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment