keboola.component 1.6.6__tar.gz → 1.6.8__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.6/src/keboola.component.egg-info → keboola.component-1.6.8}/PKG-INFO +1 -1
  2. {keboola.component-1.6.6 → keboola.component-1.6.8}/setup.py +1 -1
  3. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/base.py +52 -18
  4. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/dao.py +34 -16
  5. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/interface.py +21 -11
  6. {keboola.component-1.6.6 → keboola.component-1.6.8/src/keboola.component.egg-info}/PKG-INFO +1 -1
  7. {keboola.component-1.6.6 → keboola.component-1.6.8}/LICENSE +0 -0
  8. {keboola.component-1.6.6 → keboola.component-1.6.8}/README.md +0 -0
  9. {keboola.component-1.6.6 → keboola.component-1.6.8}/setup.cfg +0 -0
  10. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/__init__.py +0 -0
  11. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/exceptions.py +0 -0
  12. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/sync_actions.py +0 -0
  13. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola/component/table_schema.py +0 -0
  14. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola.component.egg-info/SOURCES.txt +0 -0
  15. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola.component.egg-info/dependency_links.txt +0 -0
  16. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola.component.egg-info/not-zip-safe +0 -0
  17. {keboola.component-1.6.6 → keboola.component-1.6.8}/src/keboola.component.egg-info/requires.txt +0 -0
  18. {keboola.component-1.6.6 → keboola.component-1.6.8}/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.6
3
+ Version: 1.6.8
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
@@ -13,7 +13,7 @@ project_urls = {
13
13
 
14
14
  setuptools.setup(
15
15
  name="keboola.component",
16
- version="1.6.6",
16
+ version="1.6.8",
17
17
  author="Keboola KDS Team",
18
18
  project_urls=project_urls,
19
19
  setup_requires=['pytest-runner', 'flake8'],
@@ -249,6 +249,18 @@ class ComponentBase(ABC, CommonInterface):
249
249
  raise AttributeError(f"The defined action {action} is not implemented!") from e
250
250
  return action_method()
251
251
 
252
+ def _generate_table_metadata_legacy(self, table_schema: ts.TableSchema) -> dao.TableMetadata:
253
+ """
254
+ Generates a TableMetadata object for the table definition using a TableSchema object.
255
+
256
+ """
257
+ table_metadata = dao.TableMetadata()
258
+ if table_schema.description:
259
+ table_metadata.add_table_description(table_schema.description)
260
+ table_metadata.add_column_descriptions({field.name: field.description for field in table_schema.fields})
261
+ table_metadata = self._add_field_data_types_to_table_metadata(table_schema, table_metadata)
262
+ return table_metadata
263
+
252
264
  def create_out_table_definition_from_schema(self, table_schema: ts.TableSchema, is_sliced: bool = False,
253
265
  destination: str = '', incremental: bool = None,
254
266
  enclosure: str = '"', delimiter: str = ',',
@@ -271,17 +283,33 @@ class ComponentBase(ABC, CommonInterface):
271
283
  TableDefinition object initialized with all table metadata defined in a schema
272
284
 
273
285
  """
274
- table_metadata = self._generate_table_metadata(table_schema)
275
- return self.create_out_table_definition(name=table_schema.csv_name,
276
- columns=table_schema.field_names,
277
- primary_key=table_schema.primary_keys,
278
- table_metadata=table_metadata,
279
- is_sliced=is_sliced,
280
- destination=destination,
281
- incremental=incremental,
282
- enclosure=enclosure,
283
- delimiter=delimiter,
284
- delete_where=delete_where)
286
+ if self._expects_legacy_manifest():
287
+ table_metadata = self._generate_table_metadata_legacy(table_schema)
288
+ table_def = self.create_out_table_definition(name=table_schema.csv_name,
289
+ columns=table_schema.field_names,
290
+ primary_key=table_schema.primary_keys,
291
+ table_metadata=table_metadata,
292
+ is_sliced=is_sliced,
293
+ destination=destination,
294
+ incremental=incremental,
295
+ enclosure=enclosure,
296
+ delimiter=delimiter,
297
+ delete_where=delete_where)
298
+ else:
299
+ schema = self._generate_schema_definition(table_schema)
300
+
301
+ table_def = self.create_out_table_definition(name=table_schema.csv_name,
302
+ primary_key=table_schema.primary_keys,
303
+ schema=schema,
304
+ is_sliced=is_sliced,
305
+ destination=destination,
306
+ incremental=incremental,
307
+ enclosure=enclosure,
308
+ delimiter=delimiter,
309
+ delete_where=delete_where,
310
+ description=table_schema.description)
311
+
312
+ return table_def
285
313
 
286
314
  def get_table_schema_by_name(self, schema_name: str,
287
315
  schema_folder_path: Optional[str] = None) -> ts.TableSchema:
@@ -324,18 +352,24 @@ class ComponentBase(ABC, CommonInterface):
324
352
  "from a schema. If a schema folder path is not defined, the schemas folder must be"
325
353
  " located in the 'src' directory of a component : src/schemas")
326
354
 
327
- def _generate_table_metadata(self, table_schema: ts.TableSchema) -> dao.TableMetadata:
355
+ def _generate_schema_definition(self, table_schema: ts.TableSchema) -> Dict[str, dao.ColumnDefinition]:
328
356
  """
329
357
  Generates a TableMetadata object for the table definition using a TableSchema object.
330
358
 
331
359
  """
332
- table_metadata = dao.TableMetadata()
333
- if table_schema.description:
334
- table_metadata.add_table_description(table_schema.description)
335
- table_metadata.add_column_descriptions({field.name: field.description for field in table_schema.fields})
336
- table_metadata = self._add_field_data_types_to_table_metadata(table_schema, table_metadata)
360
+ column_definitions = {}
361
+ for field in table_schema.fields:
362
+ if field.base_type:
363
+ data_types = dao.BaseType(field.base_type,
364
+ length=field.length,
365
+ default=field.default)
366
+ else:
367
+ data_types = dao.BaseType()
368
+ column_definitions[field.name] = dao.ColumnDefinition(data_types=data_types,
369
+ nullable=field.nullable,
370
+ description=field.description)
337
371
 
338
- return table_metadata
372
+ return column_definitions
339
373
 
340
374
  @staticmethod
341
375
  def _add_field_data_types_to_table_metadata(table_schema: ts.TableSchema,
@@ -1,5 +1,6 @@
1
1
  # Python 3.7 support
2
2
  from __future__ import annotations
3
+
3
4
  import dataclasses
4
5
  import json
5
6
  import logging
@@ -12,7 +13,6 @@ from enum import Enum
12
13
  from pathlib import Path
13
14
  from typing import List, Union, Dict, Optional, OrderedDict as TypeOrderedDict
14
15
 
15
-
16
16
  from deprecated import deprecated
17
17
 
18
18
  from .exceptions import UserException
@@ -66,6 +66,7 @@ class EnvironmentVariables:
66
66
  logger_port: str
67
67
 
68
68
  data_type_support: str
69
+ project_features: str
69
70
 
70
71
 
71
72
  class SupportedDataTypes(str, Enum):
@@ -203,15 +204,15 @@ class TableMetadata:
203
204
  """
204
205
  if legacy_manifest:
205
206
  final_metadata_list = [{'key': key,
206
- 'value': self.table_metadata[key]}
207
- for key in self.table_metadata]
207
+ 'value': value}
208
+ for key, value in self.table_metadata.items() if value not in [None, '']]
208
209
  else:
209
- final_metadata_list = [{key: self.table_metadata[key]}
210
- for key in self.table_metadata]
210
+ final_metadata_list = {key: value
211
+ for key, value in self.table_metadata.items() if value not in [None, '']}
211
212
 
212
213
  return final_metadata_list
213
214
 
214
- @deprecated(version='1.5.1', reason="Please use schema instead of Table Metadata")
215
+ @deprecated(version='1.5.1', reason="Please use schema instead of Column Metadata")
215
216
  def get_column_metadata_for_manifest(self) -> dict:
216
217
  """
217
218
  Returns column metadata dict as required by the
@@ -250,13 +251,14 @@ class TableMetadata:
250
251
  final_column_metadata[column] = list()
251
252
 
252
253
  column_metadata = [{'key': key,
253
- 'value': column_metadata_dicts[key]} for key in
254
- column_metadata_dicts]
254
+ 'value': value} for key, value in
255
+ column_metadata_dicts.items() if value not in [None, '']]
255
256
  final_column_metadata[column].extend(column_metadata)
256
257
 
257
258
  return final_column_metadata
258
259
 
259
260
  @property
261
+ @deprecated(version='1.5.1', reason="Please use TableDefinition.description instead of TableMetadata")
260
262
  def table_description(self) -> str:
261
263
  """
262
264
  Returns table description (KBC.description)
@@ -401,14 +403,20 @@ class TableMetadata:
401
403
  Args:
402
404
 
403
405
  """
406
+ if value is None:
407
+ return
404
408
  self.table_metadata = {**self.table_metadata, **{key: value}}
405
409
 
410
+ @deprecated(version='1.5.1', reason="Column metadata ere moved to dao.TableDefinition.schema property."
411
+ "Please use the dao.ColumnDefinition.metadata")
406
412
  def add_column_metadata(self, column: str, key: str, value: Union[str, bool, int], backend="base"):
407
413
  """
408
414
  Add/Updates column metadata and ensures the Key is unique.
409
415
  Args:
410
416
 
411
417
  """
418
+ if value is None:
419
+ return
412
420
  if not self.column_metadata.get(column):
413
421
  self.column_metadata[column] = dict()
414
422
 
@@ -416,6 +424,8 @@ class TableMetadata:
416
424
 
417
425
  # self.schema = [ColumnDefinition(name=column, data_type={backend: DataType(type=value)})]
418
426
 
427
+ @deprecated(version='1.5.1', reason="Column metadata ere moved to dao.TableDefinition.schema property."
428
+ "Please use the dao.ColumnDefinition.metadata")
419
429
  def add_multiple_column_metadata(self, column_metadata: Dict[str, List[dict]]):
420
430
  """
421
431
  Add key-value pairs to column metadata.
@@ -576,7 +586,7 @@ class SupportedManifestAttributes(SubscriptableDataclass):
576
586
  to_remove = ['primary_key', 'columns', 'distribution_key', 'column_metadata', 'metadata']
577
587
  attributes = list(set(attributes).difference(to_remove))
578
588
 
579
- to_add = ['manifest_type', 'has_header', 'description', 'table_metadata', 'schema']
589
+ to_add = ['manifest_type', 'has_header', 'table_metadata', 'schema']
580
590
  attributes.extend(to_add)
581
591
 
582
592
  elif stage == 'in':
@@ -772,13 +782,14 @@ class TableDefinition(IODefinition):
772
782
 
773
783
  MANIFEST_ATTRIBUTES = {'in': INPUT_MANIFEST_ATTRIBUTES,
774
784
  'out': OUTPUT_MANIFEST_ATTRIBUTES}
785
+ SCHEMA_TYPE = Union[Dict[str, ColumnDefinition], TypeOrderedDict[str, ColumnDefinition], List[str]]
775
786
 
776
787
  def __init__(self, name: str,
777
788
  full_path: Optional[Union[str, None]] = None,
778
789
  is_sliced: Optional[bool] = False,
779
790
  destination: Optional[str] = '',
780
791
  primary_key: Optional[List[str]] = None,
781
- schema: Optional[Union[TypeOrderedDict[str, ColumnDefinition], list[str]]] = None,
792
+ schema: SCHEMA_TYPE = None,
782
793
  incremental: Optional[bool] = None,
783
794
  table_metadata: Optional[TableMetadata] = None,
784
795
  enclosure: Optional[str] = '"',
@@ -787,6 +798,7 @@ class TableDefinition(IODefinition):
787
798
  stage: Optional[str] = 'out',
788
799
  write_always: Optional[bool] = False,
789
800
  has_header: Optional[bool] = None,
801
+ description: Optional[str] = None,
790
802
  # input
791
803
  **kwargs
792
804
  ):
@@ -814,6 +826,7 @@ class TableDefinition(IODefinition):
814
826
  write_always: Bool: If true, the table will be saved to Storage even when the job execution
815
827
  fails.
816
828
  schema: (dict|lis[str]) Mapping of column names andColumnDefinition objects, or a list of names
829
+ description: str: Table description
817
830
 
818
831
  """
819
832
  super().__init__(full_path)
@@ -823,7 +836,7 @@ class TableDefinition(IODefinition):
823
836
  # initialize manifest properties
824
837
  self._destination = None
825
838
  self.destination = destination
826
- self._schema = dict()
839
+ self._schema: Dict[str, ColumnDefinition] = dict()
827
840
 
828
841
  if schema:
829
842
  self.schema = schema
@@ -844,6 +857,8 @@ class TableDefinition(IODefinition):
844
857
  if not table_metadata:
845
858
  table_metadata = TableMetadata()
846
859
  self.table_metadata = table_metadata
860
+ if description:
861
+ self.table_metadata.add_table_description(description)
847
862
 
848
863
  self.delete_where_values = None
849
864
  self.delete_where_column = None
@@ -892,7 +907,8 @@ class TableDefinition(IODefinition):
892
907
  delimiter: Optional[str] = ',',
893
908
  delete_where: Optional[dict] = None,
894
909
  write_always: Optional[bool] = False,
895
- schema: Optional[Union[TypeOrderedDict[str, ColumnDefinition], list[str]]] = None,
910
+ schema: SCHEMA_TYPE = None,
911
+ description: Optional[str] = None,
896
912
  **kwargs
897
913
  ):
898
914
  """
@@ -913,6 +929,7 @@ class TableDefinition(IODefinition):
913
929
  delete_where (Optional[dict]): Criteria for row deletion in incremental loads. Defaults to None.
914
930
  write_always (Optional[bool]): If True, the table will be saved to storage even if the job fails.
915
931
  schema (Optional[List[ColumnDefinition]]): Dictionary of ColumnDefinition objects.
932
+ description (Optional[str]): The description of the table. Defaults to None.
916
933
 
917
934
  Returns:
918
935
  TableDefinition: An instance of TableDefinition configured for output tables.
@@ -928,6 +945,7 @@ class TableDefinition(IODefinition):
928
945
  delete_where=delete_where,
929
946
  write_always=write_always,
930
947
  schema=schema,
948
+ description=description,
931
949
  **kwargs
932
950
  )
933
951
 
@@ -1154,7 +1172,7 @@ class TableDefinition(IODefinition):
1154
1172
  name=name,
1155
1173
  destination=manifest.get('destination'),
1156
1174
  schema=cls.return_schema_from_manifest(manifest),
1157
- incremental=manifest.get('incremental'),
1175
+ incremental=manifest.get('incremental', False),
1158
1176
  primary_key=manifest.get('primary_key'),
1159
1177
  write_always=manifest.get('write_always', False),
1160
1178
  delimiter=manifest.get('delimiter', ','),
@@ -1254,7 +1272,6 @@ class TableDefinition(IODefinition):
1254
1272
  'column_metadata': self.table_metadata._get_legacy_column_metadata_for_manifest(),
1255
1273
  'manifest_type': manifest_type,
1256
1274
  'has_header': self.has_header,
1257
- 'description': None,
1258
1275
  'table_metadata': self.table_metadata.get_table_metadata_for_manifest(),
1259
1276
  'delete_where_column': self.delete_where_column,
1260
1277
  'delete_where_values': self.delete_where_values,
@@ -1262,6 +1279,7 @@ class TableDefinition(IODefinition):
1262
1279
  'schema': [col.to_dict(name)
1263
1280
  for name, col in self.schema.items()] if isinstance(self.schema, (OrderedDict, dict)) else []
1264
1281
  }
1282
+
1265
1283
  if legacy_manifest or self.stage == 'in':
1266
1284
  fields['columns'] = self.column_names
1267
1285
 
@@ -1386,8 +1404,8 @@ class TableDefinition(IODefinition):
1386
1404
 
1387
1405
  @incremental.setter
1388
1406
  def incremental(self, incremental: bool):
1389
- if incremental:
1390
- self._incremental = True
1407
+ if incremental is not None:
1408
+ self._incremental = incremental
1391
1409
 
1392
1410
  @property
1393
1411
  def write_always(self) -> bool:
@@ -18,7 +18,7 @@ from pygelf import GelfUdpHandler, GelfTcpHandler
18
18
  from pytz import utc
19
19
 
20
20
  from . import dao
21
- from .dao import ColumnDefinition
21
+ from .dao import ColumnDefinition, TableDefinition
22
22
  from .exceptions import UserException
23
23
 
24
24
 
@@ -55,7 +55,8 @@ def init_environment_variables() -> dao.EnvironmentVariables:
55
55
  real_user=os.environ.get('KBC_REALUSER', None),
56
56
  logger_addr=os.environ.get('KBC_LOGGER_ADDR', None),
57
57
  logger_port=os.environ.get('KBC_LOGGER_PORT', None),
58
- data_type_support=os.environ.get('KBC_DATA_TYPE_SUPPORT', None)
58
+ data_type_support=os.environ.get('KBC_DATA_TYPE_SUPPORT', None),
59
+ project_features=os.environ.get('KBC_PROJECT_FEATURE_GATES', '')
59
60
  )
60
61
 
61
62
 
@@ -337,7 +338,8 @@ class CommonInterface:
337
338
  write_always: bool = False,
338
339
  schema: Union[
339
340
  OrderedDict[str, ColumnDefinition], list[str]] = None,
340
- has_header: Optional[bool] = None) -> dao.TableDefinition:
341
+ has_header: Optional[bool] = None,
342
+ description: Optional[str] = None) -> dao.TableDefinition:
341
343
  """
342
344
  Helper method for dao.TableDefinition creation along with the "manifest".
343
345
  It initializes path according to the storage_stage type.
@@ -380,6 +382,7 @@ class CommonInterface:
380
382
  primary_key=primary_key,
381
383
  columns=columns,
382
384
  incremental=incremental,
385
+ description=description,
383
386
  table_metadata=table_metadata,
384
387
  enclosure=enclosure,
385
388
  delimiter=delimiter,
@@ -426,13 +429,11 @@ class CommonInterface:
426
429
  delete_where=delete_where,
427
430
  schema=schema)
428
431
 
429
- SCHEMA_TYPE = Union[Dict[str, ColumnDefinition], OrderedDict[str, ColumnDefinition], List[str]]
430
-
431
432
  def create_out_table_definition(self, name: str,
432
433
  is_sliced: bool = False,
433
434
  destination: str = '',
434
435
  primary_key: List[str] = None,
435
- schema: SCHEMA_TYPE = None,
436
+ schema: TableDefinition.SCHEMA_TYPE = None,
436
437
  incremental: bool = None,
437
438
  table_metadata: dao.TableMetadata = None,
438
439
  enclosure: str = '"',
@@ -440,6 +441,7 @@ class CommonInterface:
440
441
  delete_where: dict = None,
441
442
  write_always: bool = False,
442
443
  has_header: Optional[bool] = None,
444
+ description: Optional[str] = None,
443
445
  **kwargs
444
446
  ) -> dao.TableDefinition:
445
447
  """
@@ -463,6 +465,7 @@ class CommonInterface:
463
465
  fails.
464
466
  has_header:Optional[bool] = flag whether the header is present in the file,
465
467
  if None legacy method is used
468
+ description: Table description
466
469
  """
467
470
 
468
471
  return self._create_table_definition(name=name,
@@ -478,7 +481,8 @@ class CommonInterface:
478
481
  delete_where=delete_where,
479
482
  write_always=write_always,
480
483
  schema=schema,
481
- has_header=has_header)
484
+ has_header=has_header,
485
+ description=description)
482
486
 
483
487
  # # File processing
484
488
 
@@ -902,6 +906,10 @@ class CommonInterface:
902
906
  def files_in_path(self):
903
907
  return os.path.join(self.data_folder_path, 'in', 'files')
904
908
 
909
+ @property
910
+ def _running_in_kbc(self):
911
+ return self.environment_variables.stack_id or False
912
+
905
913
  @property
906
914
  def is_legacy_queue(self) -> bool:
907
915
  """
@@ -909,9 +917,10 @@ class CommonInterface:
909
917
  Returns:
910
918
 
911
919
  """
912
- features = os.environ.get('KBC_PROJECT_FEATURE_GATES')
920
+ features = self.environment_variables.project_features
921
+
913
922
  is_legacy_queue = True
914
- if not features or 'queuev2' in features:
923
+ if not self._running_in_kbc or 'queuev2' in features:
915
924
  is_legacy_queue = False
916
925
  return is_legacy_queue
917
926
 
@@ -950,7 +959,6 @@ class CommonInterface:
950
959
  """
951
960
 
952
961
  if not legacy_manifest:
953
-
954
962
  legacy_manifest = self._expects_legacy_manifest()
955
963
 
956
964
  manifest = io_definition.get_manifest_dictionary(legacy_queue=self.is_legacy_queue,
@@ -961,7 +969,9 @@ class CommonInterface:
961
969
  json.dump(manifest, manifest_file)
962
970
 
963
971
  def _expects_legacy_manifest(self) -> bool:
964
- legacy_manifest = self.environment_variables.data_type_support not in ('authoritative', 'hints')
972
+ legacy_manifest = \
973
+ (self._running_in_kbc and self.environment_variables.data_type_support not in ('authoritative', 'hints'))
974
+
965
975
  om_override = self.configuration.config_data.get('storage', {}).get('output', {}).get('data_type_support')
966
976
  if om_override:
967
977
  legacy_manifest = om_override not in ('authoritative', 'hints')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: keboola.component
3
- Version: 1.6.6
3
+ Version: 1.6.8
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