@@ -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