singer-python 6.2.0__tar.gz → 6.2.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 (37) hide show
  1. {singer_python-6.2.0/singer_python.egg-info → singer_python-6.2.2}/PKG-INFO +1 -1
  2. {singer_python-6.2.0 → singer_python-6.2.2}/setup.py +1 -1
  3. {singer_python-6.2.0 → singer_python-6.2.2}/singer/schema_generation.py +31 -18
  4. {singer_python-6.2.0 → singer_python-6.2.2}/singer/transform.py +4 -2
  5. {singer_python-6.2.0 → singer_python-6.2.2/singer_python.egg-info}/PKG-INFO +1 -1
  6. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_schema_generation.py +13 -17
  7. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_transform.py +2 -2
  8. {singer_python-6.2.0 → singer_python-6.2.2}/LICENSE +0 -0
  9. {singer_python-6.2.0 → singer_python-6.2.2}/README.md +0 -0
  10. {singer_python-6.2.0 → singer_python-6.2.2}/setup.cfg +0 -0
  11. {singer_python-6.2.0 → singer_python-6.2.2}/singer/__init__.py +0 -0
  12. {singer_python-6.2.0 → singer_python-6.2.2}/singer/bookmarks.py +0 -0
  13. {singer_python-6.2.0 → singer_python-6.2.2}/singer/catalog.py +0 -0
  14. {singer_python-6.2.0 → singer_python-6.2.2}/singer/exceptions.py +0 -0
  15. {singer_python-6.2.0 → singer_python-6.2.2}/singer/logger.py +0 -0
  16. {singer_python-6.2.0 → singer_python-6.2.2}/singer/logging.conf +0 -0
  17. {singer_python-6.2.0 → singer_python-6.2.2}/singer/messages.py +0 -0
  18. {singer_python-6.2.0 → singer_python-6.2.2}/singer/metadata.py +0 -0
  19. {singer_python-6.2.0 → singer_python-6.2.2}/singer/metrics.py +0 -0
  20. {singer_python-6.2.0 → singer_python-6.2.2}/singer/requests.py +0 -0
  21. {singer_python-6.2.0 → singer_python-6.2.2}/singer/schema.py +0 -0
  22. {singer_python-6.2.0 → singer_python-6.2.2}/singer/statediff.py +0 -0
  23. {singer_python-6.2.0 → singer_python-6.2.2}/singer/utils.py +0 -0
  24. {singer_python-6.2.0 → singer_python-6.2.2}/singer_python.egg-info/SOURCES.txt +0 -0
  25. {singer_python-6.2.0 → singer_python-6.2.2}/singer_python.egg-info/dependency_links.txt +0 -0
  26. {singer_python-6.2.0 → singer_python-6.2.2}/singer_python.egg-info/requires.txt +0 -0
  27. {singer_python-6.2.0 → singer_python-6.2.2}/singer_python.egg-info/top_level.txt +0 -0
  28. {singer_python-6.2.0 → singer_python-6.2.2}/tests/__init__.py +0 -0
  29. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_bookmarks.py +0 -0
  30. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_catalog.py +0 -0
  31. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_exceptions.py +0 -0
  32. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_metadata.py +0 -0
  33. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_metrics.py +0 -0
  34. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_schema.py +0 -0
  35. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_singer.py +0 -0
  36. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_statediff.py +0 -0
  37. {singer_python-6.2.0 → singer_python-6.2.2}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: singer-python
3
- Version: 6.2.0
3
+ Version: 6.2.2
4
4
  Summary: Singer.io utility library
5
5
  Home-page: http://singer.io
6
6
  Author: Stitch
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
  import subprocess
5
5
 
6
6
  setup(name="singer-python",
7
- version='6.2.0',
7
+ version='6.2.2',
8
8
  description="Singer.io utility library",
9
9
  author="Stitch",
10
10
  classifiers=['Programming Language :: Python :: 3 :: Only'],
@@ -1,6 +1,3 @@
1
- import dateutil.parser
2
-
3
-
4
1
  def add_observation(acc, path):
5
2
 
6
3
  node = acc
@@ -18,19 +15,26 @@ def add_observations(acc, path, data):
18
15
  for key in data:
19
16
  add_observations(acc, path + ["object", key], data[key])
20
17
  elif isinstance(data, list):
18
+ if len(data) == 0:
19
+ add_observations(acc, path + ["array"], None)
21
20
  for item in data:
22
21
  add_observations(acc, path + ["array"], item)
23
22
  elif isinstance(data, str):
24
- # If the string parses as a date, add an observation that its a date
25
23
  try:
26
- data = dateutil.parser.parse(data)
27
- except (dateutil.parser.ParserError, OverflowError):
28
- data = None
29
- if data:
30
- add_observation(acc, path + ["date"])
31
- else:
32
- add_observation(acc, path + ["string"])
33
-
24
+ # If the string parses as a int, add an observation that it's a integer
25
+ int(data)
26
+ add_observation(acc, path + ["integer"])
27
+ return acc
28
+ except (ValueError, TypeError):
29
+ pass
30
+ try:
31
+ # If the string parses as a float, add an observation that it's a number
32
+ float(data)
33
+ add_observation(acc, path + ["number"])
34
+ return acc
35
+ except (ValueError, TypeError):
36
+ pass
37
+ add_observation(acc, path + ["string"])
34
38
  elif isinstance(data, bool):
35
39
  add_observation(acc, path + ["boolean"])
36
40
  elif isinstance(data, int):
@@ -45,9 +49,13 @@ def add_observations(acc, path, data):
45
49
  return acc
46
50
 
47
51
  def to_json_schema(obs):
48
- result = {'type': ['null']}
52
+ types = []
53
+ # add schema types in a specific order to anyOf list
54
+ for key in ['array', 'object', 'number', 'integer', 'boolean', 'string', 'null']:
55
+ if key not in obs:
56
+ continue
49
57
 
50
- for key in obs:
58
+ result = {'type': ['null']}
51
59
 
52
60
  if key == 'object':
53
61
  result['type'] += ['object']
@@ -60,9 +68,6 @@ def to_json_schema(obs):
60
68
  result['type'] += ['array']
61
69
  result['items'] = to_json_schema(obs['array'])
62
70
 
63
- elif key == 'date':
64
- result['type'] += ['string']
65
- result['format'] = 'date-time'
66
71
  elif key == 'string':
67
72
  result['type'] += ['string']
68
73
 
@@ -83,7 +88,15 @@ def to_json_schema(obs):
83
88
  else:
84
89
  raise Exception("Unexpected data type " + key)
85
90
 
86
- return result
91
+ types.append(result)
92
+
93
+ if len(types) == 0:
94
+ return {'type': ['null', 'string']}
95
+
96
+ if len(types) == 1:
97
+ return types[0]
98
+
99
+ return {'anyOf': types}
87
100
 
88
101
  def generate_schema(records):
89
102
  obs = {}
@@ -185,6 +185,8 @@ class Transformer:
185
185
  success, transformed_data = self.transform_recur(data, subschema, path)
186
186
  if success:
187
187
  return success, transformed_data
188
+ else:
189
+ self.errors.pop()
188
190
  else: # pylint: disable=useless-else-on-loop
189
191
  # exhaused all schemas and didn't return, so we failed :-(
190
192
  self.errors.append(Error(path, data, schema, logging_level=LOGGER.level))
@@ -266,13 +268,13 @@ class Transformer:
266
268
  else:
267
269
  return False, None
268
270
 
269
- elif schema.get("format") == "date-time":
271
+ elif typ == "string" and schema.get("format") == "date-time":
270
272
  data = self._transform_datetime(data)
271
273
  if data is None:
272
274
  return False, None
273
275
 
274
276
  return True, data
275
- elif schema.get("format") == "singer.decimal":
277
+ elif typ == "string" and schema.get("format") == "singer.decimal":
276
278
  if data is None:
277
279
  return False, None
278
280
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: singer-python
3
- Version: 6.2.0
3
+ Version: 6.2.2
4
4
  Summary: Singer.io utility library
5
5
  Home-page: http://singer.io
6
6
  Author: Stitch
@@ -10,7 +10,7 @@ class TestSchemaGeneration(unittest.TestCase):
10
10
  'a': {'type': ['null', 'integer']},
11
11
  'b': {'type': ['null', 'string']},
12
12
  'c': {'type': ['null', 'boolean']},
13
- 'dt': {'type': ['null', 'string'], 'format': 'date-time'}
13
+ 'dt': {'type': ['null', 'string']}
14
14
  }
15
15
  }
16
16
  self.assertEqual(expected_schema, generate_schema(records))
@@ -23,24 +23,20 @@ class TestSchemaGeneration(unittest.TestCase):
23
23
  ]
24
24
  expected_schema = {
25
25
  'type': ['null', 'object'],
26
- 'properties': {
27
- 'a': {'type': {'null', 'integer', 'string', 'boolean'}},
28
- 'b': {'type': ['null', 'string']},
29
- 'c': {'type': {'null', 'integer', 'string'}, 'format': 'singer.decimal'},
30
- 'd': {
31
- 'type': {'null', 'array', 'object'},
32
- 'items': {'type': {'null', 'integer', 'string'}},
33
- 'properties': {'one': {'type': ['null', 'integer']},
34
- 'two': {'type': ['null', 'string']}}
35
-
36
- }
37
- }
26
+ 'properties': {'a': {'anyOf': [{'type': ['null', 'integer']},
27
+ {'type': ['null', 'boolean']},
28
+ {'type': ['null', 'string']}]},
29
+ 'b': {'type': ['null', 'string']},
30
+ 'c': {'anyOf': [{'type': ['null', 'string'], 'format': 'singer.decimal'},
31
+ {'type': ['null', 'integer']}]},
32
+ 'd': {'anyOf': [{'type': ['null', 'array'],
33
+ 'items': {'anyOf': [{'type': ['null', 'integer']},
34
+ {'type': ['null', 'string']}]}},
35
+ {'type': ['null', 'object'],
36
+ 'properties': {'one': {'type': ['null', 'integer']},
37
+ 'two': {'type': ['null', 'string']}}}]}}
38
38
  }
39
39
  actual_schema = generate_schema(records)
40
- actual_schema['properties']['a']['type'] = set(actual_schema['properties']['a']['type'])
41
- actual_schema['properties']['c']['type'] = set(actual_schema['properties']['c']['type'])
42
- actual_schema['properties']['d']['type'] = set(actual_schema['properties']['d']['type'])
43
- actual_schema['properties']['d']['items']['type'] = set(actual_schema['properties']['d']['items']['type'])
44
40
  self.assertEqual(expected_schema, actual_schema)
45
41
 
46
42
  def test_nested_structue_schema(self):
@@ -25,7 +25,7 @@ class TestTransform(unittest.TestCase):
25
25
 
26
26
  def test_multi_type_object_transform(self):
27
27
  schema = {"type": ["null", "object", "string"],
28
- "properties": {"whatever": {"type": "date-time",
28
+ "properties": {"whatever": {"type": "string",
29
29
  "format": "date-time"}}}
30
30
  data = {"whatever": "2017-01-01"}
31
31
  expected = {"whatever": "2017-01-01T00:00:00.000000Z"}
@@ -36,7 +36,7 @@ class TestTransform(unittest.TestCase):
36
36
 
37
37
  def test_multi_type_array_transform(self):
38
38
  schema = {"type": ["null", "array", "integer"],
39
- "items": {"type": "date-time", "format": "date-time"}}
39
+ "items": {"type": "string", "format": "date-time"}}
40
40
  data = ["2017-01-01"]
41
41
  expected = ["2017-01-01T00:00:00.000000Z"]
42
42
  self.assertEqual(expected, transform(data, schema))
File without changes
File without changes
File without changes