keboola.component 1.6.1__tar.gz → 1.6.4__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 (18) hide show
  1. {keboola.component-1.6.1/src/keboola.component.egg-info → keboola.component-1.6.4}/PKG-INFO +54 -1
  2. {keboola.component-1.6.1 → keboola.component-1.6.4}/README.md +53 -0
  3. {keboola.component-1.6.1 → keboola.component-1.6.4}/setup.py +1 -1
  4. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/dao.py +63 -34
  5. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/interface.py +9 -1
  6. {keboola.component-1.6.1 → keboola.component-1.6.4/src/keboola.component.egg-info}/PKG-INFO +54 -1
  7. {keboola.component-1.6.1 → keboola.component-1.6.4}/LICENSE +0 -0
  8. {keboola.component-1.6.1 → keboola.component-1.6.4}/setup.cfg +0 -0
  9. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/__init__.py +0 -0
  10. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/base.py +0 -0
  11. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/exceptions.py +0 -0
  12. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/sync_actions.py +0 -0
  13. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola/component/table_schema.py +0 -0
  14. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola.component.egg-info/SOURCES.txt +0 -0
  15. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola.component.egg-info/dependency_links.txt +0 -0
  16. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola.component.egg-info/not-zip-safe +0 -0
  17. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola.component.egg-info/requires.txt +0 -0
  18. {keboola.component-1.6.1 → keboola.component-1.6.4}/src/keboola.component.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: keboola.component
3
- Version: 1.6.1
3
+ Version: 1.6.4
4
4
  Summary: General library for Python applications running in Keboola Connection environment
5
5
  Home-page: https://github.com/keboola/python-component
6
6
  Author: Keboola KDS Team
@@ -231,6 +231,59 @@ possible to create the container for the output table using the `CommonInterface
231
231
 
232
232
  ![TableDefinition dependencies](docs/imgs/TableDefinition_class.png)
233
233
 
234
+ **Table schema example:**
235
+
236
+ ```python
237
+ from keboola.component import CommonInterface
238
+ from keboola.component.dao import ColumnDefinition, DataType, SupportedDataTypes, BaseType
239
+
240
+ # init the interface
241
+ ci = CommonInterface(data_folder_path='data')
242
+
243
+ # create container for the result
244
+ out = ci.create_out_table_definition("testDef",
245
+ columns=['foo', 'bar'],
246
+ destination='some-destination',
247
+ primary_key=['foo'],
248
+ incremental=True,
249
+ delete_where={'column': 'lilly',
250
+ 'values': ['a', 'b'],
251
+ 'operator': 'eq'})
252
+
253
+ # update column
254
+ out.update_column('foo',
255
+ ColumnDefinition(data_types=BaseType(dtype=SupportedDataTypes.INTEGER, length='20')))
256
+
257
+ # add new columns
258
+ out.add_column('note', ColumnDefinition(nullable=False))
259
+ out.add_column('test1')
260
+ out.add_columns(['test2', 'test3', 'test4'])
261
+
262
+ # add new typed column
263
+ out.add_column('id', ColumnDefinition(primary_key=True,
264
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200')})
265
+ )
266
+
267
+ out.add_columns({
268
+ 'phone': ColumnDefinition(primary_key=True,
269
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200'),
270
+ 'bigquery': DataType(dtype="BIGINT")}),
271
+ 'new2': ColumnDefinition(data_types={'snowflake': DataType(dtype="INTEGER", length='200')}),
272
+ })
273
+
274
+ # delete columns
275
+ out.delete_column('bar')
276
+ out.delete_columns(['test2', 'test3'])
277
+
278
+
279
+ # write some content
280
+ with open(out.full_path, 'w') as result:
281
+ result.write('line')
282
+
283
+ # write manifest
284
+ ci.write_manifest(out)
285
+ ```
286
+
234
287
  **Example:**
235
288
 
236
289
  ```python
@@ -212,6 +212,59 @@ possible to create the container for the output table using the `CommonInterface
212
212
 
213
213
  ![TableDefinition dependencies](docs/imgs/TableDefinition_class.png)
214
214
 
215
+ **Table schema example:**
216
+
217
+ ```python
218
+ from keboola.component import CommonInterface
219
+ from keboola.component.dao import ColumnDefinition, DataType, SupportedDataTypes, BaseType
220
+
221
+ # init the interface
222
+ ci = CommonInterface(data_folder_path='data')
223
+
224
+ # create container for the result
225
+ out = ci.create_out_table_definition("testDef",
226
+ columns=['foo', 'bar'],
227
+ destination='some-destination',
228
+ primary_key=['foo'],
229
+ incremental=True,
230
+ delete_where={'column': 'lilly',
231
+ 'values': ['a', 'b'],
232
+ 'operator': 'eq'})
233
+
234
+ # update column
235
+ out.update_column('foo',
236
+ ColumnDefinition(data_types=BaseType(dtype=SupportedDataTypes.INTEGER, length='20')))
237
+
238
+ # add new columns
239
+ out.add_column('note', ColumnDefinition(nullable=False))
240
+ out.add_column('test1')
241
+ out.add_columns(['test2', 'test3', 'test4'])
242
+
243
+ # add new typed column
244
+ out.add_column('id', ColumnDefinition(primary_key=True,
245
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200')})
246
+ )
247
+
248
+ out.add_columns({
249
+ 'phone': ColumnDefinition(primary_key=True,
250
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200'),
251
+ 'bigquery': DataType(dtype="BIGINT")}),
252
+ 'new2': ColumnDefinition(data_types={'snowflake': DataType(dtype="INTEGER", length='200')}),
253
+ })
254
+
255
+ # delete columns
256
+ out.delete_column('bar')
257
+ out.delete_columns(['test2', 'test3'])
258
+
259
+
260
+ # write some content
261
+ with open(out.full_path, 'w') as result:
262
+ result.write('line')
263
+
264
+ # write manifest
265
+ ci.write_manifest(out)
266
+ ```
267
+
215
268
  **Example:**
216
269
 
217
270
  ```python
@@ -13,7 +13,7 @@ project_urls = {
13
13
 
14
14
  setuptools.setup(
15
15
  name="keboola.component",
16
- version="1.6.1",
16
+ version="1.6.4",
17
17
  author="Keboola KDS Team",
18
18
  project_urls=project_urls,
19
19
  setup_requires=['pytest-runner', 'flake8'],
@@ -582,6 +582,7 @@ class SupportedManifestAttributes(SubscriptableDataclass):
582
582
  elif stage == 'in':
583
583
  attributes = self.in_attributes
584
584
  exclude = self.in_legacy_exclude
585
+
585
586
  else:
586
587
  raise ValueError(f'Unsupported stage {stage}')
587
588
 
@@ -597,9 +598,6 @@ class IODefinition(ABC):
597
598
  def __init__(self, full_path):
598
599
  self.full_path = full_path
599
600
 
600
- # infer stage by default
601
- self.__stage = self.__get_stage_inferred()
602
-
603
601
  @classmethod
604
602
  def build_from_manifest(cls,
605
603
  manifest_file_path: str
@@ -619,13 +617,13 @@ class IODefinition(ABC):
619
617
  """
620
618
  Helper property marking the stage of the file. (str)
621
619
  """
622
- return self.__stage
620
+ return self._stage
623
621
 
624
622
  @stage.setter
625
623
  def stage(self, stage: str):
626
624
  if stage not in ['in', 'out']:
627
625
  raise ValueError(f'Invalid stage "{stage}", supported values are: "in", "out"')
628
- self.__stage = stage
626
+ self._stage = stage
629
627
 
630
628
  @property
631
629
  @abstractmethod
@@ -643,17 +641,6 @@ class IODefinition(ABC):
643
641
  """
644
642
  raise NotImplementedError
645
643
 
646
- def __get_stage_inferred(self):
647
- stage = 'in'
648
- if not self.full_path or not Path(self.full_path).exists():
649
- return stage
650
-
651
- if Path(self.full_path).parent.parent.name == 'in':
652
- stage = 'in'
653
- elif Path(self.full_path).parent.parent.name == 'out':
654
- stage = 'out'
655
- return stage
656
-
657
644
  # ############ Staging parameters
658
645
 
659
646
  @dataclass
@@ -757,6 +744,11 @@ class TableDefinition(IODefinition):
757
744
  "columns",
758
745
  "metadata",
759
746
  "column_metadata",
747
+ "rows_count",
748
+ "data_size_bytes",
749
+ "is_alias",
750
+ "attributes",
751
+ "indexed_columns"
760
752
  ]
761
753
 
762
754
  OUTPUT_MANIFEST_ATTRIBUTES = [
@@ -792,7 +784,7 @@ class TableDefinition(IODefinition):
792
784
  enclosure: Optional[str] = '"',
793
785
  delimiter: Optional[str] = ',',
794
786
  delete_where: Optional[dict] = None,
795
- stage: Optional[str] = None,
787
+ stage: Optional[str] = 'out',
796
788
  write_always: Optional[bool] = False,
797
789
  has_header: Optional[bool] = None,
798
790
  # input
@@ -869,8 +861,10 @@ class TableDefinition(IODefinition):
869
861
  self._rows_count = kwargs.get('rows_count')
870
862
  self._data_size_bytes = kwargs.get('data_size_bytes')
871
863
  self._is_alias = kwargs.get('is_alias')
864
+ self._indexed_columns = kwargs.get('indexed_columns')
865
+ self._attributes = kwargs.get('attributes')
872
866
 
873
- self.stage = stage if stage else self.__get_stage_inferred()
867
+ self.stage = stage
874
868
  self.has_header = has_header or self._has_header_in_file()
875
869
 
876
870
  def __get_stage_inferred(self):
@@ -1101,7 +1095,13 @@ class TableDefinition(IODefinition):
1101
1095
  DeprecationWarning)
1102
1096
  force_legacy_mode = True
1103
1097
 
1098
+ if manifest.get('id'):
1099
+ stage = 'in'
1100
+ else:
1101
+ stage = 'out'
1102
+
1104
1103
  table_def = cls(name=name,
1104
+ stage=stage,
1105
1105
  full_path=full_path,
1106
1106
  is_sliced=is_sliced,
1107
1107
  id=manifest.get('id'),
@@ -1115,12 +1115,14 @@ class TableDefinition(IODefinition):
1115
1115
  rows_count=manifest.get('rows_count'),
1116
1116
  data_size_bytes=manifest.get('data_size_bytes'),
1117
1117
  is_alias=manifest.get('is_alias'),
1118
- force_legacy_mode=force_legacy_mode
1118
+ force_legacy_mode=force_legacy_mode,
1119
+ indexed_columns=manifest.get('indexed_columns'),
1120
+ attributes=manifest.get('attributes')
1119
1121
  )
1120
1122
 
1121
1123
  return table_def
1122
1124
 
1123
- def get_manifest_dictionary(self, manifest_type: Optional[str] = 'out', legacy_queue: bool = False,
1125
+ def get_manifest_dictionary(self, manifest_type: Optional[str] = None, legacy_queue: bool = False,
1124
1126
  legacy_manifest: Optional[bool] = None) -> dict:
1125
1127
  """
1126
1128
  Returns manifest dictionary in appropriate manifest_type: either 'in' or 'out'.
@@ -1192,6 +1194,8 @@ class TableDefinition(IODefinition):
1192
1194
  'rows_count': self._rows_count,
1193
1195
  'data_size_bytes': self._data_size_bytes,
1194
1196
  'is_alias': self._is_alias,
1197
+ 'indexed_columns': self._indexed_columns,
1198
+ 'attributes': self._attributes,
1195
1199
 
1196
1200
  'destination': self.destination,
1197
1201
  'incremental': self.incremental,
@@ -1395,6 +1399,8 @@ class TableDefinition(IODefinition):
1395
1399
  self._table_metadata = table_metadata
1396
1400
  # backward compatibility legacy support
1397
1401
  for col, val in table_metadata._get_legacy_column_metadata_for_manifest().items():
1402
+ if not self.schema.get(col):
1403
+ self.schema[col] = ColumnDefinition()
1398
1404
  self.schema[col].metadata = {item['key']: item['value'] for item in val}
1399
1405
 
1400
1406
  @property
@@ -1536,6 +1542,7 @@ class FileDefinition(IODefinition):
1536
1542
  "notify"]
1537
1543
 
1538
1544
  def __init__(self, full_path: str,
1545
+ stage: Optional[str] = 'out',
1539
1546
  tags: Optional[List[str]] = None,
1540
1547
  is_public: Optional[bool] = False,
1541
1548
  is_permanent: Optional[bool] = False,
@@ -1552,6 +1559,7 @@ class FileDefinition(IODefinition):
1552
1559
 
1553
1560
  Args:
1554
1561
  full_path (str): Full path of the file.
1562
+ stage (str): Storage Stage 'in' or 'out' default out
1555
1563
  tags (list):
1556
1564
  List of tags that are assigned to this file
1557
1565
  is_public: When true, the file URL will be permanent and publicly accessible.
@@ -1562,6 +1570,8 @@ class FileDefinition(IODefinition):
1562
1570
  """
1563
1571
  super().__init__(full_path)
1564
1572
 
1573
+ self.stage = stage
1574
+
1565
1575
  self.tags = tags
1566
1576
  self.is_public = is_public
1567
1577
  self.is_permanent = is_permanent
@@ -1602,7 +1612,7 @@ class FileDefinition(IODefinition):
1602
1612
  Returns:
1603
1613
  An instance of FileDefinition configured for output files.
1604
1614
  """
1605
- return cls(full_path=full_path, tags=tags, is_public=is_public, is_permanent=is_permanent,
1615
+ return cls(full_path=full_path, stage="out", tags=tags, is_public=is_public, is_permanent=is_permanent,
1606
1616
  is_encrypted=is_encrypted, notify=notify)
1607
1617
 
1608
1618
  @classmethod
@@ -1632,7 +1642,7 @@ class FileDefinition(IODefinition):
1632
1642
  Returns:
1633
1643
  An instance of FileDefinition configured for input files.
1634
1644
  """
1635
- return cls(full_path=full_path, id=id, s3=s3, abs=abs, created=created, size_bytes=size_bytes,
1645
+ return cls(full_path=full_path, stage="in", id=id, s3=s3, abs=abs, created=created, size_bytes=size_bytes,
1636
1646
  max_age_days=max_age_days)
1637
1647
 
1638
1648
  @classmethod
@@ -1667,7 +1677,13 @@ class FileDefinition(IODefinition):
1667
1677
 
1668
1678
  full_path = str(file_path)
1669
1679
 
1680
+ if manifest.get('id'):
1681
+ stage = 'in'
1682
+ else:
1683
+ stage = 'out'
1684
+
1670
1685
  file_def = cls(full_path=full_path,
1686
+ stage=stage,
1671
1687
  tags=manifest.get('tags', []),
1672
1688
  is_public=manifest.get('is_public', False),
1673
1689
  is_permanent=manifest.get('is_permanent', False),
@@ -1732,18 +1748,31 @@ class FileDefinition(IODefinition):
1732
1748
 
1733
1749
  """
1734
1750
 
1735
- return {
1736
- 'id': self.id,
1737
- 'created': self.created.strftime('%Y-%m-%dT%H:%M:%S%z') if self.created else None,
1738
- 'is_public': self.is_public,
1739
- 'is_encrypted': self.is_encrypted,
1740
- 'name': self.name,
1741
- 'size_bytes': self.size_bytes,
1742
- 'tags': self.tags,
1743
- 'notify': self.notify,
1744
- 'max_age_days': self.max_age_days,
1745
- 'is_permanent': self.is_permanent,
1746
- }
1751
+ if manifest_type == 'in':
1752
+
1753
+ manifest_dictionary = {
1754
+ 'id': self.id,
1755
+ 'created': self.created.strftime('%Y-%m-%dT%H:%M:%S%z') if self.created else None,
1756
+ 'is_public': self.is_public,
1757
+ 'is_encrypted': self.is_encrypted,
1758
+ 'name': self.name,
1759
+ 'size_bytes': self.size_bytes,
1760
+ 'tags': self.tags,
1761
+ 'notify': self.notify,
1762
+ 'max_age_days': self.max_age_days,
1763
+ 'is_permanent': self.is_permanent,
1764
+ }
1765
+
1766
+ else:
1767
+ manifest_dictionary = {
1768
+ 'is_public': self.is_public,
1769
+ 'is_permanent': self.is_permanent,
1770
+ 'is_encrypted': self.is_encrypted,
1771
+ 'tags': self.tags,
1772
+ 'notify': self.notify,
1773
+ }
1774
+
1775
+ return manifest_dictionary
1747
1776
 
1748
1777
  @property
1749
1778
  def name(self) -> str:
@@ -950,7 +950,8 @@ class CommonInterface:
950
950
  """
951
951
 
952
952
  if not legacy_manifest:
953
- legacy_manifest = self.environment_variables.data_type_support not in ('authoritative', 'hints')
953
+
954
+ legacy_manifest = self._expects_legacy_manifest()
954
955
 
955
956
  manifest = io_definition.get_manifest_dictionary(legacy_queue=self.is_legacy_queue,
956
957
  legacy_manifest=legacy_manifest)
@@ -959,6 +960,13 @@ class CommonInterface:
959
960
  with open(io_definition.full_path + '.manifest', 'w') as manifest_file:
960
961
  json.dump(manifest, manifest_file)
961
962
 
963
+ def _expects_legacy_manifest(self) -> bool:
964
+ legacy_manifest = self.environment_variables.data_type_support not in ('authoritative', 'hints')
965
+ om_override = self.configuration.config_data.get('storage', {}).get('output', {}).get('data_type_support')
966
+ if om_override:
967
+ legacy_manifest = om_override not in ('authoritative', 'hints')
968
+ return legacy_manifest
969
+
962
970
  def write_manifests(self, io_definitions: List[Union[dao.FileDefinition, dao.TableDefinition]],
963
971
  legacy_manifest: Optional[bool] = None):
964
972
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: keboola.component
3
- Version: 1.6.1
3
+ Version: 1.6.4
4
4
  Summary: General library for Python applications running in Keboola Connection environment
5
5
  Home-page: https://github.com/keboola/python-component
6
6
  Author: Keboola KDS Team
@@ -231,6 +231,59 @@ possible to create the container for the output table using the `CommonInterface
231
231
 
232
232
  ![TableDefinition dependencies](docs/imgs/TableDefinition_class.png)
233
233
 
234
+ **Table schema example:**
235
+
236
+ ```python
237
+ from keboola.component import CommonInterface
238
+ from keboola.component.dao import ColumnDefinition, DataType, SupportedDataTypes, BaseType
239
+
240
+ # init the interface
241
+ ci = CommonInterface(data_folder_path='data')
242
+
243
+ # create container for the result
244
+ out = ci.create_out_table_definition("testDef",
245
+ columns=['foo', 'bar'],
246
+ destination='some-destination',
247
+ primary_key=['foo'],
248
+ incremental=True,
249
+ delete_where={'column': 'lilly',
250
+ 'values': ['a', 'b'],
251
+ 'operator': 'eq'})
252
+
253
+ # update column
254
+ out.update_column('foo',
255
+ ColumnDefinition(data_types=BaseType(dtype=SupportedDataTypes.INTEGER, length='20')))
256
+
257
+ # add new columns
258
+ out.add_column('note', ColumnDefinition(nullable=False))
259
+ out.add_column('test1')
260
+ out.add_columns(['test2', 'test3', 'test4'])
261
+
262
+ # add new typed column
263
+ out.add_column('id', ColumnDefinition(primary_key=True,
264
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200')})
265
+ )
266
+
267
+ out.add_columns({
268
+ 'phone': ColumnDefinition(primary_key=True,
269
+ data_types={'snowflake': DataType(dtype="INTEGER", length='200'),
270
+ 'bigquery': DataType(dtype="BIGINT")}),
271
+ 'new2': ColumnDefinition(data_types={'snowflake': DataType(dtype="INTEGER", length='200')}),
272
+ })
273
+
274
+ # delete columns
275
+ out.delete_column('bar')
276
+ out.delete_columns(['test2', 'test3'])
277
+
278
+
279
+ # write some content
280
+ with open(out.full_path, 'w') as result:
281
+ result.write('line')
282
+
283
+ # write manifest
284
+ ci.write_manifest(out)
285
+ ```
286
+
234
287
  **Example:**
235
288
 
236
289
  ```python