json-stream 2.5.0__tar.gz → 2.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {json_stream-2.5.0/src/json_stream.egg-info → json_stream-2.5.2}/PKG-INFO +1 -1
  2. {json_stream-2.5.0 → json_stream-2.5.2}/pyproject.toml +1 -1
  3. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/base.py +2 -2
  4. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/loader.py +4 -1
  5. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_load_many.py +12 -0
  6. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_loader.py +17 -3
  7. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_tokenizer.py +28 -0
  8. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_visitor.py +22 -0
  9. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tokenizer.py +65 -1
  10. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/visitor.py +4 -1
  11. {json_stream-2.5.0 → json_stream-2.5.2/src/json_stream.egg-info}/PKG-INFO +1 -1
  12. {json_stream-2.5.0 → json_stream-2.5.2}/LICENSE.txt +0 -0
  13. {json_stream-2.5.0 → json_stream-2.5.2}/README.md +0 -0
  14. {json_stream-2.5.0 → json_stream-2.5.2}/setup.cfg +0 -0
  15. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/__init__.py +0 -0
  16. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/dump/__init__.py +0 -0
  17. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/dump/tests/__init__.py +0 -0
  18. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/dump/tests/test_dump.py +0 -0
  19. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/dump/threading.py +0 -0
  20. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/httpx/__init__.py +0 -0
  21. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/httpx/tests/__init__.py +0 -0
  22. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/httpx/tests/test_httpx.py +0 -0
  23. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/iterators.py +0 -0
  24. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/requests/__init__.py +0 -0
  25. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/requests/tests/__init__.py +0 -0
  26. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/requests/tests/test_requests.py +0 -0
  27. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/select_tokenizer.py +0 -0
  28. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/__init__.py +0 -0
  29. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_binary_resumption.py +0 -0
  30. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_buffering.py +0 -0
  31. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_collection_abcs.py +0 -0
  32. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_iterators.py +0 -0
  33. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_tokenizer_integration.py +0 -0
  34. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_util.py +0 -0
  35. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_visit_many.py +0 -0
  36. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/tests/test_writer.py +0 -0
  37. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/util.py +0 -0
  38. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream/writer.py +0 -0
  39. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream.egg-info/SOURCES.txt +0 -0
  40. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream.egg-info/dependency_links.txt +0 -0
  41. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream.egg-info/requires.txt +0 -0
  42. {json_stream-2.5.0 → json_stream-2.5.2}/src/json_stream.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: json-stream
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: Streaming JSON encoder and decoder
5
5
  Author-email: Jamie Cockburn <jamie_cockburn@hotmail.co.uk>
6
6
  License: Copyright (c) 2020 Jamie Cockburn
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "json-stream"
8
- version = "2.5.0"
8
+ version = "2.5.2"
9
9
  authors = [
10
10
  {name = "Jamie Cockburn", email="jamie_cockburn@hotmail.co.uk"},
11
11
  ]
@@ -81,10 +81,10 @@ class StreamingJSONBase(ABC):
81
81
  raise NotImplementedError() # pragma: no cover
82
82
 
83
83
  def __copy__(self):
84
- raise copy.Error("Copying json_steam objects leads to a bad time")
84
+ raise copy.Error("Copying json_stream objects leads to a bad time")
85
85
 
86
86
  def __deepcopy__(self, memo):
87
- raise copy.Error("Copying json_steam objects leads to a bad time")
87
+ raise copy.Error("Copying json_stream objects leads to a bad time")
88
88
 
89
89
 
90
90
  class PersistentStreamingJSONBase(StreamingJSONBase, ABC):
@@ -4,7 +4,10 @@ from json_stream.select_tokenizer import default_tokenizer
4
4
 
5
5
 
6
6
  def load(fp_or_iterable, persistent=False, tokenizer=default_tokenizer, **tokenizer_kwargs):
7
- return next(load_many(fp_or_iterable, persistent, tokenizer, **tokenizer_kwargs))
7
+ try:
8
+ return next(load_many(fp_or_iterable, persistent, tokenizer, **tokenizer_kwargs))
9
+ except StopIteration:
10
+ raise ValueError('No JSON value found') from None
8
11
 
9
12
 
10
13
  def load_many(fp_or_iterable, persistent=False, tokenizer=default_tokenizer, **tokenizer_kwargs):
@@ -7,6 +7,18 @@ from json_stream import to_standard_types
7
7
 
8
8
 
9
9
  class TestLoadMany(TestCase):
10
+ def test_load_many_without_json_value(self):
11
+ for persistent in (False, True):
12
+ for text in ('', ' \t\r\n'):
13
+ sources = [
14
+ StringIO(text), BytesIO(text.encode()),
15
+ [text], [text.encode()],
16
+ list(text), [char.encode() for char in text],
17
+ ]
18
+ for source in sources:
19
+ with self.subTest(persistent=persistent, text=text, source=source):
20
+ self.assertEqual(list(json_stream.load_many(source, persistent=persistent)), [])
21
+
10
22
  def test_load_many(self):
11
23
  # NDJSON-like: one JSON value per line
12
24
  payload = "\n".join([
@@ -1,6 +1,6 @@
1
1
  import copy
2
2
  import json
3
- from io import StringIO
3
+ from io import BytesIO, StringIO
4
4
 
5
5
  from json_stream import load
6
6
  from json_stream.base import (
@@ -14,6 +14,19 @@ from json_stream.tests import JSONLoadTestCase
14
14
 
15
15
 
16
16
  class TestLoader(JSONLoadTestCase):
17
+ def test_load_without_json_value(self):
18
+ for persistent in (False, True):
19
+ for text in ('', ' \t\r\n'):
20
+ sources = [
21
+ StringIO(text), BytesIO(text.encode()),
22
+ [text], [text.encode()],
23
+ list(text), [char.encode() for char in text],
24
+ ]
25
+ for source in sources:
26
+ with self.subTest(persistent=persistent, text=text, source=source):
27
+ with self.assertRaisesRegex(ValueError, 'No JSON value found'):
28
+ load(source, persistent=persistent)
29
+
17
30
  def test_load_empty_object(self):
18
31
  obj = {}
19
32
  self._test_object(obj, persistent=True)
@@ -177,9 +190,9 @@ class TestLoader(JSONLoadTestCase):
177
190
 
178
191
  def test_not_copiable(self):
179
192
  json_str = '[["a"], "b", "c"]'
180
- with self.assertRaisesRegex(copy.Error, "^Copying json_steam objects leads to a bad time$"):
193
+ with self.assertRaisesRegex(copy.Error, "^Copying json_stream objects leads to a bad time$"):
181
194
  copy.copy(load(StringIO(json_str)))
182
- with self.assertRaisesRegex(copy.Error, "^Copying json_steam objects leads to a bad time$"):
195
+ with self.assertRaisesRegex(copy.Error, "^Copying json_stream objects leads to a bad time$"):
183
196
  copy.deepcopy(load(StringIO(json_str)))
184
197
 
185
198
  def test_transient_to_persistent(self):
@@ -218,6 +231,7 @@ class TestLoader(JSONLoadTestCase):
218
231
 
219
232
  def test_primitives(self):
220
233
  primitives = (
234
+ ("null", None),
221
235
  ("true", True),
222
236
  ("false", False),
223
237
  ("123", 123),
@@ -5,6 +5,7 @@ https://github.com/danielyule/naya
5
5
 
6
6
  Copyright (c) 2019 Daniel Yule
7
7
  """
8
+ import math
8
9
  import re
9
10
  from io import StringIO
10
11
  from unittest import TestCase
@@ -59,6 +60,33 @@ class TestJsonTokenization(TestCase):
59
60
  self.assertRaises(ValueError, self.tokenize_sequence, "3.6ea")
60
61
  self.assertRaises(ValueError, self.tokenize_sequence, "67.8e+a")
61
62
 
63
+ def test_nan_infinity_parsing(self):
64
+ token_list = self.tokenize_sequence("NaN")
65
+ self.assertEqual(1, len(token_list))
66
+ ttype, token = token_list[0]
67
+ self.assertTrue(math.isnan(token))
68
+ self.assertEqual(ttype, TokenType.NUMBER)
69
+
70
+ self.assertNumberEquals(float("inf"), "Infinity")
71
+ self.assertNumberEquals(float("-inf"), "-Infinity")
72
+
73
+ self.assertRaises(ValueError, self.tokenize_sequence, "Na")
74
+ self.assertRaises(ValueError, self.tokenize_sequence, "Nax")
75
+ self.assertRaises(ValueError, self.tokenize_sequence, "In")
76
+ self.assertRaises(ValueError, self.tokenize_sequence, "Inf")
77
+ self.assertRaises(ValueError, self.tokenize_sequence, "Infi")
78
+ self.assertRaises(ValueError, self.tokenize_sequence, "Infin")
79
+ self.assertRaises(ValueError, self.tokenize_sequence, "Infini")
80
+ self.assertRaises(ValueError, self.tokenize_sequence, "Infinit")
81
+ self.assertRaises(ValueError, self.tokenize_sequence, "Infinityx")
82
+ self.assertRaises(ValueError, self.tokenize_sequence, "-In")
83
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Inf")
84
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Infi")
85
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Infin")
86
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Infini")
87
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Infinit")
88
+ self.assertRaises(ValueError, self.tokenize_sequence, "-Infinityx")
89
+
62
90
  def test_operator_parsing(self):
63
91
  self.assertOperatorEquals("{", "{")
64
92
  self.assertOperatorEquals("}", "}")
@@ -22,6 +22,28 @@ class TestVisitor(TestCase):
22
22
  json_stream.visit(StringIO("1"), lambda a, b: visited.append((a, b)))
23
23
  self.assertListEqual([(1, ())], visited)
24
24
 
25
+ def test_visitor_without_json_value(self):
26
+ for text in ('', ' \t\r\n'):
27
+ sources = [
28
+ StringIO(text), BytesIO(text.encode()),
29
+ [text], [text.encode()],
30
+ list(text), [char.encode() for char in text],
31
+ ]
32
+ for source in sources:
33
+ with self.subTest(text=text, source=source):
34
+ visited = []
35
+ with self.assertRaisesRegex(ValueError, 'No JSON value found'):
36
+ json_stream.visit(source, lambda a, b: visited.append((a, b)))
37
+ self.assertListEqual([], visited)
38
+
39
+ def test_visit_many_without_json_value(self):
40
+ for text in ('', ' \t\r\n'):
41
+ for source in (StringIO(text), BytesIO(text.encode())):
42
+ with self.subTest(text=text, source=source):
43
+ visited = []
44
+ self.assertListEqual([], list(json_stream.visit_many(source, lambda a, b: visited.append((a, b)))))
45
+ self.assertListEqual([], visited)
46
+
25
47
  def _assert_data_okay(self, visited):
26
48
  self.assertListEqual([
27
49
  (1, ('x',)),
@@ -45,6 +45,15 @@ class State:
45
45
  UNICODE_SURROGATE_START = 23
46
46
  UNICODE_SURROGATE_STRING_ESCAPE = 24
47
47
  UNICODE_SURROGATE = 25
48
+ NAN_1 = 26
49
+ NAN_2 = 27
50
+ INF_1 = 28
51
+ INF_2 = 29
52
+ INF_3 = 30
53
+ INF_4 = 31
54
+ INF_5 = 32
55
+ INF_6 = 33
56
+ INF_7 = 34
48
57
 
49
58
 
50
59
  class SpecialChar:
@@ -130,6 +139,10 @@ def tokenize(stream):
130
139
  next_state = State.TRUE_1
131
140
  elif char == "n":
132
141
  next_state = State.NULL_1
142
+ elif char == "N":
143
+ next_state = State.NAN_1
144
+ elif char == "I":
145
+ next_state = State.INF_1
133
146
  elif not char.isspace() and not char == SpecialChar.EOF:
134
147
  raise ValueError("Invalid JSON character: '{0}'".format(char))
135
148
  elif state == State.INTEGER:
@@ -169,8 +182,10 @@ def tokenize(stream):
169
182
  elif char in "123456789":
170
183
  next_state = State.INTEGER
171
184
  add_char = True
185
+ elif char == "I":
186
+ next_state = State.INF_1
172
187
  else:
173
- raise ValueError("A - must be followed by a digit. Got '{0}'".format(char))
188
+ raise ValueError("A - must be followed by a digit or Infinity. Got '{0}'".format(char))
174
189
  elif state == State.INTEGER_EXP_0:
175
190
  if char == "+" or char == "-" or char in "0123456789":
176
191
  next_state = State.INTEGER_EXP
@@ -262,6 +277,55 @@ def tokenize(stream):
262
277
  now_token = (TokenType.NULL, None)
263
278
  else:
264
279
  raise ValueError("Invalid JSON character: '{0}'".format(char))
280
+ elif state == State.NAN_1:
281
+ if char == "a":
282
+ next_state = State.NAN_2
283
+ else:
284
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
285
+ elif state == State.NAN_2:
286
+ if char == "N":
287
+ next_state = State.WHITESPACE
288
+ completed = True
289
+ now_token = (TokenType.NUMBER, float("NaN"))
290
+ else:
291
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
292
+ elif state == State.INF_1:
293
+ if char == "n":
294
+ next_state = State.INF_2
295
+ else:
296
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
297
+ elif state == State.INF_2:
298
+ if char == "f":
299
+ next_state = State.INF_3
300
+ else:
301
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
302
+ elif state == State.INF_3:
303
+ if char == "i":
304
+ next_state = State.INF_4
305
+ else:
306
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
307
+ elif state == State.INF_4:
308
+ if char == "n":
309
+ next_state = State.INF_5
310
+ else:
311
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
312
+ elif state == State.INF_5:
313
+ if char == "i":
314
+ next_state = State.INF_6
315
+ else:
316
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
317
+ elif state == State.INF_6:
318
+ if char == "t":
319
+ next_state = State.INF_7
320
+ else:
321
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
322
+ elif state == State.INF_7:
323
+ if char == "y":
324
+ next_state = State.WHITESPACE
325
+ completed = True
326
+ now_token = (TokenType.NUMBER, float("".join(token) + "Infinity"))
327
+ else:
328
+ raise ValueError("Invalid JSON character: '{0}'".format(char))
265
329
  elif state == State.STRING:
266
330
  if char == "\"":
267
331
  completed = True
@@ -34,4 +34,7 @@ def visit_many(fp_or_iterator, visitor, tokenizer=default_tokenizer, **tokenizer
34
34
 
35
35
 
36
36
  def visit(fp_or_iterator, visitor, tokenizer=default_tokenizer, **tokenizer_kwargs):
37
- next(visit_many(fp_or_iterator, visitor, tokenizer, **tokenizer_kwargs))
37
+ try:
38
+ next(visit_many(fp_or_iterator, visitor, tokenizer, **tokenizer_kwargs))
39
+ except StopIteration:
40
+ raise ValueError('No JSON value found') from None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: json-stream
3
- Version: 2.5.0
3
+ Version: 2.5.2
4
4
  Summary: Streaming JSON encoder and decoder
5
5
  Author-email: Jamie Cockburn <jamie_cockburn@hotmail.co.uk>
6
6
  License: Copyright (c) 2020 Jamie Cockburn
File without changes
File without changes
File without changes