Skip to content

Commit 19f354d

Browse files
committed
feat: Allow setting and deleting parameters within container
1 parent e71e541 commit 19f354d

File tree

2 files changed

+76
-15
lines changed

2 files changed

+76
-15
lines changed

src/_griffe/models.py

+43-15
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ def __init__(
182182
"""Initialize the parameter.
183183
184184
Parameters:
185-
name: The parameter name.
185+
name: The parameter name, without leading stars (`*` or `**`).
186186
annotation: The parameter annotation, if any.
187187
kind: The parameter kind.
188188
default: The parameter default, if any.
@@ -266,31 +266,61 @@ def __init__(self, *parameters: Parameter) -> None:
266266
Parameters:
267267
*parameters: The initial parameters to add to the container.
268268
"""
269-
self._parameters_list: list[Parameter] = []
270-
self._parameters_dict: dict[str, Parameter] = {}
271-
for parameter in parameters:
272-
self.add(parameter)
269+
self._params: list[Parameter] = list(parameters)
273270

274271
def __repr__(self) -> str:
275-
return f"Parameters({', '.join(repr(param) for param in self._parameters_list)})"
272+
return f"Parameters({', '.join(repr(param) for param in self._params)})"
276273

277274
def __getitem__(self, name_or_index: int | str) -> Parameter:
278275
"""Get a parameter by index or name."""
279276
if isinstance(name_or_index, int):
280-
return self._parameters_list[name_or_index]
281-
return self._parameters_dict[name_or_index.lstrip("*")]
277+
return self._params[name_or_index]
278+
name = name_or_index.lstrip("*")
279+
try:
280+
return next(param for param in self._params if param.name == name)
281+
except StopIteration as error:
282+
raise KeyError(f"parameter {name_or_index} not found") from error
283+
284+
def __setitem__(self, name_or_index: int | str, parameter: Parameter) -> None:
285+
"""Set a parameter by index or name."""
286+
if isinstance(name_or_index, int):
287+
self._params[name_or_index] = parameter
288+
else:
289+
name = name_or_index.lstrip("*")
290+
try:
291+
index = next(idx for idx, param in enumerate(self._params) if param.name == name)
292+
except StopIteration:
293+
self._params.append(parameter)
294+
else:
295+
self._params[index] = parameter
296+
297+
def __delitem__(self, name_or_index: int | str) -> None:
298+
"""Delete a parameter by index or name."""
299+
if isinstance(name_or_index, int):
300+
del self._params[name_or_index]
301+
else:
302+
name = name_or_index.lstrip("*")
303+
try:
304+
index = next(idx for idx, param in enumerate(self._params) if param.name == name)
305+
except StopIteration as error:
306+
raise KeyError(f"parameter {name_or_index} not found") from error
307+
del self._params[index]
282308

283309
def __len__(self):
284310
"""The number of parameters."""
285-
return len(self._parameters_list)
311+
return len(self._params)
286312

287313
def __iter__(self):
288314
"""Iterate over the parameters, in order."""
289-
return iter(self._parameters_list)
315+
return iter(self._params)
290316

291317
def __contains__(self, param_name: str):
292318
"""Whether a parameter with the given name is present."""
293-
return param_name.lstrip("*") in self._parameters_dict
319+
try:
320+
next(param for param in self._params if param.name == param_name.lstrip("*"))
321+
except StopIteration:
322+
return False
323+
return True
294324

295325
def add(self, parameter: Parameter) -> None:
296326
"""Add a parameter to the container.
@@ -301,11 +331,9 @@ def add(self, parameter: Parameter) -> None:
301331
Raises:
302332
ValueError: When a parameter with the same name is already present.
303333
"""
304-
if parameter.name not in self._parameters_dict:
305-
self._parameters_dict[parameter.name] = parameter
306-
self._parameters_list.append(parameter)
307-
else:
334+
if parameter.name in self:
308335
raise ValueError(f"parameter {parameter.name} already present")
336+
self._params.append(parameter)
309337

310338

311339
class Object(ObjectAliasMixin):

tests/test_models.py

+33
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
GriffeLoader,
1414
Module,
1515
NameResolutionError,
16+
Parameter,
17+
Parameters,
1618
module_vtree,
1719
temporary_inspected_module,
1820
temporary_pypackage,
@@ -493,3 +495,34 @@ def method(self):
493495
assert module["Class.method"].resolve("imported") == "imported"
494496
assert module["Class.method"].resolve("class_attribute") == "module.Class.class_attribute"
495497
assert module["Class.method"].resolve("instance_attribute") == "module.Class.instance_attribute"
498+
499+
500+
def test_set_parameters() -> None:
501+
"""We can set parameters."""
502+
parameters = Parameters()
503+
# Does not exist yet.
504+
parameters["x"] = Parameter(name="x")
505+
assert "x" in parameters
506+
# Already exists, by name.
507+
parameters["x"] = Parameter(name="x")
508+
assert "x" in parameters
509+
assert len(parameters) == 1
510+
# Already exists, by index.
511+
parameters[0] = Parameter(name="y")
512+
assert "y" in parameters
513+
assert len(parameters) == 1
514+
515+
516+
def test_delete_parameters() -> None:
517+
"""We can delete parameters."""
518+
parameters = Parameters()
519+
# By name.
520+
parameters["x"] = Parameter(name="x")
521+
del parameters["x"]
522+
assert "x" not in parameters
523+
assert len(parameters) == 0
524+
# By index.
525+
parameters["x"] = Parameter(name="x")
526+
del parameters[0]
527+
assert "x" not in parameters
528+
assert len(parameters) == 0

0 commit comments

Comments
 (0)