-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update References with pointer to target object (#34)
* Update References with pointer to target object * Suppress internal trailing-underscore fields in repr * Fix handling of multiple references to the same ID
- Loading branch information
Showing
3 changed files
with
127 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import dataclasses | ||
import weakref | ||
from typing import Any, Dict, List | ||
|
||
from .model.simple_types import LSID | ||
from .model.reference import Reference | ||
|
||
|
||
def collect_references(value: Any) -> List[Reference]: | ||
"""Return a list of all References contained in value. | ||
Recursively walks all dataclass fields and iterates over lists. The base | ||
case is when value is either a Reference object, or an uninteresting type | ||
that we don't need to inspect further. | ||
""" | ||
references: List[Reference] = [] | ||
if isinstance(value, Reference): | ||
references.append(value) | ||
elif isinstance(value, list): | ||
for v in value: | ||
references.extend(collect_references(v)) | ||
elif dataclasses.is_dataclass(value): | ||
for f in dataclasses.fields(value): | ||
references.extend(collect_references(getattr(value, f.name))) | ||
# Do nothing for uninteresting types | ||
return references | ||
|
||
|
||
def collect_ids(value: Any) -> Dict[LSID, Any]: | ||
"""Return a map of all model objects contained in value, keyed by id. | ||
Recursively walks all dataclass fields and iterates over lists. The base | ||
case is when value is neither a dataclass nor a list. | ||
""" | ||
ids: Dict[LSID, Any] = {} | ||
if isinstance(value, list): | ||
for v in value: | ||
ids.update(collect_ids(v)) | ||
elif dataclasses.is_dataclass(value): | ||
for f in dataclasses.fields(value): | ||
if f.name == "id" and not isinstance(value, Reference): | ||
# We don't need to recurse on the id string, so just record it | ||
# and move on. | ||
ids[value.id] = value | ||
else: | ||
ids.update(collect_ids(getattr(value, f.name))) | ||
# Do nothing for uninteresting types. | ||
return ids |