[Checkins] SVN: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/
refactoring of kirbifetch
Luciano Ramalho
luciano at ramalho.org
Sun Aug 5 22:29:54 EDT 2007
Log message for revision 78613:
refactoring of kirbifetch
Changed:
D Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_fetch.py
D Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_parse.py
A Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/fetch.py
A Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/interfaces.py
A Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon.py
A Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon_config.py
D Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/comp-sci-10.txt
U Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/dummy_server.py
-=-
Deleted: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_fetch.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_fetch.py 2007-08-05 22:44:43 UTC (rev 78612)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_fetch.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -1,188 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-
-from lxml import etree
-from twisted.internet import reactor
-from twisted.web import xmlrpc, client
-
-from urllib import quote
-from time import sleep
-import sys
-from StringIO import StringIO
-
-"""
-Structure of the AmazonECS XML response:
-
-ItemLookupResponse
- OperationRequest
- (...)
- Items
- Request
- IsValid
- ItemLookupRequest
- ItemId
- ResponseGroup
- (Errors)
- (Error)
- (Code)
- (Message)
- (Item)
- (ItemAttributes)
- (Author)
- (Creator Role=...)
-
-Notes:
-- Errors element occurs when ISBN is non-existent;
- in that case, Code contains the string "AWS.InvalidParameterValue"
-- Author element is not always present
-- Author element may be duplicated with the same content,
- except for whitespace; for example: ISBN=0141000511
-"""
-
-FIELD_MAP = [
- # Book schema -> Amazon ECS element
- ('title', 'Title'),
- ('isbn13', 'EAN'),
- ('edition', 'Edition'),
- ('publisher', 'Publisher'),
- ('issued', 'PublicationDate'),
- ('subject', 'DeweyDecimalNumber'),
- ]
-
-CREATOR_TAGS = ['Author', 'Creator']
-
-AMAZON_CODE_NO_MATCH = 'AWS.ECommerceService.NoExactMatches'
-
-# if True, processed XML files will be saved
-KEEP_FILES = True
-# directory where XML files will be saved (include trailing slash)
-SAVE_DIR = 'amazon_xml/'
-
-ITEMS_PER_REQUEST = 3 # maximum from Amazon is 10
-
-
-def test_parse(xml):
- xml = file(sys.argv[1])
- dic = parse(xml)
- pprint(dic)
-
-class AmazonECS(object):
-
- base_url = """http://ecs.amazonaws.com/onca/xml"""
-
- def __init__(self, AWSAccessKeyId, AssociateTag=None):
- self.base_params = { 'Service':'AWSECommerceService',
- 'AWSAccessKeyId':AWSAccessKeyId, }
- if AssociateTag:
- self.base_params['AssociateTag'] = AssociateTag
- self.xml = ''
- self.http_response = {}
-
- def buildURL(self, **kw):
- query = []
- kw.update(self.base_params)
- for key, val in kw.items():
- query.append('%s=%s' % (key,quote(val)))
- return self.base_url + '?' + '&'.join(query)
-
- def itemLookup(self,itemId,response='ItemAttributes'):
- params = { 'Operation':'ItemLookup',
- 'ItemId':itemId,
- 'ResponseGroup':response
- }
- return self.buildURL(**params)
-
- def itemSearch(self,query,response='ItemAttributes'):
- params = { 'Operation':'ItemSearch',
- 'SearchIndex':'Books',
- 'Power':query,
- 'ResponseGroup':response
- }
- return self.buildURL(**params)
-
- def isbnSearch(self, isbns):
- query = 'isbn:' + ' or '.join(isbns)
- return self.itemSearch(query)
-
- def nsPath(self, path):
- parts = path.split('/')
- return '/'.join([self.ns+part for part in parts])
-
- def parse(self):
- xml = StringIO(self.xml)
- tree = etree.parse(xml)
- root = tree.getroot()
- # get the XML namespace from the root tag
- self.ns = root.tag.split('}')[0] + '}'
- request = root.find(self.nsPath('Items/Request'))
- error_code = request.findtext(self.nsPath('Errors/Error/Code'))
- if error_code is None:
- book_list = []
- for item in root.findall(self.nsPath('Items/Item/ItemAttributes')):
- book_dic = {}
- for field, tag in FIELD_MAP:
- elem = item.find(self.ns+tag)
- if elem is not None:
- book_dic[field] = elem.text
- creators = []
- for tag in CREATOR_TAGS:
- for elem in item.findall(self.ns+tag):
- if elem is None: continue
- role = elem.attrib.get('Role')
- if role:
- creator = '%s (%s)' % (elem.text, role)
- else:
- creator = elem.text
- creators.append(creator)
- if creators:
- book_dic['creators'] = creators
- book_list.append(book_dic)
- return book_list
-
- elif error_code == AMAZON_CODE_NO_MATCH:
- return []
- else:
- raise EnvironmentError, error_code
-
-def getPending(pac):
- return pac.callRemote('list_pending_isbns').addCallback(gotPending)
-
-def gotPending(isbns):
- print 'get: ', ' '.join(isbns)
- i = 0
- if isbns:
- # fetch at most 10 isbns per request, and one request per second
- for i, start in enumerate(range(0,len(isbns),ITEMS_PER_REQUEST)):
- end = start + ITEMS_PER_REQUEST
- reactor.callLater(i, getAmazonXml, isbns[start:end])
- reactor.callLater(i+1, getPending, pac)
-
-def getAmazonXml(isbns):
- print 'fetch:', ' '.join(isbns)
-
-
-
-def gotAmazonXml(xml):
-
- pac.callRemote('del_pending_isbns',isbns).addCallback(deletedPending)
- if KEEP_FILES:
- name = '_'.join(isbns)+'.xml'
- out = file(SAVE_DIR+name,'w')
- out.write(response.replace('><','>\n<'))
- out.close()
- return response
-
-def deletedPending(n):
- print 'deleted:', n
-
-
-if __name__ == '__main__':
- from amazon_config import ACCESS_KEY_ID, ASSOCIATE_TAG
-
- pac = xmlrpc.Proxy('http://localhost:8080/RPC2')
-
-
- reactor.callLater(1, checkPending, pac)
- print 'reactor start'
- reactor.run()
- print 'reactor stop'
Deleted: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_parse.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_parse.py 2007-08-05 22:44:43 UTC (rev 78612)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_parse.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -1,103 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-
-"""
-Structure of the AmazonECS XML response:
-
-ItemLookupResponse
- OperationRequest
- (...)
- Items
- Request
- IsValid
- ItemLookupRequest
- ItemId
- ResponseGroup
- (Errors)
- (Error)
- (Code)
- (Message)
- (Item)
- (ItemAttributes)
- (Author)
- (Creator Role=...)
-
-Notes:
-- Errors element occurs when ISBN is non-existent;
- in that case, Code contains the string "AWS.InvalidParameterValue"
-- Author element is not always present
-- Author element may be duplicated with the same content,
- except for whitespace; for example: ISBN=0141000511
-"""
-
-try:
- from lxml import etree
-except ImportError:
- try:
- import cElementTree as etree
- except ImportError:
- try:
- import elementtree.ElementTree as etree
- except ImportError:
- print "Failed to import ElementTree from any known place"
-
-FIELD_MAP = [
- # Book schema -> Amazon ECS element
- ('title', 'Title'),
- ('isbn13', 'EAN'),
- ('edition', 'Edition'),
- ('publisher', 'Publisher'),
- ('issued', 'PublicationDate'),
- ]
-
-CREATOR_TAGS = ['Author', 'Creator']
-
-
-AMAZON_INVALID_PARAM = 'AWS.InvalidParameterValueXX'
-
-
-def nsPath(ns, path):
- parts = path.split('/')
- return '/'.join([ns+part for part in parts])
-
-def parse(xml):
- tree = etree.parse(xml)
- raiz = tree.getroot()
- # get the XML namespace from the root tag
- ns = raiz.tag.split('}')[0] + '}'
- request = raiz.find(nsPath(ns,'Items/Request'))
- error_code = request.findtext(nsPath(ns,'Errors/Error/Code'))
- if error_code is None:
- items = raiz.findall(nsPath(ns,'Items/Item'))
- #TODO: treat multiple Item elements in Items
- item = items[0].find(ns+'ItemAttributes')
- book_dic = {}
- for field, tag in FIELD_MAP:
- elem = item.find(ns+tag)
- if elem is not None:
- book_dic[field] = elem.text
- creators = []
- for tag in CREATOR_TAGS:
- for elem in item.findall(ns+tag):
- if elem is None: continue
- role = elem.attrib.get('Role')
- if role:
- creator = '%s (%s)' % (elem.text, role)
- else:
- creator = elem.text
- creators.append(creator)
- if creators:
- book_dic['creators'] = creators
- return book_dic
-
- elif error_code == AMAZON_INVALID_PARAM:
- return None
- else:
- raise LookupError, error_code
-
-if __name__ == '__main__':
- import sys
- from pprint import pprint
- xml = file(sys.argv[1])
- dic = parse(xml)
- pprint(dic)
Copied: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/fetch.py (from rev 78596, Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/amazon_fetch.py)
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/fetch.py (rev 0)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/fetch.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from lxml import etree
+from twisted.internet import reactor
+from twisted.web import xmlrpc, client
+from os import path
+
+import source_amazon as amazon
+
+from pprint import pprint
+
+KEEP_FILES = True
+# directory where XML files will be saved (include trailing slash)
+
+POLL_INTERVAL = 1 # minimum seconds to wait between calls to fetch.poll
+
+class Fetch(object):
+
+ def __init__(self, xmlrpc_url, poll, callback, source):
+ self.pollServer = xmlrpc.Proxy(xmlrpc_url)
+ self.pollMethod = poll
+ self.callback = callback
+ self.source = source
+
+ def poll(self):
+ deferred = self.pollServer.callRemote(self.pollMethod)
+ deferred.addCallback(self.polled).addErrback(self.pollError)
+
+ def polled(self, isbns):
+ print 'polled: ', ' '.join(isbns)
+ i = 0
+ if isbns:
+ # fetch max_ids_per_request, and one request per second
+ for i, start in enumerate(range(0,len(isbns),
+ self.source.max_ids_per_request)):
+ end = start + self.source.max_ids_per_request
+ reactor.callLater(i, self.downloadItemsPage, isbns[start:end])
+ reactor.callLater(i+POLL_INTERVAL, self.poll)
+
+ def pollError(self, error):
+ print 'Error in deferred poll call:', error
+ # if there was an error, wait a bit longer to try again
+ reactor.callLater(POLL_INTERVAL*4, self.poll)
+
+ def downloadItemsPage(self, isbns):
+ url = self.source.buildMultipleBookDetailsURL(isbns)
+ deferred = client.getPage(url)
+ deferred.addCallback(self.downloadedItemsPage, isbns)
+ deferred.addErrback(self.downloadError, url)
+
+ def downloadedItemsPage(self, xml, isbns):
+ book_list = self.source.parseMultipleBookDetails(xml)
+ delay = 1
+ for book in book_list:
+ url = book.get('image_url')
+ if url:
+ filename = book.get('isbn13',book['source_item_id'])
+ filename += '.' + url.split('.')[-1]
+ deferred = client.getPage(url)
+ deferred.addCallback(self.downloadedImage, filename)
+ deferred.addErrback(self.downloadError, url)
+
+ if KEEP_FILES:
+ filename = '_'.join(isbns)+'.xml'
+ out = file(path.join(self.source.name,filename), 'w')
+ out.write(xml.replace('><','>\n<'))
+ out.close()
+ pprint(book_list)
+
+ def downloadedImage(self, bytes, filename):
+ # XXX: find a proper way to calculate the static image dir
+ dest = '../../..'
+ dest = path.join(dest,'src','kirbi','static','covers','large'
+ ,filename)
+ print 'saving: ', dest
+ out = file(dest, 'wb')
+ out.write(bytes)
+ out.close()
+
+ def downloadError(self, error, url):
+ print 'Error in deferred download (url=%s): %s' % (url, error)
+
+ def deletedPending(n):
+ print 'deleted:', n
+
+
+if __name__ == '__main__':
+ xmlrpc_url = 'http://localhost:8080/RPC2'
+ poll_method = 'dump_pending_isbns'
+ callback = 'add_book'
+ fetcher = Fetch(xmlrpc_url, poll_method, callback, amazon.Source())
+ reactor.callLater(0, fetcher.poll)
+ print 'reactor start'
+ reactor.run()
+ print 'reactor stop'
+
Added: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/interfaces.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/interfaces.py (rev 0)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/interfaces.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -0,0 +1,43 @@
+from zope.interface import interface
+from zope.schema import TextTline, Int, ASCII
+
+# XXX This interface is currently not used.
+# It's a draft for future componentization of kirbifetch
+
+class IMetadataSource(Interface):
+ """An online book metadata source."""
+
+ name = DottedName(
+ title = u"Name to identify source in metadata records",
+ required = 1,
+ )
+
+ max_ids_per_request = Int(
+ title = u"Maximum number of ids per detail request",
+ default = 1,
+ )
+
+ def buildBbookSearchURL(query):
+ """Return the URL to be used to search for book ids."""
+
+ def buildBookDetailsURL(book_id):
+ """Return the URL to be used to get book details."""
+
+ def buildMultipleBookDetailsURL(isbns):
+ """Return the URL to be used to get details for multiple books."""
+
+ def parseBookSearch(xml):
+ """Extract the book ids referenced in a page."""
+
+ def parseBookDetails(xml):
+ """Extract book metadata from a book details page."""
+
+ def parseMultipleBookDetails(xml):
+ """Extract book metadata from a response containing multiple records.
+ This is only implemented when max_ids_per_request > 1.
+ (Currently Amazon.com is the only source we know where this happens)
+ """
+
+
+
+
Added: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon.py (rev 0)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from lxml import etree
+from twisted.internet import reactor
+from twisted.web import xmlrpc, client
+
+from urllib import quote
+from time import sleep
+import sys
+from StringIO import StringIO
+
+from source_amazon_config import ACCESS_KEY_ID, ASSOCIATE_TAG
+
+"""
+Structure of the AmazonECS XML response:
+
+ItemLookupResponse
+ OperationRequest
+ (...)
+ Items
+ Request
+ IsValid
+ ItemLookupRequest
+ ItemId
+ ResponseGroup
+ (Errors)
+ (Error)
+ (Code)
+ (Message)
+ (Item)
+ (ItemAttributes)
+ (Author)
+ (Creator Role=...)
+
+Notes:
+- Errors element occurs when ISBN is non-existent;
+ in that case, Code contains the string "AWS.InvalidParameterValue"
+- Author element is not always present
+- Author element may be duplicated with the same content,
+ except for whitespace; for example: ISBN=0141000511
+"""
+
+FIELD_MAP = [
+ # Book schema -> Amazon ECS element path, relative to Item element
+ ('title', 'ItemAttributes/Title'),
+ ('isbn13', 'ItemAttributes/EAN'),
+ ('edition', 'ItemAttributes/Edition'),
+ ('publisher', 'ItemAttributes/Publisher'),
+ ('issued', 'ItemAttributes/PublicationDate'),
+ ('subject', 'ItemAttributes/DeweyDecimalNumber'),
+ ('image_url', 'LargeImage/URL'),
+ ('source_url', 'DetailPageURL'),
+ ('source_item_id', 'ASIN'),
+ ]
+
+CREATOR_TAGS = ['ItemAttributes/Author', 'ItemAttributes/Creator']
+
+AMAZON_CODE_NO_MATCH = 'AWS.ECommerceService.NoExactMatches'
+
+class Source(object):
+
+ name = 'amazon.com'
+ max_ids_per_request = 3
+
+
+ base_url = """http://ecs.amazonaws.com/onca/xml"""
+
+ def __init__(self):
+ self.base_params = { 'Service':'AWSECommerceService',
+ 'AWSAccessKeyId':ACCESS_KEY_ID,
+ 'AssociateTag': ASSOCIATE_TAG
+ }
+ self.xml = ''
+ self.http_response = {}
+
+ def buildURL(self, **kw):
+ query = []
+ kw.update(self.base_params)
+ for key, val in kw.items():
+ query.append('%s=%s' % (key,quote(val)))
+ return self.base_url + '?' + '&'.join(query)
+
+ def buildItemLookupURL(self,itemId,response='ItemAttributes'):
+ params = { 'Operation':'ItemLookup',
+ 'ItemId':itemId,
+ 'ResponseGroup':response
+ }
+ return self.buildURL(**params)
+
+ def buildItemSearchURL(self,query,response='ItemAttributes,Images'):
+ params = { 'Operation':'ItemSearch',
+ 'SearchIndex':'Books',
+ 'Power':query,
+ 'ResponseGroup':response
+ }
+ return self.buildURL(**params)
+
+ def buildMultipleBookDetailsURL(self, isbns):
+ query = 'isbn:' + ' or '.join(isbns)
+ return self.buildItemSearchURL(query)
+
+ def nsPath(self, *paths):
+ parts = []
+ for path in paths:
+ parts.extend(path.split('/'))
+ return '/'.join([self.ns+part for part in parts])
+
+ def parseMultipleBookDetails(self, xml):
+ xml = StringIO(xml)
+ tree = etree.parse(xml)
+ root = tree.getroot()
+ # get the XML namespace from the root tag
+ self.ns = root.tag.split('}')[0] + '}'
+ request = root.find(self.nsPath('Items/Request'))
+ error_code = request.findtext(self.nsPath('Errors/Error/Code'))
+ if error_code is None:
+ book_list = []
+ for item in root.findall(self.nsPath('Items/Item')):
+ book_dic = {'source':self.name}
+ for field, tag in FIELD_MAP:
+ elem = item.find(self.nsPath(tag))
+ if elem is not None:
+ book_dic[field] = elem.text
+ creators = []
+ for tag in CREATOR_TAGS:
+ for elem in item.findall(self.nsPath(tag)):
+ if elem is None: continue
+ role = elem.attrib.get('Role')
+ if role:
+ creator = '%s (%s)' % (elem.text, role)
+ else:
+ creator = elem.text
+ creators.append(creator)
+ if creators:
+ book_dic['creators'] = creators
+ book_list.append(book_dic)
+ return book_list
+
+ elif error_code == AMAZON_CODE_NO_MATCH:
+ return []
+ else:
+ raise EnvironmentError, error_code
+
Added: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon_config.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon_config.py (rev 0)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/source_amazon_config.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -0,0 +1,9 @@
+ASSOCIATE_TAG = 'circulante-20'
+ACCESS_KEY_ID = '13W2MMDG65QJJK9GG402'
+
+#UK
+#ASSOCIATE_TAG = 'circulante-21'
+#DE
+#ASSOCIATE_TAG = 'circulante0f-21'
+#FR
+#ASSOCIATE_TAG = 'circulante07-21'
Deleted: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/comp-sci-10.txt
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/comp-sci-10.txt 2007-08-05 22:44:43 UTC (rev 78612)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/comp-sci-10.txt 2007-08-06 02:29:53 UTC (rev 78613)
@@ -1,11 +0,0 @@
-9780471311980
-9781558607743
-9780471135340
-9780471195726
-9780201400090
-9780471923046
-9780471936084
-9780471942436
-9780471624639
-9780471601753
-
Modified: Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/dummy_server.py
===================================================================
--- Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/dummy_server.py 2007-08-05 22:44:43 UTC (rev 78612)
+++ Sandbox/luciano/kirbi/kirbifetch/src/kirbifetch/tests/dummy_server.py 2007-08-06 02:29:53 UTC (rev 78613)
@@ -2,36 +2,71 @@
import sys
from SimpleXMLRPCServer import SimpleXMLRPCServer
+from pprint import pprint
PORT = 8080
-def list_pending_isbns():
- return pending
+class Server(object):
+ # pending will behave like an ordered set (no duplicates)
+ pending = []
-def del_pending_isbns(isbns):
- deleted = 0
- for isbn in isbns:
- if isbn in pending:
- pending.remove(isbn)
- deleted += 1
- return deleted
+ def __init__(self, pending = None):
+ if pending is not None:
+ self.pending = pending
+ def add_pending_isbns(self, isbns):
+ print 'add%s' % isbns
+ added = 0
+ for isbn in isbns:
+ if isbn not in self.pending:
+ self.pending.append(isbn)
+ added += 1
+ return added
+
+ def del_pending_isbns(self, isbns):
+ print 'del%s' % isbns
+ deleted = 0
+ for isbn in isbns:
+ if isbn in self.pending:
+ self.pending.remove(isbn)
+ deleted += 1
+ return deleted
+
+ def dump_pending_isbns(self, max=0):
+ print 'dump%s' % self.pending
+ if max == 0:
+ max = len(self.pending)
+ dump = self.pending[:max]
+ self.pending = self.pending[max:]
+ return dump
+
+ def show_pending_isbns(self):
+ print 'list%s' % self.pending
+ return self.pending
+
+ def add_book(self, book):
+ pprint(book)
+
+
if __name__=='__main__':
- if len(sys.argv) != 2:
- print 'usage: %s <filename>' % sys.argv[0]
+ if len(sys.argv) not in [2,3]:
+ print 'usage: %s <filename> [<n>]' % sys.argv[0]
print ' <filename> names a text file containing'
print ' ISBN-13 numbers, one per line'
+ print ' <n> (optional) max ISBN numbers loaded'
sys.exit()
+
+ pending = file(sys.argv[1]).read().split()
+ if len(sys.argv) == 3:
+ pending = pending[:int(sys.argv[2])]
- pending = file(sys.argv[1]).read().split()
+ srv = Server(pending)
- server = SimpleXMLRPCServer(("localhost", PORT))
- server.register_introspection_functions()
+ xmlrpc = SimpleXMLRPCServer(("localhost", PORT))
- server.register_function(list_pending_isbns)
- server.register_function(del_pending_isbns)
+ xmlrpc.register_instance(srv)
print 'SimpleXMLRPCServer running on port %s...', PORT
- server.serve_forever()
+ xmlrpc.serve_forever()
More information about the Checkins
mailing list