473,395 Members | 1,639 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

Block-structured resource handling via decorators

When handling resources in Python, where the scope of the resource is
known, there seem to be two schools of thought:

(1) Explicit:
f = open(fname)
try:
# ...
finally:
f.close()

(2) Implicit: let the GC handle it.

I've come up with a third method that uses decorators to achieve a
useful mix between the two. The scope of the resource is clear when
reading the code (useful if the resource is only needed in part of a
function), while one does not have to write an explicit cleanup. A
couple of examples:

@withFile(fname, 'w')
def do(f):
# ... write stuff into file f ...

@withLock(aLock):
def do():
# ... whatever you needed to do once the lock was acquired,
# safe in the knowledge it will be released afterwards ...

(The name "do" is arbitrary; this method has the "mostly harmless"
side-effect of assigning None to a local variable with the function
name.)

I find it clear because I come from a C++/C#/Java background, and I
found C#'s using-blocks to very useful, compared to the explicit
finallys of Java. I know that Python's deterministic finalization sort
of achieves the same effect, but I had been led to believe there were
complications in the face of exceptions.

The implementation is easily extensible: a handler for a new type of
resource can be written in as a couple of lines. For the examples above:

class withFile(blockScopedResource):
init, cleanup = open, 'close'

It's so simple I was wondering why I haven't seen it before. Possibly:
it's a stupid idea and I just can't see why;
everyone knows about it except me;
it's counter-intuitive (that's not the way decorators were intended);
it's "writing C# in Python" or in some other way unPythonic;
I've actually had an idea that is both Original and non-Dumb.

If the last is the case, can someone let me know, and I'll put up the
code and explain how it all works. On the other hand, if there is
something wrong with it, please can someone tell me what it is?

Thanks

John Perks
Jul 29 '05 #1
5 1702
John Perks:
When handling resources in Python, where the scope of the resource is
known, there seem to be two schools of thought:
...


This is part of what PEP 343 addresses:
http://www.python.org/peps/pep-0343.html

Neil
Jul 30 '05 #2
"John Perks and Sarah Mount" <jo**********@estragon.freeserve.co.uk> writes:
When handling resources in Python, where the scope of the resource is
known, there seem to be two schools of thought:

(1) Explicit:
f = open(fname)
try:
# ...
finally:
f.close()

(2) Implicit: let the GC handle it.
The only cases I see the first school of thought is when the resource
in question is "scarce" in some way. For example, most OS's place a
limit on the number of open files a process can have, some rather
tight. CPython's garbage collector will close an open file when it
leaves scope. Jython's GC will close it when the file is collected,
but you have no idea of when that will be, and an "open" failing won't
trigger a GC. So in this case, the first form is less likely to fail
unexpectedly.
I've come up with a third method that uses decorators to achieve a
useful mix between the two. The scope of the resource is clear when
reading the code (useful if the resource is only needed in part of a
function), while one does not have to write an explicit cleanup. A
couple of examples:

@withFile(fname, 'w')
def do(f):
# ... write stuff into file f ...

@withLock(aLock):
def do():
# ... whatever you needed to do once the lock was acquired,
# safe in the knowledge it will be released afterwards ...

The implementation is easily extensible: a handler for a new type of
resource can be written in as a couple of lines. For the examples above:

class withFile(blockScopedResource):
init, cleanup = open, 'close'

It's so simple I was wondering why I haven't seen it before. Possibly:
it's a stupid idea and I just can't see why;
everyone knows about it except me;
it's counter-intuitive (that's not the way decorators were intended);
it's "writing C# in Python" or in some other way unPythonic;
I've actually had an idea that is both Original and non-Dumb.


Well, I'd say that using a string for cleanup and a function for init
is unpythonic. But the general idea seems to be a good one. Making it
easy to deal with resources that must be explicitly released is a good
thing. The question is whether having to turn your scope into a
function to do this is more trouble than it's worth.

I'd certainly be interested in seeing the implementation.

<mike
--
Mike Meyer <mw*@mired.org> http://www.mired.org/home/mwm/
Independent WWW/Perforce/FreeBSD/Unix consultant, email for more information.
Jul 30 '05 #3
Mike Meyer <mw*@mired.org> writes:
When handling resources in Python, where the scope of the resource is
known, there seem to be two schools of thought:
(1) Explicit: ...
(2) Implicit: let the GC handle it.


The only cases I see the first school of thought is when the resource
in question is "scarce" in some way. For example, most OS's place a
limit on the number of open files a process can have, some rather
tight. CPython's garbage collector will close an open file when it
leaves scope. Jython's GC will close it when the file is collected,
but you have no idea of when that will be, and an "open" failing won't
trigger a GC. So in this case, the first form is less likely to fail
unexpectedly.


I thought there was a Zen thing somewhere saying that Python is not
Perl. One of Perl's silliest tenets is that the implementation
defines the language. There are already 4 different Python
implementations (CPython, Jython, Pypy, IronPython) and probably more
on the way. We should take the view that the manual, not the
implementation, defines the language.

The manual doesn't specify anything about reference counting or GC
happening as soon as something goes out of scope. That is a GOOD
thing since requiring CPython-like behavior would eliminate many good
GC techniques, including some that are both more efficient and easier
for extension writers to deal with. Since reference counting is not
in the manual, it is not part of the language, it's subject to change
at any time in any implementation, and non-throwaway Python apps
should not depend on it.

On the other hand, the try/finally explicit release is cumbersome and
non-tasty.

Therefore, the correct solution is a language extension along the
lines of PEP 343. I haven't studied PEP 343 specifically enough to
say that I think every detail of it is the right thing, but at least
it is the right idea.

Jul 30 '05 #4
I will shamelessly point out my decorator module:
http://www.phyast.pitt.edu/~micheles.../decorator.zip

The documentation is here:
http://www.phyast.pitt.edu/~micheles...umentation.htm

There is an example of redirecting stdout which is
relevant to this thread.
HTH,

Michele Simionato

Jul 30 '05 #5
> The only cases I see the first school of thought is when the resource
in question is "scarce" in some way.
By "resource" I meant anything with some sort of acquire/release
semantics. There may be plenty of threading.Locks available, but it's
still important that a given Lock is released when not needed.

For example, most OS's place a
class withFile(blockScopedResource):
init, cleanup = open, 'close'

Well, I'd say that using a string for cleanup and a function for init
is unpythonic.


I could have specified cleanup as lambda f:f.close(), but as I thought
it might be quite common to call a method on the resourse for cleanup,
if a string is specified a method of that name is used instead.
The question is whether having to turn your scope into a
function to do this is more trouble than it's worth.
Needing one slightly contrived-looking line (the def) vs a try-finally
block with explicit cleanup code? I know which I'd prefer, but for all I
know I could in a minority of 1 here.

I'd certainly be interested in seeing the implementation.


And so you shall...

I start with the base class. It does all the work, everything else is
just tweaks for convenience. Normally, then, you wouldn't need to bother
with all the __init__ params.

class blockScopedResource(object):
def __init__(self, init, cleanup,
initArgs, initKwargs, cleanupArgs, cleanupKwargs,
passResource, resourceIsFirstArg):

self.init = init # function to get resource
self.cleanup = cleanup # function to release resource
self.initArgs, self.initKwargs = initArgs, initKwargs
self.cleanupArgs, self.cleanupKwargs = cleanupArgs,
cleanupKwargs
self.passResource = passResource # whether resource is passed
into block
self.resourceIsFirstArg = resourceIsFirstArg # whether resource
is arg to init,
# rather than returned from it

def __call__(self, block):
resource = self.init(*self.initArgs, **self.initKwargs)
if self.resourceIsFirstArg:
resource = self.initArgs[0]

try:
if self.passResource:
block(resource)
else:
block()
finally:
self.cleanup(resource, *self.cleanupArgs,
**self.cleanupKwargs)

But this still won't do conveniently for files and locks, which are my
motivating examples.

The simpleResource class constructor gets its setup from attributes on
the type of the object being created, with sensible defaults being set
on simpleResource itself. As stated above, if a string is supplied as
init or cleanup, it is treated as a method name and that method is used
instead.

def stringToMethod(f):
# Getting the attribute from the class may have wrapped it into
# an unbound method; in this case, unwrap it
if isinstance(f, types.MethodType) and f.im_self is None:
f = f.im_func
if not isinstance(f, basestring): return f
def helper(resource, *args, **kwargs):
return getattr(resource, str(f))(*args, **kwargs)
return helper

class simpleResource(blockScopedResource):
def __init__(self, *initArgs, **initKwargs):
# get attributes off type
t = type(self)
blockScopedResource.__init__(self,
stringToMethod(t.init), stringToMethod(t.cleanup),
initArgs, initKwargs, t.cleanupArgs, t.cleanupKwargs,
t.passResource, t.resourceIsFirstArg)

# defaults supplied here
cleanupArgs, cleanupKwargs = (), {}
passResource = True
resourceIsFirstArg = False
Then useful implementations can be written by:

class withFile(simpleResource):
init, cleanup = open, 'close'

class withLock(simpleResource):
init, cleanup = 'acquire', 'release'
passResource = False
resourceIsFirstArg = True
And new ones can be created with a similar amount of effort.

Of course, one-liners can be done without using the decorator syntax:

withLock(aLock)(lambda:doSomething(withAnArg))

Gotcha: If you stack multiple resource-decorator it won't do what you
want:

# !!! DOESN'T WORK !!!
@withLock(aLock)
@withLock(anotherLock)
def do():
# ...

Either nest them explicitly (causing your code you drift ever further to
the right):
@withLock(aLock)
def do():
@withLock(anotherLock)
def do():
# ...

Or come up with a multiple-resource handler, which shouldn't be too
hard:

@withResources(withLock(aLock), withLock(anotherLock),
withFile('/dev/null'))

But I'll get round to that another day.


Jul 30 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

699
by: mike420 | last post by:
I think everyone who used Python will agree that its syntax is the best thing going for it. It is very readable and easy for everyone to learn. But, Python does not a have very good macro...
4
by: Christopher | last post by:
This should be a quick one. URL: http://cfa-www.harvard.edu/~cpilman/Stuff/flush.html Code: ============================= <!DOCTYPE HTML Public "-//W3C//DTD HTML 4.01//EN">...
2
by: TadPole | last post by:
Hi all, My main problems are::::::::: 1. Set a value within a block container that can be used and changed by subsequent templates/block-containers/tables etc.. 2. get/determine/find the...
7
by: seamoon | last post by:
Hi, I'm doing a simple compiler with C as a target language. My language uses the possibility to declare variables anywhere in a block with scope to the end of the block. As I remembered it this...
6
by: ste.paoletti | last post by:
hi, I have read that browsers support block element inside inline element but the css is not valid. Someone can tell me more about? What do you think? Thanks.
2
by: morrell | last post by:
I have a request to find out is there an easy way to solve this little poblem. ___________________ | Block 1 | | | | | | |...
8
by: Alvin | last post by:
I'm making a very simple game in SDL, and I'm not asking for SDL help I hope - this looks like something C++ related, so I'll ask here. I have a class for a simple block, or tile, in the game,...
6
by: dave8421 | last post by:
Hi, I'm a bit confused about the definition about "Prinicpal Block Boxes" in CSS 2.1 draft specification. ( http://www.w3.org/TR/2006/WD-CSS21-20061106 ) <pre> 9.2.1 Block-level elements and...
2
by: Bob Greschke | last post by:
This is the idea Block = pack("240s", "") Block = pack(">H", W) Block = pack(">H", X) Block = pack(">B", Y) Block = pack(">H", Z)) but, of course, Block, a str, can't be sliced. The real...
15
by: cssExp | last post by:
hello, Rather than going on a wild explanation on what's the the problem, it'll be much quicker and easier if i let you look at it yourself, so I'll post my page source (actual contents taken out,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.