Skip to content

Commit b3724b0

Browse files
authored
50 consolidate code for unique name generation (#51)
1 parent 2ffe5d6 commit b3724b0

File tree

10 files changed

+113
-122
lines changed

10 files changed

+113
-122
lines changed

src/easyscience/Objects/ObjectClasses.py

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class BasedBase(ComponentSerializer):
4141
def __init__(self, name: str, interface: Optional[iF] = None, unique_name: Optional[str] = None):
4242
self._global_object = global_object
4343
if unique_name is None:
44-
unique_name = self._unique_name_generator()
44+
unique_name = self._global_object.generate_unique_name(self.__class__.__name__)
4545
self._unique_name = unique_name
4646
self._name = name
47-
self._global_object.map.add_vertex(self, obj_type="created")
47+
self._global_object.map.add_vertex(self, obj_type='created')
4848
self.interface = interface
4949
self.user_data: dict = {}
5050

@@ -69,16 +69,16 @@ def __reduce__(self):
6969

7070
@property
7171
def unique_name(self) -> str:
72-
""" Get the unique name of the object."""
72+
"""Get the unique name of the object."""
7373
return self._unique_name
7474

7575
@unique_name.setter
7676
def unique_name(self, new_unique_name: str):
77-
""" Set a new unique name for the object. The old name is still kept in the map.
78-
77+
"""Set a new unique name for the object. The old name is still kept in the map.
78+
7979
:param new_unique_name: New unique name for the object"""
8080
if not isinstance(new_unique_name, str):
81-
raise TypeError("Unique name has to be a string.")
81+
raise TypeError('Unique name has to be a string.')
8282
self._unique_name = new_unique_name
8383
self._global_object.map.add_vertex(self)
8484

@@ -206,18 +206,6 @@ def get_fit_parameters(self) -> Union[List[Parameter], List[NewParameter]]:
206206
fit_list.append(item)
207207
return fit_list
208208

209-
def _unique_name_generator(self) -> str:
210-
"""
211-
Generate a generic unique name for the object using the class name and a global iterator.
212-
"""
213-
class_name = self.__class__.__name__
214-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
215-
name = class_name + "_" + iterator_string
216-
while name in self._global_object.map.vertices():
217-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
218-
name = class_name + "_" + iterator_string
219-
return name
220-
221209
def __dir__(self) -> Iterable[str]:
222210
"""
223211
This creates auto-completion and helps out in iPython notebooks.
@@ -226,7 +214,6 @@ def __dir__(self) -> Iterable[str]:
226214
"""
227215
new_class_objs = list(k for k in dir(self.__class__) if not k.startswith('_'))
228216
return sorted(new_class_objs)
229-
230217

231218

232219
if TYPE_CHECKING:
@@ -267,7 +254,7 @@ def __init__(
267254
self._kwargs = kwargs
268255
for key in kwargs.keys():
269256
if key in known_keys:
270-
raise AttributeError("Kwargs cannot overwrite class attributes in BaseObj.")
257+
raise AttributeError('Kwargs cannot overwrite class attributes in BaseObj.')
271258
if issubclass(type(kwargs[key]), (BasedBase, Descriptor, DescriptorBase)) or 'BaseCollection' in [
272259
c.__name__ for c in type(kwargs[key]).__bases__
273260
]:

src/easyscience/Objects/Variable.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def __init__(
109109
if not hasattr(self, '_args'):
110110
self._args = {'value': None, 'units': ''}
111111
if unique_name is None:
112-
unique_name = self._unique_name_generator()
112+
unique_name = self._global_object.generate_unique_name(self.__class__.__name__)
113113
self._unique_name = unique_name
114114
self.name = name
115115
# Let the collective know we've been assimilated
@@ -190,14 +190,14 @@ def unique_name(self) -> str:
190190
:return: Unique name of this object
191191
"""
192192
return self._unique_name
193-
193+
194194
@unique_name.setter
195195
def unique_name(self, new_unique_name: str):
196-
""" Set a new unique name for the object. The old name is still kept in the map.
197-
196+
"""Set a new unique name for the object. The old name is still kept in the map.
197+
198198
:param new_unique_name: New unique name for the object"""
199199
if not isinstance(new_unique_name, str):
200-
raise TypeError("Unique name has to be a string.")
200+
raise TypeError('Unique name has to be a string.')
201201
self._unique_name = new_unique_name
202202
self._global_object.map.add_vertex(self)
203203

@@ -363,19 +363,6 @@ def convert_unit(self, unit_str: str):
363363
self._args['value'] = self.raw_value
364364
self._args['units'] = str(self.unit)
365365

366-
def _unique_name_generator(self) -> str:
367-
"""
368-
Generate a generic unique name for the object using the class name and a global iterator.
369-
"""
370-
class_name = self.__class__.__name__
371-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
372-
name = class_name + "_" + iterator_string
373-
while name in self._global_object.map.vertices():
374-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
375-
name = class_name + "_" + iterator_string
376-
return name
377-
378-
379366
# @cached_property
380367
@property
381368
def compatible_units(self) -> List[str]:

src/easyscience/Objects/new_variable/descriptor_base.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def __init__(
5555
"""
5656

5757
if unique_name is None:
58-
unique_name = self._unique_name_generator()
58+
unique_name = self._global_object.generate_unique_name(self.__class__.__name__)
5959
self._unique_name = unique_name
6060

6161
if not isinstance(name, str):
@@ -178,29 +178,17 @@ def unique_name(self) -> str:
178178
:return: Unique name of this object
179179
"""
180180
return self._unique_name
181-
181+
182182
@unique_name.setter
183183
def unique_name(self, new_unique_name: str):
184-
""" Set a new unique name for the object. The old name is still kept in the map.
185-
184+
"""Set a new unique name for the object. The old name is still kept in the map.
185+
186186
:param new_unique_name: New unique name for the object"""
187187
if not isinstance(new_unique_name, str):
188-
raise TypeError("Unique name has to be a string.")
188+
raise TypeError('Unique name has to be a string.')
189189
self._unique_name = new_unique_name
190190
self._global_object.map.add_vertex(self)
191191

192-
def _unique_name_generator(self) -> str:
193-
"""
194-
Generate a generic unique name for the object using the class name and a global iterator.
195-
"""
196-
class_name = self.__class__.__name__
197-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
198-
name = class_name + "_" + iterator_string
199-
while name in self._global_object.map.vertices():
200-
iterator_string = str(self._global_object.map._get_name_iterator(class_name))
201-
name = class_name + "_" + iterator_string
202-
return name
203-
204192
@property
205193
@abc.abstractmethod
206194
def value(self) -> Any:
@@ -218,6 +206,6 @@ def __repr__(self) -> str:
218206
def __copy__(self) -> DescriptorBase:
219207
"""Return a copy of the object."""
220208
temp = self.as_dict()
221-
temp["unique_name"] = None
209+
temp['unique_name'] = None
222210
new_obj = self.__class__.from_dict(temp)
223211
return new_obj

src/easyscience/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
global_object.instantiate_stack()
1818
global_object.stack.enabled = False
1919

20+
2021
# alias for global_object, remove later
2122
def __getattr__(name):
2223
if name == 'borg':
23-
warnings.warn("The 'borg' has been renamed to 'global_object', this alias will be deprecated in the future", DeprecationWarning) # noqa: E501
24+
warnings.warn(
25+
"The 'borg' has been renamed to 'global_object', this alias will be deprecated in the future", DeprecationWarning
26+
) # noqa: E501
2427
print("The 'borg' has been renamed to 'global_object', this alias will be deprecated in the future")
25-
return global_object
28+
return global_object

src/easyscience/global_object/global_object.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# SPDX-License-Identifier: BSD-3-Clause
33
# © 2021-2023 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
44

5-
__author__ = "github.com/wardsimon"
6-
__version__ = "0.1.0"
5+
__author__ = 'github.com/wardsimon'
6+
__version__ = '0.1.0'
77

88
from easyscience.Utils.classUtils import singleton
99

@@ -47,3 +47,23 @@ def instantiate_stack(self):
4747
from easyscience.global_object.undo_redo import UndoStack
4848

4949
self.stack = UndoStack()
50+
51+
def generate_unique_name(self, name_prefix: str) -> str:
52+
"""
53+
Generate a generic unique name for the object using the class name and a global iterator.
54+
Names are in the format `name_prefix_0`, `name_prefix_1`, `name_prefix_2`, etc.
55+
56+
:param name_prefix: The prefix to be used for the name
57+
"""
58+
names_with_prefix = [name for name in self.map.vertices() if name.startswith(name_prefix + '_')]
59+
if names_with_prefix:
60+
name_with_prefix_count = [0]
61+
for name in names_with_prefix:
62+
# Strip away the prefix and trailing _
63+
name_without_prefix = name.replace(name_prefix + '_', '')
64+
if name_without_prefix.isdecimal():
65+
name_with_prefix_count.append(int(name_without_prefix))
66+
unique_name = f'{name_prefix}_{max(name_with_prefix_count) + 1}'
67+
else:
68+
unique_name = f'{name_prefix}_0'
69+
return unique_name

src/easyscience/global_object/map.py

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
# SPDX-License-Identifier: BSD-3-Clause
33
# © 2021-2023 Contributors to the EasyScience project <https://github.com/easyScience/EasyScience
44

5-
__author__ = "github.com/wardsimon"
6-
__version__ = "0.1.0"
5+
__author__ = 'github.com/wardsimon'
6+
__version__ = '0.1.0'
77

8+
import gc
89
import sys
910
import weakref
1011
from typing import List
@@ -13,22 +14,22 @@
1314
class _EntryList(list):
1415
def __init__(self, *args, my_type=None, **kwargs):
1516
super(_EntryList, self).__init__(*args, **kwargs)
16-
self.__known_types = {"argument", "created", "created_internal", "returned"}
17+
self.__known_types = {'argument', 'created', 'created_internal', 'returned'}
1718
self.finalizer = None
1819
self._type = []
1920
if my_type in self.__known_types:
2021
self._type.append(my_type)
2122

2223
def __repr__(self) -> str:
23-
s = "Map entry of type: "
24+
s = 'Map entry of type: '
2425
if self._type:
25-
s += ", ".join(self._type)
26+
s += ', '.join(self._type)
2627
else:
27-
s += "Undefined"
28-
s += ". With"
28+
s += 'Undefined'
29+
s += '. With'
2930
if self.finalizer is None:
30-
s += "out"
31-
s += "a finalizer."
31+
s += 'out'
32+
s += 'a finalizer.'
3233
return s
3334

3435
def __delitem__(self, key):
@@ -53,19 +54,19 @@ def type(self, value: str):
5354

5455
@property
5556
def is_argument(self) -> bool:
56-
return "argument" in self._type
57+
return 'argument' in self._type
5758

5859
@property
5960
def is_created(self) -> bool:
60-
return "created" in self._type
61+
return 'created' in self._type
6162

6263
@property
6364
def is_created_internal(self) -> bool:
64-
return "created_internal" in self._type
65+
return 'created_internal' in self._type
6566

6667
@property
6768
def is_returned(self) -> bool:
68-
return "returned" in self._type
69+
return 'returned' in self._type
6970

7071

7172
class Map:
@@ -74,8 +75,6 @@ def __init__(self):
7475
self._store = weakref.WeakValueDictionary()
7576
# A dict with object names as keys and a list of their object types as values, with weak references
7677
self.__type_dict = {}
77-
# A dictionary of class names and their corresponding default name_generator iterators
78-
self._name_iterator_dict = {}
7978

8079
def vertices(self) -> List[str]:
8180
"""returns the vertices of a map"""
@@ -87,36 +86,28 @@ def edges(self):
8786

8887
@property
8988
def argument_objs(self) -> List[str]:
90-
return self._nested_get("argument")
89+
return self._nested_get('argument')
9190

9291
@property
9392
def created_objs(self) -> List[str]:
94-
return self._nested_get("created")
93+
return self._nested_get('created')
9594

9695
@property
9796
def created_internal(self) -> List[str]:
98-
return self._nested_get("created_internal")
97+
return self._nested_get('created_internal')
9998

10099
@property
101100
def returned_objs(self) -> List[str]:
102-
return self._nested_get("returned")
101+
return self._nested_get('returned')
103102

104103
def _nested_get(self, obj_type: str) -> List[str]:
105104
"""Access a nested object in root by key sequence."""
106105
return [key for key, item in self.__type_dict.items() if obj_type in item.type]
107106

108-
109-
def _get_name_iterator(self, class_name: str) -> int:
110-
"""Get the iterator for the name generator for a class"""
111-
iterator = self._name_iterator_dict.setdefault(class_name, 0)
112-
self._name_iterator_dict[class_name] += 1
113-
return iterator
114-
115-
116107
def get_item_by_key(self, item_id: str) -> object:
117108
if item_id in self._store.keys():
118109
return self._store[item_id]
119-
raise ValueError("Item not in map.")
110+
raise ValueError('Item not in map.')
120111

121112
def is_known(self, vertex: object) -> bool:
122113
# All objects should have a 'unique_name' attribute
@@ -137,19 +128,17 @@ def change_type(self, obj, new_type: str):
137128
def add_vertex(self, obj: object, obj_type: str = None):
138129
name = obj.unique_name
139130
if name in self._store.keys():
140-
raise ValueError(f"Object name {name} already exists in the graph.")
131+
raise ValueError(f'Object name {name} already exists in the graph.')
141132
self._store[name] = obj
142133
self.__type_dict[name] = _EntryList() # Add objects type to the list of types
143-
self.__type_dict[name].finalizer = weakref.finalize(
144-
self._store[name], self.prune, name
145-
)
134+
self.__type_dict[name].finalizer = weakref.finalize(self._store[name], self.prune, name)
146135
self.__type_dict[name].type = obj_type
147136

148137
def add_edge(self, start_obj: object, end_obj: object):
149138
if start_obj.unique_name in self.__type_dict.keys():
150139
self.__type_dict[start_obj.unique_name].append(end_obj.unique_name)
151140
else:
152-
raise AttributeError("Start object not in map.")
141+
raise AttributeError('Start object not in map.')
153142

154143
def get_edges(self, start_obj) -> List[str]:
155144
if start_obj.unique_name in self.__type_dict.keys():
@@ -176,10 +165,7 @@ def prune_vertex_from_edge(self, parent_obj, child_obj):
176165
return
177166
vertex2 = child_obj.unique_name
178167

179-
if (
180-
vertex1 in self.__type_dict.keys()
181-
and vertex2 in self.__type_dict[vertex1]
182-
):
168+
if vertex1 in self.__type_dict.keys() and vertex2 in self.__type_dict[vertex1]:
183169
del self.__type_dict[vertex1][self.__type_dict[vertex1].index(vertex2)]
184170

185171
def prune(self, key: str):
@@ -282,20 +268,18 @@ def is_connected(self, vertices_encountered=None, start_vertex=None) -> bool:
282268
vertices_encountered.add(start_vertex)
283269
if len(vertices_encountered) != len(vertices):
284270
for vertex in graph[start_vertex]:
285-
if vertex not in vertices_encountered and self.is_connected(
286-
vertices_encountered, vertex
287-
):
271+
if vertex not in vertices_encountered and self.is_connected(vertices_encountered, vertex):
288272
return True
289273
else:
290274
return True
291275
return False
292276

293277
def _clear(self):
294-
""" Reset the map to an empty state. """
295-
self._store = weakref.WeakValueDictionary()
278+
"""Reset the map to an empty state."""
279+
for vertex in self.vertices():
280+
self.prune(vertex)
281+
gc.collect()
296282
self.__type_dict = {}
297-
self._name_iterator_dict = {}
298283

299284
def __repr__(self) -> str:
300-
return f"Map object of {len(self._store)} vertices."
301-
285+
return f'Map object of {len(self._store)} vertices.'

0 commit comments

Comments
 (0)