diff --git a/tests/artifacts/test-package/dist/test_package-0-py3-none-any-00000000.whl b/tests/artifacts/test-package/dist/test_package-0-py3-none-any-00000000.whl deleted file mode 100644 index 21754cd..0000000 Binary files a/tests/artifacts/test-package/dist/test_package-0-py3-none-any-00000000.whl and /dev/null differ diff --git a/tests/artifacts/test-package/dist/test_package-0-py3-none-any-null.whl b/tests/artifacts/test-package/dist/test_package-0-py3-none-any-null.whl new file mode 100644 index 0000000..6b8b594 Binary files /dev/null and b/tests/artifacts/test-package/dist/test_package-0-py3-none-any-null.whl differ diff --git a/tests/artifacts/test-package/dist/test_package-0-variants.json b/tests/artifacts/test-package/dist/test_package-0-variants.json index ce4a247..27c17c2 100644 --- a/tests/artifacts/test-package/dist/test_package-0-variants.json +++ b/tests/artifacts/test-package/dist/test_package-0-variants.json @@ -20,7 +20,7 @@ } }, "variants": { - "00000000": {}, + "null": {}, "5d8be4b9": { "installable_plugin": { "feat1": [ diff --git a/tests/artifacts/variant_json_files/dummy_project-1.0.0-variants.json b/tests/artifacts/variant_json_files/dummy_project-1.0.0-variants.json index 80b444d..11e8b99 100644 --- a/tests/artifacts/variant_json_files/dummy_project-1.0.0-variants.json +++ b/tests/artifacts/variant_json_files/dummy_project-1.0.0-variants.json @@ -75,7 +75,7 @@ ] } }, - "00000000": {}, + "null": {}, "3f7188c1": { "fictional_hw": { "architecture": [ diff --git a/tests/artifacts/variant_json_files/sandbox_project-1.0.0-variants.json b/tests/artifacts/variant_json_files/sandbox_project-1.0.0-variants.json index 0d75743..ac39a46 100644 --- a/tests/artifacts/variant_json_files/sandbox_project-1.0.0-variants.json +++ b/tests/artifacts/variant_json_files/sandbox_project-1.0.0-variants.json @@ -23,7 +23,7 @@ } }, "variants": { - "00000000": {}, + "null": {}, "38ea48ce": { "fictional_hw": { "architecture": [ @@ -69,4 +69,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/commands/test_analyze_wheel.py b/tests/commands/test_analyze_wheel.py index 957cc1d..889e845 100644 --- a/tests/commands/test_analyze_wheel.py +++ b/tests/commands/test_analyze_wheel.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING from variantlib.commands.main import main -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL if TYPE_CHECKING: import pytest @@ -34,13 +34,13 @@ def test_analyze_wheel_null_variant( "analyze-wheel", "-i", "tests/artifacts/test-package/dist/test_package-0-py3-none-any-" - f"{NULL_VARIANT_HASH}.whl", + f"{NULL_VARIANT_LABEL}.whl", ] ) assert ( capsys.readouterr().out == f"""\ -############################## Variant: `{NULL_VARIANT_HASH}` \ +############################## Variant: `{NULL_VARIANT_LABEL}` \ ############################# ################################################################################ """ diff --git a/tests/commands/test_generate_index_json.py b/tests/commands/test_generate_index_json.py index 2398e5c..7ee0a83 100644 --- a/tests/commands/test_generate_index_json.py +++ b/tests/commands/test_generate_index_json.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING from variantlib.commands.main import main -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL from variantlib.constants import VARIANT_INFO_DEFAULT_PRIO_KEY from variantlib.constants import VARIANT_INFO_NAMESPACE_KEY from variantlib.constants import VARIANT_INFO_PROVIDER_DATA_KEY @@ -25,7 +25,7 @@ def test_generate_index_json( ) -> None: filenames = [ "test_package-0-py3-none-any.whl", - f"test_package-0-py3-none-any-{NULL_VARIANT_HASH}.whl", + f"test_package-0-py3-none-any-{NULL_VARIANT_LABEL}.whl", "test_package-0-py3-none-any-5d8be4b9.whl", ] artifact_dir = Path("tests/artifacts/test-package/dist") @@ -51,7 +51,7 @@ def test_generate_index_json( }, }, VARIANTS_JSON_VARIANT_DATA_KEY: { - NULL_VARIANT_HASH: {}, + NULL_VARIANT_LABEL: {}, "5d8be4b9": { "installable_plugin": { "feat1": ["val1c"], diff --git a/tests/commands/test_get_variant_hash.py b/tests/commands/test_get_variant_hash.py index 052be07..12a2279 100644 --- a/tests/commands/test_get_variant_hash.py +++ b/tests/commands/test_get_variant_hash.py @@ -1,17 +1,18 @@ from __future__ import annotations +import hashlib from itertools import chain import pytest from variantlib.commands.main import main -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import VARIANT_HASH_LEN @pytest.mark.parametrize( ("properties", "expected"), [ - ([], NULL_VARIANT_HASH), + ([], hashlib.sha256(b"").hexdigest()[:VARIANT_HASH_LEN]), (["a::b::c"], "01a9783a"), (["d::e::f"], "41665eee"), (["a::b::c", "d::e::f"], "eb9a66a7"), diff --git a/tests/commands/test_make_variant.py b/tests/commands/test_make_variant.py index 52fa494..3253340 100644 --- a/tests/commands/test_make_variant.py +++ b/tests/commands/test_make_variant.py @@ -7,7 +7,7 @@ from tests.utils import assert_zips_equal from variantlib.commands.main import main -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL if TYPE_CHECKING: from pathlib import Path @@ -37,7 +37,7 @@ def mocked_plugin_reqs( ("label", "properties"), [ # Null Variant - (NULL_VARIANT_HASH, None), + (NULL_VARIANT_LABEL, None), # Variant 1 ( "5d8be4b9", @@ -86,7 +86,7 @@ def test_make_variant( itertools.chain.from_iterable(["-p", vprop] for vprop in properties) ) - if len(label) != 8: + if len(label) != 8 and label != "null": cmd_args.append(f"--variant-label={label}") main([*cmd_args]) @@ -112,9 +112,13 @@ def test_make_variant( ["--property=x::y::z", "--variant-label=123456789"], "error: invalid variant label", ), + ( + ["--property=x::y::z", "--variant-label=null"], + "error: invalid variant label", + ), ( ["--null-variant", "--variant-label=null"], - "error: --variant-label cannot be usedwith --null-variant", + "error: --variant-label cannot be used with --null-variant", ), ], ) diff --git a/tests/commands/test_unmake_variant.py b/tests/commands/test_unmake_variant.py index bae47b7..87442c1 100644 --- a/tests/commands/test_unmake_variant.py +++ b/tests/commands/test_unmake_variant.py @@ -6,13 +6,13 @@ from tests.utils import assert_zips_equal from variantlib.commands.main import main -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL @pytest.mark.parametrize( "filename", [ - f"test_package-0-py3-none-any-{NULL_VARIANT_HASH}.whl", + f"test_package-0-py3-none-any-{NULL_VARIANT_LABEL}.whl", "test_package-0-py3-none-any-5d8be4b9.whl", "test_package-0-py3-none-any-60567bd9.whl", "test_package-0-py3-none-any-fbe82642.whl", diff --git a/tests/models/test_variant.py b/tests/models/test_variant.py index 029d6f6..75ddb55 100644 --- a/tests/models/test_variant.py +++ b/tests/models/test_variant.py @@ -7,7 +7,6 @@ from hypothesis import given from hypothesis import strategies as st -from variantlib.constants import NULL_VARIANT_HASH from variantlib.constants import VALIDATION_FEATURE_NAME_REGEX from variantlib.constants import VALIDATION_NAMESPACE_REGEX from variantlib.constants import VALIDATION_VALUE_REGEX @@ -238,7 +237,7 @@ def test_variantprop_sorting() -> None: def test_null_variant() -> None: vdesc = VariantDescription() assert vdesc.properties == [] - assert vdesc.hexdigest == NULL_VARIANT_HASH + assert vdesc.hexdigest == hashlib.sha256(b"").hexdigest()[:VARIANT_HASH_LEN] def test_variantdescription_initialization() -> None: diff --git a/tests/test_api.py b/tests/test_api.py index 844cfb3..be6a309 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import re import string from collections.abc import Generator from typing import TYPE_CHECKING @@ -24,10 +25,11 @@ from variantlib.api import VariantValidationResult from variantlib.api import check_variant_supported from variantlib.api import get_variant_environment_dict +from variantlib.api import get_variant_label from variantlib.api import get_variants_by_priority from variantlib.api import make_variant_dist_info from variantlib.api import validate_variant -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL from variantlib.constants import PYPROJECT_TOML_TOP_KEY from variantlib.constants import VALIDATION_FEATURE_NAME_REGEX from variantlib.constants import VALIDATION_NAMESPACE_REGEX @@ -130,10 +132,10 @@ def test_get_variants_by_priority_roundtrip( }, VARIANTS_JSON_VARIANT_DATA_KEY: { f"foo{vdesc.hexdigest[:4]}" - if custom_labels and vdesc.hexdigest != NULL_VARIANT_HASH - else vdesc.hexdigest: vdesc.to_dict() + if custom_labels and not vdesc.is_null_variant() + else get_variant_label(vdesc): vdesc.to_dict() for vdesc in combinations - if explicit_null or vdesc.hexdigest != NULL_VARIANT_HASH + if explicit_null or not vdesc.is_null_variant() }, } @@ -146,8 +148,8 @@ def test_get_variants_by_priority_roundtrip( assert get_variants_by_priority(variants_json=typed_variants_json) == [ f"foo{vdesc.hexdigest[:4]}" - if custom_labels and vdesc.hexdigest != NULL_VARIANT_HASH - else vdesc.hexdigest + if custom_labels and not vdesc.is_null_variant() + else get_variant_label(vdesc) for vdesc in combinations ] @@ -220,7 +222,7 @@ def get_or_skip_combinations() -> Generator[VariantDescription]: variants_json = { VARIANTS_JSON_VARIANT_DATA_KEY: { - vdesc.hexdigest: vdesc.to_dict() for vdesc in combinations + get_variant_label(vdesc): vdesc.to_dict() for vdesc in combinations } } @@ -234,7 +236,7 @@ def get_or_skip_combinations() -> Generator[VariantDescription]: ).return_value = {provider_cfg.namespace: provider_cfg for provider_cfg in configs} assert get_variants_by_priority(variants_json=typed_variants_json) == [ - vdesc.hexdigest for vdesc in combinations + get_variant_label(vdesc) for vdesc in combinations ] @@ -550,24 +552,114 @@ def test_get_variant_environment_dict() -> None: def test_make_variant_dist_info_invalid_label(): with pytest.raises( - ValidationError, match=r"Variant label cannot be specified for the null variant" + ValidationError, + match=rf"Null variant must always use {NULL_VARIANT_LABEL!r} label", ): make_variant_dist_info(VariantDescription([]), variant_label="foo") with pytest.raises( ValidationError, - match=rf"{NULL_VARIANT_HASH} label can be used only for the null variant", + match=rf"{NULL_VARIANT_LABEL!r} label can be used only for the null variant", ): make_variant_dist_info( VariantDescription([VariantProperty("a", "b", "c")]), - variant_label=NULL_VARIANT_HASH, + variant_label=NULL_VARIANT_LABEL, ) - with pytest.raises(ValidationError, match=r"Invalid variant label: foo/bar"): + with pytest.raises( + ValidationError, + match=re.escape( + "Invalid variant label: 'foo/bar' (must be up to 8 alphanumeric characters)" + ), + ): make_variant_dist_info( VariantDescription([VariantProperty("a", "b", "c")]), variant_label="foo/bar", ) - with pytest.raises(ValidationError, match=r"Invalid variant label: 123456789"): + with pytest.raises( + ValidationError, + match=re.escape( + "Invalid variant label: '123456789' (must be up to 8 alphanumeric " + "characters)" + ), + ): make_variant_dist_info( VariantDescription([VariantProperty("a", "b", "c")]), variant_label="123456789", ) + + +def test_get_variant_label() -> None: + assert get_variant_label(VariantDescription()) == NULL_VARIANT_LABEL + assert ( + get_variant_label(VariantDescription(), NULL_VARIANT_LABEL) + == NULL_VARIANT_LABEL + ) + + assert ( + get_variant_label(VariantDescription([VariantProperty("a", "b", "c")])) + == "01a9783a" + ) + assert ( + get_variant_label( + VariantDescription( + [VariantProperty("a", "b", "c"), VariantProperty("d", "e", "f")] + ) + ) + == "eb9a66a7" + ) + assert ( + get_variant_label( + VariantDescription( + [VariantProperty("d", "e", "f"), VariantProperty("a", "b", "c")] + ) + ) + == "eb9a66a7" + ) + + assert ( + get_variant_label(VariantDescription([VariantProperty("a", "b", "c")]), "foo") + == "foo" + ) + assert ( + get_variant_label( + VariantDescription( + [VariantProperty("a", "b", "c"), VariantProperty("d", "e", "f")] + ), + "foo", + ) + == "foo" + ) + + with pytest.raises( + ValidationError, + match=rf"Null variant must always use {NULL_VARIANT_LABEL!r} label", + ): + get_variant_label(VariantDescription([]), "foo") + with pytest.raises( + ValidationError, + match=rf"{NULL_VARIANT_LABEL!r} label can be used only for the null variant", + ): + get_variant_label( + VariantDescription([VariantProperty("a", "b", "c")]), + NULL_VARIANT_LABEL, + ) + with pytest.raises( + ValidationError, + match=re.escape( + "Invalid variant label: 'foo/bar' (must be up to 8 alphanumeric characters)" + ), + ): + get_variant_label( + VariantDescription([VariantProperty("a", "b", "c")]), + "foo/bar", + ) + with pytest.raises( + ValidationError, + match=re.escape( + "Invalid variant label: '123456789' (must be up to 8 alphanumeric " + "characters)" + ), + ): + get_variant_label( + VariantDescription([VariantProperty("a", "b", "c")]), + "123456789", + ) diff --git a/tests/test_variants_json.py b/tests/test_variants_json.py index 20e318e..7e7e5f8 100644 --- a/tests/test_variants_json.py +++ b/tests/test_variants_json.py @@ -7,7 +7,7 @@ import pytest -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL from variantlib.constants import VARIANT_INFO_DEFAULT_PRIO_KEY from variantlib.constants import VARIANT_INFO_FEATURE_KEY from variantlib.constants import VARIANT_INFO_NAMESPACE_KEY @@ -45,7 +45,7 @@ def test_validate_variants_json() -> None: variants_json = VariantsJson(data) assert variants_json.variants == { - NULL_VARIANT_HASH: VariantDescription(), + NULL_VARIANT_LABEL: VariantDescription(), "03e04d5e": VariantDescription( properties=[ VariantProperty( @@ -503,12 +503,12 @@ def test_merge_variants() -> None: def test_null_variant_label(): with pytest.raises( ValidationError, - match=rf"{NULL_VARIANT_HASH} label can only be used for the null variant", + match=rf"{NULL_VARIANT_LABEL!r} label can only be used for the null variant", ): VariantsJson( - {VARIANTS_JSON_VARIANT_DATA_KEY: {NULL_VARIANT_HASH: {"x": {"y": ["z"]}}}} + {VARIANTS_JSON_VARIANT_DATA_KEY: {NULL_VARIANT_LABEL: {"x": {"y": ["z"]}}}} ) with pytest.raises( - ValidationError, match=rf"Null variant must use {NULL_VARIANT_HASH} label" + ValidationError, match=rf"Null variant must use {NULL_VARIANT_LABEL!r} label" ): - VariantsJson({VARIANTS_JSON_VARIANT_DATA_KEY: {"null": {}}}) + VariantsJson({VARIANTS_JSON_VARIANT_DATA_KEY: {"zuul": {}}}) diff --git a/variantlib/api.py b/variantlib/api.py index 3f3da13..a1808a9 100644 --- a/variantlib/api.py +++ b/variantlib/api.py @@ -8,7 +8,7 @@ from typing import TYPE_CHECKING from variantlib.configuration import VariantConfiguration -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL from variantlib.constants import VALIDATION_VARIANT_LABEL_REGEX from variantlib.constants import VARIANT_HASH_LEN from variantlib.constants import VariantsJsonDict @@ -41,6 +41,7 @@ "VariantProperty", "VariantValidationResult", "get_variant_environment_dict", + "get_variant_label", "get_variants_by_priority", "make_variant_dist_info", "validate_variant", @@ -82,7 +83,7 @@ def get_variants_by_priority( vdesc.hexdigest: label for label, vdesc in variants_json.variants.items() } # handle the implicit null variant - label_map.setdefault(NULL_VARIANT_HASH, NULL_VARIANT_HASH) + label_map.setdefault(VariantDescription([]).hexdigest, NULL_VARIANT_LABEL) return [ label_map[vdesc.hexdigest] @@ -148,19 +149,7 @@ def make_variant_dist_info( variant_info = VariantInfo() variant_json = VariantDistInfo(variant_info) variant_json.variant_desc = vdesc - if variant_label is not None: - if vdesc.is_null_variant(): - if variant_label != NULL_VARIANT_HASH: - raise ValidationError( - "Variant label cannot be specified for the null variant" - ) - elif variant_label == NULL_VARIANT_HASH: - raise ValidationError( - f"{NULL_VARIANT_HASH} label can be used only for the null variant" - ) - elif not VALIDATION_VARIANT_LABEL_REGEX.fullmatch(variant_label): - raise ValidationError(f"Invalid variant label: {variant_label}") - variant_json.variant_label = variant_label + variant_json.variant_label = get_variant_label(vdesc, variant_label) return variant_json.to_str() @@ -233,3 +222,39 @@ def get_variant_environment_dict( }, "variant_properties": {vprop.to_str() for vprop in variant_desc.properties}, } + + +def get_variant_label( + variant_desc: VariantDescription, + custom_label: str | None = None, +) -> str: + """ + Get the label for the specified variant + + Get the label corresponding to `variant_desc`. If `custom_label` + is provided, validate it and use it. If `custom_label` is invalid, + raises a `ValidationError`. + """ + + if custom_label is None: + return ( + NULL_VARIANT_LABEL + if variant_desc.is_null_variant() + else variant_desc.hexdigest + ) + + if variant_desc.is_null_variant(): + if custom_label != NULL_VARIANT_LABEL: + raise ValidationError( + f"Null variant must always use {NULL_VARIANT_LABEL!r} label" + ) + elif custom_label == NULL_VARIANT_LABEL: + raise ValidationError( + f"{NULL_VARIANT_LABEL!r} label can be used only for the null variant" + ) + elif not VALIDATION_VARIANT_LABEL_REGEX.fullmatch(custom_label): + raise ValidationError( + f"Invalid variant label: {custom_label!r} " + "(must be up to 8 alphanumeric characters)" + ) + return custom_label diff --git a/variantlib/commands/analyze_wheel.py b/variantlib/commands/analyze_wheel.py index 991d48f..c00065c 100644 --- a/variantlib/commands/analyze_wheel.py +++ b/variantlib/commands/analyze_wheel.py @@ -8,6 +8,7 @@ from typing import TYPE_CHECKING from variantlib import __package_name__ +from variantlib.api import get_variant_label from variantlib.constants import VALIDATION_WHEEL_NAME_REGEX from variantlib.constants import VARIANT_DIST_INFO_FILENAME from variantlib.variant_dist_info import VariantDistInfo @@ -19,7 +20,7 @@ def pretty_print(vdesc: VariantDescription) -> str: - result_str = f"{'#' * 30} Variant: `{vdesc.hexdigest}` {'#' * 29}" + result_str = f"{'#' * 30} Variant: `{get_variant_label(vdesc)}` {'#' * 29}" for vprop in vdesc.properties: result_str += f"\n{vprop.to_str()}" result_str += f"\n{'#' * 80}\n" diff --git a/variantlib/commands/make_variant.py b/variantlib/commands/make_variant.py index a9c98e1..afb36fa 100644 --- a/variantlib/commands/make_variant.py +++ b/variantlib/commands/make_variant.py @@ -15,6 +15,7 @@ from variantlib import __package_name__ from variantlib.api import VariantDescription from variantlib.api import VariantProperty +from variantlib.api import get_variant_label from variantlib.api import make_variant_dist_info from variantlib.api import validate_variant from variantlib.constants import VALIDATION_VARIANT_LABEL_REGEX @@ -108,7 +109,11 @@ def make_variant(args: list[str]) -> None: if parsed_args.variant_label is not None: if parsed_args.null_variant: - parser.error("--variant-label cannot be usedwith --null-variant") + parser.error("--variant-label cannot be used with --null-variant") + if parsed_args.variant_label == "null": + parser.error( + "invalid variant label: 'none' is reserved for the --null-variant" + ) if not VALIDATION_VARIANT_LABEL_REGEX.fullmatch(parsed_args.variant_label): parser.error( "invalid variant label (must be up to 8 alphanumeric characters): " @@ -229,8 +234,7 @@ def _make_variant( # Create a null variant vdesc = VariantDescription() - if variant_label is None: - variant_label = vdesc.hexdigest + variant_label = get_variant_label(vdesc, variant_label) # Determine output wheel filename output_filepath = ( diff --git a/variantlib/constants.py b/variantlib/constants.py index eb17ff6..dfe14cb 100644 --- a/variantlib/constants.py +++ b/variantlib/constants.py @@ -5,7 +5,7 @@ from typing import TypedDict VARIANT_HASH_LEN = 8 -NULL_VARIANT_HASH = "0" * VARIANT_HASH_LEN +NULL_VARIANT_LABEL = "null" CONFIG_FILENAME = "variants.toml" VARIANT_DIST_INFO_FILENAME = "variant.json" diff --git a/variantlib/models/variant.py b/variantlib/models/variant.py index 028dddb..d1a0f2f 100644 --- a/variantlib/models/variant.py +++ b/variantlib/models/variant.py @@ -9,7 +9,6 @@ from dataclasses import field from functools import cached_property -from variantlib.constants import NULL_VARIANT_HASH from variantlib.constants import VALIDATION_FEATURE_NAME_REGEX from variantlib.constants import VALIDATION_FEATURE_REGEX from variantlib.constants import VALIDATION_NAMESPACE_REGEX @@ -193,10 +192,6 @@ def hexdigest(self) -> str: """ Compute the hash of the object. """ - if self.is_null_variant(): - # The `null-variant` is a special case where no properties are defined. - return NULL_VARIANT_HASH - hash_object = hashlib.sha256() # Append a newline to every serialized property to ensure that they # are separated from one another. Otherwise, two "adjacent" variants diff --git a/variantlib/variants_json.py b/variantlib/variants_json.py index f4eb2ae..1ee37c8 100644 --- a/variantlib/variants_json.py +++ b/variantlib/variants_json.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING from typing import Any -from variantlib.constants import NULL_VARIANT_HASH +from variantlib.constants import NULL_VARIANT_LABEL from variantlib.constants import VALIDATION_VARIANT_LABEL_REGEX from variantlib.constants import VARIANT_INFO_DEFAULT_PRIO_KEY from variantlib.constants import VARIANT_INFO_FEATURE_KEY @@ -153,16 +153,16 @@ def _process(self, variant_table: VariantsJsonDict) -> None: ignore_subkeys=True, ) as packed_vdesc: vdesc = VariantDescription.from_dict(packed_vdesc) - if vdesc.is_null_variant() and variant_label != NULL_VARIANT_HASH: + if vdesc.is_null_variant() and variant_label != NULL_VARIANT_LABEL: raise ValidationError( - f"Null variant must use {NULL_VARIANT_HASH} label" + f"Null variant must use {NULL_VARIANT_LABEL!r} label" ) if ( not vdesc.is_null_variant() - and variant_label == NULL_VARIANT_HASH + and variant_label == NULL_VARIANT_LABEL ): raise ValidationError( - f"{NULL_VARIANT_HASH} label can only be used for " + f"{NULL_VARIANT_LABEL!r} label can only be used for " "the null variant" ) self.variants[variant_label] = vdesc