Skip to content

Commit 6c49102

Browse files
authored
Add files via upload
1 parent 7725038 commit 6c49102

1 file changed

Lines changed: 110 additions & 10 deletions

File tree

src/rheelDM/main.py

Lines changed: 110 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,28 +112,128 @@ class Section:
112112
"""
113113
Represents a single section inside an Obj.
114114
115-
Usage:
115+
Provides type-safe get/set methods, arithmetic operations for numbers
116+
and extension operations for strings, lists, sets, dicts, and Paths.
117+
118+
Usage examples:
119+
116120
sec = obj.section("user123")
117-
sec.set("name", str, "CoCo")
118-
value = sec.get("name")
121+
sec.set("score", int, 10) # 10
122+
sec.add("score", 5) # 15
123+
sec.add("score", -3) # 12
124+
sec.set("name", str, "CoCo") # "CoCo"
125+
sec.extend("name", "Bot") # "CoCoBot"
126+
sec.set("items", list, [1,2]) # [1,2]
127+
sec.extend("items", 3) # [1,2,3]
128+
sec.extend("items", [4,5]) # [1,2,3,4,5]
129+
sec.set("tags", set, {1,2}) # {1,2}
130+
sec.extend("tags", 3) # {1,2,3}
131+
sec.extend("tags", {4,5}) # {1,2,3,4,5}
132+
sec.set("config", dict, {"a":1}) # {"a": 1}
133+
sec.extend("config", {"b":2}) # {"a": 1, "b": 2}
134+
sec.set("joined", datetime.datetime.now())
135+
sec.add("joined", 3600) # adds 3600 seconds
136+
sec.set("path", Path("/tmp")) # Path("/tmp")
137+
sec.extend("path", "logs") # Path("/tmp/logs")
138+
139+
sec.delete("score") # remove key
119140
"""
120141

121142
def __init__(self, name: str):
122143
self.name = name
123144
self._items: dict[str, tuple[type, Any]] = {}
124145

125-
def set(self, key: str, typ: type, value: Any):
126-
"""Set a key with strict type validation."""
146+
def set(self, key: str, typ: type, value: Any, overwrite=True):
147+
"""
148+
Set a key with strict type validation. Mutable values (`list`, `dict`, `set`) will be deep-copied to prevent external modifications after setting.
149+
150+
`overwrite=False` will prevent overwriting existing keys, raising an error instead.
151+
"""
152+
if not overwrite and key in self._items:
153+
raise KeyError(f"{key} already exists in section {self.name}")
154+
if isinstance(value, (list, dict, set)):
155+
value = copy.deepcopy(value)
156+
127157
self._validate(value, typ)
128158
self._items[key] = (typ, value)
129159

130-
def get(self, key: str, default: Any = None):
160+
def get(self, key: str, default: Any = KeyError) -> Any:
161+
"""Retrieve a value by key. Returns a copy of default if key does not exist. Raises KeyError if key is missing and no default is provided."""
162+
val = self._items.get(key, (None, copy.deepcopy(default)))[1]
163+
if val is KeyError:
164+
raise KeyError(f"\"{key}\" does not exist in section {self.name}")
165+
else:
166+
return val
167+
168+
def delete(self, key: str):
169+
"""Remove a key from the section."""
170+
if key in self._items:
171+
del self._items[key]
172+
173+
def add(self, key: str, value: Any):
174+
"""
175+
Add or subtract a value for numbers or datetime.
176+
177+
Works for int, float, or datetime (adds seconds as timedelta).
178+
Negative numbers perform subtraction.
131179
"""
132-
Retrieve a value by key.
180+
if key not in self._items:
181+
raise KeyError(f"{key} does not exist in section {self.name}")
182+
typ, current = self._items[key]
183+
184+
if typ in (int, float):
185+
self._items[key] = (typ, current + value)
186+
elif typ is datetime:
187+
import datetime as dt
188+
self._items[key] = (typ, current + dt.timedelta(seconds=value))
189+
else:
190+
raise TypeError(f"Cannot add to type {typ}")
191+
192+
def extend(self, key: str, value: Any):
193+
"""
194+
Dynamically extend or combine values based on type:
133195
134-
If key does not exist, returns `default`.
196+
str -> concatenate
197+
list -> append item or concatenate list
198+
set -> add item or union with set
199+
dict -> merge dictionaries
200+
Path -> join with string or Path
135201
"""
136-
return self._items.get(key, (None, default))[1]
202+
if key not in self._items:
203+
raise KeyError(f"{key} does not exist in section {self.name}")
204+
typ, current = self._items[key]
205+
206+
if typ is str:
207+
if not isinstance(value, str):
208+
raise TypeError("Can only extend str with str")
209+
self._items[key] = (typ, current + value)
210+
211+
elif typ is list:
212+
if isinstance(value, list):
213+
self._items[key] = (typ, current + value)
214+
else:
215+
self._items[key] = (typ, current + [value])
216+
217+
elif typ is set:
218+
if isinstance(value, set):
219+
self._items[key] = (typ, current.union(value))
220+
else:
221+
self._items[key] = (typ, current | {value})
222+
223+
elif typ is dict:
224+
if not isinstance(value, dict):
225+
raise TypeError("Can only extend dict with dict")
226+
self._items[key] = (typ, {**current, **value})
227+
228+
elif typ is Path:
229+
from pathlib import Path
230+
if isinstance(value, (str, Path)):
231+
self._items[key] = (typ, current / value)
232+
else:
233+
raise TypeError("Can only extend Path with str or Path")
234+
235+
else:
236+
raise TypeError(f"Cannot extend type {typ}")
137237

138238
# -----------------------
139239
# Type Validation
@@ -333,4 +433,4 @@ def from_dict(cls, data: dict) -> Obj:
333433
section = obj.section(section_name)
334434
for key, (typ, value) in items.items():
335435
section.set(key, typ, value)
336-
return obj
436+
return obj

0 commit comments

Comments
 (0)