[ADD] openerp.http reference doc

* fix some docstrings so they can be autodoc'd
* intersphinx mapping (and links to) werkzeug and python
This commit is contained in:
Xavier Morel 2014-09-01 11:54:34 +02:00
parent afcb89ab3a
commit cccd3c888f
4 changed files with 206 additions and 89 deletions

View File

@ -18,7 +18,13 @@ needs_sphinx = '1.1'
# 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.todo', 'sphinx.ext.autodoc', 'odoodoc', 'patchqueue']
extensions = [
'sphinx.ext.todo',
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'odoodoc',
'patchqueue'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -152,3 +158,7 @@ html_sidebars = {
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
intersphinx_mapping = {
'python': ('https://docs.python.org/2/', None),
'werkzeug': ('http://werkzeug.pocoo.org/docs/0.9/', None),
}

View File

@ -12,16 +12,72 @@ Routing
Request
=======
The request object is automatically set on :data:`openerp.http.request` at
the start of the request
.. autoclass:: openerp.http.WebRequest
:members:
:member-order: bysource
.. autoclass:: openerp.http.HttpRequest
:members:
.. autoclass:: openerp.http.JsonRequest
:members:
Response
========
JSON-RPC
========
.. autoclass:: openerp.http.Response
:members:
:member-order: bysource
.. maybe set this to document all the fine methods on Werkzeug's Response
object? (it works)
:inherited-members:
.. _reference/http/controllers:
Extension: controllers
======================
Controllers
===========
.. this should be about inheritance/extension, do web controllers still do
anything else nowadays?
Controllers need to provide extensibility, much like
:class:`~openerp.models.Model`, but can't use the same mechanism as the
pre-requisites (a database with loaded modules) may not be available yet (e.g.
no database created, or no database selected).
Controllers thus provide their own extension mechanism, separate from that of
models:
Controllers are created by :ref:`inheriting <python:tut-inheritance>` from
.. autoclass:: openerp.http.Controller
and defining methods decorated with :func:`~openerp.http.route`::
class MyController(openerp.http.Controller):
@route('/some_url', auth='public')
def handler(self):
return stuff()
To *override* a controller, :ref:`inherit <python:tut-inheritance>` from its
class and override relevant methods::
class Extension(MyController):
@route()
def handler(self):
do_before()
return super(Extension, self).handler()
* decorating with :func:`~openerp.http.route` is necessary to keep the method
(and route) visible: if the method is redefined without decorating, it
will be "unpublished"
* the decorators of all methods are combined, if the overriding method's
decorator has no argument all previous ones will be kept, any provided
argument will override previously defined ones e.g.::
class Restrict(MyController):
@route(auth='user')
def handler(self):
return super(Restrict, self).handler()
will change ``/some_url`` from public authentication to user (requiring a
log-in)

View File

@ -142,7 +142,7 @@ def redirect_with_hash(url, code=303):
return "<html><head><script>window.location = '%s' + location.hash;</script></head></html>" % url
class WebRequest(object):
""" Parent class for all OpenERP Web request types, mostly deals with
""" Parent class for all Odoo Web request types, mostly deals with
initialization and setup of the request object (the dispatching itself has
to be handled by the subclasses)
@ -154,60 +154,20 @@ class WebRequest(object):
the original :class:`werkzeug.wrappers.Request` object provided to the
request
.. attribute:: httpsession
.. deprecated:: 8.0
Use :attr:`session` instead.
.. attribute:: params
:class:`~collections.Mapping` of request parameters, not generally
useful as they're provided directly to the handler method as keyword
arguments
.. attribute:: session_id
opaque identifier for the :class:`OpenERPSession` instance of
the current request
.. attribute:: session
a :class:`OpenERPSession` holding the HTTP session data for the
current http session
.. attribute:: context
:class:`~collections.Mapping` of context values for the current
request
.. attribute:: db
``str``, the name of the database linked to the current request. Can
be ``None`` if the current request uses the ``none`` authentication
in ``web`` module's controllers.
.. attribute:: uid
``int``, the id of the user related to the current request. Can be
``None`` if the current request uses the ``none`` authentication.
.. attribute:: env
an :class:`openerp.api.Environment` bound to the current
request's ``cr``, ``uid`` and ``context``
"""
def __init__(self, httprequest):
self.httprequest = httprequest
self.httpresponse = None
self.httpsession = httprequest.session
self.session = httprequest.session
self.session_id = httprequest.session.sid
self.disable_db = False
self.uid = None
self.endpoint = None
self.auth_method = None
self._cr_cm = None
self._cr = None
# prevents transaction commit, use when you catch an exception during handling
@ -219,44 +179,49 @@ class WebRequest(object):
threading.current_thread().dbname = self.db
if self.session.uid:
threading.current_thread().uid = self.session.uid
self.context = dict(self.session.context)
self.lang = self.context["lang"]
@property
def registry(self):
"""
The registry to the database linked to this request. Can be ``None``
if the current request uses the ``none`` authentication.
"""
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
@property
def db(self):
"""
The database linked to this request. Can be ``None``
if the current request uses the ``none`` authentication.
"""
return self.session.db if not self.disable_db else None
@property
def cr(self):
"""
The cursor initialized for the current method call. If the current
request uses the ``none`` authentication trying to access this
property will raise an exception.
"""
# some magic to lazy create the cr
if not self._cr:
self._cr = self.registry.cursor()
return self._cr
@lazy_property
def env(self):
"""
The Environment bound to current request.
The :class:`~openerp.api.Environment` bound to current request.
"""
return openerp.api.Environment(self.cr, self.uid, self.context)
@lazy_property
def context(self):
"""
:class:`~collections.Mapping` of context values for the current
request
"""
return dict(self.session.context)
@lazy_property
def lang(self):
return self.context["lang"]
@lazy_property
def session(self):
"""
a :class:`OpenERPSession` holding the HTTP session data for the
current http session
"""
return self.httprequest.session
@property
def cr(self):
"""
:class:`~openerp.sql_db.Cursor` initialized for the current method
call.
Accessing the cursor when the current request uses the ``none``
authentication will raise an exception.
"""
# can not be a lazy_property because manual rollback in _call_function
# if already set (?)
if not self._cr:
self._cr = self.registry.cursor()
return self._cr
def __enter__(self):
_request_stack.push(self)
return self
@ -316,6 +281,8 @@ class WebRequest(object):
@property
def debug(self):
""" Indicates whether the current request is in "debug" mode
"""
return 'debug' in self.httprequest.args
@contextlib.contextmanager
@ -323,6 +290,48 @@ class WebRequest(object):
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
yield (self.registry, self.cr)
@lazy_property
def session_id(self):
"""
opaque identifier for the :class:`OpenERPSession` instance of
the current request
.. deprecated:: 8.0
Use the ``id`` attribute on :attr:`.session`
"""
return self.session.id
@property
def registry(self):
"""
The registry to the database linked to this request. Can be ``None``
if the current request uses the ``none`` authentication.
.. deprecated:: 8.0
use :attr:`.env`
"""
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
@property
def db(self):
"""
The database linked to this request. Can be ``None``
if the current request uses the ``none`` authentication.
"""
return self.session.db if not self.disable_db else None
@lazy_property
def httpsession(self):
""" HTTP session data
.. deprecated:: 8.0
Use :attr:`.session` instead.
"""
return self.session
def route(route=None, **kw):
"""
Decorator marking the decorated method as being a handler for
@ -378,7 +387,15 @@ def route(route=None, **kw):
return decorator
class JsonRequest(WebRequest):
""" JSON-RPC2 over HTTP.
""" Request handler for `JSON-RPC 2
<http://www.jsonrpc.org/specification>`_ over HTTP
* ``method`` is ignored
* ``params`` must be a JSON object (not an array) and is passed as keyword
arguments to the handler method
* the handler method's result is returned as JSON-RPC ``result`` and
wrapped in the `JSON-RPC Response
<http://www.jsonrpc.org/specification#response_object>`_
Sucessful request::
@ -490,8 +507,6 @@ class JsonRequest(WebRequest):
return self._json_response(error=error)
def dispatch(self):
""" Calls the method asked for by the JSON-RPC2 or JSONP request
"""
if self.jsonp_handler:
return self.jsonp_handler()
try:
@ -541,7 +556,23 @@ def jsonrequest(f):
return route([base, base + "/<path:_ignored_path>"], type="json", auth="user", combine=True)(f)
class HttpRequest(WebRequest):
""" Regular GET/POST request
""" Handler for the ``http`` request type.
matched routing parameters, query string parameters, form_ parameters
and files are passed to the handler method as keyword arguments.
In case of name conflict, routing parameters have priority.
The handler method's result can be:
* a falsy value, in which case the HTTP response will be an
`HTTP 204`_ (No Content)
* a werkzeug Response object, which is returned as-is
* a ``str`` or ``unicode``, will be wrapped in a Response object and
interpreted as HTML
.. _form: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
.. _HTTP 204: http://tools.ietf.org/html/rfc7231#section-6.3.5
"""
_request_type = "http"
@ -596,7 +627,7 @@ class HttpRequest(WebRequest):
return response
def render(self, template, qcontext=None, lazy=True, **kw):
""" Lazy render of QWeb template.
""" Lazy render of a QWeb template.
The actual rendering of the given template will occur at then end of
the dispatching. Meanwhile, the template and/or qcontext can be
@ -604,7 +635,9 @@ class HttpRequest(WebRequest):
:param basestring template: template to render
:param dict qcontext: Rendering context to use
:param dict lazy: Lazy rendering is processed later in wsgi response layer (default True)
:param bool lazy: whether the template rendering should be deferred
until the last possible moment
:param kw: forwarded to werkzeug's Response object
"""
response = Response(template=template, qcontext=qcontext, **kw)
if not lazy:
@ -612,7 +645,9 @@ class HttpRequest(WebRequest):
return response
def not_found(self, description=None):
""" Helper for 404 response, return its result from the method
""" Shortcut for a `HTTP 404
<http://tools.ietf.org/html/rfc7231#section-6.5.4>`_ (Not Found)
response
"""
return werkzeug.exceptions.NotFound(description)
@ -1073,13 +1108,20 @@ class Retry(RuntimeError):
class Response(werkzeug.wrappers.Response):
""" Response object passed through controller route chain.
In addition to the werkzeug.wrappers.Response parameters, this
classe's constructor can take the following additional parameters
In addition to the :class:`werkzeug.wrappers.Response` parameters, this
class's constructor can take the following additional parameters
for QWeb Lazy Rendering.
:param basestring template: template to render
:param dict qcontext: Rendering context to use
:param int uid: User id to use for the ir.ui.view render call
:param int uid: User id to use for the ir.ui.view render call,
``None`` to use the request's user (the default)
these attributes are available as parameters on the Response object and
can be altered at any time before rendering
Also exposes all the attributes and methods of
:class:`werkzeug.wrappers.Response`.
"""
default_mimetype = 'text/html'
def __init__(self, *args, **kw):
@ -1108,6 +1150,8 @@ class Response(werkzeug.wrappers.Response):
return self.template is not None
def render(self):
""" Renders the Response's template, returns the result
"""
view_obj = request.registry["ir.ui.view"]
uid = self.uid or request.uid or openerp.SUPERUSER_ID
while True:
@ -1119,6 +1163,9 @@ class Response(werkzeug.wrappers.Response):
self.qcontext.update(e.updates)
def flatten(self):
""" Forces the rendering of the response's template, sets the result
as response body and unsets :attr:`.template`
"""
self.response.append(self.render())
self.template = None

View File

@ -42,6 +42,10 @@ class lazy_property(object):
setattr(obj, self.fget.__name__, value)
return value
@property
def __doc__(self):
return self.fget.__doc__
@staticmethod
def reset_all(obj):
""" Reset all lazy properties on the instance `obj`. """