Skip to content

Commit a1b5050

Browse files
authored
Merge pull request #102 from dhalbert/cleanup
Remove pre 7.0 compatibility; other cleanup
2 parents 2705656 + b260fc6 commit a1b5050

File tree

4 files changed

+49
-103
lines changed

4 files changed

+49
-103
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
repos:
66
- repo: https://github.com/python/black
7-
rev: 20.8b1
7+
rev: 22.1.0
88
hooks:
99
- id: black
1010
- repo: https://github.com/fsfe/reuse-tool

adafruit_requests.py

Lines changed: 44 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,14 @@
3939
import errno
4040
import sys
4141

42+
import json as json_module
43+
4244
if sys.implementation.name == "circuitpython":
4345

4446
def cast(_t, value):
4547
"""No-op shim for the typing.cast() function which is not available in CircuitPython."""
4648
return value
4749

48-
4950
else:
5051
from ssl import SSLContext
5152
from types import ModuleType, TracebackType
@@ -148,16 +149,6 @@ def TLS_MODE(self) -> int: # pylint: disable=invalid-name
148149
SSLContextType = Union[SSLContext, "_FakeSSLContext"]
149150

150151

151-
# CircuitPython 6.0 does not have the bytearray.split method.
152-
# This function emulates buf.split(needle)[0], which is the functionality
153-
# required.
154-
def _buffer_split0(buf: Union[bytes, bytearray], needle: Union[bytes, bytearray]):
155-
index = buf.find(needle)
156-
if index == -1:
157-
return buf
158-
return buf[:index]
159-
160-
161152
class _RawResponse:
162153
def __init__(self, response: "Response") -> None:
163154
self._response = response
@@ -177,10 +168,6 @@ def readinto(self, buf: bytearray) -> int:
177168
return self._response._readinto(buf) # pylint: disable=protected-access
178169

179170

180-
class _SendFailed(Exception):
181-
"""Custom exception to abort sending a request."""
182-
183-
184171
class OutOfRetries(Exception):
185172
"""Raised when requests has retried to make a request unsuccessfully."""
186173

@@ -240,56 +227,25 @@ def _recv_into(self, buf: bytearray, size: int = 0) -> int:
240227
return read_size
241228
return cast("SupportsRecvInto", self.socket).recv_into(buf, size)
242229

243-
@staticmethod
244-
def _find(buf: bytes, needle: bytes, start: int, end: int) -> int:
245-
if hasattr(buf, "find"):
246-
return buf.find(needle, start, end)
247-
result = -1
248-
i = start
249-
while i < end:
250-
j = 0
251-
while j < len(needle) and i + j < end and buf[i + j] == needle[j]:
252-
j += 1
253-
if j == len(needle):
254-
result = i
255-
break
256-
i += 1
257-
258-
return result
259-
260-
def _readto(self, first: bytes, second: bytes = b"") -> bytes:
230+
def _readto(self, stop: bytes) -> bytearray:
261231
buf = self._receive_buffer
262232
end = self._received_length
263233
while True:
264-
firsti = self._find(buf, first, 0, end)
265-
secondi = -1
266-
if second:
267-
secondi = self._find(buf, second, 0, end)
268-
269-
i = -1
270-
needle_len = 0
271-
if firsti >= 0:
272-
i = firsti
273-
needle_len = len(first)
274-
if secondi >= 0 and (firsti < 0 or secondi < firsti):
275-
i = secondi
276-
needle_len = len(second)
234+
i = buf.find(stop, 0, end)
277235
if i >= 0:
236+
# Stop was found. Return everything up to but not including stop.
278237
result = buf[:i]
279-
new_start = i + needle_len
280-
281-
if i + needle_len <= end:
282-
new_end = end - new_start
283-
buf[:new_end] = buf[new_start:end]
284-
self._received_length = new_end
238+
new_start = i + len(stop)
239+
# Remove everything up to and including stop from the buffer.
240+
new_end = end - new_start
241+
buf[:new_end] = buf[new_start:end]
242+
self._received_length = new_end
285243
return result
286244

287-
# Not found so load more.
288-
245+
# Not found so load more bytes.
289246
# If our buffer is full, then make it bigger to load more.
290247
if end == len(buf):
291-
new_size = len(buf) + 32
292-
new_buf = bytearray(new_size)
248+
new_buf = bytearray(len(buf) + 32)
293249
new_buf[: len(buf)] = buf
294250
buf = new_buf
295251
self._receive_buffer = buf
@@ -300,8 +256,6 @@ def _readto(self, first: bytes, second: bytes = b"") -> bytes:
300256
return buf[:end]
301257
end += read
302258

303-
return b""
304-
305259
def _read_from_buffer(
306260
self, buf: Optional[bytearray] = None, nbytes: Optional[int] = None
307261
) -> int:
@@ -333,7 +287,7 @@ def _readinto(self, buf: bytearray) -> int:
333287
# Consume trailing \r\n for chunks 2+
334288
if self._remaining == 0:
335289
self._throw_away(2)
336-
chunk_header = _buffer_split0(self._readto(b"\r\n"), b";")
290+
chunk_header = bytes(self._readto(b"\r\n")).split(b";", 1)[0]
337291
http_chunk_size = int(bytes(chunk_header), 16)
338292
if http_chunk_size == 0:
339293
self._chunked = False
@@ -374,7 +328,7 @@ def close(self) -> None:
374328
self._throw_away(self._remaining)
375329
elif self._chunked:
376330
while True:
377-
chunk_header = _buffer_split0(self._readto(b"\r\n"), b";")
331+
chunk_header = bytes(self._readto(b"\r\n")).split(b";", 1)[0]
378332
chunk_size = int(bytes(chunk_header), 16)
379333
if chunk_size == 0:
380334
break
@@ -392,11 +346,10 @@ def _parse_headers(self) -> None:
392346
Expects first line of HTTP request/response to have been read already.
393347
"""
394348
while True:
395-
title = self._readto(b": ", b"\r\n")
396-
if not title:
349+
header = self._readto(b"\r\n")
350+
if not header:
397351
break
398-
399-
content = self._readto(b"\r\n")
352+
title, content = bytes(header).split(b": ", 1)
400353
if title and content:
401354
# enforce that all headers are lowercase
402355
title = str(title, "utf-8").lower()
@@ -407,6 +360,17 @@ def _parse_headers(self) -> None:
407360
self._chunked = content.strip().lower() == "chunked"
408361
self._headers[title] = content
409362

363+
def _validate_not_gzip(self) -> None:
364+
"""gzip encoding is not supported. Raise an exception if found."""
365+
if (
366+
"content-encoding" in self.headers
367+
and self.headers["content-encoding"] == "gzip"
368+
):
369+
raise ValueError(
370+
"Content-encoding is gzip, data cannot be accessed as json or text. "
371+
"Use content property to access raw bytes."
372+
)
373+
410374
@property
411375
def headers(self) -> Dict[str, str]:
412376
"""
@@ -435,22 +399,13 @@ def text(self) -> str:
435399
return self._cached
436400
raise RuntimeError("Cannot access text after getting content or json")
437401

438-
if (
439-
"content-encoding" in self.headers
440-
and self.headers["content-encoding"] == "gzip"
441-
):
442-
raise ValueError(
443-
"Content-encoding is gzip, data cannot be accessed as json or text. "
444-
"Use content property to access raw bytes."
445-
)
402+
self._validate_not_gzip()
403+
446404
self._cached = str(self.content, self.encoding)
447405
return self._cached
448406

449407
def json(self) -> Any:
450408
"""The HTTP content, parsed into a json dictionary"""
451-
# pylint: disable=import-outside-toplevel
452-
import json
453-
454409
# The cached JSON will be a list or dictionary.
455410
if self._cached:
456411
if isinstance(self._cached, (list, dict)):
@@ -459,20 +414,9 @@ def json(self) -> Any:
459414
if not self._raw:
460415
self._raw = _RawResponse(self)
461416

462-
if (
463-
"content-encoding" in self.headers
464-
and self.headers["content-encoding"] == "gzip"
465-
):
466-
raise ValueError(
467-
"Content-encoding is gzip, data cannot be accessed as json or text. "
468-
"Use content property to access raw bytes."
469-
)
470-
try:
471-
obj = json.load(self._raw)
472-
except OSError:
473-
# <5.3.1 doesn't piecemeal load json from any object with readinto so load the whole
474-
# string.
475-
obj = json.loads(self._raw.read())
417+
self._validate_not_gzip()
418+
419+
obj = json_module.load(self._raw)
476420
if not self._cached:
477421
self._cached = obj
478422
self.close()
@@ -599,12 +543,19 @@ def _send(socket: SocketType, data: bytes):
599543
# ESP32SPI sockets raise a RuntimeError when unable to send.
600544
try:
601545
sent = socket.send(data[total_sent:])
602-
except RuntimeError:
603-
sent = 0
546+
except OSError as exc:
547+
if exc.errno == errno.EAGAIN:
548+
# Can't send right now (e.g., no buffer space), try again.
549+
continue
550+
# Some worse error.
551+
raise
552+
except RuntimeError as exc:
553+
raise OSError(errno.EIO) from exc
604554
if sent is None:
605555
sent = len(data)
606556
if sent == 0:
607-
raise _SendFailed()
557+
# Not EAGAIN; that was already handled.
558+
raise OSError(errno.EIO)
608559
total_sent += sent
609560

610561
def _send_request(
@@ -636,11 +587,6 @@ def _send_request(
636587
self._send(socket, b"\r\n")
637588
if json is not None:
638589
assert data is None
639-
# pylint: disable=import-outside-toplevel
640-
try:
641-
import json as json_module
642-
except ImportError:
643-
import ujson as json_module
644590
data = json_module.dumps(json)
645591
self._send(socket, b"Content-Type: application/json\r\n")
646592
if data:
@@ -711,7 +657,7 @@ def request(
711657
ok = True
712658
try:
713659
self._send_request(socket, host, method, path, headers, data, json)
714-
except (_SendFailed, OSError):
660+
except OSError:
715661
ok = False
716662
if ok:
717663
# Read the H of "HTTP/1.1" to make sure the socket is alive. send can appear to work

tests/legacy_mocket.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
class Mocket: # pylint: disable=too-few-public-methods
18-
""" Mock Socket """
18+
"""Mock Socket"""
1919

2020
def __init__(self, response):
2121
self.settimeout = mock.Mock()

tests/mocket.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
class MocketPool: # pylint: disable=too-few-public-methods
11-
""" Mock SocketPool """
11+
"""Mock SocketPool"""
1212

1313
SOCK_STREAM = 0
1414

@@ -18,7 +18,7 @@ def __init__(self):
1818

1919

2020
class Mocket: # pylint: disable=too-few-public-methods
21-
""" Mock Socket """
21+
"""Mock Socket"""
2222

2323
def __init__(self, response):
2424
self.settimeout = mock.Mock()
@@ -62,7 +62,7 @@ def _recv_into(self, buf, nbytes=0):
6262

6363

6464
class SSLContext: # pylint: disable=too-few-public-methods
65-
""" Mock SSL Context """
65+
"""Mock SSL Context"""
6666

6767
def __init__(self):
6868
self.wrap_socket = mock.Mock(side_effect=self._wrap_socket)

0 commit comments

Comments
 (0)