summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore29
-rw-r--r--.gitreview4
-rw-r--r--.hgtags14
-rw-r--r--.zuul.yaml5
-rw-r--r--LICENSE19
-rw-r--r--README.md3
-rw-r--r--README.rst112
-rw-r--r--doc/Makefile134
-rw-r--r--doc/_static/toggle.css10
-rw-r--r--doc/_static/toggle.js9
-rw-r--r--doc/_static/wsme.css27
-rw-r--r--doc/api.rst45
-rw-r--r--doc/changes.rst439
-rw-r--r--doc/conf.py259
-rw-r--r--doc/document.rst190
-rw-r--r--doc/functions.rst204
-rw-r--r--doc/gettingstarted.rst15
-rw-r--r--doc/index.rst27
-rw-r--r--doc/integrate.rst342
-rw-r--r--doc/make.bat155
-rw-r--r--doc/protocols.rst366
-rw-r--r--doc/requirements.txt3
-rw-r--r--doc/todo.rst31
-rw-r--r--doc/types.rst246
-rw-r--r--examples/demo/client.py32
-rw-r--r--examples/demo/demo.py104
-rw-r--r--examples/demo/setup.cfg2
-rw-r--r--examples/demo/setup.py9
-rw-r--r--examples/demo/sporeclient.py18
-rw-r--r--requirements-py3.txt5
-rw-r--r--requirements.txt5
-rw-r--r--setup.cfg54
-rw-r--r--setup.py6
-rw-r--r--tests/pecantest/setup.cfg6
-rw-r--r--tests/pecantest/setup.py22
-rw-r--r--tests/pecantest/test/__init__.py0
-rw-r--r--tests/pecantest/test/app.py15
-rw-r--r--tests/pecantest/test/controllers/__init__.py0
-rw-r--r--tests/pecantest/test/controllers/root.py21
-rw-r--r--tests/pecantest/test/controllers/ws.py150
-rw-r--r--tests/pecantest/test/model/__init__.py2
-rw-r--r--tests/pecantest/test/tests/__init__.py22
-rw-r--r--tests/pecantest/test/tests/config.py24
-rw-r--r--tests/pecantest/test/tests/test_ws.py247
-rw-r--r--tests/rest/test_args.py20
-rw-r--r--tests/sphinxexample/conf.py232
-rw-r--r--tests/sphinxexample/document.rst43
-rw-r--r--tests/sphinxexample/index.rst3
-rw-r--r--tests/test_cornice.py183
-rw-r--r--tests/test_flask.py216
-rw-r--r--tests/test_sphinxext.py51
-rw-r--r--tests/test_tg1.py196
-rw-r--r--tests/test_tg15.py177
-rw-r--r--tox-tmpl.ini141
-rw-r--r--tox.ini406
-rw-r--r--toxgen.py144
-rw-r--r--wsme/__init__.py10
-rw-r--r--wsme/api.py237
-rw-r--r--wsme/exc.py92
-rw-r--r--wsme/protocol.py147
-rw-r--r--wsme/rest/__init__.py78
-rw-r--r--wsme/rest/args.py310
-rw-r--r--wsme/rest/json.py328
-rw-r--r--wsme/rest/protocol.py133
-rw-r--r--wsme/rest/xml.py298
-rw-r--r--wsme/root.py372
-rw-r--r--wsme/runtime.py9
-rw-r--r--wsme/spore.py64
-rw-r--r--wsme/tests/__init__.py0
-rw-r--r--wsme/tests/protocol.py720
-rw-r--r--wsme/tests/test_api.py419
-rw-r--r--wsme/tests/test_exc.py40
-rw-r--r--wsme/tests/test_protocols.py70
-rw-r--r--wsme/tests/test_protocols_commons.py159
-rw-r--r--wsme/tests/test_restjson.py779
-rw-r--r--wsme/tests/test_restxml.py211
-rw-r--r--wsme/tests/test_root.py122
-rw-r--r--wsme/tests/test_spore.py51
-rw-r--r--wsme/tests/test_types.py667
-rw-r--r--wsme/tests/test_utils.py97
-rw-r--r--wsme/types.py840
-rw-r--r--wsme/utils.py118
-rw-r--r--wsmeext/__init__.py2
-rw-r--r--wsmeext/cornice.py168
-rw-r--r--wsmeext/extdirect/__init__.py1
-rw-r--r--wsmeext/extdirect/datastore.py121
-rw-r--r--wsmeext/extdirect/protocol.py450
-rw-r--r--wsmeext/extdirect/sadatastore.py19
-rw-r--r--wsmeext/flask.py108
-rw-r--r--wsmeext/pecan.py142
-rw-r--r--wsmeext/soap/__init__.py5
-rw-r--r--wsmeext/soap/protocol.py478
-rw-r--r--wsmeext/soap/simplegeneric.py107
-rw-r--r--wsmeext/soap/wsdl.py297
-rw-r--r--wsmeext/sphinxext.py600
-rw-r--r--wsmeext/sqlalchemy/__init__.py0
-rw-r--r--wsmeext/sqlalchemy/controllers.py97
-rw-r--r--wsmeext/sqlalchemy/types.py200
-rw-r--r--wsmeext/tests/__init__.py0
-rw-r--r--wsmeext/tests/test_extdirect.py243
-rw-r--r--wsmeext/tests/test_soap.py423
-rw-r--r--wsmeext/tests/test_sqlalchemy_controllers.py223
-rw-r--r--wsmeext/tests/test_sqlalchemy_types.py72
-rw-r--r--wsmeext/tg1.py173
-rw-r--r--wsmeext/tg11.py40
-rw-r--r--wsmeext/tg15.py20
106 files changed, 3 insertions, 15601 deletions
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 726575b..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,29 +0,0 @@
-*.pyc
-*.pyo
-*.egg-info
-*.swp
-*.orig
-~*
-*~
-.coverage*
-coverage*.xml
-.noseids
-.tox
-.test_sphinxext
-nosetests*.xml
-
-build
-dist
-
-doc/_build
-d2to1-*.egg
-
-WSME.egg-info/
-
-# Files created by pbr
-AUTHORS
-ChangeLog
-pbr*.egg
-
-# Cross-test logs
-cross-test-*.log
diff --git a/.gitreview b/.gitreview
deleted file mode 100644
index f6182b4..0000000
--- a/.gitreview
+++ /dev/null
@@ -1,4 +0,0 @@
-[gerrit]
-host=review.openstack.org
-port=29418
-project=openstack/wsme.git
diff --git a/.hgtags b/.hgtags
deleted file mode 100644
index 081c1f7..0000000
--- a/.hgtags
+++ /dev/null
@@ -1,14 +0,0 @@
-2bd203a084dcc785257b35e7231b2021722f60de 0.1.0a1
-0eae00db9384d52cc4a82c09ab207d631ecb82e4 0.1.0a2
-86466da44f44a97b379c7b8e94c371526be0eb9f 0.1.0a3
-b38c56a2b9130d8fade7be22c8ac66a45fa77a6e 0.1.0a4
-b0019e486c807bafe412ebaa6eb9bd9ab656c81c 0.1.0
-c17de432c1857cfa059816d0db332bcdabea0c82 0.1.1
-cfb5efc624f55710c987c7795501f3dd44a01078 0.2.0
-ebe2c6f228ad4a365fbda9418f3e113d542390f0 0.3b1
-d5eab01bf49192df2e0f24c78ca4936073e45b19 0.3b2
-603c8586b076f5cf9b70b6cd82578dba7226e0c7 0.3
-5ad01afed8779bb5a384802a2ec7d6ed0186c7d5 0.4b1
-f06e004ca8e4013bf94df0cdade23b01742b0ec0 0.4
-359199eb4e0999b5920eadfa40038013cd360df6 0.5b1
-d3e5eee0b150048762169ff20ee25b43aa0369fa 0.5b2
diff --git a/.zuul.yaml b/.zuul.yaml
deleted file mode 100644
index f42aa59..0000000
--- a/.zuul.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-- project:
- templates:
- - openstack-python-jobs
- - openstack-python36-jobs
- - openstack-python37-jobs
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index d13cc4b..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-The MIT License (MIT)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..171f73a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# This repo has moved to OpenDev
+
+It can now be found at [https://opendev.org/x/wsme](https://opendev.org/x/wsme)
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 5b0e2a3..0000000
--- a/README.rst
+++ /dev/null
@@ -1,112 +0,0 @@
-Web Services Made Easy
-======================
-
-Introduction
-------------
-
-Web Services Made Easy (WSME) simplifies the writing of REST web services
-by providing simple yet powerful typing, removing the need to directly
-manipulate the request and the response objects.
-
-WSME can work standalone or on top of your favorite Python web
-(micro)framework, so you can use both your preferred way of routing your REST
-requests and most of the features of WSME that rely on the typing system like:
-
-- Alternate protocols, including those supporting batch-calls
-- Easy documentation through a Sphinx_ extension
-
-WSME is originally a rewrite of TGWebServices
-with a focus on extensibility, framework-independance and better type handling.
-
-How Easy ?
-~~~~~~~~~~
-
-Here is a standalone wsgi example::
-
- from wsme import WSRoot, expose
-
- class MyService(WSRoot):
- @expose(unicode, unicode) # First parameter is the return type,
- # then the function argument types
- def hello(self, who=u'World'):
- return u"Hello {0} !".format(who)
-
- ws = MyService(protocols=['restjson', 'restxml', 'soap'])
- application = ws.wsgiapp()
-
-With this published at the ``/ws`` path of your application, you can access
-your hello function in various protocols:
-
-.. list-table::
- :header-rows: 1
-
- * - URL
- - Returns
-
- * - ``http://<server>/ws/hello.json?who=you``
- - ``"Hello you !"``
-
- * - ``http://<server>/ws/hello.xml``
- - ``<result>Hello World !</result>``
-
- * - ``http://<server>/ws/api.wsdl``
- - A WSDL description for any SOAP client.
-
-
-Main features
-~~~~~~~~~~~~~
-
-- Very simple API.
-- Supports user-defined simple and complex types.
-- Multi-protocol : REST+Json, REST+XML, SOAP, ExtDirect and more to come.
-- Extensible : easy to add more protocols or more base types.
-- Framework independence : adapters are provided to easily integrate
- your API in any web framework, for example a wsgi container,
- Pecan_, TurboGears_, Flask_, cornice_...
-- Very few runtime dependencies: webob, simplegeneric. Optionnaly lxml and
- simplejson if you need better performances.
-- Integration in `Sphinx`_ for making clean documentation with
- ``wsmeext.sphinxext``.
-
-.. _Pecan: http://pecanpy.org/
-.. _TurboGears: http://www.turbogears.org/
-.. _Flask: http://flask.pocoo.org/
-.. _cornice: http://pypi.python.org/pypi/cornice
-
-Install
-~~~~~~~
-
-::
-
- pip install WSME
-
-or, if you do not have pip on your system or virtualenv
-
-::
-
- easy_install WSME
-
-Changes
-~~~~~~~
-
-- Read the `Changelog`_
-
-Getting Help
-~~~~~~~~~~~~
-
-- Read the `WSME Documentation`_.
-- Questions about WSME should go to the `python-wsme mailinglist`_.
-
-Contribute
-~~~~~~~~~~
-
-* Documentation: http://packages.python.org/WSME/
-* Source: http://git.openstack.org/cgit/openstack/wsme
-* Bugs: https://bugs.launchpad.net/wsme/+bugs
-* Code review: https://review.openstack.org/#/q/project:openstack/wsme,n,z
-
-.. _Changelog: http://packages.python.org/WSME/changes.html
-.. _python-wsme mailinglist: http://groups.google.com/group/python-wsme
-.. _WSME Documentation: http://packages.python.org/WSME/
-.. _WSME issue tracker: https://bugs.launchpad.net/wsme/+bugs
-.. _Sphinx: http://sphinx.pocoo.org/
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 8cf843c..0000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,134 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-ziphtml: html
- rm -f $(BUILDDIR)/wsme-documentation.zip
- cd $(BUILDDIR)/html && zip -r ../wsme-documentation.zip .
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/WebServicesMadeEasy.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/WebServicesMadeEasy.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/WebServicesMadeEasy"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/WebServicesMadeEasy"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- make -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/doc/_static/toggle.css b/doc/_static/toggle.css
deleted file mode 100644
index df87bdc..0000000
--- a/doc/_static/toggle.css
+++ /dev/null
@@ -1,10 +0,0 @@
-dl.toggle dt {
- background-color: #eeffcc;
- border: 1px solid #ac9;
- display: inline;
-}
-
-dl.toggle dd {
- display: none;
-}
-
diff --git a/doc/_static/toggle.js b/doc/_static/toggle.js
deleted file mode 100644
index 0c779ac..0000000
--- a/doc/_static/toggle.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*global $,document*/
-$(document).ready(function () {
- "use strict";
- $("dl.toggle > dt").click(
- function (event) {
- $(this).next().toggle(250);
- }
- );
-});
diff --git a/doc/_static/wsme.css b/doc/_static/wsme.css
deleted file mode 100644
index 09675f6..0000000
--- a/doc/_static/wsme.css
+++ /dev/null
@@ -1,27 +0,0 @@
-@import "agogo.css";
-
-table.docutils {
- margin: 0;
- padding: 0;
- border: 1;
-}
-
-table.docutils th {
- margin: 0;
- padding: 0;
- border: 0;
-}
-
-table.docutils thead tr {
-}
-
-table.docutils td {
- margin: 0;
- padding: 0;
- border: 0;
-}
-
-table.docutils tr.row-odd {
- background: #EEEEEC;
-}
-
diff --git a/doc/api.rst b/doc/api.rst
deleted file mode 100644
index dc61f98..0000000
--- a/doc/api.rst
+++ /dev/null
@@ -1,45 +0,0 @@
-API
-===
-
-Public API
-----------
-
-:mod:`wsme` -- Essentials
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsme
-
-.. autoclass:: signature([return_type, [arg0_type, [arg1_type, ... ] ] ], body=None, status_code=None)
-
-.. autoclass:: wsme.types.Base
-.. autoclass:: wsattr
-.. autoclass:: wsproperty
-
-.. data:: Unset
-
- Default value of the complex type attributes.
-
-.. autoclass:: WSRoot
- :members:
-
-Internals
----------
-
-:mod:`wsme.types` -- Types
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: wsme.types
- :members: register_type
-
-:mod:`wsme.api` -- API related api
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: wsme.api
- :members: FunctionArgument, FunctionDefinition
-
-:mod:`wsme.rest.args` -- REST protocol argument handling
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. automodule:: wsme.rest.args
- :members:
-
diff --git a/doc/changes.rst b/doc/changes.rst
deleted file mode 100644
index 4ee6432..0000000
--- a/doc/changes.rst
+++ /dev/null
@@ -1,439 +0,0 @@
-Changes
-=======
-
-0.9.4 (future)
---------------
-
-* SQLAlchemy support is deprecated and will be removed in one of the next
- releases. It has never actually worked to begin with.
-
-0.8.0 (2015-08-25)
-------------------
-
-Changes that may break your app:
-
-* Returns 400 if unexpected attributes are added to complex types (#1277571).
-
-Other changes:
-
-* Returns 415 when Content-Type is invalid (#1419110)
-* Returns 400 if a complex input type is not a json object (#1423634)
-* Fix error reports with ArrayType and DictType invalid inputs (#1428185, #1428628)
-* Update README
-
-0.7.0 (2015-05-13)
-------------------
-
-* Ensure UserType objects are converted to basetype
-* Convert built-in types when passed as strings
-* Multiple protocol accept or content-type matching
-* Raise an InvalidInput if you get a ValueError from JSON data
-* Remove unsupported python versions from setup.cfg
-* Clean up setup.py and add requirements.txt
-* Add full MIT license
-* Fix i18n when formatting exception
-* Cleanup up logging
-* Make it possible to use the Response to return a non-default return type
-* several fixes for SOAP protocol
-
-0.6.4 (2014-11-20)
-------------------
-
-- Include tests in the source distribution
-
-0.6.3 (2014-11-19)
-------------------
-
-- Disable universal wheels
-
-0.6.2 (2014-11-18)
-------------------
-
-* Flask adapter complex types now supports flask.ext.restful
-* Allow disabling complex types auto-register
-* Documentation edits
-* Various documentation build fixes
-* Fix passing Dict and Array based UserType as params
-
-0.6.1 (2014-05-02)
-------------------
-
-* Fix error: variable 'kw' referenced before assignment
-* Fix default handling for zero values
-* Fixing spelling mistakes
-* A proper check of UuidType
-* pecan: cleanup, use global vars and staticmethod
-* args_from_args() to work with an instance of UserType
-
-0.6 (2014-02-06)
-----------------
-
-* Add 'readonly' parameter to wsattr
-* Fix typos in documents and comments
-* Support dynamic types
-* Support building wheels (PEP-427)
-* Fix a typo in the types documentation
-* Add IntegerType and some classes for validation
-* Use assertRaises() for negative tests
-* Remove the duplicated error message from Enum
-* Drop description from 403 flask test case
-* Fix SyntaxWarning under Python 3
-
-0.5b6 (2013-10-16)
-------------------
-
-* Add improved support for HTTP response codes in cornice apps.
-
-* Handle mandatory attributes
-
-* Fix error code returned when None is used in an Enum
-
-* Handle list and dict for body type in REST protocol
-
-* Fix Sphinx for Python 3
-
-* Add custom error code to ClientSideError
-
-* Return a ClientSideError if unable to convert data
-
-* Validate body when using Pecan
-
-
-0.5b5 (2013-09-16)
-------------------
-
-More packaging fixes.
-
-0.5b4 (2013-09-11)
-------------------
-
-Fixes some release-related files for the stackforge release process.
-No user-facing bug fixes or features over what 0.5b3 provides.
-
-0.5b3 (2013-09-04)
-------------------
-
-The project moved to stackforge. Mind the new URLs for the repository, bug
-report etc (see the documentation).
-
-* Allow non-default status code return with the pecan adapter
- (Angus Salked).
-
-* Fix returning objects with object attributes set to None on rest-json
- & ExtDirect.
-
-* Allow error details to be set on the Response object (experimental !).
-
-* Fix: Content-Type header is not set anymore when the return type is None
- on the pecan adapter.
-
-* Support unicode message in ClientSideError (Mehdi Abaakouk).
-
-* Use pbr instead of d2to1 (Julien Danjou).
-
-* Python 3.3 support (Julien Danjou).
-
-* Pecan adapter: returned status can now be set on exceptions (Vitaly
- Kostenko).
-
-* TG adapters: returned status can be set on exceptions (Ryan
- Petrello).
-
-* six >= 1.4.0 support (Julien Danjou).
-
-* Require ordereddict from pypi for python < 2.6 (Ryan Petrello).
-
-* Make the code PEP8 compliant (Ryan Petrello).
-
-0.5b2 (2013-04-18)
-------------------
-
-* Changed the way datas of complex types are stored. In previous versions, an
- attribute was added to the type for each attribute, its name being the
- attribute name prefixed with '_'.
-
- Starting with this version, a single attribute _wsme_dataholder is added to
- the instance.
-
- The motivation behind this change is to avoid adding too many attributes to
- the object.
-
-* Add a special type 'HostRequest' that allow a function to ask for the host
- framework request object in its arguments.
-
-* Pecan adapter: Debug mode (which returns the exception tracebacks to the
- client) can be enabled by the pecan application configuration.
-
-* New adapter: wsmeext.flask, for the Flask_ framework.
-
-.. _Flask: http://flask.pocoo.org/
-
-* Fix: the cornice adapter was not usable.
-
-* Fix: Submodules of wsmeext were missing in the packages.
-
-* Fix: The demo app was still depending on the WSME-Soap package (which has
- been merged into WSME in 0.5b1).
-
-* Fix: A function with only on 'body' parameter would fail when being called.
-
-* Fix: Missing arguments were poorly reported by the frameworks adapters.
-
-0.5b1 (2013-01-30)
-------------------
-
-* Introduce a new kind of adapters that rely on the framework routing.
- Adapters are provided for Pecan, TurboGears and cornice.
-
-* Reorganised the rest protocol implementation to ease the implementation of
- adapters that rely only on the host framework routing system.
-
-* The default rest ``@expose`` decorator does not wrap the decorated function
- anymore. If needed to expose a same function several times, a parameter
- ``multiple_expose=True`` has been introduced.
-
-* Remove the wsme.release module
-
-* Fix == operator on ArrayType
-
-* Adapted the wsme.sphinxext module to work with the function exposed by the
- ``wsme.pecan`` adapter.
-
-* Allow promotion of ``int`` to ``float`` on float attributes (Doug Hellman)
-
-* Add a ``samples_slot`` option to the ``.. autotype`` directive to
- choose where the data samples whould be inserted (Doug Hellman).
-
-* Add ``sample()`` to ArrayType and DictType (Doug Hellman).
-
-* New syntax for object arrays as GET parameters, without brackets. Ex:
- ``?o.f1=a&o.f1=b&o.f2=c&o.f2=d`` is an array of two objects:
- [{'f1': 'a', 'f2': 'c']}, {'f1': 'b', 'f2': 'd']}.
-
-* @signature (and its @wsexpose frontends) has a new parameter:
- ``ignore_extra_args``.
-
-* Fix boolean as input type support in the soap implementation (Craig
- McDaniel).
-
-* Fix empty/nil strings distinction in soap (Craig McDaniel).
-
-* Improved unittests code coverage.
-
-* Ported the soap implementation to python 3.
-
-* Moved non-core features (adapters, sphinx extension) to the ``wsmeext`` module.
-
-* Change the GET parameter name for passing the request body as a parameter
- is now from 'body' to '__body__'
-
-* The soap, extdirect and sqlalchemy packages have been merged into the main
- package.
-
-* Changed the documentation theme to "Cloud".
-
-0.4 (2012-10-15)
-----------------
-
-* Automatically converts unicode strings to/from ascii bytes.
-
-* Use d2to1 to simplify setup.py.
-
-* Implements the SPORE specification.
-
-* Fixed a few things in the documentation
-
-0.4b1 (2012-09-14)
-------------------
-
-* Now supports Python 3.2
-
-* String types handling is clearer.
-
-* New :class:`wsme.types.File` type.
-
-* Supports cross-referenced types.
-
-* Various bugfixes.
-
-* Tests code coverage is now over 95%.
-
-* RESTful protocol can now use the http method.
-
-* UserTypes can now be given a name that will be used in the
- documentation.
-
-* Complex types can inherit :class:`wsme.types.Base`. They will
- have a default constructor and be registered automatically.
-
-* Removed the wsme.wsgi.adapt function if favor of
- :meth:`wsme.WSRoot.wsgiapp`
-
-Extensions
-~~~~~~~~~~
-
-wsme-soap
- * Function names now starts with a lowercase letter.
-
- * Fixed issues with arrays (issue #3).
-
- * Fixed empty array handling.
-
-
-wsme-sqlalchemy
- This new extension makes it easy to create webservices on top
- of a SQLAlchemy set of mapped classes.
-
-wsme-extdirect
- * Implements server-side DataStore
- (:class:`wsmeext.extdirect.datastore.DataStoreController`).
-
- * Add Store and Model javascript definition auto-generation
-
- * Add Store server-side based on SQLAlchemy mapped classes
- (:class:`wsmeext.extdirect.sadatastore.SADataStoreController`).
-
-0.3 (2012-04-20)
-----------------
-
-* Initial Sphinx integration.
-
-0.3b2 (2012-03-29)
-------------------
-
-* Fixed issues with the TG1 adapter.
-
-* Now handle dict and UserType types as GET/POST params.
-
-* Better handling of application/x-www-form-urlencoded encoded POSTs
- in rest protocols.
-
-* :class:`wsattr` now takes a 'default' parameter that will be returned
- instead of 'Unset' if no value has been set.
-
-0.3b1 (2012-01-19)
-------------------
-
-* Per-call database transaction handling.
-
-* :class:`Unset` is now imported in the wsme module
-
-* Attributes of complex types can now have a different name in
- the public api and in the implementation.
-
-* Complex arguments can now be sent as GET/POST params in the rest
- protocols.
-
-* The restjson protocol do not nest the results in an object anymore.
-
-* Improved the documentation
-
-* Fix array attributes validation.
-
-* Fix date|time parsing errors.
-
-* Fix Unset values validation.
-
-* Fix registering of complex types inheriting form already
- registered complex types.
-
-* Fix user types, str and None values encoding/decoding.
-
-0.2.0 (2011-10-29)
-------------------
-
-* Added batch-calls abilities.
-
-* Introduce a :class:`UnsetType` and a :data:`Unset` constant
- so that non-mandatory attributes can remain unset (which is
- different from null).
-
-* Fix: If a complex type was only used as an input type, it was
- not registered.
-
-* Add support for user types.
-
-* Add an Enum type (which is a user type).
-
-* The 'binary' type is now a user type.
-
-* Complex types:
-
- - Fix inspection of complex types with inheritance.
-
- - Fix inspection of self-referencing complex types.
-
- - wsattr is now a python Descriptor, which makes it possible
- to retrieve the attribute definition on a class while
- manipulating values on the instance.
-
- - Add strong type validation on assignment (made possible by
- the use of Descriptors).
-
-* ExtDirect:
-
- - Implements batch calls
-
- - Fix None values conversion
-
- - Fix transaction result : 'action' and 'method' were missing.
-
-0.1.1 (2011-10-20)
-------------------
-
-* Changed the internal API by introducing a CallContext object.
- It makes it easier to implement some protocols that have
- a transaction or call id that has to be returned. It will also
- make it possible to implement batch-calls in a later version.
-
-* More test coverage.
-
-* Fix a problem with array attribute types not being registered.
-
-* Fix the mandatory / default detection on function arguments.
-
-* Fix issues with the SOAP protocol implementation which should now
- work properly with a suds client.
-
-* Fix issues with the ExtDirect protocol implementation.
-
-0.1.0 (2011-10-14)
-------------------
-
-* Protocol insertion order now influence the protocol selection
-
-* Move the soap protocol implementation in a separate lib,
- WSME-Soap
-
-* Introduce a new protocol ExtDirect in the WSME-ExtDirect lib.
-
-0.1.0a4 (2011-10-12)
---------------------
-
-* Change the way framework adapters works. Now the adapter modules
- have a simple adapt function that adapt a :class:`wsme.WSRoot`
- instance. This way a same root can be integrated in several
- framework.
-
-* Protocol lookup now use entry points in the group ``[wsme.protocols]``.
-
-0.1.0a3 (2011-10-11)
---------------------
-
-* Add specialised WSRoot classes for easy integration as a
- WSGI Application (:class:`wsme.wsgi.WSRoot`) or a
- TurboGears 1.x controller (:class:`wsme.tg1.WSRoot`).
-
-* Improve the documentation.
-
-* More unit tests and code-coverage.
-
-0.1.0a2 (2011-10-07)
---------------------
-
-* Added support for arrays in all the protocols
-
-0.1.0a1 (2011-10-04)
---------------------
-
-Initial public release.
diff --git a/doc/conf.py b/doc/conf.py
deleted file mode 100644
index c4b5696..0000000
--- a/doc/conf.py
+++ /dev/null
@@ -1,259 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Web Services Made Easy documentation build configuration file, created by
-# sphinx-quickstart on Sun Oct 2 20:27:45 2011.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path 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, os.path.abspath('..'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# 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', 'sphinx.ext.viewcode', 'wsmeext.sphinxext',
- 'sphinx.ext.intersphinx']
-
-# 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-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Web Services Made Easy'
-copyright = u'2011, Christophe de Vienne'
-
-# 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.
-#
-import pkg_resources
-wsmedist = pkg_resources.require('WSME')[0]
-version = wsmedist.version
-
-# The short X.Y version.
-version = '.'.join(version.split('.')[:2])
-# The full version, including alpha/beta/rc tags.
-release = 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 patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_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 = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-#html_theme = 'agogo'
-#html_theme_options = {
-# "pagewidth": "60em",
-# "documentwidth": "40em",
-#}
-
-#html_style = 'wsme.css'
-
-import cloud_sptheme as csp
-
-html_theme = 'cloud'
-html_theme_path = [csp.get_theme_dir()]
-
-html_theme_options = {
- "roottarget": "index",
- "googleanalytics_id": "UA-8510502-6"
-}
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-html_title = "WSME %s" % release
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = "WSME"
-
-# 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_domain_indices = 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, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = 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 = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'WebServicesMadeEasydoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-latex_paper_size = 'a4'
-
-# 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, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'WebServicesMadeEasy.tex', u'Web Services Made Easy Documentation',
- u'Christophe de Vienne', '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
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-latex_preamble = '''
-\usepackage[T2A]{fontenc}
-\usepackage[utf8]{inputenc}
-'''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'webservicesmadeeasy', u'Web Services Made Easy Documentation',
- [u'Christophe de Vienne'], 1)
-]
-
-
-autodoc_member_order = 'bysource'
-
-wsme_protocols = [
- 'restjson', 'restxml', 'soap', 'extdirect'
-]
-
-intersphinx_mapping = {
- 'python': ('http://docs.python.org/', None),
- 'six': ('http://packages.python.org/six/', None),
-}
-
-
-def setup(app):
- # confval directive taken from the sphinx doc
- app.add_object_type('confval', 'confval',
- objname='configuration value',
- indextemplate='pair: %s; configuration value')
diff --git a/doc/document.rst b/doc/document.rst
deleted file mode 100644
index 39a54f2..0000000
--- a/doc/document.rst
+++ /dev/null
@@ -1,190 +0,0 @@
-Document your API
-=================
-
-Web services without a proper documentation are usually useless.
-
-To make it easy to document your own API, WSME provides a Sphinx_ extension.
-
-Install the extension
----------------------
-
-Here we consider that you already quick-started a sphinx project.
-
-#. In your ``conf.py`` file, add ``'ext'`` to your extensions,
- and optionally set the enabled protocols.
-
- .. code-block:: python
-
- extensions = ['ext']
-
- wsme_protocols = ['restjson', 'restxml', 'extdirect']
-
-#. Copy :download:`toggle.js <_static/toggle.js>`
- and :download:`toggle.css <_static/toggle.css>`
- in your _static directory.
-
-The ``wsme`` domain
--------------------
-
-The extension will add a new Sphinx domain providing a few directives.
-
-Config values
-~~~~~~~~~~~~~
-
-.. confval:: wsme_protocols
-
- A list of strings that are WSME protocol names. If provided by an
- additional package (for example WSME-Soap or WSME-ExtDirect), that package must
- be installed.
-
- The types and services generated documentation will include code samples
- for each of these protocols.
-
-.. confval:: wsme_root
-
- A string that is the full name of the service root controller.
- It will be used
- to determinate the relative path of the other controllers when they
- are autodocumented, and calculate the complete webpath of the other
- controllers.
-
-.. confval:: wsme_webpath
-
- A string that is the webpath where the :confval:`wsme_root` is mounted.
-
-Directives
-~~~~~~~~~~
-
-.. rst:directive:: .. root:: <WSRoot full path>
-
- Define the service root controller for this documentation source file.
- To set it globally, see :confval:`wsme_root`.
-
- A ``webpath`` option allows override of :confval:`wsme_webpath`.
-
- Example:
-
- .. code-block:: rst
-
- .. wsme:root:: myapp.controllers.MyWSRoot
- :webpath: /api
-
-.. rst:directive:: .. service:: name/space/ServiceName
-
- Declare a service.
-
-.. rst:directive:: .. type:: MyComplexType
-
- Equivalent to the :rst:dir:`py:class` directive to document a complex type
-
-.. rst:directive:: .. attribute:: aname
-
- Equivalent to the :rst:dir:`py:attribute` directive to document a complex type
- attribute. It takes an additional ``:type:`` field.
-
-Example
-~~~~~~~
-
-.. list-table::
- :header-rows: 1
-
- * - Source
- - Result
-
- * - .. code-block:: rst
-
- .. wsme:root:: wsmeext.sphinxext.SampleService
- :webpath: /api
-
- .. wsme:type:: MyType
-
- .. wsme:attribute:: test
-
- :type: int
-
- .. wsme:service:: name/space/SampleService
-
- .. wsme:function:: doit
-
- - .. wsme:root:: wsmeext.sphinxext.SampleService
- :webpath: /api
-
- .. wsme:type:: MyType
-
- .. wsme:attribute:: test
-
- :type: int
-
- .. wsme:service:: name/space/SampleService
-
- .. wsme:function:: getType
-
- Returns a :wsme:type:`MyType <MyType>`
-
-
-Autodoc directives
-~~~~~~~~~~~~~~~~~~
-
-Theses directives scan your code to generate the documentation from the
-docstrings and your API types and controllers.
-
-.. rst:directive:: .. autotype:: myapp.MyType
-
- Generate the myapp.MyType documentation.
-
-.. rst:directive:: .. autoattribute:: myapp.MyType.aname
-
- Generate the myapp.MyType.aname documentation.
-
-.. rst:directive:: .. autoservice:: myapp.MyService
-
- Generate the myapp.MyService documentation.
-
-.. rst:directive:: .. autofunction:: myapp.MyService.myfunction
-
- Generate the myapp.MyService.myfunction documentation.
-
-Full Example
-------------
-
-Python source
-~~~~~~~~~~~~~
-
-.. literalinclude:: ../wsmeext/sphinxext.py
- :lines: 69-96
- :language: python
-
-Documentation source
-~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: rst
-
- .. default-domain:: wsmeext
-
- .. type:: int
-
- An integer
-
- .. autotype:: wsmeext.sphinxext.SampleType
- :members:
-
- .. autoservice:: wsmeext.sphinxext.SampleService
- :members:
-
-Result
-~~~~~~
-
-.. default-domain:: wsmeext
-
-.. type:: int
-
- An integer
-
-.. autotype:: wsmeext.sphinxext.SampleType
- :members:
-
-.. autoservice:: wsmeext.sphinxext.SampleService
- :members:
-
-
-.. _Sphinx: http://sphinx.pocoo.org/
diff --git a/doc/functions.rst b/doc/functions.rst
deleted file mode 100644
index da56d25..0000000
--- a/doc/functions.rst
+++ /dev/null
@@ -1,204 +0,0 @@
-Functions
-=========
-
-WSME is based on the idea that most of the time the input and output of web
-services are actually strictly typed. It uses this idea to ease the
-implementation of the actual functions by handling those input/output.
-It also proposes alternate protocols on top of a proper REST api.
-
-This chapter explains in detail how to 'sign' a function with WSME.
-
-The decorators
---------------
-
-Depending on the framework you are using, you will have to use either a
-@\ :class:`wsme.signature` decorator or a @\ :class:`wsme.wsexpose` decorator.
-
-@signature
-~~~~~~~~~~
-
-The base @\ :class:`wsme.signature` decorator defines the return and argument types
-of the function, and if needed a few more options.
-
-The Flask and Cornice adapters both propose a specific version of it, which
-also wrap the function so that it becomes suitable for the host framework.
-
-In any case, the use of @\ :class:`wsme.signature` has the same meaning: tell WSME what is the
-signature of the function.
-
-@wsexpose
-~~~~~~~~~
-
-The native Rest implementation, and the TG and Pecan adapters add a @\ :class:`wsme.wsexpose`
-decorator.
-
-It does what @\ :class:`wsme.signature` does, *and* exposes the function in the routing system
-of the host framework.
-
-This decorator is generally used in an object-dispatch routing context.
-
-.. note::
-
- Since both decorators play the same role, the rest of this
- document will alway use @signature.
-
-Signing a function
-------------------
-
-Signing a function is just a matter of decorating it with @signature:
-
-.. code-block:: python
-
- @signature(int, int, int)
- def multiply(a, b):
- return a * b
-
-In this trivial example, we tell WSME that the 'multiply' function returns an
-integer, and takes two integer parameters.
-
-WSME will match the argument types by order to determine the exact type of each
-named argument. This is important since most of the web service protocols don't
-provide strict argument ordering but only named parameters.
-
-Optional arguments
-~~~~~~~~~~~~~~~~~~
-
-Defining an argument as optional is done by providing a default value:
-
-.. code-block:: python
-
- @signature(int, int, int):
- def increment(value, delta=1):
- return value + delta
-
-In this example, the caller may omit the 'delta' argument, and no
-'MissingArgument' error will be raised.
-
-Additionally, this argument will be documented as optional by the sphinx
-extension.
-
-Body argument
-~~~~~~~~~~~~~
-
-When defining a Rest CRUD API, we generally have a URL to which we POST data.
-
-For example:
-
-.. code-block:: python
-
- @signature(Author, Author)
- def update_author(data):
- # ...
- return data
-
-Such a function will take at least one parameter, 'data', that is a structured
-type. With the default way of handling parameters, the body of the request
-would look like this:
-
-.. code-block:: javascript
-
- {
- "data":
- {
- "id": 1,
- "name": "Pierre-Joseph"
- }
- }
-
-If you think (and you should) that it has one extra level of nesting, the 'body'
-argument is here for you::
-
- @signature(Author, body=Author)
- def update_author(data):
- # ...
- return data
-
-With this syntax, we can now post a simpler body:
-
-.. code-block:: javascript
-
- {
- "id": 1,
- "name": "Pierre-Joseph"
- }
-
-Note that this does not prevent the function from having multiple parameters; it just requires
-the body argument to be the last:
-
-.. code-block:: python
-
- @signature(Author, bool, body=Author)
- def update_author(force_update=False, data=None):
- # ...
- return data
-
-In this case, the other arguments can be passed in the URL, in addition to the
-body parameter. For example, a POST on ``/author/SOMEID?force_update=true``.
-
-Status code
-~~~~~~~~~~~
-
-The default status codes returned by WSME are 200, 400 (if the client sends invalid
-inputs) and 500 (for server-side errors).
-
-Since a proper Rest API should use different return codes (201, etc), one can
-use the 'status_code=' option of @signature to do so.
-
-.. code-block:: python
-
- @signature(Author, body=Author, status_code=201)
- def create_author(data):
- # ...
- return data
-
-Of course this code will only be used if no error occurs.
-
-In case the function needs to change the status code on a per-request basis, it
-can return a :class:`wsme.Response` object, allowing it to override the status
-code:
-
-.. code-block:: python
-
- @signature(Author, body=Author, status_code=202)
- def update_author(data):
- # ...
- response = Response(data)
- if transaction_finished_and_successful:
- response.status_code = 200
- return response
-
-Extra arguments
-~~~~~~~~~~~~~~~
-
-The default behavior of WSME is to reject requests that give extra/unknown
-arguments. In some (rare) cases, this is undesirable.
-
-Adding 'ignore_extra_args=True' to @signature changes this behavior.
-
-.. note::
-
- If using this option seems to solve your problem, please think twice
- before using it!
-
-Accessing the request
-~~~~~~~~~~~~~~~~~~~~~
-
-Most of the time direct access to the request object should not be needed, but
-in some cases it is.
-
-On frameworks that propose a global access to the current request it is not an
-issue, but on frameworks like pyramid it is not the way to go.
-
-To handle this use case, WSME has a special type, :class:`HostRequest`:
-
-.. code-block:: python
-
- from wsme.types import HostRequest
-
- @signature(Author, HostRequest, body=Author)
- def create_author(request, newauthor):
- # ...
- return newauthor
-
-In this example, the request object of the host framework will be passed as the
-``request`` parameter of the create_author function.
diff --git a/doc/gettingstarted.rst b/doc/gettingstarted.rst
deleted file mode 100644
index 7a35600..0000000
--- a/doc/gettingstarted.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-Getting Started
-===============
-
-For now here is just a working example.
-You can find it in the examples directory of the source distribution.
-
-.. literalinclude:: ../examples/demo/demo.py
- :language: python
-
-When running this example, the following soap client can interrogate
-the web services:
-
-.. literalinclude:: ../examples/demo/client.py
- :language: python
-
diff --git a/doc/index.rst b/doc/index.rst
deleted file mode 100644
index 7819d57..0000000
--- a/doc/index.rst
+++ /dev/null
@@ -1,27 +0,0 @@
-
-.. include:: ../README.rst
-
-Contents
---------
-
-.. toctree::
- :maxdepth: 2
-
- gettingstarted
- api
- types
- functions
- protocols
- integrate
- document
-
- todo
- changes
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/doc/integrate.rst b/doc/integrate.rst
deleted file mode 100644
index 5adaaca..0000000
--- a/doc/integrate.rst
+++ /dev/null
@@ -1,342 +0,0 @@
-Integrating with a Framework
-============================
-
-General considerations
-----------------------
-
-Using WSME within another framework providing its own REST capabilities is
-generally done by using a specific decorator to declare the function signature,
-in addition to the framework's own way of declaring exposed functions.
-
-This decorator can have two different names depending on the adapter.
-
-``@wsexpose``
- This decorator will declare the function signature *and*
- take care of calling the adequate decorators of the framework.
-
- Generally this decorator is provided for frameworks that use
- object-dispatch controllers, such as :ref:`adapter-pecan` and
- :ref:`adapter-tg1`.
-
-``@signature``
- This decorator only sets the function signature and returns a function
- that can be used by the host framework as a REST request target.
-
- Generally this decorator is provided for frameworks that expect functions
- taking a request object as a single parameter and returning a response
- object. This is the case for :ref:`adapter-cornice` and
- :ref:`adapter-flask`.
-
-If you want to enable additional protocols, you will need to
-mount a :class:`WSRoot` instance somewhere in the application, generally
-``/ws``. This subpath will then handle the additional protocols. In a future
-version, a WSGI middleware will probably play this role.
-
-.. note::
-
- Not all the adapters are at the same level of maturity.
-
-WSGI Application
-----------------
-
-The :func:`wsme.WSRoot.wsgiapp` function of WSRoot returns a WSGI
-application.
-
-Example
-~~~~~~~
-
-The following example assumes the REST protocol will be entirely handled by
-WSME, which is the case if you write a WSME standalone application.
-
-.. code-block:: python
-
- from wsme import WSRoot, expose
-
-
- class MyRoot(WSRoot):
- @expose(unicode)
- def helloworld(self):
- return u"Hello World !"
-
- root = MyRoot(protocols=['restjson'])
- application = root.wsgiapp()
-
-
-.. _adapter-cornice:
-
-Cornice
--------
-
-.. _cornice: http://cornice.readthedocs.org/en/latest/
-
- *"* Cornice_ *provides helpers to build & document REST-ish Web Services with
- Pyramid, with decent default behaviors. It takes care of following the HTTP
- specification in an automated way where possible."*
-
-
-:mod:`wsmeext.cornice` -- Cornice adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsmeext.cornice
-
-.. function:: signature
-
- Declare the parameters of a function and returns a function suitable for
- cornice (ie that takes a request and returns a response).
-
-Configuration
-~~~~~~~~~~~~~
-
-To use WSME with Cornice you have to add a configuration option to your Pyramid application.
-
-.. code-block:: python
-
- from pyramid.config import Configurator
-
-
- def make_app():
- config = Configurator()
- config.include("cornice")
- config.include("wsmeext.cornice") # This includes WSME cornice support
- # ...
- return config.make_wsgi_app()
-
-Example
-~~~~~~~
-
-.. code-block:: python
-
- from cornice import Service
- from wsmeext.cornice import signature
- import wsme.types
-
- hello = Service(name='hello', path='/', description="Simplest app")
-
- class Info(wsme.types.Base):
- message = wsme.types.text
-
-
- @hello.get()
- @signature(Info)
- def get_info():
- """Returns Hello in JSON or XML."""
- return Info(message='Hello World')
-
-
- @hello.post()
- @signature(None, Info)
- def set_info(info):
- print("Got a message: %s" % info.message)
-
-
-.. _adapter-flask:
-
-Flask
------
-
- *"Flask is a microframework for Python based on Werkzeug, Jinja 2 and good
- intentions. And before you ask: It's BSD licensed! "*
-
-
-.. warning::
-
- Flask support is limited to function signature handling. It does not
- support additional protocols. This is a temporary limitation, if you have
- needs on that matter please tell us at python-wsme@googlegroups.com.
-
-
-:mod:`wsmeext.flask` -- Flask adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsmeext.flask
-
-.. function:: signature(return_type, \*arg_types, \*\*options)
-
- See @\ :func:`signature` for parameters documentation.
-
- Can be used on a function before routing it with flask.
-
-Example
-~~~~~~~
-
-.. code-block:: python
-
- from wsmeext.flask import signature
-
- @app.route('/multiply')
- @signature(int, int, int)
- def multiply(a, b):
- return a * b
-
-.. _adapter-pecan:
-
-Pecan
------
-
- *"*\ Pecan_ *was created to fill a void in the Python web-framework world –
- a very lightweight framework that provides object-dispatch style routing.
- Pecan does not aim to be a "full stack" framework, and therefore includes
- no out of the box support for things like sessions or databases. Pecan
- instead focuses on HTTP itself."*
-
-.. warning::
-
- A pecan application is not able to mount another WSGI application on a
- subpath. For that reason, additional protocols are not supported for now,
- until WSME provides a middleware that can do the same as a mounted
- WSRoot.
-
-:mod:`wsmeext.pecan` -- Pecan adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsmeext.pecan
-
-.. function:: wsexpose(return_type, \*arg_types, \*\*options)
-
- See @\ :func:`signature` for parameters documentation.
-
- Can be used on any function of a pecan
- `RestController <http://pecan.readthedocs.org/en/latest/rest.html>`_
- instead of the expose decorator from Pecan.
-
-Configuration
-~~~~~~~~~~~~~
-
-WSME can be configured through the application configation, by adding a 'wsme'
-configuration entry in ``config.py``:
-
-.. code-block:: python
-
- wsme = {
- 'debug': True
- }
-
-Valid configuration variables are :
-
-- ``'debug'``: Whether or not to include exception tracebacks in the returned
- server-side errors.
-
-Example
-~~~~~~~
-
-The `example <http://pecan.readthedocs.org/en/latest/rest.html#nesting-restcontroller>`_ from the Pecan documentation becomes:
-
-.. code-block:: python
-
- from wsmeext.pecan import wsexpose
-
- class BooksController(RestController):
- @wsexpose(Book, int, int)
- def get(self, author_id, id):
- # ..
-
- @wsexpose(Book, int, int, body=Book)
- def put(self, author_id, id, book):
- # ..
-
- class AuthorsController(RestController):
- books = BooksController()
-
-.. _Pecan: http://pecanpy.org/
-
-.. _adapter-tg1:
-
-Turbogears 1.x
---------------
-
-The TG adapters have an api very similar to TGWebServices. Migrating from it
-should be straightforward (a little howto migrate would not hurt though, and it
-will be written as soon as possible).
-
-:mod:`wsmeext.tg11` -- TG 1.1 adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsmeext.tg11
-
-.. function:: wsexpose(return_type, \*arg_types, \*\*options)
-
- See @\ :func:`signature` for parameters documentation.
-
- Can be used on any function of a controller
- instead of the expose decorator from TG.
-
-.. function:: wsvalidate(\*arg_types)
-
- Set the argument types of an exposed function. This decorator is provided
- so that WSME is an almost drop-in replacement for TGWebServices. If
- starting from scratch you can use \ :func:`wsexpose` only
-
-.. function:: adapt(wsroot)
-
- Returns a TG1 controller instance that publish a :class:`wsme.WSRoot`.
- It can then be mounted on a TG1 controller.
-
- Because the adapt function modifies the cherrypy filters of the controller
- the 'webpath' of the WSRoot instance must be consistent with the path it
- will be mounted on.
-
-:mod:`wsmeext.tg15` -- TG 1.5 adapter
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. module:: wsmeext.tg15
-
-This adapter has the exact same api as :mod:`wsmeext.tg11`.
-
-Example
-~~~~~~~
-
-In a freshly quickstarted tg1 application (let's say, wsmedemo), you can add
-REST-ish functions anywhere in your controller tree. Here directly on the root,
-in controllers.py:
-
-.. code-block:: python
-
- # ...
-
- # For tg 1.5, import from wsmeext.tg15 instead :
- from wsmeext.tg11 import wsexpose, WSRoot
-
- class Root(controllers.RootController):
- # Having a WSRoot on /ws is only required to enable additional
- # protocols. For REST-only services, it can be ignored.
- ws = adapt(
- WSRoot(webpath='/ws', protocols=['soap'])
- )
-
- @wsexpose(int, int, int)
- def multiply(self, a, b):
- return a * b
-
-.. _TurboGears: http://www.turbogears.org/
-
-Other frameworks
-----------------
-
-Bottle
-~~~~~~
-
-No adapter is provided yet but it should not be hard to write one, by taking
-example on the cornice adapter.
-
-This example only show how to mount a WSRoot inside a bottle application.
-
-.. code-block:: python
-
- import bottle
- import wsme
-
- class MyRoot(wsme.WSRoot):
- @wsme.expose(unicode)
- def helloworld(self):
- return u"Hello World !"
-
- root = MyRoot(webpath='/ws', protocols=['restjson'])
-
- bottle.mount('/ws', root.wsgiapp())
- bottle.run()
-
-Pyramid
-~~~~~~~
-
-The recommended way of using WSME inside Pyramid is to use
-:ref:`adapter-cornice`.
diff --git a/doc/make.bat b/doc/make.bat
deleted file mode 100644
index a88e5fe..0000000
--- a/doc/make.bat
+++ /dev/null
@@ -1,155 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. changes to make an overview over all changed/added/deprecated items
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\WebServicesMadeEasy.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\WebServicesMadeEasy.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-:end
diff --git a/doc/protocols.rst b/doc/protocols.rst
deleted file mode 100644
index a56a49d..0000000
--- a/doc/protocols.rst
+++ /dev/null
@@ -1,366 +0,0 @@
-Protocols
-=========
-
-In this document the same webservice example will be used to
-illustrate the different protocols. Its source code is in the
-last chapter (:ref:`protocols-the-example`).
-
-REST
-----
-
-.. note::
-
- This chapter applies for all adapters, not just the native REST
- implementation.
-
-The two REST protocols share common characterics.
-
-Each function corresponds to distinct webpath that starts with the
-root webpath, followed by the controllers names if any, and finally
-the function name.
-
-The example's exposed functions will be mapped to the following paths:
-
-- ``/ws/persons/create``
-- ``/ws/persons/get``
-- ``/ws/persons/list``
-- ``/ws/persons/update``
-- ``/ws/persons/destroy``
-
-In addition to this trivial function mapping, a `method` option can
-be given to the `expose` decorator. In such a case, the function
-name can be omitted by the caller, and the dispatch will look at the
-HTTP method used in the request to select the correct function.
-
-The function parameters can be transmitted in two ways (if using
-the HTTP method to select the function, one way or the other
-may be usable) :
-
-#. As a GET query string or POST form parameters.
-
- Simple types are straight forward :
-
- ``/ws/person/get?id=5``
-
- Complex types can be transmitted this way:
-
- ``/ws/person/update?p.id=1&p.name=Ross&p.hobbies[0]=Dinausaurs&p.hobbies[1]=Rachel``
-
-#. In a Json or XML encoded POST body (see below)
-
-The result will be returned Json or XML encoded (see below).
-
-In case of error, a 400 or 500 status code is returned, and the
-response body contains details about the error (see below).
-
-REST+Json
----------
-
-:name: ``'restjson'``
-
-Implements a REST+Json protocol.
-
-This protocol is selected if:
-
-- The request content-type is either 'text/javascript' or 'application/json'
-- The request 'Accept' header contains 'text/javascript' or 'application/json'
-- A trailing '.json' is added to the path
-- A 'wsmeproto=restjson' is added in the query string
-
-Options
-~~~~~~~
-
-:nest_result: Nest the encoded result in a result param of an object.
- For example, a result of ``2`` would be ``{'result': 2}``
-
-Types
-~~~~~
-
-+---------------+-------------------------------+
-| Type | Json type |
-+===============+===============================+
-| ``str`` | String |
-+---------------+-------------------------------+
-| ``unicode`` | String |
-+---------------+-------------------------------+
-| ``int`` | Number |
-+---------------+-------------------------------+
-| ``float`` | Number |
-+---------------+-------------------------------+
-| ``bool`` | Boolean |
-+---------------+-------------------------------+
-| ``Decimal`` | String |
-+---------------+-------------------------------+
-| ``date`` | String (YYYY-MM-DD) |
-+---------------+-------------------------------+
-| ``time`` | String (hh:mm:ss) |
-+---------------+-------------------------------+
-| ``datetime`` | String (YYYY-MM-DDThh:mm:ss) |
-+---------------+-------------------------------+
-| Arrays | Array |
-+---------------+-------------------------------+
-| None | null |
-+---------------+-------------------------------+
-| Complex types | Object |
-+---------------+-------------------------------+
-
-Return
-~~~~~~
-
-The Json encoded result when the response code is 200, or a Json object
-with error properties ('faulcode', 'faultstring' and 'debuginfo' if
-available) on error.
-
-For example, the '/ws/person/get' result looks like:
-
-.. code-block:: javascript
-
- {
- 'id': 2
- 'fistname': 'Monica',
- 'lastname': 'Geller',
- 'age': 28,
- 'hobbies': [
- 'Food',
- 'Cleaning'
- ]
- }
-
-And in case of error:
-
-.. code-block:: javascript
-
- {
- 'faultcode': 'Client',
- 'faultstring': 'id is missing'
- }
-
-REST+XML
---------
-
-:name: ``'restxml'``
-
-This protocol is selected if
-
-- The request content-type is 'text/xml'
-- The request 'Accept' header contains 'text/xml'
-- A trailing '.xml' is added to the path
-- A 'wsmeproto=restxml' is added in the query string
-
-Types
-~~~~~
-
-+---------------+----------------------------------------+
-| Type | XML example |
-+===============+========================================+
-| ``str`` | .. code-block:: xml |
-| | |
-| | <value>a string</value> |
-+---------------+----------------------------------------+
-| ``unicode`` | .. code-block:: xml |
-| | |
-| | <value>a string</value> |
-+---------------+----------------------------------------+
-| ``int`` | .. code-block:: xml |
-| | |
-| | <value>5</value> |
-+---------------+----------------------------------------+
-| ``float`` | .. code-block:: xml |
-| | |
-| | <value>3.14</value> |
-+---------------+----------------------------------------+
-| ``bool`` | .. code-block:: xml |
-| | |
-| | <value>true</value> |
-+---------------+----------------------------------------+
-| ``Decimal`` | .. code-block:: xml |
-| | |
-| | <value>5.46</value> |
-+---------------+----------------------------------------+
-| ``date`` | .. code-block:: xml |
-| | |
-| | <value>2010-04-27</value> |
-+---------------+----------------------------------------+
-| ``time`` | .. code-block:: xml |
-| | |
-| | <value>12:54:18</value> |
-+---------------+----------------------------------------+
-| ``datetime`` | .. code-block:: xml |
-| | |
-| | <value>2010-04-27T12:54:18</value> |
-+---------------+----------------------------------------+
-| Arrays | .. code-block:: xml |
-| | |
-| | <value> |
-| | <item>Dinausaurs<item> |
-| | <item>Rachel<item> |
-| | </value> |
-+---------------+----------------------------------------+
-| None | .. code-block:: xml |
-| | |
-| | <value nil="true"/> |
-+---------------+----------------------------------------+
-| Complex types | .. code-block:: xml |
-| | |
-| | <value> |
-| | <id>1</id> |
-| | <fistname>Ross</fistname> |
-| | </value> |
-+---------------+----------------------------------------+
-
-Return
-~~~~~~
-
-A xml tree with a top 'result' element.
-
-.. code-block:: xml
-
- <result>
- <id>1</id>
- <firstname>Ross</firstname>
- <lastname>Geller</lastname>
- </result>
-
-Errors
-~~~~~~
-
-A xml tree with a top 'error' element, having 'faultcode', 'faultstring'
-and 'debuginfo' subelements:
-
-.. code-block:: xml
-
- <error>
- <faultcode>Client</faultcode>
- <faultstring>id is missing</faultstring>
- </error>
-
-SOAP
-----
-
-:name: ``'soap'``
-
-Implements the SOAP protocol.
-
-A wsdl definition of the webservice is available at the 'api.wsdl' subpath.
-(``/ws/api.wsdl`` in our example).
-
-The protocol is selected if the request matches one of the following condition:
-
-- The Content-Type is 'application/soap+xml'
-- A header 'Soapaction' is present
-
-Options
-~~~~~~~
-
-:tns: Type namespace
-
-ExtDirect
----------
-
-:name: ``extdirect``
-
-Implements the `Ext Direct`_ protocol.
-
-The provider definition is made available at the ``/extdirect/api.js`` subpath.
-
-The router url is ``/extdirect/router[/subnamespace]``.
-
-Options
-~~~~~~~
-
-:namespace: Base namespace of the api. Used for the provider definition.
-:params_notation: Default notation for function call parameters. Can be
- overridden for individual functions by adding the
- ``extdirect_params_notation`` extra option to @expose.
-
- The possible notations are :
-
- - ``'named'`` -- The function will take only one object parameter
- in which each property will be one of the parameters.
- - ``'positional'`` -- The function will take as many parameters as
- the function has, and their position will determine which parameter
- they are.
-
-expose extra options
-~~~~~~~~~~~~~~~~~~~~
-
-:extdirect_params_notation: Override the params_notation for a particular
- function.
-
-.. _Ext Direct: http://www.sencha.com/products/extjs/extdirect
-
-.. _protocols-the-example:
-
-The example
------------
-
-In this document the same webservice example will be used to
-illustrate the different protocols:
-
-.. code-block:: python
-
- class Person(object):
- id = int
- lastname = unicode
- firstname = unicode
- age = int
-
- hobbies = [unicode]
-
- def __init__(self, id=None, lastname=None, firstname=None, age=None,
- hobbies=None):
- if id:
- self.id = id
- if lastname:
- self.lastname = lastname
- if firstname:
- self.firstname = firstname
- if age:
- self.age = age
- if hobbies:
- self.hobbies = hobbies
-
- persons = {
- 1: Person(1, "Geller", "Ross", 30, ["Dinosaurs", "Rachel"]),
- 2: Person(2, "Geller", "Monica", 28, ["Food", "Cleaning"])
- }
-
- class PersonController(object):
- @expose(Person)
- @validate(int)
- def get(self, id):
- return persons[id]
-
- @expose([Person])
- def list(self):
- return persons.values()
-
- @expose(Person)
- @validate(Person)
- def update(self, p):
- if p.id is Unset:
- raise ClientSideError("id is missing")
- persons[p.id] = p
- return p
-
- @expose(Person)
- @validate(Person)
- def create(self, p):
- if p.id is not Unset:
- raise ClientSideError("I don't want an id")
- p.id = max(persons.keys()) + 1
- persons[p.id] = p
- return p
-
- @expose()
- @validate(int)
- def destroy(self, id):
- if id not in persons:
- raise ClientSideError("Unknown ID")
-
-
- class WS(WSRoot):
- person = PersonController()
-
- root = WS(webpath='ws')
-
diff --git a/doc/requirements.txt b/doc/requirements.txt
deleted file mode 100644
index 57dd1d9..0000000
--- a/doc/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-sphinx
-cloud_sptheme
--r ../requirements.txt
diff --git a/doc/todo.rst b/doc/todo.rst
deleted file mode 100644
index 21a300b..0000000
--- a/doc/todo.rst
+++ /dev/null
@@ -1,31 +0,0 @@
-TODO
-====
-
-WSME is a work in progress. Here is a list of things that should
-be done :
-
-- Use gevents for batch-calls
-
-- Implement new protocols :
-
- - json-rpc
-
- - xml-rpc
-
-- Implement adapters for other frameworks :
-
- - TurboGears 2
-
- - Pylons
-
- - CherryPy
-
- - Flask
-
- - others ?
-
-- Add unittests for adapters
-
-- Address the authentication subject (which should be handled by
- some other wsgi framework/middleware, but a little integration
- could help).
diff --git a/doc/types.rst b/doc/types.rst
deleted file mode 100644
index 637a5b7..0000000
--- a/doc/types.rst
+++ /dev/null
@@ -1,246 +0,0 @@
-Types
-=====
-
-Three kinds of data types can be used as input or output by WSME.
-
-Native types
-------------
-
-The native types are a fixed set of standard Python types that
-different protocols map to their own basic types.
-
-The native types are :
-
- - .. wsme:type:: bytes
-
- A pure-ascii string (:py:class:`wsme.types.bytes` which is
- :py:class:`str` in Python 2 and :py:class:`bytes` in Python 3).
-
-
- - .. wsme:type:: text
-
- A unicode string (:py:class:`wsme.types.text` which is
- :py:class:`unicode` in Python 2 and :py:class:`str` in Python 3).
-
- - .. wsme:type:: int
-
- An integer (:py:class:`int`)
-
- - .. wsme:type:: float
-
- A float (:py:class:`float`)
-
- - .. wsme:type:: bool
-
- A boolean (:py:class:`bool`)
-
- - .. wsme:type:: Decimal
-
- A fixed-width decimal (:py:class:`decimal.Decimal`)
-
- - .. wsme:type:: date
-
- A date (:py:class:`datetime.date`)
-
- - .. wsme:type:: datetime
-
- A date and time (:py:class:`datetime.datetime`)
-
- - .. wsme:type:: time
-
- A time (:py:class:`datetime.time`)
-
- - Arrays -- This is a special case. When stating a list
- datatype, always state its content type as the unique element
- of a list. Example::
-
- class SomeWebService(object):
- @expose([str])
- def getlist(self):
- return ['a', 'b', 'c']
-
- - Dictionaries -- Statically typed mappings are allowed. When exposing
- a dictionary datatype, you can specify the key and value types,
- with a restriction on the key value that must be a 'pod' type.
- Example::
-
- class SomeType(object):
- amap = {str: SomeOthertype}
-
-There are other types that are supported out of the box. See the
-:ref:`pre-defined-user-types`.
-
-User types
-----------
-
-User types allow you to define new, almost-native types.
-
-The idea is that you may have Python data that should be transported as base
-types by the different protocols, but needs conversion to/from these base types,
-or needs to validate data integrity.
-
-To define a user type, you just have to inherit from
-:class:`wsme.types.UserType` and instantiate your new class. This instance
-will be your new type and can be used as @\ :class:`wsme.expose` or
-@\ :class:`wsme.validate` parameters.
-
-Note that protocols can choose to specifically handle a user type or
-a base class of user types. This is case with the two pre-defined
-user types, :class:`wsme.types.Enum` and :data:`wsme.types.binary`.
-
-.. _pre-defined-user-types:
-
-Pre-defined user types
-~~~~~~~~~~~~~~~~~~~~~~
-
-WSME provides some pre-defined user types:
-
-- :class:`binary <wsme.types.binary>` -- for transporting binary data as
- base64 strings.
-- :class:`Enum <wsme.types.Enum>` -- enforce that the values belongs to a
- pre-defined list of values.
-
-These types are good examples of how to define user types. Have
-a look at their source code!
-
-Here is a little example that combines :class:`binary <wsme.types.binary>`
-and :class:`Enum <wsme.types.Enum>`::
-
- ImageKind = Enum(str, 'jpeg', 'gif')
-
- class Image(object):
- name = unicode
- kind = ImageKind
- data = binary
-
-.. data:: wsme.types.binary
-
- The :class:`wsme.types.BinaryType` instance to use when you need to
- transfer base64 encoded data.
-
-.. autoclass:: wsme.types.BinaryType
-
-.. autoclass:: wsme.types.Enum
-
-
-Complex types
--------------
-
-Complex types are structured types. They are defined as simple Python classes
-and will be mapped to adequate structured types in the various protocols.
-
-A base class for structured types is provided, :class:`wsme.types.Base`,
-but is not mandatory. The only thing it adds is a default constructor.
-
-The attributes that are set at the class level will be used by WSME to discover
-the structure. These attributes can be:
-
- - A datatype -- Any native, user or complex type.
- - A :class:`wsattr <wsme.wsattr>` -- This allows you to add more information about
- the attribute, for example if it is mandatory.
- - A :class:`wsproperty <wsme.wsproperty>` -- A special typed property. Works
- like standard ``property`` with additional properties like
- :class:`wsattr <wsme.wsattr>`.
-
-Attributes having a leading '_' in their name will be ignored, as well as the
-attributes that are not in the above list. This means the type can have methods,
-they will not get in the way.
-
-Example
-~~~~~~~
-
-::
-
- Gender = wsme.types.Enum(str, 'male', 'female')
- Title = wsme.types.Enum(str, 'M', 'Mrs')
-
- class Person(wsme.types.Base):
- lastname = wsme.types.wsattr(unicode, mandatory=True)
- firstname = wsme.types.wsattr(unicode, mandatory=True)
-
- age = int
- gender = Gender
- title = Title
-
- hobbies = [unicode]
-
-Rules
-~~~~~
-
-A few things you should know about complex types:
-
- - The class must have a default constructor --
- Since instances of the type will be created by the protocols when
- used as input types, they must be instantiable without any argument.
-
- - Complex types are registered automatically
- (and thus inspected) as soon a they are used in expose or validate,
- even if they are nested in another complex type.
-
- If for some reason you need to control when type is inspected, you
- can use :func:`wsme.types.register_type`.
-
- - The datatype attributes will be replaced.
-
- When using the 'short' way of defining attributes, ie setting a
- simple data type, they will be replaced by a wsattr instance.
-
- So, when you write::
-
- class Person(object):
- name = unicode
-
- After type registration the class will actually be equivalent to::
-
- class Person(object):
- name = wsattr(unicode)
-
- You can still access the datatype by accessing the attribute on the
- class, along with the other wsattr properties::
-
- class Person(object):
- name = unicode
-
- register_type(Person)
-
- assert Person.name.datatype is unicode
- assert Person.name.key == "name"
- assert Person.name.mandatory is False
-
- - The default value of instance attributes is
- :data:`Unset <wsme.Unset>`.
-
- ::
-
- class Person(object):
- name = wsattr(unicode)
-
- p = Person()
- assert p.name is Unset
-
- This allows the protocol to make a clear distinction between null values
- that will be transmitted, and unset values that will not be transmitted.
-
- For input values, it allows the code to know if the values were, or were not,
- sent by the caller.
-
- - When 2 complex types refer to each other, their names can be
- used as datatypes to avoid adding attributes afterwards:
-
- ::
-
- class A(object):
- b = wsattr('B')
-
- class B(object):
- a = wsattr(A)
-
-
-Predefined Types
-~~~~~~~~~~~~~~~~
-
-.. default-domain:: wsme
-
-- .. autotype:: wsme.types.File
- :members:
-
diff --git a/examples/demo/client.py b/examples/demo/client.py
deleted file mode 100644
index 328f184..0000000
--- a/examples/demo/client.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from suds.client import Client
-
-url = 'http://127.0.0.1:8080/ws/api.wsdl'
-
-client = Client(url, cache=None)
-
-print client
-
-print client.service.multiply(4, 5)
-print client.service.helloworld()
-print client.service.getperson()
-p = client.service.listpersons()
-print repr(p)
-p = client.service.setpersons(p)
-print repr(p)
-
-p = client.factory.create('ns0:Person')
-p.id = 4
-print p
-
-a = client.factory.create('ns0:Person_Array')
-print a
-
-a = client.service.setpersons(a)
-print repr(a)
-
-a.item.append(p)
-print repr(a)
-
-a = client.service.setpersons(a)
-print repr(a)
-
diff --git a/examples/demo/demo.py b/examples/demo/demo.py
deleted file mode 100644
index 7adec2a..0000000
--- a/examples/demo/demo.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# coding=utf8
-"""
-A mini-demo of what wsme can do.
-
-To run it::
-
- python setup.py develop
-
-Then::
-
- python demo.py
-"""
-
-from wsme import WSRoot, expose, validate
-from wsme.types import File
-
-import bottle
-
-from six import u
-
-import logging
-
-
-class Person(object):
- id = int
- firstname = unicode
- lastname = unicode
-
- hobbies = [unicode]
-
- def __repr__(self):
- return "Person(%s, %s %s, %s)" % (
- self.id,
- self.firstname, self.lastname,
- self.hobbies
- )
-
-
-class DemoRoot(WSRoot):
- @expose(int)
- @validate(int, int)
- def multiply(self, a, b):
- return a * b
-
- @expose(File)
- @validate(File)
- def echofile(self, afile):
- return afile
-
- @expose(unicode)
- def helloworld(self):
- return u"Здраво, свете (<- Hello World in Serbian !)"
-
- @expose(Person)
- def getperson(self):
- p = Person()
- p.id = 12
- p.firstname = u'Ross'
- p.lastname = u'Geler'
- p.hobbies = []
- print p
- return p
-
- @expose([Person])
- def listpersons(self):
- p = Person()
- p.id = 12
- p.firstname = u('Ross')
- p.lastname = u('Geler')
- r = [p]
- p = Person()
- p.id = 13
- p.firstname = u('Rachel')
- p.lastname = u('Green')
- r.append(p)
- print r
- return r
-
- @expose(Person)
- @validate(Person)
- def setperson(self, person):
- return person
-
- @expose([Person])
- @validate([Person])
- def setpersons(self, persons):
- print persons
- return persons
-
-
-root = DemoRoot(webpath='/ws')
-
-root.addprotocol('soap',
- tns='http://example.com/demo',
- typenamespace='http://example.com/demo/types',
- baseURL='http://127.0.0.1:8080/ws/',
-)
-
-root.addprotocol('restjson')
-
-bottle.mount('/ws/', root.wsgiapp())
-
-logging.basicConfig(level=logging.DEBUG)
-bottle.run()
diff --git a/examples/demo/setup.cfg b/examples/demo/setup.cfg
deleted file mode 100644
index e815e43..0000000
--- a/examples/demo/setup.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[easy_install]
-find_links = http://www.owlfish.com/software/wsgiutils/download.html
diff --git a/examples/demo/setup.py b/examples/demo/setup.py
deleted file mode 100644
index 33e5161..0000000
--- a/examples/demo/setup.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from setuptools import setup
-
-setup(name='demo',
- install_requires=[
- 'WSME',
- 'Bottle',
- 'Pygments',
- ],
- package=['demo'])
diff --git a/examples/demo/sporeclient.py b/examples/demo/sporeclient.py
deleted file mode 100644
index d440a93..0000000
--- a/examples/demo/sporeclient.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import spyre
-import spyre.middleware
-
-
-class CTypeHeader(spyre.middleware.Middleware):
- def __call__(self, env):
- env.setdefault('spore.headers', [])
- env['spore.headers'].extend([
- ('Accept', 'application/json'),
- ('Content-Type', 'application/json')
- ])
-
-
-demo = spyre.new_from_url('http://127.0.0.1:8080/ws/api.spore')
-demo.enable(CTypeHeader)
-demo.enable('format.Json')
-
-print demo.helloworld().content
diff --git a/requirements-py3.txt b/requirements-py3.txt
deleted file mode 100644
index d15bd16..0000000
--- a/requirements-py3.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-six>=1.9.0
-WebOb>=1.2.3
-simplegeneric
-pytz
-netaddr>=0.7.12
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index d15bd16..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-six>=1.9.0
-WebOb>=1.2.3
-simplegeneric
-pytz
-netaddr>=0.7.12
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 78237d5..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,54 +0,0 @@
-[metadata]
-name = WSME
-
-author = Christophe de Vienne
-author-email = python-wsme@googlegroups.com
-
-summary = Simplify the writing of REST APIs, and extend them with additional protocols.
-
-description-file = README.rst
-
-url = http://git.openstack.org/cgit/openstack/wsme
-
-license = MIT
-
-classifier =
- Development Status :: 3 - Alpha
- Operating System :: OS Independent
- Programming Language :: Python
- Programming Language :: Python :: 2.7
- Programming Language :: Python :: 3
- Programming Language :: Python :: 3.5
- Programming Language :: Python :: 3.6
- Programming Language :: Python :: 3.7
- Programming Language :: Python :: Implementation :: CPython
- Programming Language :: Python :: Implementation :: PyPy
- License :: OSI Approved :: MIT License
- Topic :: Internet :: WWW/HTTP :: WSGI
- Topic :: Software Development :: Libraries :: Python Modules
-
-[entry_points]
-wsme.protocols =
- rest = wsme.rest.protocol:RestProtocol
- restjson = wsme.rest.protocol:RestProtocol
- restxml = wsme.rest.protocol:RestProtocol
- soap = wsmeext.soap:SoapProtocol
- extdirect = wsmeext.extdirect:ExtDirectProtocol
-
-[files]
-packages =
- wsme
- wsmeext
-
-namespace_packages =
- wsmeext
-
-extra_files =
- setup.py
- README.rst
- tests
-
-[wheel]
-# WSME has different requirements depending on the version of Python
-# being used, so we cannot build universal wheels.
-universal = 0
diff --git a/setup.py b/setup.py
deleted file mode 100644
index a92d122..0000000
--- a/setup.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import setuptools
-
-setuptools.setup(
- setup_requires=['pbr'],
- pbr=True
-)
diff --git a/tests/pecantest/setup.cfg b/tests/pecantest/setup.cfg
deleted file mode 100644
index 00ca220..0000000
--- a/tests/pecantest/setup.cfg
+++ /dev/null
@@ -1,6 +0,0 @@
-[nosetests]
-match=^test
-where=test
-nocapture=1
-cover-package=test
-cover-erase=1
diff --git a/tests/pecantest/setup.py b/tests/pecantest/setup.py
deleted file mode 100644
index b901f62..0000000
--- a/tests/pecantest/setup.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-try:
- from setuptools import setup, find_packages
-except ImportError:
- from ez_setup import use_setuptools
- use_setuptools()
- from setuptools import setup, find_packages
-
-setup(
- name = 'test',
- version = '0.1',
- description = '',
- author = '',
- author_email = '',
- install_requires = [
- "pecan",
- ],
- test_suite = 'test',
- zip_safe = False,
- include_package_data = True,
- packages = find_packages(exclude=['ez_setup'])
-)
diff --git a/tests/pecantest/test/__init__.py b/tests/pecantest/test/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/pecantest/test/__init__.py
+++ /dev/null
diff --git a/tests/pecantest/test/app.py b/tests/pecantest/test/app.py
deleted file mode 100644
index 727c357..0000000
--- a/tests/pecantest/test/app.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from pecan import make_app
-from test import model
-
-def setup_app(config):
-
- model.init_model()
-
- return make_app(
- config.app.root,
- static_root = config.app.static_root,
- template_path = config.app.template_path,
- logging = getattr(config, 'logging', {}),
- debug = getattr(config.app, 'debug', False),
- force_canonical = getattr(config.app, 'force_canonical', True)
- )
diff --git a/tests/pecantest/test/controllers/__init__.py b/tests/pecantest/test/controllers/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/pecantest/test/controllers/__init__.py
+++ /dev/null
diff --git a/tests/pecantest/test/controllers/root.py b/tests/pecantest/test/controllers/root.py
deleted file mode 100644
index 6e87267..0000000
--- a/tests/pecantest/test/controllers/root.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from pecan import expose
-from webob.exc import status_map
-from .ws import AuthorsController
-from wsmeext.pecan import wsexpose
-
-
-class RootController(object):
- authors = AuthorsController()
-
- @expose('error.html')
- def error(self, status):
- try:
- status = int(status)
- except ValueError: # pragma: no cover
- status = 500
- message = getattr(status_map.get(status), 'explanation', '')
- return dict(status=status, message=message)
-
- @wsexpose()
- def divide_by_zero(self):
- 1 / 0
diff --git a/tests/pecantest/test/controllers/ws.py b/tests/pecantest/test/controllers/ws.py
deleted file mode 100644
index b35a0c6..0000000
--- a/tests/pecantest/test/controllers/ws.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# encoding=utf8
-from pecan.rest import RestController
-
-from wsme.types import Base, text, wsattr
-
-import wsme
-import wsmeext.pecan
-
-import six
-
-
-class Author(Base):
- id = int
- firstname = text
- books = wsattr(['Book'])
-
- @staticmethod
- def validate(author):
- if author.firstname == 'Robert':
- raise wsme.exc.ClientSideError("I don't like this author!")
- return author
-
-
-class Book(Base):
- id = int
- name = text
- author = wsattr('Author')
-
-
-class BookNotFound(Exception):
- message = "Book with ID={id} Not Found"
- code = 404
-
- def __init__(self, id):
- message = self.message.format(id=id)
- super(BookNotFound, self).__init__(message)
-
-
-class NonHttpException(Exception):
- message = "Internal Exception for Book ID={id}"
- code = 684
-
- def __init__(self, id):
- message = self.message.format(id=id)
- super(NonHttpException, self).__init__(message)
-
-
-class BooksController(RestController):
-
- @wsmeext.pecan.wsexpose(Book, int, int)
- def get(self, author_id, id):
- book = Book(
- name=u"Les Confessions d’un révolutionnaire pour servir à "
- u"l’histoire de la révolution de février",
- author=Author(lastname=u"Proudhon")
- )
- return book
-
- @wsmeext.pecan.wsexpose(Book, int, int, body=Book)
- def put(self, author_id, id, book=None):
- book.id = id
- book.author = Author(id=author_id)
- return book
-
-
-class Criterion(Base):
- op = text
- attrname = text
- value = text
-
-
-class AuthorsController(RestController):
-
- _custom_actions = {
- 'json_only': ['GET'],
- 'xml_only': ['GET']
- }
-
- books = BooksController()
-
- @wsmeext.pecan.wsexpose([Author], [six.text_type], [Criterion])
- def get_all(self, q=None, r=None):
- if q:
- return [
- Author(id=i, firstname=value)
- for i, value in enumerate(q)
- ]
- if r:
- return [
- Author(id=i, firstname=c.value)
- for i, c in enumerate(r)
- ]
- return [
- Author(id=1, firstname=u'FirstName')
- ]
-
- @wsmeext.pecan.wsexpose(Author, int)
- def get(self, id):
- if id == 999:
- raise wsme.exc.ClientSideError('Wrong ID')
-
- if id == 998:
- raise BookNotFound(id)
-
- if id == 997:
- raise NonHttpException(id)
-
- if id == 996:
- raise wsme.exc.ClientSideError('Disabled ID', status_code=403)
-
- if id == 911:
- return wsme.api.Response(Author(),
- status_code=401)
- if id == 912:
- return wsme.api.Response(None, status_code=204)
-
- if id == 913:
- return wsme.api.Response('foo', status_code=200, return_type=text)
-
- author = Author()
- author.id = id
- author.firstname = u"aname"
- author.books = [
- Book(
- name=u"Les Confessions d’un révolutionnaire pour servir à "
- u"l’histoire de la révolution de février",
- )
- ]
- return author
-
- @wsmeext.pecan.wsexpose(Author, body=Author, status_code=201)
- def post(self, author):
- author.id = 10
- return author
-
- @wsmeext.pecan.wsexpose(None, int)
- def delete(self, author_id):
- print("Deleting", author_id)
-
- @wsmeext.pecan.wsexpose(Book, int, body=Author)
- def put(self, author_id, author=None):
- return author
-
- @wsmeext.pecan.wsexpose([Author], rest_content_types=('json',))
- def json_only(self):
- return [Author(id=1, firstname=u"aname", books=[])]
-
- @wsmeext.pecan.wsexpose([Author], rest_content_types=('xml',))
- def xml_only(self):
- return [Author(id=1, firstname=u"aname", books=[])]
diff --git a/tests/pecantest/test/model/__init__.py b/tests/pecantest/test/model/__init__.py
deleted file mode 100644
index ab4be6a..0000000
--- a/tests/pecantest/test/model/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def init_model():
- pass
diff --git a/tests/pecantest/test/tests/__init__.py b/tests/pecantest/test/tests/__init__.py
deleted file mode 100644
index 04fa0a6..0000000
--- a/tests/pecantest/test/tests/__init__.py
+++ /dev/null
@@ -1,22 +0,0 @@
-import os
-from unittest import TestCase
-from pecan import set_config
-from pecan import testing
-
-__all__ = ['FunctionalTest']
-
-
-class FunctionalTest(TestCase):
- """
- Used for functional tests where you need to test your
- literal application and its integration with the framework.
- """
-
- def setUp(self):
- self.app = testing.load_test_app(os.path.join(
- os.path.dirname(__file__),
- 'config.py'
- ))
-
- def tearDown(self):
- set_config({}, overwrite=True)
diff --git a/tests/pecantest/test/tests/config.py b/tests/pecantest/test/tests/config.py
deleted file mode 100644
index 3f35dcf..0000000
--- a/tests/pecantest/test/tests/config.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Server Specific Configurations
-server = {
- 'port' : '8080',
- 'host' : '0.0.0.0'
-}
-
-# Pecan Application Configurations
-app = {
- 'root' : 'test.controllers.root.RootController',
- 'modules' : ['test'],
- 'static_root' : '%(confdir)s/../../public',
- 'template_path' : '%(confdir)s/../templates',
- 'errors' : {
- '404' : '/error/404',
- '__force_dict__' : True
- }
-}
-
-# Custom Configurations must be in Python dictionary format::
-#
-# foo = {'bar':'baz'}
-#
-# All configurations are accessible at::
-# pecan.conf
diff --git a/tests/pecantest/test/tests/test_ws.py b/tests/pecantest/test/tests/test_ws.py
deleted file mode 100644
index ae06650..0000000
--- a/tests/pecantest/test/tests/test_ws.py
+++ /dev/null
@@ -1,247 +0,0 @@
-from six.moves import http_client
-from test.tests import FunctionalTest
-import json
-import pecan
-import six
-
-
-used_status_codes = [400, 401, 403, 404, 500]
-http_response_messages = {}
-for code in used_status_codes:
- http_response_messages[code] = '%s %s' % (code, http_client.responses[code])
-
-class TestWS(FunctionalTest):
-
- def test_get_all(self):
- self.app.get('/authors')
-
- def test_optional_array_param(self):
- r = self.app.get('/authors?q=a&q=b')
- l = json.loads(r.body.decode('utf-8'))
- assert len(l) == 2
- assert l[0]['firstname'] == 'a'
- assert l[1]['firstname'] == 'b'
-
- def test_optional_indexed_array_param(self):
- r = self.app.get('/authors?q[0]=a&q[1]=b')
- l = json.loads(r.body.decode('utf-8'))
- assert len(l) == 2
- assert l[0]['firstname'] == 'a'
- assert l[1]['firstname'] == 'b'
-
- def test_options_object_array_param(self):
- r = self.app.get('/authors?r.value=a&r.value=b')
- l = json.loads(r.body.decode('utf-8'))
- assert len(l) == 2
- assert l[0]['firstname'] == 'a'
- assert l[1]['firstname'] == 'b'
-
- def test_options_indexed_object_array_param(self):
- r = self.app.get('/authors?r[0].value=a&r[1].value=b')
- l = json.loads(r.body.decode('utf-8'))
- assert len(l) == 2
- assert l[0]['firstname'] == 'a'
- assert l[1]['firstname'] == 'b'
-
- def test_get_author(self):
- a = self.app.get(
- '/authors/1.json',
- )
- a = json.loads(a.body.decode('utf-8'))
-
- assert a['id'] == 1
- assert a['firstname'] == 'aname'
-
- a = self.app.get(
- '/authors/1.xml',
- )
- body = a.body.decode('utf-8')
- assert '<id>1</id>' in body
- assert '<firstname>aname</firstname>' in body
-
- def test_post_body_parameter_validation(self):
- res = self.app.post(
- '/authors', '{"firstname": "Robert"}',
- headers={"Content-Type": "application/json"},
- expect_errors=True
- )
- self.assertEqual(res.status_int, 400)
- a = json.loads(res.body.decode('utf-8'))
- self.assertEqual(a['faultcode'], 'Client')
- self.assertEqual(a['faultstring'], "I don't like this author!")
-
- def test_post_body_parameter(self):
- res = self.app.post(
- '/authors', '{"firstname": "test"}',
- headers={"Content-Type": "application/json"}
- )
- assert res.status_int == 201
- a = json.loads(res.body.decode('utf-8'))
- assert a['id'] == 10
- assert a['firstname'] == 'test'
-
- def test_put_parameter_validate(self):
- res = self.app.put(
- '/authors/foobar', '{"firstname": "test"}',
- headers={"Content-Type": "application/json"},
- expect_errors=True
- )
- self.assertEqual(res.status_int, 400)
- a = json.loads(res.body.decode('utf-8'))
- self.assertEqual(
- a['faultstring'],
- "Invalid input for field/attribute author_id. "
- "Value: 'foobar'. unable to convert to int. Error: invalid "
- "literal for int() with base 10: 'foobar'")
-
- def test_clientsideerror(self):
- expected_status_code = 400
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get(
- '/authors/999.json',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Client'
-
- res = self.app.get(
- '/authors/999.xml',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- assert '<faultcode>Client</faultcode>' in res.body.decode('utf-8')
-
- def test_custom_clientside_error(self):
- expected_status_code = 404
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get(
- '/authors/998.json',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Client'
-
- res = self.app.get(
- '/authors/998.xml',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- assert '<faultcode>Client</faultcode>' in res.body.decode('utf-8')
-
- def test_custom_non_http_clientside_error(self):
- expected_status_code = 500
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get(
- '/authors/997.json',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Server'
-
- res = self.app.get(
- '/authors/997.xml',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- assert '<faultcode>Server</faultcode>' in res.body.decode('utf-8')
-
- def test_clientsideerror_status_code(self):
- expected_status_code = 403
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get(
- '/authors/996.json',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Client'
-
- res = self.app.get(
- '/authors/996.xml',
- expect_errors=True
- )
- self.assertEqual(res.status, expected_status)
- assert '<faultcode>Client</faultcode>' in res.body.decode('utf-8')
-
- def test_non_default_response(self):
- expected_status_code = 401
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get(
- '/authors/911.json',
- expect_errors=True
- )
- self.assertEqual(res.status_int, expected_status_code)
- self.assertEqual(res.status, expected_status)
-
- def test_non_default_response_return_type(self):
- res = self.app.get(
- '/authors/913',
- )
- self.assertEqual(res.status_int, 200)
- self.assertEqual(res.body, b'"foo"')
- self.assertEqual(res.content_length, 5)
-
- def test_non_default_response_return_type_no_content(self):
- res = self.app.get(
- '/authors/912',
- )
- self.assertEqual(res.status_int, 204)
- self.assertEqual(res.body, b'')
- self.assertEqual(res.content_length, 0)
-
- def test_serversideerror(self):
- expected_status_code = 500
- expected_status = http_response_messages[expected_status_code]
- res = self.app.get('/divide_by_zero.json', expect_errors=True)
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Server'
- assert a['debuginfo'] is None
-
- def test_serversideerror_with_debug(self):
- expected_status_code = 500
- expected_status = http_response_messages[expected_status_code]
- pecan.set_config({'wsme': {'debug': True}})
- res = self.app.get('/divide_by_zero.json', expect_errors=True)
- self.assertEqual(res.status, expected_status)
- a = json.loads(res.body.decode('utf-8'))
- assert a['faultcode'] == 'Server'
- assert a['debuginfo'].startswith('Traceback (most recent call last):')
-
- def test_json_only(self):
- res = self.app.get('/authors/json_only.json')
- assert res.status_int == 200
- body = json.loads(res.body.decode('utf-8'))
- assert len(body) == 1
- assert body[0]['firstname'] == u"aname"
- assert body[0]['books'] == []
- assert body[0]['id'] == 1
- res = self.app.get('/authors/json_only.xml', expect_errors=True)
-
- def test_xml_only(self):
- res = self.app.get('/authors/xml_only.xml')
- assert res.status_int == 200
- assert '<id>1</id>' in res.body.decode('utf-8')
- assert '<firstname>aname</firstname>' in res.body.decode('utf-8')
- assert '<books />' in res.body.decode('utf-8')
- res = self.app.get('/authors/xml_only.json', expect_errors=True)
-
- def test_body_parameter(self):
- res = self.app.put(
- '/authors/1/books/2.json',
- '{"name": "Alice au pays des merveilles"}',
- headers={"Content-Type": "application/json"}
- )
- book = json.loads(res.body.decode('utf-8'))
- assert book['id'] == 2
- assert book['author']['id'] == 1
-
- def test_no_content_type_if_no_return_type(self):
- if six.PY3:
- self.skipTest(
- "This test does not work in Python 3 until https://review.openstack.org/#/c/48439/ is merged")
- res = self.app.delete('/authors/4')
- assert "Content-Type" not in res.headers, res.headers['Content-Type']
diff --git a/tests/rest/test_args.py b/tests/rest/test_args.py
deleted file mode 100644
index 4ae3246..0000000
--- a/tests/rest/test_args.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import mock
-import unittest
-
-from wsme import exc
-from wsme.rest import args
-from wsme.rest import json
-
-
-class TestArgs(unittest.TestCase):
-
- def test_args_from_body(self):
-
- funcdef = mock.MagicMock()
- body = mock.MagicMock()
- mimetype = "application/json"
- funcdef.ignore_extra_args = True
- json.parse = mock.MagicMock()
- json.parse.side_effect = (exc.UnknownArgument(""))
- resp = args.args_from_body(funcdef, body, mimetype)
- self.assertEqual(resp, ((), {}))
diff --git a/tests/sphinxexample/conf.py b/tests/sphinxexample/conf.py
deleted file mode 100644
index ec478dd..0000000
--- a/tests/sphinxexample/conf.py
+++ /dev/null
@@ -1,232 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Web Services Made Easy documentation build configuration file, created by
-# sphinx-quickstart on Sun Oct 2 20:27:45 2011.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path 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, os.path.abspath('..'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# 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', 'wsmeext.sphinxext']
-
-# 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-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'wsmeext.sphinxext Test'
-copyright = u'2011, Christophe de Vienne'
-
-# 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.
-#
-import pkg_resources
-dist = pkg_resources.require('WSME')[0]
-
-# The short X.Y version.
-version = '.'.join(dist.version[:2])
-# The full version, including alpha/beta/rc tags.
-release = dist.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 patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_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 = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'agogo'
-html_theme_options = {
- "pagewidth": "60em",
- "documentwidth": "40em",
-}
-
-html_style = 'wsme.css'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-html_title = "WSME %s" % release
-
-# 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_domain_indices = 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, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = 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 = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'WebServicesMadeEasydoc'
-
-
-# -- 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, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'WebServicesMadeEasy.tex', u'Web Services Made Easy Documentation',
- u'Christophe de Vienne', '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
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = 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_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'webservicesmadeeasy', u'Web Services Made Easy Documentation',
- [u'Christophe de Vienne'], 1)
-]
-
-
-autodoc_member_order = 'bysource'
-
-wsme_protocols = [
- 'restjson', 'restxml'
-]
diff --git a/tests/sphinxexample/document.rst b/tests/sphinxexample/document.rst
deleted file mode 100644
index 41af798..0000000
--- a/tests/sphinxexample/document.rst
+++ /dev/null
@@ -1,43 +0,0 @@
-API Documentation test
-======================
-
-Example
-~~~~~~~
-
-.. wsme:root:: wsmeext.sphinxext.SampleService
- :webpath: /api
-
-.. wsme:type:: MyType
-
- .. wsme:attribute:: test
-
- :type: int
-
-.. wsme:service:: name/space/SampleService
-
- .. wsme:function:: getType
-
- Returns a :wsme:type:`MyType <MyType>`
-
-
-.. default-domain:: wsme
-
-.. type:: int
-
- An integer
-
-.. autotype:: wsmeext.sphinxext.SampleType
- :members:
-
-.. autoservice:: wsmeext.sphinxext.SampleService
- :members:
-
-
-.. autotype:: test_sphinxext.ASampleType
- :members:
-
-.. autotype:: wsme.types.bytes
-
-.. autotype:: wsme.types.text
-
-.. _Sphinx: http://sphinx.pocoo.org/
diff --git a/tests/sphinxexample/index.rst b/tests/sphinxexample/index.rst
deleted file mode 100644
index 76d9c55..0000000
--- a/tests/sphinxexample/index.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-.. toctree::
-
- document
diff --git a/tests/test_cornice.py b/tests/test_cornice.py
deleted file mode 100644
index 77a55e0..0000000
--- a/tests/test_cornice.py
+++ /dev/null
@@ -1,183 +0,0 @@
-import unittest
-import json
-
-import webtest
-
-from cornice import Service
-from cornice import resource
-from pyramid.config import Configurator
-from pyramid.httpexceptions import HTTPUnauthorized
-
-from wsme.types import text, Base, HostRequest
-from wsmeext.cornice import signature
-
-
-class User(Base):
- id = int
- name = text
-
-users = Service(name='users', path='/users')
-
-
-@users.get()
-@signature([User])
-def users_get():
- return [User(id=1, name='first')]
-
-
-@users.post()
-@signature(User, body=User)
-def users_create(data):
- data.id = 2
- return data
-
-
-secret = Service(name='secrets', path='/secret')
-
-
-@secret.get()
-@signature()
-def secret_get():
- raise HTTPUnauthorized()
-
-
-divide = Service(name='divide', path='/divide')
-
-
-@divide.get()
-@signature(int, int, int)
-def do_divide(a, b):
- return a / b
-
-needrequest = Service(name='needrequest', path='/needrequest')
-
-
-@needrequest.get()
-@signature(bool, HostRequest)
-def needrequest_get(request):
- assert request.path == '/needrequest', request.path
- return True
-
-
-class Author(Base):
- authorId = int
- name = text
-
-
-@resource.resource(collection_path='/author', path='/author/{authorId}')
-class AuthorResource(object):
- def __init__(self, request):
- self.request = request
-
- @signature(Author, int)
- def get(self, authorId):
- return Author(authorId=authorId, name="Author %s" % authorId)
-
- @signature(Author, int, body=Author)
- def post(self, authorId, data):
- data.authorId = authorId
- return data
-
- @signature([Author], text)
- def collection_get(self, where=None):
- return [
- Author(authorId=1, name="Author 1"),
- Author(authorId=2, name="Author 2"),
- Author(authorId=3, name="Author 3")
- ]
-
-
-def make_app():
- config = Configurator()
- config.include("cornice")
- config.include("wsmeext.cornice")
- config.scan("test_cornice")
- return config.make_wsgi_app()
-
-
-class WSMECorniceTestCase(unittest.TestCase):
- def setUp(self):
- self.app = webtest.TestApp(make_app())
-
- def test_get_json_list(self):
- resp = self.app.get('/users')
- self.assertEqual(
- resp.body,
- b'[{"id": 1, "name": "first"}]'
- )
-
- def test_get_xml_list(self):
- resp = self.app.get('/users', headers={"Accept": "text/xml"})
- self.assertEqual(
- resp.body,
- b'<result><item><id>1</id><name>first</name></item></result>'
- )
-
- def test_post_json_data(self):
- data = json.dumps({"name": "new"})
- resp = self.app.post(
- '/users', data,
- headers={"Content-Type": "application/json"}
- )
- self.assertEqual(
- resp.body,
- b'{"id": 2, "name": "new"}'
- )
-
- def test_post_xml_data(self):
- data = '<data><name>new</name></data>'
- resp = self.app.post(
- '/users', data,
- headers={"Content-Type": "text/xml"}
- )
- self.assertEqual(
- resp.body,
- b'<result><id>2</id><name>new</name></result>'
- )
-
- def test_pass_request(self):
- resp = self.app.get('/needrequest')
- assert resp.json is True
-
- def test_resource_collection_get(self):
- resp = self.app.get('/author')
- assert len(resp.json) == 3
- assert resp.json[0]['name'] == 'Author 1'
- assert resp.json[1]['name'] == 'Author 2'
- assert resp.json[2]['name'] == 'Author 3'
-
- def test_resource_get(self):
- resp = self.app.get('/author/5')
- assert resp.json['name'] == 'Author 5'
-
- def test_resource_post(self):
- resp = self.app.post(
- '/author/5',
- json.dumps({"name": "Author 5"}),
- headers={"Content-Type": "application/json"}
- )
- assert resp.json['authorId'] == 5
- assert resp.json['name'] == 'Author 5'
-
- def test_server_error(self):
- resp = self.app.get('/divide?a=1&b=0', expect_errors=True)
- self.assertEqual(resp.json['faultcode'], 'Server')
- self.assertEqual(resp.status_code, 500)
-
- def test_client_error(self):
- resp = self.app.get(
- '/divide?a=1&c=0',
- headers={'Accept': 'application/json'},
- expect_errors=True
- )
- self.assertEqual(resp.json['faultcode'], 'Client')
- self.assertEqual(resp.status_code, 400)
-
- def test_runtime_error(self):
- resp = self.app.get(
- '/secret',
- headers={'Accept': 'application/json'},
- expect_errors=True
- )
- self.assertEqual(resp.json['faultcode'], 'Client')
- self.assertEqual(resp.status_code, 401)
diff --git a/tests/test_flask.py b/tests/test_flask.py
deleted file mode 100644
index f039f45..0000000
--- a/tests/test_flask.py
+++ /dev/null
@@ -1,216 +0,0 @@
-# encoding=utf8
-import unittest
-from flask import Flask, json, abort
-import flask_restful as restful
-
-from wsmeext.flask import signature
-from wsme.api import Response
-from wsme.types import Base, text
-
-
-class Model(Base):
- id = int
- name = text
-
-
-class Criterion(Base):
- op = text
- attr = text
- value = text
-
-test_app = Flask(__name__)
-api = restful.Api(test_app)
-
-
-@test_app.route('/multiply')
-@signature(int, int, int)
-def multiply(a, b):
- return a * b
-
-
-@test_app.route('/divide_by_zero')
-@signature(None)
-def divide_by_zero():
- return 1 / 0
-
-
-@test_app.route('/models')
-@signature([Model], [Criterion])
-def list_models(q=None):
- if q:
- name = q[0].value
- else:
- name = 'first'
- return [Model(name=name)]
-
-
-@test_app.route('/models/<name>')
-@signature(Model, text)
-def get_model(name):
- return Model(name=name)
-
-
-@test_app.route('/models/<name>/secret')
-@signature(Model, text)
-def model_secret(name):
- abort(403)
-
-
-@test_app.route('/models/<name>/custom-error')
-@signature(Model, text)
-def model_custom_error(name):
- class CustomError(Exception):
- code = 412
- raise CustomError("FOO!")
-
-
-@test_app.route('/models', methods=['POST'])
-@signature(Model, body=Model)
-def post_model(body):
- return Model(name=body.name)
-
-
-@test_app.route('/status_sig')
-@signature(int, status_code=201)
-def get_status_sig():
- return 1
-
-
-@test_app.route('/status_response')
-@signature(int)
-def get_status_response():
- return Response(1, status_code=201)
-
-
-class RestFullApi(restful.Resource):
- @signature(Model)
- def get(self):
- return Model(id=1, name=u"Gérard")
-
- @signature(int, body=Model)
- def post(self, model):
- return model.id
-
-api.add_resource(RestFullApi, '/restful')
-
-
-class FlaskrTestCase(unittest.TestCase):
-
- def setUp(self):
- test_app.config['TESTING'] = True
- self.app = test_app.test_client()
-
- def tearDown(self):
- pass
-
- def test_multiply(self):
- r = self.app.get('/multiply?a=2&b=5')
- assert r.data == b'10', r.data
-
- def test_get_model(self):
- resp = self.app.get('/models/test')
- assert resp.status_code == 200
-
- def test_list_models(self):
- resp = self.app.get('/models')
- assert resp.status_code == 200
-
- def test_array_parameter(self):
- resp = self.app.get('/models?q.op=%3D&q.attr=name&q.value=second')
- assert resp.status_code == 200
- self.assertEqual(
- resp.data, b'[{"name": "second"}]'
- )
-
- def test_post_model(self):
- resp = self.app.post('/models', data={"body.name": "test"})
- assert resp.status_code == 200
- resp = self.app.post(
- '/models',
- data=json.dumps({"name": "test"}),
- content_type="application/json"
- )
- assert resp.status_code == 200
-
- def test_get_status_sig(self):
- resp = self.app.get('/status_sig')
- assert resp.status_code == 201
-
- def test_get_status_response(self):
- resp = self.app.get('/status_response')
- assert resp.status_code == 201
-
- def test_custom_clientside_error(self):
- r = self.app.get(
- '/models/test/secret',
- headers={'Accept': 'application/json'}
- )
- assert r.status_code == 403, r.status_code
- assert '403 Forbidden:' in json.loads(r.data)['faultstring']
-
- r = self.app.get(
- '/models/test/secret',
- headers={'Accept': 'application/xml'}
- )
- assert r.status_code == 403, r.status_code
- assert r.data == (b"<error><faultcode>Client</faultcode>"
- b"<faultstring>403 Forbidden: You don't have the "
- b"permission to access the requested resource. It "
- b"is either read-protected or not readable by the "
- b"server."
- b"</faultstring><debuginfo /></error>")
-
- # NOTE(cdent): For reasons unclear, 'r' here has no value on data
- # even though it does earlier in the stack. If works with Werkzeug
- # <0.14.0 but not after. As WSME does not have test-requirement, nor
- # pinning, so not a lot to do here.
- @unittest.expectedFailure
- def test_custom_non_http_clientside_error(self):
- r = self.app.get(
- '/models/test/custom-error',
- headers={'Accept': 'application/json'}
- )
- assert r.status_code == 412, r.status_code
- assert json.loads(r.data)['faultstring'] == 'FOO!'
-
- r = self.app.get(
- '/models/test/custom-error',
- headers={'Accept': 'application/xml'}
- )
- assert r.status_code == 412, r.status_code
- assert r.data == (b'<error><faultcode>Client</faultcode>'
- b'<faultstring>FOO!</faultstring>'
- b'<debuginfo /></error>')
-
- def test_serversideerror(self):
- r = self.app.get('/divide_by_zero')
- assert r.status_code == 500
- data = json.loads(r.data)
- self.assertEqual(data['debuginfo'], None)
- self.assertEqual(data['faultcode'], 'Server')
- self.assertIn('by zero', data['faultstring'])
-
- def test_restful_get(self):
- r = self.app.get('/restful', headers={'Accept': 'application/json'})
- self.assertEqual(r.status_code, 200)
-
- data = json.loads(r.data)
-
- self.assertEqual(data['id'], 1)
- self.assertEqual(data['name'], u"Gérard")
-
- def test_restful_post(self):
- r = self.app.post(
- '/restful',
- data=json.dumps({'id': 5, 'name': u'Huguette'}),
- headers={
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'})
- self.assertEqual(r.status_code, 200)
-
- data = json.loads(r.data)
-
- self.assertEqual(data, 5)
-
-if __name__ == '__main__':
- test_app.run()
diff --git a/tests/test_sphinxext.py b/tests/test_sphinxext.py
deleted file mode 100644
index 78f80dd..0000000
--- a/tests/test_sphinxext.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import unittest
-import sphinx
-import os.path
-
-import wsme.types
-from wsmeext import sphinxext
-
-docpath = os.path.join(
- os.path.dirname(__file__),
- 'sphinxexample')
-
-
-class ASampleType(object):
- somebytes = wsme.types.bytes
- sometext = wsme.types.text
- someint = int
-
-
-class TestSphinxExt(unittest.TestCase):
- def test_buildhtml(self):
- if not os.path.exists('.test_sphinxext/'):
- os.makedirs('.test_sphinxext/')
- try:
- sphinx.main([
- '',
- '-b', 'html',
- '-d', '.test_sphinxext/doctree',
- docpath,
- '.test_sphinxext/html'
- ])
- assert Exception("Should raise SystemExit 0")
- except SystemExit as e:
- assert e.code == 0
-
-
-class TestDataTypeName(unittest.TestCase):
- def test_user_type(self):
- self.assertEqual(sphinxext.datatypename(ASampleType),
- 'ASampleType')
-
- def test_dict_type(self):
- d = wsme.types.DictType(str, str)
- self.assertEqual(sphinxext.datatypename(d), 'dict(str: str)')
- d = wsme.types.DictType(str, ASampleType)
- self.assertEqual(sphinxext.datatypename(d), 'dict(str: ASampleType)')
-
- def test_array_type(self):
- d = wsme.types.ArrayType(str)
- self.assertEqual(sphinxext.datatypename(d), 'list(str)')
- d = wsme.types.ArrayType(ASampleType)
- self.assertEqual(sphinxext.datatypename(d), 'list(ASampleType)')
diff --git a/tests/test_tg1.py b/tests/test_tg1.py
deleted file mode 100644
index 3a61827..0000000
--- a/tests/test_tg1.py
+++ /dev/null
@@ -1,196 +0,0 @@
-import wsmeext.tg11
-from wsme import WSRoot
-from wsmeext.tg11 import wsexpose, wsvalidate
-import wsmeext.tg1
-
-from turbogears.controllers import RootController
-import cherrypy
-
-import unittest
-
-import simplejson
-
-
-from wsmeext.tests import test_soap
-
-
-class WSController(WSRoot):
- pass
-
-
-class Subcontroller(object):
- @wsexpose(int, int, int)
- def add(self, a, b):
- return a + b
-
-
-class Root(RootController):
- class UselessSubClass:
- # This class is here only to make sure wsmeext.tg1.scan_api
- # does its job properly
- pass
-
- ws = WSController(webpath='/ws')
- ws.addprotocol(
- 'soap',
- tns=test_soap.tns,
- typenamespace=test_soap.typenamespace,
- baseURL='/ws/'
- )
- ws = wsmeext.tg11.adapt(ws)
-
- @wsexpose(int)
- @wsvalidate(int, int)
- def multiply(self, a, b):
- return a * b
-
- @wsexpose(int)
- @wsvalidate(int, int)
- def divide(self, a, b):
- if b == 0:
- raise cherrypy.HTTPError(400, 'Cannot divide by zero!')
- return a / b
-
- sub = Subcontroller()
-
-from turbogears import testutil, config, startup
-
-
-class TestController(unittest.TestCase):
- root = Root
-
- def setUp(self):
- "Tests the output of the index method"
- self.app = testutil.make_app(self.root)
- testutil.start_server()
-
- def tearDown(self):
- # implementation copied from turbogears.testutil.stop_server.
- # The only change is that cherrypy.root is set to None
- # AFTER stopTurbogears has been called so that wsmeext.tg11
- # can correctly uninstall its filter.
- if config.get("cp_started"):
- cherrypy.server.stop()
- config.update({"cp_started": False})
-
- if config.get("server_started"):
- startup.stopTurboGears()
- config.update({"server_started": False})
-
- def test_restcall(self):
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/sub/add",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json'}
- )
- print response
- assert simplejson.loads(response.body) == 15
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json', 'Accept': 'application/json'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json', 'Accept': 'text/javascript'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json',
- 'Accept': 'text/xml'}
- )
- print response
- assert response.body == "<result>50</result>"
-
- def test_custom_clientside_error(self):
- response = self.app.post(
- "/divide",
- simplejson.dumps({'a': 5, 'b': 0}),
- {'Content-Type': 'application/json', 'Accept': 'application/json'},
- expect_errors=True
- )
- assert response.status_int == 400
- assert simplejson.loads(response.body) == {
- "debuginfo": None,
- "faultcode": "Server",
- "faultstring": "(400, 'Cannot divide by zero!')"
- }
-
- response = self.app.post(
- "/divide",
- simplejson.dumps({'a': 5, 'b': 0}),
- {'Content-Type': 'application/json', 'Accept': 'text/xml'},
- expect_errors=True
- )
- assert response.status_int == 400
- assert response.body == ("<error><faultcode>Server</faultcode>"
- "<faultstring>(400, 'Cannot divide by zero!')"
- "</faultstring><debuginfo /></error>")
-
- def test_soap_wsdl(self):
- ts = test_soap.TestSOAP('test_wsdl')
- ts.app = self.app
- ts.ws_path = '/ws/'
- ts.run()
- #wsdl = self.app.get('/ws/api.wsdl').body
- #print wsdl
- #assert 'multiply' in wsdl
-
- def test_soap_call(self):
- ts = test_soap.TestSOAP('test_wsdl')
- ts.app = self.app
- ts.ws_path = '/ws/'
-
- print ts.ws_path
- assert ts.call('multiply', a=5, b=10, _rt=int) == 50
-
- def test_scan_api_loops(self):
- class MyRoot(object):
- pass
-
- MyRoot.loop = MyRoot()
-
- root = MyRoot()
-
- api = list(wsmeext.tg1._scan_api(root))
- print(api)
-
- self.assertEqual(len(api), 0)
-
- def test_scan_api_maxlen(self):
- class ARoot(object):
- pass
-
- def make_subcontrollers(n):
- c = type('Controller%s' % n, (object,), {})
- return c
-
- c = ARoot
- for n in range(55):
- subc = make_subcontrollers(n)
- c.sub = subc()
- c = subc
- root = ARoot()
- self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root))
-
- def test_templates_content_type(self):
- self.assertEqual(
- "application/json",
- wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy')
- )
- self.assertEqual(
- "text/xml",
- wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy')
- )
diff --git a/tests/test_tg15.py b/tests/test_tg15.py
deleted file mode 100644
index 91609d2..0000000
--- a/tests/test_tg15.py
+++ /dev/null
@@ -1,177 +0,0 @@
-import wsmeext.tg15
-from wsme import WSRoot
-
-from turbogears.controllers import RootController
-import cherrypy
-
-from wsmeext.tests import test_soap
-
-import simplejson
-
-
-class Subcontroller(object):
- @wsmeext.tg15.wsexpose(int, int, int)
- def add(self, a, b):
- return a + b
-
-
-class Root(RootController):
- class UselessSubClass:
- # This class is here only to make sure wsmeext.tg1.scan_api
- # does its job properly
- pass
-
- sub = Subcontroller()
-
- ws = WSRoot(webpath='/ws')
- ws.addprotocol('soap',
- tns=test_soap.tns,
- typenamespace=test_soap.typenamespace,
- baseURL='/ws/'
- )
- ws = wsmeext.tg15.adapt(ws)
-
- @wsmeext.tg15.wsexpose(int)
- @wsmeext.tg15.wsvalidate(int, int)
- def multiply(self, a, b):
- return a * b
-
- @wsmeext.tg15.wsexpose(int)
- @wsmeext.tg15.wsvalidate(int, int)
- def divide(self, a, b):
- if b == 0:
- raise cherrypy.HTTPError(400, 'Cannot divide by zero!')
- return a / b
-
-
-from turbogears import testutil
-
-
-class TestController(testutil.TGTest):
- root = Root
-
-# def setUp(self):
-# "Tests the output of the index method"
-# self.app = testutil.make_app(self.root)
-# #print cherrypy.root
-# testutil.start_server()
-
-# def tearDown(self):
-# # implementation copied from turbogears.testutil.stop_server.
-# # The only change is that cherrypy.root is set to None
-# # AFTER stopTurbogears has been called so that wsmeext.tg15
-# # can correctly uninstall its filter.
-# if config.get("cp_started"):
-# cherrypy.server.stop()
-# config.update({"cp_started": False})
-#
-# if config.get("server_started"):
-# startup.stopTurboGears()
-# config.update({"server_started": False})
-
- def test_restcall(self):
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json', 'Accept': 'application/json'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json', 'Accept': 'text/javascript'}
- )
- print response
- assert simplejson.loads(response.body) == 50
-
- response = self.app.post("/multiply",
- simplejson.dumps({'a': 5, 'b': 10}),
- {'Content-Type': 'application/json',
- 'Accept': 'text/xml'}
- )
- print response
- assert response.body == "<result>50</result>"
-
- def test_custom_clientside_error(self):
- response = self.app.post(
- "/divide",
- simplejson.dumps({'a': 5, 'b': 0}),
- {'Content-Type': 'application/json', 'Accept': 'application/json'},
- expect_errors=True
- )
- assert response.status_int == 400
- assert simplejson.loads(response.body) == {
- "debuginfo": None,
- "faultcode": "Client",
- "faultstring": "(400, 'Cannot divide by zero!')"
- }
-
- response = self.app.post(
- "/divide",
- simplejson.dumps({'a': 5, 'b': 0}),
- {'Content-Type': 'application/json', 'Accept': 'text/xml'},
- expect_errors=True
- )
- assert response.status_int == 400
- assert response.body == ("<error><faultcode>Client</faultcode>"
- "<faultstring>(400, 'Cannot divide by zero!')"
- "</faultstring><debuginfo /></error>")
-
- def test_soap_wsdl(self):
- wsdl = self.app.get('/ws/api.wsdl').body
- print wsdl
- assert 'multiply' in wsdl
-
- def test_soap_call(self):
- ts = test_soap.TestSOAP('test_wsdl')
- ts.app = self.app
- ts.ws_path = '/ws/'
-
- print ts.ws_path
- assert ts.call('multiply', a=5, b=10, _rt=int) == 50
-
- def test_scan_api_loops(self):
- class MyRoot(object):
- pass
-
- MyRoot.loop = MyRoot()
-
- root = MyRoot()
-
- api = list(wsmeext.tg1._scan_api(root))
- print(api)
-
- self.assertEqual(len(api), 0)
-
- def test_scan_api_maxlen(self):
- class ARoot(object):
- pass
-
- def make_subcontrollers(n):
- c = type('Controller%s' % n, (object,), {})
- return c
-
- c = ARoot
- for n in range(55):
- subc = make_subcontrollers(n)
- c.sub = subc()
- c = subc
- root = ARoot()
- self.assertRaises(ValueError, list, wsmeext.tg1._scan_api(root))
-
- def test_templates_content_type(self):
- self.assertEqual(
- "application/json",
- wsmeext.tg1.AutoJSONTemplate().get_content_type('dummy')
- )
- self.assertEqual(
- "text/xml",
- wsmeext.tg1.AutoXMLTemplate().get_content_type('dummy')
- )
diff --git a/tox-tmpl.ini b/tox-tmpl.ini
deleted file mode 100644
index 692b570..0000000
--- a/tox-tmpl.ini
+++ /dev/null
@@ -1,141 +0,0 @@
-# content of: tox.ini , put in same dir as setup.py
-[tox]
-envlist = py27,py27-nolxml,pypy,cornice,cornice-py3,coverage,py36,py35,py36-nolxml,py35-nolxml,pecan-dev27,pecan-dev35,pecan-dev36,pep8
-
-[common]
-testtools=
- nose
- coverage < 3.99
- pbr
- webtest
-basedeps=
- transaction
- pecan
- cloud_sptheme
- Sphinx < 1.2.99
- Flask
- flask-restful
- SQLAlchemy<=0.7.99
-
-[axes]
-python=py27,py35,py36,pypy
-lxml=lxml*,nolxml
-json=json*,simplejson
-
-[axis:python]
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-
-commands=
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-
-[axis:python:py27]
-basepython=python2.7
-
-[axis:python:py35]
-basepython=python3.5
-
-[axis:python:py36]
-basepython=python3.6
-
-[axis:json:simplejson]
-deps=
- simplejson
-
-[axis:lxml:lxml]
-deps=
- lxml
-
-[testenv]
-setenv=
- COVERAGE_FILE=.coverage.{envname}
-
-[testenv:cornice]
-basepython=python2.7
-usedevelop=True
-deps=
- pbr
- nose
- webtest
- coverage < 3.99
- cornice
-commands=
- {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
- {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
-
-[testenv:cornice-py3]
-basepython = python3.6
-usedevelop = {[testenv:cornice]usedevelop}
-deps = {[testenv:cornice]deps}
-# disable hash randomization
-setenv =
- PYTHONHASHSEED=0
-commands =
- {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
- {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
-
-[testenv:pecan-dev-base]
-deps=
- {[common]testtools}
- transaction
- suds-jurko
- https://github.com/pecan/pecan/zipball/master
-
-[testenv:pecan-dev27]
-basepython=python2.7
-deps={[testenv:pecan-dev-base]deps}
-commands=
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:pecan-dev35]
-basepython=python3.5
-deps={[testenv:pecan-dev-base]deps}
-commands=
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:pecan-dev36]
-basepython=python3.6
-deps={[testenv:pecan-dev-base]deps}
-commands=
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:coverage]
-basepython=python3
-deps=
- coverage < 3.99
-setenv=
- COVERAGE_FILE=.coverage
-commands=
- {envbindir}/coverage erase
- {envbindir}/coverage combine
- {envbindir}/coverage xml wsme/*.py wsme/rest/*.py wsmeext/*.py
- {envbindir}/coverage report --show-missing wsme/*.py wsme/protocols/*.py wsmeext/*.py
-
-[testenv:doc]
-basepython=python3
-deps=
- cloud_sptheme
- Sphinx < 1.2.99
-
-changedir=
- doc
-
-commands=
- make clean ziphtml
-
-[testenv:pep8]
-basepython=python3
-deps = flake8
-commands = flake8 wsme wsmeext setup.py
-
-# Generic environment for running commands like packaging
-[testenv:venv]
-commands = {posargs}
-usedevelop=True
-deps =
- pbr
- oslo.config
- oslotest
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 2509ec4..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,406 +0,0 @@
-[tox]
-envlist = py27,py27-nolxml,pypy,cornice,cornice-py3,coverage,py36,py35,py36-nolxml,py35-nolxml,pecan-dev27,pecan-dev35,pecan-dev36,pep8
-
-[common]
-testtools =
- nose
- coverage < 3.99
- pbr
- webtest
-basedeps =
- transaction
- pecan
- cloud_sptheme
- Sphinx < 1.2.99
- Flask
- flask-restful
- SQLAlchemy<=0.7.99
-
-[testenv]
-setenv =
- COVERAGE_FILE=.coverage.{envname}
-
-[testenv:cornice]
-basepython = python2.7
-usedevelop = True
-deps =
- pbr
- nose
- webtest
- coverage < 3.99
- cornice
-commands =
- {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
- {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
-
-[testenv:cornice-py3]
-basepython = python3.6
-usedevelop = {[testenv:cornice]usedevelop}
-deps = {[testenv:cornice]deps}
-setenv =
- PYTHONHASHSEED=0
-commands =
- {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs}
- {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py
-
-[testenv:pecan-dev-base]
-deps =
- {[common]testtools}
- transaction
- suds-jurko
- https://github.com/pecan/pecan/zipball/master
-
-[testenv:pecan-dev27]
-basepython = python2.7
-deps = {[testenv:pecan-dev-base]deps}
-commands =
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:pecan-dev35]
-basepython = python3.5
-deps = {[testenv:pecan-dev-base]deps}
-commands =
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:pecan-dev36]
-basepython = python3.6
-deps = {[testenv:pecan-dev-base]deps}
-commands =
- {envbindir}/nosetests tests/pecantest --with-xunit --xunit-file nosetests-{envname}.xml --verbose {posargs}
-
-[testenv:coverage]
-basepython = python3
-deps =
- coverage < 3.99
-setenv =
- COVERAGE_FILE=.coverage
-commands =
- {envbindir}/coverage erase
- {envbindir}/coverage combine
- {envbindir}/coverage xml wsme/*.py wsme/rest/*.py wsmeext/*.py
- {envbindir}/coverage report --show-missing wsme/*.py wsme/protocols/*.py wsmeext/*.py
-
-[testenv:doc]
-basepython = python3
-deps =
- cloud_sptheme
- Sphinx < 1.2.99
-changedir =
- doc
-commands =
- make clean ziphtml
-
-[testenv:pep8]
-basepython = python3
-deps = flake8
-commands = flake8 wsme wsmeext setup.py
-
-[testenv:venv]
-commands = {posargs}
-usedevelop = True
-deps =
- pbr
- oslo.config
- oslotest
-
-[testenv:py27-lxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python2.7
-
-[testenv:py27]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python2.7
-
-[testenv:py27-lxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python2.7
-
-[testenv:py27-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python2.7
-
-[testenv:py27-nolxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python2.7
-
-[testenv:py27-nolxml]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python2.7
-
-[testenv:py27-nolxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- simplejson
-basepython = python2.7
-
-[testenv:py35-lxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python3.5
-
-[testenv:py35]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python3.5
-
-[testenv:py35-lxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python3.5
-
-[testenv:py35-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python3.5
-
-[testenv:py35-nolxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python3.5
-
-[testenv:py35-nolxml]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python3.5
-
-[testenv:py35-nolxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- simplejson
-basepython = python3.5
-
-[testenv:py36-lxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python3.6
-
-[testenv:py36]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-basepython = python3.6
-
-[testenv:py36-lxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python3.6
-
-[testenv:py36-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-basepython = python3.6
-
-[testenv:py36-nolxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python3.6
-
-[testenv:py36-nolxml]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-basepython = python3.6
-
-[testenv:py36-nolxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- simplejson
-basepython = python3.6
-
-[testenv:pypy-lxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-
-[testenv:pypy]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
-
-[testenv:pypy-lxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-
-[testenv:pypy-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- lxml
- simplejson
-
-[testenv:pypy-nolxml-json]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-
-[testenv:pypy-nolxml]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
-
-[testenv:pypy-nolxml-simplejson]
-commands =
- {envbindir}/coverage run {envbindir}/nosetests --nologcapture --with-xunit --xunit-file nosetests-{envname}.xml wsme/tests wsmeext/tests tests/pecantest tests/test_sphinxext.py tests/test_flask.py --verbose {posargs}
- {envbindir}/coverage report --show-missing wsme/*.py wsme/rest/*.py wsmeext/*.py
-deps =
- {[common]testtools}
- {[common]basedeps}
- suds-jurko
- simplejson
-
diff --git a/toxgen.py b/toxgen.py
deleted file mode 100644
index 862c80f..0000000
--- a/toxgen.py
+++ /dev/null
@@ -1,144 +0,0 @@
-"""
-Produce a tox.ini file from a template config file.
-
-The template config file is a standard tox.ini file with additional sections.
-Theses sections will be combined to create new testenv: sections if they do
-not exists yet.
-
-See REAME.rst for more detail.
-"""
-
-import itertools
-import collections
-import optparse
-
-try:
- from configparser import ConfigParser
-except:
- from ConfigParser import ConfigParser # noqa
-
-
-parser = optparse.OptionParser(epilog=__doc__)
-parser.add_option('-i', '--input', dest='input',
- default='tox-tmpl.ini', metavar='FILE')
-parser.add_option('-o', '--output', dest='output',
- default='tox.ini', metavar='FILE')
-
-
-class AxisItem(object):
- def __init__(self, axis, name, config):
- self.axis = axis
- self.isdefault = name[-1] == '*'
- self.name = name[:-1] if self.isdefault else name
- self.load(config)
-
- def load(self, config):
- sectionname = 'axis:%s:%s' % (self.axis.name, self.name)
- if config.has_section(sectionname):
- self.options = collections.OrderedDict(config.items(sectionname))
- else:
- self.options = collections.OrderedDict()
-
- for name, value in self.axis.defaults.items():
- if name not in self.options:
- self.options[name] = value
-
-
-class Axis(object):
- def __init__(self, name, config):
- self.name = name
- self.load(config)
-
- def load(self, config):
- self.items = collections.OrderedDict()
- values = config.get('axes', self.name).split(',')
- if config.has_section('axis:%s' % self.name):
- self.defaults = collections.OrderedDict(
- config.items('axis:%s' % self.name)
- )
- else:
- self.defaults = {}
- for value in values:
- self.items[value.strip('*')] = AxisItem(self, value, config)
-
-
-def render(incfg):
- axes = collections.OrderedDict()
-
- if incfg.has_section('axes'):
- for axis in incfg.options('axes'):
- axes[axis] = Axis(axis, incfg)
-
- out = ConfigParser()
- for section in incfg.sections():
- if section == 'axes' or section.startswith('axis:'):
- continue
- out.add_section(section)
- for name, value in incfg.items(section):
- out.set(section, name, value)
-
- for combination in itertools.product(
- *[axis.items.keys() for axis in axes.values()]):
- options = collections.OrderedDict()
-
- section_name = (
- 'testenv:' + '-'.join([item for item in combination if item])
- )
- section_alt_name = (
- 'testenv:' + '-'.join([
- itemname
- for axis, itemname in zip(axes.values(), combination)
- if itemname and not axis.items[itemname].isdefault
- ])
- )
- if section_alt_name == section_name:
- section_alt_name = None
-
- axes_items = [
- '%s:%s' % (axis, itemname)
- for axis, itemname in zip(axes, combination)
- ]
-
- for axis, itemname in zip(axes.values(), combination):
- axis_options = axis.items[itemname].options
- if 'constraints' in axis_options:
- constraints = axis_options['constraints'].split('\n')
- for c in constraints:
- if c.startswith('!') and c[1:] in axes_items:
- continue
- for name, value in axis_options.items():
- if name in options:
- options[name] += value
- else:
- options[name] = value
-
- constraints = options.pop('constraints', '').split('\n')
- neg_constraints = [c[1:] for c in constraints if c and c[0] == '!']
- if not set(neg_constraints).isdisjoint(axes_items):
- continue
-
- if not out.has_section(section_name):
- out.add_section(section_name)
-
- if (section_alt_name and not out.has_section(section_alt_name)):
- out.add_section(section_alt_name)
-
- for name, value in reversed(options.items()):
- if not out.has_option(section_name, name):
- out.set(section_name, name, value)
- if section_alt_name and not out.has_option(section_alt_name, name):
- out.set(section_alt_name, name, value)
-
- return out
-
-
-def main():
- options, args = parser.parse_args()
- tmpl = ConfigParser()
- tmpl.read(options.input)
- with open(options.output, 'wb') as outfile:
- render(tmpl).write(outfile)
-
-
-if __name__ == '__main__':
- main()
diff --git a/wsme/__init__.py b/wsme/__init__.py
deleted file mode 100644
index 35109ec..0000000
--- a/wsme/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from wsme.api import signature
-from wsme.rest import expose, validate
-from wsme.root import WSRoot
-from wsme.types import wsattr, wsproperty, Unset
-
-__all__ = [
- 'expose', 'validate', 'signature',
- 'WSRoot',
- 'wsattr', 'wsproperty', 'Unset'
-]
diff --git a/wsme/api.py b/wsme/api.py
deleted file mode 100644
index 67263a0..0000000
--- a/wsme/api.py
+++ /dev/null
@@ -1,237 +0,0 @@
-import traceback
-import functools
-import inspect
-import logging
-import six
-
-import wsme.exc
-import wsme.types
-
-from wsme import utils
-
-log = logging.getLogger(__name__)
-
-
-def iswsmefunction(f):
- return hasattr(f, '_wsme_definition')
-
-
-def wrapfunc(f):
- @functools.wraps(f)
- def wrapper(*args, **kwargs):
- return f(*args, **kwargs)
- wrapper._wsme_original_func = f
- return wrapper
-
-
-def getargspec(f):
- f = getattr(f, '_wsme_original_func', f)
- return inspect.getargspec(f)
-
-
-class FunctionArgument(object):
- """
- An argument definition of an api entry
- """
- def __init__(self, name, datatype, mandatory, default):
- #: argument name
- self.name = name
-
- #: Data type
- self.datatype = datatype
-
- #: True if the argument is mandatory
- self.mandatory = mandatory
-
- #: Default value if argument is omitted
- self.default = default
-
- def resolve_type(self, registry):
- self.datatype = registry.resolve_type(self.datatype)
-
-
-class FunctionDefinition(object):
- """
- An api entry definition
- """
- def __init__(self, func):
- #: Function name
- self.name = func.__name__
-
- #: Function documentation
- self.doc = func.__doc__
-
- #: Return type
- self.return_type = None
-
- #: The function arguments (list of :class:`FunctionArgument`)
- self.arguments = []
-
- #: If the body carry the datas of a single argument, its type
- self.body_type = None
-
- #: Status code
- self.status_code = 200
-
- #: True if extra arguments should be ignored, NOT inserted in
- #: the kwargs of the function and not raise UnknownArgument
- #: exceptions
- self.ignore_extra_args = False
-
- #: name of the function argument to pass the host request object.
- #: Should be set by using the :class:`wsme.types.HostRequest` type
- #: in the function @\ :function:`signature`
- self.pass_request = False
-
- #: Dictionnary of protocol-specific options.
- self.extra_options = None
-
- @staticmethod
- def get(func):
- """
- Returns the :class:`FunctionDefinition` of a method.
- """
- if not hasattr(func, '_wsme_definition'):
- fd = FunctionDefinition(func)
- func._wsme_definition = fd
-
- return func._wsme_definition
-
- def get_arg(self, name):
- """
- Returns a :class:`FunctionArgument` from its name
- """
- for arg in self.arguments:
- if arg.name == name:
- return arg
- return None
-
- def resolve_types(self, registry):
- self.return_type = registry.resolve_type(self.return_type)
- self.body_type = registry.resolve_type(self.body_type)
- for arg in self.arguments:
- arg.resolve_type(registry)
-
- def set_options(self, body=None, ignore_extra_args=False, status_code=200,
- rest_content_types=('json', 'xml'), **extra_options):
- self.body_type = body
- self.status_code = status_code
- self.ignore_extra_args = ignore_extra_args
- self.rest_content_types = rest_content_types
- self.extra_options = extra_options
-
- def set_arg_types(self, argspec, arg_types):
- args, varargs, keywords, defaults = argspec
- if args[0] == 'self':
- args = args[1:]
- arg_types = list(arg_types)
- if self.body_type is not None:
- arg_types.append(self.body_type)
- for i, argname in enumerate(args):
- datatype = arg_types[i]
- mandatory = defaults is None or i < (len(args) - len(defaults))
- default = None
- if not mandatory:
- default = defaults[i - (len(args) - len(defaults))]
- if datatype is wsme.types.HostRequest:
- self.pass_request = argname
- else:
- self.arguments.append(FunctionArgument(argname, datatype,
- mandatory, default))
-
-
-class signature(object):
-
- """Decorator that specify the argument types of an exposed function.
-
- :param return_type: Type of the value returned by the function
- :param argN: Type of the Nth argument
- :param body: If the function takes a final argument that is supposed to be
- the request body by itself, its type.
- :param status_code: HTTP return status code of the function.
- :param ignore_extra_args: Allow extra/unknow arguments (default to False)
-
- Most of the time this decorator is not supposed to be used directly,
- unless you are not using WSME on top of another framework.
-
- If an adapter is used, it will provide either a specialised version of this
- decororator, either a new decorator named @wsexpose that takes the same
- parameters (it will in addition expose the function, hence its name).
- """
-
- def __init__(self, *types, **options):
- self.return_type = types[0] if types else None
- self.arg_types = []
- if len(types) > 1:
- self.arg_types.extend(types[1:])
- if 'body' in options:
- self.arg_types.append(options['body'])
- self.wrap = options.pop('wrap', False)
- self.options = options
-
- def __call__(self, func):
- argspec = getargspec(func)
- if self.wrap:
- func = wrapfunc(func)
- fd = FunctionDefinition.get(func)
- if fd.extra_options is not None:
- raise ValueError("This function is already exposed")
- fd.return_type = self.return_type
- fd.set_options(**self.options)
- if self.arg_types:
- fd.set_arg_types(argspec, self.arg_types)
- return func
-
-
-sig = signature
-
-
-class Response(object):
- """
- Object to hold the "response" from a view function
- """
- def __init__(self, obj, status_code=None, error=None,
- return_type=wsme.types.Unset):
- #: Store the result object from the view
- self.obj = obj
-
- #: Store an optional status_code
- self.status_code = status_code
-
- #: Return error details
- #: Must be a dictionnary with the following keys: faultcode,
- #: faultstring and an optional debuginfo
- self.error = error
-
- #: Return type
- #: Type of the value returned by the function
- #: If the return type is wsme.types.Unset it will be ignored
- #: and the default return type will prevail.
- self.return_type = return_type
-
-
-def format_exception(excinfo, debug=False):
- """Extract informations that can be sent to the client."""
- error = excinfo[1]
- code = getattr(error, 'code', None)
- if code and utils.is_valid_code(code) and utils.is_client_error(code):
- faultstring = (error.faultstring if hasattr(error, 'faultstring')
- else six.text_type(error))
- r = dict(faultcode="Client",
- faultstring=faultstring)
- log.debug("Client-side error: %s" % r['faultstring'])
- r['debuginfo'] = None
- return r
- else:
- faultstring = six.text_type(error)
- debuginfo = "\n".join(traceback.format_exception(*excinfo))
-
- log.error('Server-side error: "%s". Detail: \n%s' % (
- faultstring, debuginfo))
-
- r = dict(faultcode="Server", faultstring=faultstring)
- if debug:
- r['debuginfo'] = debuginfo
- else:
- r['debuginfo'] = None
- return r
diff --git a/wsme/exc.py b/wsme/exc.py
deleted file mode 100644
index 81dcbf8..0000000
--- a/wsme/exc.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import six
-
-from wsme.utils import _
-
-
-class ClientSideError(RuntimeError):
- def __init__(self, msg=None, status_code=400):
- self.msg = msg
- self.code = status_code
- super(ClientSideError, self).__init__(self.faultstring)
-
- @property
- def faultstring(self):
- if self.msg is None:
- return str(self)
- elif isinstance(self.msg, six.text_type):
- return self.msg
- else:
- return six.u(self.msg)
-
-
-class InvalidInput(ClientSideError):
- def __init__(self, fieldname, value, msg=''):
- self.fieldname = fieldname
- self.value = value
- super(InvalidInput, self).__init__(msg)
-
- @property
- def faultstring(self):
- return _(six.u(
- "Invalid input for field/attribute %s. Value: '%s'. %s")
- ) % (self.fieldname, self.value, self.msg)
-
-
-class MissingArgument(ClientSideError):
- def __init__(self, argname, msg=''):
- self.argname = argname
- super(MissingArgument, self).__init__(msg)
-
- @property
- def faultstring(self):
- return _(six.u('Missing argument: "%s"%s')) % (
- self.argname, self.msg and ": " + self.msg or "")
-
-
-class UnknownArgument(ClientSideError):
- def __init__(self, argname, msg=''):
- self.argname = argname
- super(UnknownArgument, self).__init__(msg)
-
- @property
- def faultstring(self):
- return _(six.u('Unknown argument: "%s"%s')) % (
- self.argname, self.msg and ": " + self.msg or "")
-
-
-class UnknownFunction(ClientSideError):
- def __init__(self, name):
- self.name = name
- super(UnknownFunction, self).__init__()
-
- @property
- def faultstring(self):
- return _(six.u("Unknown function name: %s")) % (self.name)
-
-
-class UnknownAttribute(ClientSideError):
- def __init__(self, fieldname, attributes, msg=''):
- self.fieldname = fieldname
- self.attributes = attributes
- self.msg = msg
- super(UnknownAttribute, self).__init__(self.msg)
-
- @property
- def faultstring(self):
- error = _("Unknown attribute for argument %(argn)s: %(attrs)s")
- if len(self.attributes) > 1:
- error = _("Unknown attributes for argument %(argn)s: %(attrs)s")
- str_attrs = ", ".join(self.attributes)
- return error % {'argn': self.fieldname, 'attrs': str_attrs}
-
- def add_fieldname(self, name):
- """Add a fieldname to concatenate the full name.
-
- Add a fieldname so that the whole hierarchy is displayed. Successive
- calls to this method will prepend ``name`` to the hierarchy of names.
- """
- if self.fieldname is not None:
- self.fieldname = "{}.{}".format(name, self.fieldname)
- else:
- self.fieldname = name
- super(UnknownAttribute, self).__init__(self.msg)
diff --git a/wsme/protocol.py b/wsme/protocol.py
deleted file mode 100644
index b0107ab..0000000
--- a/wsme/protocol.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import weakref
-
-import pkg_resources
-
-from wsme.exc import ClientSideError
-
-
-__all__ = [
- 'CallContext',
-
- 'register_protocol', 'getprotocol',
-]
-
-registered_protocols = {}
-
-
-def _cfg(f):
- cfg = getattr(f, '_cfg', None)
- if cfg is None:
- f._cfg = cfg = {}
- return cfg
-
-
-class expose(object):
- def __init__(self, path, content_type):
- self.path = path
- self.content_type = content_type
-
- def __call__(self, func):
- func.exposed = True
- cfg = _cfg(func)
- cfg['content-type'] = self.content_type
- cfg.setdefault('paths', []).append(self.path)
- return func
-
-
-class CallContext(object):
- def __init__(self, request):
- self._request = weakref.ref(request)
- self.path = None
-
- self.func = None
- self.funcdef = None
-
- @property
- def request(self):
- return self._request()
-
-
-class ObjectDict(object):
- def __init__(self, obj):
- self.obj = obj
-
- def __getitem__(self, name):
- return getattr(self.obj, name)
-
-
-class Protocol(object):
- name = None
- displayname = None
- content_types = []
-
- def resolve_path(self, path):
- if '$' in path:
- from string import Template
- s = Template(path)
- path = s.substitute(ObjectDict(self))
- return path
-
- def iter_routes(self):
- for attrname in dir(self):
- attr = getattr(self, attrname)
- if getattr(attr, 'exposed', False):
- for path in _cfg(attr)['paths']:
- yield self.resolve_path(path), attr
-
- def accept(self, request):
- return request.headers.get('Content-Type') in self.content_types
-
- def iter_calls(self, request):
- pass
-
- def extract_path(self, context):
- pass
-
- def read_arguments(self, context):
- pass
-
- def encode_result(self, context, result):
- pass
-
- def encode_sample_value(self, datatype, value, format=False):
- return ('none', 'N/A')
-
- def encode_sample_params(self, params, format=False):
- return ('none', 'N/A')
-
- def encode_sample_result(self, datatype, value, format=False):
- return ('none', 'N/A')
-
-
-def register_protocol(protocol):
- registered_protocols[protocol.name] = protocol
-
-
-def getprotocol(name, **options):
- protocol_class = registered_protocols.get(name)
- if protocol_class is None:
- for entry_point in pkg_resources.iter_entry_points(
- 'wsme.protocols', name):
- if entry_point.name == name:
- protocol_class = entry_point.load()
- if protocol_class is None:
- raise ValueError("Cannot find protocol '%s'" % name)
- registered_protocols[name] = protocol_class
- return protocol_class(**options)
-
-
-def media_type_accept(request, content_types):
- """Validate media types against request.method.
-
- When request.method is GET or HEAD compare with the Accept header.
- When request.method is POST, PUT or PATCH compare with the Content-Type
- header.
- When request.method is DELETE media type is irrelevant, so return True.
- """
- if request.method in ['GET', 'HEAD']:
- if request.accept:
- if request.accept.best_match(content_types):
- return True
- error_message = ('Unacceptable Accept type: %s not in %s'
- % (request.accept, content_types))
- raise ClientSideError(error_message, status_code=406)
- elif request.method in ['PUT', 'POST', 'PATCH']:
- content_type = request.headers.get('Content-Type')
- if content_type:
- for ct in content_types:
- if request.headers.get('Content-Type', '').startswith(ct):
- return True
- error_message = ('Unacceptable Content-Type: %s not in %s'
- % (content_type, content_types))
- raise ClientSideError(error_message, status_code=415)
- else:
- raise ClientSideError('missing Content-Type header')
- elif request.method in ['DELETE']:
- return True
- return False
diff --git a/wsme/rest/__init__.py b/wsme/rest/__init__.py
deleted file mode 100644
index f35d6a9..0000000
--- a/wsme/rest/__init__.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import inspect
-import wsme.api
-
-APIPATH_MAXLEN = 20
-
-
-class expose(object):
- def __init__(self, *args, **kwargs):
- self.signature = wsme.api.signature(*args, **kwargs)
-
- def __call__(self, func):
- return self.signature(func)
-
- @classmethod
- def with_method(cls, method, *args, **kwargs):
- kwargs['method'] = method
- return cls(*args, **kwargs)
-
- @classmethod
- def get(cls, *args, **kwargs):
- return cls.with_method('GET', *args, **kwargs)
-
- @classmethod
- def post(cls, *args, **kwargs):
- return cls.with_method('POST', *args, **kwargs)
-
- @classmethod
- def put(cls, *args, **kwargs):
- return cls.with_method('PUT', *args, **kwargs)
-
- @classmethod
- def delete(cls, *args, **kwargs):
- return cls.with_method('DELETE', *args, **kwargs)
-
-
-class validate(object):
- """
- Decorator that define the arguments types of a function.
-
-
- Example::
-
- class MyController(object):
- @expose(str)
- @validate(datetime.date, datetime.time)
- def format(self, d, t):
- return d.isoformat() + ' ' + t.isoformat()
- """
- def __init__(self, *param_types):
- self.param_types = param_types
-
- def __call__(self, func):
- argspec = wsme.api.getargspec(func)
- fd = wsme.api.FunctionDefinition.get(func)
- fd.set_arg_types(argspec, self.param_types)
- return func
-
-
-def scan_api(controller, path=[], objects=[]):
- """
- Recursively iterate a controller api entries.
- """
- for name in dir(controller):
- if name.startswith('_'):
- continue
- a = getattr(controller, name)
- if a in objects:
- continue
- if inspect.ismethod(a):
- if wsme.api.iswsmefunction(a):
- yield path + [name], a, []
- elif inspect.isclass(a):
- continue
- else:
- if len(path) > APIPATH_MAXLEN:
- raise ValueError("Path is too long: " + str(path))
- for i in scan_api(a, path + [name], objects + [a]):
- yield i
diff --git a/wsme/rest/args.py b/wsme/rest/args.py
deleted file mode 100644
index 9dc16c7..0000000
--- a/wsme/rest/args.py
+++ /dev/null
@@ -1,310 +0,0 @@
-import cgi
-import datetime
-import re
-
-from simplegeneric import generic
-
-from wsme.exc import ClientSideError, UnknownArgument, InvalidInput
-
-from wsme.types import iscomplex, list_attributes, Unset
-from wsme.types import UserType, ArrayType, DictType, File
-from wsme.utils import parse_isodate, parse_isotime, parse_isodatetime
-import wsme.runtime
-
-from six import moves
-
-ARRAY_MAX_SIZE = 1000
-
-
-@generic
-def from_param(datatype, value):
- return datatype(value) if value is not None else None
-
-
-@from_param.when_object(datetime.date)
-def date_from_param(datatype, value):
- return parse_isodate(value) if value else None
-
-
-@from_param.when_object(datetime.time)
-def time_from_param(datatype, value):
- return parse_isotime(value) if value else None
-
-
-@from_param.when_object(datetime.datetime)
-def datetime_from_param(datatype, value):
- return parse_isodatetime(value) if value else None
-
-
-@from_param.when_object(File)
-def filetype_from_param(datatype, value):
- if isinstance(value, cgi.FieldStorage):
- return File(fieldstorage=value)
- return File(content=value)
-
-
-@from_param.when_type(UserType)
-def usertype_from_param(datatype, value):
- return datatype.frombasetype(
- from_param(datatype.basetype, value))
-
-
-@from_param.when_type(ArrayType)
-def array_from_param(datatype, value):
- if value is None:
- return value
- return [
- from_param(datatype.item_type, item)
- for item in value
- ]
-
-
-@generic
-def from_params(datatype, params, path, hit_paths):
- if iscomplex(datatype) and datatype is not File:
- objfound = False
- for key in params:
- if key.startswith(path + '.'):
- objfound = True
- break
- if objfound:
- r = datatype()
- for attrdef in list_attributes(datatype):
- value = from_params(
- attrdef.datatype,
- params, '%s.%s' % (path, attrdef.key), hit_paths
- )
- if value is not Unset:
- setattr(r, attrdef.key, value)
- return r
- else:
- if path in params:
- hit_paths.add(path)
- return from_param(datatype, params[path])
- return Unset
-
-
-@from_params.when_type(ArrayType)
-def array_from_params(datatype, params, path, hit_paths):
- if hasattr(params, 'getall'):
- # webob multidict
- def getall(params, path):
- return params.getall(path)
- elif hasattr(params, 'getlist'):
- # werkzeug multidict
- def getall(params, path): # noqa
- return params.getlist(path)
- if path in params:
- hit_paths.add(path)
- return [
- from_param(datatype.item_type, value)
- for value in getall(params, path)]
-
- if iscomplex(datatype.item_type):
- attributes = set()
- r = re.compile(r'^%s\.(?P<attrname>[^\.])' % re.escape(path))
- for p in params.keys():
- m = r.match(p)
- if m:
- attributes.add(m.group('attrname'))
- if attributes:
- value = []
- for attrdef in list_attributes(datatype.item_type):
- attrpath = '%s.%s' % (path, attrdef.key)
- hit_paths.add(attrpath)
- attrvalues = getall(params, attrpath)
- if len(value) < len(attrvalues):
- value[-1:] = [
- datatype.item_type()
- for i in moves.range(len(attrvalues) - len(value))
- ]
- for i, attrvalue in enumerate(attrvalues):
- setattr(
- value[i],
- attrdef.key,
- from_param(attrdef.datatype, attrvalue)
- )
- return value
-
- indexes = set()
- r = re.compile(r'^%s\[(?P<index>\d+)\]' % re.escape(path))
-
- for p in params.keys():
- m = r.match(p)
- if m:
- indexes.add(int(m.group('index')))
-
- if not indexes:
- return Unset
-
- indexes = list(indexes)
- indexes.sort()
-
- return [from_params(datatype.item_type, params,
- '%s[%s]' % (path, index), hit_paths)
- for index in indexes]
-
-
-@from_params.when_type(DictType)
-def dict_from_params(datatype, params, path, hit_paths):
-
- keys = set()
- r = re.compile(r'^%s\[(?P<key>[a-zA-Z0-9_\.]+)\]' % re.escape(path))
-
- for p in params.keys():
- m = r.match(p)
- if m:
- keys.add(from_param(datatype.key_type, m.group('key')))
-
- if not keys:
- return Unset
-
- return dict((
- (key, from_params(datatype.value_type,
- params, '%s[%s]' % (path, key), hit_paths))
- for key in keys))
-
-
-@from_params.when_type(UserType)
-def usertype_from_params(datatype, params, path, hit_paths):
- value = from_params(datatype.basetype, params, path, hit_paths)
- if value is not Unset:
- return datatype.frombasetype(value)
- return Unset
-
-
-def args_from_args(funcdef, args, kwargs):
- newargs = []
- for argdef, arg in zip(funcdef.arguments[:len(args)], args):
- try:
- newargs.append(from_param(argdef.datatype, arg))
- except Exception as e:
- if isinstance(argdef.datatype, UserType):
- datatype_name = argdef.datatype.name
- elif isinstance(argdef.datatype, type):
- datatype_name = argdef.datatype.__name__
- else:
- datatype_name = argdef.datatype.__class__.__name__
- raise InvalidInput(
- argdef.name,
- arg,
- "unable to convert to %(datatype)s. Error: %(error)s" % {
- 'datatype': datatype_name, 'error': e})
- newkwargs = {}
- for argname, value in kwargs.items():
- newkwargs[argname] = from_param(
- funcdef.get_arg(argname).datatype, value
- )
- return newargs, newkwargs
-
-
-def args_from_params(funcdef, params):
- kw = {}
- hit_paths = set()
- for argdef in funcdef.arguments:
- value = from_params(
- argdef.datatype, params, argdef.name, hit_paths)
- if value is not Unset:
- kw[argdef.name] = value
- paths = set(params.keys())
- unknown_paths = paths - hit_paths
- if '__body__' in unknown_paths:
- unknown_paths.remove('__body__')
- if not funcdef.ignore_extra_args and unknown_paths:
- raise UnknownArgument(', '.join(unknown_paths))
- return [], kw
-
-
-def args_from_body(funcdef, body, mimetype):
- from wsme.rest import json as restjson
- from wsme.rest import xml as restxml
-
- if funcdef.body_type is not None:
- datatypes = {funcdef.arguments[-1].name: funcdef.body_type}
- else:
- datatypes = dict(((a.name, a.datatype) for a in funcdef.arguments))
-
- if not body:
- return (), {}
- if mimetype == "application/x-www-form-urlencoded":
- # the parameters should have been parsed in params
- return (), {}
- elif mimetype in restjson.accept_content_types:
- dataformat = restjson
- elif mimetype in restxml.accept_content_types:
- dataformat = restxml
- else:
- raise ClientSideError("Unknown mimetype: %s" % mimetype,
- status_code=415)
-
- try:
- kw = dataformat.parse(
- body, datatypes, bodyarg=funcdef.body_type is not None
- )
- except UnknownArgument:
- if not funcdef.ignore_extra_args:
- raise
- kw = {}
-
- return (), kw
-
-
-def combine_args(funcdef, akw, allow_override=False):
- newargs, newkwargs = [], {}
- for args, kwargs in akw:
- for i, arg in enumerate(args):
- n = funcdef.arguments[i].name
- if not allow_override and n in newkwargs:
- raise ClientSideError(
- "Parameter %s was given several times" % n)
- newkwargs[n] = arg
- for name, value in kwargs.items():
- n = str(name)
- if not allow_override and n in newkwargs:
- raise ClientSideError(
- "Parameter %s was given several times" % n)
- newkwargs[n] = value
- return newargs, newkwargs
-
-
-def get_args(funcdef, args, kwargs, params, form, body, mimetype):
- """Combine arguments from :
- * the host framework args and kwargs
- * the request params
- * the request body
-
- Note that the host framework args and kwargs can be overridden
- by arguments from params of body
- """
- # get the body from params if not given directly
- if not body and '__body__' in params:
- body = params['__body__']
-
- # extract args from the host args and kwargs
- from_args = args_from_args(funcdef, args, kwargs)
-
- # extract args from the request parameters
- from_params = args_from_params(funcdef, params)
-
- # extract args from the form parameters
- if form:
- from_form_params = args_from_params(funcdef, form)
- else:
- from_form_params = (), {}
-
- # extract args from the request body
- from_body = args_from_body(funcdef, body, mimetype)
-
- # combine params and body arguments
- from_params_and_body = combine_args(
- funcdef,
- (from_params, from_form_params, from_body)
- )
-
- args, kwargs = combine_args(
- funcdef,
- (from_args, from_params_and_body),
- allow_override=True
- )
- wsme.runtime.check_arguments(funcdef, args, kwargs)
- return args, kwargs
diff --git a/wsme/rest/json.py b/wsme/rest/json.py
deleted file mode 100644
index 48bd082..0000000
--- a/wsme/rest/json.py
+++ /dev/null
@@ -1,328 +0,0 @@
-"""REST+Json protocol implementation."""
-from __future__ import absolute_import
-import datetime
-import decimal
-
-import six
-
-from simplegeneric import generic
-
-import wsme.exc
-import wsme.types
-from wsme.types import Unset
-import wsme.utils
-
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-
-content_type = 'application/json'
-accept_content_types = [
- content_type,
- 'text/javascript',
- 'application/javascript'
-]
-ENUM_TRUE = ('true', 't', 'yes', 'y', 'on', '1')
-ENUM_FALSE = ('false', 'f', 'no', 'n', 'off', '0')
-
-
-@generic
-def tojson(datatype, value):
- """
- A generic converter from python to jsonify-able datatypes.
-
- If a non-complex user specific type is to be used in the api,
- a specific tojson should be added::
-
- from wsme.protocol.restjson import tojson
-
- myspecialtype = object()
-
- @tojson.when_object(myspecialtype)
- def myspecialtype_tojson(datatype, value):
- return str(value)
- """
- if value is None:
- return None
- if wsme.types.iscomplex(datatype):
- d = dict()
- for attr in wsme.types.list_attributes(datatype):
- attr_value = getattr(value, attr.key)
- if attr_value is not Unset:
- d[attr.name] = tojson(attr.datatype, attr_value)
- return d
- elif wsme.types.isusertype(datatype):
- return tojson(datatype.basetype, datatype.tobasetype(value))
- return value
-
-
-@tojson.when_object(wsme.types.bytes)
-def bytes_tojson(datatype, value):
- if value is None:
- return None
- return value.decode('ascii')
-
-
-@tojson.when_type(wsme.types.ArrayType)
-def array_tojson(datatype, value):
- if value is None:
- return None
- return [tojson(datatype.item_type, item) for item in value]
-
-
-@tojson.when_type(wsme.types.DictType)
-def dict_tojson(datatype, value):
- if value is None:
- return None
- return dict((
- (tojson(datatype.key_type, item[0]),
- tojson(datatype.value_type, item[1]))
- for item in value.items()
- ))
-
-
-@tojson.when_object(decimal.Decimal)
-def decimal_tojson(datatype, value):
- if value is None:
- return None
- return str(value)
-
-
-@tojson.when_object(datetime.date)
-def date_tojson(datatype, value):
- if value is None:
- return None
- return value.isoformat()
-
-
-@tojson.when_object(datetime.time)
-def time_tojson(datatype, value):
- if value is None:
- return None
- return value.isoformat()
-
-
-@tojson.when_object(datetime.datetime)
-def datetime_tojson(datatype, value):
- if value is None:
- return None
- return value.isoformat()
-
-
-@generic
-def fromjson(datatype, value):
- """A generic converter from json base types to python datatype.
-
- If a non-complex user specific type is to be used in the api,
- a specific fromjson should be added::
-
- from wsme.protocol.restjson import fromjson
-
- class MySpecialType(object):
- pass
-
- @fromjson.when_object(MySpecialType)
- def myspecialtype_fromjson(datatype, value):
- return MySpecialType(value)
- """
- if value is None:
- return None
- if wsme.types.iscomplex(datatype):
- obj = datatype()
- attributes = wsme.types.list_attributes(datatype)
-
- # Here we check that all the attributes in the value are also defined
- # in our type definition, otherwise we raise an Error.
- v_keys = set(value.keys())
- a_keys = set(adef.name for adef in attributes)
- if not v_keys <= a_keys:
- raise wsme.exc.UnknownAttribute(None, v_keys - a_keys)
-
- for attrdef in attributes:
- if attrdef.name in value:
- try:
- val_fromjson = fromjson(attrdef.datatype,
- value[attrdef.name])
- except wsme.exc.UnknownAttribute as e:
- e.add_fieldname(attrdef.name)
- raise
- if getattr(attrdef, 'readonly', False):
- raise wsme.exc.InvalidInput(attrdef.name, val_fromjson,
- "Cannot set read only field.")
- setattr(obj, attrdef.key, val_fromjson)
- elif attrdef.mandatory:
- raise wsme.exc.InvalidInput(attrdef.name, None,
- "Mandatory field missing.")
-
- return wsme.types.validate_value(datatype, obj)
- elif wsme.types.isusertype(datatype):
- value = datatype.frombasetype(
- fromjson(datatype.basetype, value))
- return value
-
-
-@fromjson.when_type(wsme.types.ArrayType)
-def array_fromjson(datatype, value):
- if value is None:
- return None
- if not isinstance(value, list):
- raise ValueError("Value not a valid list: %s" % value)
- return [fromjson(datatype.item_type, item) for item in value]
-
-
-@fromjson.when_type(wsme.types.DictType)
-def dict_fromjson(datatype, value):
- if value is None:
- return None
- if not isinstance(value, dict):
- raise ValueError("Value not a valid dict: %s" % value)
- return dict((
- (fromjson(datatype.key_type, item[0]),
- fromjson(datatype.value_type, item[1]))
- for item in value.items()))
-
-
-@fromjson.when_object(six.binary_type)
-def str_fromjson(datatype, value):
- if (isinstance(value, six.string_types) or
- isinstance(value, six.integer_types) or
- isinstance(value, float)):
- return six.text_type(value).encode('utf8')
-
-
-@fromjson.when_object(wsme.types.text)
-def text_fromjson(datatype, value):
- if value is not None and isinstance(value, wsme.types.bytes):
- return wsme.types.text(value)
- return value
-
-
-@fromjson.when_object(*six.integer_types + (float,))
-def numeric_fromjson(datatype, value):
- """Convert string object to built-in types int, long or float."""
- if value is None:
- return None
- return datatype(value)
-
-
-@fromjson.when_object(bool)
-def bool_fromjson(datatype, value):
- """Convert to bool, restricting strings to just unambiguous values."""
- if value is None:
- return None
- if isinstance(value, six.integer_types + (bool,)):
- return bool(value)
- if value in ENUM_TRUE:
- return True
- if value in ENUM_FALSE:
- return False
- raise ValueError("Value not an unambiguous boolean: %s" % value)
-
-
-@fromjson.when_object(decimal.Decimal)
-def decimal_fromjson(datatype, value):
- if value is None:
- return None
- return decimal.Decimal(value)
-
-
-@fromjson.when_object(datetime.date)
-def date_fromjson(datatype, value):
- if value is None:
- return None
- return wsme.utils.parse_isodate(value)
-
-
-@fromjson.when_object(datetime.time)
-def time_fromjson(datatype, value):
- if value is None:
- return None
- return wsme.utils.parse_isotime(value)
-
-
-@fromjson.when_object(datetime.datetime)
-def datetime_fromjson(datatype, value):
- if value is None:
- return None
- return wsme.utils.parse_isodatetime(value)
-
-
-def parse(s, datatypes, bodyarg, encoding='utf8'):
- jload = json.load
- if not hasattr(s, 'read'):
- if six.PY3 and isinstance(s, six.binary_type):
- s = s.decode(encoding)
- jload = json.loads
- try:
- jdata = jload(s)
- except ValueError:
- raise wsme.exc.ClientSideError("Request is not in valid JSON format")
- if bodyarg:
- argname = list(datatypes.keys())[0]
- try:
- kw = {argname: fromjson(datatypes[argname], jdata)}
- except ValueError as e:
- raise wsme.exc.InvalidInput(argname, jdata, e.args[0])
- except wsme.exc.UnknownAttribute as e:
- # We only know the fieldname at this level, not in the
- # called function. We fill in this information here.
- e.add_fieldname(argname)
- raise
- else:
- kw = {}
- extra_args = []
- if not isinstance(jdata, dict):
- raise wsme.exc.ClientSideError("Request must be a JSON dict")
- for key in jdata:
- if key not in datatypes:
- extra_args.append(key)
- else:
- try:
- kw[key] = fromjson(datatypes[key], jdata[key])
- except ValueError as e:
- raise wsme.exc.InvalidInput(key, jdata[key], e.args[0])
- except wsme.exc.UnknownAttribute as e:
- # We only know the fieldname at this level, not in the
- # called function. We fill in this information here.
- e.add_fieldname(key)
- raise
- if extra_args:
- raise wsme.exc.UnknownArgument(', '.join(extra_args))
- return kw
-
-
-def encode_result(value, datatype, **options):
- jsondata = tojson(datatype, value)
- if options.get('nest_result', False):
- jsondata = {options.get('nested_result_attrname', 'result'): jsondata}
- return json.dumps(jsondata)
-
-
-def encode_error(context, errordetail):
- return json.dumps(errordetail)
-
-
-def encode_sample_value(datatype, value, format=False):
- r = tojson(datatype, value)
- content = json.dumps(r, ensure_ascii=False, indent=4 if format else 0,
- sort_keys=format)
- return ('javascript', content)
-
-
-def encode_sample_params(params, format=False):
- kw = {}
- for name, datatype, value in params:
- kw[name] = tojson(datatype, value)
- content = json.dumps(kw, ensure_ascii=False, indent=4 if format else 0,
- sort_keys=format)
- return ('javascript', content)
-
-
-def encode_sample_result(datatype, value, format=False):
- r = tojson(datatype, value)
- content = json.dumps(r, ensure_ascii=False, indent=4 if format else 0,
- sort_keys=format)
- return ('javascript', content)
diff --git a/wsme/rest/protocol.py b/wsme/rest/protocol.py
deleted file mode 100644
index 5201ccf..0000000
--- a/wsme/rest/protocol.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import os.path
-import logging
-
-from wsme.utils import OrderedDict
-from wsme.protocol import CallContext, Protocol, media_type_accept
-
-import wsme.rest
-import wsme.rest.args
-import wsme.runtime
-
-log = logging.getLogger(__name__)
-
-
-class RestProtocol(Protocol):
- name = 'rest'
- displayname = 'REST'
- dataformats = ['json', 'xml']
- content_types = ['application/json', 'text/xml']
-
- def __init__(self, dataformats=None):
- if dataformats is None:
- dataformats = RestProtocol.dataformats
-
- self.dataformats = OrderedDict()
- self.content_types = []
-
- for dataformat in dataformats:
- __import__('wsme.rest.' + dataformat)
- dfmod = getattr(wsme.rest, dataformat)
- self.dataformats[dataformat] = dfmod
- self.content_types.extend(dfmod.accept_content_types)
-
- def accept(self, request):
- for dataformat in self.dataformats:
- if request.path.endswith('.' + dataformat):
- return True
- return media_type_accept(request, self.content_types)
-
- def iter_calls(self, request):
- context = CallContext(request)
- context.outformat = None
- ext = os.path.splitext(request.path.split('/')[-1])[1]
- inmime = request.content_type
- outmime = request.accept.best_match(self.content_types)
-
- outformat = None
- informat = None
- for dfname, df in self.dataformats.items():
- if ext == '.' + dfname:
- outformat = df
- if not inmime:
- informat = df
-
- if outformat is None and request.accept:
- for dfname, df in self.dataformats.items():
- if outmime in df.accept_content_types:
- outformat = df
- if not inmime:
- informat = df
-
- if outformat is None:
- for dfname, df in self.dataformats.items():
- if inmime == df.content_type:
- outformat = df
-
- context.outformat = outformat
- context.outformat_options = {
- 'nest_result': getattr(self, 'nest_result', False)
- }
- if not inmime and informat:
- inmime = informat.content_type
- log.debug("Inferred input type: %s" % inmime)
- context.inmime = inmime
- yield context
-
- def extract_path(self, context):
- path = context.request.path
- assert path.startswith(self.root._webpath)
- path = path[len(self.root._webpath):]
- path = path.strip('/').split('/')
-
- for dataformat in self.dataformats:
- if path[-1].endswith('.' + dataformat):
- path[-1] = path[-1][:-len(dataformat) - 1]
-
- # Check if the path is actually a function, and if not
- # see if the http method make a difference
- # TODO Re-think the function lookup phases. Here we are
- # doing the job that will be done in a later phase, which
- # is sub-optimal
- for p, fdef in self.root.getapi():
- if p == path:
- return path
-
- # No function at this path. Now check for function that have
- # this path as a prefix, and declared an http method
- for p, fdef in self.root.getapi():
- if len(p) == len(path) + 1 and p[:len(path)] == path and \
- fdef.extra_options.get('method') == context.request.method:
- return p
-
- return path
-
- def read_arguments(self, context):
- request = context.request
- funcdef = context.funcdef
-
- body = None
- if request.content_length not in (None, 0, '0'):
- body = request.body
- if not body and '__body__' in request.params:
- body = request.params['__body__']
-
- args, kwargs = wsme.rest.args.combine_args(
- funcdef,
- (wsme.rest.args.args_from_params(funcdef, request.params),
- wsme.rest.args.args_from_body(funcdef, body, context.inmime))
- )
- wsme.runtime.check_arguments(funcdef, args, kwargs)
- return kwargs
-
- def encode_result(self, context, result):
- out = context.outformat.encode_result(
- result, context.funcdef.return_type,
- **context.outformat_options
- )
- return out
-
- def encode_error(self, context, errordetail):
- out = context.outformat.encode_error(
- context, errordetail
- )
- return out
diff --git a/wsme/rest/xml.py b/wsme/rest/xml.py
deleted file mode 100644
index 286afa7..0000000
--- a/wsme/rest/xml.py
+++ /dev/null
@@ -1,298 +0,0 @@
-from __future__ import absolute_import
-
-import datetime
-
-import six
-
-import xml.etree.ElementTree as et
-
-from simplegeneric import generic
-
-import wsme.types
-from wsme.exc import UnknownArgument, InvalidInput
-
-import re
-
-content_type = 'text/xml'
-accept_content_types = [
- content_type,
-]
-
-time_re = re.compile(r'(?P<h>[0-2][0-9]):(?P<m>[0-5][0-9]):(?P<s>[0-6][0-9])')
-
-
-def xml_indent(elem, level=0):
- i = "\n" + level * " "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- for e in elem:
- xml_indent(e, level + 1)
- if not e.tail or not e.tail.strip():
- e.tail = i
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
-
-
-@generic
-def toxml(datatype, key, value):
- """
- A generic converter from python to xml elements.
-
- If a non-complex user specific type is to be used in the api,
- a specific toxml should be added::
-
- from wsme.protocol.restxml import toxml
-
- myspecialtype = object()
-
- @toxml.when_object(myspecialtype)
- def myspecialtype_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- el.text = str(value)
- return el
- """
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- if wsme.types.isusertype(datatype):
- return toxml(datatype.basetype,
- key, datatype.tobasetype(value))
- elif wsme.types.iscomplex(datatype):
- for attrdef in datatype._wsme_attributes:
- attrvalue = getattr(value, attrdef.key)
- if attrvalue is not wsme.types.Unset:
- el.append(toxml(attrdef.datatype, attrdef.name,
- attrvalue))
- else:
- el.text = six.text_type(value)
- return el
-
-
-@generic
-def fromxml(datatype, element):
- """
- A generic converter from xml elements to python datatype.
-
- If a non-complex user specific type is to be used in the api,
- a specific fromxml should be added::
-
- from wsme.protocol.restxml import fromxml
-
- class MySpecialType(object):
- pass
-
- @fromxml.when_object(MySpecialType)
- def myspecialtype_fromxml(datatype, element):
- if element.get('nil', False):
- return None
- return MySpecialType(element.text)
- """
- if element.get('nil', False):
- return None
- if wsme.types.isusertype(datatype):
- return datatype.frombasetype(fromxml(datatype.basetype, element))
- if wsme.types.iscomplex(datatype):
- obj = datatype()
- for attrdef in wsme.types.list_attributes(datatype):
- sub = element.find(attrdef.name)
- if sub is not None:
- val_fromxml = fromxml(attrdef.datatype, sub)
- if getattr(attrdef, 'readonly', False):
- raise InvalidInput(attrdef.name, val_fromxml,
- "Cannot set read only field.")
- setattr(obj, attrdef.key, val_fromxml)
- elif attrdef.mandatory:
- raise InvalidInput(attrdef.name, None,
- "Mandatory field missing.")
- return wsme.types.validate_value(datatype, obj)
- if datatype is wsme.types.bytes:
- return element.text.encode('ascii')
- return datatype(element.text)
-
-
-@toxml.when_type(wsme.types.ArrayType)
-def array_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- for item in value:
- el.append(toxml(datatype.item_type, 'item', item))
- return el
-
-
-@toxml.when_type(wsme.types.DictType)
-def dict_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- for item in value.items():
- key = toxml(datatype.key_type, 'key', item[0])
- value = toxml(datatype.value_type, 'value', item[1])
- node = et.Element('item')
- node.append(key)
- node.append(value)
- el.append(node)
- return el
-
-
-@toxml.when_object(wsme.types.bytes)
-def bytes_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- el.text = value.decode('ascii')
- return el
-
-
-@toxml.when_object(bool)
-def bool_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- el.text = value and 'true' or 'false'
- return el
-
-
-@toxml.when_object(datetime.date)
-def date_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- el.text = value.isoformat()
- return el
-
-
-@toxml.when_object(datetime.datetime)
-def datetime_toxml(datatype, key, value):
- el = et.Element(key)
- if value is None:
- el.set('nil', 'true')
- else:
- el.text = value.isoformat()
- return el
-
-
-@fromxml.when_type(wsme.types.ArrayType)
-def array_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return [
- fromxml(datatype.item_type, item)
- for item in element.findall('item')
- ]
-
-
-@fromxml.when_object(bool)
-def bool_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return element.text.lower() != 'false'
-
-
-@fromxml.when_type(wsme.types.DictType)
-def dict_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return dict((
- (fromxml(datatype.key_type, item.find('key')),
- fromxml(datatype.value_type, item.find('value')))
- for item in element.findall('item')))
-
-
-@fromxml.when_object(wsme.types.text)
-def unicode_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return wsme.types.text(element.text) if element.text else six.u('')
-
-
-@fromxml.when_object(datetime.date)
-def date_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return wsme.utils.parse_isodate(element.text)
-
-
-@fromxml.when_object(datetime.time)
-def time_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return wsme.utils.parse_isotime(element.text)
-
-
-@fromxml.when_object(datetime.datetime)
-def datetime_fromxml(datatype, element):
- if element.get('nil') == 'true':
- return None
- return wsme.utils.parse_isodatetime(element.text)
-
-
-def parse(s, datatypes, bodyarg):
- if hasattr(s, 'read'):
- tree = et.parse(s)
- else:
- tree = et.fromstring(s)
- if bodyarg:
- name = list(datatypes.keys())[0]
- return {name: fromxml(datatypes[name], tree)}
- else:
- kw = {}
- extra_args = []
- for sub in tree:
- if sub.tag not in datatypes:
- extra_args.append(sub.tag)
- kw[sub.tag] = fromxml(datatypes[sub.tag], sub)
- if extra_args:
- raise UnknownArgument(', '.join(extra_args))
- return kw
-
-
-def encode_result(value, datatype, **options):
- return et.tostring(toxml(
- datatype, options.get('nested_result_attrname', 'result'), value
- ))
-
-
-def encode_error(context, errordetail):
- el = et.Element('error')
- et.SubElement(el, 'faultcode').text = errordetail['faultcode']
- et.SubElement(el, 'faultstring').text = errordetail['faultstring']
- if 'debuginfo' in errordetail:
- et.SubElement(el, 'debuginfo').text = errordetail['debuginfo']
- return et.tostring(el)
-
-
-def encode_sample_value(datatype, value, format=False):
- r = toxml(datatype, 'value', value)
- if format:
- xml_indent(r)
- content = et.tostring(r)
- return ('xml', content)
-
-
-def encode_sample_params(params, format=False):
- node = et.Element('parameters')
- for name, datatype, value in params:
- node.append(toxml(datatype, name, value))
- if format:
- xml_indent(node)
- content = et.tostring(node)
- return ('xml', content)
-
-
-def encode_sample_result(datatype, value, format=False):
- r = toxml(datatype, 'result', value)
- if format:
- xml_indent(r)
- content = et.tostring(r)
- return ('xml', content)
diff --git a/wsme/root.py b/wsme/root.py
deleted file mode 100644
index e71f1f7..0000000
--- a/wsme/root.py
+++ /dev/null
@@ -1,372 +0,0 @@
-import logging
-import sys
-import weakref
-
-from six import u, b
-import six
-
-import webob
-
-from wsme.exc import ClientSideError, UnknownFunction
-from wsme.protocol import getprotocol
-from wsme.rest import scan_api
-from wsme import spore
-import wsme.api
-import wsme.types
-
-log = logging.getLogger(__name__)
-
-html_body = u("""
-<html>
-<head>
- <style type='text/css'>
- %(css)s
- </style>
-</head>
-<body>
-%(content)s
-</body>
-</html>
-""")
-
-
-def default_prepare_response_body(request, results):
- r = None
- sep = None
- for value in results:
- if sep is None:
- if isinstance(value, six.text_type):
- sep = u('\n')
- r = u('')
- else:
- sep = b('\n')
- r = b('')
- else:
- r += sep
- r += value
- return r
-
-
-class DummyTransaction:
- def commit(self):
- pass
-
- def abort(self):
- pass
-
-
-class WSRoot(object):
- """
- Root controller for webservices.
-
- :param protocols: A list of protocols to enable (see :meth:`addprotocol`)
- :param webpath: The web path where the webservice is published.
-
- :type transaction: A `transaction
- <http://pypi.python.org/pypi/transaction>`_-like
- object or ``True``.
- :param transaction: If specified, a transaction will be created and
- handled on a per-call base.
-
- This option *can* be enabled along with `repoze.tm2
- <http://pypi.python.org/pypi/repoze.tm2>`_
- (it will only make it void).
-
- If ``True``, the default :mod:`transaction`
- module will be imported and used.
-
- """
- __registry__ = wsme.types.registry
-
- def __init__(self, protocols=[], webpath='', transaction=None,
- scan_api=scan_api):
- self._debug = True
- self._webpath = webpath
- self.protocols = []
- self._scan_api = scan_api
-
- self._transaction = transaction
- if self._transaction is True:
- import transaction
- self._transaction = transaction
-
- for protocol in protocols:
- self.addprotocol(protocol)
-
- self._api = None
-
- def wsgiapp(self):
- """Returns a wsgi application"""
- from webob.dec import wsgify
- return wsgify(self._handle_request)
-
- def begin(self):
- if self._transaction:
- return self._transaction.begin()
- else:
- return DummyTransaction()
-
- def addprotocol(self, protocol, **options):
- """
- Enable a new protocol on the controller.
-
- :param protocol: A registered protocol name or an instance
- of a protocol.
- """
- if isinstance(protocol, str):
- protocol = getprotocol(protocol, **options)
- self.protocols.append(protocol)
- protocol.root = weakref.proxy(self)
-
- def getapi(self):
- """
- Returns the api description.
-
- :rtype: list of (path, :class:`FunctionDefinition`)
- """
- if self._api is None:
- self._api = [
- (path, f, f._wsme_definition, args)
- for path, f, args in self._scan_api(self)
- ]
- for path, f, fdef, args in self._api:
- fdef.resolve_types(self.__registry__)
- return [
- (path, fdef)
- for path, f, fdef, args in self._api
- ]
-
- def _get_protocol(self, name):
- for protocol in self.protocols:
- if protocol.name == name:
- return protocol
-
- def _select_protocol(self, request):
- log.debug("Selecting a protocol for the following request :\n"
- "headers: %s\nbody: %s", request.headers.items(),
- request.content_length and (
- request.content_length > 512 and
- request.body[:512] or
- request.body) or '')
- protocol = None
- error = ClientSideError(status_code=406)
- path = str(request.path)
- assert path.startswith(self._webpath)
- path = path[len(self._webpath) + 1:]
- if 'wsmeproto' in request.params:
- return self._get_protocol(request.params['wsmeproto'])
- else:
-
- for p in self.protocols:
- try:
- if p.accept(request):
- protocol = p
- break
- except ClientSideError as e:
- error = e
- # If we could not select a protocol, we raise the last exception
- # that we got, or the default one.
- if not protocol:
- raise error
- return protocol
-
- def _do_call(self, protocol, context):
- request = context.request
- request.calls.append(context)
- try:
- if context.path is None:
- context.path = protocol.extract_path(context)
-
- if context.path is None:
- raise ClientSideError(u(
- 'The %s protocol was unable to extract a function '
- 'path from the request') % protocol.name)
-
- context.func, context.funcdef, args = \
- self._lookup_function(context.path)
- kw = protocol.read_arguments(context)
- args = list(args)
-
- txn = self.begin()
- try:
- result = context.func(*args, **kw)
- txn.commit()
- except Exception:
- txn.abort()
- raise
-
- else:
- # TODO make sure result type == a._wsme_definition.return_type
- return protocol.encode_result(context, result)
-
- except Exception as e:
- infos = wsme.api.format_exception(sys.exc_info(), self._debug)
- if isinstance(e, ClientSideError):
- request.client_errorcount += 1
- request.client_last_status_code = e.code
- else:
- request.server_errorcount += 1
- return protocol.encode_error(context, infos)
-
- def find_route(self, path):
- for p in self.protocols:
- for routepath, func in p.iter_routes():
- if path.startswith(routepath):
- return routepath, func
- return None, None
-
- def _handle_request(self, request):
- res = webob.Response()
- res_content_type = None
-
- path = request.path
- if path.startswith(self._webpath):
- path = path[len(self._webpath):]
- routepath, func = self.find_route(path)
- if routepath:
- content = func()
- if isinstance(content, six.text_type):
- res.text = content
- elif isinstance(content, six.binary_type):
- res.body = content
- res.content_type = func._cfg['content-type']
- return res
-
- if request.path == self._webpath + '/api.spore':
- res.body = spore.getdesc(self, request.host_url)
- res.content_type = 'application/json'
- return res
-
- try:
- msg = None
- error_status = 500
- protocol = self._select_protocol(request)
- except ClientSideError as e:
- error_status = e.code
- msg = e.faultstring
- protocol = None
- except Exception as e:
- msg = ("Unexpected error while selecting protocol: %s" % str(e))
- log.exception(msg)
- protocol = None
- error_status = 500
-
- if protocol is None:
- if not msg:
- msg = ("None of the following protocols can handle this "
- "request : %s" % ','.join((
- p.name for p in self.protocols)))
- res.status = error_status
- res.content_type = 'text/plain'
- try:
- res.text = u(msg)
- except TypeError:
- res.text = msg
- log.error(msg)
- return res
-
- request.calls = []
- request.client_errorcount = 0
- request.client_last_status_code = None
- request.server_errorcount = 0
-
- try:
-
- context = None
-
- if hasattr(protocol, 'prepare_response_body'):
- prepare_response_body = protocol.prepare_response_body
- else:
- prepare_response_body = default_prepare_response_body
-
- body = prepare_response_body(request, (
- self._do_call(protocol, context)
- for context in protocol.iter_calls(request)))
-
- if isinstance(body, six.text_type):
- res.text = body
- else:
- res.body = body
-
- if len(request.calls) == 1:
- if hasattr(protocol, 'get_response_status'):
- res.status = protocol.get_response_status(request)
- else:
- if request.client_errorcount == 1:
- res.status = request.client_last_status_code
- elif request.client_errorcount:
- res.status = 400
- elif request.server_errorcount:
- res.status = 500
- else:
- res.status = 200
- else:
- res.status = protocol.get_response_status(request)
- res_content_type = protocol.get_response_contenttype(request)
- except ClientSideError as e:
- request.server_errorcount += 1
- res.status = e.code
- res.text = e.faultstring
- except Exception:
- infos = wsme.api.format_exception(sys.exc_info(), self._debug)
- request.server_errorcount += 1
- res.text = protocol.encode_error(context, infos)
- res.status = 500
-
- if res_content_type is None:
- # Attempt to correctly guess what content-type we should return.
- ctypes = [ct for ct in protocol.content_types if ct]
- if ctypes:
- res_content_type = request.accept.best_match(ctypes)
-
- # If not we will attempt to convert the body to an accepted
- # output format.
- if res_content_type is None:
- if "text/html" in request.accept:
- res.text = self._html_format(res.body, protocol.content_types)
- res_content_type = "text/html"
-
- # TODO should we consider the encoding asked by
- # the web browser ?
- res.headers['Content-Type'] = "%s; charset=UTF-8" % res_content_type
-
- return res
-
- def _lookup_function(self, path):
- if not self._api:
- self.getapi()
-
- for fpath, f, fdef, args in self._api:
- if path == fpath:
- return f, fdef, args
- raise UnknownFunction('/'.join(path))
-
- def _html_format(self, content, content_types):
- try:
- from pygments import highlight
- from pygments.lexers import get_lexer_for_mimetype
- from pygments.formatters import HtmlFormatter
-
- lexer = None
- for ct in content_types:
- try:
- lexer = get_lexer_for_mimetype(ct)
- break
- except Exception:
- pass
-
- if lexer is None:
- raise ValueError("No lexer found")
- formatter = HtmlFormatter()
- return html_body % dict(
- css=formatter.get_style_defs(),
- content=highlight(content, lexer, formatter).encode('utf8'))
- except Exception as e:
- log.warning(
- "Could not pygment the content because of the following "
- "error :\n%s" % e)
- return html_body % dict(
- css='',
- content=u('<pre>%s</pre>') %
- content.replace(b('>'), b('&gt;'))
- .replace(b('<'), b('&lt;')))
diff --git a/wsme/runtime.py b/wsme/runtime.py
deleted file mode 100644
index e114d13..0000000
--- a/wsme/runtime.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from wsme.exc import MissingArgument
-
-
-def check_arguments(funcdef, args, kw):
- """Check if some arguments are missing"""
- assert len(args) == 0
- for arg in funcdef.arguments:
- if arg.mandatory and arg.name not in kw:
- raise MissingArgument(arg.name)
diff --git a/wsme/spore.py b/wsme/spore.py
deleted file mode 100644
index 8de0ed2..0000000
--- a/wsme/spore.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from wsme import types
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-
-def getdesc(root, host_url=''):
- methods = {}
-
- for path, funcdef in root.getapi():
- method = funcdef.extra_options.get('method', None)
- name = '_'.join(path)
- if method is not None:
- path = path[:-1]
- else:
- method = 'GET'
- for argdef in funcdef.arguments:
- if types.iscomplex(argdef.datatype) \
- or types.isarray(argdef.datatype) \
- or types.isdict(argdef.datatype):
- method = 'POST'
- break
-
- required_params = []
- optional_params = []
- for argdef in funcdef.arguments:
- if method == 'GET' and argdef.mandatory:
- required_params.append(argdef.name)
- else:
- optional_params.append(argdef.name)
-
- methods[name] = {
- 'method': method,
- 'path': '/'.join(path)
- }
- if required_params:
- methods[name]['required_params'] = required_params
- if optional_params:
- methods[name]['optional_params'] = optional_params
- if funcdef.doc:
- methods[name]['documentation'] = funcdef.doc
-
- formats = []
- for p in root.protocols:
- if p.name == 'restxml':
- formats.append('xml')
- if p.name == 'restjson':
- formats.append('json')
-
- api = {
- 'base_url': host_url + root._webpath,
- 'version': '0.1',
- 'name': getattr(root, 'name', 'name'),
- 'authority': '',
- 'formats': [
- 'json',
- 'xml'
- ],
- 'methods': methods
- }
-
- return json.dumps(api, indent=4)
diff --git a/wsme/tests/__init__.py b/wsme/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/wsme/tests/__init__.py
+++ /dev/null
diff --git a/wsme/tests/protocol.py b/wsme/tests/protocol.py
deleted file mode 100644
index 1c41f8c..0000000
--- a/wsme/tests/protocol.py
+++ /dev/null
@@ -1,720 +0,0 @@
-# coding=utf-8
-
-import unittest
-import warnings
-import datetime
-import decimal
-import six
-
-from six import u, b
-
-from webtest import TestApp
-
-from wsme import WSRoot, Unset
-from wsme import expose, validate
-import wsme.types
-import wsme.utils
-
-warnings.filterwarnings('ignore', module='webob.dec')
-
-binarysample = b('\x00\xff\x43')
-
-try:
- 1 / 0
-except ZeroDivisionError as e:
- zerodivisionerrormsg = str(e)
-
-
-class CallException(RuntimeError):
- def __init__(self, faultcode, faultstring, debuginfo):
- self.faultcode = faultcode
- self.faultstring = faultstring
- self.debuginfo = debuginfo
-
- def __str__(self):
- return 'faultcode=%s, faultstring=%s, debuginfo=%s' % (
- self.faultcode, self.faultstring, self.debuginfo
- )
-
-
-myenumtype = wsme.types.Enum(wsme.types.bytes, 'v1', 'v2')
-
-
-class NestedInner(object):
- aint = int
-
- def __init__(self, aint=None):
- self.aint = aint
-
-
-class NestedOuter(object):
- inner = NestedInner
- inner_array = wsme.types.wsattr([NestedInner])
- inner_dict = {wsme.types.text: NestedInner}
-
- def __init__(self):
- self.inner = NestedInner(0)
-
-
-class NamedAttrsObject(object):
- def __init__(self, v1=Unset, v2=Unset):
- self.attr_1 = v1
- self.attr_2 = v2
-
- attr_1 = wsme.types.wsattr(int, name='attr.1')
- attr_2 = wsme.types.wsattr(int, name='attr.2')
-
-
-class CustomObject(object):
- aint = int
- name = wsme.types.text
-
-
-class ExtendedInt(wsme.types.UserType):
- basetype = int
- name = "Extended integer"
-
-
-class NestedInnerApi(object):
- @expose(bool)
- def deepfunction(self):
- return True
-
-
-class NestedOuterApi(object):
- inner = NestedInnerApi()
-
-
-class ReturnTypes(object):
- @expose(wsme.types.bytes)
- def getbytes(self):
- return b("astring")
-
- @expose(wsme.types.text)
- def gettext(self):
- return u('\xe3\x81\xae')
-
- @expose(int)
- def getint(self):
- return 2
-
- @expose(float)
- def getfloat(self):
- return 3.14159265
-
- @expose(decimal.Decimal)
- def getdecimal(self):
- return decimal.Decimal('3.14159265')
-
- @expose(datetime.date)
- def getdate(self):
- return datetime.date(1994, 1, 26)
-
- @expose(bool)
- def getbooltrue(self):
- return True
-
- @expose(bool)
- def getboolfalse(self):
- return False
-
- @expose(datetime.time)
- def gettime(self):
- return datetime.time(12, 0, 0)
-
- @expose(datetime.datetime)
- def getdatetime(self):
- return datetime.datetime(1994, 1, 26, 12, 0, 0)
-
- @expose(wsme.types.binary)
- def getbinary(self):
- return binarysample
-
- @expose(NestedOuter)
- def getnested(self):
- n = NestedOuter()
- return n
-
- @expose([wsme.types.bytes])
- def getbytesarray(self):
- return [b("A"), b("B"), b("C")]
-
- @expose([NestedOuter])
- def getnestedarray(self):
- return [NestedOuter(), NestedOuter()]
-
- @expose({wsme.types.bytes: NestedOuter})
- def getnesteddict(self):
- return {b('a'): NestedOuter(), b('b'): NestedOuter()}
-
- @expose(NestedOuter)
- def getobjectarrayattribute(self):
- obj = NestedOuter()
- obj.inner_array = [NestedInner(12), NestedInner(13)]
- return obj
-
- @expose(NestedOuter)
- def getobjectdictattribute(self):
- obj = NestedOuter()
- obj.inner_dict = {
- '12': NestedInner(12),
- '13': NestedInner(13)
- }
- return obj
-
- @expose(myenumtype)
- def getenum(self):
- return b('v2')
-
- @expose(NamedAttrsObject)
- def getnamedattrsobj(self):
- return NamedAttrsObject(5, 6)
-
-
-class ArgTypes(object):
- def assertEqual(self, a, b):
- if not (a == b):
- raise AssertionError('%s != %s' % (a, b))
-
- def assertIsInstance(self, value, v_type):
- assert isinstance(value, v_type), ("%s is not instance of type %s" %
- (value, v_type))
-
- @expose(wsme.types.bytes)
- @validate(wsme.types.bytes)
- def setbytes(self, value):
- print(repr(value))
- self.assertEqual(type(value), wsme.types.bytes)
- return value
-
- @expose(wsme.types.text)
- @validate(wsme.types.text)
- def settext(self, value):
- print(repr(value))
- self.assertEqual(type(value), wsme.types.text)
- return value
-
- @expose(wsme.types.text)
- @validate(wsme.types.text)
- def settextnone(self, value):
- print(repr(value))
- self.assertEqual(type(value), type(None))
- return value
-
- @expose(bool)
- @validate(bool)
- def setbool(self, value):
- print(repr(value))
- self.assertEqual(type(value), bool)
- return value
-
- @expose(int)
- @validate(int)
- def setint(self, value):
- print(repr(value))
- self.assertEqual(type(value), int)
- return value
-
- @expose(float)
- @validate(float)
- def setfloat(self, value):
- print(repr(value))
- self.assertEqual(type(value), float)
- return value
-
- @expose(decimal.Decimal)
- @validate(decimal.Decimal)
- def setdecimal(self, value):
- print(repr(value))
- self.assertEqual(type(value), decimal.Decimal)
- return value
-
- @expose(datetime.date)
- @validate(datetime.date)
- def setdate(self, value):
- print(repr(value))
- self.assertEqual(type(value), datetime.date)
- return value
-
- @expose(datetime.time)
- @validate(datetime.time)
- def settime(self, value):
- print(repr(value))
- self.assertEqual(type(value), datetime.time)
- return value
-
- @expose(datetime.datetime)
- @validate(datetime.datetime)
- def setdatetime(self, value):
- print(repr(value))
- self.assertEqual(type(value), datetime.datetime)
- return value
-
- @expose(wsme.types.binary)
- @validate(wsme.types.binary)
- def setbinary(self, value):
- print(repr(value))
- self.assertEqual(type(value), six.binary_type)
- return value
-
- @expose([wsme.types.bytes])
- @validate([wsme.types.bytes])
- def setbytesarray(self, value):
- print(repr(value))
- self.assertEqual(type(value), list)
- self.assertEqual(type(value[0]), wsme.types.bytes)
- return value
-
- @expose([wsme.types.text])
- @validate([wsme.types.text])
- def settextarray(self, value):
- print(repr(value))
- self.assertEqual(type(value), list)
- self.assertEqual(type(value[0]), wsme.types.text)
- return value
-
- @expose([datetime.datetime])
- @validate([datetime.datetime])
- def setdatetimearray(self, value):
- print(repr(value))
- self.assertEqual(type(value), list)
- self.assertEqual(type(value[0]), datetime.datetime)
- return value
-
- @expose(NestedOuter)
- @validate(NestedOuter)
- def setnested(self, value):
- print(repr(value))
- self.assertEqual(type(value), NestedOuter)
- return value
-
- @expose([NestedOuter])
- @validate([NestedOuter])
- def setnestedarray(self, value):
- print(repr(value))
- self.assertEqual(type(value), list)
- self.assertEqual(type(value[0]), NestedOuter)
- return value
-
- @expose({wsme.types.bytes: NestedOuter})
- @validate({wsme.types.bytes: NestedOuter})
- def setnesteddict(self, value):
- print(repr(value))
- self.assertEqual(type(value), dict)
- self.assertEqual(type(list(value.keys())[0]), wsme.types.bytes)
- self.assertEqual(type(list(value.values())[0]), NestedOuter)
- return value
-
- @expose(myenumtype)
- @validate(myenumtype)
- def setenum(self, value):
- print(value)
- self.assertEqual(type(value), wsme.types.bytes)
- return value
-
- @expose(NamedAttrsObject)
- @validate(NamedAttrsObject)
- def setnamedattrsobj(self, value):
- print(value)
- self.assertEqual(type(value), NamedAttrsObject)
- self.assertEqual(value.attr_1, 10)
- self.assertEqual(value.attr_2, 20)
- return value
-
- @expose(CustomObject)
- @validate(CustomObject)
- def setcustomobject(self, value):
- self.assertIsInstance(value, CustomObject)
- self.assertIsInstance(value.name, wsme.types.text)
- self.assertIsInstance(value.aint, int)
- return value
-
- @expose(ExtendedInt())
- @validate(ExtendedInt())
- def setextendedint(self, value):
- self.assertEqual(isinstance(value, ExtendedInt.basetype), True)
- return value
-
-
-class BodyTypes(object):
- def assertEqual(self, a, b):
- if not (a == b):
- raise AssertionError('%s != %s' % (a, b))
-
- @expose(int, body={wsme.types.text: int})
- @validate(int)
- def setdict(self, body):
- print(body)
- self.assertEqual(type(body), dict)
- self.assertEqual(type(body['test']), int)
- self.assertEqual(body['test'], 10)
- return body['test']
-
- @expose(int, body=[int])
- @validate(int)
- def setlist(self, body):
- print(body)
- self.assertEqual(type(body), list)
- self.assertEqual(type(body[0]), int)
- self.assertEqual(body[0], 10)
- return body[0]
-
-
-class WithErrors(object):
- @expose()
- def divide_by_zero(self):
- 1 / 0
-
-
-class MiscFunctions(object):
- @expose(int)
- @validate(int, int)
- def multiply(self, a, b):
- return a * b
-
-
-class WSTestRoot(WSRoot):
- argtypes = ArgTypes()
- returntypes = ReturnTypes()
- bodytypes = BodyTypes()
- witherrors = WithErrors()
- nested = NestedOuterApi()
- misc = MiscFunctions()
-
- def reset(self):
- self._touched = False
-
- @expose()
- def touch(self):
- self._touched = True
-
-
-class ProtocolTestCase(unittest.TestCase):
- protocol_options = {}
-
- def assertTypedEquals(self, a, b, convert):
- if isinstance(a, six.string_types):
- a = convert(a)
- if isinstance(b, six.string_types):
- b = convert(b)
- self.assertEqual(a, b)
-
- def assertDateEquals(self, a, b):
- self.assertTypedEquals(a, b, wsme.utils.parse_isodate)
-
- def assertTimeEquals(self, a, b):
- self.assertTypedEquals(a, b, wsme.utils.parse_isotime)
-
- def assertDateTimeEquals(self, a, b):
- self.assertTypedEquals(a, b, wsme.utils.parse_isodatetime)
-
- def assertIntEquals(self, a, b):
- self.assertTypedEquals(a, b, int)
-
- def assertFloatEquals(self, a, b):
- self.assertTypedEquals(a, b, float)
-
- def assertDecimalEquals(self, a, b):
- self.assertTypedEquals(a, b, decimal.Decimal)
-
- def setUp(self):
- if self.__class__.__name__ != 'ProtocolTestCase':
- self.root = WSTestRoot()
- self.root.getapi()
- self.root.addprotocol(self.protocol, **self.protocol_options)
-
- self.app = TestApp(self.root.wsgiapp())
-
- def test_invalid_path(self):
- try:
- res = self.call('invalid_function')
- print(res)
- assert "No error raised"
- except CallException as e:
- self.assertEqual(e.faultcode, 'Client')
- self.assertEqual(e.faultstring.lower(),
- u('unknown function name: invalid_function'))
-
- def test_serverside_error(self):
- try:
- res = self.call('witherrors/divide_by_zero')
- print(res)
- assert "No error raised"
- except CallException as e:
- self.assertEqual(e.faultcode, 'Server')
- self.assertEqual(e.faultstring, zerodivisionerrormsg)
- assert e.debuginfo is not None
-
- def test_serverside_error_nodebug(self):
- self.root._debug = False
- try:
- res = self.call('witherrors/divide_by_zero')
- print(res)
- assert "No error raised"
- except CallException as e:
- self.assertEqual(e.faultcode, 'Server')
- self.assertEqual(e.faultstring, zerodivisionerrormsg)
- assert e.debuginfo is None
-
- def test_touch(self):
- r = self.call('touch')
- assert r is None, r
-
- def test_return_bytes(self):
- r = self.call('returntypes/getbytes', _rt=wsme.types.bytes)
- self.assertEqual(r, b('astring'))
-
- def test_return_text(self):
- r = self.call('returntypes/gettext', _rt=wsme.types.text)
- self.assertEqual(r, u('\xe3\x81\xae'))
-
- def test_return_int(self):
- r = self.call('returntypes/getint')
- self.assertIntEquals(r, 2)
-
- def test_return_float(self):
- r = self.call('returntypes/getfloat')
- self.assertFloatEquals(r, 3.14159265)
-
- def test_return_decimal(self):
- r = self.call('returntypes/getdecimal')
- self.assertDecimalEquals(r, '3.14159265')
-
- def test_return_bool_true(self):
- r = self.call('returntypes/getbooltrue', _rt=bool)
- assert r
-
- def test_return_bool_false(self):
- r = self.call('returntypes/getboolfalse', _rt=bool)
- assert not r
-
- def test_return_date(self):
- r = self.call('returntypes/getdate')
- self.assertDateEquals(r, datetime.date(1994, 1, 26))
-
- def test_return_time(self):
- r = self.call('returntypes/gettime')
- self.assertTimeEquals(r, datetime.time(12))
-
- def test_return_datetime(self):
- r = self.call('returntypes/getdatetime')
- self.assertDateTimeEquals(r, datetime.datetime(1994, 1, 26, 12))
-
- def test_return_binary(self):
- r = self.call('returntypes/getbinary', _rt=wsme.types.binary)
- self.assertEqual(r, binarysample)
-
- def test_return_nested(self):
- r = self.call('returntypes/getnested', _rt=NestedOuter)
- self.assertEqual(r, {'inner': {'aint': 0}})
-
- def test_return_bytesarray(self):
- r = self.call('returntypes/getbytesarray', _rt=[six.binary_type])
- self.assertEqual(r, [b('A'), b('B'), b('C')])
-
- def test_return_nestedarray(self):
- r = self.call('returntypes/getnestedarray', _rt=[NestedOuter])
- self.assertEqual(r, [{'inner': {'aint': 0}}, {'inner': {'aint': 0}}])
-
- def test_return_nesteddict(self):
- r = self.call('returntypes/getnesteddict',
- _rt={wsme.types.bytes: NestedOuter})
- self.assertEqual(r, {
- b('a'): {'inner': {'aint': 0}},
- b('b'): {'inner': {'aint': 0}}
- })
-
- def test_return_objectarrayattribute(self):
- r = self.call('returntypes/getobjectarrayattribute', _rt=NestedOuter)
- self.assertEqual(r, {
- 'inner': {'aint': 0},
- 'inner_array': [{'aint': 12}, {'aint': 13}]
- })
-
- def test_return_objectdictattribute(self):
- r = self.call('returntypes/getobjectdictattribute', _rt=NestedOuter)
- self.assertEqual(r, {
- 'inner': {'aint': 0},
- 'inner_dict': {
- '12': {'aint': 12},
- '13': {'aint': 13}
- }
- })
-
- def test_return_enum(self):
- r = self.call('returntypes/getenum', _rt=myenumtype)
- self.assertEqual(r, b('v2'), r)
-
- def test_return_namedattrsobj(self):
- r = self.call('returntypes/getnamedattrsobj', _rt=NamedAttrsObject)
- self.assertEqual(r, {'attr.1': 5, 'attr.2': 6})
-
- def test_setbytes(self):
- assert self.call('argtypes/setbytes', value=b('astring'),
- _rt=wsme.types.bytes) == b('astring')
-
- def test_settext(self):
- assert self.call('argtypes/settext', value=u('\xe3\x81\xae'),
- _rt=wsme.types.text) == u('\xe3\x81\xae')
-
- def test_settext_empty(self):
- assert self.call('argtypes/settext', value=u(''),
- _rt=wsme.types.text) == u('')
-
- def test_settext_none(self):
- self.assertEqual(
- None,
- self.call('argtypes/settextnone', value=None, _rt=wsme.types.text)
- )
-
- def test_setint(self):
- r = self.call('argtypes/setint', value=3, _rt=int)
- self.assertEqual(r, 3)
-
- def test_setfloat(self):
- assert self.call('argtypes/setfloat', value=3.54,
- _rt=float) == 3.54
-
- def test_setbool_true(self):
- r = self.call('argtypes/setbool', value=True, _rt=bool)
- assert r
-
- def test_setbool_false(self):
- r = self.call('argtypes/setbool', value=False, _rt=bool)
- assert not r
-
- def test_setdecimal(self):
- value = decimal.Decimal('3.14')
- assert self.call('argtypes/setdecimal', value=value,
- _rt=decimal.Decimal) == value
-
- def test_setdate(self):
- value = datetime.date(2008, 4, 6)
- r = self.call('argtypes/setdate', value=value,
- _rt=datetime.date)
- self.assertEqual(r, value)
-
- def test_settime(self):
- value = datetime.time(12, 12, 15)
- r = self.call('argtypes/settime', value=value,
- _rt=datetime.time)
- self.assertEqual(r, datetime.time(12, 12, 15))
-
- def test_setdatetime(self):
- value = datetime.datetime(2008, 4, 6, 12, 12, 15)
- r = self.call('argtypes/setdatetime', value=value,
- _rt=datetime.datetime)
- self.assertEqual(r, datetime.datetime(2008, 4, 6, 12, 12, 15))
-
- def test_setbinary(self):
- value = binarysample
- r = self.call('argtypes/setbinary', value=(value, wsme.types.binary),
- _rt=wsme.types.binary) == value
- print(r)
-
- def test_setnested(self):
- value = {'inner': {'aint': 54}}
- r = self.call('argtypes/setnested',
- value=(value, NestedOuter),
- _rt=NestedOuter)
- self.assertEqual(r, value)
-
- def test_setnested_nullobj(self):
- value = {'inner': None}
- r = self.call(
- 'argtypes/setnested',
- value=(value, NestedOuter),
- _rt=NestedOuter
- )
- self.assertEqual(r, value)
-
- def test_setbytesarray(self):
- value = [b("1"), b("2"), b("three")]
- r = self.call('argtypes/setbytesarray',
- value=(value, [wsme.types.bytes]),
- _rt=[wsme.types.bytes])
- self.assertEqual(r, value)
-
- def test_settextarray(self):
- value = [u("1")]
- r = self.call('argtypes/settextarray',
- value=(value, [wsme.types.text]),
- _rt=[wsme.types.text])
- self.assertEqual(r, value)
-
- def test_setdatetimearray(self):
- value = [
- datetime.datetime(2008, 3, 6, 12, 12, 15),
- datetime.datetime(2008, 4, 6, 2, 12, 15),
- ]
- r = self.call('argtypes/setdatetimearray',
- value=(value, [datetime.datetime]),
- _rt=[datetime.datetime])
- self.assertEqual(r, value)
-
- def test_setnestedarray(self):
- value = [
- {'inner': {'aint': 54}},
- {'inner': {'aint': 55}},
- ]
- r = self.call('argtypes/setnestedarray',
- value=(value, [NestedOuter]),
- _rt=[NestedOuter])
- self.assertEqual(r, value)
-
- def test_setnesteddict(self):
- value = {
- b('o1'): {'inner': {'aint': 54}},
- b('o2'): {'inner': {'aint': 55}},
- }
- r = self.call('argtypes/setnesteddict',
- value=(value, {six.binary_type: NestedOuter}),
- _rt={six.binary_type: NestedOuter})
- print(r)
- self.assertEqual(r, value)
-
- def test_setenum(self):
- value = b('v1')
- r = self.call('argtypes/setenum', value=value,
- _rt=myenumtype)
- self.assertEqual(r, value)
-
- def test_setnamedattrsobj(self):
- value = {'attr.1': 10, 'attr.2': 20}
- r = self.call('argtypes/setnamedattrsobj',
- value=(value, NamedAttrsObject),
- _rt=NamedAttrsObject)
- self.assertEqual(r, value)
-
- def test_nested_api(self):
- r = self.call('nested/inner/deepfunction', _rt=bool)
- assert r is True
-
- def test_missing_argument(self):
- try:
- r = self.call('argtypes/setdatetime')
- print(r)
- assert "No error raised"
- except CallException as e:
- self.assertEqual(e.faultcode, 'Client')
- self.assertEqual(e.faultstring, u('Missing argument: "value"'))
-
- def test_misc_multiply(self):
- self.assertEqual(self.call('misc/multiply', a=5, b=2, _rt=int), 10)
-
- def test_html_format(self):
- res = self.call('argtypes/setdatetime', _accept="text/html",
- _no_result_decode=True)
- self.assertEqual(res.content_type, 'text/html')
-
-
-class RestOnlyProtocolTestCase(ProtocolTestCase):
- def test_body_list(self):
- r = self.call('bodytypes/setlist', body=([10], [int]), _rt=int)
- self.assertEqual(r, 10)
-
- def test_body_dict(self):
- r = self.call('bodytypes/setdict',
- body=({'test': 10}, {wsme.types.text: int}),
- _rt=int)
- self.assertEqual(r, 10)
diff --git a/wsme/tests/test_api.py b/wsme/tests/test_api.py
deleted file mode 100644
index fc1e157..0000000
--- a/wsme/tests/test_api.py
+++ /dev/null
@@ -1,419 +0,0 @@
-# encoding=utf8
-
-from six import b
-
-try:
- import unittest2 as unittest
-except ImportError:
- import unittest
-import webtest
-
-from wsme import WSRoot, expose, validate
-from wsme.rest import scan_api
-from wsme import types
-from wsme import exc
-import wsme.api as wsme_api
-import wsme.types
-
-from wsme.tests.test_protocols import DummyProtocol
-
-
-class TestController(unittest.TestCase):
- def test_expose(self):
- class MyWS(WSRoot):
- @expose(int)
- def getint(self):
- return 1
-
- assert MyWS.getint._wsme_definition.return_type == int
-
- def test_validate(self):
- class ComplexType(object):
- attr = int
-
- class MyWS(object):
- @expose(int)
- @validate(int, int, int)
- def add(self, a, b, c=0):
- return a + b + c
-
- @expose(bool)
- @validate(ComplexType)
- def setcplx(self, obj):
- pass
-
- MyWS.add._wsme_definition.resolve_types(wsme.types.registry)
- MyWS.setcplx._wsme_definition.resolve_types(wsme.types.registry)
- args = MyWS.add._wsme_definition.arguments
-
- assert args[0].name == 'a'
- assert args[0].datatype == int
- assert args[0].mandatory
- assert args[0].default is None
-
- assert args[1].name == 'b'
- assert args[1].datatype == int
- assert args[1].mandatory
- assert args[1].default is None
-
- assert args[2].name == 'c'
- assert args[2].datatype == int
- assert not args[2].mandatory
- assert args[2].default == 0
-
- assert types.iscomplex(ComplexType)
-
- def test_validate_enum_with_none(self):
- class Version(object):
- number = types.Enum(str, 'v1', 'v2', None)
-
- class MyWS(WSRoot):
- @expose(str)
- @validate(Version)
- def setcplx(self, version):
- pass
-
- r = MyWS(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/setcplx', params={'version': {'number': 'arf'}},
- expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertTrue(
- res.json_body['faultstring'].startswith(
- "Invalid input for field/attribute number. Value: 'arf'. \
-Value should be one of:"))
- self.assertIn('v1', res.json_body['faultstring'])
- self.assertIn('v2', res.json_body['faultstring'])
- self.assertIn('None', res.json_body['faultstring'])
- self.assertEqual(res.status_int, 400)
-
- def test_validate_enum_with_wrong_type(self):
- class Version(object):
- number = types.Enum(str, 'v1', 'v2', None)
-
- class MyWS(WSRoot):
- @expose(str)
- @validate(Version)
- def setcplx(self, version):
- pass
-
- r = MyWS(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/setcplx', params={'version': {'number': 1}},
- expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertTrue(
- res.json_body['faultstring'].startswith(
- "Invalid input for field/attribute number. Value: '1'. \
-Value should be one of:"))
- self.assertIn('v1', res.json_body['faultstring'])
- self.assertIn('v2', res.json_body['faultstring'])
- self.assertIn('None', res.json_body['faultstring'])
- self.assertEqual(res.status_int, 400)
-
- def test_scan_api(self):
- class NS(object):
- @expose(int)
- @validate(int, int)
- def multiply(self, a, b):
- return a * b
-
- class MyRoot(WSRoot):
- ns = NS()
-
- r = MyRoot()
-
- api = list(scan_api(r))
- assert len(api) == 1
- path, fd, args = api[0]
- assert path == ['ns', 'multiply']
- assert fd._wsme_definition.name == 'multiply'
- assert args == []
-
- def test_scan_subclass(self):
- class MyRoot(WSRoot):
- class SubClass(object):
- pass
-
- r = MyRoot()
- api = list(scan_api(r))
-
- assert len(api) == 0
-
- def test_scan_api_too_deep(self):
- class Loop(object):
- pass
-
- ell = Loop()
- for i in range(0, 21):
- nl = Loop()
- nl.ell = ell
- ell = nl
-
- class MyRoot(WSRoot):
- loop = ell
-
- r = MyRoot()
-
- try:
- list(scan_api(r))
- assert False, "ValueError not raised"
- except ValueError as e:
- assert str(e).startswith("Path is too long")
-
- def test_handle_request(self):
- class MyRoot(WSRoot):
- @expose()
- def touch(self):
- pass
-
- p = DummyProtocol()
- r = MyRoot(protocols=[p])
-
- app = webtest.TestApp(r.wsgiapp())
-
- res = app.get('/')
-
- assert p.lastreq.path == '/'
- assert p.hits == 1
-
- res = app.get('/touch?wsmeproto=dummy')
-
- assert p.lastreq.path == '/touch'
- assert p.hits == 2
-
- class NoPathProto(DummyProtocol):
- def extract_path(self, request):
- return None
-
- p = NoPathProto()
- r = MyRoot(protocols=[p])
-
- app = webtest.TestApp(r.wsgiapp())
-
- res = app.get('/', expect_errors=True)
- print(res.status, res.body)
- assert res.status_int == 400
-
- def test_no_available_protocol(self):
- r = WSRoot()
-
- app = webtest.TestApp(r.wsgiapp())
-
- res = app.get('/', expect_errors=True)
- print(res.status_int)
- assert res.status_int == 406
- print(res.body)
- assert res.body.find(
- b("None of the following protocols can handle this request")) != -1
-
- def test_return_content_type_guess(self):
- class DummierProto(DummyProtocol):
- content_types = ['text/xml', 'text/plain']
-
- r = WSRoot([DummierProto()])
-
- app = webtest.TestApp(r.wsgiapp())
-
- res = app.get('/', expect_errors=True, headers={
- 'Accept': 'text/xml,q=0.8'})
- assert res.status_int == 400
- assert res.content_type == 'text/xml', res.content_type
-
- res = app.get('/', expect_errors=True, headers={
- 'Accept': 'text/plain'})
- assert res.status_int == 400
- assert res.content_type == 'text/plain', res.content_type
-
- def test_double_expose(self):
- try:
- class MyRoot(WSRoot):
- @expose()
- @expose()
- def atest(self):
- pass
- assert False, "A ValueError should have been raised"
- except ValueError:
- pass
-
- def test_multiple_expose(self):
- class MyRoot(WSRoot):
- def multiply(self, a, b):
- return a * b
-
- mul_int = expose(int, int, int, wrap=True)(multiply)
-
- mul_float = expose(
- float, float, float,
- wrap=True)(multiply)
-
- mul_string = expose(
- wsme.types.text, wsme.types.text, int,
- wrap=True)(multiply)
-
- r = MyRoot(['restjson'])
-
- app = webtest.TestApp(r.wsgiapp())
-
- res = app.get('/mul_int?a=2&b=5', headers={
- 'Accept': 'application/json'
- })
-
- self.assertEqual(res.body, b('10'))
-
- res = app.get('/mul_float?a=1.2&b=2.9', headers={
- 'Accept': 'application/json'
- })
-
- self.assertEqual(res.body, b('3.48'))
-
- res = app.get('/mul_string?a=hello&b=2', headers={
- 'Accept': 'application/json'
- })
-
- self.assertEqual(res.body, b('"hellohello"'))
-
- def test_wsattr_mandatory(self):
- class ComplexType(object):
- attr = wsme.types.wsattr(int, mandatory=True)
-
- class MyRoot(WSRoot):
- @expose(int, body=ComplexType)
- @validate(ComplexType)
- def clx(self, a):
- return a.attr
-
- r = MyRoot(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/clx', params={}, expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertEqual(res.status_int, 400)
-
- def test_wsattr_readonly(self):
- class ComplexType(object):
- attr = wsme.types.wsattr(int, readonly=True)
-
- class MyRoot(WSRoot):
- @expose(int, body=ComplexType)
- @validate(ComplexType)
- def clx(self, a):
- return a.attr
-
- r = MyRoot(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/clx', params={'attr': 1005}, expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertIn('Cannot set read only field.',
- res.json_body['faultstring'])
- self.assertIn('1005', res.json_body['faultstring'])
- self.assertEqual(res.status_int, 400)
-
- def test_wsattr_default(self):
- class ComplexType(object):
- attr = wsme.types.wsattr(wsme.types.Enum(str, 'or', 'and'),
- default='and')
-
- class MyRoot(WSRoot):
- @expose(int)
- @validate(ComplexType)
- def clx(self, a):
- return a.attr
-
- r = MyRoot(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/clx', params={}, expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertEqual(res.status_int, 400)
-
- def test_wsproperty_mandatory(self):
- class ComplexType(object):
- def foo(self):
- pass
-
- attr = wsme.types.wsproperty(int, foo, foo, mandatory=True)
-
- class MyRoot(WSRoot):
- @expose(int, body=ComplexType)
- @validate(ComplexType)
- def clx(self, a):
- return a.attr
-
- r = MyRoot(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/clx', params={}, expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertEqual(res.status_int, 400)
-
- def test_validate_enum_mandatory(self):
- class Version(object):
- number = wsme.types.wsattr(wsme.types.Enum(str, 'v1', 'v2'),
- mandatory=True)
-
- class MyWS(WSRoot):
- @expose(str)
- @validate(Version)
- def setcplx(self, version):
- pass
-
- r = MyWS(['restjson'])
- app = webtest.TestApp(r.wsgiapp())
- res = app.post_json('/setcplx', params={'version': {}},
- expect_errors=True,
- headers={'Accept': 'application/json'})
- self.assertEqual(res.status_int, 400)
-
-
-class TestFunctionDefinition(unittest.TestCase):
-
- def test_get_arg(self):
- def myfunc(self):
- pass
-
- fd = wsme_api.FunctionDefinition(wsme_api.FunctionDefinition)
- fd.arguments.append(wsme_api.FunctionArgument('a', int, True, None))
-
- assert fd.get_arg('a').datatype is int
- assert fd.get_arg('b') is None
-
-
-class TestFormatException(unittest.TestCase):
-
- def _test_format_exception(self, exception, debug=False):
- fake_exc_info = (None, exception, None)
- return wsme_api.format_exception(fake_exc_info, debug=debug)
-
- def test_format_client_exception(self):
- faultstring = 'boom'
- ret = self._test_format_exception(exc.ClientSideError(faultstring))
- self.assertIsNone(ret['debuginfo'])
- self.assertEqual('Client', ret['faultcode'])
- self.assertEqual(faultstring, ret['faultstring'])
-
- def test_format_client_exception_unicode(self):
- faultstring = u'\xc3\xa3o'
- ret = self._test_format_exception(exc.ClientSideError(faultstring))
- self.assertIsNone(ret['debuginfo'])
- self.assertEqual('Client', ret['faultcode'])
- self.assertEqual(faultstring, ret['faultstring'])
-
- def test_format_server_exception(self):
- faultstring = 'boom'
- ret = self._test_format_exception(Exception(faultstring))
- self.assertIsNone(ret['debuginfo'])
- self.assertEqual('Server', ret['faultcode'])
- self.assertEqual(faultstring, ret['faultstring'])
-
- def test_format_server_exception_unicode(self):
- faultstring = u'\xc3\xa3o'
- ret = self._test_format_exception(Exception(faultstring))
- self.assertIsNone(ret['debuginfo'])
- self.assertEqual('Server', ret['faultcode'])
- self.assertEqual(faultstring, ret['faultstring'])
-
- def test_format_server_exception_debug(self):
- faultstring = 'boom'
- ret = self._test_format_exception(Exception(faultstring), debug=True)
- # assert debuginfo is populated
- self.assertIsNotNone(ret['debuginfo'])
- self.assertEqual('Server', ret['faultcode'])
- self.assertEqual(faultstring, ret['faultstring'])
diff --git a/wsme/tests/test_exc.py b/wsme/tests/test_exc.py
deleted file mode 100644
index ba903fb..0000000
--- a/wsme/tests/test_exc.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# encoding=utf8
-
-from wsme.exc import (ClientSideError, InvalidInput, MissingArgument,
- UnknownArgument)
-from six import u
-
-
-def test_clientside_error():
- e = ClientSideError("Test")
-
- assert e.faultstring == u("Test")
-
-
-def test_unicode_clientside_error():
- e = ClientSideError(u("\u30d5\u30a1\u30b7\u30ea"))
-
- assert e.faultstring == u("\u30d5\u30a1\u30b7\u30ea")
-
-
-def test_invalidinput():
- e = InvalidInput('field', 'badvalue', "error message")
-
- assert e.faultstring == u(
- "Invalid input for field/attribute field. Value: 'badvalue'. "
- "error message"
- ), e.faultstring
-
-
-def test_missingargument():
- e = MissingArgument('argname', "error message")
-
- assert e.faultstring == \
- u('Missing argument: "argname": error message'), e.faultstring
-
-
-def test_unknownargument():
- e = UnknownArgument('argname', "error message")
-
- assert e.faultstring == \
- u('Unknown argument: "argname": error message'), e.faultstring
diff --git a/wsme/tests/test_protocols.py b/wsme/tests/test_protocols.py
deleted file mode 100644
index f4b7270..0000000
--- a/wsme/tests/test_protocols.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# encoding=utf8
-
-import unittest
-
-from wsme import WSRoot
-from wsme.protocol import getprotocol, CallContext, Protocol
-import wsme.protocol
-
-
-class DummyProtocol(Protocol):
- name = 'dummy'
- content_types = ['', None]
-
- def __init__(self):
- self.hits = 0
-
- def accept(self, req):
- return True
-
- def iter_calls(self, req):
- yield CallContext(req)
-
- def extract_path(self, context):
- return ['touch']
-
- def read_arguments(self, context):
- self.lastreq = context.request
- self.hits += 1
- return {}
-
- def encode_result(self, context, result):
- return str(result)
-
- def encode_error(self, context, infos):
- return str(infos)
-
-
-def test_getprotocol():
- try:
- getprotocol('invalid')
- assert False, "ValueError was not raised"
- except ValueError:
- pass
-
-
-class TestProtocols(unittest.TestCase):
- def test_register_protocol(self):
- wsme.protocol.register_protocol(DummyProtocol)
- assert wsme.protocol.registered_protocols['dummy'] == DummyProtocol
-
- r = WSRoot()
- assert len(r.protocols) == 0
-
- r.addprotocol('dummy')
- assert len(r.protocols) == 1
- assert r.protocols[0].__class__ == DummyProtocol
-
- r = WSRoot(['dummy'])
- assert len(r.protocols) == 1
- assert r.protocols[0].__class__ == DummyProtocol
-
- def test_Protocol(self):
- p = wsme.protocol.Protocol()
- assert p.iter_calls(None) is None
- assert p.extract_path(None) is None
- assert p.read_arguments(None) is None
- assert p.encode_result(None, None) is None
- assert p.encode_sample_value(None, None) == ('none', 'N/A')
- assert p.encode_sample_params(None) == ('none', 'N/A')
- assert p.encode_sample_result(None, None) == ('none', 'N/A')
diff --git a/wsme/tests/test_protocols_commons.py b/wsme/tests/test_protocols_commons.py
deleted file mode 100644
index af5aa05..0000000
--- a/wsme/tests/test_protocols_commons.py
+++ /dev/null
@@ -1,159 +0,0 @@
-# encoding=utf8
-
-import datetime
-import unittest
-
-from wsme.api import FunctionArgument, FunctionDefinition
-from wsme.rest.args import from_param, from_params, args_from_args
-from wsme.exc import InvalidInput
-
-from wsme.types import UserType, Unset, ArrayType, DictType, Base
-
-
-class MyBaseType(Base):
- test = str
-
-
-class MyUserType(UserType):
- basetype = str
-
-
-class DictBasedUserType(UserType):
- basetype = DictType(int, int)
-
-
-class TestProtocolsCommons(unittest.TestCase):
- def test_from_param_date(self):
- assert from_param(datetime.date, '2008-02-28') == \
- datetime.date(2008, 2, 28)
-
- def test_from_param_time(self):
- assert from_param(datetime.time, '12:14:56') == \
- datetime.time(12, 14, 56)
-
- def test_from_param_datetime(self):
- assert from_param(datetime.datetime, '2009-12-23T12:14:56') == \
- datetime.datetime(2009, 12, 23, 12, 14, 56)
-
- def test_from_param_usertype(self):
- assert from_param(MyUserType(), 'test') == 'test'
-
- def test_from_params_empty(self):
- assert from_params(str, {}, '', set()) is Unset
-
- def test_from_params_native_array(self):
- class params(dict):
- def getall(self, path):
- return ['1', '2']
- p = params({'a': []})
- assert from_params(ArrayType(int), p, 'a', set()) == [1, 2]
-
- def test_from_params_empty_array(self):
- assert from_params(ArrayType(int), {}, 'a', set()) is Unset
-
- def test_from_params_dict(self):
- value = from_params(
- DictType(int, str),
- {'a[2]': 'a2', 'a[3]': 'a3'},
- 'a',
- set()
- )
- assert value == {2: 'a2', 3: 'a3'}, value
-
- def test_from_params_dict_unset(self):
- assert from_params(DictType(int, str), {}, 'a', set()) is Unset
-
- def test_from_params_usertype(self):
- value = from_params(
- DictBasedUserType(),
- {'a[2]': '2'},
- 'a',
- set()
- )
- self.assertEqual(value, {2: 2})
-
- def test_args_from_args_usertype(self):
-
- class FakeType(UserType):
- name = 'fake-type'
- basetype = int
-
- fake_type = FakeType()
- fd = FunctionDefinition(FunctionDefinition)
- fd.arguments.append(FunctionArgument('fake-arg', fake_type, True, 0))
-
- new_args = args_from_args(fd, [1], {})
- self.assertEqual([1], new_args[0])
-
- # can't convert str to int
- try:
- args_from_args(fd, ['invalid-argument'], {})
- except InvalidInput as e:
- assert fake_type.name in str(e)
- else:
- self.fail('Should have thrown an InvalidInput')
-
- def test_args_from_args_custom_exc(self):
-
- class FakeType(UserType):
- name = 'fake-type'
- basetype = int
-
- def validate(self, value):
- if value < 10:
- raise ValueError('should be greater than 10')
-
- def frombasetype(self, value):
- self.validate(value)
-
- fake_type = FakeType()
- fd = FunctionDefinition(FunctionDefinition)
- fd.arguments.append(FunctionArgument('fake-arg', fake_type, True, 0))
-
- try:
- args_from_args(fd, [9], {})
- except InvalidInput as e:
- assert fake_type.name in str(e)
- assert 'Error: should be greater than 10' in str(e)
- else:
- self.fail('Should have thrown an InvalidInput')
-
- def test_args_from_args_array_type(self):
- fake_type = ArrayType(MyBaseType)
- fd = FunctionDefinition(FunctionDefinition)
- fd.arguments.append(FunctionArgument('fake-arg', fake_type, True, []))
- try:
- args_from_args(fd, [['invalid-argument']], {})
- except InvalidInput as e:
- assert ArrayType.__name__ in str(e)
- else:
- self.fail('Should have thrown an InvalidInput')
-
-
-class ArgTypeConversion(unittest.TestCase):
-
- def test_int_zero(self):
- self.assertEqual(0, from_param(int, 0))
- self.assertEqual(0, from_param(int, '0'))
-
- def test_int_nonzero(self):
- self.assertEqual(1, from_param(int, 1))
- self.assertEqual(1, from_param(int, '1'))
-
- def test_int_none(self):
- self.assertEqual(None, from_param(int, None))
-
- def test_float_zero(self):
- self.assertEqual(0.0, from_param(float, 0))
- self.assertEqual(0.0, from_param(float, 0.0))
- self.assertEqual(0.0, from_param(float, '0'))
- self.assertEqual(0.0, from_param(float, '0.0'))
-
- def test_float_nonzero(self):
- self.assertEqual(1.0, from_param(float, 1))
- self.assertEqual(1.0, from_param(float, 1.0))
- self.assertEqual(1.0, from_param(float, '1'))
- self.assertEqual(1.0, from_param(float, '1.0'))
-
- def test_float_none(self):
- self.assertEqual(None, from_param(float, None))
diff --git a/wsme/tests/test_restjson.py b/wsme/tests/test_restjson.py
deleted file mode 100644
index 7afbb86..0000000
--- a/wsme/tests/test_restjson.py
+++ /dev/null
@@ -1,779 +0,0 @@
-import base64
-import datetime
-import decimal
-
-import wsme.tests.protocol
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-from wsme.rest.json import fromjson, tojson, parse
-from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
-from wsme.types import isarray, isdict, isusertype, register_type
-from wsme.types import UserType, ArrayType, DictType
-from wsme.rest import expose, validate
-from wsme.exc import ClientSideError, InvalidInput
-
-
-import six
-from six import b, u
-
-if six.PY3:
- from urllib.parse import urlencode
-else:
- from urllib import urlencode # noqa
-
-
-def prepare_value(value, datatype):
- if isinstance(datatype, list):
- return [prepare_value(item, datatype[0]) for item in value]
- if isinstance(datatype, dict):
- key_type, value_type = list(datatype.items())[0]
- return dict((
- (prepare_value(item[0], key_type),
- prepare_value(item[1], value_type))
- for item in value.items()
- ))
- if datatype in (datetime.date, datetime.time, datetime.datetime):
- return value.isoformat()
- if datatype == decimal.Decimal:
- return str(value)
- if datatype == wsme.types.binary:
- return base64.encodestring(value).decode('ascii')
- if datatype == wsme.types.bytes:
- return value.decode('ascii')
- return value
-
-
-def prepare_result(value, datatype):
- print(value, datatype)
- if value is None:
- return None
- if datatype == wsme.types.binary:
- return base64.decodestring(value.encode('ascii'))
- if isusertype(datatype):
- datatype = datatype.basetype
- if isinstance(datatype, list):
- return [prepare_result(item, datatype[0]) for item in value]
- if isarray(datatype):
- return [prepare_result(item, datatype.item_type) for item in value]
- if isinstance(datatype, dict):
- return dict((
- (prepare_result(item[0], list(datatype.keys())[0]),
- prepare_result(item[1], list(datatype.values())[0]))
- for item in value.items()
- ))
- if isdict(datatype):
- return dict((
- (prepare_result(item[0], datatype.key_type),
- prepare_result(item[1], datatype.value_type))
- for item in value.items()
- ))
- if datatype == datetime.date:
- return parse_isodate(value)
- if datatype == datetime.time:
- return parse_isotime(value)
- if datatype == datetime.datetime:
- return parse_isodatetime(value)
- if hasattr(datatype, '_wsme_attributes'):
- for attr in datatype._wsme_attributes:
- if attr.key not in value:
- continue
- value[attr.key] = prepare_result(value[attr.key], attr.datatype)
- return value
- if datatype == wsme.types.bytes:
- return value.encode('ascii')
- if type(value) != datatype:
- print(type(value), datatype)
- return datatype(value)
- return value
-
-
-class CustomInt(UserType):
- basetype = int
- name = "custom integer"
-
-
-class Obj(wsme.types.Base):
- id = int
- name = wsme.types.text
-
-
-class NestedObj(wsme.types.Base):
- o = Obj
-
-
-class CRUDResult(object):
- data = Obj
- message = wsme.types.text
-
- def __init__(self, data=wsme.types.Unset, message=wsme.types.Unset):
- self.data = data
- self.message = message
-
-
-class MiniCrud(object):
- @expose(CRUDResult, method='PUT')
- @validate(Obj)
- def create(self, data):
- print(repr(data))
- return CRUDResult(data, u('create'))
-
- @expose(CRUDResult, method='GET', ignore_extra_args=True)
- @validate(Obj)
- def read(self, ref):
- print(repr(ref))
- if ref.id == 1:
- ref.name = u('test')
- return CRUDResult(ref, u('read'))
-
- @expose(CRUDResult, method='POST')
- @validate(Obj)
- def update(self, data):
- print(repr(data))
- return CRUDResult(data, u('update'))
-
- @expose(CRUDResult, wsme.types.text, body=Obj)
- def update_with_body(self, msg, data):
- print(repr(data))
- return CRUDResult(data, msg)
-
- @expose(CRUDResult, method='DELETE')
- @validate(Obj)
- def delete(self, ref):
- print(repr(ref))
- if ref.id == 1:
- ref.name = u('test')
- return CRUDResult(ref, u('delete'))
-
-
-wsme.tests.protocol.WSTestRoot.crud = MiniCrud()
-
-
-class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
- protocol = 'restjson'
-
- def call(self, fpath, _rt=None, _accept=None, _no_result_decode=False,
- body=None, **kw):
- if body:
- if isinstance(body, tuple):
- body, datatype = body
- else:
- datatype = type(body)
- body = prepare_value(body, datatype)
- content = json.dumps(body)
- else:
- for key in kw:
- if isinstance(kw[key], tuple):
- value, datatype = kw[key]
- else:
- value = kw[key]
- datatype = type(value)
- kw[key] = prepare_value(value, datatype)
- content = json.dumps(kw)
- headers = {
- 'Content-Type': 'application/json',
- }
- if _accept is not None:
- headers["Accept"] = _accept
- res = self.app.post(
- '/' + fpath,
- content,
- headers=headers,
- expect_errors=True)
- print("Received:", res.body)
-
- if _no_result_decode:
- return res
-
- r = json.loads(res.text)
- if res.status_int == 200:
- if _rt and r:
- r = prepare_result(r, _rt)
- return r
- else:
- raise wsme.tests.protocol.CallException(
- r['faultcode'],
- r['faultstring'],
- r.get('debuginfo')
- )
-
- return json.loads(res.text)
-
- def test_fromjson(self):
- assert fromjson(str, None) is None
-
- def test_keyargs(self):
- r = self.app.get('/argtypes/setint.json?value=2')
- print(r)
- assert json.loads(r.text) == 2
-
- nestedarray = 'value[0].inner.aint=54&value[1].inner.aint=55'
- r = self.app.get('/argtypes/setnestedarray.json?' + nestedarray)
- print(r)
- assert json.loads(r.text) == [
- {'inner': {'aint': 54}},
- {'inner': {'aint': 55}}]
-
- def test_form_urlencoded_args(self):
- params = {
- 'value[0].inner.aint': 54,
- 'value[1].inner.aint': 55
- }
- body = urlencode(params)
- r = self.app.post(
- '/argtypes/setnestedarray.json',
- body,
- headers={'Content-Type': 'application/x-www-form-urlencoded'}
- )
- print(r)
-
- assert json.loads(r.text) == [
- {'inner': {'aint': 54}},
- {'inner': {'aint': 55}}]
-
- def test_body_and_params(self):
- r = self.app.post('/argtypes/setint.json?value=2', '{"value": 2}',
- headers={"Content-Type": "application/json"},
- expect_errors=True)
- print(r)
- assert r.status_int == 400
- assert json.loads(r.text)['faultstring'] == \
- "Parameter value was given several times"
-
- def test_inline_body(self):
- params = urlencode({'__body__': '{"value": 4}'})
- r = self.app.get('/argtypes/setint.json?' + params)
- print(r)
- assert json.loads(r.text) == 4
-
- def test_empty_body(self):
- params = urlencode({'__body__': ''})
- r = self.app.get('/returntypes/getint.json?' + params)
- print(r)
- assert json.loads(r.text) == 2
-
- def test_invalid_content_type_body(self):
- r = self.app.post('/argtypes/setint.json', '{"value": 2}',
- headers={"Content-Type": "application/invalid"},
- expect_errors=True)
- print(r)
- assert r.status_int == 415
- assert json.loads(r.text)['faultstring'] == \
- "Unknown mimetype: application/invalid"
-
- def test_invalid_json_body(self):
- r = self.app.post('/argtypes/setint.json', '{"value": 2',
- headers={"Content-Type": "application/json"},
- expect_errors=True)
- print(r)
- assert r.status_int == 400
- assert json.loads(r.text)['faultstring'] == \
- "Request is not in valid JSON format"
-
- def test_unknown_arg(self):
- r = self.app.post('/returntypes/getint.json', '{"a": 2}',
- headers={"Content-Type": "application/json"},
- expect_errors=True)
- print(r)
- assert r.status_int == 400
- assert json.loads(r.text)['faultstring'].startswith(
- "Unknown argument:"
- )
-
- r = self.app.get('/returntypes/getint.json?a=2', expect_errors=True)
- print(r)
- assert r.status_int == 400
- assert json.loads(r.text)['faultstring'].startswith(
- "Unknown argument:"
- )
-
- def test_set_custom_object(self):
- r = self.app.post(
- '/argtypes/setcustomobject',
- '{"value": {"aint": 2, "name": "test"}}',
- headers={"Content-Type": "application/json"}
- )
- self.assertEqual(r.status_int, 200)
- self.assertEqual(r.json, {'aint': 2, 'name': 'test'})
-
- def test_set_extended_int(self):
- r = self.app.post(
- '/argtypes/setextendedint',
- '{"value": 3}',
- headers={"Content-Type": "application/json"}
- )
- self.assertEqual(r.status_int, 200)
- self.assertEqual(r.json, 3)
-
- def test_unset_attrs(self):
- class AType(object):
- attr = int
-
- wsme.types.register_type(AType)
-
- j = tojson(AType, AType())
- assert j == {}
-
- def test_array_tojson(self):
- assert tojson([int], None) is None
- assert tojson([int], []) == []
- assert tojson([str], ['1', '4']) == ['1', '4']
-
- def test_dict_tojson(self):
- assert tojson({int: str}, None) is None
- assert tojson({int: str}, {5: '5'}) == {5: '5'}
-
- def test_None_tojson(self):
- for dt in (datetime.date, datetime.time, datetime.datetime,
- decimal.Decimal):
- assert tojson(dt, None) is None
-
- def test_None_fromjson(self):
- for dt in (str, int, datetime.date, datetime.time, datetime.datetime,
- decimal.Decimal, [int], {int: int}):
- assert fromjson(dt, None) is None
-
- def test_parse_valid_date(self):
- j = parse('{"a": "2011-01-01"}', {'a': datetime.date}, False)
- assert isinstance(j['a'], datetime.date)
-
- def test_invalid_root_dict_fromjson(self):
- try:
- parse('["invalid"]', {'a': ArrayType(str)}, False)
- assert False
- except Exception as e:
- assert isinstance(e, ClientSideError)
- assert e.msg == "Request must be a JSON dict"
-
- def test_invalid_list_fromjson(self):
- jlist = "invalid"
- try:
- parse('{"a": "%s"}' % jlist, {'a': ArrayType(str)}, False)
- assert False
- except Exception as e:
- assert isinstance(e, InvalidInput)
- assert e.fieldname == 'a'
- assert e.value == jlist
- assert e.msg == "Value not a valid list: %s" % jlist
-
- def test_invalid_dict_fromjson(self):
- jdict = "invalid"
- try:
- parse('{"a": "%s"}' % jdict, {'a': DictType(str, str)}, False)
- assert False
- except Exception as e:
- assert isinstance(e, InvalidInput)
- assert e.fieldname == 'a'
- assert e.value == jdict
- assert e.msg == "Value not a valid dict: %s" % jdict
-
- def test_invalid_date_fromjson(self):
- jdate = "2015-01-invalid"
- try:
- parse('{"a": "%s"}' % jdate, {'a': datetime.date}, False)
- assert False
- except Exception as e:
- assert isinstance(e, InvalidInput)
- assert e.fieldname == 'a'
- assert e.value == jdate
- assert e.msg == "'%s' is not a legal date value" % jdate
-
- def test_parse_valid_date_bodyarg(self):
- j = parse('"2011-01-01"', {'a': datetime.date}, True)
- assert isinstance(j['a'], datetime.date)
-
- def test_invalid_date_fromjson_bodyarg(self):
- jdate = "2015-01-invalid"
- try:
- parse('"%s"' % jdate, {'a': datetime.date}, True)
- assert False
- except Exception as e:
- assert isinstance(e, InvalidInput)
- assert e.fieldname == 'a'
- assert e.value == jdate
- assert e.msg == "'%s' is not a legal date value" % jdate
-
- def test_valid_str_to_builtin_fromjson(self):
- types = six.integer_types + (bool, float)
- value = '2'
- for t in types:
- for ba in True, False:
- jd = '%s' if ba else '{"a": %s}'
- i = parse(jd % value, {'a': t}, ba)
- self.assertEqual(
- i, {'a': t(value)},
- "Parsed value does not correspond for %s: "
- "%s != {'a': %s}" % (
- t, repr(i), repr(t(value))
- )
- )
- self.assertIsInstance(i['a'], t)
-
- def test_valid_int_fromjson(self):
- value = 2
- for ba in True, False:
- jd = '%d' if ba else '{"a": %d}'
- i = parse(jd % value, {'a': int}, ba)
- self.assertEqual(i, {'a': 2})
- self.assertIsInstance(i['a'], int)
-
- def test_valid_num_to_float_fromjson(self):
- values = 2, 2.3
- for v in values:
- for ba in True, False:
- jd = '%f' if ba else '{"a": %f}'
- i = parse(jd % v, {'a': float}, ba)
- self.assertEqual(i, {'a': float(v)})
- self.assertIsInstance(i['a'], float)
-
- def test_invalid_str_to_buitin_fromjson(self):
- types = six.integer_types + (float, bool)
- value = '2a'
- for t in types:
- for ba in True, False:
- jd = '"%s"' if ba else '{"a": "%s"}'
- try:
- parse(jd % value, {'a': t}, ba)
- assert False, (
- "Value '%s' should not parse correctly for %s." %
- (value, t)
- )
- except ClientSideError as e:
- self.assertIsInstance(e, InvalidInput)
- self.assertEqual(e.fieldname, 'a')
- self.assertEqual(e.value, value)
-
- def test_ambiguous_to_bool(self):
- amb_values = ('', 'randomstring', '2', '-32', 'not true')
- for value in amb_values:
- for ba in True, False:
- jd = '"%s"' if ba else '{"a": "%s"}'
- try:
- parse(jd % value, {'a': bool}, ba)
- assert False, (
- "Value '%s' should not parse correctly for %s." %
- (value, bool)
- )
- except ClientSideError as e:
- self.assertIsInstance(e, InvalidInput)
- self.assertEqual(e.fieldname, 'a')
- self.assertEqual(e.value, value)
-
- def test_true_strings_to_bool(self):
- true_values = ('true', 't', 'yes', 'y', 'on', '1')
- for value in true_values:
- for ba in True, False:
- jd = '"%s"' if ba else '{"a": "%s"}'
- i = parse(jd % value, {'a': bool}, ba)
- self.assertIsInstance(i['a'], bool)
- self.assertTrue(i['a'])
-
- def test_false_strings_to_bool(self):
- false_values = ('false', 'f', 'no', 'n', 'off', '0')
- for value in false_values:
- for ba in True, False:
- jd = '"%s"' if ba else '{"a": "%s"}'
- i = parse(jd % value, {'a': bool}, ba)
- self.assertIsInstance(i['a'], bool)
- self.assertFalse(i['a'])
-
- def test_true_ints_to_bool(self):
- true_values = (1, 5, -3)
- for value in true_values:
- for ba in True, False:
- jd = '%d' if ba else '{"a": %d}'
- i = parse(jd % value, {'a': bool}, ba)
- self.assertIsInstance(i['a'], bool)
- self.assertTrue(i['a'])
-
- def test_false_ints_to_bool(self):
- value = 0
- for ba in True, False:
- jd = '%d' if ba else '{"a": %d}'
- i = parse(jd % value, {'a': bool}, ba)
- self.assertIsInstance(i['a'], bool)
- self.assertFalse(i['a'])
-
- def test_valid_simple_custom_type_fromjson(self):
- value = 2
- for ba in True, False:
- jd = '"%d"' if ba else '{"a": "%d"}'
- i = parse(jd % value, {'a': CustomInt()}, ba)
- self.assertEqual(i, {'a': 2})
- self.assertIsInstance(i['a'], int)
-
- def test_invalid_simple_custom_type_fromjson(self):
- value = '2b'
- for ba in True, False:
- jd = '"%s"' if ba else '{"a": "%s"}'
- try:
- i = parse(jd % value, {'a': CustomInt()}, ba)
- self.assertEqual(i, {'a': 2})
- except ClientSideError as e:
- self.assertIsInstance(e, InvalidInput)
- self.assertEqual(e.fieldname, 'a')
- self.assertEqual(e.value, value)
- self.assertEqual(
- e.msg,
- "invalid literal for int() with base 10: '%s'" % value
- )
-
- def test_parse_unexpected_attribute(self):
- o = {
- "id": "1",
- "name": "test",
- "other": "unknown",
- "other2": "still unknown",
- }
- for ba in True, False:
- jd = o if ba else {"o": o}
- try:
- parse(json.dumps(jd), {'o': Obj}, ba)
- raise AssertionError("Object should not parse correcty.")
- except wsme.exc.UnknownAttribute as e:
- self.assertEqual(e.attributes, set(['other', 'other2']))
-
- def test_parse_unexpected_nested_attribute(self):
- no = {
- "o": {
- "id": "1",
- "name": "test",
- "other": "unknown",
- },
- }
- for ba in False, True:
- jd = no if ba else {"no": no}
- try:
- parse(json.dumps(jd), {'no': NestedObj}, ba)
- except wsme.exc.UnknownAttribute as e:
- self.assertEqual(e.attributes, set(['other']))
- self.assertEqual(e.fieldname, "no.o")
-
- def test_nest_result(self):
- self.root.protocols[0].nest_result = True
- r = self.app.get('/returntypes/getint.json')
- print(r)
- assert json.loads(r.text) == {"result": 2}
-
- def test_encode_sample_value(self):
- class MyType(object):
- aint = int
- astr = str
-
- register_type(MyType)
-
- v = MyType()
- v.aint = 4
- v.astr = 's'
-
- r = wsme.rest.json.encode_sample_value(MyType, v, True)
- print(r)
- assert r[0] == ('javascript')
- assert r[1] == json.dumps({'aint': 4, 'astr': 's'}, ensure_ascii=False,
- indent=4, sort_keys=True)
-
- def test_bytes_tojson(self):
- assert tojson(wsme.types.bytes, None) is None
- assert tojson(wsme.types.bytes, b('ascii')) == u('ascii')
-
- def test_encode_sample_params(self):
- r = wsme.rest.json.encode_sample_params(
- [('a', int, 2)], True
- )
- assert r[0] == 'javascript', r[0]
- assert r[1] == '''{
- "a": 2
-}''', r[1]
-
- def test_encode_sample_result(self):
- r = wsme.rest.json.encode_sample_result(
- int, 2, True
- )
- assert r[0] == 'javascript', r[0]
- assert r[1] == '''2'''
-
- def test_PUT(self):
- data = {"id": 1, "name": u("test")}
- content = json.dumps(dict(data=data))
- headers = {
- 'Content-Type': 'application/json',
- }
- res = self.app.put(
- '/crud',
- content,
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
- assert result['message'] == "create"
-
- def test_GET(self):
- headers = {
- 'Accept': 'application/json',
- }
- res = self.app.get(
- '/crud?ref.id=1',
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
-
- def test_GET_complex_accept(self):
- headers = {
- 'Accept': 'text/html,application/xml;q=0.9,*/*;q=0.8'
- }
- res = self.app.get(
- '/crud?ref.id=1',
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
-
- def test_GET_complex_choose_xml(self):
- headers = {
- 'Accept': 'text/html,text/xml;q=0.9,*/*;q=0.8'
- }
- res = self.app.get(
- '/crud?ref.id=1',
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- assert res.content_type == 'text/xml'
-
- def test_GET_complex_accept_no_match(self):
- headers = {
- 'Accept': 'text/html,application/xml;q=0.9'
- }
- res = self.app.get(
- '/crud?ref.id=1',
- headers=headers,
- status=406)
- print("Received:", res.body)
- assert res.body == b("Unacceptable Accept type: "
- "text/html, application/xml;q=0.9 not in "
- "['application/json', 'text/javascript', "
- "'application/javascript', 'text/xml']")
-
- def test_GET_bad_simple_accept(self):
- headers = {
- 'Accept': 'text/plain',
- }
- res = self.app.get(
- '/crud?ref.id=1',
- headers=headers,
- status=406)
- print("Received:", res.body)
- assert res.body == b("Unacceptable Accept type: text/plain not in "
- "['application/json', 'text/javascript', "
- "'application/javascript', 'text/xml']")
-
- def test_POST(self):
- headers = {
- 'Content-Type': 'application/json',
- }
- res = self.app.post(
- '/crud',
- json.dumps(dict(data=dict(id=1, name=u('test')))),
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
- assert result['message'] == "update"
-
- def test_POST_bad_content_type(self):
- headers = {
- 'Content-Type': 'text/plain',
- }
- res = self.app.post(
- '/crud',
- json.dumps(dict(data=dict(id=1, name=u('test')))),
- headers=headers,
- status=415)
- print("Received:", res.body)
- assert res.body == b("Unacceptable Content-Type: text/plain not in "
- "['application/json', 'text/javascript', "
- "'application/javascript', 'text/xml']")
-
- def test_DELETE(self):
- res = self.app.delete(
- '/crud.json?ref.id=1',
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
- assert result['message'] == "delete"
-
- def test_extra_arguments(self):
- headers = {
- 'Accept': 'application/json',
- }
- res = self.app.get(
- '/crud?ref.id=1&extraarg=foo',
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
- assert result['message'] == "read"
-
- def test_unexpected_extra_arg(self):
- headers = {
- 'Content-Type': 'application/json',
- }
- data = {"id": 1, "name": "test"}
- content = json.dumps({"data": data, "other": "unexpected"})
- res = self.app.put(
- '/crud',
- content,
- headers=headers,
- expect_errors=True)
- self.assertEqual(res.status_int, 400)
-
- def test_unexpected_extra_attribute(self):
- """Expect a failure if we send an unexpected object attribute."""
- headers = {
- 'Content-Type': 'application/json',
- }
- data = {"id": 1, "name": "test", "other": "unexpected"}
- content = json.dumps({"data": data})
- res = self.app.put(
- '/crud',
- content,
- headers=headers,
- expect_errors=True)
- self.assertEqual(res.status_int, 400)
-
- def test_body_arg(self):
- headers = {
- 'Content-Type': 'application/json',
- }
- res = self.app.post(
- '/crud/update_with_body?msg=hello',
- json.dumps(dict(id=1, name=u('test'))),
- headers=headers,
- expect_errors=False)
- print("Received:", res.body)
- result = json.loads(res.text)
- print(result)
- assert result['data']['id'] == 1
- assert result['data']['name'] == u("test")
- assert result['message'] == "hello"
diff --git a/wsme/tests/test_restxml.py b/wsme/tests/test_restxml.py
deleted file mode 100644
index b677a65..0000000
--- a/wsme/tests/test_restxml.py
+++ /dev/null
@@ -1,211 +0,0 @@
-import decimal
-import datetime
-import base64
-
-from six import u, b
-import six
-
-import wsme.tests.protocol
-from wsme.utils import parse_isodatetime, parse_isodate, parse_isotime
-from wsme.types import isarray, isdict, isusertype, register_type
-
-from wsme.rest.xml import fromxml, toxml
-
-try:
- import xml.etree.ElementTree as et
-except ImportError:
- import cElementTree as et # noqa
-
-
-def dumpxml(key, obj, datatype=None):
- el = et.Element(key)
- if isinstance(obj, tuple):
- obj, datatype = obj
- if isinstance(datatype, list):
- for item in obj:
- el.append(dumpxml('item', item, datatype[0]))
- elif isinstance(datatype, dict):
- key_type, value_type = list(datatype.items())[0]
- for item in obj.items():
- node = et.SubElement(el, 'item')
- node.append(dumpxml('key', item[0], key_type))
- node.append(dumpxml('value', item[1], value_type))
- elif datatype == wsme.types.binary:
- el.text = base64.encodestring(obj).decode('ascii')
- elif isinstance(obj, wsme.types.bytes):
- el.text = obj.decode('ascii')
- elif isinstance(obj, wsme.types.text):
- el.text = obj
- elif type(obj) in (int, float, bool, decimal.Decimal):
- el.text = six.text_type(obj)
- elif type(obj) in (datetime.date, datetime.time, datetime.datetime):
- el.text = obj.isoformat()
- elif isinstance(obj, type(None)):
- el.set('nil', 'true')
- elif hasattr(datatype, '_wsme_attributes'):
- for attr in datatype._wsme_attributes:
- name = attr.name
- if name not in obj:
- continue
- o = obj[name]
- el.append(dumpxml(name, o, attr.datatype))
- elif type(obj) == dict:
- for name, value in obj.items():
- el.append(dumpxml(name, value))
- print(obj, datatype, et.tostring(el))
- return el
-
-
-def loadxml(el, datatype):
- print(el, datatype, len(el))
- if el.get('nil') == 'true':
- return None
- if isinstance(datatype, list):
- return [loadxml(item, datatype[0]) for item in el.findall('item')]
- elif isarray(datatype):
- return [
- loadxml(item, datatype.item_type) for item in el.findall('item')
- ]
- elif isinstance(datatype, dict):
- key_type, value_type = list(datatype.items())[0]
- return dict((
- (loadxml(item.find('key'), key_type),
- loadxml(item.find('value'), value_type))
- for item in el.findall('item')
- ))
- elif isdict(datatype):
- return dict((
- (loadxml(item.find('key'), datatype.key_type),
- loadxml(item.find('value'), datatype.value_type))
- for item in el.findall('item')
- ))
- elif isdict(datatype):
- return dict((
- (loadxml(item.find('key'), datatype.key_type),
- loadxml(item.find('value'), datatype.value_type))
- for item in el.findall('item')
- ))
- elif len(el):
- d = {}
- for attr in datatype._wsme_attributes:
- name = attr.name
- child = el.find(name)
- print(name, attr, child)
- if child is not None:
- d[name] = loadxml(child, attr.datatype)
- print(d)
- return d
- else:
- if datatype == wsme.types.binary:
- return base64.decodestring(el.text.encode('ascii'))
- if isusertype(datatype):
- datatype = datatype.basetype
- if datatype == datetime.date:
- return parse_isodate(el.text)
- if datatype == datetime.time:
- return parse_isotime(el.text)
- if datatype == datetime.datetime:
- return parse_isodatetime(el.text)
- if datatype == wsme.types.text:
- return datatype(el.text if el.text else u(''))
- if datatype == bool:
- return el.text.lower() != 'false'
- if datatype is None:
- return el.text
- if datatype is wsme.types.bytes:
- return el.text.encode('ascii')
- return datatype(el.text)
-
-
-class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
- protocol = 'restxml'
-
- def call(self, fpath, _rt=None, _accept=None, _no_result_decode=False,
- body=None, **kw):
- if body:
- el = dumpxml('body', body)
- else:
- el = dumpxml('parameters', kw)
- content = et.tostring(el)
- headers = {
- 'Content-Type': 'text/xml',
- }
- if _accept is not None:
- headers['Accept'] = _accept
- res = self.app.post(
- '/' + fpath,
- content,
- headers=headers,
- expect_errors=True)
- print("Received:", res.body)
-
- if _no_result_decode:
- return res
-
- el = et.fromstring(res.body)
- if el.tag == 'error':
- raise wsme.tests.protocol.CallException(
- el.find('faultcode').text,
- el.find('faultstring').text,
- el.find('debuginfo') is not None and
- el.find('debuginfo').text or None
- )
-
- else:
- return loadxml(et.fromstring(res.body), _rt)
-
- def test_encode_sample_value(self):
- class MyType(object):
- aint = int
- atext = wsme.types.text
-
- register_type(MyType)
-
- value = MyType()
- value.aint = 5
- value.atext = u('test')
-
- language, sample = wsme.rest.xml.encode_sample_value(
- MyType, value, True)
- print(language, sample)
-
- assert language == 'xml'
- assert sample == b("""<value>
- <aint>5</aint>
- <atext>test</atext>
-</value>""")
-
- def test_encode_sample_params(self):
- lang, content = wsme.rest.xml.encode_sample_params(
- [('a', int, 2)], True)
- assert lang == 'xml', lang
- assert content == b('<parameters>\n <a>2</a>\n</parameters>'), content
-
- def test_encode_sample_result(self):
- lang, content = wsme.rest.xml.encode_sample_result(int, 2, True)
- assert lang == 'xml', lang
- assert content == b('<result>2</result>'), content
-
- def test_nil_fromxml(self):
- for dt in (
- str, [int], {int: str}, bool,
- datetime.date, datetime.time, datetime.datetime):
- e = et.Element('value', nil='true')
- assert fromxml(dt, e) is None
-
- def test_nil_toxml(self):
- for dt in (
- wsme.types.bytes,
- [int], {int: str}, bool,
- datetime.date, datetime.time, datetime.datetime):
- x = et.tostring(toxml(dt, 'value', None))
- assert x == b('<value nil="true" />'), x
-
- def test_unset_attrs(self):
- class AType(object):
- someattr = wsme.types.bytes
-
- wsme.types.register_type(AType)
-
- x = et.tostring(toxml(AType, 'value', AType()))
- assert x == b('<value />'), x
diff --git a/wsme/tests/test_root.py b/wsme/tests/test_root.py
deleted file mode 100644
index 8ccb0c1..0000000
--- a/wsme/tests/test_root.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# encoding=utf8
-
-import unittest
-
-from wsme import WSRoot
-import wsme.protocol
-import wsme.rest.protocol
-from wsme.root import default_prepare_response_body
-
-from six import b, u
-from webob import Request
-
-
-class TestRoot(unittest.TestCase):
- def test_default_transaction(self):
- import transaction
- root = WSRoot(transaction=True)
- assert root._transaction is transaction
-
- txn = root.begin()
- txn.abort()
-
- def test_default_prepare_response_body(self):
- default_prepare_response_body(None, [b('a')]) == b('a')
- default_prepare_response_body(None, [b('a'), b('b')]) == b('a\nb')
- default_prepare_response_body(None, [u('a')]) == u('a')
- default_prepare_response_body(None, [u('a'), u('b')]) == u('a\nb')
-
- def test_protocol_selection_error(self):
- class P(wsme.protocol.Protocol):
- name = "test"
-
- def accept(self, r):
- raise Exception('test')
-
- root = WSRoot()
- root.addprotocol(P())
-
- from webob import Request
- req = Request.blank('/test?check=a&check=b&name=Bob')
- res = root._handle_request(req)
- assert res.status_int == 500
- assert res.content_type == 'text/plain'
- assert (res.text ==
- 'Unexpected error while selecting protocol: test'), req.text
-
- def test_protocol_selection_accept_mismatch(self):
- """Verify that we get a 406 error on wrong Accept header."""
- class P(wsme.protocol.Protocol):
- name = "test"
-
- def accept(self, r):
- return False
-
- root = WSRoot()
- root.addprotocol(wsme.rest.protocol.RestProtocol())
- root.addprotocol(P())
-
- req = Request.blank('/test?check=a&check=b&name=Bob')
- req.method = 'GET'
- res = root._handle_request(req)
- assert res.status_int == 406
- assert res.content_type == 'text/plain'
- assert res.text.startswith(
- 'None of the following protocols can handle this request'
- ), req.text
-
- def test_protocol_selection_content_type_mismatch(self):
- """Verify that we get a 415 error on wrong Content-Type header."""
- class P(wsme.protocol.Protocol):
- name = "test"
-
- def accept(self, r):
- return False
-
- root = WSRoot()
- root.addprotocol(wsme.rest.protocol.RestProtocol())
- root.addprotocol(P())
-
- req = Request.blank('/test?check=a&check=b&name=Bob')
- req.method = 'POST'
- req.headers['Content-Type'] = "test/unsupported"
- res = root._handle_request(req)
- assert res.status_int == 415
- assert res.content_type == 'text/plain'
- assert res.text.startswith(
- 'Unacceptable Content-Type: test/unsupported not in'
- ), req.text
-
- def test_protocol_selection_get_method(self):
- class P(wsme.protocol.Protocol):
- name = "test"
-
- def accept(self, r):
- return True
-
- root = WSRoot()
- root.addprotocol(wsme.rest.protocol.RestProtocol())
- root.addprotocol(P())
-
- req = Request.blank('/test?check=a&check=b&name=Bob')
- req.method = 'GET'
- req.headers['Accept'] = 'test/fake'
- p = root._select_protocol(req)
- assert p.name == "test"
-
- def test_protocol_selection_post_method(self):
- class P(wsme.protocol.Protocol):
- name = "test"
-
- def accept(self, r):
- return True
-
- root = WSRoot()
- root.addprotocol(wsme.rest.protocol.RestProtocol())
- root.addprotocol(P())
-
- req = Request.blank('/test?check=a&check=b&name=Bob')
- req.headers['Content-Type'] = 'test/fake'
- req.method = 'POST'
- p = root._select_protocol(req)
- assert p.name == "test"
diff --git a/wsme/tests/test_spore.py b/wsme/tests/test_spore.py
deleted file mode 100644
index d6509f3..0000000
--- a/wsme/tests/test_spore.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import unittest
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-from wsme.tests.protocol import WSTestRoot
-import wsme.tests.test_restjson
-import wsme.spore
-
-
-class TestSpore(unittest.TestCase):
- def test_spore(self):
- spore = wsme.spore.getdesc(WSTestRoot())
-
- print(spore)
-
- spore = json.loads(spore)
-
- assert len(spore['methods']) == 51, str(len(spore['methods']))
-
- m = spore['methods']['argtypes_setbytesarray']
- assert m['path'] == 'argtypes/setbytesarray', m['path']
- assert m['optional_params'] == ['value']
- assert m['method'] == 'POST'
-
- m = spore['methods']['argtypes_setdecimal']
- assert m['path'] == 'argtypes/setdecimal'
- assert m['required_params'] == ['value']
- assert m['method'] == 'GET'
-
- m = spore['methods']['crud_create']
- assert m['path'] == 'crud'
- assert m['method'] == 'PUT'
- assert m['optional_params'] == ['data']
-
- m = spore['methods']['crud_read']
- assert m['path'] == 'crud'
- assert m['method'] == 'GET'
- assert m['required_params'] == ['ref']
-
- m = spore['methods']['crud_update']
- assert m['path'] == 'crud'
- assert m['method'] == 'POST'
- assert m['optional_params'] == ['data']
-
- m = spore['methods']['crud_delete']
- assert m['path'] == 'crud'
- assert m['method'] == 'DELETE'
- assert m['optional_params'] == ['ref']
diff --git a/wsme/tests/test_types.py b/wsme/tests/test_types.py
deleted file mode 100644
index 4a07130..0000000
--- a/wsme/tests/test_types.py
+++ /dev/null
@@ -1,667 +0,0 @@
-import re
-try:
- import unittest2 as unittest
-except ImportError:
- import unittest
-import six
-
-from wsme import exc
-from wsme import types
-
-
-def gen_class():
- d = {}
- exec('''class tmp(object): pass''', d)
- return d['tmp']
-
-
-class TestTypes(unittest.TestCase):
- def setUp(self):
- types.registry = types.Registry()
-
- def test_default_usertype(self):
- class MyType(types.UserType):
- basetype = str
-
- My = MyType()
-
- assert My.validate('a') == 'a'
- assert My.tobasetype('a') == 'a'
- assert My.frombasetype('a') == 'a'
-
- def test_unset(self):
- u = types.Unset
-
- assert not u
-
- def test_flat_type(self):
- class Flat(object):
- aint = int
- abytes = six.binary_type
- atext = six.text_type
- afloat = float
-
- types.register_type(Flat)
-
- assert len(Flat._wsme_attributes) == 4
- attrs = Flat._wsme_attributes
- print(attrs)
-
- assert attrs[0].key == 'aint'
- assert attrs[0].name == 'aint'
- assert isinstance(attrs[0], types.wsattr)
- assert attrs[0].datatype == int
- assert attrs[0].mandatory is False
- assert attrs[1].key == 'abytes'
- assert attrs[1].name == 'abytes'
- assert attrs[2].key == 'atext'
- assert attrs[2].name == 'atext'
- assert attrs[3].key == 'afloat'
- assert attrs[3].name == 'afloat'
-
- def test_private_attr(self):
- class WithPrivateAttrs(object):
- _private = 12
-
- types.register_type(WithPrivateAttrs)
-
- assert len(WithPrivateAttrs._wsme_attributes) == 0
-
- def test_attribute_order(self):
- class ForcedOrder(object):
- _wsme_attr_order = ('a2', 'a1', 'a3')
- a1 = int
- a2 = int
- a3 = int
-
- types.register_type(ForcedOrder)
-
- print(ForcedOrder._wsme_attributes)
- assert ForcedOrder._wsme_attributes[0].key == 'a2'
- assert ForcedOrder._wsme_attributes[1].key == 'a1'
- assert ForcedOrder._wsme_attributes[2].key == 'a3'
-
- c = gen_class()
- print(c)
- types.register_type(c)
- del c._wsme_attributes
-
- c.a2 = int
- c.a1 = int
- c.a3 = int
-
- types.register_type(c)
-
- assert c._wsme_attributes[0].key == 'a1', c._wsme_attributes[0].key
- assert c._wsme_attributes[1].key == 'a2'
- assert c._wsme_attributes[2].key == 'a3'
-
- def test_wsproperty(self):
- class WithWSProp(object):
- def __init__(self):
- self._aint = 0
-
- def get_aint(self):
- return self._aint
-
- def set_aint(self, value):
- self._aint = value
-
- aint = types.wsproperty(int, get_aint, set_aint, mandatory=True)
-
- types.register_type(WithWSProp)
-
- print(WithWSProp._wsme_attributes)
- assert len(WithWSProp._wsme_attributes) == 1
- a = WithWSProp._wsme_attributes[0]
- assert a.key == 'aint'
- assert a.datatype == int
- assert a.mandatory
-
- o = WithWSProp()
- o.aint = 12
-
- assert o.aint == 12
-
- def test_nested(self):
- class Inner(object):
- aint = int
-
- class Outer(object):
- inner = Inner
-
- types.register_type(Outer)
-
- assert hasattr(Inner, '_wsme_attributes')
- assert len(Inner._wsme_attributes) == 1
-
- def test_inspect_with_inheritance(self):
- class Parent(object):
- parent_attribute = int
-
- class Child(Parent):
- child_attribute = int
-
- types.register_type(Parent)
- types.register_type(Child)
-
- assert len(Child._wsme_attributes) == 2
-
- def test_selfreftype(self):
- class SelfRefType(object):
- pass
-
- SelfRefType.parent = SelfRefType
-
- types.register_type(SelfRefType)
-
- def test_inspect_with_property(self):
- class AType(object):
- @property
- def test(self):
- return 'test'
-
- types.register_type(AType)
-
- assert len(AType._wsme_attributes) == 0
- assert AType().test == 'test'
-
- def test_enum(self):
- aenum = types.Enum(str, 'v1', 'v2')
- assert aenum.basetype is str
-
- class AType(object):
- a = aenum
-
- types.register_type(AType)
-
- assert AType.a.datatype is aenum
-
- obj = AType()
- obj.a = 'v1'
- assert obj.a == 'v1', repr(obj.a)
-
- self.assertRaisesRegexp(exc.InvalidInput,
- "Invalid input for field/attribute a. \
-Value: 'v3'. Value should be one of: v., v.",
- setattr,
- obj,
- 'a',
- 'v3')
-
- def test_attribute_validation(self):
- class AType(object):
- alist = [int]
- aint = int
-
- types.register_type(AType)
-
- obj = AType()
-
- obj.alist = [1, 2, 3]
- assert obj.alist == [1, 2, 3]
- obj.aint = 5
- assert obj.aint == 5
-
- self.assertRaises(exc.InvalidInput, setattr, obj, 'alist', 12)
- self.assertRaises(exc.InvalidInput, setattr, obj, 'alist', [2, 'a'])
-
- def test_attribute_validation_minimum(self):
- class ATypeInt(object):
- attr = types.IntegerType(minimum=1, maximum=5)
-
- types.register_type(ATypeInt)
-
- obj = ATypeInt()
- obj.attr = 2
-
- # comparison between 'zero' value and intger minimum (1) raises a
- # TypeError which must be wrapped into an InvalidInput exception
- self.assertRaises(exc.InvalidInput, setattr, obj, 'attr', 'zero')
-
- def test_text_attribute_conversion(self):
- class SType(object):
- atext = types.text
- abytes = types.bytes
-
- types.register_type(SType)
-
- obj = SType()
-
- obj.atext = six.b('somebytes')
- assert obj.atext == six.u('somebytes')
- assert isinstance(obj.atext, types.text)
-
- obj.abytes = six.u('sometext')
- assert obj.abytes == six.b('sometext')
- assert isinstance(obj.abytes, types.bytes)
-
- def test_named_attribute(self):
- class ABCDType(object):
- a_list = types.wsattr([int], name='a.list')
- astr = str
-
- types.register_type(ABCDType)
-
- assert len(ABCDType._wsme_attributes) == 2
- attrs = ABCDType._wsme_attributes
-
- assert attrs[0].key == 'a_list', attrs[0].key
- assert attrs[0].name == 'a.list', attrs[0].name
- assert attrs[1].key == 'astr', attrs[1].key
- assert attrs[1].name == 'astr', attrs[1].name
-
- def test_wsattr_del(self):
- class MyType(object):
- a = types.wsattr(int)
-
- types.register_type(MyType)
-
- value = MyType()
-
- value.a = 5
- assert value.a == 5
- del value.a
- assert value.a is types.Unset
-
- def test_validate_dict(self):
- assert types.validate_value({int: str}, {1: '1', 5: '5'})
-
- self.assertRaises(ValueError, types.validate_value,
- {int: str}, [])
-
- assert types.validate_value({int: str}, {'1': '1', 5: '5'})
-
- self.assertRaises(ValueError, types.validate_value,
- {int: str}, {1: 1, 5: '5'})
-
- def test_validate_list_valid(self):
- assert types.validate_value([int], [1, 2])
- assert types.validate_value([int], ['5'])
-
- def test_validate_list_empty(self):
- assert types.validate_value([int], []) == []
-
- def test_validate_list_none(self):
- v = types.ArrayType(int)
- assert v.validate(None) is None
-
- def test_validate_list_invalid_member(self):
- self.assertRaises(ValueError, types.validate_value, [int],
- ['not-a-number'])
-
- def test_validate_list_invalid_type(self):
- self.assertRaises(ValueError, types.validate_value, [int], 1)
-
- def test_validate_float(self):
- self.assertEqual(types.validate_value(float, 1), 1.0)
- self.assertEqual(types.validate_value(float, '1'), 1.0)
- self.assertEqual(types.validate_value(float, 1.1), 1.1)
- self.assertRaises(ValueError, types.validate_value, float, [])
- self.assertRaises(ValueError, types.validate_value, float,
- 'not-a-float')
-
- def test_validate_int(self):
- self.assertEqual(types.validate_value(int, 1), 1)
- self.assertEqual(types.validate_value(int, '1'), 1)
- self.assertEqual(types.validate_value(int, six.u('1')), 1)
- self.assertRaises(ValueError, types.validate_value, int, 1.1)
-
- def test_validate_integer_type(self):
- v = types.IntegerType(minimum=1, maximum=10)
- v.validate(1)
- v.validate(5)
- v.validate(10)
- self.assertRaises(ValueError, v.validate, 0)
- self.assertRaises(ValueError, v.validate, 11)
-
- def test_validate_string_type(self):
- v = types.StringType(min_length=1, max_length=10,
- pattern='^[a-zA-Z0-9]*$')
- v.validate('1')
- v.validate('12345')
- v.validate('1234567890')
- self.assertRaises(ValueError, v.validate, '')
- self.assertRaises(ValueError, v.validate, '12345678901')
-
- # Test a pattern validation
- v.validate('a')
- v.validate('A')
- self.assertRaises(ValueError, v.validate, '_')
-
- def test_validate_string_type_precompile(self):
- precompile = re.compile('^[a-zA-Z0-9]*$')
- v = types.StringType(min_length=1, max_length=10,
- pattern=precompile)
-
- # Test a pattern validation
- v.validate('a')
- v.validate('A')
- self.assertRaises(ValueError, v.validate, '_')
-
- def test_validate_string_type_pattern_exception_message(self):
- regex = '^[a-zA-Z0-9]*$'
- v = types.StringType(pattern=regex)
- try:
- v.validate('_')
- self.assertFail()
- except ValueError as e:
- self.assertIn(regex, str(e))
-
- def test_validate_ipv4_address_type(self):
- v = types.IPv4AddressType()
- self.assertEqual(v.validate('127.0.0.1'), '127.0.0.1')
- self.assertEqual(v.validate('192.168.0.1'), '192.168.0.1')
- self.assertEqual(v.validate(u'8.8.1.1'), u'8.8.1.1')
- self.assertRaises(ValueError, v.validate, '')
- self.assertRaises(ValueError, v.validate, 'foo')
- self.assertRaises(ValueError, v.validate,
- '2001:0db8:bd05:01d2:288a:1fc0:0001:10ee')
- self.assertRaises(ValueError, v.validate, '1.2.3')
-
- def test_validate_ipv6_address_type(self):
- v = types.IPv6AddressType()
- self.assertEqual(v.validate('0:0:0:0:0:0:0:1'),
- '0:0:0:0:0:0:0:1')
- self.assertEqual(v.validate(u'0:0:0:0:0:0:0:1'), u'0:0:0:0:0:0:0:1')
- self.assertEqual(v.validate('2001:0db8:bd05:01d2:288a:1fc0:0001:10ee'),
- '2001:0db8:bd05:01d2:288a:1fc0:0001:10ee')
- self.assertRaises(ValueError, v.validate, '')
- self.assertRaises(ValueError, v.validate, 'foo')
- self.assertRaises(ValueError, v.validate, '192.168.0.1')
- self.assertRaises(ValueError, v.validate, '0:0:0:0:0:0:1')
-
- def test_validate_uuid_type(self):
- v = types.UuidType()
- self.assertEqual(v.validate('6a0a707c-45ef-4758-b533-e55adddba8ce'),
- '6a0a707c-45ef-4758-b533-e55adddba8ce')
- self.assertEqual(v.validate('6a0a707c45ef4758b533e55adddba8ce'),
- '6a0a707c-45ef-4758-b533-e55adddba8ce')
- self.assertRaises(ValueError, v.validate, '')
- self.assertRaises(ValueError, v.validate, 'foo')
- self.assertRaises(ValueError, v.validate,
- '6a0a707c-45ef-4758-b533-e55adddba8ce-a')
-
- def test_register_invalid_array(self):
- self.assertRaises(ValueError, types.register_type, [])
- self.assertRaises(ValueError, types.register_type, [int, str])
- self.assertRaises(AttributeError, types.register_type, [1])
-
- def test_register_invalid_dict(self):
- self.assertRaises(ValueError, types.register_type, {})
- self.assertRaises(ValueError, types.register_type,
- {int: str, str: int})
- self.assertRaises(ValueError, types.register_type,
- {types.Unset: str})
-
- def test_list_attribute_no_auto_register(self):
- class MyType(object):
- aint = int
-
- assert not hasattr(MyType, '_wsme_attributes')
-
- self.assertRaises(TypeError, types.list_attributes, MyType)
-
- assert not hasattr(MyType, '_wsme_attributes')
-
- def test_list_of_complextypes(self):
- class A(object):
- bs = types.wsattr(['B'])
-
- class B(object):
- i = int
-
- types.register_type(A)
- types.register_type(B)
-
- assert A.bs.datatype.item_type is B
-
- def test_cross_referenced_types(self):
- class A(object):
- b = types.wsattr('B')
-
- class B(object):
- a = A
-
- types.register_type(A)
- types.register_type(B)
-
- assert A.b.datatype is B
-
- def test_base(self):
- class B1(types.Base):
- b2 = types.wsattr('B2')
-
- class B2(types.Base):
- b2 = types.wsattr('B2')
-
- assert B1.b2.datatype is B2, repr(B1.b2.datatype)
- assert B2.b2.datatype is B2
-
- def test_base_init(self):
- class C1(types.Base):
- s = six.text_type
-
- c = C1(s=six.u('test'))
- assert c.s == six.u('test')
-
- def test_array_eq(self):
- ell = [types.ArrayType(str)]
- assert types.ArrayType(str) in ell
-
- def test_array_sample(self):
- s = types.ArrayType(str).sample()
- assert isinstance(s, list)
- assert s
- assert s[0] == ''
-
- def test_dict_sample(self):
- s = types.DictType(str, str).sample()
- assert isinstance(s, dict)
- assert s
- assert s == {'': ''}
-
- def test_binary_to_base(self):
- import base64
- assert types.binary.tobasetype(None) is None
- expected = base64.encodestring(six.b('abcdef'))
- assert types.binary.tobasetype(six.b('abcdef')) == expected
-
- def test_binary_from_base(self):
- import base64
- assert types.binary.frombasetype(None) is None
- encoded = base64.encodestring(six.b('abcdef'))
- assert types.binary.frombasetype(encoded) == six.b('abcdef')
-
- def test_wsattr_weakref_datatype(self):
- # If the datatype inside the wsattr ends up a weakref, it
- # should be converted to the real type when accessed again by
- # the property getter.
- import weakref
- a = types.wsattr(int)
- a.datatype = weakref.ref(int)
- assert a.datatype is int
-
- def test_wsattr_list_datatype(self):
- # If the datatype inside the wsattr ends up a list of weakrefs
- # to types, it should be converted to the real types when
- # accessed again by the property getter.
- import weakref
- a = types.wsattr(int)
- a.datatype = [weakref.ref(int)]
- assert isinstance(a.datatype, list)
- assert a.datatype[0] is int
-
- def test_file_get_content_by_reading(self):
- class buffer:
- def read(self):
- return 'abcdef'
- f = types.File(file=buffer())
- assert f.content == 'abcdef'
-
- def test_file_content_overrides_file(self):
- class buffer:
- def read(self):
- return 'from-file'
- f = types.File(content='from-content', file=buffer())
- assert f.content == 'from-content'
-
- def test_file_setting_content_discards_file(self):
- class buffer:
- def read(self):
- return 'from-file'
- f = types.File(file=buffer())
- f.content = 'from-content'
- assert f.content == 'from-content'
-
- def test_file_field_storage(self):
- class buffer:
- def read(self):
- return 'from-file'
-
- class fieldstorage:
- filename = 'static.json'
- file = buffer()
- type = 'application/json'
- f = types.File(fieldstorage=fieldstorage)
- assert f.content == 'from-file'
-
- def test_file_field_storage_value(self):
- class buffer:
- def read(self):
- return 'from-file'
-
- class fieldstorage:
- filename = 'static.json'
- file = None
- type = 'application/json'
- value = 'from-value'
- f = types.File(fieldstorage=fieldstorage)
- assert f.content == 'from-value'
-
- def test_file_property_file(self):
- class buffer:
- def read(self):
- return 'from-file'
- buf = buffer()
- f = types.File(file=buf)
- assert f.file is buf
-
- def test_file_property_content(self):
- class buffer:
- def read(self):
- return 'from-file'
- f = types.File(content=six.b('from-content'))
- assert f.file.read() == six.b('from-content')
-
- def test_unregister(self):
- class TempType(object):
- pass
- types.registry.register(TempType)
- v = types.registry.lookup('TempType')
- self.assertIs(v, TempType)
- types.registry._unregister(TempType)
- after = types.registry.lookup('TempType')
- self.assertIs(after, None)
-
- def test_unregister_twice(self):
- class TempType(object):
- pass
- types.registry.register(TempType)
- v = types.registry.lookup('TempType')
- self.assertIs(v, TempType)
- types.registry._unregister(TempType)
- # Second call should not raise an exception
- types.registry._unregister(TempType)
- after = types.registry.lookup('TempType')
- self.assertIs(after, None)
-
- def test_unregister_array_type(self):
- class TempType(object):
- pass
- t = [TempType]
- types.registry.register(t)
- self.assertNotEqual(types.registry.array_types, set())
- types.registry._unregister(t)
- self.assertEqual(types.registry.array_types, set())
-
- def test_unregister_array_type_twice(self):
- class TempType(object):
- pass
- t = [TempType]
- types.registry.register(t)
- self.assertNotEqual(types.registry.array_types, set())
- types.registry._unregister(t)
- # Second call should not raise an exception
- types.registry._unregister(t)
- self.assertEqual(types.registry.array_types, set())
-
- def test_unregister_dict_type(self):
- class TempType(object):
- pass
- t = {str: TempType}
- types.registry.register(t)
- self.assertNotEqual(types.registry.dict_types, set())
- types.registry._unregister(t)
- self.assertEqual(types.registry.dict_types, set())
-
- def test_unregister_dict_type_twice(self):
- class TempType(object):
- pass
- t = {str: TempType}
- types.registry.register(t)
- self.assertNotEqual(types.registry.dict_types, set())
- types.registry._unregister(t)
- # Second call should not raise an exception
- types.registry._unregister(t)
- self.assertEqual(types.registry.dict_types, set())
-
- def test_reregister(self):
- class TempType(object):
- pass
- types.registry.register(TempType)
- v = types.registry.lookup('TempType')
- self.assertIs(v, TempType)
- types.registry.reregister(TempType)
- after = types.registry.lookup('TempType')
- self.assertIs(after, TempType)
-
- def test_reregister_and_add_attr(self):
- class TempType(object):
- pass
- types.registry.register(TempType)
- attrs = types.list_attributes(TempType)
- self.assertEqual(attrs, [])
- TempType.one = str
- types.registry.reregister(TempType)
- after = types.list_attributes(TempType)
- self.assertNotEqual(after, [])
-
- def test_dynamicbase_add_attributes(self):
- class TempType(types.DynamicBase):
- pass
- types.registry.register(TempType)
- attrs = types.list_attributes(TempType)
- self.assertEqual(attrs, [])
- TempType.add_attributes(one=str)
- after = types.list_attributes(TempType)
- self.assertEqual(len(after), 1)
-
- def test_dynamicbase_add_attributes_second(self):
- class TempType(types.DynamicBase):
- pass
- types.registry.register(TempType)
- attrs = types.list_attributes(TempType)
- self.assertEqual(attrs, [])
- TempType.add_attributes(one=str)
- TempType.add_attributes(two=int)
- after = types.list_attributes(TempType)
- self.assertEqual(len(after), 2)
-
- def test_non_registered_complex_type(self):
- class TempType(types.Base):
- __registry__ = None
-
- self.assertFalse(types.iscomplex(TempType))
- types.registry.register(TempType)
- self.assertTrue(types.iscomplex(TempType))
diff --git a/wsme/tests/test_utils.py b/wsme/tests/test_utils.py
deleted file mode 100644
index f9464c8..0000000
--- a/wsme/tests/test_utils.py
+++ /dev/null
@@ -1,97 +0,0 @@
-import datetime
-import unittest
-import pytz
-
-from wsme import utils
-
-
-class TestUtils(unittest.TestCase):
- def test_parse_isodate(self):
- good_dates = [
- ('2008-02-01', datetime.date(2008, 2, 1)),
- ('2009-01-04', datetime.date(2009, 1, 4)),
- ]
- ill_formatted_dates = [
- '24-12-2004'
- ]
- out_of_range_dates = [
- '0000-00-00',
- '2012-02-30',
- ]
- for s, d in good_dates:
- assert utils.parse_isodate(s) == d
- for s in ill_formatted_dates + out_of_range_dates:
- self.assertRaises(ValueError, utils.parse_isodate, s)
-
- def test_parse_isotime(self):
- good_times = [
- ('12:03:54', datetime.time(12, 3, 54)),
- ('23:59:59.000004', datetime.time(23, 59, 59, 4)),
- ('01:02:03+00:00', datetime.time(1, 2, 3, 0, pytz.UTC)),
- ('01:02:03+23:59', datetime.time(1, 2, 3, 0,
- pytz.FixedOffset(1439))),
- ('01:02:03-23:59', datetime.time(1, 2, 3, 0,
- pytz.FixedOffset(-1439))),
- ]
- ill_formatted_times = [
- '24-12-2004'
- ]
- out_of_range_times = [
- '32:12:00',
- '00:54:60',
- '01:02:03-24:00',
- '01:02:03+24:00',
- ]
- for s, t in good_times:
- assert utils.parse_isotime(s) == t
- for s in ill_formatted_times + out_of_range_times:
- self.assertRaises(ValueError, utils.parse_isotime, s)
-
- def test_parse_isodatetime(self):
- good_datetimes = [
- ('2008-02-12T12:03:54',
- datetime.datetime(2008, 2, 12, 12, 3, 54)),
- ('2012-05-14T23:59:59.000004',
- datetime.datetime(2012, 5, 14, 23, 59, 59, 4)),
- ('1856-07-10T01:02:03+00:00',
- datetime.datetime(1856, 7, 10, 1, 2, 3, 0, pytz.UTC)),
- ('1856-07-10T01:02:03+23:59',
- datetime.datetime(1856, 7, 10, 1, 2, 3, 0,
- pytz.FixedOffset(1439))),
- ('1856-07-10T01:02:03-23:59',
- datetime.datetime(1856, 7, 10, 1, 2, 3, 0,
- pytz.FixedOffset(-1439))),
- ]
- ill_formatted_datetimes = [
- '24-32-2004',
- '1856-07-10+33:00'
- ]
- out_of_range_datetimes = [
- '2008-02-12T32:12:00',
- '2012-13-12T00:54:60',
- ]
- for s, t in good_datetimes:
- assert utils.parse_isodatetime(s) == t
- for s in ill_formatted_datetimes + out_of_range_datetimes:
- self.assertRaises(ValueError, utils.parse_isodatetime, s)
-
- def test_validator_with_valid_code(self):
- valid_code = 404
- self.assertTrue(
- utils.is_valid_code(valid_code),
- "Valid status code not detected"
- )
-
- def test_validator_with_invalid_int_code(self):
- invalid_int_code = 648
- self.assertFalse(
- utils.is_valid_code(invalid_int_code),
- "Invalid status code not detected"
- )
-
- def test_validator_with_invalid_str_code(self):
- invalid_str_code = '404'
- self.assertFalse(
- utils.is_valid_code(invalid_str_code),
- "Invalid status code not detected"
- )
diff --git a/wsme/types.py b/wsme/types.py
deleted file mode 100644
index 0a5fc02..0000000
--- a/wsme/types.py
+++ /dev/null
@@ -1,840 +0,0 @@
-import base64
-import datetime
-import decimal
-import inspect
-import logging
-import netaddr
-import re
-import six
-import sys
-import uuid
-import weakref
-
-from wsme import exc
-
-log = logging.getLogger(__name__)
-
-#: The 'str' (python 2) or 'bytes' (python 3) type.
-#: Its use should be restricted to
-#: pure ascii strings as the protocols will generally not be
-#: be able to send non-unicode strings.
-#: To transmit binary strings, use the :class:`binary` type
-bytes = six.binary_type
-
-#: Unicode string.
-text = six.text_type
-
-
-class ArrayType(object):
- def __init__(self, item_type):
- if iscomplex(item_type):
- self._item_type = weakref.ref(item_type)
- else:
- self._item_type = item_type
-
- def __hash__(self):
- return hash(self.item_type)
-
- def __eq__(self, other):
- return isinstance(other, ArrayType) \
- and self.item_type == other.item_type
-
- def sample(self):
- return [getattr(self.item_type, 'sample', self.item_type)()]
-
- @property
- def item_type(self):
- if isinstance(self._item_type, weakref.ref):
- return self._item_type()
- else:
- return self._item_type
-
- def validate(self, value):
- if value is None:
- return
- if not isinstance(value, list):
- raise ValueError("Wrong type. Expected '[%s]', got '%s'" % (
- self.item_type, type(value)
- ))
- return [
- validate_value(self.item_type, item)
- for item in value
- ]
-
-
-class DictType(object):
- def __init__(self, key_type, value_type):
- if key_type not in pod_types:
- raise ValueError("Dictionaries key can only be a pod type")
- self.key_type = key_type
- if iscomplex(value_type):
- self._value_type = weakref.ref(value_type)
- else:
- self._value_type = value_type
-
- def __hash__(self):
- return hash((self.key_type, self.value_type))
-
- def sample(self):
- key = getattr(self.key_type, 'sample', self.key_type)()
- value = getattr(self.value_type, 'sample', self.value_type)()
- return {key: value}
-
- @property
- def value_type(self):
- if isinstance(self._value_type, weakref.ref):
- return self._value_type()
- else:
- return self._value_type
-
- def validate(self, value):
- if not isinstance(value, dict):
- raise ValueError("Wrong type. Expected '{%s: %s}', got '%s'" % (
- self.key_type, self.value_type, type(value)
- ))
- return dict((
- (
- validate_value(self.key_type, key),
- validate_value(self.value_type, v)
- ) for key, v in value.items()
- ))
-
-
-class UserType(object):
- basetype = None
- name = None
-
- def validate(self, value):
- return value
-
- def tobasetype(self, value):
- return value
-
- def frombasetype(self, value):
- return value
-
-
-def isusertype(class_):
- return isinstance(class_, UserType)
-
-
-class BinaryType(UserType):
- """
- A user type that use base64 strings to carry binary data.
- """
- basetype = bytes
- name = 'binary'
-
- def tobasetype(self, value):
- if value is None:
- return None
- return base64.encodestring(value)
-
- def frombasetype(self, value):
- if value is None:
- return None
- return base64.decodestring(value)
-
-
-#: The binary almost-native type
-binary = BinaryType()
-
-
-class IntegerType(UserType):
- """
- A simple integer type. Can validate a value range.
-
- :param minimum: Possible minimum value
- :param maximum: Possible maximum value
-
- Example::
-
- Price = IntegerType(minimum=1)
-
- """
- basetype = int
- name = "integer"
-
- def __init__(self, minimum=None, maximum=None):
- self.minimum = minimum
- self.maximum = maximum
-
- @staticmethod
- def frombasetype(value):
- return int(value) if value is not None else None
-
- def validate(self, value):
- if self.minimum is not None and value < self.minimum:
- error = 'Value should be greater or equal to %s' % self.minimum
- raise ValueError(error)
-
- if self.maximum is not None and value > self.maximum:
- error = 'Value should be lower or equal to %s' % self.maximum
- raise ValueError(error)
-
- return value
-
-
-class StringType(UserType):
- """
- A simple string type. Can validate a length and a pattern.
-
- :param min_length: Possible minimum length
- :param max_length: Possible maximum length
- :param pattern: Possible string pattern
-
- Example::
-
- Name = StringType(min_length=1, pattern='^[a-zA-Z ]*$')
-
- """
- basetype = six.string_types
- name = "string"
-
- def __init__(self, min_length=None, max_length=None, pattern=None):
- self.min_length = min_length
- self.max_length = max_length
- if isinstance(pattern, six.string_types):
- self.pattern = re.compile(pattern)
- else:
- self.pattern = pattern
-
- def validate(self, value):
- if not isinstance(value, self.basetype):
- error = 'Value should be string'
- raise ValueError(error)
-
- if self.min_length is not None and len(value) < self.min_length:
- error = 'Value should have a minimum character requirement of %s' \
- % self.min_length
- raise ValueError(error)
-
- if self.max_length is not None and len(value) > self.max_length:
- error = 'Value should have a maximum character requirement of %s' \
- % self.max_length
- raise ValueError(error)
-
- if self.pattern is not None and not self.pattern.search(value):
- error = 'Value should match the pattern %s' % self.pattern.pattern
- raise ValueError(error)
-
- return value
-
-
-class IPv4AddressType(UserType):
- """
- A simple IPv4 type.
- """
- basetype = six.string_types
- name = "ipv4address"
-
- @staticmethod
- def validate(value):
- try:
- netaddr.IPAddress(value, version=4, flags=netaddr.INET_PTON)
- except netaddr.AddrFormatError:
- error = 'Value should be IPv4 format'
- raise ValueError(error)
- else:
- return value
-
-
-class IPv6AddressType(UserType):
- """
- A simple IPv6 type.
-
- This type represents IPv6 addresses in the short format.
- """
- basetype = six.string_types
- name = "ipv6address"
-
- @staticmethod
- def validate(value):
- try:
- netaddr.IPAddress(value, version=6, flags=netaddr.INET_PTON)
- except netaddr.AddrFormatError:
- error = 'Value should be IPv6 format'
- raise ValueError(error)
- else:
- return value
-
-
-class UuidType(UserType):
- """
- A simple UUID type.
-
- This type allows not only UUID having dashes but also UUID not
- having dashes. For example, '6a0a707c-45ef-4758-b533-e55adddba8ce'
- and '6a0a707c45ef4758b533e55adddba8ce' are distinguished as valid.
- """
- basetype = six.string_types
- name = "uuid"
-
- @staticmethod
- def validate(value):
- try:
- return six.text_type((uuid.UUID(value)))
- except (TypeError, ValueError, AttributeError):
- error = 'Value should be UUID format'
- raise ValueError(error)
-
-
-class Enum(UserType):
- """
- A simple enumeration type. Can be based on any non-complex type.
-
- :param basetype: The actual data type
- :param values: A set of possible values
-
- If nullable, 'None' should be added the values set.
-
- Example::
-
- Gender = Enum(str, 'male', 'female')
- Specie = Enum(str, 'cat', 'dog')
-
- """
- def __init__(self, basetype, *values, **kw):
- self.basetype = basetype
- self.values = set(values)
- name = kw.pop('name', None)
- if name is None:
- name = "Enum(%s)" % ', '.join((six.text_type(v) for v in values))
- self.name = name
-
- def validate(self, value):
- if value not in self.values:
- raise ValueError("Value should be one of: %s" %
- ', '.join(map(six.text_type, self.values)))
- return value
-
- def tobasetype(self, value):
- return value
-
- def frombasetype(self, value):
- return value
-
-
-class UnsetType(object):
- if sys.version < '3':
- def __nonzero__(self):
- return False
- else:
- def __bool__(self):
- return False
-
- def __repr__(self):
- return 'Unset'
-
-
-Unset = UnsetType()
-
-#: A special type that corresponds to the host framework request object.
-#: It can only be used in the function parameters, and if so the request object
-#: of the host framework will be passed to the function.
-HostRequest = object()
-
-
-pod_types = six.integer_types + (
- bytes, text, float, bool)
-dt_types = (datetime.date, datetime.time, datetime.datetime)
-extra_types = (binary, decimal.Decimal)
-native_types = pod_types + dt_types + extra_types
-# The types for which we allow promotion to certain numbers.
-_promotable_types = six.integer_types + (text, bytes)
-
-
-def iscomplex(datatype):
- return inspect.isclass(datatype) \
- and '_wsme_attributes' in datatype.__dict__
-
-
-def isarray(datatype):
- return isinstance(datatype, ArrayType)
-
-
-def isdict(datatype):
- return isinstance(datatype, DictType)
-
-
-def validate_value(datatype, value):
- if value in (Unset, None):
- return value
-
- # Try to promote the data type to one of our complex types.
- if isinstance(datatype, list):
- datatype = ArrayType(datatype[0])
- elif isinstance(datatype, dict):
- datatype = DictType(*list(datatype.items())[0])
-
- # If the datatype has its own validator, use that.
- if hasattr(datatype, 'validate'):
- return datatype.validate(value)
-
- # Do type promotion/conversion and data validation for builtin
- # types.
- v_type = type(value)
- if datatype in six.integer_types:
- if v_type in _promotable_types:
- try:
- # Try to turn the value into an int
- value = datatype(value)
- except ValueError:
- # An error is raised at the end of the function
- # when the types don't match.
- pass
- elif datatype is float and v_type in _promotable_types:
- try:
- value = float(value)
- except ValueError:
- # An error is raised at the end of the function
- # when the types don't match.
- pass
- elif datatype is text and isinstance(value, bytes):
- value = value.decode()
- elif datatype is bytes and isinstance(value, text):
- value = value.encode()
-
- if not isinstance(value, datatype):
- raise ValueError(
- "Wrong type. Expected '%s', got '%s'" % (
- datatype, v_type
- ))
- return value
-
-
-class wsproperty(property):
- """
- A specialised :class:`property` to define typed-property on complex types.
- Example::
-
- class MyComplexType(wsme.types.Base):
- def get_aint(self):
- return self._aint
-
- def set_aint(self, value):
- assert avalue < 10 # Dummy input validation
- self._aint = value
-
- aint = wsproperty(int, get_aint, set_aint, mandatory=True)
- """
- def __init__(self, datatype, fget, fset=None,
- mandatory=False, doc=None, name=None):
- property.__init__(self, fget, fset)
- #: The property name in the parent python class
- self.key = None
- #: The attribute name on the public of the api.
- #: Defaults to :attr:`key`
- self.name = name
- #: property data type
- self.datatype = datatype
- #: True if the property is mandatory
- self.mandatory = mandatory
-
-
-class wsattr(object):
- """
- Complex type attribute definition.
-
- Example::
-
- class MyComplexType(wsme.types.Base):
- optionalvalue = int
- mandatoryvalue = wsattr(int, mandatory=True)
- named_value = wsattr(int, name='named.value')
-
- After inspection, the non-wsattr attributes will be replaced, and
- the above class will be equivalent to::
-
- class MyComplexType(wsme.types.Base):
- optionalvalue = wsattr(int)
- mandatoryvalue = wsattr(int, mandatory=True)
-
- """
- def __init__(self, datatype, mandatory=False, name=None, default=Unset,
- readonly=False):
- #: The attribute name in the parent python class.
- #: Set by :func:`inspect_class`
- self.key = None # will be set by class inspection
- #: The attribute name on the public of the api.
- #: Defaults to :attr:`key`
- self.name = name
- self._datatype = (datatype,)
- #: True if the attribute is mandatory
- self.mandatory = mandatory
- #: Default value. The attribute will return this instead
- #: of :data:`Unset` if no value has been set.
- self.default = default
- #: If True value cannot be set from json/xml input data
- self.readonly = readonly
-
- self.complextype = None
-
- def _get_dataholder(self, instance):
- dataholder = getattr(instance, '_wsme_dataholder', None)
- if dataholder is None:
- dataholder = instance._wsme_DataHolderClass()
- instance._wsme_dataholder = dataholder
- return dataholder
-
- def __get__(self, instance, owner):
- if instance is None:
- return self
- return getattr(
- self._get_dataholder(instance),
- self.key,
- self.default
- )
-
- def __set__(self, instance, value):
- try:
- value = validate_value(self.datatype, value)
- except (ValueError, TypeError) as e:
- raise exc.InvalidInput(self.name, value, six.text_type(e))
- dataholder = self._get_dataholder(instance)
- if value is Unset:
- if hasattr(dataholder, self.key):
- delattr(dataholder, self.key)
- else:
- setattr(dataholder, self.key, value)
-
- def __delete__(self, instance):
- self.__set__(instance, Unset)
-
- def _get_datatype(self):
- if isinstance(self._datatype, tuple):
- self._datatype = \
- self.complextype().__registry__.resolve_type(self._datatype[0])
- if isinstance(self._datatype, weakref.ref):
- return self._datatype()
- if isinstance(self._datatype, list):
- return [
- item() if isinstance(item, weakref.ref) else item
- for item in self._datatype
- ]
- return self._datatype
-
- def _set_datatype(self, datatype):
- self._datatype = datatype
-
- #: attribute data type. Can be either an actual type,
- #: or a type name, in which case the actual type will be
- #: determined when needed (generally just before scanning the api).
- datatype = property(_get_datatype, _set_datatype)
-
-
-def iswsattr(attr):
- if inspect.isfunction(attr) or inspect.ismethod(attr):
- return False
- if isinstance(attr, property) and not isinstance(attr, wsproperty):
- return False
- return True
-
-
-def sort_attributes(class_, attributes):
- """Sort a class attributes list.
-
- 3 mechanisms are attempted :
-
- #. Look for a _wsme_attr_order attribute on the class_. This allow
- to define an arbitrary order of the attributes (useful for
- generated types).
-
- #. Access the object source code to find the declaration order.
-
- #. Sort by alphabetically"""
-
- if not len(attributes):
- return
-
- attrs = dict((a.key, a) for a in attributes)
-
- if hasattr(class_, '_wsme_attr_order'):
- names_order = class_._wsme_attr_order
- else:
- names = attrs.keys()
- names_order = []
- try:
- lines = []
- for cls in inspect.getmro(class_):
- if cls is object:
- continue
- lines[len(lines):] = inspect.getsourcelines(cls)[0]
- for line in lines:
- line = line.strip().replace(" ", "")
- if '=' in line:
- aname = line[:line.index('=')]
- if aname in names and aname not in names_order:
- names_order.append(aname)
- if len(names_order) < len(names):
- names_order.extend((
- name for name in names if name not in names_order))
- assert len(names_order) == len(names)
- except (TypeError, IOError):
- names_order = list(names)
- names_order.sort()
-
- attributes[:] = [attrs[name] for name in names_order]
-
-
-def inspect_class(class_):
- """Extract a list of (name, wsattr|wsproperty) for the given class_"""
- attributes = []
- for name, attr in inspect.getmembers(class_, iswsattr):
- if name.startswith('_'):
- continue
- if inspect.isroutine(attr):
- continue
-
- if isinstance(attr, (wsattr, wsproperty)):
- attrdef = attr
- else:
- if attr not in native_types and (
- inspect.isclass(attr) or
- isinstance(attr, (list, dict))):
- register_type(attr)
- attrdef = getattr(class_, '__wsattrclass__', wsattr)(attr)
-
- attrdef.key = name
- if attrdef.name is None:
- attrdef.name = name
- attrdef.complextype = weakref.ref(class_)
- attributes.append(attrdef)
- setattr(class_, name, attrdef)
-
- sort_attributes(class_, attributes)
- return attributes
-
-
-def list_attributes(class_):
- """
- Returns a list of a complex type attributes.
- """
- if not iscomplex(class_):
- raise TypeError("%s is not a registered type")
- return class_._wsme_attributes
-
-
-def make_dataholder(class_):
- # the slots are computed outside the class scope to avoid
- # 'attr' to pullute the class namespace, which leads to weird
- # things if one of the slots is named 'attr'.
- slots = [attr.key for attr in class_._wsme_attributes]
-
- class DataHolder(object):
- __slots__ = slots
-
- DataHolder.__name__ = class_.__name__ + 'DataHolder'
- return DataHolder
-
-
-class Registry(object):
- def __init__(self):
- self._complex_types = []
- self.array_types = set()
- self.dict_types = set()
-
- @property
- def complex_types(self):
- return [t() for t in self._complex_types if t()]
-
- def register(self, class_):
- """
- Make sure a type is registered.
-
- It is automatically called by :class:`expose() <wsme.expose>`
- and :class:`validate() <wsme.validate>`.
- Unless you want to control when the class inspection is done there
- is no need to call it.
- """
- if class_ is None or \
- class_ in native_types or \
- isusertype(class_) or iscomplex(class_) or \
- isarray(class_) or isdict(class_):
- return class_
-
- if isinstance(class_, list):
- if len(class_) != 1:
- raise ValueError("Cannot register type %s" % repr(class_))
- dt = ArrayType(class_[0])
- self.register(dt.item_type)
- self.array_types.add(dt)
- return dt
-
- if isinstance(class_, dict):
- if len(class_) != 1:
- raise ValueError("Cannot register type %s" % repr(class_))
- dt = DictType(*list(class_.items())[0])
- self.register(dt.value_type)
- self.dict_types.add(dt)
- return dt
-
- class_._wsme_attributes = None
- class_._wsme_attributes = inspect_class(class_)
- class_._wsme_DataHolderClass = make_dataholder(class_)
-
- class_.__registry__ = self
- self._complex_types.append(weakref.ref(class_))
- return class_
-
- def reregister(self, class_):
- """Register a type which may already have been registered.
- """
- self._unregister(class_)
- return self.register(class_)
-
- def _unregister(self, class_):
- """Remove a previously registered type.
- """
- # Clear the existing attribute reference so it is rebuilt if
- # the class is registered again later.
- if hasattr(class_, '_wsme_attributes'):
- del class_._wsme_attributes
- # FIXME(dhellmann): This method does not recurse through the
- # types like register() does. Should it?
- if isinstance(class_, list):
- at = ArrayType(class_[0])
- try:
- self.array_types.remove(at)
- except KeyError:
- pass
- elif isinstance(class_, dict):
- key_type, value_type = list(class_.items())[0]
- self.dict_types = set(
- dt for dt in self.dict_types
- if (dt.key_type, dt.value_type) != (key_type, value_type)
- )
- # We can't use remove() here because the items in
- # _complex_types are weakref objects pointing to the classes,
- # so we can't compare with them directly.
- self._complex_types = [
- ct for ct in self._complex_types
- if ct() is not class_
- ]
-
- def lookup(self, typename):
- log.debug('Lookup %s' % typename)
- modname = None
- if '.' in typename:
- modname, typename = typename.rsplit('.', 1)
- for ct in self._complex_types:
- ct = ct()
- if ct is not None and typename == ct.__name__ and (
- modname is None or modname == ct.__module__):
- return ct
-
- def resolve_type(self, type_):
- if isinstance(type_, six.string_types):
- return self.lookup(type_)
- if isinstance(type_, list):
- type_ = ArrayType(type_[0])
- if isinstance(type_, dict):
- type_ = DictType(list(type_.keys())[0], list(type_.values())[0])
- if isinstance(type_, ArrayType):
- type_ = ArrayType(self.resolve_type(type_.item_type))
- self.array_types.add(type_)
- elif isinstance(type_, DictType):
- type_ = DictType(
- type_.key_type,
- self.resolve_type(type_.value_type)
- )
- self.dict_types.add(type_)
- else:
- type_ = self.register(type_)
- return type_
-
-
-# Default type registry
-registry = Registry()
-
-
-def register_type(class_):
- return registry.register(class_)
-
-
-class BaseMeta(type):
- def __new__(cls, name, bases, dct):
- if bases and bases[0] is not object and '__registry__' not in dct:
- dct['__registry__'] = registry
- return type.__new__(cls, name, bases, dct)
-
- def __init__(cls, name, bases, dct):
- if bases and bases[0] is not object and cls.__registry__:
- cls.__registry__.register(cls)
-
-
-class Base(six.with_metaclass(BaseMeta)):
- """Base type for complex types"""
- def __init__(self, **kw):
- for key, value in kw.items():
- if hasattr(self, key):
- setattr(self, key, value)
-
-
-class File(Base):
- """A complex type that represents a file.
-
- In the particular case of protocol accepting form encoded data as
- input, File can be loaded from a form file field.
- """
- #: The file name
- filename = wsattr(text)
-
- #: Mime type of the content
- contenttype = wsattr(text)
-
- def _get_content(self):
- if self._content is None and self._file:
- self._content = self._file.read()
- return self._content
-
- def _set_content(self, value):
- self._content = value
- self._file = None
-
- #: File content
- content = wsproperty(binary, _get_content, _set_content)
-
- def __init__(self, filename=None, file=None, content=None,
- contenttype=None, fieldstorage=None):
- self.filename = filename
- self.contenttype = contenttype
- self._file = file
- self._content = content
-
- if fieldstorage is not None:
- if fieldstorage.file:
- self._file = fieldstorage.file
- self.filename = fieldstorage.filename
- self.contenttype = text(fieldstorage.type)
- else:
- self._content = fieldstorage.value
-
- @property
- def file(self):
- if self._file is None and self._content:
- self._file = six.BytesIO(self._content)
- return self._file
-
-
-class DynamicBase(Base):
- """Base type for complex types for which all attributes are not
- defined when the class is constructed.
-
- This class is meant to be used as a base for types that have
- properties added after the main class is created, such as by
- loading plugins.
-
- """
-
- @classmethod
- def add_attributes(cls, **attrs):
- """Add more attributes
-
- The arguments should be valid Python attribute names
- associated with a type for the new attribute.
-
- """
- for n, t in attrs.items():
- setattr(cls, n, t)
- cls.__registry__.reregister(cls)
diff --git a/wsme/utils.py b/wsme/utils.py
deleted file mode 100644
index e52b0ef..0000000
--- a/wsme/utils.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import decimal
-import datetime
-import pytz
-import re
-from six.moves import builtins, http_client
-
-try:
- import dateutil.parser
-except ImportError:
- dateutil = None # noqa
-
-date_re = r'(?P<year>-?\d{4,})-(?P<month>\d{2})-(?P<day>\d{2})'
-time_re = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})' + \
- r'(\.(?P<sec_frac>\d+))?'
-tz_re = r'((?P<tz_sign>[+-])(?P<tz_hour>\d{2}):(?P<tz_min>\d{2}))' + \
- r'|(?P<tz_z>Z)'
-
-datetime_re = re.compile(
- '%sT%s(%s)?' % (date_re, time_re, tz_re))
-date_re = re.compile(date_re)
-time_re = re.compile('%s(%s)?' % (time_re, tz_re))
-
-
-if hasattr(builtins, '_'):
- _ = builtins._
-else:
- def _(s):
- return s
-
-
-def parse_isodate(value):
- m = date_re.match(value)
- if m is None:
- raise ValueError("'%s' is not a legal date value" % (value))
- try:
- return datetime.date(
- int(m.group('year')),
- int(m.group('month')),
- int(m.group('day')))
- except ValueError:
- raise ValueError("'%s' is a out-of-range date" % (value))
-
-
-def parse_isotime(value):
- m = time_re.match(value)
- if m is None:
- raise ValueError("'%s' is not a legal time value" % (value))
- try:
- ms = 0
- if m.group('sec_frac') is not None:
- f = decimal.Decimal('0.' + m.group('sec_frac'))
- f = str(f.quantize(decimal.Decimal('0.000001')))
- ms = int(f[2:])
- tz = _parse_tzparts(m.groupdict())
- return datetime.time(
- int(m.group('hour')),
- int(m.group('min')),
- int(m.group('sec')),
- ms,
- tz)
- except ValueError:
- raise ValueError("'%s' is a out-of-range time" % (value))
-
-
-def parse_isodatetime(value):
- if dateutil:
- return dateutil.parser.parse(value)
- m = datetime_re.match(value)
- if m is None:
- raise ValueError("'%s' is not a legal datetime value" % (value))
- try:
- ms = 0
- if m.group('sec_frac') is not None:
- f = decimal.Decimal('0.' + m.group('sec_frac'))
- f = f.quantize(decimal.Decimal('0.000001'))
- ms = int(str(f)[2:])
- tz = _parse_tzparts(m.groupdict())
- return datetime.datetime(
- int(m.group('year')),
- int(m.group('month')),
- int(m.group('day')),
- int(m.group('hour')),
- int(m.group('min')),
- int(m.group('sec')),
- ms,
- tz)
- except ValueError:
- raise ValueError("'%s' is a out-of-range datetime" % (value))
-
-
-def _parse_tzparts(parts):
- if 'tz_z' in parts and parts['tz_z'] == 'Z':
- return pytz.UTC
- if 'tz_min' not in parts or not parts['tz_min']:
- return None
-
- tz_minute_offset = (int(parts['tz_hour']) * 60 + int(parts['tz_min']))
- tz_multiplier = -1 if parts['tz_sign'] == '-' else 1
-
- return pytz.FixedOffset(tz_multiplier * tz_minute_offset)
-
-
-def is_valid_code(code_value):
- """
- This function checks if incoming value in http response codes range.
- """
- return code_value in http_client.responses
-
-
-def is_client_error(code):
- """ Checks client error code (RFC 2616)."""
- return 400 <= code < 500
-
-
-try:
- from collections import OrderedDict
-except ImportError:
- from ordereddict import OrderedDict # noqa
diff --git a/wsmeext/__init__.py b/wsmeext/__init__.py
deleted file mode 100644
index ece379c..0000000
--- a/wsmeext/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-import pkg_resources
-pkg_resources.declare_namespace(__name__)
diff --git a/wsmeext/cornice.py b/wsmeext/cornice.py
deleted file mode 100644
index 6c81386..0000000
--- a/wsmeext/cornice.py
+++ /dev/null
@@ -1,168 +0,0 @@
-"""
-WSME for cornice
-
-
-Activate it::
-
- config.include('wsme.cornice')
-
-
-And use it::
-
- @hello.get()
- @wsexpose(Message, wsme.types.text)
- def get_hello(who=u'World'):
- return Message(text='Hello %s' % who)
-"""
-from __future__ import absolute_import
-
-import inspect
-import sys
-
-import wsme
-from wsme.rest import json as restjson
-from wsme.rest import xml as restxml
-import wsme.runtime
-import wsme.api
-import functools
-
-from wsme.rest.args import (
- args_from_args, args_from_params, args_from_body, combine_args
-)
-
-
-class WSMEJsonRenderer(object):
- def __init__(self, info):
- pass
-
- def __call__(self, data, context):
- response = context['request'].response
- response.content_type = 'application/json'
- if 'faultcode' in data:
- if 'orig_code' in data:
- response.status_code = data['orig_code']
- elif data['faultcode'] == 'Client':
- response.status_code = 400
- else:
- response.status_code = 500
- return restjson.encode_error(None, data)
- obj = data['result']
- if isinstance(obj, wsme.api.Response):
- response.status_code = obj.status_code
- if obj.error:
- return restjson.encode_error(None, obj.error)
- obj = obj.obj
- return restjson.encode_result(obj, data['datatype'])
-
-
-class WSMEXmlRenderer(object):
- def __init__(self, info):
- pass
-
- def __call__(self, data, context):
- response = context['request'].response
- if 'faultcode' in data:
- if data['faultcode'] == 'Client':
- response.status_code = 400
- else:
- response.status_code = 500
- return restxml.encode_error(None, data)
- response.content_type = 'text/xml'
- return restxml.encode_result(data['result'], data['datatype'])
-
-
-def get_outputformat(request):
- df = None
- if 'Accept' in request.headers:
- if 'application/json' in request.headers['Accept']:
- df = 'json'
- elif 'text/xml' in request.headers['Accept']:
- df = 'xml'
- if df is None and 'Content-Type' in request.headers:
- if 'application/json' in request.headers['Content-Type']:
- df = 'json'
- elif 'text/xml' in request.headers['Content-Type']:
- df = 'xml'
- return df if df else 'json'
-
-
-def signature(*args, **kwargs):
- sig = wsme.signature(*args, **kwargs)
-
- def decorate(f):
- args = inspect.getargspec(f)[0]
- with_self = args[0] == 'self' if args else False
- f = sig(f)
- funcdef = wsme.api.FunctionDefinition.get(f)
- funcdef.resolve_types(wsme.types.registry)
-
- @functools.wraps(f)
- def callfunction(*args):
- if with_self:
- if len(args) == 1:
- self = args[0]
- request = self.request
- elif len(args) == 2:
- self, request = args
- else:
- raise ValueError("Cannot do anything with these arguments")
- else:
- request = args[0]
- request.override_renderer = 'wsme' + get_outputformat(request)
- try:
- args, kwargs = combine_args(funcdef, (
- args_from_args(funcdef, (), request.matchdict),
- args_from_params(funcdef, request.params),
- args_from_body(funcdef, request.body, request.content_type)
- ))
- wsme.runtime.check_arguments(funcdef, args, kwargs)
- if funcdef.pass_request:
- kwargs[funcdef.pass_request] = request
- if with_self:
- args.insert(0, self)
-
- result = f(*args, **kwargs)
- return {
- 'datatype': funcdef.return_type,
- 'result': result
- }
- except Exception:
- try:
- exception_info = sys.exc_info()
- orig_exception = exception_info[1]
- orig_code = getattr(orig_exception, 'code', None)
- data = wsme.api.format_exception(exception_info)
- if orig_code is not None:
- data['orig_code'] = orig_code
- return data
- finally:
- del exception_info
-
- callfunction.wsme_func = f
- return callfunction
- return decorate
-
-
-def scan_api(root=None):
- from cornice.service import get_services
- for service in get_services():
- for method, func, options in service.definitions:
- wsme_func = getattr(func, 'wsme_func')
- basepath = service.path.split('/')
- if basepath and not basepath[0]:
- del basepath[0]
- if wsme_func:
- yield (
- basepath + [method.lower()],
- wsme_func._wsme_definition
- )
-
-
-def includeme(config):
- import pyramid.wsgi
- wsroot = wsme.WSRoot(scan_api=scan_api, webpath='/ws')
- wsroot.addprotocol('extdirect')
- config.add_renderer('wsmejson', WSMEJsonRenderer)
- config.add_renderer('wsmexml', WSMEXmlRenderer)
- config.add_route('wsme', '/ws/*path')
- config.add_view(pyramid.wsgi.wsgiapp(wsroot.wsgiapp()), route_name='wsme')
diff --git a/wsmeext/extdirect/__init__.py b/wsmeext/extdirect/__init__.py
deleted file mode 100644
index ff14bd5..0000000
--- a/wsmeext/extdirect/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from wsmeext.extdirect.protocol import ExtDirectProtocol # noqa
diff --git a/wsmeext/extdirect/datastore.py b/wsmeext/extdirect/datastore.py
deleted file mode 100644
index 287e3a7..0000000
--- a/wsmeext/extdirect/datastore.py
+++ /dev/null
@@ -1,121 +0,0 @@
-import wsme
-import wsme.types
-
-try:
- import simplejson as json
-except ImportError:
- import json
-
-
-class ReadResultBase(wsme.types.Base):
- total = int
- success = bool
- message = wsme.types.text
-
-
-def make_readresult(datatype):
- ReadResult = type(
- datatype.__name__ + 'ReadResult',
- (ReadResultBase,), {
- 'data': [datatype]
- }
- )
- return ReadResult
-
-
-class DataStoreControllerMeta(type):
- def __init__(cls, name, bases, dct):
- if cls.__datatype__ is None:
- return
- if getattr(cls, '__readresulttype__', None) is None:
- cls.__readresulttype__ = make_readresult(cls.__datatype__)
-
- cls.create = wsme.expose(
- cls.__readresulttype__,
- extdirect_params_notation='positional')(cls.create)
- cls.create = wsme.validate(cls.__datatype__)(cls.create)
-
- cls.read = wsme.expose(
- cls.__readresulttype__,
- extdirect_params_notation='named')(cls.read)
- cls.read = wsme.validate(str, str, int, int, int)(cls.read)
-
- cls.update = wsme.expose(
- cls.__readresulttype__,
- extdirect_params_notation='positional')(cls.update)
- cls.update = wsme.validate(cls.__datatype__)(cls.update)
-
- cls.destroy = wsme.expose(
- cls.__readresulttype__,
- extdirect_params_notation='positional')(cls.destroy)
- cls.destroy = wsme.validate(cls.__idtype__)(cls.destroy)
-
-
-class DataStoreControllerMixin(object):
- __datatype__ = None
- __idtype__ = int
-
- __readresulttype__ = None
-
- def create(self, obj):
- pass
-
- def read(self, query=None, sort=None, page=None, start=None, limit=None):
- pass
-
- def update(self, obj):
- pass
-
- def destroy(self, obj_id):
- pass
-
- def model(self):
- tpl = """
-Ext.define('%(appns)s.model.%(classname)s', {
- extend: 'Ext.data.Model',
- fields: %(fields)s,
-
- proxy: {
- type: 'direct',
- api: {
- create: %(appns)s.%(controllerns)s.create,
- read: %(appns)s.%(controllerns)s.read,
- update: %(appns)s.%(controllerns)s.update,
- destroy: %(appns)s.%(controllerns)s.destroy
- },
- reader: {
- root: 'data'
- }
- }
-});
- """
- fields = [
- attr.name for attr in self.__datatype__._wsme_attributes
- ]
- d = {
- 'appns': 'Demo',
- 'controllerns': 'stores.' + self.__datatype__.__name__.lower(),
- 'classname': self.__datatype__.__name__,
- 'fields': json.dumps(fields)
- }
- return tpl % d
-
- def store(self):
- tpl = """
-Ext.define('%(appns)s.store.%(classname)s', {
- extend: 'Ext.data.Store',
- model: '%(appns)s.model.%(classname)s'
-});
-"""
- d = {
- 'appns': 'Demo',
- 'classname': self.__datatype__.__name__,
- }
-
- return tpl % d
-
-
-DataStoreController = DataStoreControllerMeta(
- 'DataStoreController',
- (DataStoreControllerMixin,), {}
-)
diff --git a/wsmeext/extdirect/protocol.py b/wsmeext/extdirect/protocol.py
deleted file mode 100644
index 23793b4..0000000
--- a/wsmeext/extdirect/protocol.py
+++ /dev/null
@@ -1,450 +0,0 @@
-import datetime
-import decimal
-
-from simplegeneric import generic
-
-from wsme.exc import ClientSideError
-from wsme.protocol import CallContext, Protocol, expose
-from wsme.utils import parse_isodate, parse_isodatetime, parse_isotime
-from wsme.rest.args import from_params
-from wsme.types import iscomplex, isusertype, list_attributes, Unset
-import wsme.types
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-from six import u
-
-
-class APIDefinitionGenerator(object):
- tpl = """\
-Ext.ns("%(rootns)s");
-
-if (!%(rootns)s.wsroot) {
- %(rootns)s.wsroot = "%(webpath)s.
-}
-
-%(descriptors)s
-
-Ext.syncRequire(['Ext.direct.*'], function() {
- %(providers)s
-});
-"""
- descriptor_tpl = """\
-Ext.ns("%(fullns)s");
-
-%(fullns)s.Descriptor = {
- "url": %(rootns)s.wsroot + "extdirect/router/%(ns)s",
- "namespace": "%(fullns)s",
- "type": "remoting",
- "actions": %(actions)s
- "enableBuffer": true
-};
-"""
- provider_tpl = """\
- Ext.direct.Manager.addProvider(%(fullns)s.Descriptor);
-"""
-
- def __init__(self):
- pass
-
- def render(self, rootns, webpath, namespaces, fullns):
- descriptors = u('')
- for ns in sorted(namespaces):
- descriptors += self.descriptor_tpl % {
- 'ns': ns,
- 'rootns': rootns,
- 'fullns': fullns(ns),
- 'actions': '\n'.join((
- ' ' * 4 + line
- for line
- in json.dumps(namespaces[ns], indent=4).split('\n')
- ))
- }
-
- providers = u('')
- for ns in sorted(namespaces):
- providers += self.provider_tpl % {
- 'fullns': fullns(ns)
- }
-
- r = self.tpl % {
- 'rootns': rootns,
- 'webpath': webpath,
- 'descriptors': descriptors,
- 'providers': providers,
- }
- return r
-
-
-@generic
-def fromjson(datatype, value):
- if value is None:
- return None
- if iscomplex(datatype):
- newvalue = datatype()
- for attrdef in list_attributes(datatype):
- if attrdef.name in value:
- setattr(newvalue, attrdef.key,
- fromjson(attrdef.datatype, value[attrdef.name]))
- value = newvalue
- elif isusertype(datatype):
- value = datatype.frombasetype(fromjson(datatype.basetype, value))
- return value
-
-
-@generic
-def tojson(datatype, value):
- if value is None:
- return value
- if iscomplex(datatype):
- d = {}
- for attrdef in list_attributes(datatype):
- attrvalue = getattr(value, attrdef.key)
- if attrvalue is not Unset:
- d[attrdef.name] = tojson(attrdef.datatype, attrvalue)
- value = d
- elif isusertype(datatype):
- value = tojson(datatype.basetype, datatype.tobasetype(value))
- return value
-
-
-@fromjson.when_type(wsme.types.ArrayType)
-def array_fromjson(datatype, value):
- return [fromjson(datatype.item_type, item) for item in value]
-
-
-@tojson.when_type(wsme.types.ArrayType)
-def array_tojson(datatype, value):
- if value is None:
- return value
- return [tojson(datatype.item_type, item) for item in value]
-
-
-@fromjson.when_type(wsme.types.DictType)
-def dict_fromjson(datatype, value):
- if value is None:
- return value
- return dict((
- (fromjson(datatype.key_type, key),
- fromjson(datatype.value_type, value))
- for key, value in value.items()
- ))
-
-
-@tojson.when_type(wsme.types.DictType)
-def dict_tojson(datatype, value):
- if value is None:
- return value
- return dict((
- (tojson(datatype.key_type, key),
- tojson(datatype.value_type, value))
- for key, value in value.items()
- ))
-
-
-@tojson.when_object(wsme.types.bytes)
-def bytes_tojson(datatype, value):
- if value is None:
- return value
- return value.decode('ascii')
-
-
-# raw strings
-@fromjson.when_object(wsme.types.bytes)
-def bytes_fromjson(datatype, value):
- if value is not None:
- value = value.encode('ascii')
- return value
-
-
-# unicode strings
-
-@fromjson.when_object(wsme.types.text)
-def text_fromjson(datatype, value):
- if isinstance(value, wsme.types.bytes):
- return value.decode('utf-8')
- return value
-
-
-# datetime.time
-
-@fromjson.when_object(datetime.time)
-def time_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isotime(value)
-
-
-@tojson.when_object(datetime.time)
-def time_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# datetime.date
-
-@fromjson.when_object(datetime.date)
-def date_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isodate(value)
-
-
-@tojson.when_object(datetime.date)
-def date_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# datetime.datetime
-
-@fromjson.when_object(datetime.datetime)
-def datetime_fromjson(datatype, value):
- if value is None or value == '':
- return None
- return parse_isodatetime(value)
-
-
-@tojson.when_object(datetime.datetime)
-def datetime_tojson(datatype, value):
- if value is None:
- return value
- return value.isoformat()
-
-
-# decimal.Decimal
-
-@fromjson.when_object(decimal.Decimal)
-def decimal_fromjson(datatype, value):
- if value is None:
- return value
- return decimal.Decimal(value)
-
-
-@tojson.when_object(decimal.Decimal)
-def decimal_tojson(datatype, value):
- if value is None:
- return value
- return str(value)
-
-
-class ExtCallContext(CallContext):
- def __init__(self, request, namespace, calldata):
- super(ExtCallContext, self).__init__(request)
- self.namespace = namespace
-
- self.tid = calldata['tid']
- self.action = calldata['action']
- self.method = calldata['method']
- self.params = calldata['data']
-
-
-class FormExtCallContext(CallContext):
- def __init__(self, request, namespace):
- super(FormExtCallContext, self).__init__(request)
- self.namespace = namespace
-
- self.tid = request.params['extTID']
- self.action = request.params['extAction']
- self.method = request.params['extMethod']
- self.params = []
-
-
-class ExtDirectProtocol(Protocol):
- """
- ExtDirect protocol.
-
- For more detail on the protocol, see
- http://www.sencha.com/products/extjs/extdirect.
-
- .. autoattribute:: name
- .. autoattribute:: content_types
- """
- name = 'extdirect'
- displayname = 'ExtDirect'
- content_types = ['application/json', 'text/javascript']
-
- def __init__(self, namespace='', params_notation='named', nsfolder=None):
- self.namespace = namespace
- self.appns, self.apins = namespace.rsplit('.', 2) \
- if '.' in namespace else (namespace, '')
- self.default_params_notation = params_notation
- self.appnsfolder = nsfolder
-
- @property
- def api_alias(self):
- if self.appnsfolder:
- alias = '/%s/%s.js' % (
- self.appnsfolder,
- self.apins.replace('.', '/'))
- return alias
-
- def accept(self, req):
- path = req.path
- assert path.startswith(self.root._webpath)
- path = path[len(self.root._webpath):]
-
- return (
- path == self.api_alias or
- path == "/extdirect/api" or
- path.startswith("/extdirect/router")
- )
-
- def iter_calls(self, req):
- path = req.path
-
- assert path.startswith(self.root._webpath)
- path = path[len(self.root._webpath):].strip()
-
- assert path.startswith('/extdirect/router'), path
- path = path[17:].strip('/')
-
- if path:
- namespace = path.split('.')
- else:
- namespace = []
-
- if 'extType' in req.params:
- req.wsme_extdirect_batchcall = False
- yield FormExtCallContext(req, namespace)
- else:
- data = json.loads(req.body.decode('utf8'))
- req.wsme_extdirect_batchcall = isinstance(data, list)
- if not req.wsme_extdirect_batchcall:
- data = [data]
- req.callcount = len(data)
-
- for call in data:
- yield ExtCallContext(req, namespace, call)
-
- def extract_path(self, context):
- path = list(context.namespace)
-
- if context.action:
- path.append(context.action)
-
- path.append(context.method)
-
- return path
-
- def read_std_arguments(self, context):
- funcdef = context.funcdef
- notation = funcdef.extra_options.get('extdirect_params_notation',
- self.default_params_notation)
- args = context.params
- if notation == 'positional':
- kw = dict(
- (argdef.name, fromjson(argdef.datatype, arg))
- for argdef, arg in zip(funcdef.arguments, args)
- )
- elif notation == 'named':
- if len(args) == 0:
- args = [{}]
- elif len(args) > 1:
- raise ClientSideError(
- "Named arguments: takes a single object argument")
- args = args[0]
- kw = dict(
- (argdef.name, fromjson(argdef.datatype, args[argdef.name]))
- for argdef in funcdef.arguments if argdef.name in args
- )
- else:
- raise ValueError("Invalid notation: %s" % notation)
- return kw
-
- def read_form_arguments(self, context):
- kw = {}
- for argdef in context.funcdef.arguments:
- value = from_params(argdef.datatype, context.request.params,
- argdef.name, set())
- if value is not Unset:
- kw[argdef.name] = value
- return kw
-
- def read_arguments(self, context):
- if isinstance(context, ExtCallContext):
- kwargs = self.read_std_arguments(context)
- elif isinstance(context, FormExtCallContext):
- kwargs = self.read_form_arguments(context)
- wsme.runtime.check_arguments(context.funcdef, (), kwargs)
- return kwargs
-
- def encode_result(self, context, result):
- return json.dumps({
- 'type': 'rpc',
- 'tid': context.tid,
- 'action': context.action,
- 'method': context.method,
- 'result': tojson(context.funcdef.return_type, result)
- })
-
- def encode_error(self, context, infos):
- return json.dumps({
- 'type': 'exception',
- 'tid': context.tid,
- 'action': context.action,
- 'method': context.method,
- 'message': '%(faultcode)s: %(faultstring)s' % infos,
- 'where': infos['debuginfo']})
-
- def prepare_response_body(self, request, results):
- r = ",\n".join(results)
- if request.wsme_extdirect_batchcall:
- return "[\n%s\n]" % r
- else:
- return r
-
- def get_response_status(self, request):
- return 200
-
- def get_response_contenttype(self, request):
- return "text/javascript"
-
- def fullns(self, ns):
- return ns and '%s.%s' % (self.namespace, ns) or self.namespace
-
- @expose('/extdirect/api', "text/javascript")
- @expose('${api_alias}', "text/javascript")
- def api(self):
- namespaces = {}
- for path, funcdef in self.root.getapi():
- if len(path) > 1:
- namespace = '.'.join(path[:-2])
- action = path[-2]
- else:
- namespace = ''
- action = ''
- if namespace not in namespaces:
- namespaces[namespace] = {}
- if action not in namespaces[namespace]:
- namespaces[namespace][action] = []
- notation = funcdef.extra_options.get('extdirect_params_notation',
- self.default_params_notation)
- method = {
- 'name': funcdef.name}
-
- if funcdef.extra_options.get('extdirect_formhandler', False):
- method['formHandler'] = True
- method['len'] = 1 if notation == 'named' \
- else len(funcdef.arguments)
- namespaces[namespace][action].append(method)
- webpath = self.root._webpath
- if webpath and not webpath.endswith('/'):
- webpath += '/'
- return APIDefinitionGenerator().render(
- namespaces=namespaces,
- webpath=webpath,
- rootns=self.namespace,
- fullns=self.fullns,
- )
-
- def encode_sample_value(self, datatype, value, format=False):
- r = tojson(datatype, value)
- content = json.dumps(r, ensure_ascii=False, indent=4 if format else 0,
- sort_keys=format)
- return ('javascript', content)
diff --git a/wsmeext/extdirect/sadatastore.py b/wsmeext/extdirect/sadatastore.py
deleted file mode 100644
index 44d79cb..0000000
--- a/wsmeext/extdirect/sadatastore.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from wsmeext.extdirect import datastore
-
-
-class SADataStoreController(datastore.DataStoreController):
- __dbsession__ = None
- __datatype__ = None
-
- def read(self, query=None, sort=None, page=None, start=None, limit=None):
- q = self.__dbsession__.query(self.__datatype__.__saclass__)
- total = q.count()
- if start is not None and limit is not None:
- q = q.slice(start, limit)
- return self.__readresulttype__(
- data=[
- self.__datatype__(o) for o in q
- ],
- success=True,
- total=total
- )
diff --git a/wsmeext/flask.py b/wsmeext/flask.py
deleted file mode 100644
index 4d9e446..0000000
--- a/wsmeext/flask.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from __future__ import absolute_import
-
-import functools
-import logging
-import sys
-import inspect
-
-import wsme
-import wsme.api
-import wsme.rest.json
-import wsme.rest.xml
-import wsme.rest.args
-from wsme.utils import is_valid_code
-
-import flask
-
-log = logging.getLogger(__name__)
-
-
-TYPES = {
- 'application/json': wsme.rest.json,
- 'application/xml': wsme.rest.xml,
- 'text/xml': wsme.rest.xml
-}
-
-
-def get_dataformat():
- if 'Accept' in flask.request.headers:
- for t in TYPES:
- if t in flask.request.headers['Accept']:
- return TYPES[t]
-
- # Look for the wanted data format in the request.
- req_dataformat = getattr(flask.request, 'response_type', None)
- if req_dataformat in TYPES:
- return TYPES[req_dataformat]
-
- log.info('''Could not determine what format is wanted by the
- caller, falling back to json''')
- return wsme.rest.json
-
-
-def signature(*args, **kw):
- sig = wsme.signature(*args, **kw)
-
- def decorator(f):
- args = inspect.getargspec(f)[0]
- ismethod = args and args[0] == 'self'
- sig(f)
- funcdef = wsme.api.FunctionDefinition.get(f)
- funcdef.resolve_types(wsme.types.registry)
-
- @functools.wraps(f)
- def wrapper(*args, **kwargs):
- if ismethod:
- self, args = args[0], args[1:]
- args, kwargs = wsme.rest.args.get_args(
- funcdef, args, kwargs,
- flask.request.args, flask.request.form,
- flask.request.data,
- flask.request.mimetype
- )
-
- if funcdef.pass_request:
- kwargs[funcdef.pass_request] = flask.request
-
- dataformat = get_dataformat()
-
- try:
- if ismethod:
- args = [self] + list(args)
- result = f(*args, **kwargs)
-
- # NOTE: Support setting of status_code with default 20
- status_code = funcdef.status_code
- if isinstance(result, wsme.api.Response):
- status_code = result.status_code
- result = result.obj
-
- res = flask.make_response(
- dataformat.encode_result(
- result,
- funcdef.return_type
- )
- )
- res.mimetype = dataformat.content_type
- res.status_code = status_code
- except Exception:
- try:
- exception_info = sys.exc_info() or None
- orig_exception = exception_info[1]
- orig_code = getattr(orig_exception, 'code', None)
- data = wsme.api.format_exception(exception_info)
- finally:
- del exception_info
-
- res = flask.make_response(dataformat.encode_error(None, data))
- if orig_code and is_valid_code(orig_code):
- res.status_code = orig_code
- elif data['faultcode'].lower() == 'client':
- res.status_code = 400
- else:
- res.status_code = 500
- return res
-
- wrapper.wsme_func = f
- return wrapper
- return decorator
diff --git a/wsmeext/pecan.py b/wsmeext/pecan.py
deleted file mode 100644
index 9d63dde..0000000
--- a/wsmeext/pecan.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from __future__ import absolute_import
-
-import functools
-import inspect
-import sys
-
-import wsme
-import wsme.rest.args
-import wsme.rest.json
-import wsme.rest.xml
-
-import pecan
-
-from wsme.utils import is_valid_code
-
-
-class JSonRenderer(object):
- @staticmethod
- def __init__(path, extra_vars):
- pass
-
- @staticmethod
- def render(template_path, namespace):
- if 'faultcode' in namespace:
- return wsme.rest.json.encode_error(None, namespace)
- return wsme.rest.json.encode_result(
- namespace['result'],
- namespace['datatype']
- )
-
-
-class XMLRenderer(object):
- @staticmethod
- def __init__(path, extra_vars):
- pass
-
- @staticmethod
- def render(template_path, namespace):
- if 'faultcode' in namespace:
- return wsme.rest.xml.encode_error(None, namespace)
- return wsme.rest.xml.encode_result(
- namespace['result'],
- namespace['datatype']
- )
-
-
-pecan.templating._builtin_renderers['wsmejson'] = JSonRenderer
-pecan.templating._builtin_renderers['wsmexml'] = XMLRenderer
-
-pecan_json_decorate = pecan.expose(
- template='wsmejson:',
- content_type='application/json',
- generic=False)
-pecan_xml_decorate = pecan.expose(
- template='wsmexml:',
- content_type='application/xml',
- generic=False
-)
-pecan_text_xml_decorate = pecan.expose(
- template='wsmexml:',
- content_type='text/xml',
- generic=False
-)
-
-
-def wsexpose(*args, **kwargs):
- sig = wsme.signature(*args, **kwargs)
-
- def decorate(f):
- sig(f)
- funcdef = wsme.api.FunctionDefinition.get(f)
- funcdef.resolve_types(wsme.types.registry)
-
- @functools.wraps(f)
- def callfunction(self, *args, **kwargs):
- return_type = funcdef.return_type
-
- try:
- args, kwargs = wsme.rest.args.get_args(
- funcdef, args, kwargs, pecan.request.params, None,
- pecan.request.body, pecan.request.content_type
- )
- if funcdef.pass_request:
- kwargs[funcdef.pass_request] = pecan.request
- result = f(self, *args, **kwargs)
-
- # NOTE: Support setting of status_code with default 201
- pecan.response.status = funcdef.status_code
- if isinstance(result, wsme.api.Response):
- pecan.response.status = result.status_code
-
- # NOTE(lucasagomes): If the return code is 204
- # (No Response) we have to make sure that we are not
- # returning anything in the body response and the
- # content-length is 0
- if result.status_code == 204:
- return_type = None
- elif not isinstance(result.return_type,
- wsme.types.UnsetType):
- return_type = result.return_type
-
- result = result.obj
-
- except Exception:
- try:
- exception_info = sys.exc_info()
- orig_exception = exception_info[1]
- orig_code = getattr(orig_exception, 'code', None)
- data = wsme.api.format_exception(
- exception_info,
- pecan.conf.get('wsme', {}).get('debug', False)
- )
- finally:
- del exception_info
-
- if orig_code and is_valid_code(orig_code):
- pecan.response.status = orig_code
- else:
- pecan.response.status = 500
-
- return data
-
- if return_type is None:
- pecan.request.pecan['content_type'] = None
- pecan.response.content_type = None
- return ''
-
- return dict(
- datatype=return_type,
- result=result
- )
-
- if 'xml' in funcdef.rest_content_types:
- pecan_xml_decorate(callfunction)
- pecan_text_xml_decorate(callfunction)
- if 'json' in funcdef.rest_content_types:
- pecan_json_decorate(callfunction)
- pecan.util._cfg(callfunction)['argspec'] = inspect.getargspec(f)
- callfunction._wsme_definition = funcdef
- return callfunction
-
- return decorate
diff --git a/wsmeext/soap/__init__.py b/wsmeext/soap/__init__.py
deleted file mode 100644
index 7a237ba..0000000
--- a/wsmeext/soap/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from __future__ import absolute_import
-
-from wsmeext.soap.protocol import SoapProtocol
-
-__all__ = ['SoapProtocol']
diff --git a/wsmeext/soap/protocol.py b/wsmeext/soap/protocol.py
deleted file mode 100644
index 0d87af8..0000000
--- a/wsmeext/soap/protocol.py
+++ /dev/null
@@ -1,478 +0,0 @@
-"""
-A SOAP implementation for wsme.
-Parts of the code were taken from the tgwebservices soap implmentation.
-"""
-from __future__ import absolute_import
-
-import pkg_resources
-import datetime
-import decimal
-import base64
-import logging
-
-import six
-
-from wsmeext.soap.simplegeneric import generic
-from wsmeext.soap.wsdl import WSDLGenerator
-
-try:
- from lxml import etree as ET
- use_lxml = True
-except ImportError:
- from xml.etree import cElementTree as ET # noqa
- use_lxml = False
-
-from wsme.protocol import CallContext, Protocol, expose
-
-import wsme.types
-import wsme.runtime
-
-from wsme import exc
-from wsme.utils import parse_isodate, parse_isotime, parse_isodatetime
-
-log = logging.getLogger(__name__)
-
-xsd_ns = 'http://www.w3.org/2001/XMLSchema'
-xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance'
-soapenv_ns = 'http://schemas.xmlsoap.org/soap/envelope/'
-
-if not use_lxml:
- ET.register_namespace('soap', soapenv_ns)
-
-type_qn = '{%s}type' % xsi_ns
-nil_qn = '{%s}nil' % xsi_ns
-
-Envelope_qn = '{%s}Envelope' % soapenv_ns
-Body_qn = '{%s}Body' % soapenv_ns
-Fault_qn = '{%s}Fault' % soapenv_ns
-faultcode_qn = '{%s}faultcode' % soapenv_ns
-faultstring_qn = '{%s}faultstring' % soapenv_ns
-detail_qn = '{%s}detail' % soapenv_ns
-
-
-type_registry = {
- wsme.types.bytes: 'xs:string',
- wsme.types.text: 'xs:string',
- int: 'xs:int',
- float: "xs:float",
- bool: "xs:boolean",
- datetime.datetime: "xs:dateTime",
- datetime.date: "xs:date",
- datetime.time: "xs:time",
- decimal.Decimal: "xs:decimal",
- wsme.types.binary: "xs:base64Binary",
-}
-
-if not six.PY3:
- type_registry[long] = "xs:long" # noqa
-
-array_registry = {
- wsme.types.text: "String_Array",
- wsme.types.bytes: "String_Array",
- int: "Int_Array",
- float: "Float_Array",
- bool: "Boolean_Array",
-}
-
-if not six.PY3:
- array_registry[long] = "Long_Array" # noqa
-
-
-def soap_array(datatype, ns):
- if datatype.item_type in array_registry:
- name = array_registry[datatype.item_type]
- else:
- name = soap_type(datatype.item_type, False) + '_Array'
- if ns:
- name = 'types:' + name
- return name
-
-
-def soap_type(datatype, ns):
- name = None
- if wsme.types.isarray(datatype):
- return soap_array(datatype, ns)
- if wsme.types.isdict(datatype):
- return None
- if datatype in type_registry:
- stype = type_registry[datatype]
- if not ns:
- stype = stype[3:]
- return stype
- if wsme.types.iscomplex(datatype):
- name = datatype.__name__
- if name and ns:
- name = 'types:' + name
- return name
- if wsme.types.isusertype(datatype):
- return soap_type(datatype.basetype, ns)
-
-
-def soap_fname(path, funcdef):
- return "".join([path[0]] + [i.capitalize() for i in path[1:]])
-
-
-class SoapEncoder(object):
- def __init__(self, types_ns):
- self.types_ns = types_ns
-
- def make_soap_element(self, datatype, tag, value, xsitype=None):
- el = ET.Element(tag)
- if value is None:
- el.set(nil_qn, 'true')
- elif xsitype is not None:
- el.set(type_qn, xsitype)
- el.text = value
- elif wsme.types.isusertype(datatype):
- return self.tosoap(datatype.basetype, tag,
- datatype.tobasetype(value))
- elif wsme.types.iscomplex(datatype):
- el.set(type_qn, 'types:%s' % (datatype.__name__))
- for attrdef in wsme.types.list_attributes(datatype):
- attrvalue = getattr(value, attrdef.key)
- if attrvalue is not wsme.types.Unset:
- el.append(self.tosoap(
- attrdef.datatype,
- '{%s}%s' % (self.types_ns, attrdef.name),
- attrvalue
- ))
- else:
- el.set(type_qn, type_registry.get(datatype))
- if not isinstance(value, wsme.types.text):
- value = wsme.types.text(value)
- el.text = value
- return el
-
- @generic
- def tosoap(self, datatype, tag, value):
- """Converts a value into xml Element objects for inclusion in the SOAP
- response output (after adding the type to the type_registry).
-
- If a non-complex user specific type is to be used in the api,
- a specific toxml should be added::
-
- from wsme.protocol.soap import tosoap, make_soap_element, \
- type_registry
-
- class MySpecialType(object):
- pass
-
- type_registry[MySpecialType] = 'xs:MySpecialType'
-
- @tosoap.when_object(MySpecialType)
- def myspecialtype_tosoap(datatype, tag, value):
- return make_soap_element(datatype, tag, str(value))
- """
- return self.make_soap_element(datatype, tag, value)
-
- @tosoap.when_type(wsme.types.ArrayType)
- def array_tosoap(self, datatype, tag, value):
- el = ET.Element(tag)
- el.set(type_qn, soap_array(datatype, self.types_ns))
- if value is None:
- el.set(nil_qn, 'true')
- elif len(value) == 0:
- el.append(ET.Element('item'))
- else:
- for item in value:
- el.append(self.tosoap(datatype.item_type, 'item', item))
- return el
-
- @tosoap.when_object(bool)
- def bool_tosoap(self, datatype, tag, value):
- return self.make_soap_element(
- datatype,
- tag,
- 'true' if value is True else 'false' if value is False else None
- )
-
- @tosoap.when_object(wsme.types.bytes)
- def bytes_tosoap(self, datatype, tag, value):
- log.debug('(bytes_tosoap, %s, %s, %s, %s)', datatype,
- tag, value, type(value))
- if isinstance(value, wsme.types.bytes):
- value = value.decode('ascii')
- return self.make_soap_element(datatype, tag, value)
-
- @tosoap.when_object(datetime.datetime)
- def datetime_tosoap(self, datatype, tag, value):
- return self.make_soap_element(
- datatype,
- tag,
- value is not None and value.isoformat() or None
- )
-
- @tosoap.when_object(wsme.types.binary)
- def binary_tosoap(self, datatype, tag, value):
- log.debug("(%s, %s, %s)", datatype, tag, value)
- value = base64.encodestring(value) if value is not None else None
- if six.PY3:
- value = value.decode('ascii')
- return self.make_soap_element(
- datatype.basetype, tag, value, 'xs:base64Binary'
- )
-
- @tosoap.when_object(None)
- def None_tosoap(self, datatype, tag, value):
- return self.make_soap_element(datatype, tag, None)
-
-
-@generic
-def fromsoap(datatype, el, ns):
- """
- A generic converter from soap elements to python datatype.
-
- If a non-complex user specific type is to be used in the api,
- a specific fromsoap should be added.
- """
- if el.get(nil_qn) == 'true':
- return None
- if datatype in type_registry:
- value = datatype(el.text)
- elif wsme.types.isusertype(datatype):
- value = datatype.frombasetype(
- fromsoap(datatype.basetype, el, ns))
- else:
- value = datatype()
- for attr in wsme.types.list_attributes(datatype):
- child = el.find('{%s}%s' % (ns['type'], attr.name))
- if child is not None:
- setattr(value, attr.key, fromsoap(attr.datatype, child, ns))
- return value
-
-
-@fromsoap.when_type(wsme.types.ArrayType)
-def array_fromsoap(datatype, el, ns):
- if len(el) == 1:
- if datatype.item_type \
- not in wsme.types.pod_types + wsme.types.dt_types \
- and len(el[0]) == 0:
- return []
- return [fromsoap(datatype.item_type, child, ns) for child in el]
-
-
-@fromsoap.when_object(wsme.types.bytes)
-def bytes_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:string'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return el.text.encode('ascii') if el.text else six.b('')
-
-
-@fromsoap.when_object(wsme.types.text)
-def text_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:string'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return datatype(el.text if el.text else '')
-
-
-@fromsoap.when_object(bool)
-def bool_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:boolean'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return el.text.lower() != 'false'
-
-
-@fromsoap.when_object(datetime.date)
-def date_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:date'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return parse_isodate(el.text)
-
-
-@fromsoap.when_object(datetime.time)
-def time_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:time'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return parse_isotime(el.text)
-
-
-@fromsoap.when_object(datetime.datetime)
-def datetime_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:dateTime'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return parse_isodatetime(el.text)
-
-
-@fromsoap.when_object(wsme.types.binary)
-def binary_fromsoap(datatype, el, ns):
- if el.get(nil_qn) == 'true':
- return None
- if el.get(type_qn) not in (None, 'xs:base64Binary'):
- raise exc.InvalidInput(el.tag, ET.tostring(el))
- return base64.decodestring(el.text.encode('ascii'))
-
-
-class SoapProtocol(Protocol):
- """
- SOAP protocol.
-
- .. autoattribute:: name
- .. autoattribute:: content_types
- """
- name = 'soap'
- displayname = 'SOAP'
- content_types = ['application/soap+xml']
-
- ns = {
- "soap": "http://www.w3.org/2001/12/soap-envelope",
- "soapenv": "http://schemas.xmlsoap.org/soap/envelope/",
- "soapenc": "http://schemas.xmlsoap.org/soap/encoding/",
- }
-
- def __init__(self, tns=None, typenamespace=None, baseURL=None,
- servicename='MyApp'):
- self.tns = tns
- self.typenamespace = typenamespace
- self.servicename = servicename
- self.baseURL = baseURL
- self._name_mapping = {}
-
- self.encoder = SoapEncoder(typenamespace)
-
- def get_name_mapping(self, service=None):
- if service not in self._name_mapping:
- self._name_mapping[service] = dict(
- (soap_fname(path, f), path)
- for path, f in self.root.getapi()
- if service is None or (path and path[0] == service)
- )
- return self._name_mapping[service]
-
- def accept(self, req):
- for ct in self.content_types:
- if req.headers['Content-Type'].startswith(ct):
- return True
- if req.headers.get("Soapaction"):
- return True
- return False
-
- def iter_calls(self, request):
- yield CallContext(request)
-
- def extract_path(self, context):
- request = context.request
- el = ET.fromstring(request.body)
- body = el.find('{%(soapenv)s}Body' % self.ns)
- # Extract the service name from the tns
- message = list(body)[0]
- fname = message.tag
- if fname.startswith('{%s}' % self.typenamespace):
- fname = fname[len(self.typenamespace) + 2:]
- mapping = self.get_name_mapping()
- if fname not in mapping:
- raise exc.UnknownFunction(fname)
- path = mapping[fname]
- context.soap_message = message
- return path
- return None
-
- def read_arguments(self, context):
- kw = {}
- if not hasattr(context, 'soap_message'):
- return kw
- msg = context.soap_message
- for param in msg:
- # FIX for python2.6 (only for lxml)
- if use_lxml and isinstance(param, ET._Comment):
- continue
- name = param.tag[len(self.typenamespace) + 2:]
- arg = context.funcdef.get_arg(name)
- value = fromsoap(arg.datatype, param, {
- 'type': self.typenamespace,
- })
- kw[name] = value
- wsme.runtime.check_arguments(context.funcdef, (), kw)
- return kw
-
- def soap_response(self, path, funcdef, result):
- r = ET.Element('{%s}%sResponse' % (
- self.typenamespace, soap_fname(path, funcdef)
- ))
- log.debug('(soap_response, %s, %s)', funcdef.return_type, result)
- r.append(self.encoder.tosoap(
- funcdef.return_type, '{%s}result' % self.typenamespace, result
- ))
- return r
-
- def encode_result(self, context, result):
- log.debug('(encode_result, %s)', result)
- if use_lxml:
- envelope = ET.Element(
- Envelope_qn,
- nsmap={'xs': xsd_ns, 'types': self.typenamespace}
- )
- else:
- envelope = ET.Element(Envelope_qn, {
- 'xmlns:xs': xsd_ns,
- 'xmlns:types': self.typenamespace
- })
- body = ET.SubElement(envelope, Body_qn)
- body.append(self.soap_response(context.path, context.funcdef, result))
- s = ET.tostring(envelope)
- return s
-
- def get_template(self, name):
- return pkg_resources.resource_string(
- __name__, '%s.html' % name)
-
- def encode_error(self, context, infos):
- envelope = ET.Element(Envelope_qn)
- body = ET.SubElement(envelope, Body_qn)
- fault = ET.SubElement(body, Fault_qn)
- ET.SubElement(fault, faultcode_qn).text = infos['faultcode']
- ET.SubElement(fault, faultstring_qn).text = infos['faultstring']
- if 'debuginfo' in infos:
- ET.SubElement(fault, detail_qn).text = infos['debuginfo']
- s = ET.tostring(envelope)
- return s
-
- @expose('/api.wsdl', 'text/xml')
- def api_wsdl(self, service=None):
- if service is None:
- servicename = self.servicename
- else:
- servicename = self.servicename + service.capitalize()
- return WSDLGenerator(
- tns=self.tns,
- types_ns=self.typenamespace,
- soapenc=self.ns['soapenc'],
- service_name=servicename,
- complex_types=self.root.__registry__.complex_types,
- funclist=self.root.getapi(),
- arrays=self.root.__registry__.array_types,
- baseURL=self.baseURL,
- soap_array=soap_array,
- soap_type=soap_type,
- soap_fname=soap_fname,
- ).generate(True)
-
- def encode_sample_value(self, datatype, value, format=False):
- r = self.encoder.make_soap_element(datatype, 'value', value)
- if format:
- xml_indent(r)
- return ('xml', six.text_type(r))
-
-
-def xml_indent(elem, level=0):
- i = "\n" + level * " "
- if len(elem):
- if not elem.text or not elem.text.strip():
- elem.text = i + " "
- for e in elem:
- xml_indent(e, level + 1)
- if not e.tail or not e.tail.strip():
- e.tail = i
- if level and (not elem.tail or not elem.tail.strip()):
- elem.tail = i
diff --git a/wsmeext/soap/simplegeneric.py b/wsmeext/soap/simplegeneric.py
deleted file mode 100644
index 97c169b..0000000
--- a/wsmeext/soap/simplegeneric.py
+++ /dev/null
@@ -1,107 +0,0 @@
-import inspect
-
-__all__ = ["generic"]
-try:
- from types import ClassType, InstanceType
- classtypes = type, ClassType
-except ImportError:
- classtypes = type
- InstanceType = None
-
-
-def generic(func, argpos=None):
- """Create a simple generic function"""
-
- if argpos is None:
- if hasattr(func, 'argpos'):
- argpos = func.argpos
- else:
- argnames = inspect.getargspec(func)[0]
- if argnames and argnames[0] == 'self':
- argpos = 1
- else:
- argpos = 0
-
- _sentinel = object()
-
- def _by_class(*args, **kw):
- cls = args[argpos].__class__
- for t in type(cls.__name__, (cls, object), {}).__mro__:
- f = _gbt(t, _sentinel)
- if f is not _sentinel:
- return f(*args, **kw)
- else:
- return func(*args, **kw)
-
- _by_type = {object: func, InstanceType: _by_class}
- _gbt = _by_type.get
-
- def when_type(*types):
- """Decorator to add a method that will be called for the given types"""
- for t in types:
- if not isinstance(t, classtypes):
- raise TypeError(
- "%r is not a type or class" % (t,)
- )
-
- def decorate(f):
- for t in types:
- if _by_type.setdefault(t, f) is not f:
- raise TypeError(
- "%r already has method for type %r" % (func, t)
- )
- return f
- return decorate
-
- _by_object = {}
- _gbo = _by_object.get
-
- def when_object(*obs):
- """Decorator to add a method to be called for the given object(s)"""
- def decorate(f):
- for o in obs:
- if _by_object.setdefault(id(o), (o, f))[1] is not f:
- raise TypeError(
- "%r already has method for object %r" % (func, o)
- )
- return f
- return decorate
-
- def dispatch(*args, **kw):
- f = _gbo(id(args[argpos]), _sentinel)
- if f is _sentinel:
- for t in type(args[argpos]).__mro__:
- f = _gbt(t, _sentinel)
- if f is not _sentinel:
- return f(*args, **kw)
- else:
- return func(*args, **kw)
- else:
- return f[1](*args, **kw)
-
- dispatch.__name__ = func.__name__
- dispatch.__dict__ = func.__dict__.copy()
- dispatch.__doc__ = func.__doc__
- dispatch.__module__ = func.__module__
-
- dispatch.when_type = when_type
- dispatch.when_object = when_object
- dispatch.default = func
- dispatch.has_object = lambda o: id(o) in _by_object
- dispatch.has_type = lambda t: t in _by_type
- dispatch.argpos = argpos
- return dispatch
-
-
-def test_suite():
- import doctest
- return doctest.DocFileSuite(
- 'README.txt',
- optionflags=doctest.ELLIPSIS | doctest.REPORT_ONLY_FIRST_FAILURE,
- )
-
-
-if __name__ == '__main__':
- import unittest
- r = unittest.TextTestRunner()
- r.run(test_suite())
diff --git a/wsmeext/soap/wsdl.py b/wsmeext/soap/wsdl.py
deleted file mode 100644
index b60aff4..0000000
--- a/wsmeext/soap/wsdl.py
+++ /dev/null
@@ -1,297 +0,0 @@
-import six
-import wsme.types
-
-try:
- from lxml import etree as ET
- use_lxml = True
-except ImportError:
- from xml.etree import cElementTree as ET # noqa
- use_lxml = False
-
-
-def xml_tostring(el, pretty_print=False):
- if use_lxml:
- return ET.tostring(el, pretty_print=pretty_print)
- return ET.tostring(el)
-
-
-class NS(object):
- def __init__(self, url):
- self.url = url
-
- def __call__(self, name):
- return self.qn(name)
-
- def __str__(self):
- return self.url
-
- def qn(self, name):
- return '{%s}%s' % (self.url, name)
-
-
-wsdl_ns = NS("http://schemas.xmlsoap.org/wsdl/")
-soap_ns = NS("http://schemas.xmlsoap.org/wsdl/soap/")
-xs_ns = NS("http://www.w3.org/2001/XMLSchema")
-soapenc_ns = NS("http://schemas.xmlsoap.org/soap/encoding/")
-
-
-class WSDLGenerator(object):
- def __init__(
- self,
- tns,
- types_ns,
- soapenc,
- service_name,
- complex_types,
- funclist,
- arrays,
- baseURL,
- soap_array,
- soap_type,
- soap_fname):
-
- self.tns = NS(tns)
- self.types_ns = NS(types_ns)
- self.soapenc = soapenc
- self.service_name = service_name
- self.complex_types = complex_types
- self.funclist = funclist
- self.arrays = arrays
- self.baseURL = baseURL or ''
- self.soap_array = soap_array
- self.soap_fname = soap_fname
- self.soap_type = soap_type
-
- def gen_complex_type(self, cls):
- complexType = ET.Element(xs_ns('complexType'))
- complexType.set('name', cls.__name__)
- sequence = ET.SubElement(complexType, xs_ns('sequence'))
- for attrdef in wsme.types.list_attributes(cls):
- soap_type = self.soap_type(attrdef.datatype, str(self.types_ns))
- if soap_type is None:
- continue
- element = ET.SubElement(sequence, xs_ns('element'))
- element.set('name', attrdef.name)
- element.set('type', soap_type)
- element.set('minOccurs', '1' if attrdef.mandatory else '0')
- element.set('maxOccurs', '1')
- return complexType
-
- def gen_array(self, array):
- complexType = ET.Element(xs_ns('complexType'))
- complexType.set('name', self.soap_array(array, False))
- ET.SubElement(
- ET.SubElement(complexType, xs_ns('sequence')),
- xs_ns('element'),
- name='item',
- maxOccurs='unbounded',
- nillable='true',
- type=self.soap_type(array.item_type, self.types_ns)
- )
- return complexType
-
- def gen_function_types(self, path, funcdef):
- args_el = ET.Element(
- xs_ns('element'),
- name=self.soap_fname(path, funcdef)
- )
-
- sequence = ET.SubElement(
- ET.SubElement(args_el, xs_ns('complexType')),
- xs_ns('sequence')
- )
-
- for farg in funcdef.arguments:
- t = self.soap_type(farg.datatype, True)
- if t is None:
- continue
- element = ET.SubElement(
- sequence, xs_ns('element'),
- name=farg.name,
- type=self.soap_type(farg.datatype, True)
- )
- if not farg.mandatory:
- element.set('minOccurs', '0')
-
- response_el = ET.Element(
- xs_ns('element'),
- name=self.soap_fname(path, funcdef) + 'Response'
- )
- element = ET.SubElement(
- ET.SubElement(
- ET.SubElement(
- response_el,
- xs_ns('complexType')
- ),
- xs_ns('sequence')
- ),
- xs_ns('element'),
- name='result'
- )
- return_soap_type = self.soap_type(funcdef.return_type, True)
- if return_soap_type is not None:
- element.set('type', return_soap_type)
-
- return args_el, response_el
-
- def gen_types(self):
- types = ET.Element(wsdl_ns('types'))
- schema = ET.SubElement(types, xs_ns('schema'))
- schema.set('elementFormDefault', 'qualified')
- schema.set('targetNamespace', str(self.types_ns))
- for cls in self.complex_types:
- schema.append(self.gen_complex_type(cls))
- for array in self.arrays:
- schema.append(self.gen_array(array))
- for path, funcdef in self.funclist:
- schema.extend(self.gen_function_types(path, funcdef))
- return types
-
- def gen_functions(self):
- messages = []
-
- binding = ET.Element(
- wsdl_ns('binding'),
- name='%s_Binding' % self.service_name,
- type='tns:%s_PortType' % self.service_name
- )
- ET.SubElement(
- binding,
- soap_ns('binding'),
- style='document',
- transport='http://schemas.xmlsoap.org/soap/http'
- )
-
- portType = ET.Element(
- wsdl_ns('portType'),
- name='%s_PortType' % self.service_name
- )
-
- for path, funcdef in self.funclist:
- soap_fname = self.soap_fname(path, funcdef)
-
- # message
- req_message = ET.Element(
- wsdl_ns('message'),
- name=soap_fname + 'Request',
- xmlns=str(self.types_ns)
- )
- ET.SubElement(
- req_message,
- wsdl_ns('part'),
- name='parameters',
- element='types:%s' % soap_fname
- )
- messages.append(req_message)
-
- res_message = ET.Element(
- wsdl_ns('message'),
- name=soap_fname + 'Response',
- xmlns=str(self.types_ns)
- )
- ET.SubElement(
- res_message,
- wsdl_ns('part'),
- name='parameters',
- element='types:%sResponse' % soap_fname
- )
- messages.append(res_message)
-
- # portType/operation
- operation = ET.SubElement(
- portType,
- wsdl_ns('operation'),
- name=soap_fname
- )
- if funcdef.doc:
- ET.SubElement(
- operation,
- wsdl_ns('documentation')
- ).text = funcdef.doc
- ET.SubElement(
- operation, wsdl_ns('input'),
- message='tns:%sRequest' % soap_fname
- )
- ET.SubElement(
- operation, wsdl_ns('output'),
- message='tns:%sResponse' % soap_fname
- )
-
- # binding/operation
- operation = ET.SubElement(
- binding,
- wsdl_ns('operation'),
- name=soap_fname
- )
- ET.SubElement(
- operation,
- soap_ns('operation'),
- soapAction=soap_fname
- )
- ET.SubElement(
- ET.SubElement(
- operation,
- wsdl_ns('input')
- ),
- soap_ns('body'),
- use='literal'
- )
- ET.SubElement(
- ET.SubElement(
- operation,
- wsdl_ns('output')
- ),
- soap_ns('body'),
- use='literal'
- )
-
- return messages + [portType, binding]
-
- def gen_service(self):
- service = ET.Element(wsdl_ns('service'), name=self.service_name)
- ET.SubElement(
- service,
- wsdl_ns('documentation')
- ).text = six.u('WSDL File for %s') % self.service_name
- ET.SubElement(
- ET.SubElement(
- service,
- wsdl_ns('port'),
- binding='tns:%s_Binding' % self.service_name,
- name='%s_PortType' % self.service_name
- ),
- soap_ns('address'),
- location=self.baseURL
- )
-
- return service
-
- def gen_definitions(self):
- attrib = {
- 'name': self.service_name,
- 'targetNamespace': str(self.tns)
- }
- if use_lxml:
- definitions = ET.Element(
- wsdl_ns('definitions'),
- attrib=attrib,
- nsmap={
- 'xs': str(xs_ns),
- 'soap': str(soap_ns),
- 'types': str(self.types_ns),
- 'tns': str(self.tns)
- }
- )
- else:
- definitions = ET.Element(wsdl_ns('definitions'), **attrib)
- definitions.set('xmlns:types', str(self.types_ns))
- definitions.set('xmlns:tns', str(self.tns))
-
- definitions.set('name', self.service_name)
- definitions.append(self.gen_types())
- definitions.extend(self.gen_functions())
- definitions.append(self.gen_service())
- return definitions
-
- def generate(self, format=False):
- return xml_tostring(self.gen_definitions(), pretty_print=format)
diff --git a/wsmeext/sphinxext.py b/wsmeext/sphinxext.py
deleted file mode 100644
index 7c45a0f..0000000
--- a/wsmeext/sphinxext.py
+++ /dev/null
@@ -1,600 +0,0 @@
-import inspect
-import re
-import sys
-
-import six
-
-from sphinx import addnodes
-from sphinx.ext import autodoc
-from sphinx.domains.python import PyClasslike, PyClassmember
-from sphinx.domains import Domain, ObjType
-from sphinx.directives import ObjectDescription
-from sphinx.util.docfields import Field
-from sphinx.util.nodes import make_refnode
-
-from sphinx.roles import XRefRole
-from sphinx.locale import l_, _
-
-from docutils.parsers.rst import Directive
-from docutils.parsers.rst import directives
-
-import wsme
-import wsme.types
-import wsme.rest.json
-import wsme.rest.xml
-
-field_re = re.compile(r':(?P<field>\w+)(\s+(?P<name>\w+))?:')
-
-
-def datatypename(datatype):
- if isinstance(datatype, wsme.types.UserType):
- return datatype.name
- if isinstance(datatype, wsme.types.DictType):
- return 'dict(%s: %s)' % (datatypename(datatype.key_type),
- datatypename(datatype.value_type))
- if isinstance(datatype, wsme.types.ArrayType):
- return 'list(%s)' % datatypename(datatype.item_type)
- return datatype.__name__
-
-
-def make_sample_object(datatype):
- if datatype is wsme.types.bytes:
- return six.b('samplestring')
- if datatype is wsme.types.text:
- return u'sample unicode'
- if datatype is int:
- return 5
- sample_obj = getattr(datatype, 'sample', datatype)()
- return sample_obj
-
-
-def get_protocols(names):
- names = list(names)
- protocols = []
- if 'rest' in names:
- names.remove('rest')
- protocols.extend('restjson', 'restxml')
- if 'restjson' in names:
- names.remove('restjson')
- protocols.append(('Json', wsme.rest.json))
- if 'restxml' in names:
- names.remove('restxml')
- protocols.append(('XML', wsme.rest.xml))
- for name in names:
- p = wsme.protocol.getprotocol(name)
- protocols.append((p.displayname or p.name, p))
- return protocols
-
-
-class SampleType(object):
- """A Sample Type"""
-
- #: A Int
- aint = int
-
- def __init__(self, aint=None):
- if aint:
- self.aint = aint
-
- @classmethod
- def sample(cls):
- return cls(10)
-
-
-class SampleService(wsme.WSRoot):
- @wsme.expose(SampleType)
- @wsme.validate(SampleType, int, str)
- def change_aint(data, aint, dummy='useless'):
- """
- :param aint: The new value
-
- :return: The data object with its aint field value changed.
- """
- data.aint = aint
- return data
-
-
-def getroot(env, force=False):
- root = env.temp_data.get('wsme:root')
- if not force and root:
- return root
- rootpath = env.temp_data.get('wsme:rootpath', env.app.config.wsme_root)
-
- if rootpath is None:
- return None
-
- modname, classname = rootpath.rsplit('.', 1)
- __import__(modname)
- module = sys.modules[modname]
- root = getattr(module, classname)
- env.temp_data['wsme:root'] = root
- return root
-
-
-def scan_services(service, path=[]):
- has_functions = False
- for name in dir(service):
- if name.startswith('_'):
- continue
- a = getattr(service, name)
- if inspect.ismethod(a):
- if hasattr(a, '_wsme_definition'):
- has_functions = True
- if inspect.isclass(a):
- continue
- if len(path) > wsme.rest.APIPATH_MAXLEN:
- raise ValueError("Path is too long: " + str(path))
- for value in scan_services(a, path + [name]):
- yield value
- if has_functions:
- yield service, path
-
-
-def find_service_path(env, service):
- root = getroot(env)
- if service == root:
- return []
- for s, path in scan_services(root):
- if s == service:
- return path
- return None
-
-
-class TypeDirective(PyClasslike):
- def get_index_text(self, modname, name_cls):
- return _('%s (webservice type)') % name_cls[0]
-
- def add_target_and_index(self, name_cls, sig, signode):
- ret = super(TypeDirective, self).add_target_and_index(
- name_cls, sig, signode
- )
- name = name_cls[0]
- types = self.env.domaindata['wsme']['types']
- if name in types:
- self.state_machine.reporter.warning(
- 'duplicate type description of %s ' % name)
- types[name] = self.env.docname
- return ret
-
-
-class AttributeDirective(PyClassmember):
- doc_field_types = [
- Field('datatype', label=l_('Type'), has_arg=False,
- names=('type', 'datatype'))
- ]
-
-
-def check_samples_slot(value):
- """Validate the samples_slot option to the TypeDocumenter.
-
- Valid positions are 'before-docstring' and
- 'after-docstring'. Using the explicit 'none' disables sample
- output. The default is after-docstring.
- """
- if not value:
- return 'after-docstring'
- val = directives.choice(
- value,
- ('none', # do not include
- 'before-docstring', # show samples then docstring
- 'after-docstring', # show docstring then samples
- ))
- return val
-
-
-class TypeDocumenter(autodoc.ClassDocumenter):
- objtype = 'type'
- directivetype = 'type'
- domain = 'wsme'
-
- required_arguments = 1
- default_samples_slot = 'after-docstring'
-
- option_spec = dict(
- autodoc.ClassDocumenter.option_spec,
- **{'protocols': lambda l: [v.strip() for v in l.split(',')],
- 'samples-slot': check_samples_slot,
- })
-
- @staticmethod
- def can_document_member(member, membername, isattr, parent):
- # we don't want to be automaticaly used
- # TODO check if the member is registered an an exposed type
- return False
-
- def format_name(self):
- return self.object.__name__
-
- def format_signature(self):
- return u''
-
- def add_directive_header(self, sig):
- super(TypeDocumenter, self).add_directive_header(sig)
- # remove the :module: option that was added by ClassDocumenter
- result_len = len(self.directive.result)
- for index, item in zip(reversed(range(result_len)),
- reversed(self.directive.result)):
- if ':module:' in item:
- self.directive.result.pop(index)
-
- def import_object(self):
- if super(TypeDocumenter, self).import_object():
- wsme.types.register_type(self.object)
- return True
- else:
- return False
-
- def add_content(self, more_content, no_docstring=False):
- # Check where to include the samples
- samples_slot = self.options.samples_slot or self.default_samples_slot
-
- def add_docstring():
- super(TypeDocumenter, self).add_content(
- more_content, no_docstring)
-
- def add_samples():
- protocols = get_protocols(
- self.options.protocols or self.env.app.config.wsme_protocols
- )
- content = []
- if protocols:
- sample_obj = make_sample_object(self.object)
- content.extend([
- l_(u'Data samples:'),
- u'',
- u'.. cssclass:: toggle',
- u''
- ])
- for name, protocol in protocols:
- language, sample = protocol.encode_sample_value(
- self.object, sample_obj, format=True)
- content.extend([
- name,
- u' .. code-block:: ' + language,
- u'',
- ])
- content.extend(
- u' ' * 8 + line
- for line in six.text_type(sample).split('\n'))
- for line in content:
- self.add_line(line, u'<wsmeext.sphinxext')
-
- self.add_line(u'', '<wsmeext.sphinxext>')
-
- if samples_slot == 'after-docstring':
- add_docstring()
- add_samples()
- elif samples_slot == 'before-docstring':
- add_samples()
- add_docstring()
- else:
- add_docstring()
-
-
-class AttributeDocumenter(autodoc.AttributeDocumenter):
- datatype = None
- domain = 'wsme'
-
- @staticmethod
- def can_document_member(member, membername, isattr, parent):
- return isinstance(parent, TypeDocumenter)
-
- def import_object(self):
- success = super(AttributeDocumenter, self).import_object()
- if success:
- self.datatype = self.object.datatype
- return success
-
- def add_content(self, more_content, no_docstring=False):
- self.add_line(
- u':type: %s' % datatypename(self.datatype),
- '<wsmeext.sphinxext>'
- )
- self.add_line(u'', '<wsmeext.sphinxext>')
- super(AttributeDocumenter, self).add_content(
- more_content, no_docstring)
-
- def add_directive_header(self, sig):
- super(AttributeDocumenter, self).add_directive_header(sig)
-
-
-class RootDirective(Directive):
- """
- This directive is to tell what class is the Webservice root
- """
- has_content = False
- required_arguments = 1
- optional_arguments = 0
- final_argument_whitespace = False
- option_spec = {
- 'webpath': directives.unchanged
- }
-
- def run(self):
- env = self.state.document.settings.env
- rootpath = self.arguments[0].strip()
- env.temp_data['wsme:rootpath'] = rootpath
- if 'wsme:root' in env.temp_data:
- del env.temp_data['wsme:root']
- if 'webpath' in self.options:
- env.temp_data['wsme:webpath'] = self.options['webpath']
- return []
-
-
-class ServiceDirective(ObjectDescription):
- name = 'service'
-
- optional_arguments = 1
-
- def handle_signature(self, sig, signode):
- path = sig.split('/')
-
- namespace = '/'.join(path[:-1])
- if namespace and not namespace.endswith('/'):
- namespace += '/'
-
- servicename = path[-1]
-
- if not namespace and not servicename:
- servicename = '/'
-
- signode += addnodes.desc_annotation('service ', 'service ')
-
- if namespace:
- signode += addnodes.desc_addname(namespace, namespace)
-
- signode += addnodes.desc_name(servicename, servicename)
-
- return sig
-
-
-class ServiceDocumenter(autodoc.ClassDocumenter):
- domain = 'wsme'
- objtype = 'service'
- directivetype = 'service'
-
- def add_directive_header(self, sig):
- super(ServiceDocumenter, self).add_directive_header(sig)
- # remove the :module: option that was added by ClassDocumenter
- result_len = len(self.directive.result)
- for index, item in zip(reversed(range(result_len)),
- reversed(self.directive.result)):
- if ':module:' in item:
- self.directive.result.pop(index)
-
- def format_signature(self):
- return u''
-
- def format_name(self):
- path = find_service_path(self.env, self.object)
- if path is None:
- return
- return '/' + '/'.join(path)
-
-
-class FunctionDirective(PyClassmember):
- name = 'function'
- objtype = 'function'
-
- def get_signature_prefix(self, sig):
- return 'function '
-
-
-def document_function(funcdef, docstrings=None, protocols=['restjson']):
- """A helper function to complete a function documentation with return and
- parameter types"""
- # If the function doesn't have a docstring, add an empty list
- # so the default behaviors below work correctly.
- if not docstrings:
- docstrings = [[]]
- found_params = set()
-
- for si, docstring in enumerate(docstrings):
- for i, line in enumerate(docstring):
- m = field_re.match(line)
- if m and m.group('field') == 'param':
- found_params.add(m.group('name'))
-
- next_param_pos = (0, 0)
-
- for arg in funcdef.arguments:
- content = [
- u':type %s: :wsme:type:`%s`' % (
- arg.name, datatypename(arg.datatype))
- ]
- if arg.name not in found_params:
- content.insert(0, u':param %s: ' % (arg.name))
- pos = next_param_pos
- else:
- for si, docstring in enumerate(docstrings):
- for i, line in enumerate(docstring):
- m = field_re.match(line)
- if m and m.group('field') == 'param' \
- and m.group('name') == arg.name:
- pos = (si, i + 1)
- break
- docstring = docstrings[pos[0]]
- docstring[pos[1]:pos[1]] = content
- next_param_pos = (pos[0], pos[1] + len(content))
-
- if funcdef.return_type:
- content = [
- u':rtype: %s' % datatypename(funcdef.return_type)
- ]
- pos = None
- for si, docstring in enumerate(docstrings):
- for i, line in enumerate(docstring):
- m = field_re.match(line)
- if m and m.group('field') == 'return':
- pos = (si, i + 1)
- break
- else:
- pos = next_param_pos
- docstring = docstrings[pos[0]]
- docstring[pos[1]:pos[1]] = content
-
- codesamples = []
-
- if protocols:
- params = []
- for arg in funcdef.arguments:
- params.append((
- arg.name,
- arg.datatype,
- make_sample_object(arg.datatype)
- ))
- codesamples.extend([
- u':%s:' % l_(u'Parameters samples'),
- u' .. cssclass:: toggle',
- u''
- ])
- for name, protocol in protocols:
- language, sample = protocol.encode_sample_params(
- params, format=True)
- codesamples.extend([
- u' ' * 4 + name,
- u' .. code-block:: ' + language,
- u'',
- ])
- codesamples.extend((
- u' ' * 12 + line
- for line in six.text_type(sample).split('\n')
- ))
-
- if funcdef.return_type:
- codesamples.extend([
- u':%s:' % l_(u'Return samples'),
- u' .. cssclass:: toggle',
- u''
- ])
- sample_obj = make_sample_object(funcdef.return_type)
- for name, protocol in protocols:
- language, sample = protocol.encode_sample_result(
- funcdef.return_type, sample_obj, format=True)
- codesamples.extend([
- u' ' * 4 + name,
- u' .. code-block:: ' + language,
- u'',
- ])
- codesamples.extend((
- u' ' * 12 + line
- for line in six.text_type(sample).split('\n')
- ))
-
- docstrings[0:0] = [codesamples]
- return docstrings
-
-
-class FunctionDocumenter(autodoc.MethodDocumenter):
- domain = 'wsme'
- directivetype = 'function'
- objtype = 'function'
- priority = 1
-
- option_spec = {
- 'path': directives.unchanged,
- 'method': directives.unchanged
- }
-
- @staticmethod
- def can_document_member(member, membername, isattr, parent):
- return (isinstance(parent, ServiceDocumenter) and
- wsme.api.iswsmefunction(member))
-
- def import_object(self):
- ret = super(FunctionDocumenter, self).import_object()
- self.directivetype = 'function'
- self.wsme_fd = wsme.api.FunctionDefinition.get(self.object)
- self.retann = datatypename(self.wsme_fd.return_type)
- return ret
-
- def format_args(self):
- args = [arg.name for arg in self.wsme_fd.arguments]
- defaults = [
- arg.default
- for arg in self.wsme_fd.arguments if not arg.mandatory
- ]
- return inspect.formatargspec(args, defaults=defaults)
-
- def get_doc(self, encoding=None):
- """Inject the type and param fields into the docstrings so that the
- user can add its own param fields to document the parameters"""
- docstrings = super(FunctionDocumenter, self).get_doc(encoding)
-
- protocols = get_protocols(
- self.options.protocols or self.env.app.config.wsme_protocols
- )
-
- return document_function(
- self.wsme_fd, docstrings, protocols
- )
-
- def add_content(self, more_content, no_docstring=False):
- super(FunctionDocumenter, self).add_content(more_content, no_docstring)
-
- def format_name(self):
- return self.wsme_fd.name
-
- def add_directive_header(self, sig):
- super(FunctionDocumenter, self).add_directive_header(sig)
- # remove the :module: option that was added by ClassDocumenter
- result_len = len(self.directive.result)
- for index, item in zip(reversed(range(result_len)),
- reversed(self.directive.result)):
- if ':module:' in item:
- self.directive.result.pop(index)
-
-
-class WSMEDomain(Domain):
- name = 'wsme'
- label = 'WSME'
-
- object_types = {
- 'type': ObjType(l_('type'), 'type', 'obj'),
- 'service': ObjType(l_('service'), 'service', 'obj')
- }
-
- directives = {
- 'type': TypeDirective,
- 'attribute': AttributeDirective,
- 'service': ServiceDirective,
- 'root': RootDirective,
- 'function': FunctionDirective,
- }
-
- roles = {
- 'type': XRefRole()
- }
-
- initial_data = {
- 'types': {}, # fullname -> docname
- }
-
- def clear_doc(self, docname):
- keys = list(self.data['types'].keys())
- for key in keys:
- value = self.data['types'][key]
- if value == docname:
- del self.data['types'][key]
-
- def resolve_xref(self, env, fromdocname, builder,
- type, target, node, contnode):
- if target not in self.data['types']:
- return None
- todocname = self.data['types'][target]
- return make_refnode(
- builder, fromdocname, todocname, target, contnode, target)
-
-
-def setup(app):
- app.add_domain(WSMEDomain)
- app.add_autodocumenter(TypeDocumenter)
- app.add_autodocumenter(AttributeDocumenter)
- app.add_autodocumenter(ServiceDocumenter)
- app.add_autodocumenter(FunctionDocumenter)
-
- app.add_config_value('wsme_root', None, 'env')
- app.add_config_value('wsme_webpath', '/', 'env')
- app.add_config_value('wsme_protocols', ['restjson', 'restxml'], 'env')
- app.add_javascript('toggle.js')
- app.add_stylesheet('toggle.css')
diff --git a/wsmeext/sqlalchemy/__init__.py b/wsmeext/sqlalchemy/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/wsmeext/sqlalchemy/__init__.py
+++ /dev/null
diff --git a/wsmeext/sqlalchemy/controllers.py b/wsmeext/sqlalchemy/controllers.py
deleted file mode 100644
index 504a46a..0000000
--- a/wsmeext/sqlalchemy/controllers.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from wsme.rest import expose, validate
-import wsme.types
-
-from wsmeext.sqlalchemy.types import SQLAlchemyRegistry
-
-
-class CRUDControllerMeta(type):
- def __init__(cls, name, bases, dct):
- if cls.__saclass__ is not None:
- if cls.__registry__ is None:
- cls.__registry__ = wsme.types.registry
- if cls.__wstype__ is None:
- cls.__wstype__ = cls.__registry__.resolve_type(
- SQLAlchemyRegistry.get(
- cls.__registry__).getdatatype(cls.__saclass__))
-
- cls.create = expose(
- cls.__wstype__,
- method='PUT',
- wrap=True
- )(cls.create)
- cls.create = validate(cls.__wstype__)(cls.create)
-
- cls.read = expose(
- cls.__wstype__,
- method='GET',
- wrap=True
- )(cls.read)
- cls.read = validate(cls.__wstype__)(cls.read)
-
- cls.update = expose(
- cls.__wstype__,
- method='POST',
- wrap=True
- )(cls.update)
- cls.update = validate(cls.__wstype__)(cls.update)
-
- cls.delete = expose(
- method='DELETE',
- wrap=True
- )(cls.delete)
- cls.delete = validate(cls.__wstype__)(cls.delete)
-
- super(CRUDControllerMeta, cls).__init__(name, bases, dct)
-
-
-class CRUDControllerBase(object):
- __registry__ = None
- __saclass__ = None
- __wstype__ = None
- __dbsession__ = None
-
- def _create_one(self, data):
- obj = self.__saclass__()
- data.to_instance(obj)
- self.__dbsession__.add(obj)
- return obj
-
- def _get_one(self, ref):
- q = self.__dbsession__.query(self.__saclass__)
- q = q.filter(ref.get_ref_criterion())
- return q.one()
-
- def _update_one(self, data):
- obj = self._get_one(data)
- if obj is None:
- raise ValueError("No match for data=%s" % data)
- data.to_instance(obj)
- return obj
-
- def _delete(self, ref):
- obj = self._get_one(ref)
- self.__dbsession__.delete(obj)
-
- def create(self, data):
- obj = self._create_one(data)
- self.__dbsession__.flush()
- return self.__wstype__(obj)
-
- def read(self, ref):
- obj = self._get_one(ref)
- return self.__wstype__(obj)
-
- def update(self, data):
- obj = self._update_one(data)
- self.__dbsession__.flush()
- return self.__wstype__(obj)
-
- def delete(self, ref):
- self._delete(ref)
- self.__dbsession__.flush()
- return None
-
-
-CRUDController = CRUDControllerMeta(
- 'CRUDController', (CRUDControllerBase,), {}
-)
diff --git a/wsmeext/sqlalchemy/types.py b/wsmeext/sqlalchemy/types.py
deleted file mode 100644
index 414bf52..0000000
--- a/wsmeext/sqlalchemy/types.py
+++ /dev/null
@@ -1,200 +0,0 @@
-import datetime
-import decimal
-import logging
-
-import six
-
-from sqlalchemy.orm import class_mapper
-from sqlalchemy.orm.properties import ColumnProperty, RelationProperty
-
-import sqlalchemy.types
-
-import wsme.types
-
-log = logging.getLogger(__name__)
-
-
-class SQLAlchemyRegistry(object):
- @classmethod
- def get(cls, registry):
- if not hasattr(registry, 'sqlalchemy'):
- registry.sqlalchemy = cls()
- return registry.sqlalchemy
-
- def __init__(self):
- self.types = {}
- self.satypeclasses = {
- sqlalchemy.types.Integer: int,
- sqlalchemy.types.Boolean: bool,
- sqlalchemy.types.Float: float,
- sqlalchemy.types.Numeric: decimal.Decimal,
- sqlalchemy.types.Date: datetime.date,
- sqlalchemy.types.Time: datetime.time,
- sqlalchemy.types.DateTime: datetime.datetime,
- sqlalchemy.types.String: wsme.types.text,
- sqlalchemy.types.Unicode: wsme.types.text,
- }
-
- def getdatatype(self, sadatatype):
- if sadatatype.__class__ in self.satypeclasses:
- return self.satypeclasses[sadatatype.__class__]
- elif sadatatype in self.types:
- return self.types[sadatatype]
- else:
- return sadatatype.__name__
-
-
-def register_saclass(registry, saclass, typename=None):
- """Associate a webservice type name to a SQLAlchemy mapped class.
- The default typename if the saclass name itself.
- """
- if typename is None:
- typename = saclass.__name__
-
- SQLAlchemyRegistry.get(registry).types[saclass] = typename
-
-
-class wsattr(wsme.types.wsattr):
- def __init__(self, datatype, saproperty=None, **kw):
- super(wsattr, self).__init__(datatype, **kw)
- self.saname = saproperty.key
- self.saproperty = saproperty
- self.isrelation = isinstance(saproperty, RelationProperty)
-
-
-def make_wsattr(registry, saproperty):
- datatype = None
- if isinstance(saproperty, ColumnProperty):
- if len(saproperty.columns) > 1:
- log.warning("Cannot handle multi-column ColumnProperty")
- return None
- datatype = SQLAlchemyRegistry.get(registry).getdatatype(
- saproperty.columns[0].type)
- elif isinstance(saproperty, RelationProperty):
- other_saclass = saproperty.mapper.class_
- datatype = SQLAlchemyRegistry.get(registry).getdatatype(other_saclass)
- if saproperty.uselist:
- datatype = [datatype]
- else:
- log.warning("Don't know how to handle %s attributes" %
- saproperty.__class__)
-
- if datatype:
- return wsattr(datatype, saproperty)
-
-
-class BaseMeta(wsme.types.BaseMeta):
- def __new__(cls, name, bases, dct):
- if '__registry__' not in dct:
- dct['__registry__'] = wsme.types.registry
- return type.__new__(cls, name, bases, dct)
-
- def __init__(cls, name, bases, dct):
- saclass = getattr(cls, '__saclass__', None)
- if saclass:
- mapper = class_mapper(saclass)
- cls._pkey_attrs = []
- cls._ref_attrs = []
- for prop in mapper.iterate_properties:
- key = prop.key
- if hasattr(cls, key):
- continue
- if key.startswith('_'):
- continue
- attr = make_wsattr(cls.__registry__, prop)
- if attr is not None:
- setattr(cls, key, attr)
-
- if attr and isinstance(prop, ColumnProperty) and \
- prop.columns[0] in mapper.primary_key:
- cls._pkey_attrs.append(attr)
- cls._ref_attrs.append(attr)
-
- register_saclass(cls.__registry__, cls.__saclass__, cls.__name__)
- super(BaseMeta, cls).__init__(name, bases, dct)
-
-
-class Base(six.with_metaclass(BaseMeta, wsme.types.Base)):
- def __init__(self, instance=None, keyonly=False, attrs=None, eagerload=[]):
- if instance:
- self.from_instance(instance, keyonly, attrs, eagerload)
-
- def from_instance(self, instance, keyonly=False, attrs=None, eagerload=[]):
- if keyonly:
- attrs = self._pkey_attrs + self._ref_attrs
- for attr in self._wsme_attributes:
- if not isinstance(attr, wsattr):
- continue
- if attrs and not attr.isrelation and attr.name not in attrs:
- continue
- if attr.isrelation and attr.name not in eagerload:
- continue
- value = getattr(instance, attr.saname)
- if attr.isrelation:
- attr_keyonly = attr.name not in eagerload
- attr_attrs = None
- attr_eagerload = []
- if not attr_keyonly:
- attr_attrs = [
- aname[len(attr.name) + 1:]
- for aname in attrs
- if aname.startswith(attr.name + '.')
- ]
- attr_eagerload = [
- aname[len(attr.name) + 1:]
- for aname in eagerload
- if aname.startswith(attr.name + '.')
- ]
- if attr.saproperty.uselist:
- value = [
- attr.datatype.item_type(
- o,
- keyonly=attr_keyonly,
- attrs=attr_attrs,
- eagerload=attr_eagerload
- )
- for o in value
- ]
- else:
- value = attr.datatype(
- value,
- keyonly=attr_keyonly,
- attrs=attr_attrs,
- eagerload=attr_eagerload
- )
- attr.__set__(self, value)
-
- def to_instance(self, instance):
- for attr in self._wsme_attributes:
- if isinstance(attr, wsattr):
- value = attr.__get__(self, self.__class__)
- if value is not wsme.types.Unset:
- setattr(instance, attr.saname, value)
-
- def get_ref_criterion(self):
- """Returns a criterion that match a database object
- having the pkey/ref attribute values of this webservice object"""
- criterions = []
- for attr in self._pkey_attrs + self._ref_attrs:
- value = attr.__get__(self, self.__class__)
- if value is not wsme.types.Unset:
- criterions.append(attr.saproperty == value)
-
-
-def generate_types(*classes, **kw):
- registry = kw.pop('registry', wsme.types.registry)
- prefix = kw.pop('prefix', '')
- postfix = kw.pop('postfix', '')
- makename = kw.pop('makename', lambda s: prefix + s + postfix)
-
- newtypes = {}
- for c in classes:
- if isinstance(c, list):
- newtypes.update(generate_types(c))
- else:
- name = makename(c.__name__)
- newtypes[name] = BaseMeta(name, (Base, ), {
- '__saclass__': c,
- '__registry__': registry
- })
- return newtypes
diff --git a/wsmeext/tests/__init__.py b/wsmeext/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/wsmeext/tests/__init__.py
+++ /dev/null
diff --git a/wsmeext/tests/test_extdirect.py b/wsmeext/tests/test_extdirect.py
deleted file mode 100644
index 4c5bea8..0000000
--- a/wsmeext/tests/test_extdirect.py
+++ /dev/null
@@ -1,243 +0,0 @@
-import base64
-import datetime
-import decimal
-
-try:
- import simplejson as json
-except ImportError:
- import json # noqa
-
-import wsme.tests.protocol
-from wsme.utils import parse_isodatetime, parse_isodate, parse_isotime
-from wsme.types import isarray, isdict, isusertype
-
-import six
-
-if six.PY3:
- from urllib.parse import urlencode
-else:
- from urllib import urlencode # noqa
-
-
-def encode_arg(value):
- if isinstance(value, tuple):
- value, datatype = value
- else:
- datatype = type(value)
-
- if isinstance(datatype, list):
- value = [encode_arg((item, datatype[0])) for item in value]
- elif isinstance(datatype, dict):
- key_type, value_type = list(datatype.items())[0]
- value = dict((
- (encode_arg((key, key_type)),
- encode_arg((value, value_type)))
- for key, value in value.items()
- ))
- elif datatype in (datetime.date, datetime.time, datetime.datetime):
- value = value.isoformat()
- elif datatype == wsme.types.binary:
- value = base64.encodestring(value).decode('ascii')
- elif datatype == wsme.types.bytes:
- value = value.decode('ascii')
- elif datatype == decimal.Decimal:
- value = str(value)
- return value
-
-
-def decode_result(value, datatype):
- if value is None:
- return None
- if datatype == wsme.types.binary:
- value = base64.decodestring(value.encode('ascii'))
- return value
- if isusertype(datatype):
- datatype = datatype.basetype
- if isinstance(datatype, list):
- value = [decode_result(item, datatype[0]) for item in value]
- elif isarray(datatype):
- value = [decode_result(item, datatype.item_type) for item in value]
- elif isinstance(datatype, dict):
- key_type, value_type = list(datatype.items())[0]
- value = dict((
- (decode_result(key, key_type),
- decode_result(value, value_type))
- for key, value in value.items()
- ))
- elif isdict(datatype):
- key_type, value_type = datatype.key_type, datatype.value_type
- value = dict((
- (decode_result(key, key_type),
- decode_result(value, value_type))
- for key, value in value.items()
- ))
- elif datatype == datetime.time:
- value = parse_isotime(value)
- elif datatype == datetime.date:
- value = parse_isodate(value)
- elif datatype == datetime.datetime:
- value = parse_isodatetime(value)
- elif hasattr(datatype, '_wsme_attributes'):
- for attr in datatype._wsme_attributes:
- if attr.key not in value:
- continue
- value[attr.key] = decode_result(value[attr.key], attr.datatype)
- elif datatype == decimal.Decimal:
- value = decimal.Decimal(value)
- elif datatype == wsme.types.bytes:
- value = value.encode('ascii')
- elif datatype is not None and type(value) != datatype:
- value = datatype(value)
- return value
-
-
-class TestExtDirectProtocol(wsme.tests.protocol.ProtocolTestCase):
- protocol = 'extdirect'
- protocol_options = {
- 'namespace': 'MyNS.api',
- 'nsfolder': 'app'
- }
-
- def call(self, fname, _rt=None, _no_result_decode=False, _accept=None,
- **kw):
- path = fname.split('/')
- try:
- func, funcdef, args = self.root._lookup_function(path)
- arguments = funcdef.arguments
- except Exception:
- arguments = []
- if len(path) == 1:
- ns, action, fname = '', '', path[0]
- elif len(path) == 2:
- ns, action, fname = '', path[0], path[1]
- else:
- ns, action, fname = '.'.join(path[:-2]), path[-2], path[-1]
- print(kw)
-
- args = [
- dict(
- (arg.name, encode_arg(kw[arg.name]))
- for arg in arguments if arg.name in kw
- )
- ]
- print("args =", args)
- data = json.dumps({
- 'type': 'rpc',
- 'tid': 0,
- 'action': action,
- 'method': fname,
- 'data': args,
- })
- print(data)
- headers = {'Content-Type': 'application/json'}
- if _accept:
- headers['Accept'] = _accept
- res = self.app.post('/extdirect/router/%s' % ns, data, headers=headers,
- expect_errors=True)
-
- print(res.body)
-
- if _no_result_decode:
- return res
-
- data = json.loads(res.text)
- if data['type'] == 'rpc':
- r = data['result']
- return decode_result(r, _rt)
- elif data['type'] == 'exception':
- faultcode, faultstring = data['message'].split(': ', 1)
- debuginfo = data.get('where')
- raise wsme.tests.protocol.CallException(
- faultcode, faultstring, debuginfo)
-
- def test_api_alias(self):
- assert self.root._get_protocol('extdirect').api_alias == '/app/api.js'
-
- def test_get_api(self):
- res = self.app.get('/app/api.js')
- print(res.body)
- assert res.body
-
- def test_positional(self):
- self.root._get_protocol('extdirect').default_params_notation = \
- 'positional'
-
- data = json.dumps({
- 'type': 'rpc',
- 'tid': 0,
- 'action': 'misc',
- 'method': 'multiply',
- 'data': [2, 5],
- })
- headers = {'Content-Type': 'application/json'}
- res = self.app.post('/extdirect/router', data, headers=headers)
-
- print(res.body)
-
- data = json.loads(res.text)
- assert data['type'] == 'rpc'
- r = data['result']
- assert r == 10
-
- def test_batchcall(self):
- data = json.dumps([{
- 'type': 'rpc',
- 'tid': 1,
- 'action': 'argtypes',
- 'method': 'setdate',
- 'data': [{'value': '2011-04-06'}],
- }, {
- 'type': 'rpc',
- 'tid': 2,
- 'action': 'returntypes',
- 'method': 'getbytes',
- 'data': []
- }])
- print(data)
- headers = {'Content-Type': 'application/json'}
- res = self.app.post('/extdirect/router', data, headers=headers)
-
- print(res.body)
-
- rdata = json.loads(res.text)
-
- assert len(rdata) == 2
-
- assert rdata[0]['tid'] == 1
- assert rdata[0]['result'] == '2011-04-06'
- assert rdata[1]['tid'] == 2
- assert rdata[1]['result'] == 'astring'
-
- def test_form_call(self):
- params = {
- 'value[0].inner.aint': 54,
- 'value[1].inner.aint': 55,
- 'extType': 'rpc',
- 'extTID': 1,
- 'extAction': 'argtypes',
- 'extMethod': 'setnestedarray',
- }
-
- body = urlencode(params)
- r = self.app.post(
- '/extdirect/router',
- body,
- headers={'Content-Type': 'application/x-www-form-urlencoded'}
- )
- print(r)
-
- assert json.loads(r.text) == {
- "tid": "1",
- "action": "argtypes",
- "type": "rpc",
- "method": "setnestedarray",
- "result": [{
- "inner": {
- "aint": 54
- }
- }, {
- "inner": {
- "aint": 55
- }
- }]
- }
diff --git a/wsmeext/tests/test_soap.py b/wsmeext/tests/test_soap.py
deleted file mode 100644
index bc17696..0000000
--- a/wsmeext/tests/test_soap.py
+++ /dev/null
@@ -1,423 +0,0 @@
-import decimal
-import datetime
-import base64
-
-import six
-
-import wsme.tests.protocol
-
-try:
- import xml.etree.ElementTree as et
-except ImportError:
- import cElementTree as et # noqa
-
-import suds.cache
-import suds.client
-import suds.transport
-
-import wsme.utils
-
-
-class XDecimal(suds.xsd.sxbuiltin.XBuiltin):
- def translate(self, value, topython=True):
- if topython:
- if isinstance(value, six.string_types) and len(value):
- return decimal.Decimal(value)
- else:
- if isinstance(value, (decimal.Decimal, int, float)):
- return str(value)
- return value
-
-
-suds.xsd.sxbuiltin.Factory.tags['decimal'] = XDecimal
-
-
-class WebtestSudsTransport(suds.transport.Transport):
- def __init__(self, app):
- suds.transport.Transport.__init__(self)
- self.app = app
-
- def open(self, request):
- res = self.app.get(request.url, headers=request.headers)
- return six.BytesIO(res.body)
-
- def send(self, request):
- res = self.app.post(
- request.url,
- request.message,
- headers=dict((
- (key, str(value)) for key, value in request.headers.items()
- )),
- expect_errors=True
- )
- return suds.transport.Reply(
- res.status_int,
- dict(res.headers),
- res.body
- )
-
-
-class SudsCache(suds.cache.Cache):
- def __init__(self):
- self.d = {}
-
- def get(self, id):
- return self.d.get(id)
-
- def getf(self, id):
- b = self.get(id)
- if b is not None:
- return six.StringIO(self.get(id))
-
- def put(self, id, bfr):
- self.d[id] = bfr
-
- def putf(self, id, fp):
- self.put(id, fp.read())
-
- def purge(self, id):
- try:
- del self.d[id]
- except KeyError:
- pass
-
- def clear(self, id):
- self.d = {}
-
-
-sudscache = SudsCache()
-
-tns = "http://foo.bar.baz/soap/"
-typenamespace = "http://foo.bar.baz/types/"
-
-soapenv_ns = 'http://schemas.xmlsoap.org/soap/envelope/'
-xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance'
-body_qn = '{%s}Body' % soapenv_ns
-fault_qn = '{%s}Fault' % soapenv_ns
-faultcode_qn = '{%s}faultcode' % soapenv_ns
-faultstring_qn = '{%s}faultstring' % soapenv_ns
-faultdetail_qn = '{%s}detail' % soapenv_ns
-type_qn = '{%s}type' % xsi_ns
-nil_qn = '{%s}nil' % xsi_ns
-
-
-def build_soap_message(method, params=""):
- message = """<?xml version="1.0"?>
-<soap:Envelope
-xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
-xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
-
- <soap:Body xmlns="%(typenamespace)s">
- <%(method)s>
- %(params)s
- </%(method)s>
- </soap:Body>
-
-</soap:Envelope>
-""" % dict(method=method, params=params, typenamespace=typenamespace)
- return message
-
-
-python_types = {
- int: ('xs:int', str),
- float: ('xs:float', str),
- bool: ('xs:boolean', str),
- wsme.types.bytes: (
- 'xs:string',
- lambda x: x.decode('ascii') if isinstance(x, wsme.types.bytes) else x
- ),
- wsme.types.text: ('xs:string', wsme.types.text),
- wsme.types.binary: (
- 'xs:base64Binary',
- lambda x: base64.encodestring(x).decode('ascii')
- ),
- decimal.Decimal: ('xs:decimal', str),
- datetime.date: ('xs:date', datetime.date.isoformat),
- datetime.time: ('xs:time', datetime.time.isoformat),
- datetime.datetime: ('xs:dateTime', datetime.datetime.isoformat),
-}
-
-array_types = {
- wsme.types.bytes: "String_Array",
- wsme.types.text: "String_Array",
- int: "Int_Array",
- float: "Float_Array",
- bool: "Boolean_Array",
- datetime.datetime: "dateTime_Array"
-}
-
-if not six.PY3:
- array_types[long] = "Long_Array" # noqa
-
-
-def tosoap(tag, value):
- el = et.Element(tag)
- if isinstance(value, tuple):
- value, datatype = value
- else:
- datatype = type(value)
- if value is None:
- el.set('xsi:nil', 'true')
- return el
- if datatype in python_types:
- stype, conv = python_types[datatype]
- el.text = conv(value)
- el.set('xsi:type', stype)
- el.text = str(value)
- return el
-
-
-def tosuds(client, value):
- if value is None:
- return None
- if isinstance(value, tuple):
- value, datatype = value
- else:
- datatype = type(value)
- if value is None:
- return None
- if isinstance(datatype, list):
- if datatype[0] in array_types:
- tname = array_types[datatype[0]]
- else:
- tname = datatype[0].__name__ + '_Array'
- o = client.factory.create('types:' + tname)
- o.item = [tosuds(client, (item, datatype[0])) for item in value]
- return o
- elif datatype in python_types:
- return python_types[datatype][1](value)
- else:
- o = client.factory.create('types:' + datatype.__name__)
-
- for attr in datatype._wsme_attributes:
- if attr.name in value:
- setattr(
- o, attr.name,
- tosuds(client, (value[attr.name], attr.datatype))
- )
- return o
-
-
-def read_bool(value):
- return value == 'true'
-
-
-soap_types = {
- 'xs:string': wsme.types.text,
- 'xs:int': int,
- 'xs:long': int if six.PY3 else long, # noqa
- 'xs:float': float,
- 'xs:decimal': decimal.Decimal,
- 'xs:boolean': read_bool,
- 'xs:date': wsme.utils.parse_isodate,
- 'xs:time': wsme.utils.parse_isotime,
- 'xs:dateTime': wsme.utils.parse_isodatetime,
- 'xs:base64Binary': base64.decodestring,
-}
-
-
-def fromsoap(el):
- if el.get(nil_qn) == 'true':
- return None
- t = el.get(type_qn)
- if t == 'xs:string':
- return wsme.types.text(el.text if el.text else '')
- if t in soap_types:
- return soap_types[t](el.text)
- elif t and t.endswith('_Array'):
- return [fromsoap(i) for i in el]
- else:
- d = {}
- for child in el:
- name = child.tag
- assert name.startswith('{%s}' % typenamespace), name
- name = name[len(typenamespace) + 2:]
- d[name] = fromsoap(child)
- return d
-
-
-def tobytes(value):
- if isinstance(value, wsme.types.text):
- value = value.encode()
- return value
-
-
-def tobin(value):
- value = base64.decodestring(value.encode())
- return value
-
-
-fromsuds_types = {
- wsme.types.binary: tobin,
- wsme.types.bytes: tobytes,
- decimal.Decimal: decimal.Decimal,
-}
-
-
-def fromsuds(dt, value):
- if value is None:
- return None
- if isinstance(dt, list):
- return [fromsuds(dt[0], item) for item in value.item]
- if wsme.types.isarray(dt):
- return [fromsuds(dt.item_type, item) for item in value.item]
- if wsme.types.isusertype(dt) and dt not in fromsuds_types:
- dt = dt.basetype
- if dt in fromsuds_types:
- print(dt, value)
- value = fromsuds_types[dt](value)
- print(value)
- return value
- if wsme.types.iscomplex(dt):
- d = {}
- for attrdef in dt._wsme_attributes:
- if not hasattr(value, attrdef.name):
- continue
- d[attrdef.name] = fromsuds(
- attrdef.datatype, getattr(value, attrdef.name)
- )
- return d
- return value
-
-
-class TestSOAP(wsme.tests.protocol.ProtocolTestCase):
- protocol = 'soap'
- protocol_options = dict(tns=tns, typenamespace=typenamespace)
- ws_path = '/'
- _sudsclient = None
-
- def setUp(self):
- wsme.tests.protocol.ProtocolTestCase.setUp(self)
-
- def test_simple_call(self):
- message = build_soap_message('touch')
- print(message)
- res = self.app.post(
- self.ws_path,
- message,
- headers={"Content-Type": "application/soap+xml; charset=utf-8"},
- expect_errors=True
- )
- print(res.body)
- assert res.status.startswith('200')
-
- def call(self, fpath, _rt=None, _accept=None, _no_result_decode=False,
- **kw):
-
- if _no_result_decode or _accept or self._testMethodName in (
- 'test_missing_argument', 'test_invalid_path', 'test_settext_empty',
- 'test_settext_none'
- ):
- return self.raw_call(fpath, _rt, _accept, _no_result_decode, **kw)
-
- path = fpath.strip('/').split('/')
- methodname = ''.join([path[0]] + [i.capitalize() for i in path[1:]])
-
- m = getattr(self.sudsclient.service, methodname)
- kw = dict((
- (key, tosuds(self.sudsclient, value)) for key, value in kw.items()
- ))
- print(kw)
- try:
- return fromsuds(_rt, m(**kw))
- except suds.WebFault as exc:
- raise wsme.tests.protocol.CallException(
- exc.fault.faultcode,
- exc.fault.faultstring,
- getattr(exc.fault, 'detail', None) or None
- )
-
- def raw_call(self, fpath, _rt=None, _accept=None, _no_result_decode=False,
- **kw):
- path = fpath.strip('/').split('/')
- methodname = ''.join([path[0]] + [i.capitalize() for i in path[1:]])
- # get the actual definition so we can build the adequate request
- if kw:
- el = et.Element('parameters')
- for key, value in kw.items():
- el.append(tosoap(key, value))
-
- params = six.b("\n").join(et.tostring(el) for el in el)
- else:
- params = ""
- methodname = ''.join([path[0]] + [i.capitalize() for i in path[1:]])
- message = build_soap_message(methodname, params)
- print(message)
- headers = {"Content-Type": "application/soap+xml; charset=utf-8"}
- if _accept is not None:
- headers['Accept'] = _accept
- res = self.app.post(
- self.ws_path,
- message,
- headers=headers,
- expect_errors=True
- )
- print("Status: ", res.status, "Received:", res.body)
-
- if _no_result_decode:
- return res
-
- el = et.fromstring(res.body)
- body = el.find(body_qn)
- print(body)
-
- if res.status_int == 200:
- response_tag = '{%s}%sResponse' % (typenamespace, methodname)
- r = body.find(response_tag)
- result = r.find('{%s}result' % typenamespace)
- print("Result element: ", result)
- return fromsoap(result)
- elif res.status_int == 400:
- fault = body.find(fault_qn)
- raise wsme.tests.protocol.CallException(
- fault.find(faultcode_qn).text,
- fault.find(faultstring_qn).text,
- "")
-
- elif res.status_int == 500:
- fault = body.find(fault_qn)
- raise wsme.tests.protocol.CallException(
- fault.find(faultcode_qn).text,
- fault.find(faultstring_qn).text,
- fault.find(faultdetail_qn) is not None and
- fault.find(faultdetail_qn).text or None)
-
- @property
- def sudsclient(self):
- if self._sudsclient is None:
- self._sudsclient = suds.client.Client(
- self.ws_path + 'api.wsdl',
- transport=WebtestSudsTransport(self.app),
- cache=sudscache
- )
- return self._sudsclient
-
- def test_wsdl(self):
- c = self.sudsclient
- assert c.wsdl.tns[1] == tns, c.wsdl.tns
-
- sd = c.sd[0]
-
- assert len(sd.ports) == 1
- port, methods = sd.ports[0]
- self.assertEqual(len(methods), 51)
-
- methods = dict(methods)
-
- assert 'returntypesGettext' in methods
- print(methods)
-
- assert methods['argtypesSettime'][0][0] == 'value'
-
- def test_return_nesteddict(self):
- pass
-
- def test_setnesteddict(self):
- pass
-
- def test_return_objectdictattribute(self):
- pass
-
- def test_setnested_nullobj(self):
- pass # TODO write a soap adapted version of this test.
diff --git a/wsmeext/tests/test_sqlalchemy_controllers.py b/wsmeext/tests/test_sqlalchemy_controllers.py
deleted file mode 100644
index 1956788..0000000
--- a/wsmeext/tests/test_sqlalchemy_controllers.py
+++ /dev/null
@@ -1,223 +0,0 @@
-import datetime
-
-try:
- import json
-except ImportError:
- import simplejson as json
-
-from webtest import TestApp
-
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import Column, Integer, Unicode, Date, ForeignKey
-from sqlalchemy.orm import relation
-
-from sqlalchemy import create_engine
-from sqlalchemy.orm import sessionmaker, scoped_session
-
-from wsme import WSRoot
-import wsme.types
-
-from wsmeext.sqlalchemy.types import generate_types
-from wsmeext.sqlalchemy.controllers import CRUDController
-
-from six import u
-
-engine = create_engine('sqlite:///')
-DBSession = scoped_session(sessionmaker(autocommit=False, autoflush=False,
- bind=engine))
-DBBase = declarative_base()
-
-registry = wsme.types.Registry()
-
-
-class DBPerson(DBBase):
- __tablename__ = 'person'
-
- id = Column(Integer, primary_key=True)
- name = Column(Unicode(50))
- birthdate = Column(Date)
-
- addresses = relation('DBAddress')
-
-
-class DBAddress(DBBase):
- __tablename__ = 'address'
-
- id = Column(Integer, primary_key=True)
-
- _person_id = Column('person_id', ForeignKey(DBPerson.id))
-
- street = Column(Unicode(50))
- city = Column(Unicode(50))
-
- person = relation(DBPerson)
-
-
-globals().update(
- generate_types(DBPerson, DBAddress, makename=lambda s: s[2:],
- registry=registry))
-
-
-class PersonController(CRUDController):
- __saclass__ = DBPerson
- __dbsession__ = DBSession
- __registry__ = registry
-
-
-class AddressController(CRUDController):
- __saclass__ = DBAddress
- __dbsession__ = DBSession
- __registry__ = registry
-
-
-class Root(WSRoot):
- __registry__ = registry
-
- person = PersonController()
- address = AddressController()
-
-
-class TestCRUDController():
- def setUp(self):
- DBBase.metadata.create_all(DBSession.bind)
-
- self.root = Root()
- self.root.getapi()
- self.root.addprotocol('restjson')
-
- self.app = TestApp(self.root.wsgiapp())
-
- def tearDown(self):
- DBBase.metadata.drop_all(DBSession.bind)
-
- def test_create(self):
- data = dict(data=dict(
- name=u('Pierre-Joseph'),
- birthdate=u('1809-01-15')
- ))
- r = self.app.post('/person/create', json.dumps(data),
- headers={'Content-Type': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_PUT(self):
- data = dict(data=dict(
- name=u('Pierre-Joseph'),
- birthdate=u('1809-01-15')
- ))
- r = self.app.put('/person', json.dumps(data),
- headers={'Content-Type': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_read(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- r = self.app.post('/person/read', '{"ref": {"id": %s}}' % pid,
- headers={'Content-Type': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_GET(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- r = self.app.get('/person?ref.id=%s' % pid,
- headers={'Accept': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_GET_bad_accept(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- r = self.app.get('/person?ref.id=%s' % pid,
- headers={'Accept': 'text/plain'},
- status=406)
- assert r.text == ("Unacceptable Accept type: text/plain not in "
- "['application/json', 'text/javascript', "
- "'application/javascript', 'text/xml']")
-
- def test_update(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- data = {
- "id": pid,
- "name": u('Pierre-Joseph Proudon')
- }
- r = self.app.post('/person/update', json.dumps(dict(data=data)),
- headers={'Content-Type': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph Proudon')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_POST(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- data = {
- "id": pid,
- "name": u('Pierre-Joseph Proudon')
- }
- r = self.app.post('/person', json.dumps(dict(data=data)),
- headers={'Content-Type': 'application/json'})
- r = json.loads(r.text)
- print(r)
- assert r['name'] == u('Pierre-Joseph Proudon')
- assert r['birthdate'] == u('1809-01-15')
-
- def test_delete(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- r = self.app.post('/person/delete', json.dumps(
- dict(ref=dict(id=pid))),
- headers={
- 'Content-Type': 'application/json'
- })
- print(r)
- assert DBSession.query(DBPerson).get(pid) is None
-
- def test_DELETE(self):
- p = DBPerson(
- name=u('Pierre-Joseph'),
- birthdate=datetime.date(1809, 1, 15))
- DBSession.add(p)
- DBSession.flush()
- pid = p.id
- r = self.app.delete('/person?ref.id=%s' % pid,
- headers={'Content-Type': 'application/json'})
- print(r)
- assert DBSession.query(DBPerson).get(pid) is None
-
- def test_nothing(self):
- pass
diff --git a/wsmeext/tests/test_sqlalchemy_types.py b/wsmeext/tests/test_sqlalchemy_types.py
deleted file mode 100644
index 8512015..0000000
--- a/wsmeext/tests/test_sqlalchemy_types.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import datetime
-
-import wsmeext.sqlalchemy.types
-
-from wsme.types import text, Unset, isarray
-
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy import Column, Integer, String, Date, ForeignKey
-from sqlalchemy.orm import relation
-
-from six import u
-
-SABase = declarative_base()
-
-
-class SomeClass(SABase):
- __tablename__ = 'some_table'
- id = Column(Integer, primary_key=True)
- name = Column(String(50))
-
- adate = Column(Date)
-
-
-def test_complextype():
- class AType(wsmeext.sqlalchemy.types.Base):
- __saclass__ = SomeClass
-
- assert AType.id.datatype is int
- assert AType.name.datatype is text
- assert AType.adate.datatype is datetime.date
-
- a = AType()
- s = SomeClass(name=u('aname'), adate=datetime.date(2012, 6, 26))
- assert s.name == u('aname')
-
- a.from_instance(s)
- assert a.name == u('aname')
- assert a.adate == datetime.date(2012, 6, 26)
-
- a.name = u('test')
- del a.adate
- assert a.adate is Unset
-
- a.to_instance(s)
- assert s.name == u('test')
- assert s.adate == datetime.date(2012, 6, 26)
-
-
-def test_generate():
- class A(SABase):
- __tablename__ = 'a'
- id = Column(Integer, primary_key=True)
- name = Column(String(50))
-
- _b_id = Column(ForeignKey('b.id'))
-
- b = relation('B')
-
- class B(SABase):
- __tablename__ = 'b'
- id = Column(Integer, primary_key=True)
- name = Column(String(50))
-
- alist = relation(A)
-
- newtypes = wsmeext.sqlalchemy.types.generate_types(A, B)
-
- assert newtypes['A'].id.datatype is int
- assert newtypes['A'].b.datatype is newtypes['B']
- assert newtypes['B'].id.datatype is int
- assert isarray(newtypes['B'].alist.datatype)
- assert newtypes['B'].alist.datatype.item_type is newtypes['A']
diff --git a/wsmeext/tg1.py b/wsmeext/tg1.py
deleted file mode 100644
index b978166..0000000
--- a/wsmeext/tg1.py
+++ /dev/null
@@ -1,173 +0,0 @@
-try:
- import json
-except ImportError:
- import simplejson as json # noqa
-
-import functools
-import sys
-
-import cherrypy
-import webob
-from turbogears import expose, util
-import turbogears.view
-
-from wsme.rest import validate as wsvalidate
-import wsme.api
-import wsme.rest
-import wsme.rest.args
-import wsme.rest.json
-from wsme.utils import is_valid_code
-
-import inspect
-
-APIPATH_MAXLEN = 50
-
-__all__ = ['wsexpose', 'wsvalidate']
-
-
-def wsexpose(*args, **kwargs):
- tg_json_expose = expose(
- 'wsmejson:',
- accept_format='application/json',
- content_type='application/json',
- tg_format='json'
- )
- tg_altjson_expose = expose(
- 'wsmejson:',
- accept_format='text/javascript',
- content_type='application/json'
- )
- tg_xml_expose = expose(
- 'wsmexml:',
- accept_format='text/xml',
- content_type='text/xml',
- tg_format='xml'
- )
- sig = wsme.signature(*args, **kwargs)
-
- def decorate(f):
- sig(f)
- funcdef = wsme.api.FunctionDefinition.get(f)
-
- @functools.wraps(f)
- def callfunction(self, *args, **kwargs):
- args, kwargs = wsme.rest.args.get_args(
- funcdef, args, kwargs,
- cherrypy.request.params, None,
- cherrypy.request.body,
- cherrypy.request.headers['Content-Type']
- )
- if funcdef.pass_request:
- kwargs[funcdef.pass_request] = cherrypy.request
- try:
- result = f(self, *args, **kwargs)
- except Exception:
- try:
- exception_info = sys.exc_info()
- orig_exception = exception_info[1]
- if isinstance(orig_exception, cherrypy.HTTPError):
- orig_code = getattr(orig_exception, 'status', None)
- else:
- orig_code = getattr(orig_exception, 'code', None)
- data = wsme.api.format_exception(exception_info)
- finally:
- del exception_info
-
- cherrypy.response.status = 500
- if data['faultcode'] == 'client':
- cherrypy.response.status = 400
- elif orig_code and is_valid_code(orig_code):
- cherrypy.response.status = orig_code
-
- accept = cherrypy.request.headers.get('Accept', "").lower()
- accept = util.simplify_http_accept_header(accept)
-
- decorators = {'text/xml': wsme.rest.xml.encode_error}
- return decorators.get(
- accept,
- wsme.rest.json.encode_error
- )(None, data)
-
- return dict(
- datatype=funcdef.return_type,
- result=result
- )
-
- callfunction = tg_xml_expose(callfunction)
- callfunction = tg_altjson_expose(callfunction)
- callfunction = tg_json_expose(callfunction)
- callfunction._wsme_original_function = f
- return callfunction
-
- return decorate
-
-
-class AutoJSONTemplate(object):
- def __init__(self, extra_vars_func=None, options=None):
- pass
-
- def render(self, info, format="json", fragment=False, template=None):
- "Renders the template to a string using the provided info."
- return wsme.rest.json.encode_result(
- info['result'], info['datatype']
- )
-
- def get_content_type(self, user_agent):
- return "application/json"
-
-
-class AutoXMLTemplate(object):
- def __init__(self, extra_vars_func=None, options=None):
- pass
-
- def render(self, info, format="json", fragment=False, template=None):
- "Renders the template to a string using the provided info."
- return wsme.rest.xml.encode_result(
- info['result'], info['datatype']
- )
-
- def get_content_type(self, user_agent):
- return "text/xml"
-
-
-turbogears.view.engines['wsmejson'] = AutoJSONTemplate(turbogears.view.stdvars)
-turbogears.view.engines['wsmexml'] = AutoXMLTemplate(turbogears.view.stdvars)
-
-
-class Controller(object):
- def __init__(self, wsroot):
- self._wsroot = wsroot
-
- @expose()
- def default(self, *args, **kw):
- req = webob.Request(cherrypy.request.wsgi_environ)
- res = self._wsroot._handle_request(req)
- cherrypy.response.header_list = res.headerlist
- cherrypy.response.status = res.status
- return res.body
-
-
-def _scan_api(controller, path=[], objects=[]):
- """
- Recursively iterate a controller api entries.
- """
- for name in dir(controller):
- if name.startswith('_'):
- continue
- a = getattr(controller, name)
- if a in objects:
- continue
- if inspect.ismethod(a):
- if wsme.api.iswsmefunction(a):
- yield path + [name], a._wsme_original_function, [controller]
- elif inspect.isclass(a):
- continue
- else:
- if len(path) > APIPATH_MAXLEN:
- raise ValueError("Path is too long: " + str(path))
- for i in _scan_api(a, path + [name], objects + [a]):
- yield i
-
-
-def scan_api(root=None):
- return _scan_api(cherrypy.root)
diff --git a/wsmeext/tg11.py b/wsmeext/tg11.py
deleted file mode 100644
index 80ec50c..0000000
--- a/wsmeext/tg11.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from turbogears import config
-import cherrypy
-from cherrypy.filters.basefilter import BaseFilter
-from turbogears.startup import call_on_startup, call_on_shutdown
-from wsmeext.tg1 import wsexpose, wsvalidate
-import wsmeext.tg1
-
-__all__ = ['adapt', 'wsexpose', 'wsvalidate']
-
-
-class WSMECherrypyFilter(BaseFilter):
- def __init__(self, controller):
- self.controller = controller
- self.webpath = None
-
- def on_start_resource(self):
- path = cherrypy.request.path
- if path.startswith(self.controller._wsroot._webpath):
- cherrypy.request.processRequestBody = False
-
-
-def adapt(wsroot):
- wsroot._scan_api = wsmeext.tg1.scan_api
- controller = wsmeext.tg1.Controller(wsroot)
- filter_ = WSMECherrypyFilter(controller)
-
- def install_filter():
- filter_.webpath = config.get('server.webpath') or ''
- controller._wsroot._webpath = \
- filter_.webpath + controller._wsroot._webpath
- cherrypy.root._cp_filters.append(filter_)
-
- def uninstall_filter():
- cherrypy.root._cp_filters.remove(filter_)
- controller._wsroot._webpath = \
- controller._wsroot._webpath[len(filter_.webpath):]
-
- call_on_startup.append(install_filter)
- call_on_shutdown.insert(0, uninstall_filter)
- return controller
diff --git a/wsmeext/tg15.py b/wsmeext/tg15.py
deleted file mode 100644
index 4b8a9b1..0000000
--- a/wsmeext/tg15.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import cherrypy
-
-from wsmeext.tg1 import wsexpose, wsvalidate
-import wsmeext.tg1
-
-
-__all__ = ['adapt', 'wsexpose', 'wsvalidate']
-
-
-def scan_api(root=None):
- for baseurl, instance in cherrypy.tree.apps.items():
- path = [token for token in baseurl.split('/') if token]
- for i in wsmeext.tg1._scan_api(instance.root, path):
- yield i
-
-
-def adapt(wsroot):
- wsroot._scan_api = scan_api
- controller = wsmeext.tg1.Controller(wsroot)
- return controller