Skip to content

Commit 0757664

Browse files
committed
pylint
1 parent 85c248d commit 0757664

5 files changed

+171
-148
lines changed

Diff for: adafruit_json_stream.py

+152-140
Original file line numberDiff line numberDiff line change
@@ -39,155 +39,167 @@
3939
__version__ = "0.0.0+auto.0"
4040
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_JSON_Stream.git"
4141

42-
class IterToStream:
43-
def __init__(self, data_iter):
44-
self.data_iter = data_iter
45-
self.i = 0
46-
self.chunk = b""
47-
48-
def read(self):
49-
if self.i >= len(self.chunk):
50-
try:
51-
self.chunk = next(self.data_iter)
52-
except StopIteration:
53-
raise EOFError
54-
self.i = 0
55-
c = self.chunk[self.i]
56-
self.i += 1
57-
return c
58-
59-
def fast_forward(self, closer):
60-
closer = ord(closer)
61-
close_stack = [closer]
62-
count = 0
63-
while close_stack:
64-
c = self.read()
65-
count += 1
66-
if c == close_stack[-1]:
67-
close_stack.pop()
68-
elif c == ord("\""):
69-
close_stack.append(ord("\""))
70-
elif close_stack[-1] == ord("\""):
71-
# in a string so ignore [] and {}
72-
pass
73-
elif c in (ord("}"), ord("]")):
74-
# Mismatched list or object means we're done and already past the last comma.
75-
return True
76-
elif c == ord("{"):
77-
close_stack.append(ord("}"))
78-
elif c == ord("["):
79-
close_stack.append(ord("]"))
80-
return False
81-
82-
def next_value(self, endswith):
83-
buf = array.array("B")
84-
endswith = ord(endswith)
85-
in_string = False
86-
while True:
87-
try:
88-
c = self.read()
89-
except EOFError:
90-
c = endswith
91-
if c == endswith or (not in_string and c in (ord("]"), ord("}"))):
92-
if len(buf) == 0:
93-
return None
94-
value_string = bytes(buf).decode("utf-8")
95-
# print(repr(value_string))
96-
return json.loads(value_string)
97-
elif c == ord("{"):
98-
return TransientObject(self)
99-
elif c == ord("["):
100-
return TransientList(self)
101-
else:
102-
if not in_string:
103-
in_string = c == ord("\"")
104-
else:
105-
in_string = c != ord("\"")
106-
buf.append(c)
107-
108-
class Transient:
109-
pass
11042

111-
class TransientList(Transient):
112-
def __init__(self, stream):
113-
self.data = stream
114-
self.done = False
115-
self.active_child = None
116-
117-
def finish(self):
118-
if not self.done:
119-
if self.active_child:
120-
self.active_child.finish()
121-
self.active_child = None
122-
self.data.fast_forward("]")
123-
self.done = True
124-
125-
def __iter__(self):
126-
return self
127-
128-
def __next__(self):
129-
if self.active_child:
130-
self.active_child.finish()
131-
self.done = self.data.fast_forward(",")
132-
self.active_child = None
133-
if self.done:
134-
raise StopIteration()
135-
next_value = self.data.next_value(",")
136-
if next_value is None:
137-
self.done = True
138-
raise StopIteration()
139-
if isinstance(next_value, Transient):
140-
self.active_child = next_value
141-
return next_value
43+
class _IterToStream:
44+
"""Converts an iterator to a JSON data stream."""
45+
46+
def __init__(self, data_iter):
47+
self.data_iter = data_iter
48+
self.i = 0
49+
self.chunk = b""
50+
51+
def read(self):
52+
"""Read the next character from the stream."""
53+
if self.i >= len(self.chunk):
54+
try:
55+
self.chunk = next(self.data_iter)
56+
except StopIteration as exc:
57+
raise EOFError from exc
58+
self.i = 0
59+
char = self.chunk[self.i]
60+
self.i += 1
61+
return char
62+
63+
def fast_forward(self, closer):
64+
"""Read through the stream until the character is ``closer``, ``]``
65+
(ending a list) or ``}`` (ending an object.) Intermediate lists and
66+
objects are skipped."""
67+
closer = ord(closer)
68+
close_stack = [closer]
69+
count = 0
70+
while close_stack:
71+
char = self.read()
72+
count += 1
73+
if char == close_stack[-1]:
74+
close_stack.pop()
75+
elif char == ord('"'):
76+
close_stack.append(ord('"'))
77+
elif close_stack[-1] == ord('"'):
78+
# in a string so ignore [] and {}
79+
pass
80+
elif char in (ord("}"), ord("]")):
81+
# Mismatched list or object means we're done and already past the last comma.
82+
return True
83+
elif char == ord("{"):
84+
close_stack.append(ord("}"))
85+
elif char == ord("["):
86+
close_stack.append(ord("]"))
87+
return False
88+
89+
def next_value(self, endswith=None):
90+
"""Read and parse the next JSON data."""
91+
buf = array.array("B")
92+
if isinstance(endswith, str):
93+
endswith = ord(endswith)
94+
in_string = False
95+
while True:
96+
try:
97+
char = self.read()
98+
except EOFError:
99+
char = endswith
100+
if char == endswith or (not in_string and char in (ord("]"), ord("}"))):
101+
if len(buf) == 0:
102+
return None
103+
value_string = bytes(buf).decode("utf-8")
104+
# print(repr(value_string))
105+
return json.loads(value_string)
106+
if char == ord("{"):
107+
return TransientObject(self)
108+
if char == ord("["):
109+
return TransientList(self)
110+
111+
if not in_string:
112+
in_string = char == ord('"')
113+
else:
114+
in_string = char != ord('"')
115+
buf.append(char)
116+
117+
118+
class Transient: # pylint: disable=too-few-public-methods
119+
"""Transient object representing a JSON object."""
120+
121+
# This is helpful for checking that something is a TransientList or TransientObject.
142122

143-
class TransientObject(Transient):
144-
def __init__(self, stream):
145-
self.data = stream
146-
self.done = False
147-
self.buf = array.array("B")
148123

149-
self.active_child = None
124+
class TransientList(Transient):
125+
"""Transient object that acts like a list through the stream."""
150126

151-
def finish(self):
152-
if not self.done:
153-
if self.active_child:
154-
self.active_child.finish()
127+
def __init__(self, stream):
128+
self.data = stream
129+
self.done = False
155130
self.active_child = None
156-
self.data.fast_forward("}")
157-
self.done = True
158-
159-
def __getitem__(self, key):
160-
if self.active_child:
161-
self.active_child.finish()
162-
self.done = self.data.fast_forward(",")
163-
self.active_child = None
164-
if self.done:
165-
raise KeyError()
166-
# print("get", key)
167-
found = False
168-
while True:
169-
current_key = self.data.next_value(":")
170-
if current_key is None:
171-
# print("object done", self)
131+
132+
def finish(self):
133+
"""Consume all of the characters for this list from the stream."""
134+
if not self.done:
135+
if self.active_child:
136+
self.active_child.finish()
137+
self.active_child = None
138+
self.data.fast_forward("]")
172139
self.done = True
173-
break
174-
if current_key == key:
140+
141+
def __iter__(self):
142+
return self
143+
144+
def __next__(self):
145+
if self.active_child:
146+
self.active_child.finish()
147+
self.done = self.data.fast_forward(",")
148+
self.active_child = None
149+
if self.done:
150+
raise StopIteration()
175151
next_value = self.data.next_value(",")
152+
if next_value is None:
153+
self.done = True
154+
raise StopIteration()
176155
if isinstance(next_value, Transient):
177-
self.active_child = next_value
156+
self.active_child = next_value
178157
return next_value
179-
else:
180-
self.data.fast_forward(",")
181-
raise KeyError()
158+
159+
160+
class TransientObject(Transient):
161+
"""Transient object that acts like a dictionary through the stream."""
162+
163+
def __init__(self, stream):
164+
self.data = stream
165+
self.done = False
166+
self.buf = array.array("B")
167+
168+
self.active_child = None
169+
170+
def finish(self):
171+
"""Consume all of the characters for this object from the stream."""
172+
if not self.done:
173+
if self.active_child:
174+
self.active_child.finish()
175+
self.active_child = None
176+
self.data.fast_forward("}")
177+
self.done = True
178+
179+
def __getitem__(self, key):
180+
if self.active_child:
181+
self.active_child.finish()
182+
self.done = self.data.fast_forward(",")
183+
self.active_child = None
184+
if self.done:
185+
raise KeyError()
186+
187+
while True:
188+
current_key = self.data.next_value(":")
189+
if current_key is None:
190+
# print("object done", self)
191+
self.done = True
192+
break
193+
if current_key == key:
194+
next_value = self.data.next_value(",")
195+
if isinstance(next_value, Transient):
196+
self.active_child = next_value
197+
return next_value
198+
self.data.fast_forward(",")
199+
raise KeyError()
182200

183201

184202
def load(data_iter):
185-
stream = IterToStream(data_iter)
186-
c = stream.read()
187-
if c == ord("{"):
188-
return TransientObject(stream)
189-
elif c == ord("["):
190-
return TransientList(stream)
191-
else:
192-
# TODO: single value?
193-
return None
203+
"""Returns an object to represent the top level of the given JSON stream."""
204+
stream = _IterToStream(data_iter)
205+
return stream.next_value(None)

Diff for: examples/test.py renamed to examples/json_stream_local_file.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
# import adafruit_json_stream as json_stream
2-
import json_stream
1+
# SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
35
import sys
46

7+
import adafruit_json_stream as json_stream
8+
9+
# import json_stream
10+
11+
512
class FakeResponse:
613
def __init__(self, file):
714
self.file = file
@@ -10,7 +17,8 @@ def iter_content(self, chunk_size):
1017
while True:
1118
yield self.file.read(chunk_size)
1219

13-
f = open(sys.argv[1], "rb")
20+
21+
f = open(sys.argv[1], "rb") # pylint: disable=consider-using-with
1422
obj = json_stream.load(FakeResponse(f).iter_content(32))
1523

1624
currently = obj["currently"]

Diff for: examples/json_stream_simpletest.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
#
44
# SPDX-License-Identifier: Unlicense
55

6+
import ssl
7+
import time
68
import adafruit_requests
7-
import adafruit_json_stream as json_stream
89
import socketpool
9-
import ssl
1010
import wifi
11-
import time
11+
import adafruit_json_stream as json_stream
1212

1313
pool = socketpool.SocketPool(wifi.radio)
1414
session = adafruit_requests.Session(pool, ssl.create_default_context())
1515

16-
SCORE_URL = f"http://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard"
16+
SCORE_URL = "http://site.api.espn.com/apis/site/v2/sports/baseball/mlb/scoreboard"
1717

1818
while True:
1919
resp = session.get(SCORE_URL)
@@ -24,4 +24,5 @@
2424
for competition in event["competitions"]:
2525
for competitor in competition["competitors"]:
2626
print(competitor["team"]["displayName"], competitor["score"])
27+
resp.close()
2728
time.sleep(60)

Diff for: examples/weather_data.txt renamed to examples/pirate_weather.txt

+1-1
Large diffs are not rendered by default.

Diff for: examples/pirate_weather.txt.license

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries
2+
SPDX-License-Identifier: Unlicense

0 commit comments

Comments
 (0)