I could have hacked in a strip of the "anonymous@" part, but since
the example didn't show the format used if a password was also
needed, I didn't bother to do it. Maybe if I dig out a copy of the
URL format, I'll do something else with it. But then, maybe that's
already in demo/www/wwwlib.py. Well - I wanted to try out the ftplib
routines anyway - I need to write a program that backups up PC's
thru NCSA Telnet's FTP server to unix tape. That has been on my
wish list, but I haven't done anything about it - now that I know
ftplib is there, I'll probably give it a try when I have a bit of
spare time.
The error handling is not as clean and neat as I would probaly
want in a final version. This has been minimally tested.
( and not on anything that refuses connections --- I'm relying
that the FTP class methods will do the right thing. )
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.
- Steve Majewski (804-982-0831) <sdm7g@Virginia.EDU>
- UVA Department of Molecular Physiology and Biological Physics
------------------------
#!/usr/local/bin/python
#
# fetch : a simple higher level function to python's ftplib
#
# given a pathname of format: host.part:/path/filename
# 'fetch( pathname )' will fetch pathname into current local directory
# 'fetch( pathname, open( FNAME, 'w').write )' will copy remote pathname
# into local FNAME, and
# 'fetch( pathname, func ) will send blocks pathname to output function func.
# ( see test examples at end )
#
# other option arg is one of:
# 'ascii', 'text', 'mode=ascii', 'mode=text'
# which change the transfer mode from binary to text
# ( note that retrlines strips tailing newlines )
#
# options to be added later:
# FTP().login() supports optional args for USER & PASSWD -
# add check for optional args "user=" and "pass=" to fetch.
#
#
# shell usage:
# fetch.py remotepathnames...
#
#
# - S.D.Majewski/UVA <sdm7g@Virginia.EDU>
from ftplib import FTP
import string
import posixpath
def null(): pass
FUNC_TYPES = ( type( null ), type( type ) )
# to include both <type 'function'> and <type 'builtin_function_or_method'>
TEXT_MODES = ( 'text', 'ascii', 'mode=text', 'mode=ascii' )
def fetch( fname, *opts ):
mode = 'bin' # default transfer mode
func = None
if opts :
for arg in opts:
if type( arg ) in FUNC_TYPES : func = arg
elif arg in TEXT_MODES : mode = 'text'
host, dir, file = parse( fname )
if not func : func = open( file, 'w' ).write
ftp = FTP().init( host )
ftp.login()
ftp.cwd( dir )
print ftp.nlst( file ), 'mode='+mode # this can be removed
if mode == 'text' :
try:
ftp.retrlines( 'RETR ' + file, func )
except IOError:
ftp.abort()
ftp.quit()
raise IOError
elif mode == 'bin' :
try:
ftp.retrbinary( 'RETR '+ file, func, 512 )
except IOError:
ftp.abort()
ftp.quit()
raise IOError
ftp.quit()
def parse( fname ):
list = string.splitfields( fname, ':' )
if len( list ) <> 2 : raise 'NoHostPart'
host = list[0]
dir, file = posixpath.split( list[1] )
return ( host, dir, file )
# example usage
#
from posix import popen
def test1( ):
path = 'uvaarpa.virginia.edu:/pub/hosts'
fetch( path )
# "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()
def test3():
z = popen( 'zcat | more', 'w' )
try:
fetch( 'uvaarpa.virginia.edu:/pub/rfc/rfc822.Z', z.write )
finally: z.close()
import sys
if sys.argv[1:] :
for file in sys.argv[1:] :
fetch( file )
# ---------- end fetch.py