diff --git a/requirements.txt b/requirements.txt index 48443cf..0d644fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ regex~=2022.9.13 pyffmpeg~=2.4.2.18 ffmpeg-progress-yield~=0.7.8 pathvalidate~=2.5.2 +guppy3~=3.1.3 diff --git a/src/create_custom_objects.py b/src/create_custom_objects.py index b3551b6..306aa97 100644 --- a/src/create_custom_objects.py +++ b/src/create_custom_objects.py @@ -6,9 +6,10 @@ from music_kraken.objects import ( Source, DatabaseObject ) +from music_kraken.objects.new_collection import Collection from music_kraken.utils.enums import SourcePages - +""" only_smile = Artist( name="Only Smile", source_list=[Source(SourcePages.BANDCAMP, "https://onlysmile.bandcamp.com/")], @@ -102,3 +103,47 @@ for _id, _object in objects_by_id.items(): print(_id, _object, sep=": ") print(only_smile) +""" + +c = Collection([Song(title="hi"), Song(title="hi2"), Song(title="hi3")]) +c1 = Collection([Song(title="he"), Song(title="hi5")]) +c11 = Collection([Song(title="wow how ultra subby")]) +c2 = Collection([Song(title="heeee")]) + +b = Collection([Song(title="some b"), Song(title="other b")]) +b1 = Collection([Song(title="sub b")]) +b11 = Collection([Song(title="This shouldn't work")]) + +b1.contain_collection_inside(b11) + +b.contain_collection_inside(b1) +b.contain_collection_inside(c1) + + +c.contain_collection_inside(c1) +c.contain_collection_inside(c2) + +c1.contain_collection_inside(c11) +c1.contain_collection_inside(c11) + +print(c.data) +print(c1.data) + +c11.append(Song(title="after creation")) + +print() +print(c.data, len(c)) +print(c1.data) + +print() +print("c: ", c) +print("b: ", b) + +c.sync_with_other_collection(b) +print("synced: ") + +print("c: ", c) +print("b: ", b) + +print(c.data) +print(c._data) diff --git a/src/music_kraken/objects/new_collection.py b/src/music_kraken/objects/new_collection.py new file mode 100644 index 0000000..c6fa1aa --- /dev/null +++ b/src/music_kraken/objects/new_collection.py @@ -0,0 +1,93 @@ +from typing import List, Iterable, Iterator, Optional, TypeVar, Generic +import guppy +from guppy.heapy import Path + +from .parents import DatabaseObject + + +T = TypeVar('T', bound=DatabaseObject) + + +hp = guppy.hpy() + +def _replace_all_refs(replace_with, replace): + """ + NO + I have a very good reason to use this here + DONT use this anywhere else... + + This replaces **ALL** references to replace with a reference to replace_with. + + https://benkurtovic.com/2015/01/28/python-object-replacement.html + """ + for path in hp.iso(replace).pathsin: + relation = path.path[1] + if isinstance(relation, Path.R_INDEXVAL): + path.src.theone[relation.r] = replace_with + + +class Collection(Generic[T]): + _data: List[T] + + shallow_list = property(fget=lambda self: self.data) + + def __init__(self, data: Optional[Iterable[T]]) -> None: + self._data = [] + self.contained_collections: List[Collection[T]] = [] + + self.extend(data) + + def append(self, __object: T): + self._data.append(__object) + + def extend(self, __iterable: Optional[Iterable[T]]): + if __iterable is None: + return + + for __object in __iterable: + self.append(__object) + + def sync_with_other_collection(self, equal_collection: "Collection"): + """ + If two collections always need to have the same values, this can be used. + + Internally: + 1. import the data from other to self + - _data + - contained_collections + 2. replace all refs from the other object, with refs from this object + """ + if equal_collection is self: + return + + # don't add the elements from the subelements from the other collection. + # this will be done in the next step. + self.extend(equal_collection._data) + # add all submodules + for equal_sub_collection in equal_collection.contained_collections: + self.contain_collection_inside(equal_sub_collection) + + # now the ugly part + # replace all refs of the other element with this one + _replace_all_refs(self, equal_collection) + + + def contain_collection_inside(self, sub_collection: "Collection"): + """ + This collection will ALWAYS contain everything from the passed in collection + """ + if sub_collection in self.contained_collections: + return + + self.contained_collections.append(sub_collection) + + @property + def data(self) -> List[T]: + return [*self._data, *(__object for collection in self.contained_collections for __object in collection.shallow_list)] + + def __len__(self) -> int: + return len(self._data) + sum(len(collection) for collection in self.contained_collections) + + def __iter__(self) -> Iterator[T]: + for element in self._data: + yield element \ No newline at end of file