[Python-ideas] Automatic context managers

33 views
Skip to first unread message

anatoly techtonik

unread,
Apr 24, 2013, 4:59:40 AM4/24/13
to python-ideas
Long time no see, all. :P

PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.

I often use this one-shot code in setup.py:
   ...
   long_description = open('README.txt').read(),
   ....

Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.

I don't know why it was not implemented in the first place. Any ideas?

Depending on the answer to the above, the solution can be different. I assume that this was done for a reason (probably immediate garbage collection is too expensive), but confirmation is welcome. Meanwhile the solution can be implemented with auto context manager, which __exit__ method is automatically called when all links to created object are lost.

Difference from usual "with something" is that it is transparent to the user (leaves less details to worry about) and makes code more beautiful -- in the example above the assignment is made inside setup(...) parameter assignment. An example with ordinary "with" statement would look like:

    with open('README.txt') as readme:
      setup(
        ....
        long_description=readme.read(),
        ....
      )

The nesting level increases with every new file you need to read.
Another variation is intermediate variables, which is also less nice.

    with open('README.txt') as readme:
      content = readme.read()
    setup(
      ....
      long_description=content,
      ....
    )

--
anatoly t.

Ronald Oussoren

unread,
Apr 24, 2013, 5:23:09 AM4/24/13
to anatoly techtonik, python-ideas

On 24 Apr, 2013, at 10:59, anatoly techtonik <tech...@gmail.com> wrote:

> Long time no see, all. :P
>
> PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.
>
> I often use this one-shot code in setup.py:
> ...
> long_description = open('README.txt').read(),
> ....
>
> Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.

The file is automaticly closed as soon as the file object is garbage collected. In your example CPython would currently collect at the end of the read call (unles there is an exception) because of the reference counting garbage collector, but other implementations have other garbage collectors and can collect the file object (much) later.

>
> I don't know why it was not implemented in the first place. Any ideas?

It was implemented a long time ago. The with statement was added because relying on automatic resource cleanup by a destructor might clean up the resource too late (for example because the file object is referenced by a local variable in a frame that's referenced by an exception).



>
> Depending on the answer to the above, the solution can be different. I assume that this was done for a reason (probably immediate garbage collection is too expensive), but confirmation is welcome. Meanwhile the solution can be implemented with auto context manager, which __exit__ method is automatically called when all links to created object are lost.
>
> Difference from usual "with something" is that it is transparent to the user (leaves less details to worry about) and makes code more beautiful -- in the example above the assignment is made inside setup(...) parameter assignment. An example with ordinary "with" statement would look like:
>
> with open('README.txt') as readme:
> setup(
> ....
> long_description=readme.read(),
> ....
> )

In python 3.3 and later you can use contexlib.ExitStack:

with contextlib.ExitStack() as stack:
setup(
...
long_description = stack.enter_context(open('README.txt')).read(),
...
)

But for simple scripts like a setup.py I wouldn't worry too much about closing files later than expected.

Ronald

>
> The nesting level increases with every new file you need to read.
> Another variation is intermediate variables, which is also less nice.
>
> with open('README.txt') as readme:
> content = readme.read()
> setup(
> ....
> long_description=content,
> ....
> )
>
> --
> anatoly t.
> _______________________________________________
> Python-ideas mailing list
> Python...@python.org
> http://mail.python.org/mailman/listinfo/python-ideas

_______________________________________________
Python-ideas mailing list
Python...@python.org
http://mail.python.org/mailman/listinfo/python-ideas

Joao S. O. Bueno

unread,
Apr 24, 2013, 7:50:34 AM4/24/13
to anatoly techtonik, python-ideas
On 24 April 2013 05:59, anatoly techtonik <tech...@gmail.com> wrote:
> PySide Qt binding have an interesting property - when you create widgets,
> you need to assign them to variables. When such variable is lost, object is
> immediately destroyed.

I truly hope it is not quite as you describe - otherwisew pySie would
be completly unusable.
What if one adds created objects to a list, instead of assigning them
to a variable?

Otherwiser, reference counting is usually enough in cPython to trigger
object destruction -
and if it was not working as this before, it was broken. (It is so in
tkinter, for example:
>>> import tkinter
>>> t = tkinter.Tk()
>>> del t
>>>
And the window it created is kept open - it indeed should be destroyed
just as you put it. It can't be changed
in tkinter now, or it would certainly break more than half the
programs that use it.

js
-><-

anatoly techtonik

unread,
Apr 24, 2013, 11:49:50 PM4/24/13
to Joao S. O. Bueno, python-ideas
On Wed, Apr 24, 2013 at 2:50 PM, Joao S. O. Bueno <jsb...@python.org.br> wrote:
On 24 April 2013 05:59, anatoly techtonik <tech...@gmail.com> wrote:
> PySide Qt binding have an interesting property - when you create widgets,
> you need to assign them to variables. When such variable is lost, object is
> immediately destroyed.

I truly hope it is not quite as you describe - otherwisew pySie would
be completly unusable.
What if one adds created objects to a list, instead of assigning them
to a variable?

As long as the list references the widget, the widget won't be destroyed. "assign to variable" is not a correct term - I guess "reference keeping" is better. 
 
Otherwiser, reference counting is usually enough in cPython to trigger
object destruction -

It is no guaranteed, so you can have a delayed shot in the foot, which is times worse that immediate.

anatoly techtonik

unread,
Apr 25, 2013, 12:00:59 AM4/25/13
to Ronald Oussoren, python-ideas
On Wed, Apr 24, 2013 at 12:23 PM, Ronald Oussoren <ronaldo...@mac.com> wrote:

On 24 Apr, 2013, at 10:59, anatoly techtonik <tech...@gmail.com> wrote:

> Long time no see, all. :P
>
> PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.
>
> I often use this one-shot code in setup.py:
>    ...
>    long_description = open('README.txt').read(),
>    ....
>
> Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.

The file is automaticly closed as soon as the file object is garbage collected. In your example CPython would currently collect at the end of the read call (unles there is an exception) because of the reference counting garbage collector, but other implementations have other garbage collectors and can collect the file object (much) later.

Right. Automatic context manager proposal brings this mechanism from garbage collection implementation level to language definition level.
 
>
> I don't know why it was not implemented in the first place. Any ideas?

It was implemented a long time ago. The with statement was added because relying on automatic resource cleanup by a destructor might clean up the resource too late (for example because the file object is referenced by a local variable in a frame that's referenced by an exception).

You're speaking about immediate garbage collection for file object in CPython, which is a narrowing. I am still questioning about the whole concept. Automatic content managers are useful for other cases and other Python implementations.
 
>
> Depending on the answer to the above, the solution can be different. I assume that this was done for a reason (probably immediate garbage collection is too expensive), but confirmation is welcome. Meanwhile the solution can be implemented with auto context manager, which __exit__ method is automatically called when all links to created object are lost.
>
> Difference from usual "with something" is that it is transparent to the user (leaves less details to worry about) and makes code more beautiful -- in the example above the assignment is made inside setup(...) parameter assignment. An example with ordinary "with" statement would look like:
>
>     with open('README.txt') as readme:
>       setup(
>         ....
>         long_description=readme.read(),
>         ....
>       )

In python 3.3 and later you can use contexlib.ExitStack:

    with contextlib.ExitStack() as stack:
        setup(
           ...
           long_description = stack.enter_context(open('README.txt')).read(),
           ...
        )

It is no better than an additional indented with statement - both require a change to the whole parent block.
 
But for simple scripts like a setup.py I wouldn't worry too much about closing files later than expected.

It is just a real-world user story to backup the rationale.

anatoly techtonik

unread,
Apr 25, 2013, 12:47:00 AM4/25/13
to Cameron Simpson, python-ideas
On Thu, Apr 25, 2013 at 7:23 AM, Cameron Simpson <c...@zip.com.au> wrote:
On 25Apr2013 06:49, anatoly techtonik <tech...@gmail.com> wrote:
| On Wed, Apr 24, 2013 at 2:50 PM, Joao S. O. Bueno <jsb...@python.org.br>wrote:
| > On 24 April 2013 05:59, anatoly techtonik <tech...@gmail.com> wrote:
| > > PySide Qt binding have an interesting property - when you create widgets,
| > > you need to assign them to variables. When such variable is lost, object
| > is
| > > immediately destroyed.
| >
| > I truly hope it is not quite as you describe - otherwisew pySie would
| > be completly unusable.
| > What if one adds created objects to a list, instead of assigning them
| > to a variable?
|
| As long as the list references the widget, the widget won't be destroyed.
| "assign to variable" is not a correct term - I guess "reference keeping" is
| better.

Then aren't you just talking about the __del__ method?

No. The __del__ method is only called during garbage collection phase which may be delayed. In PySide the QObject is deleted immediately.
-- 
anatoly t.
 

Cameron Simpson

unread,
Apr 25, 2013, 12:23:35 AM4/25/13
to anatoly techtonik, python-ideas
On 25Apr2013 06:49, anatoly techtonik <tech...@gmail.com> wrote:
| On Wed, Apr 24, 2013 at 2:50 PM, Joao S. O. Bueno <jsb...@python.org.br>wrote:
| > On 24 April 2013 05:59, anatoly techtonik <tech...@gmail.com> wrote:
| > > PySide Qt binding have an interesting property - when you create widgets,
| > > you need to assign them to variables. When such variable is lost, object
| > is
| > > immediately destroyed.
| >
| > I truly hope it is not quite as you describe - otherwisew pySie would
| > be completly unusable.
| > What if one adds created objects to a list, instead of assigning them
| > to a variable?
|
| As long as the list references the widget, the widget won't be destroyed.
| "assign to variable" is not a correct term - I guess "reference keeping" is
| better.

Then aren't you just talking about the __del__ method?
--
Cameron Simpson <c...@zip.com.au>

Ninety percent of everything is crud. - Theodore Sturgeon

Ronald Oussoren

unread,
Apr 25, 2013, 1:46:26 AM4/25/13
to anatoly techtonik, python-ideas

On 25 Apr, 2013, at 6:00, anatoly techtonik <tech...@gmail.com> wrote:

> On Wed, Apr 24, 2013 at 12:23 PM, Ronald Oussoren <ronaldo...@mac.com> wrote:
>
>
>
>
> On 24 Apr, 2013, at 10:59, anatoly techtonik <tech...@gmail.com> wrote:
>
> > Long time no see, all. :P
> >
> > PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.
> >
> > I often use this one-shot code in setup.py:
> > ...
> > long_description = open('README.txt').read(),
> > ....
> >
> > Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.
>
> The file is automaticly closed as soon as the file object is garbage collected. In your example CPython would currently collect at the end of the read call (unles there is an exception) because of the reference counting garbage collector, but other implementations have other garbage collectors and can collect the file object (much) later.
>
> Right. Automatic context manager proposal brings this mechanism from garbage collection implementation level to language definition level.

What proposal? What you appear to propose is either that implementations must use a reference counting collector (more or less ensuring that the file will be closed after the call to read in your example), or that the exit part of the context protocol is run whenever an object is going out of scope.

Neither is going to happen, the language specification doesn't proscribe the garbage collection algorithm for a reason: implementations of Python like Jython and IronPython inherit the garbage collector from their host environment (the JVM and CLR). Automaticly calling __exit__ when an object goes out of scope won't work either, it would break passing arguments to functions.

Ronald

Steven D'Aprano

unread,
Apr 25, 2013, 2:17:10 AM4/25/13
to python...@python.org
Citation please. Where is this documented?

I find your claim difficult to believe, unless PySide implements it's own garbage collector which runs side-by-side to the Python one and knows about PySide objects. Otherwise when objects are deleted depends on Python, not the framework. Objects in Python do not know when they are deleted unless Python calls their __del__ method.

My guess is that if you set up a circular reference between two PySide objects, they will suffer the exact same delay in garbage collection as any other two objects.

This thread on the PySide mailing list suggests that you are mistaken, PySide does not have superpowers over and above Python's garbage collector, and is subject to the exact same non-deterministic destructors as any other Python object. Whether you call that destructor __del__ or __exit__ makes no difference.

http://www.mail-archive.com/pys...@lists.openbossa.org/msg01029.html

Or, and for the record, the reason that with statements work so well is because they are guaranteed to be deterministic. You cannot leave the with block without the __exit__ method being called. It doesn't matter whether you have one reference to the context manager object or ten references, the __exit__ method is still called, and the object still exists. That is *very* different from a destructor method.


py> with open('/tmp/rubbish', 'w') as f:
... f.write('hello world')
...
11
py> f.write('goodbye')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.
py> f
<_io.TextIOWrapper name='/tmp/rubbish' mode='w' encoding='UTF-8'>


On the other hand, objects being freed is not deterministic. They'll be freed when there are no longer any references to it, which may be Never.

Reference counting GCs are deterministic, but cannot deal with circular references. Other GCs can deal with circular references, but are non-deterministic. Even the Java GC doesn't guarantee that the finalize() method will always be called.



--
Steven

Cameron Simpson

unread,
Apr 25, 2013, 4:16:40 AM4/25/13
to Steven D'Aprano, python...@python.org
On 25/04/13 14:47, anatoly techtonik wrote:
| >On Thu, Apr 25, 2013 at 7:23 AM, Cameron Simpson <c...@zip.com.au> wrote:
|
| >>Then aren't you just talking about the __del__ method?
| >
| >No. The __del__ method is only called during garbage collection phase which
| >may be delayed. In PySide the QObject is deleted immediately.

The CPython doco says when the reference count goes to zero. (Snippet
below.) So in your example, also immediately.

Garbage collection can find find detached objects whose count hasn't
gone to zero, but I think your example would fit the refs-gone-to-zero,
and anyway I think that was your criterion for having this work in
the first place (automatically).

So I'm not sure how __del__ is particularly less predictable than
your "implicit" scenario.

Then at 25Apr2013 16:17, Steven D'Aprano <st...@pearwood.info> wrote:
| Citation please. Where is this documented?

Well, the 3.2.3 doco on __del__ says:


Called when the instance is about to be destroyed. [...] It is
not guaranteed that __del__() methods are called for objects that
still exist when the interpreter exits. [...]
del x doesn’t directly call x.__del__() — the former decrements
the reference count for x by one, and the latter is only called
when x‘s reference count reaches zero. [ <-- Obviously only valid
for ref counting Python implementations. - Cameron ] Some common
situations that may prevent the reference count of an object from
going to zero include: [...]

The 2.7 doco is very similar.

So, yes, when references go to zero. But as you say later, that may
never happen. The GC _may_ find isolated circles and delete then,
thus at GC time in anatoly's nomenclature.

Reference counting makes __del__ fairly predictable if you have
tight control over the references to an object. Not always the case
of course. And other Pythons don't necessarily do reference counting
unless I misremember.

| Or, and for the record, the reason that with statements work so
| well is because they are guaranteed to be deterministic. You cannot
| leave the with block without the __exit__ method being called. It
| doesn't matter whether you have one reference to the context manager
| object or ten references, the __exit__ method is still called, and
| the object still exists.

This is why I'm for with statements also.

| That is *very* different from a destructor method.

[... snip other stuff I agree with...; I fact I agree with everything
you say but anatoly is not totally off the mark with "GC time". ]

Yep.
--
Cameron Simpson <c...@zip.com.au>

I am now convinced that theoretical physics is actual philosophy.
- Max Born

Steven D'Aprano

unread,
Apr 25, 2013, 2:20:36 PM4/25/13
to python...@python.org
On 25/04/13 18:16, Cameron Simpson wrote:

> Then at 25Apr2013 16:17, Steven D'Aprano <st...@pearwood.info> wrote:
> | Citation please. Where is this documented?
>
> Well, the 3.2.3 doco on __del__ says:

Please be more careful about quoting me out of context. I'm perfectly aware of what the docs for __del__ say. I was addressing my question to Anatoly, asking him for documentation for his claims that PySide objects are more deterministic than __del__, that is, that they don't suffer from the same issues regarding circular references and __del__ as other, non-PySide objects. I won't categorically say that's impossible, but I find it an extraordinary claim that requires more evidence than just one person's say so.


[...]
> Reference counting makes __del__ fairly predictable if you have
> tight control over the references to an object. Not always the case
> of course. And other Pythons don't necessarily do reference counting
> unless I misremember.

You remember correctly. IronPython and Jython use the .Net and Java virtual machines, including their garbage collectors, neither of which are reference counting. (In fact, there's often a fair bit of snobbery in Java circles about CPython's ref counting not being a "real" GC.) PyPy can use various GCs, selected at build-time (I think), including a ref counting one. I don't know about other implementations.



--
Steven

anatoly techtonik

unread,
Apr 26, 2013, 9:02:28 AM4/26/13
to Steven D'Aprano, python-ideas
On Thu, Apr 25, 2013 at 9:17 AM, Steven D'Aprano <st...@pearwood.info> wrote:
On 25/04/13 14:47, anatoly techtonik wrote:
On Thu, Apr 25, 2013 at 7:23 AM, Cameron Simpson <c...@zip.com.au> wrote:

Then aren't you just talking about the __del__ method?


No. The __del__ method is only called during garbage collection phase which
may be delayed. In PySide the QObject is deleted immediately.

Citation please. Where is this documented?


"""
If a QObject falls out of scope in Python, it will get deleted. You have to take care of keeping a reference to the object:

* Store it as an attribute of an object you keep around, e.g. self.window = QMainWindow()
* Pass a parent QObject to the object’s constructor, so it gets owned by the parent
"""

This thread on the PySide mailing list suggests that you are mistaken, PySide does not have superpowers over and above Python's garbage collector, and is subject to the exact same non-deterministic destructors as any other Python object. Whether you call that destructor __del__ or __exit__ makes no difference.

http://www.mail-archive.com/pys...@lists.openbossa.org/msg01029.html

I am not an expert in internals. I guess the QObject is on a C side - not on a Python side, so it is destroyed immediately. And perhaps when you wrap (subclass) it on Python side, it will start to suffer from delayed garbage collection. Is that plausible?
 
Or, and for the record, the reason that with statements work so well is because they are guaranteed to be deterministic. You cannot leave the with block without the __exit__ method being called. It doesn't matter whether you have one reference to the context manager object or ten references, the __exit__ method is still called, and the object still exists. That is *very* different from a destructor method.

I am not touching destructor methods. The idea is to make with statement transparent - embed inside objects that require it. I am not sure what the implementation should be. Probably object should have an ability to enable context scope tracking in its constructor, to tell Python to call its __exit__ method at the moment when its reference count reaches zero, and before it is garbage collected.
 
On the other hand, objects being freed is not deterministic. They'll be freed when there are no longer any references to it, which may be Never.

Reference counting GCs are deterministic, but cannot deal with circular references. Other GCs can deal with circular references, but are non-deterministic. Even the Java GC doesn't guarantee that the finalize() method will always be called.

This circular reference problem is interesting. In object space it probably looks like a stellar detached from the visible (attached) universe. Is the main problem in detecting it?

anatoly techtonik

unread,
Apr 26, 2013, 9:22:48 AM4/26/13
to Ronald Oussoren, python-ideas
On Thu, Apr 25, 2013 at 8:46 AM, Ronald Oussoren <ronaldo...@mac.com> wrote:

On 25 Apr, 2013, at 6:00, anatoly techtonik <tech...@gmail.com> wrote:

> On Wed, Apr 24, 2013 at 12:23 PM, Ronald Oussoren <ronaldo...@mac.com> wrote:
>
>
>
>
> On 24 Apr, 2013, at 10:59, anatoly techtonik <tech...@gmail.com> wrote:
>
> > Long time no see, all. :P
> >
> > PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.
> >
> > I often use this one-shot code in setup.py:
> >    ...
> >    long_description = open('README.txt').read(),
> >    ....
> >
> > Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.
>
> The file is automaticly closed as soon as the file object is garbage collected. In your example CPython would currently collect at the end of the read call (unles there is an exception) because of the reference counting garbage collector, but other implementations have other garbage collectors and can collect the file object (much) later.
>
> Right. Automatic context manager proposal brings this mechanism from garbage collection implementation level to language definition level.

What proposal?   What you appear to propose is either that implementations must use a reference counting collector (more or less ensuring that the file will be closed after the call to read in your example), or that the exit part of the context protocol is run whenever an object is going out of scope.

The proposal is fully illustrated by the user story above - immediately close file after it is read operation is complete. The proposed implementation is automatic context manager -- optional, Python level mechanism to run exit part of the context protocol when object loses all references. GC is out of scope here.
 
Automaticly calling __exit__ when an object goes out of scope won't work either, it would break passing arguments to functions.

Why? Can you provide an example?

Ned Batchelder

unread,
Apr 26, 2013, 9:33:38 AM4/26/13
to anatoly techtonik, python-ideas

On 4/26/2013 9:22 AM, anatoly techtonik wrote:
The proposal is fully illustrated by the user story above - immediately close file after it is read operation is complete. The proposed implementation is automatic context manager -- optional, Python level mechanism to run exit part of the context protocol when object loses all references. GC is out of scope here.

The behavior is already that files are closed as part of reclaiming the file object.  I don't see how your proposal could change that behavior, since your proposal (I think) is to call __exit__ when the object is reclaimed, but all that does for a file is close the file.  Since your proposal doesn't seem to change existing behavior, I can only conclude that either you misunderstand the existing behavior, or I misunderstand your proposal.


 
Automaticly calling __exit__ when an object goes out of scope won't work either, it would break passing arguments to functions.

Why? Can you provide an example?


Part of the confusion here is the phrase "when an object goes out of scope".  Values in Python have no scope.  Names have scope.  This proposal doesn't involve names, it involves values, and so can have nothing to do with scope, unless I've misunderstood something.

--Ned.

Ronald Oussoren

unread,
Apr 26, 2013, 9:45:58 AM4/26/13
to anatoly techtonik, python-ideas

On 26 Apr, 2013, at 15:22, anatoly techtonik <tech...@gmail.com> wrote:

> On Thu, Apr 25, 2013 at 8:46 AM, Ronald Oussoren <ronaldo...@mac.com> wrote:
>
> On 25 Apr, 2013, at 6:00, anatoly techtonik <tech...@gmail.com> wrote:
>
> > On Wed, Apr 24, 2013 at 12:23 PM, Ronald Oussoren <ronaldo...@mac.com> wrote:
> >
> >
> >
> >
> > On 24 Apr, 2013, at 10:59, anatoly techtonik <tech...@gmail.com> wrote:
> >
> > > Long time no see, all. :P
> > >
> > > PySide Qt binding have an interesting property - when you create widgets, you need to assign them to variables. When such variable is lost, object is immediately destroyed.
> > >
> > > I often use this one-shot code in setup.py:
> > > ...
> > > long_description = open('README.txt').read(),
> > > ....
> > >
> > > Which probably leaves the README.txt file open until the setup.py exits. So, the idea is to close the file as soon as the variable is lost.
> >
> > The file is automaticly closed as soon as the file object is garbage collected. In your example CPython would currently collect at the end of the read call (unles there is an exception) because of the reference counting garbage collector, but other implementations have other garbage collectors and can collect the file object (much) later.
> >
> > Right. Automatic context manager proposal brings this mechanism from garbage collection implementation level to language definition level.
>
> What proposal? What you appear to propose is either that implementations must use a reference counting collector (more or less ensuring that the file will be closed after the call to read in your example), or that the exit part of the context protocol is run whenever an object is going out of scope.
>
> The proposal is fully illustrated by the user story above

Right, there is no proposal, only vague handwaving. I haven't seen anything yet that wouldn't require the use of refcounting (the file is closed as soon as the last reference to the file object goes away), or some serious magic (when you want the file object to be closed even when read raises an exeption).

When you want to propose something you need to do some work yourself. That doesn't mean you have to provide a patch, but you do need to specify your proposal detailed enough to understand it without trying to second guess you.

The batteries of my crystal ball ran out,

Ethan Furman

unread,
Apr 26, 2013, 9:55:25 AM4/26/13
to python...@python.org
On 04/26/2013 06:02 AM, anatoly techtonik wrote:
> On Thu, Apr 25, 2013 at 9:17 AM, Steven D'Aprano <st...@pearwood.info <mailto:st...@pearwood.info>> wrote:
>
> On 25/04/13 14:47, anatoly techtonik wrote:
>
> On Thu, Apr 25, 2013 at 7:23 AM, Cameron Simpson <c...@zip.com.au <mailto:c...@zip.com.au>> wrote:
>
>
> Then aren't you just talking about the __del__ method?
>
>
> No. The __del__ method is only called during garbage collection phase which
> may be delayed. In PySide the QObject is deleted immediately.
>
>
> Citation please. Where is this documented?
>
>
> Here:Â http://qt-project.org/wiki/PySide_Pitfalls
>
> """
> If a QObject falls out of scope in Python, it will get deleted. You have to take care of keeping a reference to the object:
>
> * Store it as an attribute of an object you keep around, e.g. self.window = QMainWindow()
> * Pass a parent QObject to the object’s constructor, so it gets owned by the parent
> """
>
> This thread on the PySide mailing list suggests that you are mistaken, PySide does not have superpowers over and
> above Python's garbage collector, and is subject to the exact same non-deterministic destructors as any other Python
> object. Whether you call that destructor __del__ or __exit__ makes no difference.
>
> http://www.mail-archive.com/__py...@lists.openbossa.org/__msg01029.html
> <http://www.mail-archive.com/pys...@lists.openbossa.org/msg01029.html>

You'll notice it doesn't say "gets /immediately/ deleted" -- because it doesn't. It gets deleted when it gets garbage
collected.

--
~Ethan~

anatoly techtonik

unread,
Apr 26, 2013, 11:21:25 AM4/26/13
to Ethan Furman, python-ideas
On Fri, Apr 26, 2013 at 4:55 PM, Ethan Furman <et...@stoneleaf.us> wrote:
On 04/26/2013 06:02 AM, anatoly techtonik wrote:
On Thu, Apr 25, 2013 at 9:17 AM, Steven D'Aprano <st...@pearwood.info <mailto:st...@pearwood.info>> wrote:

    On 25/04/13 14:47, anatoly techtonik wrote:

        On Thu, Apr 25, 2013 at 7:23 AM, Cameron Simpson <c...@zip.com.au <mailto:c...@zip.com.au>> wrote:


            Then aren't you just talking about the __del__ method?


        No. The __del__ method is only called during garbage collection phase which
        may be delayed. In PySide the QObject is deleted immediately.


    Citation please. Where is this documented?


Here:Â http://qt-project.org/wiki/PySide_Pitfalls


"""
If a QObject falls out of scope in Python, it will get deleted. You have to take care of keeping a reference to the object:

* Store it as an attribute of an object you keep around, e.g. self.window = QMainWindow()
* Pass a parent QObject to the object’s constructor, so it gets owned by the parent

"""

    This thread on the PySide mailing list suggests that you are mistaken, PySide does not have superpowers over and
    above Python's garbage collector, and is subject to the exact same non-deterministic destructors as any other Python
    object. Whether you call that destructor __del__ or __exit__ makes no difference.


You'll notice it doesn't say "gets /immediately/ deleted" -- because it doesn't.  It gets deleted when it gets garbage collected.

Are you sure about that? The example on the PySide wiki is pretty reproducible. With current garbage collector lazyness it should be at least in some cases non-reliable.

anatoly techtonik

unread,
Apr 26, 2013, 11:25:17 AM4/26/13
to Ronald Oussoren, python-ideas
Ok. The proposal is patch Python to be able to write:

   boolean = open(resource).use()

Instead of:

   boolean = None
   with open(resource) as tempvar:
       boolean = tempvar.use()

That's it. I am not pretending I know how to implement it. I just expressed my opinion that this might be possible, because PySide seems to do this somehow.

Ned Batchelder

unread,
Apr 26, 2013, 11:39:24 AM4/26/13
to anatoly techtonik, python-ideas

On 4/26/2013 11:21 AM, anatoly techtonik wrote:
"""
If a QObject falls out of scope in Python, it will get deleted. You have to take care of keeping a reference to the object:

* Store it as an attribute of an object you keep around, e.g. self.window = QMainWindow()
* Pass a parent QObject to the object’s constructor, so it gets owned by the parent

"""

    This thread on the PySide mailing list suggests that you are mistaken, PySide does not have superpowers over and
    above Python's garbage collector, and is subject to the exact same non-deterministic destructors as any other Python
    object. Whether you call that destructor __del__ or __exit__ makes no difference.


You'll notice it doesn't say "gets /immediately/ deleted" -- because it doesn't.  It gets deleted when it gets garbage collected.

Are you sure about that? The example on the PySide wiki is pretty reproducible. With current garbage collector lazyness it should be at least in some cases non-reliable.

Again, I suspect we are falling prey to fuzzy language.  CPython will reclaim objects as soon as their reference count reaches zero.  This is not the garbage collector.  The garbage collector is a separate facility which kicks in every once in a while to find objects that have non-zero reference counts, even though they are unreachable, because of circular references.  Some people say "garbage collector" or "garbage collection" to mean the usual reclamation of objects when their refcount reaches zero, but this is imprecise and confusing when mixed with people who use the term differently.

I know nothing about the internals of QObjects, but like most others, I *strongly* suspect that they are doing nothing special above and beyond what Python does to determine the lifetime of an object.  Their cleanup happens when the object is reclaimed (note I am careful not to say, "when the object is garbage collected"). 

The example on the PySide wiki is reproducible because it is not subject to garbage collector laziness.  The "animation" name is a local in animate_stuff.  At the end of that function, the name "animation" falls out of scope, decrementing the reference count on the QPropertyAnimation object it referenced.  That object now has a reference count of zero, so it is reclaimed.  The cleanup code is then invoked, destroying the native objects as well.

--Ned.

MRAB

unread,
Apr 26, 2013, 11:54:49 AM4/26/13
to python-ideas
On 26/04/2013 14:02, anatoly techtonik wrote:
> On Thu, Apr 25, 2013 at 9:17 AM, Steven D'Aprano <st...@pearwood.info
> <mailto:st...@pearwood.info>> wrote:
[snip]
> I am not touching destructor methods. The idea is to make with statement
> transparent - embed inside objects that require it. I am not sure what
> the implementation should be. Probably object should have an ability to
> enable context scope tracking in its constructor, to tell Python to call
> its __exit__ method at the moment when its reference count reaches zero,
> and before it is garbage collected.
>
> On the other hand, objects being freed is not deterministic. They'll
> be freed when there are no longer any references to it, which may be
> Never.
>
> Reference counting GCs are deterministic, but cannot deal with
> circular references. Other GCs can deal with circular references,
> but are non-deterministic. Even the Java GC doesn't guarantee that
> the finalize() method will always be called.
>
>
> This circular reference problem is interesting. In object space it
> probably looks like a stellar detached from the visible (attached)
> universe. Is the main problem in detecting it?
>
The problem is in knowing in which order the objects should be
collected.

For example, if A refers to B and B refers to A, should you collect A
then B, or B then A? If you collect A first, then, for a time, B will
be referring to a non-existent object. That's not good if the objects
have destructors which need to be run.

Georg Brandl

unread,
Apr 26, 2013, 1:17:26 PM4/26/13
to python...@python.org
Am 26.04.2013 17:25, schrieb anatoly techtonik:

> Right, there is no proposal, only vague handwaving. I haven't seen anything
> yet that wouldn't require the use of refcounting (the file is closed as soon
> as the last reference to the file object goes away), or some serious magic
> (when you want the file object to be closed even when read raises an exeption).
>
> When you want to propose something you need to do some work yourself. That
> doesn't mean you have to provide a patch, but you do need to specify your
> proposal detailed enough to understand it without trying to second guess you.
>
> The batteries of my crystal ball ran out,
>
>
> Ok. The proposal is patch Python to be able to write:
>
> boolean = open(resource).use()
>
> Instead of:
>
> boolean = None
> with open(resource) as tempvar:
> boolean = tempvar.use()
>
> That's it. I am not pretending I know how to implement it. I just expressed my
> opinion that this might be possible, because PySide seems to do this somehow.


Well, then consider this proposal rejected. It's nothing new, objects have
cleaned up resources in their __del__ (or equivalent C-level
destructor/finalizer) for ages. PySide doesn't do anything different, and
as others have mentioned, due to CPython's use of reference counting you can
get deterministic behavior if you are careful not to create cycles.

However, at least for resources like files this is *exactly* what we have been
moving away from even before the introduction of the "with" statement; in fact,
in today's Python the destructor of file objects emits a ResourceWarning (which
is silent by default, since many users still rely on this behavior; but you
can see it with "python -Wa"). There are several good reasons for this:

* Explicit is better than implicit: letting Python handle resource cleanup is
possible to manage, especially for small numbers of resources and small pieces
of code, it quickly gets annoying and creates exactly the sort of problem that
Python usually does away with: tracking object lifetimes yourself. With a
"with" statement, you know that your resources *are* cleaned up, and when.

* Most other Python implementations have never had reference counting, and never
will (e.g. PyPy). Collecting their unreachable objects differently enables
optimizations.

Georg

MRAB

unread,
Apr 26, 2013, 1:20:09 PM4/26/13
to python-ideas
On 26/04/2013 17:52, Chris Angelico wrote:
> Spin-off thread from python-ideas to discuss a more general question
> of garbage collection of cyclic structures.
>
> Once it's been proven that there's an unreferenced cycle, why not
> simply dispose of one of the objects, and replace all references to it
> (probably only one - preferably pick an object with the fewest
> references) with a special temporary object? In fact, that could
> probably be done in CPython by wiping out the object in memory and
> replacing it with a special marker of some sort, which would then
> automatically "take over" all references to the old object. Any
> attempt to manipulate this object could simply pop back with a
> DestructedObject exception or something.
>
I wonder whether it would be best to call the __del__ method of the
newest object (if it's possible to determine which is the newest) in
such a case, then replace _that_ object with the DestructedObject (the
"special marker" would be just a special "destructed" object).

> Is this a plausible (never mind viable yet, just conceptually
> plausible) alternative to sticking them into gc.garbage and ignoring
> them? It'd allow a double-linked list/tree to function cleanly -
> imagine, for instance, something like the DOM facilities available to
> web browser scripts:
>
> class DOMObject:
> def __init__(self,parent):
> self.parent=parent
> self.firstchild=self.sibling=None
> if not parent: return
> if not parent.firstchild:
> parent.firstchild=self
> else:
> child=parent.firstchild
> while child.sibling:
> child=child.sibling
> child.sibling=self
> def __del__(self):
> print("Disposing of id #%d"%id(self))
>
> document=DOMObject(None)
> body=DOMObject(document)
> p=DOMObject(body)
> p=DOMObject(body)
> p=DOMObject(body)
> del document,body,p
> gc.collect()
>
> The __del__ method would need to clean up the external resources used
> by this object, but wouldn't have to walk the tree. Yet, just because
> there is a reference loop and there are __del__ methods, the garbage
> collector gives up and leaves it to the program author to deal with.
>
> I can understand if this is considered too complicated and too unusual
> a circumstance to be worth bothering to support, but I'm curious as to
> whether it's at least conceptually reasonable to do something like
> this.
>
It does sound like an interesting idea.

anatoly techtonik

unread,
Apr 26, 2013, 1:26:44 PM4/26/13
to Ned Batchelder, python-ideas
Thanks. That makes it more clear. So if I create a circular reference with two QObjects and lose a link to both of them - they won't be reclaimed until GC starts, because their mutual reference count is not zero, right? And the same will be true for the object returned by open() involved in circular reference.

I guess that's the reason why the example in file.close() could not be updated to note that "with" is not needed anymore.

anatoly techtonik

unread,
Apr 26, 2013, 1:31:06 PM4/26/13
to python-ideas
And how does GC solve that? Can it complain about those stellars?

MRAB

unread,
Apr 26, 2013, 2:20:35 PM4/26/13
to python-ideas, python-ideas
On 26/04/2013 18:31, anatoly techtonik wrote:
> On Fri, Apr 26, 2013 at 6:54 PM, MRAB <pyt...@mrabarnett.plus.com
> <mailto:pyt...@mrabarnett.plus.com>> wrote:
>
> On 26/04/2013 14:02, anatoly techtonik wrote:
>
> On Thu, Apr 25, 2013 at 9:17 AM, Steven D'Aprano
> <st...@pearwood.info <mailto:st...@pearwood.info>
It doesn't solve that.

Georg Brandl

unread,
Apr 26, 2013, 2:44:21 PM4/26/13
to python...@python.org
You got it backwards. "with" has been introduced to supersede both the
correct-but-clumsy try-finally idiom and the sometimes-but-not-everywhere-
correct implicit "automatic" cleanup.

Georg

rand...@fastmail.us

unread,
Apr 26, 2013, 2:54:20 PM4/26/13
to python...@python.org
On Fri, Apr 26, 2013, at 11:25, anatoly techtonik wrote:
> Ok. The proposal is patch Python to be able to write:
>
> boolean = open(resource).use()
>
> Instead of:
>
> boolean = None
> with open(resource) as tempvar:
> boolean = tempvar.use()

What about a with expression?

boolean = x.use() with x as open(resource)

Andrew Svetlov

unread,
Apr 26, 2013, 3:30:18 PM4/26/13
to rand...@fastmail.us, python-ideas
On Fri, Apr 26, 2013 at 9:54 PM, <rand...@fastmail.us> wrote:
> What about a with expression?
>
> boolean = x.use() with x as open(resource)

interesting idea. I see nothing bad with proposed construction.
Any objections? I've miss something?

--
Thanks,
Andrew Svetlov

David Mertz

unread,
Apr 26, 2013, 3:37:29 PM4/26/13
to rand...@fastmail.us, python...@python.org
On Apr 26, 2013, at 11:54 AM, rand...@fastmail.us wrote:
>> Ok. The proposal is patch Python to be able to write:
>>
>> boolean = open(resource).use()
>>
>> Instead of:
>>
>> boolean = None
>> with open(resource) as tempvar:
>> boolean = tempvar.use()
>
> What about a with expression?
> boolean = x.use() with x as open(resource)

The initial 'boolean=None' is superfluous in any case. So the ideas are that instead of the current:

with open(resources) as x: boolean = x.use()

We might right:

boolean = open(resource).use()

Or:

boolean = x.use() with x as open(resource)

The with expression saves two characters, the probably entirely unworkable "automatic context manager" would save 13 characters. The existing one-liner seems perfectly clear to me and perfectly compact.

A "with expression" doesn't seem absurd to me, but I'm not sure that the percentage of the time you really do want a single expression inside the 'with' block is common enough to warrant a special form. In contrast, the 'if expression' ternary operator that was introduced really does seem to express a very common pattern.

I'd note that there's no reason you couldn't use the so-called "automatic context manager" already, it's just a matter of writing your own function rather than the built-in 'open()'. So, e.g. with a few lines of definition, you might use:

boolean = SafeOpen(resource).use()



--
mertz@ | The specter of free information is haunting the `Net! All the
gnosis | powers of IP- and crypto-tyranny have entered into an unholy
.cx | alliance...ideas have nothing to lose but their chains. Unite
| against "intellectual property" and anti-privacy regimes!

Bruce Leban

unread,
Apr 26, 2013, 3:45:45 PM4/26/13
to Andrew Svetlov, python-ideas
On Fri, Apr 26, 2013 at 12:30 PM, Andrew Svetlov <andrew....@gmail.com> wrote:
On Fri, Apr 26, 2013 at 9:54 PM,  <rand...@fastmail.us> wrote:
> What about a with expression?
>
> boolean = x.use() with x as open(resource)

interesting idea. I see nothing bad with proposed construction.
Any objections? I've miss something?

The most obvious thing as that 'as' is backwards from the with statement. Also this requires you to come up with a name which you have to repeat while

with(open(resource)).use()

doesn't. On the other hand, it allows you to do:

result = [x.foo(), x.bar()] with open(resource) as x

which opens the way to freewheeling inline assignment: 

@contextmanager
def assign(x):
    yield x

result = (-b + sqrt(b*b - 4 * a * c)) / (2 * a) with assign(3) as a with assign(4) as b with assign(5) as c

I don't think that's a good thing.

--- Bruce
Latest blog post: Alice's Puzzle Page http://www.vroospeak.com
Learn how hackers think: http://j.mp/gruyere-security
 

Bruce Leban

unread,
Apr 26, 2013, 3:46:57 PM4/26/13
to David Mertz, python...@python.org

On Fri, Apr 26, 2013 at 12:37 PM, David Mertz <me...@gnosis.cx> wrote:
I'd note that there's no reason you couldn't use the so-called "automatic context manager" already, it's just a matter of writing your own function rather than the built-in 'open()'.  So, e.g. with a few lines of definition, you might use:

  boolean = SafeOpen(resource).use()

I'd like to see those few lines if they are indeed possible. I don't see how the function would know when to call __exit__. It has to be after use() finishes.

Jim Jewett

unread,
Apr 26, 2013, 3:45:58 PM4/26/13
to python-ideas
(Quoting from MRAB's quote, since I don't see the original -- I
suspect I also mangled some attributions internally)

On 26/04/2013 17:52, Chris Angelico wrote:
> On Sat, Apr 27, 2013 at 1:54 AM, MRAB <pyt...@mrabarnett.plus.com> wrote:
>> On 26/04/2013 14:02, anatoly techtonik wrote:

>>> This circular reference problem is interesting. In object space it
>>> probably looks like a stellar detached from the visible (attached)
>>> universe. Is the main problem in detecting it?


Yes. That is where Reference Counting fails, and is the reason that
CPython added (cylic) garbage collection.

Note that Garbage Collectors need to have a list of "roots" which can
keep things alive, and a way of recognizing links. If some objects
(even those implemented in C) use pointers that the garbage collector
doesn't know about (e.g., by adding a constant to a base address
instead of storing the address directly, or storing tag bits in the
low-order portion of the address), then there will be objects that
cannot ever be safely collected. Officially, that can be a bug in the
object implementation, but if it leads to a segfault, python still
looks bad.


>> The problem is in knowing in which order the objects should be
>> collected.

This is a problem only once a garbage cycle has already been detected.
But it is indeed a major problem.

The above mean that garbage collectors must look at every live object
in the entire system for every full collection; there is plenty of
research on how to speed things up (or even just make the system more
responsive) by doing "extra" work for partial collections. (I put
"extra" in scare-quotes, because these heuristics increase the
worst-case and the theoretical average case, but often decrease the
normal-case workload.)

Of course, if you're paying this full price anyhow, why bother paying
the additional price of reference-counting? (Because it is one of
those heuristics that actually save work in practice, if your data
isn't very cyclic. But if you use a very cyclic style, or library...)

>> For example, if A refers to B and B refers to A, should you collect A
>> then B, or B then A? If you collect A first, then, for a time, B will
>> be referring to a non-existent object. That's not good if the objects
>> have destructors which need to be run.

> Once it's been proven that there's an unreferenced cycle, why not
> simply dispose of one of the objects, and replace all references to it
> (probably only one - preferably pick an object with the fewest
> references) with a special temporary object?

Backwards compatibility. If my pointed-to object no longer has the
methods I expect (perhaps even just "close"), I will get exceptions.
They won't be the ones for which I was prepared. Now, instead of
leaking a few resources (only until the program exits), I will be
exiting prematurely, perhaps without a chance to do other cleanup.

(Mrab wrote:)
I wonder whether it would be best to call the __del__ method of the
newest object (if it's possible to determine which is the newest) in
such a case, then replace _that_ object with the DestructedObject (the
"special marker" would be just a special "destructed" object).

You can get most of the way there with object address, and farther
with timestamping at creation (which also costs more memory).

But is the difference between 99.5 and 99.8 worth complicating things
and possibly breaking the last 0.2 more severely?

I *would* like a __close__ magic method that worked like __del__,
except that it would be OK to call as soon as you found the object
in a garbage cycle. (This also means that the __close__ method's
contract should state explicitly that it might be called multiple times,
and cycles might be broken in an arbitrary order.)

In the past, this has been rejected as insufficiently motivated, but
that may have changed.

-jJ

David Mertz

unread,
Apr 26, 2013, 4:12:26 PM4/26/13
to Bruce Leban, python...@python.org
On Apr 26, 2013, at 12:46 PM, Bruce Leban wrote:
> On Fri, Apr 26, 2013 at 12:37 PM, David Mertz <me...@gnosis.cx> wrote:
> I'd note that there's no reason you couldn't use the so-called "automatic context manager" already, it's just a matter of writing your own function rather than the built-in 'open()'. So, e.g. with a few lines of definition, you might use:
>
> boolean = SafeOpen(resource).use()
>
> I'd like to see those few lines if they are indeed possible. I don't see how the function would know when to call __exit__. It has to be after use() finishes.

You might get really perverse with stack inspection and whatnot. But I was thinking more of just a proxy method that introduced the safety.

class SafeOpen(object):
def __init__(self, resource):
self.resource = resource
def __getattr__(self, name):
def f(*args, **kws):
with open(self.resource) as x:
y = getattr(x, name)(*args, **kws)
return y
return f





--
If I seem shortsighted to you, it is only because I have stood on the backs of midgets.

Bruce Leban

unread,
Apr 26, 2013, 6:33:33 PM4/26/13
to David Mertz, python...@python.org

On Fri, Apr 26, 2013 at 1:12 PM, David Mertz <me...@gnosis.cx> wrote:
You might get really perverse with stack inspection and whatnot.  But I was thinking more of just a proxy method that introduced the safety.

class SafeOpen(object):
    def __init__(self, resource):
        self.resource = resource
    def __getattr__(self, name):
        def f(*args, **kws):
            with open(self.resource) as x:
                y = getattr(x, name)(*args, **kws)
            return y
        return f

Wouldn't that close the resource before the use function is actually called? As I read it, it opens, calls getattr(x, 'use'), closes x, then calls x.use().

Bruce Leban

unread,
Apr 26, 2013, 7:14:41 PM4/26/13
to David Mertz, python...@python.org
On Fri, Apr 26, 2013 at 3:55 PM, David Mertz <me...@gnosis.cx> wrote:
> Wouldn't that close the resource before the use function is actually called? As I read it, it opens, calls getattr(x, 'use'), closes x, then calls x.use().

Nope.  The call is inside the context manager.  Whatever is returned is stored in 'y' before the resource is closed, and that value of y is returned by the proxy function f.

Yup, you're right. Clever. And I read the code a bit too quickly. :-) I think it's a bit more complicated to write a general wrapper though. For example, to handle chained calls that SQLAlchemy uses you have to know which calls should close the object and which ones shouldn't.

    data = SafeSession(...).query(...).filter(...).order_by(...).values(...)
    
The session should be closed after the call to values() [assuming for the purpose of this discussion that we actually want to close the session]. If we do it before, it will fail. If we don't do it then, we no longer have a handle to the session to close it. So you have to know which calls should close it and which ones shouldn't.

Caveat: Actually, SQLAlchemy is unsuitable for automatic closing because in some cases it returns an array of objects which are attached to the session and you need to keep the session alive as long as you need to access any of the objects, which an automatic mechanism like this wouldn't be able to handle But it serves to illustrate the point.

David Mertz

unread,
Apr 26, 2013, 6:55:39 PM4/26/13
to Bruce Leban, python...@python.org
On Apr 26, 2013, at 3:33 PM, Bruce Leban wrote:
> class SafeOpen(object):
> def __init__(self, resource):
> self.resource = resource
> def __getattr__(self, name):
> def f(*args, **kws):
> with open(self.resource) as x:
> y = getattr(x, name)(*args, **kws)
> return y
> return f
> Wouldn't that close the resource before the use function is actually called? As I read it, it opens, calls getattr(x, 'use'), closes x, then calls x.use().

Nope. The call is inside the context manager. Whatever is returned is stored in 'y' before the resource is closed, and that value of y is returned by the proxy function f.

It's easy to try:

In [23]: SafeOpen('test').read(5)
Out[23]: 'this\n'

In [24]: SafeOpen('test').readlines()
Out[24]: ['this\n', 'and\n', 'that\n', 'and\n', 'other\n']

It would be easy to generalize this to be a SafeAnything class rather than only handle 'open()'. You could just pass in the name of the context manager to the initializer rather than hardcode it as being 'open()'. Actually, I sort of like a factory for producing SafeAnythings better:

def safe_factory(context_manager):
class SafeAnything(object):
def __init__(self, resource, cm=context_manager):
self.resource = resource
self.cm = cm
def __getattr__(self, name):
def f(*args, **kws):
with self.cm(self.resource) as x:
y = getattr(x, name)(*args, **kws)
return y
return f
return SafeAnything
SafeOpen = safe_factory(open)

--
The dead increasingly dominate and strangle both the living and the
not-yet born. Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.

David Mertz

unread,
Apr 26, 2013, 7:51:47 PM4/26/13
to Bruce Leban, python...@python.org
On Apr 26, 2013, at 4:14 PM, Bruce Leban wrote:
> Yup, you're right. Clever. And I read the code a bit too quickly. :-) I think it's a bit more complicated to write a general wrapper though. For example, to handle chained calls that SQLAlchemy uses you have to know which calls should close the object and which ones shouldn't.
> data = SafeSession(...).query(...).filter(...).order_by(...).values(...)

Yeah sure. I was just demonstrating what's possible in a toy way. I actually feel like simply using the 'with' statement is perfectly fine and perfectly clear. But you *can* you could also wrap the bunch of chained methods if you wanted too. I'd have to think about how to define that properly for a few minutes, but if you get to the point where there are various conditional exits to the chain, just use a 'with' block, for gosh sake.

with session(...) as x:
x.query(...)
x.filter(...)
if x.something():
x.order_by(...)
else:
x.order_by(...)
if not x.time_to_leave():
data = x.values()

Or whatever details apply to your own program logic. I don't really know SQLAlchemy, but I guess it must be that most of those chained methods return a mutated object, right? That would be easy enough to check for in the proxy, and I guess whenever it got to something that wasn't a mutated object but rather some "plain" values (a list, scalar, dict, etc), that would be time to leave the context manager. I'll leave that as an exercise :-).


--
>>> THE MERTZ PRINCIPLE <<<
There are two essential virtues in which a sentence might engage:
seduction and alliteration. Ancillary virtues include truthfulness,
effectivity, and artfulness.

Greg Ewing

unread,
Apr 26, 2013, 8:50:47 PM4/26/13
to python-ideas
Jim Jewett wrote:
> Note that Garbage Collectors need to have a list of "roots" which can
> keep things alive, and a way of recognizing links. If some objects
> ... use pointers that the garbage collector
> doesn't know about ... then there will be objects that
> cannot ever be safely collected. Officially, that can be a bug in the
> object implementation, but if it leads to a segfault, python still
> looks bad.

Python's GC is more robust in this respect than traditional
mark-and-sweep collectors, because it uses the opposite
logic: instead of assuming everything is garbage unless it
can prove that it's not, it assumes that nothing is garbage
unless it can prove that it is. So if it misses a reference,
that might lead to a memory leak, but it won't cause a
crash. It also doesn't rely on "roots".

It does relies on reference counting to achieve this, however;
if something keeps a pointer to an object without increasing
its refcount, that could lead to a crash.

> Of course, if you're paying this full price anyhow, why bother paying
> the additional price of reference-counting?

In the case of CPython, it's because the cyclic GC is actually
built on top of the refcounting mechanism and wouldn't work
without it. So it's not really an additional cost at all.

--
Greg

Random832

unread,
Apr 26, 2013, 9:43:29 PM4/26/13
to python...@python.org
On 04/26/2013 03:45 PM, Bruce Leban wrote:

On Fri, Apr 26, 2013 at 12:30 PM, Andrew Svetlov <andrew....@gmail.com> wrote:
On Fri, Apr 26, 2013 at 9:54 PM,  <rand...@fastmail.us> wrote:
> What about a with expression?
>
> boolean = x.use() with x as open(resource)

interesting idea. I see nothing bad with proposed construction.
Any objections? I've miss something?

The most obvious thing as that 'as' is backwards from the with statement. Also this requires you to come up with a name which you have to repeat while

with(open(resource)).use()

doesn't. On the other hand, it allows you to do:

result = [x.foo(), x.bar()] with open(resource) as x

which opens the way to freewheeling inline assignment: 

@contextmanager
def assign(x):
    yield x

result = (-b + sqrt(b*b - 4 * a * c)) / (2 * a) with assign(3) as a with assign(4) as b with assign(5) as c

I don't think that's a good thing.

If someone wanted to do that, lambda's already here.

(lambda a, b, c: (-b + sqrt(b*b - 4 * a * c)) / (2 * a))(3,4,5)

The original syntax I was going to suggest would have been something more along the lines of (with open(resource) as x: x.use()), but figured the syntax I ended up posting would be more pythonic (by analogy to list comprehensions or if-else)

The purpose of this would be to call cleanup code. Anything can be misused.

Andrew Barnert

unread,
Apr 26, 2013, 11:35:49 PM4/26/13
to anatoly techtonik, python-ideas
You're missing something fundamental here. 

PySide isn't advertising that you can automatically clean up objects just by leaking them; it's warning you that you must retain objects if you don't want them cleaned up. 

Put another way: correct Python code cannot assume that objects _do_ outlive their last reference... But that doesn't mean you can assume they _don't_, either. You have to assume that both are possible, and code accordingly.

And there is really no obvious change to the language that would "fix" that in either direction.

Or, rather, there is: make it easier to do the right thing explicitly. Which is exactly what with statements are for.
Reply all
Reply to author
Forward
0 new messages