From 94858302cb2eadceabba23071c0a3190fda6e238 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Fri, 20 Feb 2026 13:08:06 -0800 Subject: [PATCH 1/2] update: add hash constant --- src/py/mat3ra/code/constants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/py/mat3ra/code/constants.py b/src/py/mat3ra/code/constants.py index 5081fe6b..a47976a2 100644 --- a/src/py/mat3ra/code/constants.py +++ b/src/py/mat3ra/code/constants.py @@ -2,6 +2,8 @@ from mat3ra.esse.models.definitions.constants import FundamentalConstants +HASH_TOLERANCE = 3 # decimal places; mirrors @mat3ra/code/dist/js/constants.ts HASH_TOLERANCE + CONSTANTS = FundamentalConstants() From d089074d0425db9bedd7678798ee9e3ba6501706 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Fri, 20 Feb 2026 16:15:25 -0800 Subject: [PATCH 2/2] update: hash for wf --- src/py/mat3ra/code/utils.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/py/mat3ra/code/utils.py diff --git a/src/py/mat3ra/code/utils.py b/src/py/mat3ra/code/utils.py new file mode 100644 index 00000000..92aaba70 --- /dev/null +++ b/src/py/mat3ra/code/utils.py @@ -0,0 +1,37 @@ +import hashlib +import json +import re +from typing import Any + + +def sort_keys_deep(obj: Any) -> Any: + """Mirrors JS sortKeysDeepForObject: recursively sort object keys alphabetically.""" + if callable(getattr(obj, "model_dump", None)): + return sort_keys_deep(obj.model_dump(mode="json", exclude_none=True)) + if isinstance(obj, list): + return [sort_keys_deep(item) for item in obj] + if isinstance(obj, dict): + return {k: sort_keys_deep(obj[k]) for k in sorted(obj.keys())} + return obj + + +def calculate_hash_from_object(obj: Any) -> str: + """Mirrors JS calculateHashFromObject: MD5 of JSON.stringify(sortKeysDeep(obj)).""" + message = json.dumps(sort_keys_deep(obj), separators=(",", ":")) + return hashlib.md5(message.encode()).hexdigest() + + +def remove_timestampable_keys(config: dict) -> dict: + """Mirrors JS removeTimestampableKeysFromConfig: removes createdAt, updatedAt, removedAt.""" + return {k: v for k, v in config.items() if k not in ("createdAt", "updatedAt", "removedAt")} + + +def remove_comments_from_source_code(text: str, language: str = "shell") -> str: + """Mirrors JS removeCommentsFromSourceCode: removes lines starting with # (except shebang).""" + return re.sub(r"^(\s+)?#(?!!).*$", "", text, flags=re.MULTILINE) + + +def remove_empty_lines_from_string(text: str) -> str: + """Mirrors JS removeEmptyLinesFromString: removes empty lines and trims.""" + return re.sub(r"^\s*[\r\n]", "", text, flags=re.MULTILINE).strip() +