Merge branch 'blender-v4.2-release'

This commit is contained in:
Campbell Barton
2024-06-06 17:44:20 +10:00
3 changed files with 90 additions and 37 deletions

View File

@@ -828,6 +828,9 @@ def extensions_panel_draw_impl(
if is_addon:
col_a.label(text="Permissions")
# WARNING: while this is documented to be a dict, old packages may contain a list of strings.
# As it happens dictionary keys & list values both iterate over string,
# however we will want to show the dictionary values eventually.
if (value := item_remote.get("permissions")):
col_b.label(text=", ".join([iface_(x.title()) for x in value]), translate=False)
else:

View File

@@ -101,6 +101,11 @@ RE_CONTROL_CHARS = re.compile(r'[\x00-\x1f\x7f-\x9f]')
# 16kb to be responsive even on slow connections.
CHUNK_SIZE_DEFAULT = 1 << 14
# Short descriptions for the UI:
# Used for project tag-line & permissions values.
TERSE_DESCRIPTION_MAX_LENGTH = 64
# Standard out may be communicating with a parent process,
# arbitrary prints are NOT acceptable.
@@ -1167,6 +1172,25 @@ def pkg_idname_is_valid_or_error(pkg_idname: str) -> Optional[str]:
return None
def pkg_manifest_validate_terse_description_or_error(value: str) -> Optional[str]:
# Could be an argument.
length_limit = TERSE_DESCRIPTION_MAX_LENGTH
if (length_limit != -1) and (len(value) > length_limit):
return "a value no longer than {:d} characters expected, found {:d}".format(length_limit, len(value))
if (error := pkg_manifest_validate_field_any_non_empty_string_stripped_no_control_chars(value, True)) is not None:
return error
# As we don't have a reliable (unicode aware) punctuation check, just check the last character is alpha/numeric.
if value[-1].isalnum():
pass # OK.
elif value[-1] in {")", "]", "}"}:
pass # Allow closing brackets (sometimes used to mention formats).
else:
return "alpha-numeric suffix expected, the string must not end with punctuation"
return None
# -----------------------------------------------------------------------------
# Manifest Validation (Generic Callbacks)
#
@@ -1282,19 +1306,7 @@ def pkg_manifest_validate_field_type(value: str, strict: bool) -> Optional[str]:
def pkg_manifest_validate_field_tagline(value: str, strict: bool) -> Optional[str]:
if strict:
if (error := pkg_manifest_validate_field_any_non_empty_string_stripped_no_control_chars(value, strict)) is not None:
return error
# Additional requirements.
if len(value) > 64:
return "a value no longer than 64 characters expected, found {:d}".format(len(value))
# As we don't have a reliable (unicode aware) punctuation check, just check the last character is alpha/numeric.
if value[-1].isalnum():
pass # OK.
elif value[-1] in {")", "]", "}"}:
pass # Allow closing brackets (sometimes used to mention formats).
else:
return "alpha-numeric suffix expected, the string must not end with punctuation"
return pkg_manifest_validate_terse_description_or_error(value)
else:
if (error := pkg_manifest_validate_field_any_non_empty_string(value, strict)) is not None:
return error
@@ -1303,33 +1315,57 @@ def pkg_manifest_validate_field_tagline(value: str, strict: bool) -> Optional[st
def pkg_manifest_validate_field_permissions(
# `Dict[str, str]` is expected but at this point it's only guaranteed to be a dict.
value: Dict[Any, Any],
value: Union[
# `Dict[str, str]` is expected but at this point it's only guaranteed to be a dict.
Dict[Any, Any],
# Kept for old files.
List[Any],
],
strict: bool,
) -> Optional[str]:
_ = strict
# Always strict for now as it doesn't seem as there are repositories using invalid values.
strict = True
keys_valid = {
"files",
"network",
"clipboard",
"camera",
"microphone",
}
if strict:
keys_valid = {
"files",
"network",
"clipboard",
"camera",
"microphone",
}
# A list may be passed in when not-strict.
if not isinstance(value, dict):
return "permissions must be a table of strings, not a {:s}".format(str(type(value)))
for item_key, item_value in value.items():
# Validate the key.
if not isinstance(item_key, str):
return "key \"{:s}\" must be a string not a {:s}".format(str(item_key), str(type(item_key)))
if not isinstance(item_value, str):
return "value of \"{:s}\" must be a string not a {:s}".format(item_key, str(type(item_value)))
if item_key not in keys_valid:
return "value of \"{:s}\" must be a value in {!r}".format(item_key, tuple(keys_valid))
# Validate the value.
if not isinstance(item_value, str):
return "value of \"{:s}\" must be a string not a {:s}".format(item_key, str(type(item_value)))
if (error := pkg_manifest_validate_terse_description_or_error(item_value)) is not None:
return "value of \"{:s}\": {:s}".format(item_key, error)
else:
# TODO: basic validation.
# if (error := pkg_manifest_validate_field_any_dict_of_non_empty_strings(value, strict)) is not None:
# return error
pass
if isinstance(value, dict):
for item_key, item_value in value.items():
if not isinstance(item_key, str):
return "key \"{:s}\" must be a string not a {:s}".format(str(item_key), str(type(item_key)))
if not isinstance(item_value, str):
return "value of \"{:s}\" must be a string not a {:s}".format(item_key, str(type(item_value)))
elif isinstance(value, list):
# Historic beta convention, keep for compatibility.
for i, item in enumerate(value):
if not isinstance(item_key, str):
return "Expected item at index {:d} to be an int not a {:s}".format(i, str(type(item)))
else:
# The caller doesn't allow this.
assert False, "internal error, disallowed type"
return None
@@ -1434,7 +1470,7 @@ def pkg_manifest_validate_field_archive_hash(
# Keep in sync with `PkgManifest`.
# key, type, check_fn.
pkg_manifest_known_keys_and_types: Tuple[
Tuple[str, type, Callable[[Any, bool], Optional[str]]],
Tuple[str, Union[type, Tuple[type, ...]], Callable[[Any, bool], Optional[str]]],
...,
] = (
("id", str, pkg_manifest_validate_field_idname),
@@ -1451,7 +1487,8 @@ pkg_manifest_known_keys_and_types: Tuple[
("blender_version_max", str, pkg_manifest_validate_field_any_version_primitive_or_empty),
("website", str, pkg_manifest_validate_field_any_non_empty_string_stripped_no_control_chars),
("copyright", list, pkg_manifest_validate_field_any_non_empty_list_of_non_empty_strings),
("permissions", dict, pkg_manifest_validate_field_permissions),
# Type should be `dict` eventually, some existing packages will have a list of strings instead.
("permissions", (dict, list), pkg_manifest_validate_field_permissions),
("tags", list, pkg_manifest_validate_field_any_non_empty_list_of_non_empty_strings),
("wheels", list, pkg_manifest_validate_field_wheels),
)
@@ -1514,8 +1551,11 @@ def pkg_manifest_is_valid_or_error_impl(
pass
else:
error_list.append("\"{:s}\" must be a {:s}, not a {:s}".format(
x_key,
x_ty.__name__,
x_key, (
"[{:s}]".format(", ".join(x_ty_elem.__name__ for x_ty_elem in x_ty))
if isinstance(x_ty, tuple) else
x_ty.__name__
),
type(x_val).__name__,
))
if not all_errors:
@@ -3044,6 +3084,9 @@ class subcmd_dummy:
fh.write("""version = "1.0.0"\n""")
fh.write("""tagline = "This is a tagline"\n""")
fh.write("""blender_version_min = "0.0.0"\n""")
fh.write("""[permissions]\n""")
for value in ("files", "network", "clipboard", "camera", "microphone"):
fh.write("""{:s} = "Example text"\n""".format(value))
with open(os.path.join(pkg_src_dir, "__init__.py"), "w", encoding="utf-8") as fh:
fh.write("""

View File

@@ -10,12 +10,19 @@ version = "0.2.0"
blender_version_min = "2.80.0"
maintainer = "Maintainer Name"
license = [
"SPDX:CC-0"
"SPDX:CC-0",
]
copyright = [
"Developer Name"
"Developer Name",
]
[permissions]
network = "Need to sync data to server"
files = "Import/export files from/to disk"
clipboard = "Copy rotations"
camera = "Reason for connecting to a camera"
microphone = "An add-on could do this in theory"
[build]
paths_exclude_pattern = [
# Exclude all Python cache.