comparison env/lib/python3.9/site-packages/cachecontrol/filewrapper.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 from io import BytesIO
2
3
4 class CallbackFileWrapper(object):
5 """
6 Small wrapper around a fp object which will tee everything read into a
7 buffer, and when that file is closed it will execute a callback with the
8 contents of that buffer.
9
10 All attributes are proxied to the underlying file object.
11
12 This class uses members with a double underscore (__) leading prefix so as
13 not to accidentally shadow an attribute.
14 """
15
16 def __init__(self, fp, callback):
17 self.__buf = BytesIO()
18 self.__fp = fp
19 self.__callback = callback
20
21 def __getattr__(self, name):
22 # The vaguaries of garbage collection means that self.__fp is
23 # not always set. By using __getattribute__ and the private
24 # name[0] allows looking up the attribute value and raising an
25 # AttributeError when it doesn't exist. This stop thigns from
26 # infinitely recursing calls to getattr in the case where
27 # self.__fp hasn't been set.
28 #
29 # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers
30 fp = self.__getattribute__('_CallbackFileWrapper__fp')
31 return getattr(fp, name)
32
33 def __is_fp_closed(self):
34 try:
35 return self.__fp.fp is None
36 except AttributeError:
37 pass
38
39 try:
40 return self.__fp.closed
41 except AttributeError:
42 pass
43
44 # We just don't cache it then.
45 # TODO: Add some logging here...
46 return False
47
48 def _close(self):
49 if self.__callback:
50 self.__callback(self.__buf.getvalue())
51
52 # We assign this to None here, because otherwise we can get into
53 # really tricky problems where the CPython interpreter dead locks
54 # because the callback is holding a reference to something which
55 # has a __del__ method. Setting this to None breaks the cycle
56 # and allows the garbage collector to do it's thing normally.
57 self.__callback = None
58
59 def read(self, amt=None):
60 data = self.__fp.read(amt)
61 self.__buf.write(data)
62 if self.__is_fp_closed():
63 self._close()
64
65 return data
66
67 def _safe_read(self, amt):
68 data = self.__fp._safe_read(amt)
69 if amt == 2 and data == b'\r\n':
70 # urllib executes this read to toss the CRLF at the end
71 # of the chunk.
72 return data
73
74 self.__buf.write(data)
75 if self.__is_fp_closed():
76 self._close()
77
78 return data