Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First cut at XML-RPC support for repoze.bfg.

git-svn-id: http://svn.repoze.org/repoze.bfg.xmlrpc/trunk@2437 8f1d8bf8-68d2-4fbe-a113-2afb08c80ed9
  • Loading branch information...
commit 46975e56df0fc90ec8cd43ce73e5147da92cee6a 0 parents
Chris McDonough chrism@agendaless.com authored
7 CHANGES.txt
@@ -0,0 +1,7 @@
+repoze.atemplate Changelog
+==========================
+
+0.1 (unreleaesd)
+----------------
+
+- Initial release.
3  COPYRIGHT.txt
@@ -0,0 +1,3 @@
+Copyright (c) 2008 Agendaless Consulting and Contributors.
+(http://www.agendaless.com), All Rights Reserved
+
41 LICENSE.txt
@@ -0,0 +1,41 @@
+License
+
+ A copyright notice accompanies this license document that identifies
+ the copyright holders.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ 1. Redistributions in source code must retain the accompanying
+ copyright notice, this list of conditions, and the following
+ disclaimer.
+
+ 2. Redistributions in binary form must reproduce the accompanying
+ copyright notice, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ 3. Names of the copyright holders must not be used to endorse or
+ promote products derived from this software without prior
+ written permission from the copyright holders.
+
+ 4. If any files are modified, you must cause the modified files to
+ carry prominent notices stating that you changed the files and
+ the date of any change.
+
+ Disclaimer
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
+ ANY EXPRESSED 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 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.
+
4 README.txt
@@ -0,0 +1,4 @@
+repoze.atemplate README
+=======================
+
+Please see docs/index.rst for the documentation.
4 TODO.txt
@@ -0,0 +1,4 @@
+repoze.atemplate README
+=======================
+
+- [_] TBD
BIN  docs/.static/logo_hi.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 docs/.static/repoze.css
@@ -0,0 +1,21 @@
+@import url('default.css');
+body {
+ background-color: #006339;
+}
+
+div.document {
+ background-color: #dad3bd;
+}
+
+div.sphinxsidebar h3,h4,h5,li,a {
+ color: #127c56 !important;
+}
+
+div.related {
+ color: #dad3bd;
+ background-color: #00744a;
+}
+
+div.related a {
+ color: #dad3bd;
+}
70 docs/Makefile
@@ -0,0 +1,70 @@
+# 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 (usable by e.g. sphinx-web)"
+ @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 or run"
+ @echo " sphinx-web .build/pickle"
+ @echo "to start the sphinx-web server."
+
+web: pickle
+
+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."
17 docs/api.rst
@@ -0,0 +1,17 @@
+.. _api:
+
+API Documentation for :mod:`repoze.bfg.xmlrpc`
+----------------------------------------------
+
+.. automodule:: repoze.bfg.xmlrpc
+
+ .. autofunction:: xmlrpc_view
+
+ .. autofunction:: xmlrpc_marshal
+
+ .. autofunction:: xmlrpc_response
+
+ .. autofunction:: parse_xmlrpc_request
+
+
+
185 docs/conf.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+#
+# repoze.atemplate documentation build configuration file
+#
+# 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 value; values that are commented
+# out serve to show the default value.
+
+import sys, 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.append(os.path.abspath('some/directory'))
+
+# 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 master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'repoze.atemplate'
+copyright = '2008, Repoze Developers <repoze-dev@lists.repoze.org>'
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.1'
+
+# 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 directories, that shouldn't be
+# searched for source files.
+#exclude_dirs = []
+
+# 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 = 'sphinx'
+
+
+# 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 = 'repoze.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 (within the static path) to place at the top of
+# the sidebar.
+html_logo = '.static/logo_hi.gif'
+
+# 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 = 'atemplatedoc'
+
+
+# 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', 'atemplate.tex', 'repoze.atemplate Documentation',
+ 'Repoze Developers', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the
+# top of the title page.
+latex_logo = '.static/logo_hi.gif'
+
+# 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
69 docs/index.rst
@@ -0,0 +1,69 @@
+Documentation for repoze.bfg.xmlrpc
+===================================
+
+XML-RPC support for the :mod:`repoze.bfg` web framework.
+
+:mod:`repoze.bfg.xmlrpc` Usage
+------------------------------
+
+Create a function in the form below. The function will be meant to be
+called with positional parameters from an XML-RPC request.
+
+.. code-block:: python
+ :linenos:
+
+ def say_hello(context, name):
+ return 'Hello, %s' % name
+
+Then add the ``@xmlrpc_view`` decorator to the function.
+
+.. code-block:: python
+ :linenos:
+
+ from repoze.bfg.xmlrpc import xmlrpc_view
+
+ @xmlrpc_view
+ def say_hello(context, name):
+ return 'Hello, %s' % name
+
+Then configure your application registry to point to the ``say_hello``
+view.
+
+.. code-block:: xml
+ :linenos:
+
+ <bfg:view
+ name="say_hello"
+ for="*"
+ />
+
+Then call the function via an XML-RPC client. Note that any XML-RPC
+``methodName`` will be ignored; you must point the client directly at
+the view URL; traversal doesn't work from there.
+
+.. code-block:: python
+ :linenos:
+
+ >>> from xmlrpclib import ServerProxy
+ >>> s = ServerProxy('http://localhost:6543/say_hello')
+ >>> s('Chris')
+ Hello, Chris
+
+.. toctree::
+ :maxdepth: 2
+
+ api.rst
+
+
+Topic A
+-------
+
+Explain topic.
+
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
272 ez_setup.py
@@ -0,0 +1,272 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from ez_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c8"
+DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+ 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+ 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+ 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+ 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+ 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+ 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+ 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+ 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+ 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+ 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+ 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+ 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+ 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+ 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+ 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+ 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+ 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+ 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+ 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+ 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+ 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+ 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+ 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+ 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+ 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+ 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+ 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+ 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+ 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+ 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+}
+
+import sys, os
+
+def _validate_md5(egg_name, data):
+ if egg_name in md5_data:
+ from md5 import md5
+ digest = md5(data).hexdigest()
+ if digest != md5_data[egg_name]:
+ print >>sys.stderr, (
+ "md5 validation of %s failed! (Possible download problem?)"
+ % egg_name
+ )
+ sys.exit(2)
+ return data
+
+
+def use_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ download_delay=15
+):
+ """Automatically find/download setuptools and make it available on sys.path
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end with
+ a '/'). `to_dir` is the directory where setuptools will be downloaded, if
+ it is not already available. If `download_delay` is specified, it should
+ be the number of seconds that will be paused before initiating a download,
+ should one be required. If an older version of setuptools is installed,
+ this routine will print a message to ``sys.stderr`` and raise SystemExit in
+ an attempt to abort the calling script.
+ """
+ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+ def do_download():
+ egg = download_setuptools(version, download_base, to_dir, download_delay)
+ sys.path.insert(0, egg)
+ import setuptools; setuptools.bootstrap_install_from = egg
+ try:
+ import pkg_resources
+ except ImportError:
+ return do_download()
+ try:
+ pkg_resources.require("setuptools>="+version); return
+ except pkg_resources.VersionConflict, e:
+ if was_imported:
+ print >>sys.stderr, (
+ "The required version of setuptools (>=%s) is not available, and\n"
+ "can't be installed while this script is running. Please install\n"
+ " a more recent version first, using 'easy_install -U setuptools'."
+ "\n\n(Currently using %r)"
+ ) % (version, e.args[0])
+ sys.exit(2)
+ else:
+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
+ return do_download()
+ except pkg_resources.DistributionNotFound:
+ return do_download()
+
+def download_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ delay = 15
+):
+ """Download setuptools from a specified location and return its filename
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download attempt.
+ """
+ import urllib2, shutil
+ egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+ url = download_base + egg_name
+ saveto = os.path.join(to_dir, egg_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ from distutils import log
+ if delay:
+ log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help). I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+ %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+ version, download_base, delay, url
+ ); from time import sleep; sleep(delay)
+ log.warn("Downloading %s", url)
+ src = urllib2.urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = _validate_md5(egg_name, src.read())
+ dst = open(saveto,"wb"); dst.write(data)
+ finally:
+ if src: src.close()
+ if dst: dst.close()
+ return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+ try:
+ import setuptools
+ except ImportError:
+ egg = None
+ try:
+ egg = download_setuptools(version, delay=0)
+ sys.path.insert(0,egg)
+ from setuptools.command.easy_install import main
+ return main(list(argv)+[egg]) # we're done here
+ finally:
+ if egg and os.path.exists(egg):
+ os.unlink(egg)
+ else:
+ if setuptools.__version__ == '0.0.1':
+ print >>sys.stderr, (
+ "You have an obsolete version of setuptools installed. Please\n"
+ "remove it from your system entirely before rerunning this script."
+ )
+ sys.exit(2)
+
+ req = "setuptools>="+version
+ import pkg_resources
+ try:
+ pkg_resources.require(req)
+ except pkg_resources.VersionConflict:
+ try:
+ from setuptools.command.easy_install import main
+ except ImportError:
+ from easy_install import main
+ main(list(argv)+[download_setuptools(delay=0)])
+ sys.exit(0) # try to force an exit
+ else:
+ if argv:
+ from setuptools.command.easy_install import main
+ main(argv)
+ else:
+ print "Setuptools version",version,"or greater has been installed."
+ print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+ """Update our built-in md5 registry"""
+
+ import re
+ from md5 import md5
+
+ for name in filenames:
+ base = os.path.basename(name)
+ f = open(name,'rb')
+ md5_data[base] = md5(f.read()).hexdigest()
+ f.close()
+
+ data = [" %r: %r,\n" % it for it in md5_data.items()]
+ data.sort()
+ repl = "".join(data)
+
+ import inspect
+ srcfile = inspect.getsourcefile(sys.modules[__name__])
+ f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+ match = re.search("\nmd5_data = {\n([^}]+)}", src)
+ if not match:
+ print >>sys.stderr, "Internal error!"
+ sys.exit(2)
+
+ src = src[:match.start(1)] + repl + src[match.end(1):]
+ f = open(srcfile,'w')
+ f.write(src)
+ f.close()
+
+
+if __name__=='__main__':
+ if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+ update_md5(sys.argv[2:])
+ else:
+ main(sys.argv[1:])
+
+
+
+
+
1  repoze/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
1  repoze/bfg/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
94 repoze/bfg/xmlrpc/__init__.py
@@ -0,0 +1,94 @@
+import xmlrpclib
+import webob
+
+def xmlrpc_marshal(data):
+ """ Marshal a Python data structure into an XML document suitable
+ for use as an XML-RPC response and return the document. If
+ ``data`` is an ``xmlrpclib.Fault`` instance, it will be marshalled
+ into a suitable XML-RPC fault response."""
+ if isinstance(data, xmlrpclib.Fault):
+ return xmlrpclib.dumps(data)
+ else:
+ return xmlrpclib.dumps((data,), methodresponse=True)
+
+def xmlrpc_response(data):
+ """ Marshal a Python data structure into a webob ``Response``
+ object with a body that is an XML document suitable for use as an
+ XML-RPC response with a content-type of ``text/xml`` and return
+ the response."""
+ xml = xmlrpc_marshal(data)
+ response = webob.Response(xml)
+ response.content_type = 'text/xml'
+ response.content_length = len(xml)
+ return response
+
+def parse_xmlrpc_request(request):
+ """ Deserialize the body of a request from an XML-RPC request
+ document into a set of params and return a two-tuple. The first
+ element in the tuple is the method params as a sequence, the
+ second element in the tuple is the method name."""
+ if request.content_length > (1 << 23):
+ # protect from DOS (> 8MB body)
+ raise ValueError('Body too large (%s bytes)' % request.content_length)
+ params, method = xmlrpclib.loads(request.body)
+ return params, method
+
+def xmlrpc_view(wrapped):
+ """ Function meant to be used as a decorator. The ``xmlrpc_view``
+ function turns functions which accept params and return Python
+ structures into functions suitable for use as bfg views which an
+ XML-RPC response. The decorated function must accept a
+ ``context`` argument and zero or more positional arguments
+ (conventionally named ``*params``).
+
+ E.g.::
+
+ from repoze.bfg.xmlrpc import xmlrpc_view
+
+ @xmlrpc_view
+ def say(context, what):
+ if what == 'hello'
+ return {'say':'Hello!'}
+ else:
+ return {'say':'Goodbye!'}
+
+ Equates to::
+
+ from repoze.bfg.xmlrpc import parse_xmlrpc_request
+ from repoze.bfg.xmlrpc import xmlrpc_response
+
+ def say_view(context, request):
+ params, method = parse_xmlrpc_request(request)
+ return say(context, *params)
+
+ def say(context, what):
+ if what == 'hello'
+ return {'say':'Hello!'}
+ else:
+ return {'say':'Goodbye!'}
+
+ Note that if you use ``repoze.bfg.convention``, you must decorate your
+ view function in the following order for it to be recognized by the
+ convention machinery as a view::
+
+ @bfg_view(name='say')
+ @xmlrpc_view
+ def say(context, what):
+ if what == 'hello'
+ return {'say':'Hello!'}
+ else:
+ return {'say':'Goodbye!'}
+
+ In other words do *not* decorate it in ``xmlrpc_view``, then
+ ``bfg_view order``; it won't work.
+ """
+
+ def _curried(context, request):
+ params, method = parse_xmlrpc_request(request)
+ value = wrapped(context, *params)
+ return xmlrpc_response(value)
+ _curried.__name__ = wrapped.__name__
+
+ return _curried
+
+
78 repoze/bfg/xmlrpc/tests.py
@@ -0,0 +1,78 @@
+import unittest
+from repoze.bfg import testing
+
+class TestXMLRPCMarshal(unittest.TestCase):
+ def _callFUT(self, value):
+ from repoze.bfg.xmlrpc import xmlrpc_marshal
+ return xmlrpc_marshal(value)
+
+ def test_xmlrpc_marshal_normal(self):
+ data = 1
+ marshalled = self._callFUT(data)
+ import xmlrpclib
+ self.assertEqual(marshalled, xmlrpclib.dumps((data,),
+ methodresponse=True))
+
+ def test_xmlrpc_marshal_fault(self):
+ import xmlrpclib
+ fault = xmlrpclib.Fault(1, 'foo')
+ data = self._callFUT(fault)
+ self.assertEqual(data, xmlrpclib.dumps(fault))
+
+class TestXMLRPResponse(unittest.TestCase):
+ def _callFUT(self, value):
+ from repoze.bfg.xmlrpc import xmlrpc_response
+ return xmlrpc_response(value)
+
+ def test_xmlrpc_response(self):
+ import xmlrpclib
+ data = 1
+ response = self._callFUT(data)
+ self.assertEqual(response.content_type, 'text/xml')
+ self.assertEqual(response.body, xmlrpclib.dumps((1,),
+ methodresponse=True))
+ self.assertEqual(response.content_length, len(response.body))
+ self.assertEqual(response.status, '200 OK')
+
+class TestParseXMLRPCRequest(unittest.TestCase):
+ def _callFUT(self, request):
+ from repoze.bfg.xmlrpc import parse_xmlrpc_request
+ return parse_xmlrpc_request(request)
+
+ def test_normal(self):
+ import xmlrpclib
+ param = 1
+ packet = xmlrpclib.dumps((param,), methodname='__call__')
+ request = testing.DummyRequest()
+ request.body = packet
+ request.content_length = len(packet)
+ params, method = self._callFUT(request)
+ self.assertEqual(params[0], param)
+ self.assertEqual(method, '__call__')
+
+ def test_toobig(self):
+ request = testing.DummyRequest()
+ request.content_length = 1 << 24
+ self.assertRaises(ValueError, self._callFUT, request)
+
+class TestXMLRPCView(unittest.TestCase):
+ def _callFUT(self, unwrapped):
+ from repoze.bfg.xmlrpc import xmlrpc_view
+ return xmlrpc_view(unwrapped)
+
+ def test_normal(self):
+ def unwrapped(context, what):
+ return what
+ wrapped = self._callFUT(unwrapped)
+ self.assertEqual(wrapped.__name__, 'unwrapped')
+ context = testing.DummyModel()
+ request = testing.DummyRequest()
+ param = 'what'
+ import xmlrpclib
+ packet = xmlrpclib.dumps((param,), methodname='__call__')
+ request = testing.DummyRequest()
+ request.body = packet
+ request.content_length = len(packet)
+ response = wrapped(context, request)
+ self.assertEqual(response.body, xmlrpclib.dumps((param,),
+ methodresponse=True))
3  setup.cfg
@@ -0,0 +1,3 @@
+[easy_install]
+index_url = http://dist.repoze.org/lemonade/dev/simple
+zip_ok = false
53 setup.py
@@ -0,0 +1,53 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
+# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+__version__ = '0.1'
+
+import os
+
+from ez_setup import use_setuptools
+use_setuptools()
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+README = open(os.path.join(here, 'README.txt')).read()
+CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+
+setup(name='repoze.bfg.xmlrpc',
+ version=__version__,
+ description='XML-RPC support for repoze.bfg',
+ long_description=README + '\n\n' + CHANGES,
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Intended Audience :: Developers",
+ "Programming Language :: Python",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+ "Topic :: Internet :: WWW/HTTP :: WSGI",
+ ],
+ keywords='web wsgi zope xml-rpc',
+ author="Agendaless Consulting",
+ author_email="repoze-dev@lists.repoze.org",
+ url="http://www.repoze.org",
+ license="BSD-derived (http://www.repoze.org/LICENSE.txt)",
+ packages=find_packages(),
+ include_package_data=True,
+ namespace_packages=['repoze', 'repoze.bfg'],
+ zip_safe=False,
+ tests_require = ['repoze.bfg'],
+ install_requires=['repoze.bfg'],
+ test_suite="repoze.bfg.xmlrpc",
+ )
+
Please sign in to comment.
Something went wrong with that request. Please try again.