Commits

bsm...@localhost  committed 17e1a51 Merge
  • Participants
  • Parent commits 3a5c85a, 4c35356

Comments (0)

Files changed (33)

 ^dist$
 ^stomp.egg-info$
 ^push_to_bitbucket$
+\..*sw[po]
+.*egg-info
+docs/\.build
-Dead simple Python STOMP client library
+==========================================
+ stompy - Python STOMP client library
+==========================================
 
-    This is useful for connecting to and communicating with
-    Apache ActiveMQ, an open source Java Message Service (JMS)
-    message broker.
+This is useful for connecting to and communicating with
+Apache `ActiveMQ`_ (an open source `Java Message Service`_ (JMS)
+message broker) or other brokers with support for the `STOMP`_ protocol.
 
-    The majority of the methods available take a single argument; a dictionary.
-    This dictionary should contain the necessary bits you need
-    to pass to the STOMP server.  It is outlined in each method
-    exactly what it needs to work.
+The majority of the methods available take a single argument; a dictionary.
+This dictionary should contain the necessary bits you need
+to pass to the `STOMP`_ server.  It is outlined in each method
+exactly what it needs to work.
 
-    For specifics on the protocol, see: http://stomp.codehaus.org/Protocol
+For specifics on the protocol, see the `STOMP protocol specification`_.
 
-    This library is basically a Python implementation of Perl's Net::Stomp
-    See: http://search.cpan.org/dist/Net-Stomp/lib/Net/Stomp.pm
+This library is basically a Python implementation of Perl's `Net::Stomp`_.
 
-    To enable the ActiveMQ Broker for Stomp add the following to the activemq.xml configuration:
+To enable the `ActiveMQ`_ Broker for `STOMP`_ add the following to the
+``activemq.xml`` configuration::
 
     <connector>
         <serverTransport uri="stomp://localhost:61613"/>
     </connector>
 
+See http://bitbucket.org/asksol/python-stomp/ for latest code.
 
-See http://bitbucket.org/benjaminws/python-stomp/ for lastest code.
-
-See http://just-another.net/python-stomp for documentation
+.. _`ActiveMQ`: http://activemq.apache.org/
+.. _`Java Message Service`: http://java.sun.com/products/jms/
+.. _`STOMP`: http://stomp.codehaus.org/
+.. _`STOMP protocol specification`: http://stomp.codehaus.org/Protocol
+.. _`Net::Stomp`: http://search.cpan.org/perldoc?Net::Stomp

File docs/Makefile

+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html      to make standalone HTML files"
+	@echo "  pickle    to make pickle files"
+	@echo "  json      to make JSON files"
+	@echo "  htmlhelp  to make HTML files and a HTML help project"
+	@echo "  latex     to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  changes   to make an overview over all changed/added/deprecated items"
+	@echo "  linkcheck to check all external links for integrity"
+
+clean:
+	-rm -rf .build/*
+
+html:
+	mkdir -p .build/html .build/doctrees
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
+	@echo
+	@echo "Build finished. The HTML pages are in .build/html."
+
+pickle:
+	mkdir -p .build/pickle .build/doctrees
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+web: pickle
+
+json:
+	mkdir -p .build/json .build/doctrees
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) .build/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	mkdir -p .build/htmlhelp .build/doctrees
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in .build/htmlhelp."
+
+latex:
+	mkdir -p .build/latex .build/doctrees
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in .build/latex."
+	@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+	      "run these through (pdf)latex."
+
+changes:
+	mkdir -p .build/changes .build/doctrees
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
+	@echo
+	@echo "The overview file is in .build/changes."
+
+linkcheck:
+	mkdir -p .build/linkcheck .build/doctrees
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in .build/linkcheck/output.txt."

File docs/_ext/applyxrefs.py

+"""Adds xref targets to the top of files."""
+
+import sys
+import os
+
+testing = False
+
+DONT_TOUCH = (
+        './index.txt',
+        )
+
+
+def target_name(fn):
+    if fn.endswith('.txt'):
+        fn = fn[:-4]
+    return '_' + fn.lstrip('./').replace('/', '-')
+
+
+def process_file(fn, lines):
+    lines.insert(0, '\n')
+    lines.insert(0, '.. %s:\n' % target_name(fn))
+    try:
+        f = open(fn, 'w')
+    except IOError:
+        print("Can't open %s for writing. Not touching it." % fn)
+        return
+    try:
+        f.writelines(lines)
+    except IOError:
+        print("Can't write to %s. Not touching it." % fn)
+    finally:
+        f.close()
+
+
+def has_target(fn):
+    try:
+        f = open(fn, 'r')
+    except IOError:
+        print("Can't open %s. Not touching it." % fn)
+        return (True, None)
+    readok = True
+    try:
+        lines = f.readlines()
+    except IOError:
+        print("Can't read %s. Not touching it." % fn)
+        readok = False
+    finally:
+        f.close()
+        if not readok:
+            return (True, None)
+
+    #print fn, len(lines)
+    if len(lines) < 1:
+        print("Not touching empty file %s." % fn)
+        return (True, None)
+    if lines[0].startswith('.. _'):
+        return (True, None)
+    return (False, lines)
+
+
+def main(argv=None):
+    if argv is None:
+        argv = sys.argv
+
+    if len(argv) == 1:
+        argv.extend('.')
+
+    files = []
+    for root in argv[1:]:
+        for (dirpath, dirnames, filenames) in os.walk(root):
+            files.extend([(dirpath, f) for f in filenames])
+    files.sort()
+    files = [os.path.join(p, fn) for p, fn in files if fn.endswith('.txt')]
+    #print files
+
+    for fn in files:
+        if fn in DONT_TOUCH:
+            print("Skipping blacklisted file %s." % fn)
+            continue
+
+        target_found, lines = has_target(fn)
+        if not target_found:
+            if testing:
+                print '%s: %s' % (fn, lines[0]),
+            else:
+                print "Adding xref to %s" % fn
+                process_file(fn, lines)
+        else:
+            print "Skipping %s: already has a xref" % fn
+
+if __name__ == '__main__':
+    sys.exit(main())

File docs/_ext/literals_to_xrefs.py

+"""
+Runs through a reST file looking for old-style literals, and helps replace them
+with new-style references.
+"""
+
+import re
+import sys
+import shelve
+
+refre = re.compile(r'``([^`\s]+?)``')
+
+ROLES = (
+    'attr',
+    'class',
+    "djadmin",
+    'data',
+    'exc',
+    'file',
+    'func',
+    'lookup',
+    'meth',
+    'mod',
+    "djadminopt",
+    "ref",
+    "setting",
+    "term",
+    "tfilter",
+    "ttag",
+
+    # special
+    "skip",
+)
+
+ALWAYS_SKIP = [
+    "NULL",
+    "True",
+    "False",
+]
+
+
+def fixliterals(fname):
+    data = open(fname).read()
+
+    last = 0
+    new = []
+    storage = shelve.open("/tmp/literals_to_xref.shelve")
+    lastvalues = storage.get("lastvalues", {})
+
+    for m in refre.finditer(data):
+
+        new.append(data[last:m.start()])
+        last = m.end()
+
+        line_start = data.rfind("\n", 0, m.start())
+        line_end = data.find("\n", m.end())
+        prev_start = data.rfind("\n", 0, line_start)
+        next_end = data.find("\n", line_end + 1)
+
+        # Skip always-skip stuff
+        if m.group(1) in ALWAYS_SKIP:
+            new.append(m.group(0))
+            continue
+
+        # skip when the next line is a title
+        next_line = data[m.end():next_end].strip()
+        if next_line[0] in "!-/:-@[-`{-~" and \
+                all(c == next_line[0] for c in next_line):
+            new.append(m.group(0))
+            continue
+
+        sys.stdout.write("\n"+"-"*80+"\n")
+        sys.stdout.write(data[prev_start+1:m.start()])
+        sys.stdout.write(colorize(m.group(0), fg="red"))
+        sys.stdout.write(data[m.end():next_end])
+        sys.stdout.write("\n\n")
+
+        replace_type = None
+        while replace_type is None:
+            replace_type = raw_input(
+                colorize("Replace role: ", fg="yellow")).strip().lower()
+            if replace_type and replace_type not in ROLES:
+                replace_type = None
+
+        if replace_type == "":
+            new.append(m.group(0))
+            continue
+
+        if replace_type == "skip":
+            new.append(m.group(0))
+            ALWAYS_SKIP.append(m.group(1))
+            continue
+
+        default = lastvalues.get(m.group(1), m.group(1))
+        if default.endswith("()") and \
+                replace_type in ("class", "func", "meth"):
+            default = default[:-2]
+        replace_value = raw_input(
+            colorize("Text <target> [", fg="yellow") + default + \
+                    colorize("]: ", fg="yellow")).strip()
+        if not replace_value:
+            replace_value = default
+        new.append(":%s:`%s`" % (replace_type, replace_value))
+        lastvalues[m.group(1)] = replace_value
+
+    new.append(data[last:])
+    open(fname, "w").write("".join(new))
+
+    storage["lastvalues"] = lastvalues
+    storage.close()
+
+#
+# The following is taken from django.utils.termcolors and is copied here to
+# avoid the dependancy.
+#
+
+
+def colorize(text='', opts=(), **kwargs):
+    """
+    Returns your text, enclosed in ANSI graphics codes.
+
+    Depends on the keyword arguments 'fg' and 'bg', and the contents of
+    the opts tuple/list.
+
+    Returns the RESET code if no parameters are given.
+
+    Valid colors:
+        'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
+
+    Valid options:
+        'bold'
+        'underscore'
+        'blink'
+        'reverse'
+        'conceal'
+        'noreset' - string will not be auto-terminated with the RESET code
+
+    Examples:
+        colorize('hello', fg='red', bg='blue', opts=('blink',))
+        colorize()
+        colorize('goodbye', opts=('underscore',))
+        print colorize('first line', fg='red', opts=('noreset',))
+        print 'this should be red too'
+        print colorize('and so should this')
+        print 'this should not be red'
+    """
+    color_names = ('black', 'red', 'green', 'yellow',
+                   'blue', 'magenta', 'cyan', 'white')
+    foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
+    background = dict([(color_names[x], '4%s' % x) for x in range(8)])
+
+    RESET = '0'
+    opt_dict = {'bold': '1',
+                'underscore': '4',
+                'blink': '5',
+                'reverse': '7',
+                'conceal': '8'}
+
+    text = str(text)
+    code_list = []
+    if text == '' and len(opts) == 1 and opts[0] == 'reset':
+        return '\x1b[%sm' % RESET
+    for k, v in kwargs.iteritems():
+        if k == 'fg':
+            code_list.append(foreground[v])
+        elif k == 'bg':
+            code_list.append(background[v])
+    for o in opts:
+        if o in opt_dict:
+            code_list.append(opt_dict[o])
+    if 'noreset' not in opts:
+        text = text + '\x1b[%sm' % RESET
+    return ('\x1b[%sm' % ';'.join(code_list)) + text
+
+if __name__ == '__main__':
+    try:
+        fixliterals(sys.argv[1])
+    except (KeyboardInterrupt, SystemExit):
+        print

File docs/_theme/agogo/layout.html

+{%- block doctype -%}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+{%- endblock %}
+{%- set reldelim1 = reldelim1 is not defined and ' &raquo;' or reldelim1 %}
+{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
+
+{%- macro relbar() %}
+    <div class="related">
+      <h3>{{ _('Navigation') }}</h3>
+      <ul>
+        {%- for rellink in rellinks %}
+        <li class="right" {% if loop.first %}style="margin-right: 10px"{% endif %}>
+          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
+             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
+          {%- if not loop.first %}{{ reldelim2 }}{% endif %}</li>
+        {%- endfor %}
+        {%- block rootrellink %}
+        <li><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}</li>
+        {%- endblock %}
+        {%- for parent in parents %}
+        <li><a href="{{ parent.link|e }}" {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>{{ reldelim1 }}</li>
+        {%- endfor %}
+        {%- block relbaritems %} {% endblock %}
+      </ul>
+    </div>
+    {%- endmacro %}
+
+    {%- macro sidebar() %}
+    {%- if not embedded %}{% if not theme_nosidebar|tobool %}
+    <div class="sphinxsidebar">
+      <div class="sphinxsidebarwrapper">
+        {%- block sidebarlogo %}
+        {%- if logo %}
+        <p class="logo"><a href="{{ pathto(master_doc) }}">
+            <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
+        </a></p>
+        {%- endif %}
+        {%- endblock %}
+        {%- block sidebartoc %}
+        {%- if display_toc %}
+        <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
+        {{ toc }}
+        {%- endif %}
+        {%- endblock %}
+        {%- block sidebarrel %}
+        {%- if prev %}
+        <h4>{{ _('Previous topic') }}</h4>
+        <p class="topless"><a href="{{ prev.link|e }}"
+                              title="{{ _('previous chapter') }}">{{ prev.title }}</a></p>
+        {%- endif %}
+        {%- if next %}
+        <h4>{{ _('Next topic') }}</h4>
+        <p class="topless"><a href="{{ next.link|e }}"
+                              title="{{ _('next chapter') }}">{{ next.title }}</a></p>
+        {%- endif %}
+        {%- endblock %}
+        {%- block sidebarsourcelink %}
+        {%- if show_source and has_source and sourcename %}
+        <h3>{{ _('This Page') }}</h3>
+        <ul class="this-page-menu">
+          <li><a href="{{ pathto('_sources/' + sourcename, true)|e }}"
+                 rel="nofollow">{{ _('Show Source') }}</a></li>
+        </ul>
+        {%- endif %}
+        {%- endblock %}
+        {%- if customsidebar %}
+        {% include customsidebar %}
+        {%- endif %}
+        {%- block sidebarsearch %}
+        {%- if pagename != "search" %}
+        <div id="searchbox" style="display: none">
+          <h3>{{ _('Quick search') }}</h3>
+          <form class="search" action="{{ pathto('search') }}" method="get">
+            <input type="text" name="q" size="18" />
+            <input type="submit" value="{{ _('Go') }}" />
+            <input type="hidden" name="check_keywords" value="yes" />
+            <input type="hidden" name="area" value="default" />
+          </form>
+          <p class="searchtip" style="font-size: 90%">
+            {{ _('Enter search terms or a module, class or function name.') }}
+          </p>
+        </div>
+        <script type="text/javascript">$('#searchbox').show(0);</script>
+        {%- endif %}
+        {%- endblock %}
+      </div>
+    </div>
+    {%- endif %}{% endif %}
+    {%- endmacro %}
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    {{ metatags }}
+    {%- if not embedded %}
+    {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
+    {%- else %}
+    {%- set titlesuffix = "" %}
+    {%- endif %}
+    <title>{{ title|striptags }}{{ titlesuffix }}</title>
+    <link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
+    <link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
+    {%- if not embedded %}
+    <script type="text/javascript">
+      var DOCUMENTATION_OPTIONS = {
+      URL_ROOT:    '{{ pathto("", 1) }}',
+      VERSION:     '{{ release|e }}',
+      COLLAPSE_MODINDEX: false,
+      FILE_SUFFIX: '{{ file_suffix }}',
+      HAS_SOURCE:  {{ has_source|lower }}
+      };
+    </script>
+    {%- for scriptfile in script_files %}
+    <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
+    {%- endfor %}
+    {%- if use_opensearch %}
+    <link rel="search" type="application/opensearchdescription+xml"
+          title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
+          href="{{ pathto('_static/opensearch.xml', 1) }}"/>
+    {%- endif %}
+    {%- if favicon %}
+    <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
+    {%- endif %}
+    {%- endif %}
+    {%- block linktags %}
+    {%- if hasdoc('about') %}
+    <link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
+    {%- endif %}
+    {%- if hasdoc('genindex') %}
+    <link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
+    {%- endif %}
+    {%- if hasdoc('search') %}
+    <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
+    {%- endif %}
+    {%- if hasdoc('copyright') %}
+    <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
+    {%- endif %}
+    <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
+    {%- if parents %}
+    <link rel="up" title="{{ parents[-1].title|striptags }}" href="{{ parents[-1].link|e }}" />
+    {%- endif %}
+    {%- if next %}
+    <link rel="next" title="{{ next.title|striptags }}" href="{{ next.link|e }}" />
+    {%- endif %}
+    {%- if prev %}
+    <link rel="prev" title="{{ prev.title|striptags }}" href="{{ prev.link|e }}" />
+    {%- endif %}
+    {%- endblock %}
+    {%- block extrahead %} {% endblock %}
+  </head>
+  <body>
+
+    <div class="header-wrapper">
+      <div class="header">
+	<h1><a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a></h1>
+	<div class="rel">
+          {%- for rellink in rellinks %}
+          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
+             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
+          {%- if not loop.last %}{{ reldelim2 }}{% endif %}
+          {%- endfor %}
+	</div>
+      </div>
+    </div>
+
+    <div class="content-wrapper">
+      <div class="content">
+	<div class="document">
+	  {%- block document %}
+	  <div class="documentwrapper">
+	    {%- if not embedded %}{% if not theme_nosidebar|tobool %}
+	    <div class="bodywrapper">
+	      {%- endif %}{% endif %}
+	      <div class="body">
+		{% block body %} {% endblock %}
+	      </div>
+	      {%- if not embedded %}{% if not theme_nosidebar|tobool %}
+	    </div>
+	    {%- endif %}{% endif %}
+	  </div>
+	  {%- endblock %}
+	</div>
+	<div class="sidebar">
+	  <h3>{{ _('Contents') }}</h3>
+	  {{ toctree() }}
+	  <h3 style="margin-top: 1.5em;">{{ _('Search') }}</h3>
+	  <form class="search" action="{{ pathto('search') }}" method="get">
+            <input type="text" name="q" size="18" />
+            <input type="submit" value="{{ _('Go') }}" />
+            <input type="hidden" name="check_keywords" value="yes" />
+            <input type="hidden" name="area" value="default" />
+          </form>
+          <p class="searchtip" style="font-size: 90%">
+            {{ _('Enter search terms or a module, class or function name.') }}
+          </p>
+	</div>
+	<div class="clearer"></div>
+      </div>
+    </div>
+
+    <div class="footer-wrapper">
+      {%- block footer %}
+      <div class="footer">
+	<div class="left">
+	  {%- for rellink in rellinks %}
+          <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags }}"
+             {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
+          {%- if not loop.last %}{{ reldelim2 }}{% endif %}
+	  {%- endfor %}
+	  {%- if show_source and has_source and sourcename %}
+            <br/>
+            <a href="{{ pathto('_sources/' + sourcename, true)|e }}"
+               rel="nofollow">{{ _('Show Source') }}</a>
+          {%- endif %}
+	</div>
+
+	<div class="right">
+	  {%- if hasdoc('copyright') %}
+	  {% trans path=pathto('copyright'), copyright=copyright|e %}&copy; <a href="{{ path }}">Copyright</a> {{ copyright }}.{% endtrans %}
+	  {%- else %}
+	  {% trans copyright=copyright|e %}&copy; Copyright {{ copyright }}.{% endtrans %}
+	  {%- endif %}
+	  {%- if last_updated %}
+	  {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
+	  {%- endif %}
+	  {%- if show_sphinx %}<br/>
+	  {% trans sphinx_version=sphinx_version|e %}Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> {{ sphinx_version }}.{% endtrans %}
+	  {%- endif %}
+	</div>
+	<div class="clearer"></div>
+      </div>
+      {%- endblock %}
+    </div>
+
+  </body>
+</html>

File docs/_theme/agogo/static/agogo.css_t

+* {
+  margin: 0px;
+  padding: 0px;
+}
+
+body {
+  font-family: {{ theme_bodyfont }};
+  line-height: 1.4em;
+  font-size: 14px;
+  color: black;
+  background-color: {{ theme_bgcolor }};
+}
+
+
+/* Page layout */
+
+div.header, div.content, div.footer {
+  width: {{ theme_pagewidth }};
+  margin-left: auto;
+  margin-right: auto;
+}
+
+div.header-wrapper {
+  background: {{ theme_headerbg }};
+  border-bottom: 3px solid #2e3436;
+}
+
+
+/* Default body styles */
+a {
+  text-decoration: none;
+  color: {{ theme_linkcolor }};
+}
+
+.clearer {
+  clear: both;
+}
+
+.left {
+  float: left;
+}
+
+.right {
+  float: right;
+}
+
+h1, h2, h3, h4 {
+  font-family: {{ theme_headerfont }};
+  font-weight: normal;
+  color: {{ theme_headercolor2 }};
+  margin-bottom: .8em;
+}
+
+h1 {
+  color: {{ theme_headercolor1 }};
+}
+
+h2 {
+  padding-bottom: .5em;
+  border-bottom: 1px solid {{ theme_headercolor2 }};
+}
+
+a.headerlink {
+  visibility: hidden;
+  color: #dddddd;
+  padding-left: .3em;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+  visibility: visible;
+}
+
+
+
+/* Header */
+
+div.header {
+  padding-top: 10px;
+  padding-bottom: 10px;
+}
+
+div.header h1 {
+  font-family: {{ theme_headerfont }};
+  font-weight: normal;
+  font-size: 160%;
+  letter-spacing: .08em;
+}
+
+div.header h1 a {
+  color: white;
+}
+
+div.header div.rel {
+  margin-top: 1em;
+}
+
+div.header div.rel a {
+  color: {{ theme_headerlinkcolor }};
+  letter-spacing: .1em;
+  text-transform: uppercase;
+}
+
+
+/* Content */
+div.content-wrapper {
+  background-color: white;
+  padding-top: 20px;
+  padding-bottom: 20px;
+}
+
+div.document {
+  width: {{ theme_documentwidth }};
+  float: left;
+}
+
+div.body {
+  padding-right: 2em;
+  text-align: justify;
+}
+
+div.document ul {
+  margin-left: 1.2em;
+  list-style-type: square;
+}
+
+div.document dd {
+  margin-left: 1.2em;
+  margin-top: .4em;
+  margin-bottom: 1em;
+}
+
+div.document .section {
+  margin-top: 1.7em;
+}
+div.document .section:first-child {
+  margin-top: 0px;
+}
+
+div.document div.highlight {
+  padding: 3px;
+  background-color: #eeeeec;
+  border-top: 2px solid #dddddd;
+  border-bottom: 2px solid #dddddd;
+  margin-top: .8em;
+  margin-bottom: .8em;
+}
+
+div.document h2 {
+  margin-top: .7em;
+}
+
+div.document p {
+  margin-bottom: .5em;
+}
+
+div.document li.toctree-l1 {
+  margin-bottom: 1em;
+}
+
+div.document .descname {
+  font-weight: bold;
+}
+
+div.document .docutils.literal {
+  background-color: #eeeeec;
+  padding: 1px;
+}
+
+div.document .docutils.xref.literal {
+  background-color: transparent;
+  padding: 0px;
+}
+
+
+/* Sidebar */
+
+div.sidebar {
+  width: {{ theme_sidebarwidth }};
+  float: right;
+  font-size: .9em;
+}
+
+div.sidebar h3 {
+  color: #2e3436;
+  text-transform: uppercase;
+  font-size: 130%;
+  letter-spacing: .1em;
+}
+
+div.sidebar ul {
+  list-style-type: none;
+}
+
+div.sidebar li.toctree-l1 a {
+  display: block;
+  padding: 1px;
+  border: 1px solid #dddddd;
+  background-color: #eeeeec;
+  margin-bottom: .4em;
+  padding-left: 3px;
+  color: #2e3436;
+}
+
+div.sidebar li.toctree-l2 a {
+  background-color: transparent;
+  border: none;
+  border-bottom: 1px solid #dddddd;
+}
+
+div.sidebar li.toctree-l2:last-child a {
+  border-bottom: none;
+}
+
+div.sidebar li.toctree-l1.current a {
+  border-right: 5px solid {{ theme_headerlinkcolor }};
+}
+
+div.sidebar li.toctree-l1.current li.toctree-l2 a {
+  border-right: none;
+}
+
+
+/* Footer */
+
+div.footer-wrapper {
+  background: {{ theme_footerbg }};
+  border-top: 4px solid #babdb6;
+  padding-top: 10px;
+  padding-bottom: 10px;
+  min-height: 80px;
+}
+
+div.footer, div.footer a {
+  color: #888a85;
+}
+
+div.footer .right {
+  text-align: right;
+}
+
+div.footer .left {
+  text-transform: uppercase;
+}
+
+
+/* Styles copied form basic theme */
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+    margin: 10px 0 0 20px;
+    padding: 0;
+}
+
+ul.search li {
+    padding: 5px 0 5px 20px;
+    background-image: url(file.png);
+    background-repeat: no-repeat;
+    background-position: 0 7px;
+}
+
+ul.search li a {
+    font-weight: bold;
+}
+
+ul.search li div.context {
+    color: #888;
+    margin: 2px 0 0 30px;
+    text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+    font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+    width: 90%;
+}
+
+table.contentstable p.biglink {
+    line-height: 150%;
+}
+
+a.biglink {
+    font-size: 1.3em;
+}
+
+span.linkdescr {
+    font-style: italic;
+    padding-top: 5px;
+    font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable td {
+    text-align: left;
+    vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+    height: 10px;
+}
+
+table.indextable tr.cap {
+    margin-top: 10px;
+    background-color: #f2f2f2;
+}
+
+img.toggler {
+    margin-right: 3px;
+    margin-top: 3px;
+    cursor: pointer;
+}
+
+

File docs/_theme/agogo/static/bgfooter.png

Added
New image

File docs/_theme/agogo/static/bgtop.png

Added
New image

File docs/_theme/agogo/theme.conf

+[theme]
+inherit = basic
+stylesheet = agogo.css
+pygments_style = tango
+
+[options]
+bodyfont = "Verdana", Arial, sans-serif
+headerfont = "Georgia", "Times New Roman", serif
+pagewidth = 70em
+documentwidth = 50em
+sidebarwidth = 20em
+bgcolor = #eeeeec
+headerbg = url(bgtop.png) top left repeat-x
+footerbg = url(bgfooter.png) top left repeat-x
+linkcolor = #ce5c00
+headercolor1 = #204a87
+headercolor2 = #3465a4
+headerlinkcolor = #fcaf3e

File docs/_theme/nature/static/nature.css_t

+/**
+ * Sphinx stylesheet -- default theme
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+ 
+@import url("basic.css");
+ 
+/* -- page layout ----------------------------------------------------------- */
+ 
+body {
+    font-family: Arial, sans-serif;
+    font-size: 100%;
+    background-color: #111;
+    color: #555;
+    margin: 0;
+    padding: 0;
+}
+
+hr{
+    border: 1px solid #B1B4B6;
+}
+ 
+div.document {
+    background-color: #eee;
+}
+ 
+div.body {
+    background-color: #ffffff;
+    color: #3E4349;
+    padding: 0 30px 30px 30px;
+    font-size: 0.8em;
+}
+ 
+div.footer {
+    color: #555;
+    width: 100%;
+    padding: 13px 0;
+    text-align: center;
+    font-size: 75%;
+}
+ 
+div.footer a {
+    color: #444;
+    text-decoration: underline;
+}
+ 
+div.related {
+    background-color: #6BA81E;
+    line-height: 32px;
+    color: #fff;
+    text-shadow: 0px 1px 0 #444;
+    font-size: 0.80em;
+}
+ 
+div.related a {
+    color: #E2F3CC;
+}
+ 
+div.sphinxsidebar {
+    font-size: 0.75em;
+    line-height: 1.5em;
+}
+
+div.sphinxsidebarwrapper{
+    padding: 20px 0;
+}
+ 
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+    font-family: Arial, sans-serif;
+    color: #222;
+    font-size: 1.2em;
+    font-weight: normal;
+    margin: 0;
+    padding: 5px 10px;
+    background-color: #ddd;
+    text-shadow: 1px 1px 0 white
+}
+
+div.sphinxsidebar h4{
+    font-size: 1.1em;
+}
+ 
+div.sphinxsidebar h3 a {
+    color: #444;
+}
+ 
+ 
+div.sphinxsidebar p {
+    color: #888;
+    padding: 5px 20px;
+}
+ 
+div.sphinxsidebar p.topless {
+}
+ 
+div.sphinxsidebar ul {
+    margin: 10px 20px;
+    padding: 0;
+    color: #000;
+}
+ 
+div.sphinxsidebar a {
+    color: #444;
+}
+ 
+div.sphinxsidebar input {
+    border: 1px solid #ccc;
+    font-family: sans-serif;
+    font-size: 1em;
+}
+
+div.sphinxsidebar input[type=text]{
+    margin-left: 20px;
+}
+ 
+/* -- body styles ----------------------------------------------------------- */
+ 
+a {
+    color: #005B81;
+    text-decoration: none;
+}
+ 
+a:hover {
+    color: #E32E00;
+    text-decoration: underline;
+}
+ 
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: Arial, sans-serif;
+    background-color: #BED4EB;
+    font-weight: normal;
+    color: #212224;
+    margin: 30px 0px 10px 0px;
+    padding: 5px 0 5px 10px;
+    text-shadow: 0px 1px 0 white
+}
+ 
+div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 150%; background-color: #C8D5E3; }
+div.body h3 { font-size: 120%; background-color: #D8DEE3; }
+div.body h4 { font-size: 110%; background-color: #D8DEE3; }
+div.body h5 { font-size: 100%; background-color: #D8DEE3; }
+div.body h6 { font-size: 100%; background-color: #D8DEE3; }
+ 
+a.headerlink {
+    color: #c60f0f;
+    font-size: 0.8em;
+    padding: 0 4px 0 4px;
+    text-decoration: none;
+}
+ 
+a.headerlink:hover {
+    background-color: #c60f0f;
+    color: white;
+}
+ 
+div.body p, div.body dd, div.body li {
+    text-align: justify;
+    line-height: 1.5em;
+}
+ 
+div.admonition p.admonition-title + p {
+    display: inline;
+}
+
+div.highlight{
+    background-color: white;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+ 
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+ 
+div.topic {
+    background-color: #eee;
+}
+ 
+div.warning {
+    background-color: #ffe4e4;
+    border: 1px solid #f66;
+}
+ 
+p.admonition-title {
+    display: inline;
+}
+ 
+p.admonition-title:after {
+    content: ":";
+}
+ 
+pre {
+    padding: 10px;
+    background-color: White;
+    color: #222;
+    line-height: 1.2em;
+    border: 1px solid #C6C9CB;
+    font-size: 1.2em;
+    margin: 1.5em 0 1.5em 0;
+    -webkit-box-shadow: 1px 1px 1px #d8d8d8;
+    -moz-box-shadow: 1px 1px 1px #d8d8d8;
+}
+ 
+tt {
+    background-color: #ecf0f3;
+    color: #222;
+    padding: 1px 2px;
+    font-size: 1.2em;
+    font-family: monospace;
+}

File docs/_theme/nature/static/pygments.css

+.c { color: #999988; font-style: italic } /* Comment */
+.k { font-weight: bold } /* Keyword */
+.o { font-weight: bold } /* Operator */
+.cm { color: #999988; font-style: italic } /* Comment.Multiline */
+.cp { color: #999999; font-weight: bold } /* Comment.preproc */
+.c1 { color: #999988; font-style: italic } /* Comment.Single */
+.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.ge { font-style: italic } /* Generic.Emph */
+.gr { color: #aa0000 } /* Generic.Error */
+.gh { color: #999999 } /* Generic.Heading */
+.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.go { color: #111 } /* Generic.Output */
+.gp { color: #555555 } /* Generic.Prompt */
+.gs { font-weight: bold } /* Generic.Strong */
+.gu { color: #aaaaaa } /* Generic.Subheading */
+.gt { color: #aa0000 } /* Generic.Traceback */
+.kc { font-weight: bold } /* Keyword.Constant */
+.kd { font-weight: bold } /* Keyword.Declaration */
+.kp { font-weight: bold } /* Keyword.Pseudo */
+.kr { font-weight: bold } /* Keyword.Reserved */
+.kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.m { color: #009999 } /* Literal.Number */
+.s { color: #bb8844 } /* Literal.String */
+.na { color: #008080 } /* Name.Attribute */
+.nb { color: #999999 } /* Name.Builtin */
+.nc { color: #445588; font-weight: bold } /* Name.Class */
+.no { color: #ff99ff } /* Name.Constant */
+.ni { color: #800080 } /* Name.Entity */
+.ne { color: #990000; font-weight: bold } /* Name.Exception */
+.nf { color: #990000; font-weight: bold } /* Name.Function */
+.nn { color: #555555 } /* Name.Namespace */
+.nt { color: #000080 } /* Name.Tag */
+.nv { color: purple } /* Name.Variable */
+.ow { font-weight: bold } /* Operator.Word */
+.mf { color: #009999 } /* Literal.Number.Float */
+.mh { color: #009999 } /* Literal.Number.Hex */
+.mi { color: #009999 } /* Literal.Number.Integer */
+.mo { color: #009999 } /* Literal.Number.Oct */
+.sb { color: #bb8844 } /* Literal.String.Backtick */
+.sc { color: #bb8844 } /* Literal.String.Char */
+.sd { color: #bb8844 } /* Literal.String.Doc */
+.s2 { color: #bb8844 } /* Literal.String.Double */
+.se { color: #bb8844 } /* Literal.String.Escape */
+.sh { color: #bb8844 } /* Literal.String.Heredoc */
+.si { color: #bb8844 } /* Literal.String.Interpol */
+.sx { color: #bb8844 } /* Literal.String.Other */
+.sr { color: #808000 } /* Literal.String.Regex */
+.s1 { color: #bb8844 } /* Literal.String.Single */
+.ss { color: #bb8844 } /* Literal.String.Symbol */
+.bp { color: #999999 } /* Name.Builtin.Pseudo */
+.vc { color: #ff99ff } /* Name.Variable.Class */
+.vg { color: #ff99ff } /* Name.Variable.Global */
+.vi { color: #ff99ff } /* Name.Variable.Instance */
+.il { color: #009999 } /* Literal.Number.Integer.Long */

File docs/_theme/nature/theme.conf

+[theme]
+inherit = basic
+stylesheet = nature.css
+pygments_style = tango
+
+[options]

File docs/conf.py

+# -*- coding: utf-8 -*-
+#
+# Four documentation build configuration file, created by
+# sphinx-quickstart on Mon May 18 21:37:44 2009.
+#
+# This file is execfile()d with the current directory set to its
+#containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed
+#automatically).
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+sys.path.insert(0, "../")
+from stompy import distmeta
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings.
+# They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Four'
+copyright = u'2009, Benjamin W. Smith'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = ".".join(map(str, distmeta.VERSION[0:2]))
+# The full version, including alpha/beta/rc tags.
+release = distmeta.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['.build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'trac'
+
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+#html_style = 'agogo.css'
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_use_modindex = True
+
+# If false, no index is generated.
+html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Fourdoc'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class
+# [howto/manual]).
+latex_documents = [
+  ('index', 'Four.tex', ur'Four Documentation',
+   ur'Benjamin W. Smith', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
+
+html_theme = "nature"
+html_theme_path = ["_theme"]

File docs/index.rst

+===============================================
+ stompy - Python STOMP Client  Documentation
+===============================================
+
+Contents:
+
+.. toctree::
+    :maxdepth: 3
+
+    introduction
+    reference/index
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+

File docs/introduction.rst

+==========================================
+ stompy - Python STOMP client library
+==========================================
+
+This is useful for connecting to and communicating with
+Apache `ActiveMQ`_ (an open source `Java Message Service`_ (JMS)
+message broker) or other brokers with support for the `STOMP`_ protocol.
+
+The majority of the methods available take a single argument; a dictionary.
+This dictionary should contain the necessary bits you need
+to pass to the `STOMP`_ server.  It is outlined in each method
+exactly what it needs to work.
+
+For specifics on the protocol, see the `STOMP protocol specification`_.
+
+This library is basically a Python implementation of Perl's `Net::Stomp`_.
+
+To enable the `ActiveMQ`_ Broker for `STOMP`_ add the following to the
+``activemq.xml`` configuration::
+
+    <connector>
+        <serverTransport uri="stomp://localhost:61613"/>
+    </connector>
+
+See http://bitbucket.org/asksol/python-stomp/ for latest code.
+
+.. _`ActiveMQ`: http://activemq.apache.org/
+.. _`Java Message Service`: http://java.sun.com/products/jms/
+.. _`STOMP`: http://stomp.codehaus.org/
+.. _`STOMP protocol specification`: http://stomp.codehaus.org/Protocol
+.. _`Net::Stomp`: http://search.cpan.org/perldoc?Net::Stomp

File docs/reference/index.rst

+===========================
+ API Reference
+===========================
+
+:Release: |version|
+:Date: |today|
+
+.. toctree::
+    :maxdepth: 2
+  
+    stompy.stomp
+    stompy.frame 

File docs/reference/stompy.frame.rst

+=======================================
+ Frames and communication - stompy.frame
+=======================================
+
+.. currentmodule:: stompy.frame
+
+.. automodule:: stompy.frame
+    :members:
+
+

File docs/stompy.stomp.rst

+=====================================
+Client - stompy.stomp
+=====================================
+
+.. currentmodule:: stompy.stomp
+
+.. automodule:: stompy.stomp
+    :members:
+
+

File examples/simple_example.py

+#!/usr/bin/env python
+import sys
+import itertools
+from stompy import Client as StompClient
+from itertools import count
+from optparse import OptionParser
+
+
+def consume(host, port, queue, num=None):
+    stomp = StompClient(host, port)
+    stomp.connect()
+
+    stomp.subscribe(queue, ack="client")
+
+    def _handle_message(frame):
+        print(frame.headers.get("message-id"))
+        print(frame.body)
+        stomp.ack(frame)
+
+    # if num is not set, iterate forever.
+    it = xrange(0, num) if num else itertools.count()
+
+    try:
+        for i in it:
+            frame = stomp.get()
+            _handle_message(frame)
+    finally:
+        stomp.disconnect()
+
+
+def produce(host, port, queue, num=1000):
+    stomp = StompClient(host, port)
+    stomp.connect()
+
+    for i in xrange(0, num):
+        print("Message #%d" % i)
+        this_frame = stomp.put("Testing %d" % i,
+                               destination=queue,
+                               persistent=True)
+        print("Receipt: %s" % this_frame.headers.get("receipt-id"))
+
+    stomp.disconnect()
+
+
+if __name__ == '__main__':
+    parser = OptionParser()
+    parser.add_option('-H', '--host', action='store', default="localhost",
+                      type='string', dest='host', help='hostname')
+    parser.add_option('-p', '--port', action='store',
+                      type='int', dest='port', help='port', default=61613)
+    parser.add_option('-q', '--queue', action='store',
+                      type='string', dest='queue', help='destination queue')
+    parser.add_option('-P', '--produce', action='store_true',
+                      default=False, dest='produce', help='produce messages')
+    parser.add_option('-c', '--consume', action='store_true',
+                      default=False, dest='consume', help='consume messages')
+    parser.add_option('-n', '--number', action='store',
+                      type='int', dest='number', default="100",
+                      help='produce or consume NUMBER messages')
+
+    options, args = parser.parse_args()
+
+    if not options.queue:
+        print("Queue name is required!")
+        parser.print_help()
+        sys.exit(1)
+
+    if options.produce:
+        print("PRODUCING")
+        produce(options.host, options.port, options.queue, options.number)
+    elif options.consume:
+        consume(options.host, options.port, options.queue, options.number)

File examples/stomp_example.py

 #!/usr/bin/env python
-import sys,time
-from stomp import Stomp
+import sys
+import time
+from stompy import Stomp
 from optparse import OptionParser
 
-def consume(host,port,queue,num=None):
+
+def consume(host, port, queue, num=None):
     try:
-        stomp = Stomp(host,port)
+        stomp = Stomp(host, port)
         stomp.connect()
     except:
-        print "Cannot connect"
+        print("Cannot connect")
         raise
-    
-    stomp.subscribe({'destination':queue,
-                     'ack':'client'})
+
+    stomp.subscribe({'destination': queue, 'ack': 'client'})
 
     if not num:
         while True:
             try:
                 frame = stomp.receive_frame()
                 stomp.ack(frame)
-                print frame.headers.get('message-id')
-                print frame.body
+                print(frame.headers.get('message-id'))
+                print(frame.body)
             except KeyboardInterrupt:
                 stomp.disconnect()
                 break
     else:
-        for i in xrange(0,num):
+        for i in xrange(0, num):
             try:
                 frame = stomp.receive_frame()
                 stomp.ack(frame)
-                print frame.headers.get('message-id')
-                print frame.body
+                print(frame.headers.get('message-id'))
+                print(frame.body)
             except KeyboardInterrupt:
                 stomp.disconnect()
                 break
         stomp.disconnect()
 
-def produce(host,port,queue,num=1000):
+
+def produce(host, port, queue, num=1000):
     try:
-        stomp = Stomp(host,port)
+        stomp = Stomp(host, port)
         stomp.connect()
     except:
-        print "Cannot connect"
+        print("Cannot connect")
         raise
-    for i in xrange(0,num):
-        print "Message #%d" % i
-        this_frame = stomp.send({'destination':queue,
-                                 'body':'Testing %d' % i,
-                                 'persistent':'true'})
-        print "Receipt: %s" % this_frame.headers.get('receipt-id')
+
+    for i in xrange(0, num):
+        print("Message #%d" % i)
+        this_frame = stomp.send({'destination': queue,
+                                 'body': 'Testing %d' % i,
+                                 'persistent': 'true'})
+        print("Receipt: %s" % this_frame.headers.get('receipt-id'))
 
     stomp.disconnect()
 
 if __name__ == '__main__':
     parser = OptionParser()
-    parser.add_option('-H', '--host', action='store', 
+    parser.add_option('-H', '--host', action='store',
                       type='string', dest='host', help='hostname')
-    parser.add_option('-p', '--port', action='store', 
+    parser.add_option('-p', '--port', action='store',
                       type='int', dest='port', help='port')
-    parser.add_option('-q', '--queue', action='store', 
+    parser.add_option('-q', '--queue', action='store',
                       type='string', dest='queue', help='destination queue')
-    parser.add_option('-P', '--produce', action='store_true', 
+    parser.add_option('-P', '--produce', action='store_true',
                       default=False, dest='produce', help='produce messages')
-    parser.add_option('-c', '--consume', action='store_true', 
+    parser.add_option('-c', '--consume', action='store_true',
                       default=False, dest='consume', help='consume messages')
     parser.add_option('-n', '--number', action='store',
-                      type='int', dest='number', 
+                      type='int', dest='number',
                       help='produce or consume NUMBER messages')
 
-    (options,args) = parser.parse_args()
+    options, args = parser.parse_args()
 
     if not options.host:
-        print "Host name is required!"
+        print("Host name is required!")
         parser.print_help()
-        raise SystemExit
+        sys.exit(1)
     if not options.port:
-        print "Port is required!"
+        print("Port is required!")
         parser.print_help()
-        raise SystemExit
+        sys.exit(1)
     if not options.queue:
-        print "Queue name is required!"
+        print("Queue name is required!")
         parser.print_help()
-        raise SystemExit
+        sys.exit(1)
 
     if options.produce:
-        if options.number:
-            produce(options.host,options.port,options.queue,options.number)
-        else:
-            produce(options.host,options.port,options.queue)
+        produce(options.host, options.port, options.queue, options.number)
     elif options.consume:
-        if options.number:
-            consume(options.host,options.port,options.queue,options.number)
-        else:
-            consume(options.host,options.port,options.queue)
+        consume(options.host, options.port, options.queue, options.number)
+[nosetests]
+verbosity = 1
+detailed-errors = 1
+with-coverage = 1
+match = ((?:^|[b_.-])(:?[Tt]est|When|should))
+cover-package = stompy
+cover-erase = True
 from setuptools import setup, find_packages
+from stompy import distmeta
 
-setup(name='stomp',
-      version='0.1',
-      description='Implementation of the STOMP protocol in Python.',
-      author='Benjamin W. Smith',
-      author_email='benjaminwarfield@just-another.net',
-      packages = ['stomp'],
+setup(name='stompy',
+      version=distmeta.__version__,
+      description=distmeta.__doc__,
+      author=distmeta.__author__,
+      author_email=distmeta.__contact__,
+      packages = ['stompy'],
       license='BSD',
-      url='http://just-another.net/python-stomp',
+      url=distmeta.__homepage__,
       keywords='stomp activemq jms messaging',
+      test_suite="nose.collector",
+      setup_requires=['nose>=0.11', 'dingus'],
       classifiers=["Development Status :: 2 - Pre-Alpha",
                    "Intended Audience :: Developers",
                    "License :: OSI Approved :: BSD License",

File stomp/__init__.py

-from stomp import Stomp

File stomp/frame.py

-#!/usr/bin/env python
-import socket
-import random
-from pprint import pformat
-
-class Frame(object):
-    """Build and manage a STOMP Frame.
-
-    This is useful for connecting to and communicating with
-    Apache ActiveMQ, an open source Java Message Service (JMS)
-    message broker.
-
-    For specifics on the protocol, see: http://stomp.codehaus.org/Protocol
-
-    This library is basically a Python implementation of Perl's Net::Stomp
-    See: http://search.cpan.org/dist/Net-Stomp/lib/Net/Stomp.pm
-
-    To enable the ActiveMQ Broker for Stomp add the following to the activemq.xml configuration:
-
-    <connector>
-        <serverTransport uri="stomp://localhost:61613"/>
-    </connector>
-
-    """
-    def __init__(self,sock=None, debug=False):
-        """Initialize Frame object
-        Passing a socket object is optional
-        >>> frameobj = Frame(socket)
-        """
-        self.command  = None
-        self.headers  = {}
-        self.body     = None
-        self.session  = None
-        self.my_name  = socket.gethostbyname(socket.gethostname())
-        self.sock     = sock
-        self.debug    = debug
-
-    def connect(self,sock, conf):
-        """Connect to the STOMP server, get session id
-        >>> frameobj.connect(sock)
-        """
-        self.sock = sock
-        if not conf:
-            conf = {}
-        frame = self.build_frame({'command':'CONNECT','headers':conf})
-        self.send_frame(frame.as_string())
-        self._set_session()
-
-    def _set_session(self):
-        this_frame = self.parse_frame()
-        self.session = this_frame.headers
-
-    def build_frame(self,args,want_receipt=False):
-        """Build a frame based on arguments
-        >>> frame = frameobj.build_frame(({'command':'CONNECT','headers':{}})
-        Optional argument to get receipt of message when planning to send.
-        >>> frame = frameobj.build_frame(({'command':'SEND',
-        ...                                'headers':headers,
-        ...                                'body':body},want_receipt=True)
-        """
-        self.command = args.get('command')
-        self.headers = args.get('headers')
-        self.body    = args.get('body')
-        if want_receipt:
-            receipt_stamp = str(random.randint(0,10000000))
-            self.headers['receipt'] = self.session.get('session') + "-" + receipt_stamp
-        return self
-
-    def as_string(self):
-        """Make raw string from frame
-        Suitable for passing over socket to STOMP server
-        >>> stomp.send(frameobj.as_string())
-        """
-        command = self.command
-        headers = self.headers
-        body    = self.body
-        frame   = "%s\n" % command
-        
-        headers['x_client'] = self.my_name
-
-        bytes_message = False
-        if 'bytes_message' in headers:
-            bytes_message = True
-            del headers['bytes_message']
-            headers['content-length'] = len(body)
-
-        if headers:
-            for k,v in headers.iteritems():
-                frame += "%s:%s\n" %(k,v)
-
-        frame += "\n%s\x00" % body
-        return frame
-
-    def parse_frame(self):
-        """Parse data from socket
-        >>> frameobj.parse_frame()
-        """
-        command = None 
-        body    = None
-        headers = {}
-
-        while True:
-            line = self._getline()
-            if self.debug:
-                print "Received: " +  '=' * 20
-                print line
-                print '-' * 20
-
-            command = self.parse_command(line)
-            line = line[len(command)+1:]
-            headers_str, body = line.split('\n\n')
-            headers = self.parse_headers(headers_str)
-
-            if 'content-length' in headers:
-                headers['bytes_message'] = True
-            break
-
-        frame = Frame(self.sock)
-        frame = frame.build_frame({'command':command,'headers':headers,'body':body})
-        return frame
-
-    def parse_command(self,str):
-        """Parse command, return
-        >>> frameobj.parse_command(str)
-        """
-        command = str.split('\n',1)[0] 
-        return command
-
-    def parse_headers(self,str):
-        """Parse headers, return
-        >>> frameobj.parse_headers(str)
-        """
-        headers = {}
-        for line in str.split('\n'):
-            (key,value) = line.split(':',1)
-            headers[key] = value
-        return headers
-
-    def send_frame(self,frame, debug=False):
-        """Send frame to server, get receipt if needed
-        >>> frameobj.send_frame(frame)
-        """
-        if debug:
-            print "Sending: " +  '=' * 20
-            print frame
-            print '-' * 20
-        self.sock.sendall(frame)
-
-        if 'receipt' in self.headers:
-            frame = self.parse_frame()
-            return frame
-        else:
-            return None 
-
-    def _getline(self):
-        """Get a single line from socket
-        >>> self._getline()
-        """
-        buffer = ''
-        while not buffer.endswith('\x00\n'):
-            buffer += self.sock.recv(1)
-        return buffer[:-2]
-
-    def __repr__(self):
-        return "<Frame %s>" % pformat(self.headers)

File stomp/stomp.py

-#!/usr/bin/env python
-
-import socket
-from frame import Frame
-
-class NotConnectedError(Exception):
-    """Raise if not connected"""
-    def __init__(self, value):
-        self.value = value
-    
-    def __str__(self):
-        return repr(self.value)
-
-class Stomp(object):
-    """Dead simple Python STOMP client library
-
-    This is useful for connecting to and communicating with
-    Apache ActiveMQ, an open source Java Message Service (JMS)
-    message broker.
-
-    The majority of the methods available take a single argument; a dictionary.
-    This dictionary should contain the necessary bits you need
-    to pass to the STOMP server.  It is outlined in each method
-    exactly what it needs to work.
-
-    For specifics on the protocol, see: http://stomp.codehaus.org/Protocol
-
-    This library is basically a Python implementation of Perl's Net::Stomp
-    See: http://search.cpan.org/dist/Net-Stomp/lib/Net/Stomp.pm
-
-    To enable the ActiveMQ Broker for Stomp add the following to the activemq.xml configuration:
-
-    <connector>
-        <serverTransport uri="stomp://localhost:61613"/>
-    </connector>
-    """
-    def __init__(self,hostname,port, debug=False):
-        """Initialize Stomp object
-        Also accepts arguments needed to build the TCP connection.
-        
-        >>> from stomp import Stomp