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)
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)
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)
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)
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)
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)
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)
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)
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)