-
Notifications
You must be signed in to change notification settings - Fork 18
Implement _collection_ref to link objects to their NumberedObjectCollection parent #867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
83b61f7
ae3e6c1
622632d
3ca4d87
de5c480
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |
| import itertools | ||
| from typing import Union | ||
| from numbers import Integral | ||
| import weakref | ||
|
|
||
| from montepy.mcnp_object import MCNP_Object, InitInput | ||
| import montepy | ||
|
|
@@ -14,20 +15,30 @@ | |
| def _number_validator(self, number): | ||
| if number < 0: | ||
| raise ValueError("number must be >= 0") | ||
| if self._problem: | ||
| obj_map = montepy.MCNP_Problem._NUMBERED_OBJ_MAP | ||
| try: | ||
| collection_type = obj_map[type(self)] | ||
| except KeyError as e: | ||
| found = False | ||
| for obj_class in obj_map: | ||
| if isinstance(self, obj_class): | ||
| collection_type = obj_map[obj_class] | ||
| found = True | ||
| break | ||
| if not found: | ||
| raise e | ||
| collection = getattr(self._problem, collection_type.__name__.lower()) | ||
|
|
||
| # Only validate against collection if linked to a problem | ||
| if self._problem is not None: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can skip a level of |
||
| if self._collection is not None: | ||
| collection = self._collection | ||
| else: | ||
| # Find collection via _problem | ||
| obj_map = montepy.MCNP_Problem._NUMBERED_OBJ_MAP | ||
| collection_type = obj_map.get(type(self)) | ||
|
|
||
| if collection_type is None: | ||
| # Finding via inheritance | ||
| for obj_class in obj_map: | ||
| if isinstance(self, obj_class): | ||
| collection_type = obj_map[obj_class] | ||
| break | ||
|
|
||
| if collection_type is not None: | ||
| collection = getattr(self._problem, collection_type.__name__.lower()) | ||
| else: | ||
| raise TypeError( | ||
| f"Could not find collection type for {type(self).__name__} in problem." | ||
| ) | ||
|
|
||
|
Comment on lines
+23
to
+41
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can delete this whole branch. I don't see a case where |
||
| collection.check_number(number) | ||
| collection._update_number(self.number, number, self) | ||
|
|
||
|
|
@@ -59,6 +70,7 @@ def __init__( | |
| self._number = self._generate_default_node(int, -1) | ||
| super().__init__(input, parser) | ||
| self._load_init_num(number) | ||
| self._collection_ref = None | ||
|
|
||
| def _load_init_num(self, number): | ||
| if number is not None: | ||
|
|
@@ -123,6 +135,24 @@ def _add_children_objs(self, problem): | |
| except (TypeError, AssertionError): | ||
| prob_collect.append(child_collect) | ||
|
|
||
| @property | ||
| def _collection(self): | ||
| """Returns the parent collection this object belongs to, if any.""" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to also reimplement
This will be the secondary way to update this ref, besides appending.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though this may lead to weird situations where objects are linked to problems without being in the collections. I'm going to belay this until I read further. |
||
| if self._collection_ref is not None: | ||
| return self._collection_ref() | ||
| return None | ||
|
|
||
| def __getstate__(self): | ||
| state = super().__getstate__() | ||
| # Remove _collection_ref weakref as it can't be pickled | ||
| if "_collection_ref" in state: | ||
| del state["_collection_ref"] | ||
|
Comment on lines
+148
to
+149
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good code. Could also use |
||
| return state | ||
|
|
||
| def __setstate__(self, crunchy_data): | ||
| crunchy_data["_collection_ref"] = None | ||
| super().__setstate__(crunchy_data) | ||
|
|
||
| def clone(self, starting_number=None, step=None): | ||
| """Create a new independent instance of this object with a new number. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,6 +178,7 @@ def __init__( | |
| ) | ||
| ) | ||
| self.__num_cache[obj.number] = obj | ||
| self._link_to_collection(obj) | ||
| self._objects = objects | ||
|
|
||
| def link_to_problem(self, problem): | ||
|
|
@@ -198,13 +199,38 @@ def link_to_problem(self, problem): | |
| self._problem_ref = weakref.ref(problem) | ||
| for obj in self: | ||
| obj.link_to_problem(problem) | ||
| # the _collection_ref that points to the main cells collection. | ||
| if problem is not None: | ||
| existing_coll = obj._collection | ||
| if existing_coll is None or existing_coll._problem is not problem: | ||
| self._link_to_collection(obj) | ||
|
|
||
| @property | ||
| def _problem(self): | ||
| if self._problem_ref is not None: | ||
| return self._problem_ref() | ||
| return None | ||
|
|
||
| def _link_to_collection(self, obj): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry this is the flip of what I meant. I think these should be functions of
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason being is objects should respect each other's private attributes. I am ok with calling each other's private functions as that in my mind is more marking it as internal use only. |
||
| """Links the given object to this collection via a weakref. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| obj : Numbered_MCNP_Object | ||
| The object to link to this collection. | ||
| """ | ||
| obj._collection_ref = weakref.ref(self) | ||
|
|
||
| def _unlink_from_collection(self, obj): | ||
| """Unlinks the given object from this collection. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| obj : Numbered_MCNP_Object | ||
| The object to unlink from this collection. | ||
| """ | ||
| obj._collection_ref = None | ||
|
|
||
| def __getstate__(self): | ||
| state = self.__dict__.copy() | ||
| weakref_key = "_problem_ref" | ||
|
|
@@ -341,10 +367,8 @@ def extend(self, other_list): | |
| ) | ||
| if obj.number in nums: | ||
| raise NumberConflictError( | ||
| ( | ||
| f"When adding to {type(self).__name__} there was a number collision due to " | ||
| f"adding {obj} which conflicts with {self[obj.number]}" | ||
| ) | ||
| f"When adding to {type(self).__name__} there was a number collision due to " | ||
| f"adding {obj} which conflicts with existing object number {obj.number}" | ||
| ) | ||
| nums.add(obj.number) | ||
| for obj in other_list: | ||
|
|
@@ -496,6 +520,7 @@ def __internal_append(self, obj, **kwargs): | |
| ) | ||
| self.__num_cache[obj.number] = obj | ||
| self._objects.append(obj) | ||
| self._link_to_collection(obj) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be |
||
| self._append_hook(obj, **kwargs) | ||
| if self._problem: | ||
| obj.link_to_problem(self._problem) | ||
|
|
@@ -507,6 +532,7 @@ def __internal_delete(self, obj, **kwargs): | |
| """ | ||
| self.__num_cache.pop(obj.number, None) | ||
| self._objects.remove(obj) | ||
| self._unlink_from_collection(obj) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also |
||
| self._delete_hook(obj, **kwargs) | ||
|
|
||
| def add(self, obj: Numbered_MCNP_Object): | ||
|
|
@@ -613,6 +639,7 @@ def append_renumber(self, obj, step=1): | |
| number = obj.number if obj.number > 0 else 1 | ||
| if self._problem: | ||
| obj.link_to_problem(self._problem) | ||
| self._unlink_from_collection(obj) | ||
| try: | ||
| self.append(obj) | ||
| except (NumberConflictError, ValueError) as e: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still not the desired behavior in all cases. Generally they should be added to the same problem. I know that this can lead to a number collision in an edge case with hypothesis, that I haven't fixed yet, and I just ignore it with
rm -r .hypothesis. I think that's a separate issue from this, and I need to open a bug report. Try it without these lines, and with purging the.hypothesisfolder.