Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assign with coalesce [question] #226

Open
radugrosu opened this issue Sep 25, 2021 · 5 comments
Open

assign with coalesce [question] #226

radugrosu opened this issue Sep 25, 2021 · 5 comments

Comments

@radugrosu
Copy link

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.

@mahmoud
Copy link
Owner

mahmoud commented Sep 26, 2021

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 :)

@radugrosu
Copy link
Author

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)

@kurtbrose
Copy link
Collaborator

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]))

@radugrosu
Copy link
Author

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.

@kurtbrose
Copy link
Collaborator

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])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants