singer-python 6.2.3__tar.gz → 6.4.0__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.
- {singer_python-6.2.3/singer_python.egg-info → singer_python-6.4.0}/PKG-INFO +1 -1
- {singer_python-6.2.3 → singer_python-6.4.0}/setup.py +1 -1
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/bookmarks.py +1 -3
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/messages.py +9 -4
- {singer_python-6.2.3 → singer_python-6.4.0/singer_python.egg-info}/PKG-INFO +1 -1
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_transform.py +59 -1
- {singer_python-6.2.3 → singer_python-6.4.0}/LICENSE +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/README.md +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/setup.cfg +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/__init__.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/catalog.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/exceptions.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/logger.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/logging.conf +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/metadata.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/metrics.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/requests.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/schema.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/schema_generation.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/statediff.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/transform.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer/utils.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer_python.egg-info/SOURCES.txt +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer_python.egg-info/dependency_links.txt +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer_python.egg-info/requires.txt +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/singer_python.egg-info/top_level.txt +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/__init__.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_bookmarks.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_catalog.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_exceptions.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_metadata.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_metrics.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_schema.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_schema_generation.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_singer.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_statediff.py +0 -0
- {singer_python-6.2.3 → singer_python-6.4.0}/tests/test_utils.py +0 -0
|
@@ -31,9 +31,7 @@ def set_offset(state, tap_stream_id, offset_key, offset_value):
|
|
|
31
31
|
return state
|
|
32
32
|
|
|
33
33
|
def clear_offset(state, tap_stream_id):
|
|
34
|
-
|
|
35
|
-
state['bookmarks'][tap_stream_id]["offset"] = {}
|
|
36
|
-
return state
|
|
34
|
+
return clear_bookmark(state, tap_stream_id, "offset")
|
|
37
35
|
|
|
38
36
|
def get_offset(state, tap_stream_id, default=None):
|
|
39
37
|
return state.get('bookmarks', {}).get(tap_stream_id, {}).get("offset", default)
|
|
@@ -218,12 +218,17 @@ def parse_message(msg):
|
|
|
218
218
|
return None
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
def format_message(message, ensure_ascii=True):
|
|
222
|
-
return json.dumps(
|
|
221
|
+
def format_message(message, ensure_ascii=True, allow_nan=False):
|
|
222
|
+
return json.dumps(
|
|
223
|
+
message.asdict(),
|
|
224
|
+
use_decimal=True,
|
|
225
|
+
ensure_ascii=ensure_ascii,
|
|
226
|
+
allow_nan=allow_nan
|
|
227
|
+
)
|
|
223
228
|
|
|
224
229
|
|
|
225
|
-
def write_message(message, ensure_ascii=True):
|
|
226
|
-
sys.stdout.write(format_message(message, ensure_ascii=ensure_ascii) + '\n')
|
|
230
|
+
def write_message(message, ensure_ascii=True, allow_nan=False):
|
|
231
|
+
sys.stdout.write(format_message(message, ensure_ascii=ensure_ascii, allow_nan=allow_nan) + '\n')
|
|
227
232
|
sys.stdout.flush()
|
|
228
233
|
|
|
229
234
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import sys
|
|
1
3
|
import unittest
|
|
2
4
|
import decimal
|
|
5
|
+
import simplejson as json
|
|
6
|
+
import singer.messages as messages
|
|
3
7
|
from singer import transform
|
|
4
8
|
from singer.transform import *
|
|
5
9
|
|
|
6
|
-
|
|
7
10
|
class TestTransform(unittest.TestCase):
|
|
8
11
|
def test_integer_transform(self):
|
|
9
12
|
schema = {'type': 'integer'}
|
|
@@ -486,3 +489,58 @@ class TestPatternProperties(unittest.TestCase):
|
|
|
486
489
|
dict_value = {"name": "chicken", "unit_cost": 1.45, "SKU": '123456'}
|
|
487
490
|
expected = dict(dict_value)
|
|
488
491
|
self.assertEqual(expected, transform(dict_value, schema))
|
|
492
|
+
|
|
493
|
+
class DummyMessage:
|
|
494
|
+
"""A dummy message object with an asdict() method."""
|
|
495
|
+
def __init__(self, value):
|
|
496
|
+
self.value = value
|
|
497
|
+
|
|
498
|
+
def asdict(self):
|
|
499
|
+
return {"value": self.value}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class TestAllowNan(unittest.TestCase):
|
|
503
|
+
"""Unit tests for allow_nan support in singer.messages."""
|
|
504
|
+
|
|
505
|
+
def test_format_message_allow_nan_true(self):
|
|
506
|
+
"""Should serialize NaN successfully when allow_nan=True."""
|
|
507
|
+
msg = DummyMessage(float("nan"))
|
|
508
|
+
result = messages.format_message(msg, allow_nan=True)
|
|
509
|
+
|
|
510
|
+
# The output JSON should contain NaN literal (not quoted)
|
|
511
|
+
self.assertIn("NaN", result)
|
|
512
|
+
|
|
513
|
+
# Replace NaN with null to make it valid JSON for parsing check
|
|
514
|
+
json.loads(result.replace("NaN", "null"))
|
|
515
|
+
|
|
516
|
+
def test_format_message_allow_nan_false(self):
|
|
517
|
+
"""Should raise ValueError when allow_nan=False and value is NaN."""
|
|
518
|
+
msg = DummyMessage(float("nan"))
|
|
519
|
+
with self.assertRaises(ValueError):
|
|
520
|
+
messages.format_message(msg, allow_nan=False)
|
|
521
|
+
|
|
522
|
+
def test_write_message_allow_nan_true(self):
|
|
523
|
+
"""Should write to stdout successfully when allow_nan=True."""
|
|
524
|
+
msg = DummyMessage(float("nan"))
|
|
525
|
+
fake_stdout = io.StringIO()
|
|
526
|
+
original_stdout = sys.stdout
|
|
527
|
+
sys.stdout = fake_stdout
|
|
528
|
+
try:
|
|
529
|
+
messages.write_message(msg, allow_nan=True)
|
|
530
|
+
output = fake_stdout.getvalue()
|
|
531
|
+
self.assertIn("NaN", output)
|
|
532
|
+
self.assertTrue(output.endswith("\n"))
|
|
533
|
+
finally:
|
|
534
|
+
sys.stdout = original_stdout
|
|
535
|
+
|
|
536
|
+
def test_write_message_allow_nan_false(self):
|
|
537
|
+
"""Should raise ValueError when allow_nan=False and message has NaN."""
|
|
538
|
+
msg = DummyMessage(float("nan"))
|
|
539
|
+
fake_stdout = io.StringIO()
|
|
540
|
+
original_stdout = sys.stdout
|
|
541
|
+
sys.stdout = fake_stdout
|
|
542
|
+
try:
|
|
543
|
+
with self.assertRaises(ValueError):
|
|
544
|
+
messages.write_message(msg, allow_nan=False)
|
|
545
|
+
finally:
|
|
546
|
+
sys.stdout = original_stdout
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|