summaryrefslogtreecommitdiff
path: root/meta
diff options
context:
space:
mode:
authorSefa Eyeoglu <contact@scrumplex.net>2022-06-26 12:16:11 +0200
committerSefa Eyeoglu <contact@scrumplex.net>2022-06-26 12:16:11 +0200
commit75006147ddc166ed3f6f148940018475159bd326 (patch)
treecca8c90154e467f33763ed67d354d9859b3968f9 /meta
parentd5e359508bbec270fe81191b6eb209998559cc20 (diff)
downloadProject-Tick-75006147ddc166ed3f6f148940018475159bd326.tar.gz
Project-Tick-75006147ddc166ed3f6f148940018475159bd326.zip
feat: implement merge function for MetaBase
Diffstat (limited to 'meta')
-rw-r--r--meta/common/__init__.py19
-rw-r--r--meta/model/__init__.py37
2 files changed, 55 insertions, 1 deletions
diff --git a/meta/common/__init__.py b/meta/common/__init__.py
index 8e463f8448..ee294f00b3 100644
--- a/meta/common/__init__.py
+++ b/meta/common/__init__.py
@@ -49,3 +49,22 @@ def replace_old_launchermeta_url(url):
return o._replace(netloc="piston-meta.mojang.com").geturl()
return url
+
+
+def get_all_bases(cls, bases=None):
+ bases = bases or []
+ bases.append(cls)
+ for c in cls.__bases__:
+ get_all_bases(c, bases)
+ return tuple(bases)
+
+
+def merge_dict(base: dict, overlay: dict):
+ for k, v in base.items():
+ if isinstance(v, dict):
+ merge_dict(v, overlay.setdefault(k, {}))
+ else:
+ if k not in overlay:
+ overlay[k] = v
+
+ return overlay
diff --git a/meta/model/__init__.py b/meta/model/__init__.py
index b089040ee8..caa0e6d893 100644
--- a/meta/model/__init__.py
+++ b/meta/model/__init__.py
@@ -1,10 +1,11 @@
+import copy
from datetime import datetime
from typing import Optional, List, Dict, Any, Iterator
import pydantic
from pydantic import Field, validator
-from ..common import serialize_datetime, replace_old_launchermeta_url
+from ..common import serialize_datetime, replace_old_launchermeta_url, get_all_bases, merge_dict
META_FORMAT_VERSION = 1
@@ -119,6 +120,40 @@ class MetaBase(pydantic.BaseModel):
with open(file_path, "w") as f:
f.write(self.json())
+ def merge(self, other):
+ """
+ Merge other object with self.
+ - Concatenates lists
+ - Combines sets
+ - Merges dictionaries (other takes priority)
+ - Recurses for all fields that are also MetaBase classes
+ - Overwrites for any other field type (int, string, ...)
+ """
+ assert type(other) is type(self)
+ for key, field in self.__fields__.items():
+ ours = getattr(self, key)
+ theirs = getattr(other, key)
+ if theirs is None:
+ continue
+ if ours is None:
+ setattr(self, key, theirs)
+ continue
+
+ if isinstance(ours, list):
+ ours += theirs
+ elif isinstance(ours, set):
+ ours |= theirs
+ elif isinstance(ours, dict):
+ result = merge_dict(ours, copy.deepcopy(theirs))
+ setattr(self, key, result)
+ elif MetaBase in get_all_bases(field.type_):
+ ours.merge(theirs)
+ else:
+ setattr(self, key, theirs)
+
+ def __hash__(self):
+ return hash(self.json())
+
class Config:
allow_population_by_field_name = True