Richard Jones' Log: Ugly python made better

Fri, 17 Dec 2004

Yeah, it bugged me too... so here's an eminently more readable version of With a couple of bugfixes thrown in too :)

It's still a bit more compact than I guess I'd like, but it's certainly much easier to understand what the code does now.

# v1.1 (C) 2004, Richard Jones
# with litle touch from Leonardo Santagada
# A slightly more readable version of (C) 2004, E.W. Felten
# (also handles binary transmissions now too)
# license:
# Usage:
#   python password server hostname portnum [otherurl]
# or
#   python password client serverurl pattern
import os, SimpleXMLRPCServer, re, hmac, sets, base64
from sys import argv
from xmlrpclib import ServerProxy

def gen_password(url):
   return[1], url).hexdigest()
def ls(pat=""):
    ''' List the files in the current working directory that optionall match
    a regular expression "pat". '''
    return [fn for fn in os.listdir(os.getcwd())
        if not pat or, fn)]

if argv[2] == "server":
    my_url = "http://"+argv[3]+":"+argv[4]

    # keep a list of servers we know about
    servers = sets.Set([my_url] + argv[5:])
    def update_servers(new_servers=[]):
        return list(servers)
    def discover(other_url):
        if other_url == my_url: return servers
        pw = gen_password(other_url)
        server = ServerProxy(other_url)
        return update_servers(server.list_servers(pw, update_servers()))

    # ask all our known servers about the servers *they* know about
    if servers: [discover(url) for url in list(servers)]

    # serve up the files
    def list_servers(password, arg=[]):
        if password == gen_password(my_url):
            return update_servers(arg)
    def list_files(password, arg):
        if password == gen_password(my_url):
            return ls(arg)
    def get_file(password, arg):
        if password == gen_password(my_url):
            f = file(arg)
                return base64.encodestring(
    server = SimpleXMLRPCServer.SimpleXMLRPCServer((argv[3], int(argv[4])))

# client - contact our server
for url in ServerProxy(argv[3]).list_servers(gen_password(argv[3])):
    # ask for the files we want, that we don't already have
    files = sets.Set(ServerProxy(url).list_files(gen_password(url), argv[4]))
    my_files = sets.Set(ls())
    for fn in files - my_files:
        # and fetch
        c = ServerProxy(url).get_file(gen_password(url), fn)
        f = file(fn, "wb")

Update: v1.1 incorporates some feedback from Leonardo Santagada.

Note: will not talk with servers.

Comment by Julian on Fri, 07 Jan 2005

Thanks for decrypting the original script!

If you have the time, could you explain how the lambda's used in the original map onto/do the same functionality as this clearer script?



Comment by Richard on Fri, 07 Jan 2005

It's unlikely I'll find time to revisit this, sorry.