feat: contains in collection

This commit is contained in:
Hellow 2023-10-17 22:29:55 +02:00
parent 7725ebc306
commit a6cea55eb2
3 changed files with 88 additions and 27 deletions

View File

@ -1,45 +1,85 @@
from typing import List, Iterable, Iterator, Optional, TypeVar, Generic
import guppy
from guppy.heapy import Path
from typing import List, Iterable, Iterator, Optional, TypeVar, Generic, Dict, Type
from collections import defaultdict
from .parents import DatabaseObject
from ..utils.functions import replace_all_refs
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]
_indexed_values: Dict[str, set]
_indexed_to_objects: Dict[any, list]
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._indexed_values = defaultdict(set)
self._indexed_to_objects = defaultdict(list)
self.extend(data)
def append(self, __object: T):
def _map_element(self, __object: T):
for name, value in __object.indexing_values:
if value is None:
continue
self._indexed_values[name].add(value)
self._indexed_to_objects[value].append(__object)
def _unmap_element(self, __object: T):
for name, value in __object.indexing_values:
if value is None:
continue
if value not in self._indexed_values[name]:
continue
try:
self._indexed_to_objects[value].remove(__object)
except ValueError:
continue
if not len(self._indexed_to_objects[value]):
self._indexed_values[name].remove(value)
def _contained_in_self(self, __object: T) -> bool:
for name, value in __object.indexing_values:
if value is None:
continue
if value in self._indexed_values[name]:
return True
return False
def _contained_in(self, __object: T) -> Optional["Collection"]:
if self._contained_in_self(__object):
return self
for collection in self.contained_collections:
if collection._contained_in_self(__object):
return collection
return None
def contains(self, __object: T) -> bool:
return self._contained_in(__object) is not None
def _append(self, __object: T):
self._map_element(__object)
self._data.append(__object)
def append(self, __object: Optional[T]):
if __object is None:
return
self._append(__object)
def extend(self, __iterable: Optional[Iterable[T]]):
if __iterable is None:
return
@ -69,7 +109,7 @@ class Collection(Generic[T]):
# now the ugly part
# replace all refs of the other element with this one
_replace_all_refs(self, equal_collection)
replace_all_refs(self, equal_collection)
def contain_collection_inside(self, sub_collection: "Collection"):

View File

@ -7,6 +7,7 @@ from .metadata import Metadata
from .option import Options
from ..utils.shared import HIGHEST_ID
from ..utils.config import main_settings, logging_settings
from ..utils.functions import replace_all_refs
LOGGER = logging_settings["object_logger"]
@ -25,9 +26,6 @@ class StaticAttribute(Generic[P]):
is_upwards_collection: bool = False
class Attribute(Generic[P]):
def __init__(self, database_object: "DatabaseObject", static_attribute: StaticAttribute) -> None:
self.database_object: DatabaseObject = database_object
@ -148,7 +146,7 @@ class DatabaseObject:
return list()
def merge(self, other, override: bool = False):
def merge(self, other, override: bool = False, replace_all_refs: bool = False):
if other is None:
return
@ -171,6 +169,9 @@ class DatabaseObject:
if override or getattr(self, simple_attribute) == default_value:
setattr(self, simple_attribute, getattr(other, simple_attribute))
if replace_all_refs:
replace_all_refs(self, other)
def strip_details(self):
for collection in type(self).DOWNWARDS_COLLECTION_STRING_ATTRIBUTES:
getattr(self, collection).clear()

View File

@ -1,5 +1,25 @@
import os
from datetime import datetime
import guppy
from guppy.heapy import Path
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
def clear_console():