Example #1
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + cjson.encode(obj) + "\n"
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
Example #2
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + json.dumps(obj) + "\n"
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            except Exception as exp:
                print("ERROR, json.dumps failed to serialize %s, type %s\nException: %s" \
                        % (obj, type(obj), str(exp)))
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #3
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + json.dumps(obj, indent=2)
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #4
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        try:
            etag.update(preamble)
            yield preamble

            try:
                for obj in stream:
                    chunk = PrettyJSONHTMLFormat.format_obj(obj)
                    etag.update(chunk)
                    yield chunk
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #5
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        try:
            etag.update(preamble)
            yield preamble

            try:
                for obj in stream:
                    chunk = XMLFormat.format_obj(obj)
                    etag.update(chunk)
                    yield chunk
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #6
0
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + json.dumps(obj) + "\n"
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            except Exception as exp:
                print("ERROR, json.dumps failed to serialize %s, type %s\nException: %s" \
                        % (obj, type(obj), str(exp)))
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError as e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception as e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #7
0
    def stream_chunked(self, stream, etag):
        """Generator for actually producing the output."""
        try:
            for chunk in stream:
                etag.update(chunk)
                yield chunk

        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
Example #8
0
    def stream_chunked(self, stream, etag):
        """Generator for actually producing the output."""
        try:
            for chunk in stream:
                etag.update(chunk)
                yield chunk

        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
Example #9
0
class RawFormat(RESTFormat):
    """Format an iterable of objects as raw data.

    Generates raw data completely unmodified, for example image data or
    streaming arbitrary external data files including even plain text.
    Computes an ETag on the output in the process. The result is always
    chunked, even simple strings on input. Usually small enough responses
    will automatically be converted back to a single string response post
    compression and ETag processing.

    Any exceptions raised by input stream are reported to `report_rest_error`
    and swallowed, as this is normally used to generate output for CherryPy
    responses, which cannot handle exceptions reasonably after the output
    generation begins; later processing may reconvert those back to exceptions
    however (cf. stream_maybe_etag()). A X-REST-Status trailer header is added
    if (and only if) an exception occurs; the client must inspect that to find
    out if it got the complete output. There is normally 'X-REST-Status: 100'
    in normal response headers, and it remains valid in case of success.
    No ETag header is generated in case of an exception."""
    def stream_chunked(self, stream, etag):
        """Generator for actually producing the output."""
        try:
            for chunk in stream:
                etag.update(chunk)
                yield chunk

        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception, e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #10
0
class XMLFormat(RESTFormat):
    """Format an iterable of objects into XML encoded in UTF-8.

    Generates normally first a preamble, a stream of XML-rendered objects,
    then the trailer, computing an ETag on the output string in the process.
    This is designed exclusively for use with iterables for chunked transfer
    encoding HTTP responses; it's not a general purpose formatting utility.

    Outputs first a preamble, then XML encoded output of input stream, and
    finally a trailer. Any exceptions raised by input stream are reported to
    `report_rest_error` and swallowed, as this is normally used to generate
    output for CherryPy responses, which cannot handle exceptions reasonably
    after the output generation begins; later processing may reconvert those
    back to exceptions however (cf. stream_maybe_etag()). Once the preamble
    has been emitted, the trailer is also emitted even if the input stream
    raises an exception, in order to make the output well-formed; the client
    must inspect the X-REST-Status trailer header to find out if it got the
    complete output. No ETag header is generated in case of an exception.

    The ETag generation is deterministic only if iterating over input is
    deterministic. Beware in particular the key order for a dict is
    arbitrary and may differ for two semantically identical dicts.

    A X-REST-Status trailer header is added only in case of error. There is
    normally 'X-REST-Status: 100' in normal response headers, and it remains
    valid in case of success.

    The output is generated as an XML document whose top-level entity name
    is defined by the label given at the formatter construction time. The
    caller must define ``cherrypy.request.rest_generate_data`` to element
    name for wrapping stream contents. Usually the top-level entity is the
    application name and the ``cherrypy.request.rest_generate_data`` is
    ``result``.

    Iterables are output as ``<array><i>ITEM</i><i>ITEM</i></array>``,
    dictionaries as ``<dict><key>KEY</key><value>VALUE</value></dict>``.
    `None` is output as empty contents, and hence there is no way to
    distinguish `None` and an empty string from each other. Scalar types
    are output as rendered by `str()`, but obviously XML encoding unsafe
    characters. This class does not support formatting arbitrary types.

    The formatter does not insert any spaces into the output. Although the
    output is generated as a preamble, stream of objects, and trailer just
    like by the `JSONFormatter`, each of which is a separate HTTP transfer
    chunk, the output does *not* have guaranteed line-oriented structure
    like the `JSONFormatter` produces. Note in particular that if the data
    stream contains strings with newlines, the output will have arbitrary
    line structure. On the other hand, as the output is well-formed XML,
    virtually all SAX processors can read the stream incrementally even if
    the client isn't able to fully preserve chunked HTTP transfer encoding."""
    def __init__(self, label):
        self.label = label

    @staticmethod
    def format_obj(obj):
        """Render an object `obj` into XML."""
        if isinstance(obj, type(None)):
            result = ""
        elif isinstance(obj, (unicode, str)):
            result = xml.sax.saxutils.escape(obj).encode("utf-8")
        elif isinstance(obj, (int, float, bool)):
            result = xml.sax.saxutils.escape(str(obj)).encode("utf-8")
        elif isinstance(obj, dict):
            result = "<dict>"
            for k, v in obj.iteritems():
                result += "<key>%s</key><value>%s</value>" % \
                  (xml.sax.saxutils.escape(k).encode("utf-8"),
                   XMLFormat.format_obj(v))
            result += "</dict>"
        elif is_iterable(obj):
            result = "<array>"
            for v in obj:
                result += "<i>%s</i>" % XMLFormat.format_obj(v)
            result += "</array>"
        else:
            cherrypy.log("cannot represent object of type %s in xml (%s)" %
                         (type(obj).__class__.__name__, repr(obj)))
            raise ExecutionError("cannot represent object in xml")
        return result

    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        try:
            etag.update(preamble)
            yield preamble

            try:
                for obj in stream:
                    chunk = XMLFormat.format_obj(obj)
                    etag.update(chunk)
                    yield chunk
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception, e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)
Example #11
0
class JSONFormat(RESTFormat):
    """Format an iterable of objects into JSON.

    Generates normally first a preamble, a stream of JSON-rendered objects,
    then the trailer, computing an ETag on the output string in the process.
    This is designed exclusively for use with iterables for chunked transfer
    encoding HTTP responses; it's not a general purpose formatting utility.

    Outputs first a preamble, then JSON encoded output of input stream, and
    finally a trailer. Any exceptions raised by input stream are reported to
    `report_rest_error` and swallowed, as this is normally used to generate
    output for CherryPy responses, which cannot handle exceptions reasonably
    after the output generation begins; later processing may reconvert those
    back to exceptions however (cf. stream_maybe_etag()). Once the preamble
    has been emitted, the trailer is also emitted even if the input stream
    raises an exception, in order to make the output well-formed; the client
    must inspect the X-REST-Status trailer header to find out if it got the
    complete output. No ETag header is generated in case of an exception.

    The ETag generation is deterministic only if `cjson.encode()` output is
    deterministic for the input. Beware in particular the key order for a
    dict is arbitrary and may differ for two semantically identical dicts.

    A X-REST-Status trailer header is added only in case of error. There is
    normally 'X-REST-Status: 100' in normal response headers, and it remains
    valid in case of success.

    The output is always generated as a JSON dictionary. The caller must
    define ``cherrypy.request.rest_generate_data`` as the key for actual
    contents, usually something like "result". The `stream` value will be
    generated as an array value for that key.

    If ``cherrypy.request.rest_generate_preamble`` is a non-empty list, it
    is output as the ``desc`` key value in the preamble before outputting
    the `stream` contents. Otherwise the output consists solely of `stream`.
    A common use of ``rest_generate_preamble`` is list of column labels
    with `stream` an iterable of lists of column values.

    The output is guaranteed to contain one line of preamble which starts a
    dictionary and an array ("``{key: [``"), one line of JSON rendering of
    each object in `stream`, with the first line starting with exactly one
    space and second and subsequent lines starting with a comma, and one
    final trailer line consisting of "``]}``". Each line is generated as a
    HTTP transfer chunk. This format is fixed so readers can be constructed
    to read and parse the stream incrementally one line at a time,
    facilitating maximum throughput processing of the response."""
    def stream_chunked(self, stream, etag, preamble, trailer):
        """Generator for actually producing the output."""
        comma = " "

        try:
            if preamble:
                etag.update(preamble)
                yield preamble

            try:
                for obj in stream:
                    chunk = comma + cjson.encode(obj) + "\n"
                    etag.update(chunk)
                    yield chunk
                    comma = ","
            except GeneratorExit:
                etag.invalidate()
                trailer = None
                raise
            finally:
                if trailer:
                    etag.update(trailer)
                    yield trailer

            cherrypy.response.headers["X-REST-Status"] = 100
        except RESTError, e:
            etag.invalidate()
            report_rest_error(e, format_exc(), False)
        except Exception, e:
            etag.invalidate()
            report_rest_error(ExecutionError(), format_exc(), False)