Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
serializer.py186 linesDownload Raw Back to ext
1# ext/serializer.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7# mypy: ignore-errors
8
9"""Serializer/Deserializer objects for usage with SQLAlchemy query structures,
10allowing "contextual" deserialization.
11
12.. legacy::
13
14    The serializer extension is **legacy** and should not be used for
15    new development.
16
17Any SQLAlchemy query structure, either based on sqlalchemy.sql.*
18or sqlalchemy.orm.* can be used.  The mappers, Tables, Columns, Session
19etc. which are referenced by the structure are not persisted in serialized
20form, but are instead re-associated with the query structure
21when it is deserialized.
22
23.. warning:: The serializer extension uses pickle to serialize and
24   deserialize objects, so the same security consideration mentioned
25   in the `python documentation
26   <https://docs.python.org/3/library/pickle.html>`_ apply.
27
28Usage is nearly the same as that of the standard Python pickle module::
29
30    from sqlalchemy.ext.serializer import loads, dumps
31
32    metadata = MetaData(bind=some_engine)
33    Session = scoped_session(sessionmaker())
34
35    # ... define mappers
36
37    query = (
38        Session.query(MyClass)
39        .filter(MyClass.somedata == "foo")
40        .order_by(MyClass.sortkey)
41    )
42
43    # pickle the query
44    serialized = dumps(query)
45
46    # unpickle.  Pass in metadata + scoped_session
47    query2 = loads(serialized, metadata, Session)
48
49    print(query2.all())
50
51Similar restrictions as when using raw pickle apply; mapped classes must be
52themselves be pickleable, meaning they are importable from a module-level
53namespace.
54
55The serializer module is only appropriate for query structures.  It is not
56needed for:
57
58* instances of user-defined classes.   These contain no references to engines,
59  sessions or expression constructs in the typical case and can be serialized
60  directly.
61
62* Table metadata that is to be loaded entirely from the serialized structure
63  (i.e. is not already declared in the application).   Regular
64  pickle.loads()/dumps() can be used to fully dump any ``MetaData`` object,
65  typically one which was reflected from an existing database at some previous
66  point in time.  The serializer module is specifically for the opposite case,
67  where the Table metadata is already present in memory.
68
69"""
70
71from io import BytesIO
72import pickle
73import re
74
75from .. import Column
76from .. import Table
77from ..engine import Engine
78from ..orm import class_mapper
79from ..orm.interfaces import MapperProperty
80from ..orm.mapper import Mapper
81from ..orm.session import Session
82from ..util import b64decode
83from ..util import b64encode
84
85
86__all__ = ["Serializer", "Deserializer", "dumps", "loads"]
87
88
89class Serializer(pickle.Pickler):
90
91    def persistent_id(self, obj):
92        # print "serializing:", repr(obj)
93        if isinstance(obj, Mapper) and not obj.non_primary:
94            id_ = "mapper:" + b64encode(pickle.dumps(obj.class_))
95        elif isinstance(obj, MapperProperty) and not obj.parent.non_primary:
96            id_ = (
97                "mapperprop:"
98                + b64encode(pickle.dumps(obj.parent.class_))
99                + ":"
100                + obj.key
101            )
102        elif isinstance(obj, Table):
103            if "parententity" in obj._annotations:
104                id_ = "mapper_selectable:" + b64encode(
105                    pickle.dumps(obj._annotations["parententity"].class_)
106                )
107            else:
108                id_ = f"table:{obj.key}"
109        elif isinstance(obj, Column) and isinstance(obj.table, Table):
110            id_ = f"column:{obj.table.key}:{obj.key}"
111        elif isinstance(obj, Session):
112            id_ = "session:"
113        elif isinstance(obj, Engine):
114            id_ = "engine:"
115        else:
116            return None
117        return id_
118
119
120our_ids = re.compile(
121    r"(mapperprop|mapper|mapper_selectable|table|column|"
122    r"session|attribute|engine):(.*)"
123)
124
125
126class Deserializer(pickle.Unpickler):
127
128    def __init__(self, file, metadata=None, scoped_session=None, engine=None):
129        super().__init__(file)
130        self.metadata = metadata
131        self.scoped_session = scoped_session
132        self.engine = engine
133
134    def get_engine(self):
135        if self.engine:
136            return self.engine
137        elif self.scoped_session and self.scoped_session().bind:
138            return self.scoped_session().bind
139        else:
140            return None
141
142    def persistent_load(self, id_):
143        m = our_ids.match(str(id_))
144        if not m:
145            return None
146        else:
147            type_, args = m.group(1, 2)
148            if type_ == "attribute":
149                key, clsarg = args.split(":")
150                cls = pickle.loads(b64decode(clsarg))
151                return getattr(cls, key)
152            elif type_ == "mapper":
153                cls = pickle.loads(b64decode(args))
154                return class_mapper(cls)
155            elif type_ == "mapper_selectable":
156                cls = pickle.loads(b64decode(args))
157                return class_mapper(cls).__clause_element__()
158            elif type_ == "mapperprop":
159                mapper, keyname = args.split(":")
160                cls = pickle.loads(b64decode(mapper))
161                return class_mapper(cls).attrs[keyname]
162            elif type_ == "table":
163                return self.metadata.tables[args]
164            elif type_ == "column":
165                table, colname = args.split(":")
166                return self.metadata.tables[table].c[colname]
167            elif type_ == "session":
168                return self.scoped_session()
169            elif type_ == "engine":
170                return self.get_engine()
171            else:
172                raise Exception("Unknown token: %s" % type_)
173
174
175def dumps(obj, protocol=pickle.HIGHEST_PROTOCOL):
176    buf = BytesIO()
177    pickler = Serializer(buf, protocol)
178    pickler.dump(obj)
179    return buf.getvalue()
180
181
182def loads(data, metadata=None, scoped_session=None, engine=None):
183    buf = BytesIO(data)
184    unpickler = Deserializer(buf, metadata, scoped_session, engine)
185    return unpickler.load()
186 
codekingpro/portable-devtools · Team Ai