|
| 1 | +"""JSON serialization of Scrapy requests and cached responses for storage on the Apify platform. |
| 2 | +
|
| 3 | +Scrapy requests and cached responses are stored in the Apify request queue and key-value store, |
| 4 | +which hold JSON, so they are serialized as JSON here rather than pickled. |
| 5 | +
|
| 6 | +Only `body` (`bytes`) and `headers` (`{bytes: [bytes]}`) are not natively JSON-serializable; both sit at |
| 7 | +fixed keys and are base64-encoded in place. A `str` `body` is encoded as its UTF-8 bytes and comes back as |
| 8 | +`bytes`, matching Scrapy, which always stores `body` as `bytes`. Pydantic models such as Crawlee's |
| 9 | +`UserData` are dumped via `model_dump()`. Everything else, notably `meta` and `cb_kwargs`, must already be |
| 10 | +JSON-serializable, otherwise serialization fails with a clear error naming the offending value. No in-band |
| 11 | +sentinel is used, so no user value can collide with the encoding. |
| 12 | +
|
| 13 | +Known limitations of the pickle -> JSON switch (a documented breaking change): JSON has fewer types than |
| 14 | +pickle, so values in `meta`/`cb_kwargs` are subject to JSON's coercions. A `tuple` round-trips as a `list` |
| 15 | +and non-string `dict` keys round-trip as strings (e.g. `{1: 'a'}` becomes `{'1': 'a'}`). Values JSON cannot |
| 16 | +represent at all (`datetime`, `set`, `Decimal`, arbitrary objects, ...) are not coerced silently: |
| 17 | +serialization raises and the request is skipped loudly rather than stored in a corrupted form. |
| 18 | +""" |
| 19 | + |
1 | 20 | from __future__ import annotations |
2 | 21 |
|
3 | 22 | import base64 |
|
6 | 25 |
|
7 | 26 | from pydantic import BaseModel |
8 | 27 |
|
9 | | -# Scrapy requests and cached responses are stored in the Apify request queue and key-value store, |
10 | | -# which hold JSON, so they are serialized as JSON here rather than pickled. |
11 | | -# |
12 | | -# Only `body` (`bytes`) and `headers` (`{bytes: [bytes]}`) are not natively JSON-serializable; both sit at |
13 | | -# fixed keys and are base64-encoded in place. A `str` `body` is encoded as its UTF-8 bytes and comes back as |
14 | | -# `bytes`, matching Scrapy, which always stores `body` as `bytes`. Pydantic models such as Crawlee's |
15 | | -# `UserData` are dumped via `model_dump()`. Everything else, notably `meta` and `cb_kwargs`, must already be |
16 | | -# JSON-serializable, otherwise serialization fails with a clear error naming the offending value. No in-band |
17 | | -# sentinel is used, so no user value can collide with the encoding. |
18 | | -# |
19 | | -# Known limitations of the pickle -> JSON switch (a documented breaking change): JSON has fewer types than |
20 | | -# pickle, so values in `meta`/`cb_kwargs` are subject to JSON's coercions. A `tuple` round-trips as a `list` |
21 | | -# and non-string `dict` keys round-trip as strings (e.g. `{1: 'a'}` becomes `{'1': 'a'}`). Values JSON cannot |
22 | | -# represent at all (`datetime`, `set`, `Decimal`, arbitrary objects, ...) are not coerced silently: |
23 | | -# serialization raises and the request is skipped loudly rather than stored in a corrupted form. |
24 | | - |
25 | 28 | # Cap the offending value's repr in a serialization error message so a huge value cannot bloat the log. |
26 | 29 | _MAX_ERROR_VALUE_REPR_LEN = 200 |
27 | 30 |
|
|
0 commit comments