xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/shelve.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Manage shelves of pickled objects.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerA "shelf" is a persistent, dictionary-like object.  The difference
4*cda5da8dSAndroid Build Coastguard Workerwith dbm databases is that the values (not the keys!) in a shelf can
5*cda5da8dSAndroid Build Coastguard Workerbe essentially arbitrary Python objects -- anything that the "pickle"
6*cda5da8dSAndroid Build Coastguard Workermodule can handle.  This includes most class instances, recursive data
7*cda5da8dSAndroid Build Coastguard Workertypes, and objects containing lots of shared sub-objects.  The keys
8*cda5da8dSAndroid Build Coastguard Workerare ordinary strings.
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard WorkerTo summarize the interface (key is a string, data is an arbitrary
11*cda5da8dSAndroid Build Coastguard Workerobject):
12*cda5da8dSAndroid Build Coastguard Worker
13*cda5da8dSAndroid Build Coastguard Worker        import shelve
14*cda5da8dSAndroid Build Coastguard Worker        d = shelve.open(filename) # open, with (g)dbm filename -- no suffix
15*cda5da8dSAndroid Build Coastguard Worker
16*cda5da8dSAndroid Build Coastguard Worker        d[key] = data   # store data at key (overwrites old data if
17*cda5da8dSAndroid Build Coastguard Worker                        # using an existing key)
18*cda5da8dSAndroid Build Coastguard Worker        data = d[key]   # retrieve a COPY of the data at key (raise
19*cda5da8dSAndroid Build Coastguard Worker                        # KeyError if no such key) -- NOTE that this
20*cda5da8dSAndroid Build Coastguard Worker                        # access returns a *copy* of the entry!
21*cda5da8dSAndroid Build Coastguard Worker        del d[key]      # delete data stored at key (raises KeyError
22*cda5da8dSAndroid Build Coastguard Worker                        # if no such key)
23*cda5da8dSAndroid Build Coastguard Worker        flag = key in d # true if the key exists
24*cda5da8dSAndroid Build Coastguard Worker        list = d.keys() # a list of all existing keys (slow!)
25*cda5da8dSAndroid Build Coastguard Worker
26*cda5da8dSAndroid Build Coastguard Worker        d.close()       # close it
27*cda5da8dSAndroid Build Coastguard Worker
28*cda5da8dSAndroid Build Coastguard WorkerDependent on the implementation, closing a persistent dictionary may
29*cda5da8dSAndroid Build Coastguard Workeror may not be necessary to flush changes to disk.
30*cda5da8dSAndroid Build Coastguard Worker
31*cda5da8dSAndroid Build Coastguard WorkerNormally, d[key] returns a COPY of the entry.  This needs care when
32*cda5da8dSAndroid Build Coastguard Workermutable entries are mutated: for example, if d[key] is a list,
33*cda5da8dSAndroid Build Coastguard Worker        d[key].append(anitem)
34*cda5da8dSAndroid Build Coastguard Workerdoes NOT modify the entry d[key] itself, as stored in the persistent
35*cda5da8dSAndroid Build Coastguard Workermapping -- it only modifies the copy, which is then immediately
36*cda5da8dSAndroid Build Coastguard Workerdiscarded, so that the append has NO effect whatsoever.  To append an
37*cda5da8dSAndroid Build Coastguard Workeritem to d[key] in a way that will affect the persistent mapping, use:
38*cda5da8dSAndroid Build Coastguard Worker        data = d[key]
39*cda5da8dSAndroid Build Coastguard Worker        data.append(anitem)
40*cda5da8dSAndroid Build Coastguard Worker        d[key] = data
41*cda5da8dSAndroid Build Coastguard Worker
42*cda5da8dSAndroid Build Coastguard WorkerTo avoid the problem with mutable entries, you may pass the keyword
43*cda5da8dSAndroid Build Coastguard Workerargument writeback=True in the call to shelve.open.  When you use:
44*cda5da8dSAndroid Build Coastguard Worker        d = shelve.open(filename, writeback=True)
45*cda5da8dSAndroid Build Coastguard Workerthen d keeps a cache of all entries you access, and writes them all back
46*cda5da8dSAndroid Build Coastguard Workerto the persistent mapping when you call d.close().  This ensures that
47*cda5da8dSAndroid Build Coastguard Workersuch usage as d[key].append(anitem) works as intended.
48*cda5da8dSAndroid Build Coastguard Worker
49*cda5da8dSAndroid Build Coastguard WorkerHowever, using keyword argument writeback=True may consume vast amount
50*cda5da8dSAndroid Build Coastguard Workerof memory for the cache, and it may make d.close() very slow, if you
51*cda5da8dSAndroid Build Coastguard Workeraccess many of d's entries after opening it in this way: d has no way to
52*cda5da8dSAndroid Build Coastguard Workercheck which of the entries you access are mutable and/or which ones you
53*cda5da8dSAndroid Build Coastguard Workeractually mutate, so it must cache, and write back at close, all of the
54*cda5da8dSAndroid Build Coastguard Workerentries that you access.  You can call d.sync() to write back all the
55*cda5da8dSAndroid Build Coastguard Workerentries in the cache, and empty the cache (d.sync() also synchronizes
56*cda5da8dSAndroid Build Coastguard Workerthe persistent dictionary on disk, if feasible).
57*cda5da8dSAndroid Build Coastguard Worker"""
58*cda5da8dSAndroid Build Coastguard Worker
59*cda5da8dSAndroid Build Coastguard Workerfrom pickle import DEFAULT_PROTOCOL, Pickler, Unpickler
60*cda5da8dSAndroid Build Coastguard Workerfrom io import BytesIO
61*cda5da8dSAndroid Build Coastguard Worker
62*cda5da8dSAndroid Build Coastguard Workerimport collections.abc
63*cda5da8dSAndroid Build Coastguard Worker
64*cda5da8dSAndroid Build Coastguard Worker__all__ = ["Shelf", "BsdDbShelf", "DbfilenameShelf", "open"]
65*cda5da8dSAndroid Build Coastguard Worker
66*cda5da8dSAndroid Build Coastguard Workerclass _ClosedDict(collections.abc.MutableMapping):
67*cda5da8dSAndroid Build Coastguard Worker    'Marker for a closed dict.  Access attempts raise a ValueError.'
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard Worker    def closed(self, *args):
70*cda5da8dSAndroid Build Coastguard Worker        raise ValueError('invalid operation on closed shelf')
71*cda5da8dSAndroid Build Coastguard Worker    __iter__ = __len__ = __getitem__ = __setitem__ = __delitem__ = keys = closed
72*cda5da8dSAndroid Build Coastguard Worker
73*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
74*cda5da8dSAndroid Build Coastguard Worker        return '<Closed Dictionary>'
75*cda5da8dSAndroid Build Coastguard Worker
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Workerclass Shelf(collections.abc.MutableMapping):
78*cda5da8dSAndroid Build Coastguard Worker    """Base class for shelf implementations.
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker    This is initialized with a dictionary-like object.
81*cda5da8dSAndroid Build Coastguard Worker    See the module's __doc__ string for an overview of the interface.
82*cda5da8dSAndroid Build Coastguard Worker    """
83*cda5da8dSAndroid Build Coastguard Worker
84*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, dict, protocol=None, writeback=False,
85*cda5da8dSAndroid Build Coastguard Worker                 keyencoding="utf-8"):
86*cda5da8dSAndroid Build Coastguard Worker        self.dict = dict
87*cda5da8dSAndroid Build Coastguard Worker        if protocol is None:
88*cda5da8dSAndroid Build Coastguard Worker            protocol = DEFAULT_PROTOCOL
89*cda5da8dSAndroid Build Coastguard Worker        self._protocol = protocol
90*cda5da8dSAndroid Build Coastguard Worker        self.writeback = writeback
91*cda5da8dSAndroid Build Coastguard Worker        self.cache = {}
92*cda5da8dSAndroid Build Coastguard Worker        self.keyencoding = keyencoding
93*cda5da8dSAndroid Build Coastguard Worker
94*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
95*cda5da8dSAndroid Build Coastguard Worker        for k in self.dict.keys():
96*cda5da8dSAndroid Build Coastguard Worker            yield k.decode(self.keyencoding)
97*cda5da8dSAndroid Build Coastguard Worker
98*cda5da8dSAndroid Build Coastguard Worker    def __len__(self):
99*cda5da8dSAndroid Build Coastguard Worker        return len(self.dict)
100*cda5da8dSAndroid Build Coastguard Worker
101*cda5da8dSAndroid Build Coastguard Worker    def __contains__(self, key):
102*cda5da8dSAndroid Build Coastguard Worker        return key.encode(self.keyencoding) in self.dict
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Worker    def get(self, key, default=None):
105*cda5da8dSAndroid Build Coastguard Worker        if key.encode(self.keyencoding) in self.dict:
106*cda5da8dSAndroid Build Coastguard Worker            return self[key]
107*cda5da8dSAndroid Build Coastguard Worker        return default
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, key):
110*cda5da8dSAndroid Build Coastguard Worker        try:
111*cda5da8dSAndroid Build Coastguard Worker            value = self.cache[key]
112*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
113*cda5da8dSAndroid Build Coastguard Worker            f = BytesIO(self.dict[key.encode(self.keyencoding)])
114*cda5da8dSAndroid Build Coastguard Worker            value = Unpickler(f).load()
115*cda5da8dSAndroid Build Coastguard Worker            if self.writeback:
116*cda5da8dSAndroid Build Coastguard Worker                self.cache[key] = value
117*cda5da8dSAndroid Build Coastguard Worker        return value
118*cda5da8dSAndroid Build Coastguard Worker
119*cda5da8dSAndroid Build Coastguard Worker    def __setitem__(self, key, value):
120*cda5da8dSAndroid Build Coastguard Worker        if self.writeback:
121*cda5da8dSAndroid Build Coastguard Worker            self.cache[key] = value
122*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO()
123*cda5da8dSAndroid Build Coastguard Worker        p = Pickler(f, self._protocol)
124*cda5da8dSAndroid Build Coastguard Worker        p.dump(value)
125*cda5da8dSAndroid Build Coastguard Worker        self.dict[key.encode(self.keyencoding)] = f.getvalue()
126*cda5da8dSAndroid Build Coastguard Worker
127*cda5da8dSAndroid Build Coastguard Worker    def __delitem__(self, key):
128*cda5da8dSAndroid Build Coastguard Worker        del self.dict[key.encode(self.keyencoding)]
129*cda5da8dSAndroid Build Coastguard Worker        try:
130*cda5da8dSAndroid Build Coastguard Worker            del self.cache[key]
131*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
132*cda5da8dSAndroid Build Coastguard Worker            pass
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
135*cda5da8dSAndroid Build Coastguard Worker        return self
136*cda5da8dSAndroid Build Coastguard Worker
137*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, type, value, traceback):
138*cda5da8dSAndroid Build Coastguard Worker        self.close()
139*cda5da8dSAndroid Build Coastguard Worker
140*cda5da8dSAndroid Build Coastguard Worker    def close(self):
141*cda5da8dSAndroid Build Coastguard Worker        if self.dict is None:
142*cda5da8dSAndroid Build Coastguard Worker            return
143*cda5da8dSAndroid Build Coastguard Worker        try:
144*cda5da8dSAndroid Build Coastguard Worker            self.sync()
145*cda5da8dSAndroid Build Coastguard Worker            try:
146*cda5da8dSAndroid Build Coastguard Worker                self.dict.close()
147*cda5da8dSAndroid Build Coastguard Worker            except AttributeError:
148*cda5da8dSAndroid Build Coastguard Worker                pass
149*cda5da8dSAndroid Build Coastguard Worker        finally:
150*cda5da8dSAndroid Build Coastguard Worker            # Catch errors that may happen when close is called from __del__
151*cda5da8dSAndroid Build Coastguard Worker            # because CPython is in interpreter shutdown.
152*cda5da8dSAndroid Build Coastguard Worker            try:
153*cda5da8dSAndroid Build Coastguard Worker                self.dict = _ClosedDict()
154*cda5da8dSAndroid Build Coastguard Worker            except:
155*cda5da8dSAndroid Build Coastguard Worker                self.dict = None
156*cda5da8dSAndroid Build Coastguard Worker
157*cda5da8dSAndroid Build Coastguard Worker    def __del__(self):
158*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(self, 'writeback'):
159*cda5da8dSAndroid Build Coastguard Worker            # __init__ didn't succeed, so don't bother closing
160*cda5da8dSAndroid Build Coastguard Worker            # see http://bugs.python.org/issue1339007 for details
161*cda5da8dSAndroid Build Coastguard Worker            return
162*cda5da8dSAndroid Build Coastguard Worker        self.close()
163*cda5da8dSAndroid Build Coastguard Worker
164*cda5da8dSAndroid Build Coastguard Worker    def sync(self):
165*cda5da8dSAndroid Build Coastguard Worker        if self.writeback and self.cache:
166*cda5da8dSAndroid Build Coastguard Worker            self.writeback = False
167*cda5da8dSAndroid Build Coastguard Worker            for key, entry in self.cache.items():
168*cda5da8dSAndroid Build Coastguard Worker                self[key] = entry
169*cda5da8dSAndroid Build Coastguard Worker            self.writeback = True
170*cda5da8dSAndroid Build Coastguard Worker            self.cache = {}
171*cda5da8dSAndroid Build Coastguard Worker        if hasattr(self.dict, 'sync'):
172*cda5da8dSAndroid Build Coastguard Worker            self.dict.sync()
173*cda5da8dSAndroid Build Coastguard Worker
174*cda5da8dSAndroid Build Coastguard Worker
175*cda5da8dSAndroid Build Coastguard Workerclass BsdDbShelf(Shelf):
176*cda5da8dSAndroid Build Coastguard Worker    """Shelf implementation using the "BSD" db interface.
177*cda5da8dSAndroid Build Coastguard Worker
178*cda5da8dSAndroid Build Coastguard Worker    This adds methods first(), next(), previous(), last() and
179*cda5da8dSAndroid Build Coastguard Worker    set_location() that have no counterpart in [g]dbm databases.
180*cda5da8dSAndroid Build Coastguard Worker
181*cda5da8dSAndroid Build Coastguard Worker    The actual database must be opened using one of the "bsddb"
182*cda5da8dSAndroid Build Coastguard Worker    modules "open" routines (i.e. bsddb.hashopen, bsddb.btopen or
183*cda5da8dSAndroid Build Coastguard Worker    bsddb.rnopen) and passed to the constructor.
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker    See the module's __doc__ string for an overview of the interface.
186*cda5da8dSAndroid Build Coastguard Worker    """
187*cda5da8dSAndroid Build Coastguard Worker
188*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, dict, protocol=None, writeback=False,
189*cda5da8dSAndroid Build Coastguard Worker                 keyencoding="utf-8"):
190*cda5da8dSAndroid Build Coastguard Worker        Shelf.__init__(self, dict, protocol, writeback, keyencoding)
191*cda5da8dSAndroid Build Coastguard Worker
192*cda5da8dSAndroid Build Coastguard Worker    def set_location(self, key):
193*cda5da8dSAndroid Build Coastguard Worker        (key, value) = self.dict.set_location(key)
194*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO(value)
195*cda5da8dSAndroid Build Coastguard Worker        return (key.decode(self.keyencoding), Unpickler(f).load())
196*cda5da8dSAndroid Build Coastguard Worker
197*cda5da8dSAndroid Build Coastguard Worker    def next(self):
198*cda5da8dSAndroid Build Coastguard Worker        (key, value) = next(self.dict)
199*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO(value)
200*cda5da8dSAndroid Build Coastguard Worker        return (key.decode(self.keyencoding), Unpickler(f).load())
201*cda5da8dSAndroid Build Coastguard Worker
202*cda5da8dSAndroid Build Coastguard Worker    def previous(self):
203*cda5da8dSAndroid Build Coastguard Worker        (key, value) = self.dict.previous()
204*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO(value)
205*cda5da8dSAndroid Build Coastguard Worker        return (key.decode(self.keyencoding), Unpickler(f).load())
206*cda5da8dSAndroid Build Coastguard Worker
207*cda5da8dSAndroid Build Coastguard Worker    def first(self):
208*cda5da8dSAndroid Build Coastguard Worker        (key, value) = self.dict.first()
209*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO(value)
210*cda5da8dSAndroid Build Coastguard Worker        return (key.decode(self.keyencoding), Unpickler(f).load())
211*cda5da8dSAndroid Build Coastguard Worker
212*cda5da8dSAndroid Build Coastguard Worker    def last(self):
213*cda5da8dSAndroid Build Coastguard Worker        (key, value) = self.dict.last()
214*cda5da8dSAndroid Build Coastguard Worker        f = BytesIO(value)
215*cda5da8dSAndroid Build Coastguard Worker        return (key.decode(self.keyencoding), Unpickler(f).load())
216*cda5da8dSAndroid Build Coastguard Worker
217*cda5da8dSAndroid Build Coastguard Worker
218*cda5da8dSAndroid Build Coastguard Workerclass DbfilenameShelf(Shelf):
219*cda5da8dSAndroid Build Coastguard Worker    """Shelf implementation using the "dbm" generic dbm interface.
220*cda5da8dSAndroid Build Coastguard Worker
221*cda5da8dSAndroid Build Coastguard Worker    This is initialized with the filename for the dbm database.
222*cda5da8dSAndroid Build Coastguard Worker    See the module's __doc__ string for an overview of the interface.
223*cda5da8dSAndroid Build Coastguard Worker    """
224*cda5da8dSAndroid Build Coastguard Worker
225*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, filename, flag='c', protocol=None, writeback=False):
226*cda5da8dSAndroid Build Coastguard Worker        import dbm
227*cda5da8dSAndroid Build Coastguard Worker        Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback)
228*cda5da8dSAndroid Build Coastguard Worker
229*cda5da8dSAndroid Build Coastguard Worker
230*cda5da8dSAndroid Build Coastguard Workerdef open(filename, flag='c', protocol=None, writeback=False):
231*cda5da8dSAndroid Build Coastguard Worker    """Open a persistent dictionary for reading and writing.
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Worker    The filename parameter is the base filename for the underlying
234*cda5da8dSAndroid Build Coastguard Worker    database.  As a side-effect, an extension may be added to the
235*cda5da8dSAndroid Build Coastguard Worker    filename and more than one file may be created.  The optional flag
236*cda5da8dSAndroid Build Coastguard Worker    parameter has the same interpretation as the flag parameter of
237*cda5da8dSAndroid Build Coastguard Worker    dbm.open(). The optional protocol parameter specifies the
238*cda5da8dSAndroid Build Coastguard Worker    version of the pickle protocol.
239*cda5da8dSAndroid Build Coastguard Worker
240*cda5da8dSAndroid Build Coastguard Worker    See the module's __doc__ string for an overview of the interface.
241*cda5da8dSAndroid Build Coastguard Worker    """
242*cda5da8dSAndroid Build Coastguard Worker
243*cda5da8dSAndroid Build Coastguard Worker    return DbfilenameShelf(filename, flag, protocol, writeback)
244