Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
serializer.py186 linesDownload Raw Back to ext
1# ext/serializer.py
2# Copyright (C) 2005-2024 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    metadata = MetaData(bind=some_engine)
32    Session = scoped_session(sessionmaker())
33
34    # ... define mappers
35
36    query = Session.query(MyClass).
37        filter(MyClass.somedata=='foo').order_by(MyClass.sortkey)
38
39    # pickle the query
40    serialized = dumps(query)
41
42    # unpickle.  Pass in metadata + scoped_session
43    query2 = loads(serialized, metadata, Session)
44
45    print query2.all()
46
47Similar restrictions as when using raw pickle apply; mapped classes must be
48themselves be pickleable, meaning they are importable from a module-level
49namespace.
50
51The serializer module is only appropriate for query structures.  It is not
52needed for:
53
54* instances of user-defined classes.   These contain no references to engines,
55  sessions or expression constructs in the typical case and can be serialized
56  directly.
57
58* Table metadata that is to be loaded entirely from the serialized structure
59  (i.e. is not already declared in the application).   Regular
60  pickle.loads()/dumps() can be used to fully dump any ``MetaData`` object,
61  typically one which was reflected from an existing database at some previous
62  point in time.  The serializer module is specifically for the opposite case,
63  where the Table metadata is already present in memory.
64
65"""
66
67from io import BytesIO
68import pickle
69import re
70
71from .. import Column
72from .. import Table
73from ..engine import Engine
74from ..orm import class_mapper
75from ..orm.interfaces import MapperProperty
76from ..orm.mapper import Mapper
77from ..orm.session import Session
78from ..util import b64decode
79from ..util import b64encode
80
81
82__all__ = ["Serializer", "Deserializer", "dumps", "loads"]
83
84
85def Serializer(*args, **kw):
86    pickler = pickle.Pickler(*args, **kw)
87
88    def persistent_id(obj):
89        # print "serializing:", repr(obj)
90        if isinstance(obj, Mapper) and not obj.non_primary:
91            id_ = "mapper:" + b64encode(pickle.dumps(obj.class_))
92        elif isinstance(obj, MapperProperty) and not obj.parent.non_primary:
93            id_ = (
94                "mapperprop:"
95                + b64encode(pickle.dumps(obj.parent.class_))
96                + ":"
97                + obj.key
98            )
99        elif isinstance(obj, Table):
100            if "parententity" in obj._annotations:
101                id_ = "mapper_selectable:" + b64encode(
102                    pickle.dumps(obj._annotations["parententity"].class_)
103                )
104            else:
105                id_ = f"table:{obj.key}"
106        elif isinstance(obj, Column) and isinstance(obj.table, Table):
107            id_ = f"column:{obj.table.key}:{obj.key}"
108        elif isinstance(obj, Session):
109            id_ = "session:"
110        elif isinstance(obj, Engine):
111            id_ = "engine:"
112        else:
113            return None
114        return id_
115
116    pickler.persistent_id = persistent_id
117    return pickler
118
119
120our_ids = re.compile(
121    r"(mapperprop|mapper|mapper_selectable|table|column|"
122    r"session|attribute|engine):(.*)"
123)
124
125
126def Deserializer(file, metadata=None, scoped_session=None, engine=None):
127    unpickler = pickle.Unpickler(file)
128
129    def get_engine():
130        if engine:
131            return engine
132        elif scoped_session and scoped_session().bind:
133            return scoped_session().bind
134        elif metadata and metadata.bind:
135            return metadata.bind
136        else:
137            return None
138
139    def persistent_load(id_):
140        m = our_ids.match(str(id_))
141        if not m:
142            return None
143        else:
144            type_, args = m.group(1, 2)
145            if type_ == "attribute":
146                key, clsarg = args.split(":")
147                cls = pickle.loads(b64decode(clsarg))
148                return getattr(cls, key)
149            elif type_ == "mapper":
150                cls = pickle.loads(b64decode(args))
151                return class_mapper(cls)
152            elif type_ == "mapper_selectable":
153                cls = pickle.loads(b64decode(args))
154                return class_mapper(cls).__clause_element__()
155            elif type_ == "mapperprop":
156                mapper, keyname = args.split(":")
157                cls = pickle.loads(b64decode(mapper))
158                return class_mapper(cls).attrs[keyname]
159            elif type_ == "table":
160                return metadata.tables[args]
161            elif type_ == "column":
162                table, colname = args.split(":")
163                return metadata.tables[table].c[colname]
164            elif type_ == "session":
165                return scoped_session()
166            elif type_ == "engine":
167                return get_engine()
168            else:
169                raise Exception("Unknown token: %s" % type_)
170
171    unpickler.persistent_load = persistent_load
172    return unpickler
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