Documented most of the api
This commit is contained in:
parent
f3e8a2435b
commit
22f9ea3d67
33
doc/api.rst
33
doc/api.rst
|
@ -1,8 +1,39 @@
|
|||
API
|
||||
===
|
||||
|
||||
.. autoclass:: wsme.controller.WSRoot
|
||||
Public API
|
||||
----------
|
||||
|
||||
:mod:`wsme` -- Essentials
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. module:: wsme
|
||||
|
||||
.. autoclass:: wsme.expose
|
||||
.. autoclass:: wsme.validate
|
||||
|
||||
.. autoclass:: wsme.wsproperty
|
||||
.. autoclass:: wsme.wsattr
|
||||
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
:mod:`wsme.types` -- Types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automodule:: wsme.types
|
||||
:members: register_type
|
||||
|
||||
:mod:`wsme.controller` -- Controller
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automodule:: wsme.controller
|
||||
:members: scan_api, FunctionArgument, FunctionDefinition, WSRoot
|
||||
|
||||
:mod:`wsme.rest` -- REST protocol commons
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automodule:: wsme.rest
|
||||
:members:
|
||||
|
||||
|
|
|
@ -4,14 +4,16 @@ Changes
|
|||
next
|
||||
----
|
||||
|
||||
* 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`).
|
||||
* 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.
|
||||
|
||||
0.1.0a2 (2011-10-07)
|
||||
--------------------
|
||||
|
||||
* Added support for arrays in all the protocols
|
||||
* Added support for arrays in all the protocols
|
||||
|
||||
0.1.0a1 (2011-10-04)
|
||||
--------------------
|
||||
|
|
|
@ -25,7 +25,7 @@ sys.path.insert(0, os.path.abspath('..'))
|
|||
|
||||
# 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']
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
@ -92,7 +92,7 @@ pygments_style = 'sphinx'
|
|||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
html_theme = 'agogo'
|
||||
|
||||
# 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
|
||||
|
@ -215,3 +215,6 @@ man_pages = [
|
|||
('index', 'webservicesmadeeasy', u'Web Services Made Easy Documentation',
|
||||
[u'Christophe de Vienne'], 1)
|
||||
]
|
||||
|
||||
|
||||
autodoc_member_order = 'bysource'
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
Protocols
|
||||
=========
|
||||
|
||||
REST+Json
|
||||
---------
|
||||
:mod:`wsme.protocols.restjson` -- REST+Json
|
||||
-------------------------------------------
|
||||
|
||||
.. autoclass:: wsme.protocols.restjson.RestJsonProtocol
|
||||
.. automodule:: wsme.protocols.restjson
|
||||
:members:
|
||||
|
||||
|
||||
REST+XML
|
||||
--------
|
||||
:mod:`wsme.protocols.restxml` -- REST+XML
|
||||
-----------------------------------------
|
||||
|
||||
.. autoclass:: wsme.protocols.restxml.RestXmlProtocol
|
||||
.. automodule:: wsme.protocols.restxml
|
||||
:members:
|
||||
|
||||
|
||||
SOAP
|
||||
----
|
||||
:mod:`wsme.protocols.soap` -- SOAP
|
||||
----------------------------------
|
||||
|
||||
.. autoclass:: wsme.protocols.soap.SoapProtocol
|
||||
.. automodule:: wsme.protocols.soap
|
||||
:members:
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@ html_body = """
|
|||
|
||||
|
||||
def scan_api(controller, path=[]):
|
||||
"""
|
||||
Recursively iterate a controller api entries, while setting
|
||||
their :attr:`FunctionDefinition.path`.
|
||||
"""
|
||||
for name in dir(controller):
|
||||
if name.startswith('_'):
|
||||
continue
|
||||
|
@ -50,25 +54,56 @@ def scan_api(controller, path=[]):
|
|||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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 = []
|
||||
|
||||
#: True if this function is exposed by a protocol and not in
|
||||
#: the api tree, which means it is not part of the api.
|
||||
self.protocol_specific = False
|
||||
|
||||
#: Override the contenttype of the returned value.
|
||||
#: Make sense only with :attr:`protocol_specific` functions.
|
||||
self.contenttype = None
|
||||
|
||||
#: Path of the function in the api tree.
|
||||
self.path = None
|
||||
|
||||
@classmethod
|
||||
def get(cls, func):
|
||||
"""
|
||||
Returns the :class:`FunctionDefinition` of a method.
|
||||
"""
|
||||
fd = getattr(func, '_wsme_definition', None)
|
||||
if fd is None:
|
||||
fd = FunctionDefinition(func)
|
||||
|
@ -76,6 +111,9 @@ class FunctionDefinition(object):
|
|||
return fd
|
||||
|
||||
def get_arg(self, name):
|
||||
"""
|
||||
Returns a :class:`FunctionArgument` from its name
|
||||
"""
|
||||
for arg in self.arguments:
|
||||
if arg.name == name:
|
||||
return arg
|
||||
|
@ -88,6 +126,18 @@ def register_protocol(protocol):
|
|||
|
||||
|
||||
class expose(object):
|
||||
"""
|
||||
Decorator that expose a function.
|
||||
|
||||
:param return_type: Return type of the function
|
||||
|
||||
Example::
|
||||
|
||||
class MyController(object):
|
||||
@expose(int)
|
||||
def getint(self):
|
||||
return 1
|
||||
"""
|
||||
def __init__(self, return_type=None):
|
||||
self.return_type = return_type
|
||||
register_type(return_type)
|
||||
|
@ -113,8 +163,20 @@ class pexpose(object):
|
|||
|
||||
|
||||
class validate(object):
|
||||
def __init__(self, *args, **kw):
|
||||
self.param_types = args
|
||||
"""
|
||||
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):
|
||||
fd = FunctionDefinition.get(func)
|
||||
|
@ -133,6 +195,12 @@ class validate(object):
|
|||
|
||||
|
||||
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.
|
||||
"""
|
||||
def __init__(self, protocols=[], webpath=''):
|
||||
self._debug = True
|
||||
self._webpath = webpath
|
||||
|
@ -143,12 +211,23 @@ class WSRoot(object):
|
|||
self._api = None
|
||||
|
||||
def addprotocol(self, protocol):
|
||||
"""
|
||||
Enable a new protocol on the controller.
|
||||
|
||||
:param protocol: A registered protocol name or an instance
|
||||
of a protocol.
|
||||
"""
|
||||
if isinstance(protocol, str):
|
||||
protocol = registered_protocols[protocol]()
|
||||
self.protocols[protocol.name] = protocol
|
||||
protocol.root = weakref.proxy(self)
|
||||
|
||||
def getapi(self):
|
||||
"""
|
||||
Returns the api description.
|
||||
|
||||
:rtype: list of :class:`FunctionDefinition`
|
||||
"""
|
||||
if self._api is None:
|
||||
self._api = [i for i in scan_api(self)]
|
||||
return self._api
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
"""
|
||||
REST+Json protocol implementation.
|
||||
"""
|
||||
import base64
|
||||
import datetime
|
||||
import decimal
|
||||
|
@ -16,6 +19,20 @@ except ImportError:
|
|||
|
||||
@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 wsme.types.iscomplex(datatype):
|
||||
d = dict()
|
||||
for name, attr in wsme.types.list_attributes(datatype):
|
||||
|
@ -56,6 +73,21 @@ def datetime_tojson(datatype, value):
|
|||
|
||||
@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):
|
||||
|
@ -98,6 +130,14 @@ def binary_fromjson(datatype, value):
|
|||
|
||||
|
||||
class RestJsonProtocol(RestProtocol):
|
||||
"""
|
||||
REST+Json protocol.
|
||||
|
||||
.. autoattribute:: name
|
||||
.. autoattribute:: dataformat
|
||||
.. autoattribute:: content_types
|
||||
"""
|
||||
|
||||
name = 'REST+Json'
|
||||
dataformat = 'json'
|
||||
content_types = [
|
||||
|
|
|
@ -20,6 +20,25 @@ time_re = re.compile(r'(?P<h>[0-2][0-9]):(?P<m>[0-5][0-9]):(?P<s>[0-6][0-9])')
|
|||
|
||||
@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')
|
||||
|
@ -34,6 +53,23 @@ def toxml(datatype, key, value):
|
|||
|
||||
@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.iscomplex(datatype):
|
||||
|
@ -130,6 +166,13 @@ def binary_fromxml(datatype, element):
|
|||
|
||||
|
||||
class RestXmlProtocol(RestProtocol):
|
||||
"""
|
||||
REST+XML protocol.
|
||||
|
||||
.. autoattribute:: name
|
||||
.. autoattribute:: dataformat
|
||||
.. autoattribute:: content_types
|
||||
"""
|
||||
name = 'REST+XML'
|
||||
dataformat = 'xml'
|
||||
content_types = ['text/xml']
|
||||
|
|
|
@ -95,7 +95,22 @@ def make_soap_element(datatype, tag, value):
|
|||
@generic
|
||||
def tosoap(datatype, tag, value):
|
||||
"""Converts a value into xml Element objects for inclusion in the SOAP
|
||||
response output"""
|
||||
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] = 'xsd:MySpecialType'
|
||||
|
||||
@tosoap.when_object(MySpecialType)
|
||||
def myspecialtype_tosoap(datatype, tag, value):
|
||||
return make_soap_element(datatype, tag, str(value))
|
||||
"""
|
||||
return make_soap_element(datatype, tag, value)
|
||||
|
||||
|
||||
|
@ -130,6 +145,12 @@ def None_tosoap(datatype, tag, value):
|
|||
|
||||
@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
|
||||
soaptype = el.get(type_qn)
|
||||
|
@ -188,6 +209,13 @@ def binary_fromsoap(datatype, el, ns):
|
|||
|
||||
|
||||
class SoapProtocol(object):
|
||||
"""
|
||||
REST+XML protocol.
|
||||
|
||||
.. autoattribute:: name
|
||||
.. autoattribute:: dataformat
|
||||
.. autoattribute:: content_types
|
||||
"""
|
||||
name = 'SOAP'
|
||||
content_types = ['application/soap+xml']
|
||||
|
||||
|
|
|
@ -19,6 +19,20 @@ def iscomplex(datatype):
|
|||
|
||||
|
||||
class wsproperty(property):
|
||||
"""
|
||||
A specialised :class:`property` to define typed-property on complex types.
|
||||
Example::
|
||||
|
||||
class MyComplexType(object):
|
||||
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):
|
||||
property.__init__(self, fget, fset)
|
||||
|
@ -27,6 +41,16 @@ class wsproperty(property):
|
|||
|
||||
|
||||
class wsattr(object):
|
||||
"""
|
||||
Complex type attribute definition when the datatype only is not
|
||||
enough.
|
||||
Example::
|
||||
|
||||
class MyComplexType(object):
|
||||
optionalvalue = int
|
||||
mandatoryvalue = wsattr(int, mandatory=True)
|
||||
|
||||
"""
|
||||
def __init__(self, datatype, mandatory=False):
|
||||
self.datatype = datatype
|
||||
self.mandatory = mandatory
|
||||
|
@ -100,6 +124,14 @@ def inspect_class(class_):
|
|||
|
||||
|
||||
def register_type(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 \
|
||||
hasattr(class_, '_wsme_attributes'):
|
||||
|
@ -118,6 +150,9 @@ def register_type(class_):
|
|||
|
||||
|
||||
def list_attributes(class_):
|
||||
"""
|
||||
Returns a list of a complex type attributes.
|
||||
"""
|
||||
if not hasattr(class_, '_wsme_attributes'):
|
||||
register_type(class_)
|
||||
return class_._wsme_attributes
|
||||
|
|
Loading…
Reference in New Issue