comparison lib/python3.8/site-packages/pip/_vendor/cachecontrol/filewrapper.py @ 0:9e54283cc701 draft

"planemo upload commit d12c32a45bcd441307e632fca6d9af7d60289d44"
author guerler
date Mon, 27 Jul 2020 03:47:31 -0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:9e54283cc701
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
37 except AttributeError:
38 pass
39
40 try:
41 return self.__fp.closed
42
43 except AttributeError:
44 pass
45
46 # We just don't cache it then.
47 # TODO: Add some logging here...
48 return False
49
50 def _close(self):
51 if self.__callback:
52 self.__callback(self.__buf.getvalue())
53
54 # We assign this to None here, because otherwise we can get into
55 # really tricky problems where the CPython interpreter dead locks
56 # because the callback is holding a reference to something which
57 # has a __del__ method. Setting this to None breaks the cycle
58 # and allows the garbage collector to do it's thing normally.
59 self.__callback = None
60
61 def read(self, amt=None):
62 data = self.__fp.read(amt)
63 self.__buf.write(data)
64 if self.__is_fp_closed():
65 self._close()
66
67 return data
68
69 def _safe_read(self, amt):
70 data = self.__fp._safe_read(amt)
71 if amt == 2 and data == b"\r\n":
72 # urllib executes this read to toss the CRLF at the end
73 # of the chunk.
74 return data
75
76 self.__buf.write(data)
77 if self.__is_fp_closed():
78 self._close()
79
80 return data