Re: fetch - a simple function using python's ftplib

Guido.van.Rossum@cwi.nl
Mon, 18 Oct 1993 10:13:53 +0100

> I hadn't noticed ftplib was even there until it came up on the list.
> So I hacked out a fetch function. The *intention* was to handle
> URL's and/or the pathnames in T.Brannon's paper, [...]

Thanks! (There is indeed URL parsing support in demo/www/wwwlib.py
and/or elsewhere in demo/www.)

> There is a question for Guido (or some other master of python internals)
> burried in the code: When I moved mt test code into a test function
> ( so that I could have some cleanup - otherwise when I types 'q' to
> more, my terminal got zonked! ) and out of the modules global scope,
> I had to add the "global" declaration - otherwise I got a NameError.
> I suppose this is a subtle effect of dynamic scoping and the way the
> function is used as a callback function in another scope. But I would
> welcome it if someone can explain what's going on in more detail.
[referring to:]
> # "global m" appears to be necessary because mwrite callback
> # function is called in a different context from the one
> # in which it is defined. Without it, fetch will raise a
> # NameError exception. ( The wonders of Dynamic scoping? )
>
> def test2():
> global m
> m = popen( 'more', 'w' )
> def mwrite( line ): m.write( line + '\n' )
> try:
> fetch( 'uvaarpa.virginia.edu:/pub/hosts', mwrite, 'text' )
> finally: m.close()

This is because nested function definitions don't have access to the
local variables of the surrounding block -- only to the globals of the
containing module. This is done so that lookup of globals doesn't
have to walk a chain of dictionaries -- as in C, there are just two
nested scopes: locals and globals (and beyond this, built-ins).
Therefore, nested functions have only a limited use. This was a
deliberate decision, based upon experience with languages allowing
arbitraries nesting such as Pascal and both Algols -- code with too
many nested scopes is about as readable as code with too many GOTOs.
And, of course, in Python, the "proper" way is to use an
object-oriented programming style:

def test2():
class C:
def __init__(self, m ): self.m = m
def write( self, line ): self.m.write( line + '\n' )
def close( self ): self.m.close()
x = C( popen( 'more', 'w' ) )
try:
fetch( 'uvaarpa.virginia.edu:/pub/hosts', x.write, 'text' )
finally: x.close()

--Guido van Rossum, CWI, Amsterdam <Guido.van.Rossum@cwi.nl>