demokritos-0.1.0/ 0000755 0000765 0000765 00000000000 10343115344 015012 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/lib/ 0000755 0000765 0000765 00000000000 10343115344 015560 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/lib/app/ 0000755 0000765 0000765 00000000000 10343115344 016340 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/lib/app/__init__.py 0000644 0000765 0000765 00000000330 10340507163 020446 0 ustar jtauber jtauber 0000000 0000000 from parse import parse_introspection, parse_entry, AtomProtocolException
from generate import generate_introspection, generate_collection
from model import Service, Workspace, CollectionReference, Collection, Member demokritos-0.1.0/lib/app/generate.py 0000644 0000765 0000765 00000002475 10335060165 020515 0 ustar jtauber jtauber 0000000 0000000 from atom import generate_entry
def generate_introspection(wfile, service):
wfile.write("""\n""")
for workspace in service.workspaces:
generate_workspace(wfile, workspace)
wfile.write("""\n""")
def generate_workspace(wfile, workspace):
wfile.write("""\n""" % workspace.title)
for collection_reference in workspace.collections:
generate_collection_reference(wfile, collection_reference)
wfile.write("""\n""")
def generate_collection_reference(wfile, collection_reference):
wfile.write("""\n%s\n%s\n\n""" % (collection_reference.title, collection_reference.href, collection_reference.member_type, collection_reference.list_template))
def generate_collection(wfile, collection):
wfile.write("""\n\n""")
wfile.write("""%s""" % collection.id)
wfile.write("""%s""" % collection.title)
wfile.write("""@@@""")
# @@@ sort entries by updated
for member in collection.members:
generate_entry(wfile, member)
wfile.write("""\n""")
demokritos-0.1.0/lib/app/model.py 0000644 0000765 0000765 00000003275 10335060165 020022 0 ustar jtauber jtauber 0000000 0000000 from atom import Entry
class Service:
# workspaces[]
def __init__(self):
self.workspaces = []
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
class Workspace:
# title
# collections[]
def __init__(self, title=None):
if title:
self.title = title
self.collections = []
# extension elements
self.children = []
def add_collection(self, title, href, member_type, list_template):
self.collections.append(CollectionReference(title, href, member_type, list_template))
def add(self, child):
self.children.append(child)
class CollectionReference:
# title
# href
# member_type
# list_template
def __init__(self, title=None, href=None, member_type=None, list_template=None):
# note that if arguments are not provided here, the attributes must
# be set individually.
self.title = title
self.href = href
self.member_type = member_type
self.list_template = list_template
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
def updated_sort_key(member):
return member.updated # @@@ need to normalize timezone
class Collection:
# members[]
def __init__(self):
self.members = []
def add_member(self, member):
self.members.insert(0, member)
self.members.sort(key=updated_sort_key, reverse=True)
class Member(Entry):
def __init__(self):
Entry.__init__(self) demokritos-0.1.0/lib/app/parse.py 0000644 0000765 0000765 00000011744 10340507163 020034 0 ustar jtauber jtauber 0000000 0000000 from xmlbase import HandlerBase, parse, Generic_ElementHandler, NAMESPACE_SEPARATOR
from atom.parse import Atom_EntryHandler, atom_ns
from model import *
APP_NAMESPACE = "http://purl.org/atom/app#"
def app_ns(name):
return APP_NAMESPACE + NAMESPACE_SEPARATOR + name
class AtomProtocolException(Exception):
pass
class AtomProtocol_HandlerBase(HandlerBase):
def no_children(self, name, attribute):
raise AtomProtocolException("%s not allowed here" % name)
class Introspection_RootHandler(AtomProtocol_HandlerBase):
def child(self, name, attributes):
if name == app_ns("service"):
Introspection_ServiceHandler(self.parser, self, name, attributes, self.inherited)
else:
raise AtomProtocolException("%s not allowed here" % name)
class Introspection_ServiceHandler(AtomProtocol_HandlerBase):
def init(self):
self.product = Service()
def child(self, name, attributes):
if name == app_ns("workspace"):
Introspection_WorkspaceHandler(self.parser, self, name, attributes, self.inherited)
else: # @@@ extensionElement
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
if len(self.product.workspaces) == 0:
raise AtomProtocolException("service must have at least one workspace")
self.parent_handler.product = self.product
AtomProtocol_HandlerBase.end(self, name)
class Introspection_WorkspaceHandler(AtomProtocol_HandlerBase):
def init(self):
self.product = Workspace()
if "title" not in self.attributes:
raise AtomProtocolException("workspace must have a title")
self.product.title = self.attributes["title"]
def child(self, name, attributes):
if name == app_ns("collection"):
Introspection_CollectionHandler(self.parser, self, name, attributes, self.inherited)
else: # @@@ extensionElement
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
self.parent_handler.product.workspaces.append(self.product)
AtomProtocol_HandlerBase.end(self, name)
class Introspection_CollectionHandler(AtomProtocol_HandlerBase):
def init(self):
self.product = CollectionReference()
if "title" not in self.attributes:
raise AtomProtocolException("collection must have 'title' attribute")
if "href" not in self.attributes:
raise AtomProtocolException("collection must have 'href' attribute")
for name, value in self.attributes.items():
if name == "title":
self.product.title = value
elif name == "href":
self.product.href = value
else:
raise AtomException("%s not allowed here" % name)
def child(self, name, attributes):
if name == app_ns("member-type"):
Introspection_MemberTypeHandler(self.parser, self, name, attributes, self.inherited)
elif name == app_ns("list-template"):
Introspection_ListTemplateHandler(self.parser, self, name, attributes, self.inherited)
else: # @@@ extensionElement
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
if self.product.member_type is None:
raise AtomProtocolException("collection must have a member-type")
if self.product.list_template is None:
raise AtomProtocolException("collection must have a list-template")
self.parent_handler.product.collections.append(self.product)
AtomProtocol_HandlerBase.end(self, name)
class Introspection_MemberTypeHandler(AtomProtocol_HandlerBase):
def end(self, name):
self.parent_handler.product.member_type = self.char_data
AtomProtocol_HandlerBase.end(self, name)
class Introspection_ListTemplateHandler(AtomProtocol_HandlerBase):
def end(self, name):
self.parent_handler.product.list_template = self.char_data
AtomProtocol_HandlerBase.end(self, name)
# this is version of Atom_RootHandler for the more lax entries that can be
# POSTed to a collection
class LaxEntry_RootHandler(AtomProtocol_HandlerBase):
def child(self, name, attributes):
if name == atom_ns("entry"):
LaxEntry_EntryHandler(self.parser, self, name, attributes, self.inherited)
else:
raise AtomProtocolException("document element must be entry")
# this is version of Atom_EntryHandler for the more lax entries that can be
# POSTed to a collection
class LaxEntry_EntryHandler(Atom_EntryHandler):
def validate(self):
# unlike superclass, this doesn't have restrictions on id, updated
if not self.got_title:
raise AtomException("entry must have title element")
def parse_introspection(xml):
return parse(xml, Introspection_RootHandler)
def parse_entry(xml):
return parse(xml, LaxEntry_RootHandler) demokritos-0.1.0/lib/atom/ 0000755 0000765 0000765 00000000000 10343115344 016520 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/lib/atom/__init__.py 0000644 0000765 0000765 00000000166 10335060165 020635 0 ustar jtauber jtauber 0000000 0000000 from parse import parse_atom, AtomException
from generate import generate_atom, generate_entry
from model import Entry demokritos-0.1.0/lib/atom/generate.py 0000644 0000765 0000765 00000011427 10316664521 020677 0 ustar jtauber jtauber 0000000 0000000 # @@@ dates and IRIs must not be surrounded by whitespace
import re
from model import Feed, Entry, InlineXHTMLContent
from xmlbase import generate_xml
def generate_atom(wfile, atom_object):
if isinstance(atom_object, Feed):
generate_feed(wfile, atom_object)
elif isinstance(atom_object, Entry):
generate_entry(wfile, atom_object, top=True)
else:
raise AtomException("unknown object '%s'" % atom_object)
def generate_feed(wfile, feed, top=True):
if top:
wfile.write("""\n""")
if top:
wfile.write("""\n""")
else:
wfile.write("""\n""")
generate_text("title", wfile, feed.title)
if hasattr(feed, "subtitle"):
generate_text("subtitle", wfile, feed.subtitle)
generate_id(wfile, feed.id)
generate_date("updated", wfile, feed.updated)
generate_links(wfile, feed.links)
generate_people("author", wfile, feed.authors)
generate_people("contributor", wfile, feed.contributors)
if hasattr(feed, "rights"):
generate_rights(wfile, feed.rights)
if hasattr(feed, "generator"):
# @@@ should this actually just be us?
generate_generator(wfile, feed.generator)
generate_entries(wfile, feed.entries)
wfile.write("""\n""")
def generate_text(name, wfile, text_construct):
if hasattr(text_construct, "type_"):
typ = text_construct.type_
if typ in ["text", "html"]:
value = re.sub("&", "&", text_construct.value)
value = re.sub("<", "<", value)
wfile.write("""<%s type="%s">%s%s>\n""" % (name, typ, value, name))
else:
pass # @@@
else:
value = re.sub("&", "&", text_construct.value)
value = re.sub("<", "<", value)
wfile.write("""<%s>%s%s>\n""" % (name, value, name))
def generate_id(wfile, id):
wfile.write("""%s\n""" % id)
def generate_date(name, wfile, date_construct):
wfile.write("""<%s>%s%s>\n""" % (name, date_construct.value, name))
def generate_links(wfile, links):
for link in links:
attrs = " href=\"%s\"" % link.href
# note underscore in rel_ to get explicit value
if hasattr(link, "rel_"):
attrs += " rel=\"%s\"" % link.rel_
if hasattr(link, "type"):
attrs += " type=\"%s\"" % link.type
if hasattr(link, "hreflang"):
attrs += " hreflang=\"%s\"" % link.hreflang
if hasattr(link, "title"):
attrs += " title=\"%s\"" % link.title
if hasattr(link, "length"):
attrs += " length=\"%s\"" % link.length
wfile.write("""\n""" % attrs)
def generate_people(name, wfile, people):
for person in people:
wfile.write("""<%s>\n""" % name)
wfile.write("""%s\n""" % person.name)
if hasattr(person, "uri"):
wfile.write("""%s\n""" % person.uri)
if hasattr(person, "email"):
wfile.write("""%s\n""" % person.email)
wfile.write("""%s>\n""" % name)
def generate_rights(wfile, rights):
wfile.write("""%s\n""" % rights)
def generate_generator(wfile, generator):
attrs = ""
if hasattr(generator, "uri"):
attrs += " uri=\"%s\"" % generator.uri
if hasattr(generator, "version"):
attrs += " version=\"%s\"" % generator.version
wfile.write("""%s\n""" % (attrs, generator.value))
def generate_entries(wfile, entries):
for entry in entries:
generate_entry(wfile, entry)
def generate_entry(wfile, entry, top=False):
if top:
wfile.write("""\n""")
if top:
wfile.write("""\n""")
else:
wfile.write("""\n""")
generate_text("title", wfile, entry.title)
generate_id(wfile, entry.id)
generate_date("updated", wfile, entry.updated)
if hasattr(entry, "published"):
generate_date("published", wfile, entry.published)
generate_links(wfile, entry.links)
generate_people("author", wfile, entry.authors)
generate_people("contributor", wfile, entry.contributors)
if hasattr(entry, "summary"):
generate_text("summary", wfile, entry.summary)
if hasattr(entry, "content"):
generate_content(wfile, entry.content)
wfile.write("""\n""")
def generate_content(wfile, content):
if isinstance(content, InlineXHTMLContent):
wfile.write("""\n""")
generate_xml(wfile, content.children[0])
wfile.write("""\n""")
else:
pass # @@@
demokritos-0.1.0/lib/atom/model.py 0000644 0000765 0000765 00000005003 10335060165 020171 0 ustar jtauber jtauber 0000000 0000000 class Feed:
def __init__(self):
self.links = []
self.authors = []
self.contributors = []
self.categories = []
self.entries = []
class Entry:
def __init__(self):
self.links = []
self.authors = []
self.contributors = []
self.categories = []
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
class Category:
def __init__(self):
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
class Source:
def __init__(self):
self.links = []
self.authors = []
self.contributors = []
self.categories = []
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
class PlainText(object):
def __init__(self, value=None):
if value:
self.value = value
def get_type(self):
if hasattr(self, "type_"):
return self.type_
else:
return "text"
def set_type(self, type):
self.type_ = type
type = property(get_type, set_type)
class XHTMLText:
def __init__(self):
self.children = []
def add(self, child):
self.children.append(child)
class Link(object):
def __init__(self, rel=None, href=None):
if rel:
self.rel_ = rel
if href:
self.href = href
def get_rel(self):
if hasattr(self, "rel_"):
return self.rel_
else:
return "alternate"
def set_rel(self, rel):
self.rel_ = rel
rel = property(get_rel, set_rel)
class Generator:
pass
class Person:
def __init__(self, name=None, uri=None, email=None):
if name:
self.name = name
if uri:
self.uri = uri
if email:
self.email = email
# extension elements
self.children = []
def add(self, child):
self.children.append(child)
class Date:
def __init__(self, value=None):
if value:
self.value = value
class InlineTextContent:
pass
class InlineXHTMLContent:
def __init__(self):
self.children = []
def add(self, child):
self.children.append(child)
class InlineOtherContent:
pass
class OutOfLineContent:
pass
demokritos-0.1.0/lib/atom/parse.py 0000644 0000765 0000765 00000063047 10340507163 020217 0 ustar jtauber jtauber 0000000 0000000 from xmlbase import HandlerBase, parse, Generic_ElementHandler, NAMESPACE_SEPARATOR
from model import *
ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"
def atom_ns(name):
return ATOM_NAMESPACE + NAMESPACE_SEPARATOR + name
def xhtml_ns(name):
return "http://www.w3.org/1999/xhtml" + NAMESPACE_SEPARATOR + name
class AtomException(Exception):
pass
class Atom_HandlerBase(HandlerBase):
def no_children(self, name, attribute):
raise AtomException("%s not allowed here" % name)
# @@@ dsig support (5.1)
# @@@ language sensitive
# @@@ xml:base
class Atom_RootHandler(Atom_HandlerBase):
def child(self, name, attributes):
if name == atom_ns("feed"):
Atom_FeedHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("entry"):
Atom_EntryHandler(self.parser, self, name, attributes, self.inherited)
else:
raise AtomException("%s not allowed here" % name)
class Atom_FeedHandler(Atom_HandlerBase):
def init(self):
self.product = Feed()
self.got_author = False
self.got_generator = False
self.got_icon = False
self.got_id = False
self.got_logo = False
self.got_rights = False
self.got_subtitle = False
self.got_title = False
self.got_updated = False
self.got_entries = False
def child(self, name, attributes):
if self.got_entries:
if name == atom_ns("entry"):
Atom_EntryHandler(self.parser, self, name, attributes, self.inherited)
else:
raise AtomException("once entries start in feed, only further entries can follow, not '%s'" % name)
else:
if name == atom_ns("author"):
Atom_AuthorHandler(self.parser, self, name, attributes, self.inherited)
self.got_author = True
elif name == atom_ns("category"):
Atom_CategoryHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("contributor"):
Atom_ContributorHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("generator"):
if self.got_generator:
raise AtomException("feed can only have one generator element")
else:
Atom_GeneratorHandler(self.parser, self, name, attributes, self.inherited)
self.got_generator = True
elif name == atom_ns("icon"):
if self.got_icon:
raise AtomException("feed can only have one icon element")
else:
Atom_IconHandler(self.parser, self, name, attributes, self.inherited)
self.got_icon = True
elif name == atom_ns("id"):
if self.got_id:
raise AtomException("feed can only have one id element")
else:
Atom_IdHandler(self.parser, self, name, attributes, self.inherited)
self.got_id = True
elif name == atom_ns("link"):
Atom_LinkHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("logo"):
if self.got_logo:
raise AtomException("feed can only have one logo element")
else:
Atom_LogoHandler(self.parser, self, name, attributes, self.inherited)
self.got_logo = True
elif name == atom_ns("rights"):
if self.got_rights:
raise AtomException("feed can only have one rights element")
else:
Atom_RightsHandler(self.parser, self, name, attributes, self.inherited)
self.got_rights = True
elif name == atom_ns("subtitle"):
if self.got_subtitle:
raise AtomException("feed can only have one subtitle element")
else:
Atom_SubtitleHandler(self.parser, self, name, attributes, self.inherited)
self.got_subtitle = True
elif name == atom_ns("title"):
if self.got_title:
raise AtomException("feed can only have one title element")
else:
Atom_TitleHandler(self.parser, self, name, attributes, self.inherited)
self.got_title = True
elif name == atom_ns("updated"):
if self.got_updated:
raise AtomException("feed can only have one updated element")
else:
Atom_UpdatedHandler(self.parser, self, name, attributes, self.inherited)
self.got_updated = True
elif name == atom_ns("entry"):
Atom_EntryHandler(self.parser, self, name, attributes, self.inherited)
self.got_entries = True
else:
raise AtomException("%s not allowed here" % name)
def end(self, name):
if not self.got_id:
raise AtomException("feed must have id element")
elif not self.got_title:
raise AtomException("feed must have title element")
elif not self.got_updated:
raise AtomException("feed must have updated element")
else:
self.parent_handler.product = self.product
Atom_HandlerBase.end(self, name)
class Atom_TextConstructHandlerBase(Atom_HandlerBase):
# ABSTRACT BASE
def init(self):
self.product = None
for name, value in self.attributes.items():
if name == "type":
if value in ["text", "html"]: # plain text construct
self.product = PlainText()
self.product.type = value
elif value in ["xhtml"]: # xhtml text construct
self.product = XHTMLText()
self.product.type = value
else:
raise AtomException("type must be one of 'text', 'html' or 'xhtml', not '%s'" % value)
else:
raise AtomException("%s not allowed here" % name)
if self.product is None:
self.product = PlainText()
def child(self, name, attributes):
if self.product.type in ["text", "html"]:
raise AtomException("child elements not allowed when type is '%s'" % self.product.type)
elif name != xhtml_ns("div"):
raise AtomException("must contain single xhtml:div element, not '%s'" % name)
else:
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
class Atom_TitleHandler(Atom_TextConstructHandlerBase):
def end(self, name):
self.product.value = self.char_data.strip()
self.parent_handler.product.title = self.product
Atom_TextConstructHandlerBase.end(self, name)
class Atom_SubtitleHandler(Atom_TextConstructHandlerBase):
def end(self, name):
self.product.value = self.char_data.strip()
self.parent_handler.product.subtitle = self.product
Atom_TextConstructHandlerBase.end(self, name)
class Atom_SummaryHandler(Atom_TextConstructHandlerBase):
def end(self, name):
self.product.value = self.char_data.strip()
self.parent_handler.product.summary = self.product
Atom_TextConstructHandlerBase.end(self, name)
class Atom_LinkHandler(Atom_HandlerBase):
def init(self):
self.product = Link()
# @@@ atom:feed elements MUST NOT contain more than one atom:link
# element with a rel attribute value of "alternate" that has the
# same combination of type and hreflang attribute values.
if "href" not in self.attributes:
raise AtomException("link must have 'href' attribute")
for name, value in self.attributes.items():
if name == "href":
self.product.href = value
elif name == "rel":
self.product.rel = value
elif name == "type":
self.product.type = value
elif name == "hreflang":
self.product.hreflang = value
elif name == "length":
self.product.length = value
elif name == "title":
self.product.title = value
else:
raise AtomException("%s not allowed here" % name)
child = Atom_HandlerBase.no_children
def end(self, name):
self.parent_handler.product.links.append(self.product)
Atom_HandlerBase.end(self, name)
class Atom_CategoryHandler(Atom_HandlerBase):
def init(self):
self.product = Category()
if "term" not in self.attributes:
raise AtomException("category must have term attribute")
for name, value in self.attributes.items():
if name == "term":
self.product.term = value
elif name == "scheme":
self.product.scheme = value
elif name == "label":
self.product.label = value # @@@ language sensitive
else:
raise AtomException("%s not allowed here" % name)
def child(self, name, attributes):
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
self.parent_handler.product.categories.append(self.product)
Atom_HandlerBase.end(self, name)
class Atom_DateConstructHandlerBase(Atom_HandlerBase):
def init(self):
self.product = Date()
# @@@ should validate date (see section 3.3)
class Atom_UpdatedHandler(Atom_DateConstructHandlerBase):
def end(self, name):
self.product.value = self.char_data
self.parent_handler.product.updated = self.product
Atom_DateConstructHandlerBase.end(self, name)
class Atom_PublishedHandler(Atom_DateConstructHandlerBase):
def end(self, name):
self.product.value = self.char_data
self.parent_handler.product.published = self.product
Atom_DateConstructHandlerBase.end(self, name)
class Atom_PersonConstructHandlerBase(Atom_HandlerBase):
# ABSTRACT BASE
def init(self):
self.product = Person()
self.got_name = False
self.got_uri = False
self.got_email = False
def child(self, name, attributes):
if name == atom_ns("name"):
if self.got_name:
raise AtomException("can only have one name element in a person construct")
else:
Atom_NameHandler(self.parser, self, name, attributes, self.inherited)
self.got_name = True
elif name == atom_ns("uri"):
if self.got_uri:
raise AtomException("can only have one uri element in a person construct")
else:
Atom_UriHandler(self.parser, self, name, attributes, self.inherited)
self.got_uri = True
elif name == atom_ns("email"):
if self.got_email:
raise AtomException("can only have one email element in a person construct")
else:
Atom_EmailHandler(self.parser, self, name, attributes, self.inherited)
self.got_email = True
else:
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
if not self.got_name:
raise AtomException("person construct must have a name")
Atom_HandlerBase.end(self, name)
# @@@ If an atom:entry element does not contain atom:author elements, then
# the atom:author elements of the contained atom:source element are
# considered to apply. In an Atom Feed Document, the atom:author
# elements of the containing atom:feed element are considered to apply
# to the entry if there are no atom:author elements in the locations
# described above.
class Atom_AuthorHandler(Atom_PersonConstructHandlerBase):
def end(self, name):
self.parent_handler.product.authors.append(self.product)
Atom_PersonConstructHandlerBase.end(self, name)
class Atom_ContributorHandler(Atom_PersonConstructHandlerBase):
def end(self, name):
self.parent_handler.product.contributors.append(self.product)
Atom_PersonConstructHandlerBase.end(self, name)
class Atom_NameHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.name = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_UriHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.uri = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_EmailHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.email = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_IdHandler(Atom_HandlerBase):
# @@@ 4.2.6
def end(self, name):
self.parent_handler.product.id = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_IconHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.icon = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_LogoHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.icon = self.char_data
Atom_HandlerBase.end(self, name)
class Atom_RightsHandler(Atom_HandlerBase):
def end(self, name):
self.parent_handler.product.rights = self.char_data.strip()
Atom_HandlerBase.end(self, name)
class Atom_GeneratorHandler(Atom_HandlerBase):
def init(self):
self.product = Generator()
for name, value in self.attributes.items():
if name == "uri":
self.product.uri = value
elif name == "version":
self.product.version = value
else:
raise AtomException("%s not allowed here" % name)
def end(self, name):
self.product.value = self.char_data.strip()
self.parent_handler.product.generator = self.product
Atom_HandlerBase.end(self, name)
class Atom_EntryHandler(Atom_HandlerBase):
def init(self):
self.product = Entry()
self.got_author = False
self.got_content = False
self.got_id = False
self.got_published = False
self.got_rights = False
self.got_source = False
self.got_summary = False
self.got_title = False
self.got_updated = False
def child(self, name, attributes):
if name == atom_ns("author"):
Atom_AuthorHandler(self.parser, self, name, attributes, self.inherited)
self.got_author = True
elif name == atom_ns("category"):
Atom_CategoryHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("content"):
if self.got_content:
raise AtomException("entry can only have one content element")
else:
Atom_ContentHandler(self.parser, self, name, attributes, self.inherited)
self.got_content = True
elif name == atom_ns("contributor"):
Atom_ContributorHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("id"):
if self.got_id:
raise AtomException("entry can only have one id element")
else:
Atom_IdHandler(self.parser, self, name, attributes, self.inherited)
self.got_id = True
elif name == atom_ns("link"):
Atom_LinkHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("published"):
if self.got_published:
raise AtomException("entry can only have one published element")
else:
Atom_PublishedHandler(self.parser, self, name, attributes, self.inherited)
self.got_published = True
elif name == atom_ns("rights"):
if self.got_rights:
raise AtomException("entry can only have one rights element")
else:
Atom_RightsHandler(self.parser, self, name, attributes, self.inherited)
self.got_rights = True
elif name == atom_ns("source"):
if self.got_source:
raise AtomException("entry can only have one source element")
else:
Atom_SourceHandler(self.parser, self, name, attributes, self.inherited)
self.got_source = True
elif name == atom_ns("summary"):
if self.got_summary:
raise AtomException("entry can only have one summary element")
else:
Atom_SummaryHandler(self.parser, self, name, attributes, self.inherited)
self.got_summary = True
elif name == atom_ns("title"):
if self.got_title:
raise AtomException("entry can only have one title element")
else:
Atom_TitleHandler(self.parser, self, name, attributes, self.inherited)
self.got_title = True
elif name == atom_ns("updated"):
if self.got_updated:
raise AtomException("entry can only have one updated element")
else:
Atom_UpdatedHandler(self.parser, self, name, attributes, self.inherited)
self.got_updated = True
else:
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
# @@@ atom:entry elements that contain no child atom:content element
# MUST contain at least one atom:link element with a rel attribute
# value of "alternate".
# @@@ atom:entry elements MUST contain an atom:summary element in either
# of the following cases:
#
# * the atom:entry contains an atom:content that has a "src"
# attribute (and is thus empty).
#
# * the atom:entry contains content that is encoded in Base64; i.e.
# the "type" attribute of atom:content is a MIME media type
# [MIMEREG], but is not an XML media type [RFC3023], does not
# begin with "text/", and does not end with "/xml" or "+xml".
def end(self, name):
self.validate()
if isinstance(self.parent_handler, Atom_FeedHandler):
self.parent_handler.product.entries.append(self.product)
else:
self.parent_handler.product = self.product
Atom_HandlerBase.end(self, name)
def validate(self):
if isinstance(self.parent_handler, Atom_FeedHandler):
if not self.got_author and not self.parent_handler.got_author:
raise AtomException("entry may not omit author element if feed does")
# if not in a feed, ignore the author requirement @@@
if not self.got_id:
raise AtomException("entry must have id element")
if not self.got_title:
raise AtomException("entry must have title element")
if not self.got_updated:
raise AtomException("entry must have updated element")
class Atom_ContentHandler(Atom_HandlerBase):
def init(self):
self.product = None
if "src" in self.attributes:
self.product = OutOfLineContent()
self.product.src = self.attributes["src"]
type_attr = self.attributes.get("type")
if type_attr in ["text", "html", "xhtml"]:
raise AtomException("content with src cannot have type of '%s'" % type_attr)
if type_attr:
self.product.type = type_attr
else:
type_attr = self.attributes.get("type")
if type_attr in ["text", "html"]:
self.product = InlineTextContent()
elif type_attr == "xhtml":
self.product = InlineXHTMLContent()
elif type_attr == None:
self.product = InlineTextContent()
else:
self.product = InlineOtherContent()
if type_attr:
self.product.type = type_attr
def child(self, name, attributes):
if isinstance(self.product, InlineTextContent):
raise AtomException("child elements not allowed when type is inline text")
elif isinstance(self.product, InlineXHTMLContent):
if name != xhtml_ns("div"):
raise AtomException("content of type 'xhtml' must contain single xhtml:div element, not '%s'" % name)
else:
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
elif isinstance(self.product, OutOfLineContent):
raise AtomException("child elements not allowed in out of line content")
else: # InlineOtherContent
pass # @@@
# @@@
# 4. If the value of "type" is an XML media type [RFC3023], or ends
# with "+xml" or "/xml" (case-insensitive), the content of
# atom:content MAY include child elements, and SHOULD be suitable
# for handling as the indicated media type. If the "src" attribute
# is not provided, this would normally mean that the "atom:content"
#
# element would contain a single child element which would serve as
# the root element of the XML document of the indicated type.
#
# 5. If the value of "type" begins with "text/" (case-insensitive),
# the content of atom:content MUST NOT contain child elements.
#
# 6. For all other values of "type", the content of atom:content MUST
# be a valid Base64 encoding, as described in [RFC3548], section 3.
# When decoded, it SHOULD be suitable for handling as the indicated
# media type. In this case, the characters in the Base64 encoding
# MAY be preceded and followed in the atom:content element by
# white-space, and lines are separated by a single newline (U+000A)
# character.
#
def end(self, name):
self.parent_handler.product.content = self.product
Atom_HandlerBase.end(self, name)
class Atom_SourceHandler(Atom_HandlerBase):
def init(self):
self.product = Source()
self.got_author = False
self.got_generator = False
self.got_icon = False
self.got_id = False
self.got_logo = False
self.got_rights = False
self.got_subtitle = False
self.got_title = False
self.got_updated = False
def child(self, name, attributes):
if name == atom_ns("author"):
Atom_AuthorHandler(self.parser, self, name, attributes, self.inherited)
self.got_author = True
elif name == atom_ns("category"):
Atom_CategoryHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("contributor"):
Atom_ContributorHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("generator"):
if self.got_generator:
raise AtomException("source can only have one generator element")
else:
Atom_GeneratorHandler(self.parser, self, name, attributes, self.inherited)
self.got_generator = True
elif name == atom_ns("icon"):
if self.got_icon:
raise AtomException("source can only have one icon element")
else:
Atom_IconHandler(self.parser, self, name, attributes, self.inherited)
self.got_icon = True
elif name == atom_ns("id"):
if self.got_id:
raise AtomException("source can only have one id element")
else:
Atom_IdHandler(self.parser, self, name, attributes, self.inherited)
self.got_id = True
elif name == atom_ns("link"):
Atom_LinkHandler(self.parser, self, name, attributes, self.inherited)
elif name == atom_ns("logo"):
if self.got_logo:
raise AtomException("source can only have one logo element")
else:
Atom_LogoHandler(self.parser, self, name, attributes, self.inherited)
self.got_logo = True
elif name == atom_ns("rights"):
if self.got_rights:
raise AtomException("source can only have one rights element")
else:
Atom_RightsHandler(self.parser, self, name, attributes, self.inherited)
self.got_rights = True
elif name == atom_ns("subtitle"):
if self.got_subtitle:
raise AtomException("source can only have one subtitle element")
else:
Atom_SubtitleHandler(self.parser, self, name, attributes, self.inherited)
self.got_subtitle = True
elif name == atom_ns("title"):
if self.got_title:
raise AtomException("source can only have one title element")
else:
Atom_TitleHandler(self.parser, self, name, attributes, self.inherited)
self.got_title = True
elif name == atom_ns("updated"):
if self.got_updated:
raise AtomException("source can only have one updated element")
else:
Atom_UpdatedHandler(self.parser, self, name, attributes, self.inherited)
self.got_updated = True
else: # @@@ extensionElement
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def end(self, name):
self.parent_handler.product.source = self.product
Atom_HandlerBase.end(self, name)
def parse_atom(xml):
return parse(xml, Atom_RootHandler)
demokritos-0.1.0/lib/server.py 0000755 0000765 0000765 00000017346 10343114573 017461 0 ustar jtauber jtauber 0000000 0000000 #!/usr/bin/env python
import traceback
from cStringIO import StringIO
import BaseHTTPServer
import time
from store import UnsupportedMediaTypeError
from app import generate_introspection, generate_collection
from atom.model import PlainText, Date, Person
class IntrospectionHandler:
def HEAD(self, handler):
handler.send_response(200)
handler.send_header("Content-type", "application/atomserv+xml")
handler.end_headers()
def GET(self, handler):
handler.send_response(200)
handler.send_header("Content-type", "application/atomserv+xml")
handler.end_headers()
generate_introspection(handler.wfile, atom_store.service)
def POST(self, handler):
handler.send_error(405, "Cannot POST to this URI")
def PUT(self, handler):
handler.send_error(405, "Cannot PUT to this URI")
def DELETE(self, handler):
handler.send_error(405, "Cannot DELETE this URI")
class CollectionHandler:
def HEAD(self, handler):
# get head of collection document
collection = collection_manager.get(handler.path)
if collection:
content = StringIO()
generate_collection(content, collection)
handler.send_response(200)
handler.send_header("Content-type", "application/atom+xml")
handler.send_header("Content-Length", str(len(content.getvalue())))
handler.end_headers()
else:
handler.send_error(404, "Resource Not Found")
def GET(self, handler):
# get collection document
collection = atom_store.get_collection(handler.path)
if collection:
content = StringIO()
generate_collection(content, collection)
handler.send_response(200)
handler.send_header("Content-type", "application/atom+xml")
handler.send_header("Content-Length", str(len(content.getvalue())))
handler.end_headers()
handler.wfile.write(content.getvalue())
else:
handler.send_error(404, "Collection Not Found")
def POST(self, handler):
# add new resource to collection and return 201 with location
collection = atom_store.get_collection(handler.path)
if collection:
suggested_title = handler.headers.get("Title")
new_uri = atom_store.make_uri(collection, suggested_title)
media_type = handler.headers.get("Content-type")
length = handler.headers.get("Content-Length")
content = handler.rfile.read(int(length))
# throw away additional data (from CGIHTTPServer)
#while select.select([self.rfile._sock], [], [], 0)[0]:
# if not self.rfile._sock.recv(1):
# break
# @@@ seems inefficient to mint then parse again
import urlparse
server, path = urlparse.urlparse(new_uri)[1:3]
member = atom_store.create_member(path)
member.title = PlainText(suggested_title or "no title")
member.updated = Date(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
member.authors = [Person(name="Sam Pell")] # @@@ eventually whoever is authenticated
member.media_type = media_type
member.content = content
try:
collection.add_member(member)
handler.send_response(201)
handler.send_header("Location", new_uri)
handler.end_headers()
except UnsupportedMediaTypeError:
handler.send_error(415, "Unsupported Media Type")
else:
handler.send_error(404, "Collection Not Found")
def PUT(self, handler):
handler.send_error(405, "Cannot PUT to this URI")
def DELETE(self, handler):
handler.send_error(405, "Cannot DELETE this URI")
class MemberHandler:
def HEAD(self, handler):
# get head of member resource
pass # @@@
def GET(self, handler):
# get member resource
member = atom_store.get_member(handler.path)
if member:
handler.send_response(200)
handler.send_header("Content-type", member.media_type)
# @@@ length
handler.end_headers()
handler.wfile.write(member.content)
else:
handler.send_error(404, "Resource Not Found")
def POST(self, handler):
handler.send_error(405, "Cannot POST to this URI")
def PUT(self, handler):
# replace resource, return 200 with resource
media_type = handler.headers.get("Content-type")
length = handler.headers.get("Content-Length")
suggested_title = handler.headers.get("Title")
content = handler.rfile.read(int(length))
# throw away additional data (from CGIHTTPServer)
#while select.select([self.rfile._sock], [], [], 0)[0]:
# if not self.rfile._sock.recv(1):
# break
if atom_store.member_exists(handler.path):
member = atom_store.get_member(handler.path)
else:
member = atom_store.create_member(handler.path)
# @@@ won't be in any collections
member.title = suggested_title or "no title" # @@@
member.updated = Date(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())) # @@@
member.media_type = media_type
member.content = content
handler.send_response(200)
handler.send_header("Content-type", media_type)
# @@@ length
handler.end_headers()
handler.wfile.write(content)
def DELETE(self, handler):
# delete resource, return 204
member = atom_store.get_member(handler.path)
if member:
atom_store.remove_member(member)
else:
handler.send_error(404, "Resource Not Found")
handler.send_response(204)
class AtomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
server_version = "Demokritos/0.1"
def do_HEAD(self):
try:
resource_handler = self.dispatch(self.path)
resource_handler.HEAD(self)
except:
traceback.print_exc()
self.send_error(500, "Server Got Exception")
def do_GET(self):
try:
resource_handler = self.dispatch(self.path)
resource_handler.GET(self)
except:
traceback.print_exc()
self.send_error(500, "Server Got Exception")
def do_POST(self):
try:
resource_handler = self.dispatch(self.path)
resource_handler.POST(self)
except:
traceback.print_exc()
self.send_error(500, "Server Got Exception")
def do_PUT(self):
try:
resource_handler = self.dispatch(self.path)
resource_handler.PUT(self)
except:
traceback.print_exc()
self.send_error(500, "Server Got Exception")
def do_DELETE(self):
try:
resource_handler = self.dispatch(self.path)
resource_handler.DELETE(self)
except:
traceback.print_exc()
self.send_error(500, "Server Got Exception")
def dispatch(self, path):
if path == "/introspection":
return IntrospectionHandler()
elif atom_store.collection_exists(path):
return CollectionHandler()
else:
return MemberHandler()
class AtomServer:
def __init__(self, host="", port=8000):
self.httpd = BaseHTTPServer.HTTPServer((host, port), AtomRequestHandler)
def serve_forever(self):
self.httpd.serve_forever() demokritos-0.1.0/lib/store.py 0000644 0000765 0000765 00000013342 10343114573 017274 0 ustar jtauber jtauber 0000000 0000000 import sys
sys.path.append("lib")
from app import Service, Workspace, Collection, Member
from atom.model import Link, Date
from app import parse_entry
from atom import generate_entry
from cStringIO import StringIO
import re
import time
class UnsupportedMediaTypeError(Exception):
pass
class AtomStore:
def __init__(self, root_uri):
self.root_uri = root_uri
self.service = Service()
self.collection_manager = CollectionManager()
self.member_manager = MemberManager()
self.make_uri = UriMint(self.root_uri)
def create_workspace(self, name):
workspace = ServerWorkspace(name)
self.service.workspaces.append(workspace)
return workspace
def create_collection(self, title, path, list_template, member_type):
if member_type == "media":
collection = ServerMediaCollection(title, path, list_template)
elif member_type == "entry":
collection = ServerEntryCollection(title, path, list_template)
collection.href = self.root_uri + path
collection.id = collection.href
self.collection_manager.register(collection)
return collection
def create_member(self, path):
member = ServerMember(path)
self.member_manager.register(member)
href = self.root_uri + path
member.id = href
member.links.append(Link(rel="edit", href=href))
return member
def collection_exists(self, path):
return self.collection_manager.exists(path)
def member_exists(self, path):
return self.member_manager.exists(path)
def get_collection(self, path):
return self.collection_manager.get(path)
def get_member(self, path):
return self.member_manager.get(path)
def remove_member(self, member):
self.member_manager.remove(member)
class UriMint:
"""
This example of a URI Mint concatenates the collection path
with yyyy/mm/dd with a modified suggested_name.
"""
def __init__(self, root_uri):
self.root_uri = root_uri
def __call__(self, collection, suggested_name=None):
if suggested_name:
base_name = re.sub(" ", "_", suggested_name).lower()[:20]
else:
base_name = "resource"
date = "/%04d/%02d/%02d/" % tuple(time.localtime()[:3])
# @@@ doesn't check for duplicates yet
return self.root_uri + collection.path + date + base_name
class ServerWorkspace(Workspace):
def add_collection(self, collection):
Workspace.add_collection(self, collection.title, collection.href, collection.member_type, collection.list_template)
class ServerCollection(Collection):
def __init__(self, title, path, list_template):
Collection.__init__(self)
self.title = title
self.path = path
self.list_template = list_template
self.by_path = {}
def add_member(self, member):
if not self.supported_media_type(member.media_type):
raise UnsupportedMediaTypeError()
Collection.add_member(self, member)
self.by_path[member.path] = member
member.collections.add(self)
def replace(self, member):
if member.path in self.by_path:
self.members.remove(self.by_path[member.path])
del self.by_path[member.path]
self.add_member(member)
def remove(self, member):
self.members.remove(member)
del self.by_path[member.path]
class ServerMediaCollection(ServerCollection):
# @@@ add support for restricting media types
def __init__(self, title, path, list_template):
ServerCollection.__init__(self, title, path, list_template)
self.member_type = "media"
def supported_media_type(self, media_type):
return True
class ServerEntryCollection(ServerCollection):
def __init__(self, title, path, list_template):
ServerCollection.__init__(self, title, path, list_template)
self.member_type = "entry"
def add_member(self, member):
if member.media_type != "application/atom+xml":
raise UnsupportedMediaTypeError()
entry = parse_entry(member.content)
entry.id = member.id
entry.updated = Date(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
out = StringIO()
generate_entry(out, entry)
member.content = out.getvalue()
Collection.add_member(self, member)
self.by_path[member.path] = member
member.collections.add(self)
class ServerMember(Member):
def __init__(self, path):
Member.__init__(self)
self.path = path
self.collections = set()
class CollectionManager:
def __init__(self):
self.collections = {}
def register(self, collection):
self.collections[collection.path] = collection
def get(self, path):
return self.collections.get(path)
def exists(self, path):
return path in self.collections
class MemberManager:
def __init__(self):
self.members = {}
def register(self, member):
self.members[member.path] = member
def replace(self, member):
old_member = self.get(member.path)
if old_member:
for collection in old_member.collections:
collection.replace(member)
member.collections.add(collection)
self.members[member.path] = member
def get(self, path):
return self.members.get(path)
def exists(self, path):
return path in self.members
def remove(self, member):
for collection in member.collections:
collection.remove(member)
del self.members[member.path]
demokritos-0.1.0/lib/xmlbase.py 0000644 0000765 0000765 00000006550 10316664521 017601 0 ustar jtauber jtauber 0000000 0000000 from xml.parsers import expat
XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"
NAMESPACE_SEPARATOR = "^"
def xml_ns(name):
return XML_NAMESPACE + NAMESPACE_SEPARATOR + name
class HandlerBase:
def __init__(self, parser, parent_handler=None, name=None, attributes=None, inherited={}):
self.parser = parser
self.parent_handler = parent_handler
self.name = name
self.attributes = {}
self.char_data = ""
self.set_handlers()
self.inherited = inherited
if attributes:
for name, value in attributes.items():
if name == xml_ns("lang"):
self.inherited["xml:lang"] = value
elif name == xml_ns("base"):
self.inherited["xml:base"] = value
else:
self.attributes[name] = value
self.init()
def init(self):
pass
def set_handlers(self):
self.parser.StartElementHandler = self.child
self.parser.CharacterDataHandler = self.char
self.parser.EndElementHandler = self.end
def child(self, name, attributes):
pass
def char(self, data):
self.char_data += data
def end(self, name):
if self.parent_handler is not None:
self.parent_handler.set_handlers()
else: # must be root
pass
class Element:
def __init__(self, name, attributes):
self.name = name
self.attributes = attributes
self.children = []
def add(self, child):
self.children.append(child)
class CharData:
def __init__(self, chardata):
self.chardata = chardata
class Generic_ElementHandler(HandlerBase):
def init(self):
self.product = Element(self.name, self.attributes)
def child(self, name, attributes):
Generic_ElementHandler(self.parser, self, name, attributes, self.inherited)
def char(self, data):
self.product.add(CharData(data))
def end(self, name):
self.parent_handler.product.add(self.product)
HandlerBase.end(self, name)
def parse(xml, root_handler_class):
parser = expat.ParserCreate(namespace_separator=NAMESPACE_SEPARATOR)
root_handler = root_handler_class(parser)
parser.Parse(xml)
return root_handler.product
def make_prefix(uri):
if uri == "http://www.w3.org/1999/xhtml":
return "xhtml"
else:
pass # @@@
def generate_xml(wfile, element, namespaces = {}):
attrs = ""
for name, value in element.attributes.items():
# @@@ escaping
attrs += " %s=\"%s\"" % (name, value)
if NAMESPACE_SEPARATOR in element.name: # @@@ should really be pre-split
uri, name = element.name.split(NAMESPACE_SEPARATOR)
if uri not in namespaces:
prefix = make_prefix(uri)
attrs += " xmlns:%s=\"%s\"" % (prefix, uri)
namespaces[uri] = prefix
element_name = namespaces[uri] + ":" + name
else:
element_name = element.name
wfile.write("<%s%s>" % (element_name, attrs))
for child in element.children:
if isinstance(child, Element):
generate_xml(wfile, child, namespaces)
else: # chardata
wfile.write(child.chardata)
wfile.write("%s>" % element_name) demokritos-0.1.0/README 0000644 0000765 0000765 00000000333 10343114757 015700 0 ustar jtauber jtauber 0000000 0000000 Demokritos Atom Server
version 0.1
http://jtauber.com/demokritos
To run a test server, cd to test directory and run test_server.py
client_test.py in the same directory will run through a sequence of tests on
this.
demokritos-0.1.0/test/ 0000755 0000765 0000765 00000000000 10343115344 015771 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/test/client_test.py 0000755 0000765 0000765 00000017453 10340507163 020676 0 ustar jtauber jtauber 0000000 0000000 #!/usr/bin/env python
import httplib, socket, urlparse
import sys
sys.path.append("../lib")
from app import parse_introspection
from atom import parse_atom
SERVER = "localhost:8000"
def request(server, method, path, body=None, headers=None):
conn = httplib.HTTPConnection(server)
try:
print "trying %s %s to %s" % (method, path, server)
if body is not None and headers is not None:
conn.request(method, path, body, headers)
else:
conn.request(method, path)
resp = conn.getresponse()
conn.close()
print "got %s %s" % (resp.status, resp.reason)
return resp
except socket.error:
print "got socket error"
return None
print "getting introspection document..."
collection_href = None
response = request(SERVER, "GET", "/introspection")
if response:
assert response.status == 200
doc = response.read()
service = parse_introspection(doc)
print "service:"
for workspace in service.workspaces:
print "\tworkspace:", workspace.title
for collection in workspace.collections:
print "\t\tcollection:", collection.title
print "\t\t\thref =", collection.href
print "\t\t\tlist-template =", collection.list_template
print "\t\t\tmember-type =", collection.member_type
collection_href = service.workspaces[0].collections[0].href
raw_input("press return")
member_href = None
if collection_href:
print "getting first collection in first workspace..."
server, path = urlparse.urlparse(collection_href)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
collection = parse_atom(doc)
print "collection:"
for entry in collection.entries:
print "\tmember:", entry.title.value
print "\t\tupdated:", entry.updated.value
for link in entry.links:
print "\t\tlink %s: %s" % (link.rel, link.href)
member_href = collection.entries[0].links[0].href
raw_input("press return")
if member_href:
print "getting first member in that collection..."
server, path = urlparse.urlparse(member_href)[1:3]
response = request(server, "GET", path)
if response and response.status == 200:
doc = response.read()
print doc
raw_input("press return")
location = None
if collection_href:
print "POSTing to collection..."
server, path = urlparse.urlparse(collection_href)[1:3]
body = "Hello world!"
headers = {"Content-type": "text/plain", "Title": "My Title"}
response = request(server, "POST", path, body, headers)
if response:
assert response.status == 201
location = response.getheader("Location")
print "got location:", location
raw_input("press return")
if collection_href:
print "getting collection we just posted to..."
server, path = urlparse.urlparse(collection_href)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
collection = parse_atom(doc)
print "collection:"
for entry in collection.entries:
print "\tmember:", entry.title.value
print "\t\tupdated:", entry.updated.value
for link in entry.links:
print "\t\tlink %s: %s" % (link.rel, link.href)
member_href = collection.entries[0].links[0].href
raw_input("press return")
if location:
print "GETting new resource..."
server, path = urlparse.urlparse(location)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
print doc
assert doc == "Hello world!"
raw_input("press return")
if collection_href:
print "PUTing new resource..."
server, path = urlparse.urlparse(location)[1:3]
body = "Goodbye world!"
headers = {"Content-type": "text/plain"}
response = request(server, "PUT", path, body, headers)
if response:
assert response.status == 200
doc = response.read()
print doc
assert doc == "Goodbye world!"
raw_input("press return")
if location:
print "GETting newer resource..."
server, path = urlparse.urlparse(location)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
print doc
assert doc == "Goodbye world!"
raw_input("press return")
if location:
print "DELETEing resource..."
server, path = urlparse.urlparse(location)[1:3]
response = request(server, "DELETE", path)
if response:
assert response.status == 204
raw_input("press return")
if location:
print "GETting deleted resource..."
server, path = urlparse.urlparse(location)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 404
raw_input("press return")
if collection_href:
print "getting collection we just deleted from..."
server, path = urlparse.urlparse(collection_href)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
collection = parse_atom(doc)
print "collection:"
for entry in collection.entries:
print "\tmember:", entry.title.value
print "\t\tupdated:", entry.updated.value
for link in entry.links:
print "\t\tlink %s: %s" % (link.rel, link.href)
raw_input("press return")
## atom collection tests
collection_href = None
response = request(SERVER, "GET", "/introspection")
if response:
assert response.status == 200
doc = response.read()
service = parse_introspection(doc)
collection_href = service.workspaces[0].collections[1].href
if collection_href:
print "getting second collection in first workspace..."
server, path = urlparse.urlparse(collection_href)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
collection = parse_atom(doc)
print "collection:"
for entry in collection.entries:
print "\tmember:", entry.title.value
print "\t\tupdated:", entry.updated.value
for link in entry.links:
print "\t\tlink %s: %s" % (link.rel, link.href)
raw_input("press return")
if collection_href:
print "POSTing non-atom to entry collection..."
server, path = urlparse.urlparse(collection_href)[1:3]
body = "Hello world!"
headers = {"Content-type": "text/plain", "Title": "My Title"}
response = request(server, "POST", path, body, headers)
if response:
assert response.status == 415
raw_input("press return")
location = None
if collection_href:
print "POSTing atom to entry collection..."
server, path = urlparse.urlparse(collection_href)[1:3]
body = """
Atom-Powered Robots Run Amok2003-12-13T18:30:02ZSome text."""
headers = {"Content-type": "application/atom+xml"}
response = request(server, "POST", path, body, headers)
if response:
assert response.status == 201
location = response.getheader("Location")
print "got location:", location
raw_input("press return")
if location:
print "GETting new resource..."
server, path = urlparse.urlparse(location)[1:3]
response = request(server, "GET", path)
if response:
assert response.status == 200
doc = response.read()
print doc
demokritos-0.1.0/test/coverage.py 0000644 0000765 0000765 00000073245 10316664521 020157 0 ustar jtauber jtauber 0000000 0000000 #!/usr/bin/python
#
# Perforce Defect Tracking Integration Project
#
#
# COVERAGE.PY -- COVERAGE TESTING
#
# Gareth Rees, Ravenbrook Limited, 2001-12-04
# Ned Batchelder, 2004-12-12
# http://nedbatchelder.com/code/modules/coverage.html
#
#
# 1. INTRODUCTION
#
# This module provides coverage testing for Python code.
#
# The intended readership is all Python developers.
#
# This document is not confidential.
#
# See [GDR 2001-12-04a] for the command-line interface, programmatic
# interface and limitations. See [GDR 2001-12-04b] for requirements and
# design.
"""Usage:
coverage.py -x MODULE.py [ARG1 ARG2 ...]
Execute module, passing the given command-line arguments, collecting
coverage data.
coverage.py -e
Erase collected coverage data.
coverage.py -r [-m] FILE1 FILE2 ...
Report on the statement coverage for the given files. With the -m
option, show line numbers of the statements that weren't executed.
coverage.py -a [-d dir] FILE1 FILE2 ...
Make annotated copies of the given files, marking statements that
are executed with > and statements that are missed with !. With
the -d option, make the copies in that directory. Without the -d
option, make each copy in the same directory as the original.
Coverage data is saved in the file .coverage by default. Set the
COVERAGE_FILE environment variable to save it somewhere else."""
__version__ = "2.2.20041231" # see detailed history at the end of this file.
import compiler
import compiler.visitor
import os
import re
import string
import sys
import types
# 2. IMPLEMENTATION
#
# This uses the "singleton" pattern.
#
# The word "morf" means a module object (from which the source file can
# be deduced by suitable manipulation of the __file__ attribute) or a
# filename.
#
# When we generate a coverage report we have to canonicalize every
# filename in the coverage dictionary just in case it refers to the
# module we are reporting on. It seems a shame to throw away this
# information so the data in the coverage dictionary is transferred to
# the 'cexecuted' dictionary under the canonical filenames.
#
# The coverage dictionary is called "c" and the trace function "t". The
# reason for these short names is that Python looks up variables by name
# at runtime and so execution time depends on the length of variables!
# In the bottleneck of this application it's appropriate to abbreviate
# names to increase speed.
# A dictionary with an entry for (Python source file name, line number
# in that file) if that line has been executed.
c = {}
# t(f, x, y). This method is passed to sys.settrace as a trace
# function. See [van Rossum 2001-07-20b, 9.2] for an explanation of
# sys.settrace and the arguments and return value of the trace function.
# See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
# objects.
def t(f, w, a):
#print w, f.f_code.co_filename, f.f_lineno
if w == 'line':
c[(f.f_code.co_filename, f.f_lineno)] = 1
return t
class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
def __init__(self, statements, excluded, suite_spots):
compiler.visitor.ASTVisitor.__init__(self)
self.statements = statements
self.excluded = excluded
self.suite_spots = suite_spots
self.excluding_suite = 0
def doRecursive(self, node):
self.recordNodeLine(node)
for n in node.getChildNodes():
self.dispatch(n)
visitStmt = visitModule = doRecursive
def doCode(self, node):
if hasattr(node, 'decorators') and node.decorators:
self.dispatch(node.decorators)
self.doSuite(node, node.code)
visitFunction = visitClass = doCode
def getFirstLine(self, node):
# Find the first line in the tree node.
lineno = node.lineno
for n in node.getChildNodes():
f = self.getFirstLine(n)
if lineno and f:
lineno = min(lineno, f)
else:
lineno = lineno or f
return lineno
def getLastLine(self, node):
# Find the first line in the tree node.
lineno = node.lineno
for n in node.getChildNodes():
lineno = max(lineno, self.getLastLine(n))
return lineno
def doStatement(self, node):
self.recordLine(self.getFirstLine(node))
visitAssert = visitAssign = visitAssTuple = visitDiscard = visitPrint = \
visitPrintnl = visitRaise = visitSubscript = \
visitDecorators = \
doStatement
def recordNodeLine(self, node):
return self.recordLine(node.lineno)
def recordLine(self, lineno):
# Returns a bool, whether the line is included or excluded.
if lineno:
# Multi-line tests introducing suites have to get charged to their
# keyword.
if lineno in self.suite_spots:
lineno = self.suite_spots[lineno][0]
# If we're inside an exluded suite, record that this line was
# excluded.
if self.excluding_suite:
self.excluded[lineno] = 1
return 0
# If this line is excluded, or suite_spots maps this line to
# another line that is exlcuded, then we're excluded.
elif self.excluded.has_key(lineno) or \
self.suite_spots.has_key(lineno) and \
self.excluded.has_key(self.suite_spots[lineno][1]):
return 0
# Otherwise, this is an executable line.
else:
self.statements[lineno] = 1
return 1
return 0
default = recordNodeLine
def recordAndDispatch(self, node):
self.recordNodeLine(node)
self.dispatch(node)
def doSuite(self, intro, body, exclude=0):
exsuite = self.excluding_suite
if exclude or (intro and not self.recordNodeLine(intro)):
self.excluding_suite = 1
self.recordAndDispatch(body)
self.excluding_suite = exsuite
def doPlainWordSuite(self, prevsuite, suite):
# Finding the exclude lines for else's is tricky, because they aren't
# present in the compiler parse tree. Look at the previous suite,
# and find its last line. If any line between there and the else's
# first line are excluded, then we exclude the else.
lastprev = self.getLastLine(prevsuite)
firstelse = self.getFirstLine(suite)
for l in range(lastprev+1, firstelse):
if self.suite_spots.has_key(l):
self.doSuite(None, suite, exclude=self.excluded.has_key(l))
break
else:
self.doSuite(None, suite)
def doElse(self, prevsuite, node):
if node.else_:
self.doPlainWordSuite(prevsuite, node.else_)
def visitFor(self, node):
self.doSuite(node, node.body)
self.doElse(node.body, node)
def visitIf(self, node):
# The first test has to be handled separately from the rest.
# The first test is credited to the line with the "if", but the others
# are credited to the line with the test for the elif.
self.doSuite(node, node.tests[0][1])
for t, n in node.tests[1:]:
self.doSuite(t, n)
self.doElse(node.tests[-1][1], node)
def visitWhile(self, node):
self.doSuite(node, node.body)
self.doElse(node.body, node)
def visitTryExcept(self, node):
self.doSuite(node, node.body)
for i in range(len(node.handlers)):
a, b, h = node.handlers[i]
if not a:
# It's a plain "except:". Find the previous suite.
if i > 0:
prev = node.handlers[i-1][2]
else:
prev = node.body
self.doPlainWordSuite(prev, h)
else:
self.doSuite(a, h)
self.doElse(node.handlers[-1][2], node)
def visitTryFinally(self, node):
self.doSuite(node, node.body)
self.doPlainWordSuite(node.body, node.final)
def visitGlobal(self, node):
# "global" statements don't execute like others (they don't call the
# trace function), so don't record their line numbers.
pass
the_coverage = None
class coverage:
error = "coverage error"
# Name of the cache file (unless environment variable is set).
cache_default = ".coverage"
# Environment variable naming the cache file.
cache_env = "COVERAGE_FILE"
# A map from canonical Python source file name to a dictionary in
# which there's an entry for each line number that has been
# executed.
cexecuted = {}
# Cache of results of calling the analysis2() method, so that you can
# specify both -r and -a without doing double work.
analysis_cache = {}
# Cache of results of calling the canonical_filename() method, to
# avoid duplicating work.
canonical_filename_cache = {}
def __init__(self):
global the_coverage
if the_coverage:
raise self.error, "Only one coverage object allowed."
self.usecache = 1
self.cache = None
self.exclude_re = ''
def help(self, error=None):
if error:
print error
print
print __doc__
sys.exit(1)
def command_line(self):
import getopt
settings = {}
optmap = {
'-a': 'annotate',
'-d:': 'directory=',
'-e': 'erase',
'-h': 'help',
'-i': 'ignore-errors',
'-m': 'show-missing',
'-r': 'report',
'-x': 'execute',
}
short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
long_opts = optmap.values()
options, args = getopt.getopt(sys.argv[1:], short_opts,
long_opts)
for o, a in options:
if optmap.has_key(o):
settings[optmap[o]] = 1
elif optmap.has_key(o + ':'):
settings[optmap[o + ':']] = a
elif o[2:] in long_opts:
settings[o[2:]] = 1
elif o[2:] + '=' in long_opts:
settings[o[2:]] = a
else:
self.help("Unknown option: '%s'." % o)
if settings.get('help'):
self.help()
for i in ['erase', 'execute']:
for j in ['annotate', 'report']:
if settings.get(i) and settings.get(j):
self.help("You can't specify the '%s' and '%s' "
"options at the same time." % (i, j))
args_needed = (settings.get('execute')
or settings.get('annotate')
or settings.get('report'))
action = settings.get('erase') or args_needed
if not action:
self.help("You must specify at least one of -e, -x, -r, or -a.")
if not args_needed and args:
self.help("Unexpected arguments %s." % args)
self.get_ready()
self.exclude('#pragma[: ]+[nN][oO] [cC][oO][vV][eE][rR]')
if settings.get('erase'):
self.erase()
if settings.get('execute'):
if not args:
self.help("Nothing to do.")
sys.argv = args
self.start()
import __main__
sys.path[0] = os.path.dirname(sys.argv[0])
execfile(sys.argv[0], __main__.__dict__)
if not args:
args = self.cexecuted.keys()
ignore_errors = settings.get('ignore-errors')
show_missing = settings.get('show-missing')
directory = settings.get('directory=')
if settings.get('report'):
self.report(args, show_missing, ignore_errors)
if settings.get('annotate'):
self.annotate(args, directory, ignore_errors)
def use_cache(self, usecache):
self.usecache = usecache
def get_ready(self):
if self.usecache and not self.cache:
self.cache = os.environ.get(self.cache_env, self.cache_default)
self.restore()
self.analysis_cache = {}
def start(self):
self.get_ready()
sys.settrace(t)
def stop(self):
sys.settrace(None)
def erase(self):
global c
c = {}
self.analysis_cache = {}
self.cexecuted = {}
if self.cache and os.path.exists(self.cache):
os.remove(self.cache)
self.exclude_re = ''
def exclude(self, re):
if self.exclude_re:
self.exclude_re += "|"
self.exclude_re += "(" + re + ")"
# save(). Save coverage data to the coverage cache.
def save(self):
if self.usecache and self.cache:
self.canonicalize_filenames()
cache = open(self.cache, 'wb')
import marshal
marshal.dump(self.cexecuted, cache)
cache.close()
# restore(). Restore coverage data from the coverage cache (if it
# exists).
def restore(self):
global c
c = {}
self.cexecuted = {}
assert self.usecache
if not os.path.exists(self.cache):
return
try:
cache = open(self.cache, 'rb')
import marshal
cexecuted = marshal.load(cache)
cache.close()
if isinstance(cexecuted, types.DictType):
self.cexecuted = cexecuted
except:
pass
# canonical_filename(filename). Return a canonical filename for the
# file (that is, an absolute path with no redundant components and
# normalized case). See [GDR 2001-12-04b, 3.3].
def canonical_filename(self, filename):
if not self.canonical_filename_cache.has_key(filename):
f = filename
if os.path.isabs(f) and not os.path.exists(f):
f = os.path.basename(f)
if not os.path.isabs(f):
for path in [os.curdir] + sys.path:
g = os.path.join(path, f)
if os.path.exists(g):
f = g
break
cf = os.path.normcase(os.path.abspath(f))
self.canonical_filename_cache[filename] = cf
return self.canonical_filename_cache[filename]
# canonicalize_filenames(). Copy results from "executed" to
# "cexecuted", canonicalizing filenames on the way. Clear the
# "executed" map.
def canonicalize_filenames(self):
global c
for filename, lineno in c.keys():
f = self.canonical_filename(filename)
if not self.cexecuted.has_key(f):
self.cexecuted[f] = {}
self.cexecuted[f][lineno] = 1
c = {}
# morf_filename(morf). Return the filename for a module or file.
def morf_filename(self, morf):
if isinstance(morf, types.ModuleType):
if not hasattr(morf, '__file__'):
raise self.error, "Module has no __file__ attribute."
file = morf.__file__
else:
file = morf
return self.canonical_filename(file)
# analyze_morf(morf). Analyze the module or filename passed as
# the argument. If the source code can't be found, raise an error.
# Otherwise, return a tuple of (1) the canonical filename of the
# source code for the module, (2) a list of lines of statements
# in the source code, and (3) a list of lines of excluded statements.
def analyze_morf(self, morf):
if self.analysis_cache.has_key(morf):
return self.analysis_cache[morf]
filename = self.morf_filename(morf)
ext = os.path.splitext(filename)[1]
if ext == '.pyc':
if not os.path.exists(filename[0:-1]):
raise self.error, ("No source for compiled code '%s'."
% filename)
filename = filename[0:-1]
elif ext != '.py':
raise self.error, "File '%s' not Python source." % filename
source = open(filename, 'r')
lines, excluded_lines = self.find_executable_statements(
source.read(), exclude=self.exclude_re
)
source.close()
result = filename, lines, excluded_lines
self.analysis_cache[morf] = result
return result
def get_suite_spots(self, tree, spots):
import symbol, token
for i in range(1, len(tree)):
if type(tree[i]) == type(()):
if tree[i][0] == symbol.suite:
# Found a suite, look back for the colon and keyword.
lineno_colon = lineno_word = None
for j in range(i-1, 0, -1):
if tree[j][0] == token.COLON:
lineno_colon = tree[j][2]
elif tree[j][0] == token.NAME:
if tree[j][1] == 'elif':
# Find the line number of the first non-terminal
# after the keyword.
t = tree[j+1]
while t and token.ISNONTERMINAL(t[0]):
t = t[1]
if t:
lineno_word = t[2]
else:
lineno_word = tree[j][2]
break
elif tree[j][0] == symbol.except_clause:
# "except" clauses look like:
# ('except_clause', ('NAME', 'except', lineno), ...)
if tree[j][1][0] == token.NAME:
lineno_word = tree[j][1][2]
break
if lineno_colon and lineno_word:
# Found colon and keyword, mark all the lines
# between the two with the two line numbers.
for l in range(lineno_word, lineno_colon+1):
spots[l] = (lineno_word, lineno_colon)
self.get_suite_spots(tree[i], spots)
def find_executable_statements(self, text, exclude=None):
# Find lines which match an exclusion pattern.
excluded = {}
suite_spots = {}
if exclude:
reExclude = re.compile(exclude)
lines = text.split('\n')
for i in range(len(lines)):
if reExclude.search(lines[i]):
excluded[i+1] = 1
import parser
tree = parser.suite(text+'\n\n').totuple(1)
self.get_suite_spots(tree, suite_spots)
# Use the compiler module to parse the text and find the executable
# statements. We add newlines to be impervious to final partial lines.
statements = {}
ast = compiler.parse(text+'\n\n')
visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
compiler.walk(ast, visitor, walker=visitor)
lines = statements.keys()
lines.sort()
excluded_lines = excluded.keys()
excluded_lines.sort()
return lines, excluded_lines
# format_lines(statements, lines). Format a list of line numbers
# for printing by coalescing groups of lines as long as the lines
# represent consecutive statements. This will coalesce even if
# there are gaps between statements, so if statements =
# [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
# format_lines will return "1-2, 5-11, 13-14".
def format_lines(self, statements, lines):
pairs = []
i = 0
j = 0
start = None
pairs = []
while i < len(statements) and j < len(lines):
if statements[i] == lines[j]:
if start == None:
start = lines[j]
end = lines[j]
j = j + 1
elif start:
pairs.append((start, end))
start = None
i = i + 1
if start:
pairs.append((start, end))
def stringify(pair):
start, end = pair
if start == end:
return "%d" % start
else:
return "%d-%d" % (start, end)
import string
return string.join(map(stringify, pairs), ", ")
# Backward compatibility with version 1.
def analysis(self, morf):
f, s, _, m, mf = self.analysis2(morf)
return f, s, m, mf
def analysis2(self, morf):
filename, statements, excluded = self.analyze_morf(morf)
self.canonicalize_filenames()
if not self.cexecuted.has_key(filename):
self.cexecuted[filename] = {}
missing = []
for line in statements:
if not self.cexecuted[filename].has_key(line):
missing.append(line)
return (filename, statements, excluded, missing,
self.format_lines(statements, missing))
def morf_name(self, morf):
if isinstance(morf, types.ModuleType):
return morf.__name__
else:
return os.path.splitext(os.path.basename(morf))[0]
def report(self, morfs, show_missing=1, ignore_errors=0):
if not isinstance(morfs, types.ListType):
morfs = [morfs]
max_name = max([5,] + map(len, map(self.morf_name, morfs)))
fmt_name = "%%- %ds " % max_name
fmt_err = fmt_name + "%s: %s"
header = fmt_name % "Name" + " Stmts Exec Cover"
fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
if show_missing:
header = header + " Missing"
fmt_coverage = fmt_coverage + " %s"
print header
print "-" * len(header)
total_statements = 0
total_executed = 0
for morf in morfs:
name = self.morf_name(morf)
try:
_, statements, _, missing, readable = self.analysis2(morf)
n = len(statements)
m = n - len(missing)
if n > 0:
pc = 100.0 * m / n
else:
pc = 100.0
args = (name, n, m, pc)
if show_missing:
args = args + (readable,)
print fmt_coverage % args
total_statements = total_statements + n
total_executed = total_executed + m
except KeyboardInterrupt:
raise
except:
if not ignore_errors:
type, msg = sys.exc_info()[0:2]
print fmt_err % (name, type, msg)
if len(morfs) > 1:
print "-" * len(header)
if total_statements > 0:
pc = 100.0 * total_executed / total_statements
else:
pc = 100.0
args = ("TOTAL", total_statements, total_executed, pc)
if show_missing:
args = args + ("",)
print fmt_coverage % args
# annotate(morfs, ignore_errors).
blank_re = re.compile("\\s*(#|$)")
else_re = re.compile("\\s*else\\s*:\\s*(#|$)")
def annotate(self, morfs, directory=None, ignore_errors=0):
for morf in morfs:
try:
filename, statements, excluded, missing, _ = self.analysis2(morf)
self.annotate_file(filename, statements, excluded, missing, directory)
except KeyboardInterrupt:
raise
except:
if not ignore_errors:
raise
def annotate_file(self, filename, statements, excluded, missing, directory=None):
source = open(filename, 'r')
if directory:
dest_file = os.path.join(directory,
os.path.basename(filename)
+ ',cover')
else:
dest_file = filename + ',cover'
dest = open(dest_file, 'w')
lineno = 0
i = 0
j = 0
covered = 1
while 1:
line = source.readline()
if line == '':
break
lineno = lineno + 1
while i < len(statements) and statements[i] < lineno:
i = i + 1
while j < len(missing) and missing[j] < lineno:
j = j + 1
if i < len(statements) and statements[i] == lineno:
covered = j >= len(missing) or missing[j] > lineno
if self.blank_re.match(line):
dest.write(' ')
elif self.else_re.match(line):
# Special logic for lines containing only
# 'else:'. See [GDR 2001-12-04b, 3.2].
if i >= len(statements) and j >= len(missing):
dest.write('! ')
elif i >= len(statements) or j >= len(missing):
dest.write('> ')
elif statements[i] == missing[j]:
dest.write('! ')
else:
dest.write('> ')
elif lineno in excluded:
dest.write('- ')
elif covered:
dest.write('> ')
else:
dest.write('! ')
dest.write(line)
source.close()
dest.close()
# Singleton object.
the_coverage = coverage()
# Module functions call methods in the singleton object.
def use_cache(*args, **kw): return the_coverage.use_cache(*args, **kw)
def start(*args, **kw): return the_coverage.start(*args, **kw)
def stop(*args, **kw): return the_coverage.stop(*args, **kw)
def erase(*args, **kw): return the_coverage.erase(*args, **kw)
def exclude(*args, **kw): return the_coverage.exclude(*args, **kw)
def analysis(*args, **kw): return the_coverage.analysis(*args, **kw)
def analysis2(*args, **kw): return the_coverage.analysis2(*args, **kw)
def report(*args, **kw): return the_coverage.report(*args, **kw)
def annotate(*args, **kw): return the_coverage.annotate(*args, **kw)
def annotate_file(*args, **kw): return the_coverage.annotate_file(*args, **kw)
# Save coverage data when Python exits. (The atexit module wasn't
# introduced until Python 2.0, so use sys.exitfunc when it's not
# available.)
try:
import atexit
atexit.register(the_coverage.save)
except ImportError:
sys.exitfunc = the_coverage.save
# Command-line interface.
if __name__ == '__main__':
the_coverage.command_line()
# A. REFERENCES
#
# [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
# Ravenbrook Limited; 2001-12-04;
# .
#
# [GDR 2001-12-04b] "Statement coverage for Python: design and
# analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
# .
#
# [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
# Guide van Rossum; 2001-07-20;
# .
#
# [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
# 2001-07-20; .
#
#
# B. DOCUMENT HISTORY
#
# 2001-12-04 GDR Created.
#
# 2001-12-06 GDR Added command-line interface and source code
# annotation.
#
# 2001-12-09 GDR Moved design and interface to separate documents.
#
# 2001-12-10 GDR Open cache file as binary on Windows. Allow
# simultaneous -e and -x, or -a and -r.
#
# 2001-12-12 GDR Added command-line help. Cache analysis so that it
# only needs to be done once when you specify -a and -r.
#
# 2001-12-13 GDR Improved speed while recording. Portable between
# Python 1.5.2 and 2.1.1.
#
# 2002-01-03 GDR Module-level functions work correctly.
#
# 2002-01-07 GDR Update sys.path when running a file with the -x option,
# so that it matches the value the program would get if it were run on
# its own.
#
# 2004-12-12 NMB Significant code changes.
# - Finding executable statements has been rewritten so that docstrings and
# other quirks of Python execution aren't mistakenly identified as missing
# lines.
# - Lines can be excluded from consideration, even entire suites of lines.
# - The filesystem cache of covered lines can be disabled programmatically.
# - Modernized the code.
#
# 2004-12-14 NMB Minor tweaks. Return 'analysis' to its original behavior
# and add 'analysis2'. Add a global for 'annotate', and factor it, adding
# 'annotate_file'.
#
# 2004-12-31 NMB Allow for keyword arguments in the module global functions.
#
# C. COPYRIGHT AND LICENCE
#
# Copyright 2001 Gareth Rees. All rights reserved.
# Copyright 2004 Ned Batchelder. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
# $Id: coverage.py 5 2004-12-14 12:08:23Z ned $
demokritos-0.1.0/test/data/ 0000755 0000765 0000765 00000000000 10343115344 016702 5 ustar jtauber jtauber 0000000 0000000 demokritos-0.1.0/test/data/category.atom 0000644 0000765 0000765 00000002661 10316664521 021414 0 ustar jtauber jtauber 0000000 0000000
dive into mark
A <em>lot</em> of effort
went into making this effortless
Mark Pilgrimhttp://example.org/f8dy@example.com2005-07-31T12:29:29Ztag:example.org,2003:3Copyright (c) 2003, Mark Pilgrim
Example Toolkit
Atom draft-07 snapshottag:example.org,2003:3.23972005-07-31T12:29:29ZhelloAtom draft-07 snapshottag:example.org,2003:3.23972005-07-31T12:29:29Zhellowhat goes here?