Allow with block (context manager) to return a value (e.g. to use as a builder)
it would be nice to be able to use the "with" block syntax to do things like implement a builder. To do this, the with block must be able to return a final value to the surrounding context ass part of its __exit__ behavior. obj = with builder() as b: … This would be a little like what can be done in Ruby by passing a {…} block argument to a function.
Could you supply some real life examples of the proposed feature? A thing to consider here is that the with block in python doesn't introduce a scope so after: with foo() as bar: a = 2 b = 3 now bar, a and b are all available in the scope.
On 13 Oct 2019, at 15:23, Steve Jorgensen <stevej@stevej.name> wrote:
it would be nice to be able to use the "with" block syntax to do things like implement a builder. To do this, the with block must be able to return a final value to the surrounding context ass part of its __exit__ behavior.
obj = with builder() as b: …
This would be a little like what can be done in Ruby by passing a {…} block argument to a function. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/A7VPZ7... Code of Conduct: http://python.org/psf/codeofconduct/
Here's a more fleshed-out example of the kind of usage I have in mind. Note that this does not require `with` to have any affect on variable scope. The object returned from the context manager's __enter__ method is all the context that is needed or wanted for this pattern. restaurant_choices = with build_choices('Restaurant') as cb: # Get of nonexistent attr auto-generates matching entry. cb.BURGER_KING cb.FIVE_GUYS # Returned value is a ChoiceItem that implements __mod__ to return a # new ChoiceItem instance w/ substituted label. cb.MCDONALDS %= "McDonald's" print(restaurant_choices) # RestaurantChoices( # ( # ('BURGER_KING', 'Burger King'), # ('FIVE_GUYS', 'Five Guys'), # ('MCDONALDS', "McDonald's"))) print(RestaurantChoices.BURGER_KING) # Prints 'BURGER_KING' print(RestaurantChoices.value_labels['FIVE_GUYS']) # Prints 'Five Guys'
Why not just: with build_choices('Restaurant') as restaurant_choices : ... print(restaurant_choices) On Sun, Oct 13, 2019, 2:49 PM Steve Jorgensen <stevej@stevej.name> wrote:
Here's a more fleshed-out example of the kind of usage I have in mind. Note that this does not require `with` to have any affect on variable scope. The object returned from the context manager's __enter__ method is all the context that is needed or wanted for this pattern.
restaurant_choices = with build_choices('Restaurant') as cb: # Get of nonexistent attr auto-generates matching entry. cb.BURGER_KING cb.FIVE_GUYS # Returned value is a ChoiceItem that implements __mod__ to return a # new ChoiceItem instance w/ substituted label. cb.MCDONALDS %= "McDonald's"
print(restaurant_choices) # RestaurantChoices( # ( # ('BURGER_KING', 'Burger King'), # ('FIVE_GUYS', 'Five Guys'), # ('MCDONALDS', "McDonald's")))
print(RestaurantChoices.BURGER_KING) # Prints 'BURGER_KING'
print(RestaurantChoices.value_labels['FIVE_GUYS']) # Prints 'Five Guys' _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZPZ47K... Code of Conduct: http://python.org/psf/codeofconduct/
Or if the context object needs different behavior, stick the "return value" in an attribute like restaurant_choices.data. On Sun, Oct 13, 2019, 2:56 PM David Mertz <mertz@gnosis.cx> wrote:
Why not just:
with build_choices('Restaurant') as restaurant_choices : ...
print(restaurant_choices)
On Sun, Oct 13, 2019, 2:49 PM Steve Jorgensen <stevej@stevej.name> wrote:
Here's a more fleshed-out example of the kind of usage I have in mind. Note that this does not require `with` to have any affect on variable scope. The object returned from the context manager's __enter__ method is all the context that is needed or wanted for this pattern.
restaurant_choices = with build_choices('Restaurant') as cb: # Get of nonexistent attr auto-generates matching entry. cb.BURGER_KING cb.FIVE_GUYS # Returned value is a ChoiceItem that implements __mod__ to return a # new ChoiceItem instance w/ substituted label. cb.MCDONALDS %= "McDonald's"
print(restaurant_choices) # RestaurantChoices( # ( # ('BURGER_KING', 'Burger King'), # ('FIVE_GUYS', 'Five Guys'), # ('MCDONALDS', "McDonald's")))
print(RestaurantChoices.BURGER_KING) # Prints 'BURGER_KING'
print(RestaurantChoices.value_labels['FIVE_GUYS']) # Prints 'Five Guys' _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZPZ47K... Code of Conduct: http://python.org/psf/codeofconduct/
Ah. Now I get the key point you were making about scope — that the `as <something>` variable is in the surrounding scope following the execution of the `with` block. You're right. That really does already do pretty close to what I am asking for. It's just a hair short in that it requires an additional line to get the result out of the builder, but that's not enough of a big deal to make significant changes to the capabilities of context managers.
On Sun, Oct 13, 2019 at 03:59:19PM +0200, Anders Hovmöller wrote:
A thing to consider here is that the with block in python doesn't introduce a scope so after:
with foo() as bar: a = 2 b = 3
now bar, a and b are all available in the scope.
That's not a problem. We could introduce a rule that when the with block is part of an assignment, the with block runs in a seperate scope. A bigger problem is that this would be the first statement which returns a value (as opposed to having an effect via side-effects, like the class and def statements), and it would only be usable in assignments: spam = with eggs() as cheese: ... but not: items = [x, y, with eggs() as cheese: ... , z] -- Steven
On Mon, Oct 14, 2019 at 9:39 AM Steven D'Aprano <steve@pearwood.info> wrote:
On Sun, Oct 13, 2019 at 03:59:19PM +0200, Anders Hovmöller wrote:
A thing to consider here is that the with block in python doesn't introduce a scope so after:
with foo() as bar: a = 2 b = 3
now bar, a and b are all available in the scope.
That's not a problem. We could introduce a rule that when the with block is part of an assignment, the with block runs in a seperate scope.
A bigger problem is that this would be the first statement which returns a value (as opposed to having an effect via side-effects, like the class and def statements), and it would only be usable in assignments:
spam = with eggs() as cheese: ...
but not:
items = [x, y, with eggs() as cheese: ... , z]
Scope... return value... this sounds familiar. What if this were actually a function? You'd have to implement build_choices appropriately for this to work, but you'd need to do that for the context manager anyway. @build_choices('Restaurant') def restaurant_choices(cb): # Get of nonexistent attr auto-generates matching entry. cb.BURGER_KING cb.FIVE_GUYS # Returned value is a ChoiceItem that implements __mod__ to return a # new ChoiceItem instance w/ substituted label. cb.MCDONALDS %= "McDonald's" print(restaurant_choices) # RestaurantChoices( # ( # ('BURGER_KING', 'Burger King'), # ('FIVE_GUYS', 'Five Guys'), # ('MCDONALDS', "McDonald's"))) print(RestaurantChoices.BURGER_KING) To make this work, your build_choices would need to have something like this: class build_choices: def __call__(self, builder): builder(self) return self Does that look clean enough? ChrisA
participants (5)
-
Anders Hovmöller
-
Chris Angelico
-
David Mertz
-
Steve Jorgensen
-
Steven D'Aprano