glom
glom copied to clipboard
assign with coalesce [question]
Somewhat surprisingly, glom.assign(obj, glom.Coalesce(*specs), value)
doesn't work.
Is there an easier way to get the result, e.g. something like:
def glom_assign(config: Config, spec: Union[str, glom.Coalesce], value: Any) -> None:
specs = [spec] if isinstance(spec, str) else spec.subspecs
for spec in specs:
try:
glom.glom(config, spec)
except glom.PathAccessError:
continue
glom.assign(config, spec, value)
Thanks.
Hey Radu! Interesting question. So the goal is to assign the value
to config
at the first spec
that actually exists (according to the Coalesce
call)? Is the desired use case focused on overwrites only? I notice in your function that glom_assign({}, Coalesce('key'), 'val')
would result in no change to the input, is that desired behavior?
If so, I can't think of a way to do this as a single spec using assign
/Assign
as implemented, as it only works with paths/T
objects. With a bit more detail, maybe we can come up with an enhancement PR :)
Hi Mahmoud, thanks for replying. Indeed, I only needed the overwrite use case so far, but the functionality could be extended to work something like:
def glom_assign(config: Config, spec: Union[str, glom.Coalesce], value: Any, strict: bool = True) -> None:
specs = [spec] if isinstance(spec, str) else spec.subspecs
for spec in specs:
try:
glom.glom(config, spec)
except glom.PathAccessError:
if not strict:
try:
parent, leaf = spec.rsplit(".", maxsplit=1)
except ValueError:
continue
try:
glom.glom(config, parent)
except glom.PathAccessError:
continue
glom.assign(config, spec, value)
continue
glom.assign(config, spec, value)
Hi, I'm not sure your exact use case, but the first thing that comes to mind for me:
glom.assign(obj, glom.Coalesce(*specs), value)
# instead of assign(coalesce), coalesce(assign)
glom.glom(obj, Coalesce(*[Assign(T, spec, value) for spec in specs]))
# if the only desired behavior is "try these in order, return the first one that doesn't fail", then Or can also work
glom.glom(obj, Or(*[Assign(T, spec, value) for spec in specs]))
Hi Kurt, your suggestions are close to what I want, but both bail out after the first success, and they are too readily successful (even if the path doesn't actually exist - which would correspond to what I described as non-strict behaviour in my implementation above). Anyway, thanks for the idea, it's pretty cool, I'll keep that in mind.
Oh, just re-reading this -- you want to ensure that EACH branch gets exercised, not stop executing on the first success?
strict = And(*[Assign(T, spec, value) for spec in specs])
if you want to try to execute each, but ignore failures:
best_effort = And(*[Or(Assign(T, spec, value), Val(None)) for spec in specs])