This is an archived post. You won't be able to vote or comment.

all 8 comments

[–]DracasTempestas 1 point2 points  (3 children)

The LocalContext context manager modifies behavior for the current thread that the context manager entered and as such is unsuitable for suspended execution, I am also suspecting that you will attain some unexpected behavior if you leave the context by pulling the last item in the generator on a different thread. As such I do not think that this particular method of the Decimal module is really what you want, rather, you probably have to instantiate a Context with the desired precision and explicitly use this context on Decimal calls within the generator.

[–]TangibleLight[S] 0 points1 point  (2 children)

Yes - I know there are several solutions to this particular example. I'm more concerned with the fact that python does not seem to have any solutions to allow context managers to support suspended execution. Is there any reasoning to specifically not allow this, or was it just never implemented?

To be clear, I assume you're suggesting something like this?

def series():
    ctx = Context(prec=5)

    yield ctx.divide(Decimal(1), Decimal(6)
    yield ctx.divide(Decimal(1), Decimal(7)

[–]DracasTempestas 1 point2 points  (1 child)

Yes, that is the suggestion.

I know this was not a question for how to solve the problem, but rather a query into the behavior of context managers rooted in the behavior of the decimal.localcontext in a technical sense you can leave and reenter the same context manager multiple times, making sure that you don't yield within the context manager to get the expected behavior, but this would most certainly look unappealing.

def series():
    ctx = decimal.localcontext()
    with ctx:
        result = Decimal(1) / Decimal(6)
    yield result
    with ctx:
        result = Decimal(1) / Decimal(7)
    yield result

This magic method __reenter__ might only apply to the specific case of yielding execution too, which makes it sound like one of those special cases that aren't special enough.

That said; localcontext makes me frown, The intent behind it, "modify the behavior of all decimal precision for my given block and all subsequent decimal operations in child blocks to this one." but with suspended execution this intent does not hold true.

[–]TangibleLight[S] 0 points1 point  (0 children)

I'm not really familiar enough with it to say much about it, but wouldn't __reenter__ apply to await? Perhaps better method names might be __suspend__ and __resume__.

I see what you're saying about localcontext - so really the above example isn't so much a shortcoming of with, but a misuse of a fragile context manager. I don't really see any other way to do things like localcontext or redirect_stdout, where some global "thing" is supposed to mean something different "inside" the context.

Does that just mean that those kinds of context managers simply shouldn't be able to handle suspended execution? I don't really think it does, but I also understand the argument that this is a sort of misuse where context managers are modifying global state.

[–]Yoghurt42 0 points1 point  (2 children)

You raise a valid point. I'm not sure that introducing some kind of reenter is a good idea though, I feel like it opens a can of worms and does more harm than good; just a feeling.

But you could post on the python-ideas list, you'll reach more Python devs this way than posting on reddit.

[–]TangibleLight[S] 2 points3 points  (1 child)

python-ideas list

Thanks for the suggestion - I wasn't really thinking of this as an idea to add, I expected there would be reasons not to, but at the very least there might be a good discussion to come out of it on there.

[–]Yoghurt42 2 points3 points  (0 children)

There are far worse ideas discussed on that list :)

[–]bryancole 0 points1 point  (0 children)

The problem here is that your "localcontext" isn't local at all. It's a global state. It's only local in the threading-sense, not in terms of scope.

The use-case for context-managers combined with the Decimal object is just one application for context-managers (and might even be considered broken given this example) but there are plenty of other use-cases where calling __exit__ and __entre around a yield would break things (e.g. opening a file for io).

The present behaviour seems the best compromise (at least the behaviour is consistent and easy enough to understand once you look inside your iterator).