keboola.component 1.6.0__tar.gz → 1.6.1__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.
- {keboola.component-1.6.0/src/keboola.component.egg-info → keboola.component-1.6.1}/PKG-INFO +1 -1
- {keboola.component-1.6.0 → keboola.component-1.6.1}/setup.py +1 -1
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/dao.py +165 -70
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/interface.py +33 -10
- {keboola.component-1.6.0 → keboola.component-1.6.1/src/keboola.component.egg-info}/PKG-INFO +1 -1
- {keboola.component-1.6.0 → keboola.component-1.6.1}/LICENSE +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/README.md +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/setup.cfg +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/__init__.py +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/base.py +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/exceptions.py +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/sync_actions.py +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/table_schema.py +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/SOURCES.txt +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/dependency_links.txt +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/not-zip-safe +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/requires.txt +0 -0
- {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/top_level.txt +0 -0
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
# Python 3.7 support
|
|
2
|
+
from __future__ import annotations
|
|
1
3
|
import dataclasses
|
|
2
4
|
import json
|
|
3
5
|
import logging
|
|
6
|
+
import warnings
|
|
4
7
|
from abc import ABC, abstractmethod
|
|
8
|
+
from collections import OrderedDict
|
|
5
9
|
from dataclasses import dataclass, field
|
|
6
10
|
from datetime import datetime
|
|
7
11
|
from enum import Enum
|
|
8
12
|
from pathlib import Path
|
|
9
13
|
from typing import List, Union, Dict, Optional, OrderedDict as TypeOrderedDict
|
|
10
|
-
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
from deprecated import deprecated
|
|
17
|
+
|
|
12
18
|
from .exceptions import UserException
|
|
13
19
|
|
|
14
20
|
try:
|
|
@@ -218,6 +224,22 @@ class TableMetadata:
|
|
|
218
224
|
|
|
219
225
|
Returns: dict
|
|
220
226
|
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
return self._get_legacy_column_metadata_for_manifest()
|
|
230
|
+
|
|
231
|
+
def _get_legacy_column_metadata_for_manifest(self) -> dict:
|
|
232
|
+
"""
|
|
233
|
+
Returns column metadata dict as required by the
|
|
234
|
+
[manifest format](https://developers.keboola.com/extend/common-interface/manifest-files/#dataintables
|
|
235
|
+
-manifests)
|
|
236
|
+
|
|
237
|
+
e.g.
|
|
238
|
+
tm = TableMetadata()
|
|
239
|
+
manifest['column_metadata'] = tm.column_metadata
|
|
240
|
+
|
|
241
|
+
Returns: dict
|
|
242
|
+
|
|
221
243
|
"""
|
|
222
244
|
final_column_metadata = dict()
|
|
223
245
|
|
|
@@ -420,9 +442,9 @@ class TableMetadata:
|
|
|
420
442
|
|
|
421
443
|
|
|
422
444
|
@dataclass
|
|
423
|
-
class DataType
|
|
445
|
+
class DataType:
|
|
424
446
|
dtype: str
|
|
425
|
-
length: Optional[
|
|
447
|
+
length: Optional[str] = None
|
|
426
448
|
default: Optional[str] = None
|
|
427
449
|
|
|
428
450
|
def __post_init__(self):
|
|
@@ -430,10 +452,41 @@ class DataType(dict):
|
|
|
430
452
|
self.dtype = self.dtype.value
|
|
431
453
|
|
|
432
454
|
|
|
433
|
-
class BaseType(
|
|
434
|
-
def __init__(self, dtype: SupportedDataTypes = SupportedDataTypes.STRING, length: Optional[
|
|
455
|
+
class BaseType(dict):
|
|
456
|
+
def __init__(self, dtype: SupportedDataTypes = SupportedDataTypes.STRING, length: Optional[str] = None,
|
|
435
457
|
default: Optional[str] = None):
|
|
436
|
-
super().__init__(dtype=dtype, length=length, default=default)
|
|
458
|
+
super().__init__(base=DataType(dtype=dtype, length=length, default=default))
|
|
459
|
+
|
|
460
|
+
@classmethod
|
|
461
|
+
def string(cls, length: Optional[str] = None, default: Optional[str] = None) -> 'BaseType':
|
|
462
|
+
return BaseType(dtype=SupportedDataTypes.STRING, length=length, default=default)
|
|
463
|
+
|
|
464
|
+
@classmethod
|
|
465
|
+
def integer(cls, length: Optional[str] = None,
|
|
466
|
+
default: Optional[str] = None) -> 'BaseType':
|
|
467
|
+
return BaseType(dtype=SupportedDataTypes.INTEGER, length=length, default=default)
|
|
468
|
+
|
|
469
|
+
@classmethod
|
|
470
|
+
def numeric(cls, length: Optional[str] = None,
|
|
471
|
+
default: Optional[str] = None) -> 'BaseType':
|
|
472
|
+
return BaseType(dtype=SupportedDataTypes.NUMERIC, length=length, default=default)
|
|
473
|
+
|
|
474
|
+
@classmethod
|
|
475
|
+
def float(cls, length: Optional[str] = None,
|
|
476
|
+
default: Optional[str] = None) -> 'BaseType':
|
|
477
|
+
return BaseType(dtype=SupportedDataTypes.FLOAT, length=length, default=default)
|
|
478
|
+
|
|
479
|
+
@classmethod
|
|
480
|
+
def boolean(cls, default: Optional[str] = None) -> 'BaseType':
|
|
481
|
+
return BaseType(dtype=SupportedDataTypes.BOOLEAN, default=default)
|
|
482
|
+
|
|
483
|
+
@classmethod
|
|
484
|
+
def date(cls, default: Optional[str] = None) -> 'BaseType':
|
|
485
|
+
return BaseType(dtype=SupportedDataTypes.DATE, default=default)
|
|
486
|
+
|
|
487
|
+
@classmethod
|
|
488
|
+
def timestamp(cls, default: Optional[str] = None) -> 'BaseType':
|
|
489
|
+
return BaseType(dtype=SupportedDataTypes.TIMESTAMP, default=default)
|
|
437
490
|
|
|
438
491
|
|
|
439
492
|
@dataclass
|
|
@@ -457,15 +510,6 @@ class ColumnDefinition:
|
|
|
457
510
|
description: Optional[str] = None
|
|
458
511
|
metadata: Optional[Dict[str, str]] = None
|
|
459
512
|
|
|
460
|
-
def __post_init__(self):
|
|
461
|
-
if isinstance(self.data_types, DataType):
|
|
462
|
-
self.data_types = {"base": self.data_types}
|
|
463
|
-
|
|
464
|
-
def normalize_data_type(self, data_type: Union[Dict[str, DataType], BaseType]) -> Dict[str, DataType]:
|
|
465
|
-
if isinstance(data_type, DataType):
|
|
466
|
-
return {"base": data_type}
|
|
467
|
-
return data_type
|
|
468
|
-
|
|
469
513
|
def update_properties(self, **kwargs):
|
|
470
514
|
for key, value in kwargs.items():
|
|
471
515
|
if hasattr(self, key):
|
|
@@ -740,29 +784,19 @@ class TableDefinition(IODefinition):
|
|
|
740
784
|
def __init__(self, name: str,
|
|
741
785
|
full_path: Optional[Union[str, None]] = None,
|
|
742
786
|
is_sliced: Optional[bool] = False,
|
|
743
|
-
|
|
744
787
|
destination: Optional[str] = '',
|
|
745
788
|
primary_key: Optional[List[str]] = None,
|
|
746
|
-
|
|
789
|
+
schema: Optional[Union[TypeOrderedDict[str, ColumnDefinition], list[str]]] = None,
|
|
747
790
|
incremental: Optional[bool] = None,
|
|
748
791
|
table_metadata: Optional[TableMetadata] = None,
|
|
749
792
|
enclosure: Optional[str] = '"',
|
|
750
793
|
delimiter: Optional[str] = ',',
|
|
751
794
|
delete_where: Optional[dict] = None,
|
|
752
|
-
stage: Optional[str] =
|
|
795
|
+
stage: Optional[str] = None,
|
|
753
796
|
write_always: Optional[bool] = False,
|
|
754
|
-
schema: Optional[TypeOrderedDict[str, ColumnDefinition]] = None,
|
|
755
|
-
rows_count: Optional[int] = None,
|
|
756
|
-
data_size_bytes: Optional[int] = None,
|
|
757
|
-
is_alias: Optional[bool] = False,
|
|
758
797
|
has_header: Optional[bool] = None,
|
|
759
|
-
|
|
760
798
|
# input
|
|
761
|
-
|
|
762
|
-
id: Optional[str] = '',
|
|
763
|
-
created: Optional[str] = None,
|
|
764
|
-
last_change_date: Optional[str] = None,
|
|
765
|
-
last_import_date: Optional[str] = None
|
|
799
|
+
**kwargs
|
|
766
800
|
):
|
|
767
801
|
"""
|
|
768
802
|
|
|
@@ -776,9 +810,9 @@ class TableDefinition(IODefinition):
|
|
|
776
810
|
The full_path is None when dealing with [workspaces](
|
|
777
811
|
https://developers.keboola.com/extend/common-interface/folders/#exchanging-data-via-workspace)
|
|
778
812
|
is_sliced: True if the full_path points to a folder with sliced tables
|
|
813
|
+
has_header: True if the file has a header, if emtpy inferred.
|
|
779
814
|
destination: String name of the table in Storage.
|
|
780
815
|
primary_key: List with names of columns used for primary key.
|
|
781
|
-
columns: List of columns for headless CSV files
|
|
782
816
|
incremental: Set to true to enable incremental loading
|
|
783
817
|
table_metadata: <.dao.TableMetadata> object containing column and table metadata (deprecated)
|
|
784
818
|
enclosure: str: CSV enclosure, by default "
|
|
@@ -787,19 +821,28 @@ class TableDefinition(IODefinition):
|
|
|
787
821
|
stage: str: Storage Stage 'in' or 'out'
|
|
788
822
|
write_always: Bool: If true, the table will be saved to Storage even when the job execution
|
|
789
823
|
fails.
|
|
790
|
-
schema: (dict) Mapping of column names andColumnDefinition objects
|
|
824
|
+
schema: (dict|lis[str]) Mapping of column names andColumnDefinition objects, or a list of names
|
|
791
825
|
|
|
792
826
|
"""
|
|
793
827
|
super().__init__(full_path)
|
|
794
828
|
self._name = name
|
|
795
829
|
self.is_sliced = is_sliced
|
|
796
830
|
|
|
797
|
-
self.schema = schema
|
|
798
|
-
|
|
799
831
|
# initialize manifest properties
|
|
800
832
|
self._destination = None
|
|
801
833
|
self.destination = destination
|
|
802
|
-
self.
|
|
834
|
+
self._schema = dict()
|
|
835
|
+
|
|
836
|
+
if schema:
|
|
837
|
+
self.schema = schema
|
|
838
|
+
# deprecated argument for backward compatibility
|
|
839
|
+
self._legacy_mode = False
|
|
840
|
+
if kwargs.get('force_legacy_mode'):
|
|
841
|
+
self._legacy_mode = True
|
|
842
|
+
if kwargs.get('columns'):
|
|
843
|
+
self.columns = kwargs['columns']
|
|
844
|
+
|
|
845
|
+
self._legacy_primary_key = list()
|
|
803
846
|
self.primary_key = primary_key
|
|
804
847
|
self._incremental = incremental
|
|
805
848
|
|
|
@@ -815,20 +858,25 @@ class TableDefinition(IODefinition):
|
|
|
815
858
|
self.delete_where_operator = None
|
|
816
859
|
|
|
817
860
|
self.set_delete_where_from_dict(delete_where)
|
|
818
|
-
self.stage = stage
|
|
819
861
|
self.write_always = write_always
|
|
820
|
-
self.legacy_columns = columns
|
|
821
|
-
self.has_header = has_header or self._has_header_in_file()
|
|
822
862
|
|
|
823
863
|
# input manifest properties
|
|
824
|
-
self._id = id
|
|
825
|
-
self._uri = uri
|
|
826
|
-
self._created = created
|
|
827
|
-
self._last_change_date = last_change_date
|
|
828
|
-
self._last_import_date = last_import_date
|
|
829
|
-
self._rows_count = rows_count
|
|
830
|
-
self._data_size_bytes = data_size_bytes
|
|
831
|
-
self._is_alias = is_alias
|
|
864
|
+
self._id = kwargs.get('id')
|
|
865
|
+
self._uri = kwargs.get('uri')
|
|
866
|
+
self._created = kwargs.get('created')
|
|
867
|
+
self._last_change_date = kwargs.get('last_change_date')
|
|
868
|
+
self._last_import_date = kwargs.get('last_import_date')
|
|
869
|
+
self._rows_count = kwargs.get('rows_count')
|
|
870
|
+
self._data_size_bytes = kwargs.get('data_size_bytes')
|
|
871
|
+
self._is_alias = kwargs.get('is_alias')
|
|
872
|
+
|
|
873
|
+
self.stage = stage if stage else self.__get_stage_inferred()
|
|
874
|
+
self.has_header = has_header or self._has_header_in_file()
|
|
875
|
+
|
|
876
|
+
def __get_stage_inferred(self):
|
|
877
|
+
if self._uri:
|
|
878
|
+
return 'in'
|
|
879
|
+
return 'out'
|
|
832
880
|
|
|
833
881
|
@classmethod
|
|
834
882
|
def build_output_definition(cls, name: str,
|
|
@@ -979,6 +1027,7 @@ class TableDefinition(IODefinition):
|
|
|
979
1027
|
schema[col.get("name")] = ColumnDefinition().from_dict(col)
|
|
980
1028
|
|
|
981
1029
|
else:
|
|
1030
|
+
# legacy support
|
|
982
1031
|
columns_metadata = json_data.get('column_metadata', {})
|
|
983
1032
|
primary_key = json_data.get('primary_key', [])
|
|
984
1033
|
columns = json_data.get('columns', [])
|
|
@@ -1045,11 +1094,19 @@ class TableDefinition(IODefinition):
|
|
|
1045
1094
|
else:
|
|
1046
1095
|
name = Path(manifest_file_path).stem
|
|
1047
1096
|
|
|
1097
|
+
# test if the manifest is output and incompatible
|
|
1098
|
+
force_legacy_mode = False
|
|
1099
|
+
if not manifest.get('columns') and manifest.get('primary_key'):
|
|
1100
|
+
warnings.warn('Primary key is set but columns are not. Forcing legacy mode for CSV file.',
|
|
1101
|
+
DeprecationWarning)
|
|
1102
|
+
force_legacy_mode = True
|
|
1103
|
+
|
|
1048
1104
|
table_def = cls(name=name,
|
|
1049
1105
|
full_path=full_path,
|
|
1050
1106
|
is_sliced=is_sliced,
|
|
1051
1107
|
id=manifest.get('id'),
|
|
1052
1108
|
table_metadata=TableMetadata(manifest),
|
|
1109
|
+
primary_key=manifest.get('primary_key'),
|
|
1053
1110
|
schema=cls.return_schema_from_manifest(manifest),
|
|
1054
1111
|
uri=manifest.get('uri'),
|
|
1055
1112
|
created=manifest.get('created'),
|
|
@@ -1057,12 +1114,13 @@ class TableDefinition(IODefinition):
|
|
|
1057
1114
|
last_import_date=manifest.get('last_import_date'),
|
|
1058
1115
|
rows_count=manifest.get('rows_count'),
|
|
1059
1116
|
data_size_bytes=manifest.get('data_size_bytes'),
|
|
1060
|
-
is_alias=manifest.get('is_alias')
|
|
1117
|
+
is_alias=manifest.get('is_alias'),
|
|
1118
|
+
force_legacy_mode=force_legacy_mode
|
|
1061
1119
|
)
|
|
1062
1120
|
|
|
1063
1121
|
return table_def
|
|
1064
1122
|
|
|
1065
|
-
def get_manifest_dictionary(self, manifest_type: Optional[str] =
|
|
1123
|
+
def get_manifest_dictionary(self, manifest_type: Optional[str] = 'out', legacy_queue: bool = False,
|
|
1066
1124
|
legacy_manifest: Optional[bool] = None) -> dict:
|
|
1067
1125
|
"""
|
|
1068
1126
|
Returns manifest dictionary in appropriate manifest_type: either 'in' or 'out'.
|
|
@@ -1089,6 +1147,8 @@ class TableDefinition(IODefinition):
|
|
|
1089
1147
|
if not manifest_type:
|
|
1090
1148
|
manifest_type = self.stage
|
|
1091
1149
|
|
|
1150
|
+
if self._legacy_mode:
|
|
1151
|
+
legacy_manifest = True
|
|
1092
1152
|
dictionary = self._filter_attributes_by_manifest_type(manifest_type, legacy_queue, legacy_manifest)
|
|
1093
1153
|
|
|
1094
1154
|
filtered_dictionary = self._filter_dictionary(dictionary)
|
|
@@ -1134,14 +1194,13 @@ class TableDefinition(IODefinition):
|
|
|
1134
1194
|
'is_alias': self._is_alias,
|
|
1135
1195
|
|
|
1136
1196
|
'destination': self.destination,
|
|
1137
|
-
'columns': self.columns if not legacy_manifest else self.legacy_columns,
|
|
1138
1197
|
'incremental': self.incremental,
|
|
1139
1198
|
'primary_key': self.primary_key,
|
|
1140
1199
|
'write_always': self.write_always,
|
|
1141
1200
|
'delimiter': self.delimiter,
|
|
1142
1201
|
'enclosure': self.enclosure,
|
|
1143
1202
|
'metadata': self.table_metadata.get_table_metadata_for_manifest(legacy_manifest=True),
|
|
1144
|
-
'column_metadata': self.table_metadata.
|
|
1203
|
+
'column_metadata': self.table_metadata._get_legacy_column_metadata_for_manifest(),
|
|
1145
1204
|
'manifest_type': manifest_type,
|
|
1146
1205
|
'has_header': self.has_header,
|
|
1147
1206
|
'description': None,
|
|
@@ -1150,8 +1209,10 @@ class TableDefinition(IODefinition):
|
|
|
1150
1209
|
'delete_where_values': self.delete_where_values,
|
|
1151
1210
|
'delete_where_operator': self.delete_where_operator,
|
|
1152
1211
|
'schema': [col.to_dict(name)
|
|
1153
|
-
for name, col in self.schema.items()] if isinstance(self.schema, OrderedDict) else []
|
|
1212
|
+
for name, col in self.schema.items()] if isinstance(self.schema, (OrderedDict, dict)) else []
|
|
1154
1213
|
}
|
|
1214
|
+
if legacy_manifest:
|
|
1215
|
+
fields['columns'] = self.column_names
|
|
1155
1216
|
|
|
1156
1217
|
new_dict = fields.copy()
|
|
1157
1218
|
|
|
@@ -1164,7 +1225,7 @@ class TableDefinition(IODefinition):
|
|
|
1164
1225
|
def _has_header_in_file(self):
|
|
1165
1226
|
if self.is_sliced:
|
|
1166
1227
|
has_header = False
|
|
1167
|
-
elif self.
|
|
1228
|
+
elif self.column_names and not self.stage == 'in':
|
|
1168
1229
|
has_header = False
|
|
1169
1230
|
else:
|
|
1170
1231
|
has_header = True
|
|
@@ -1175,10 +1236,17 @@ class TableDefinition(IODefinition):
|
|
|
1175
1236
|
return self._schema
|
|
1176
1237
|
|
|
1177
1238
|
@schema.setter
|
|
1178
|
-
def schema(self, value):
|
|
1179
|
-
self._schema = OrderedDict()
|
|
1239
|
+
def schema(self, value: Union[TypeOrderedDict[str, ColumnDefinition], list[str]]):
|
|
1180
1240
|
if value:
|
|
1181
|
-
|
|
1241
|
+
if not isinstance(value, (list, dict, OrderedDict)):
|
|
1242
|
+
raise TypeError("Columns must be a list or a mapping of column names and ColumnDefinition objects")
|
|
1243
|
+
|
|
1244
|
+
if isinstance(value, list):
|
|
1245
|
+
self._schema = OrderedDict()
|
|
1246
|
+
for col in value:
|
|
1247
|
+
self._schema[col] = ColumnDefinition()
|
|
1248
|
+
else:
|
|
1249
|
+
self._schema = value
|
|
1182
1250
|
|
|
1183
1251
|
@property
|
|
1184
1252
|
def _manifest_attributes(self) -> SupportedManifestAttributes:
|
|
@@ -1232,21 +1300,27 @@ class TableDefinition(IODefinition):
|
|
|
1232
1300
|
@property
|
|
1233
1301
|
@deprecated(version='1.5.1', reason="Please use new column_names method instead of columns property")
|
|
1234
1302
|
def columns(self) -> List[str]:
|
|
1235
|
-
if isinstance(self.schema, OrderedDict):
|
|
1303
|
+
if isinstance(self.schema, (OrderedDict, dict)):
|
|
1236
1304
|
return list(self.schema.keys())
|
|
1237
1305
|
else:
|
|
1238
1306
|
return []
|
|
1239
1307
|
|
|
1240
1308
|
@columns.setter
|
|
1241
|
-
@deprecated(version='1.5.1', reason="
|
|
1309
|
+
@deprecated(version='1.5.1', reason="Please use new column_names method instead of schema property")
|
|
1242
1310
|
def columns(self, val: List[str]):
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1311
|
+
"""
|
|
1312
|
+
Set columns for the table.
|
|
1313
|
+
If list of names provided, the columns will be created with default settings Basetype.String.
|
|
1314
|
+
Args:
|
|
1315
|
+
val:
|
|
1246
1316
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1317
|
+
Returns:
|
|
1318
|
+
|
|
1319
|
+
"""
|
|
1320
|
+
if not isinstance(val, list):
|
|
1321
|
+
raise TypeError("Columns must be a list")
|
|
1322
|
+
|
|
1323
|
+
self.schema = val
|
|
1250
1324
|
|
|
1251
1325
|
@property
|
|
1252
1326
|
def column_names(self) -> List[str]:
|
|
@@ -1274,8 +1348,10 @@ class TableDefinition(IODefinition):
|
|
|
1274
1348
|
|
|
1275
1349
|
@property
|
|
1276
1350
|
def primary_key(self) -> List[str]:
|
|
1277
|
-
if
|
|
1351
|
+
if not self._legacy_mode:
|
|
1278
1352
|
return [column_name for column_name, column_def in self.schema.items() if column_def.primary_key]
|
|
1353
|
+
else:
|
|
1354
|
+
return self._legacy_primary_key
|
|
1279
1355
|
|
|
1280
1356
|
@primary_key.setter
|
|
1281
1357
|
def primary_key(self, primary_key: List[str]):
|
|
@@ -1284,12 +1360,15 @@ class TableDefinition(IODefinition):
|
|
|
1284
1360
|
|
|
1285
1361
|
if not isinstance(primary_key, list):
|
|
1286
1362
|
raise TypeError("Primary key must be a list")
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1363
|
+
if not self._legacy_mode:
|
|
1364
|
+
for col in primary_key:
|
|
1365
|
+
if col in self.schema:
|
|
1366
|
+
self.schema[col].primary_key = True
|
|
1367
|
+
else:
|
|
1368
|
+
raise UserException(f"Primary key column {col} not found in schema. "
|
|
1369
|
+
f"Please specify all columns / schema")
|
|
1370
|
+
else:
|
|
1371
|
+
self._legacy_primary_key = primary_key
|
|
1293
1372
|
|
|
1294
1373
|
@property
|
|
1295
1374
|
def delimiter(self) -> str:
|
|
@@ -1314,8 +1393,8 @@ class TableDefinition(IODefinition):
|
|
|
1314
1393
|
@table_metadata.setter
|
|
1315
1394
|
def table_metadata(self, table_metadata: TableMetadata):
|
|
1316
1395
|
self._table_metadata = table_metadata
|
|
1317
|
-
|
|
1318
|
-
for col, val in table_metadata.
|
|
1396
|
+
# backward compatibility legacy support
|
|
1397
|
+
for col, val in table_metadata._get_legacy_column_metadata_for_manifest().items():
|
|
1319
1398
|
self.schema[col].metadata = {item['key']: item['value'] for item in val}
|
|
1320
1399
|
|
|
1321
1400
|
@property
|
|
@@ -1325,6 +1404,22 @@ class TableDefinition(IODefinition):
|
|
|
1325
1404
|
else:
|
|
1326
1405
|
return None
|
|
1327
1406
|
|
|
1407
|
+
@property
|
|
1408
|
+
def uri(self) -> str:
|
|
1409
|
+
return self._uri
|
|
1410
|
+
|
|
1411
|
+
@property
|
|
1412
|
+
def last_change_date(self) -> str:
|
|
1413
|
+
return self._last_change_date
|
|
1414
|
+
|
|
1415
|
+
@property
|
|
1416
|
+
def last_import_date(self) -> str:
|
|
1417
|
+
return self._last_import_date
|
|
1418
|
+
|
|
1419
|
+
@property
|
|
1420
|
+
def is_alias(self) -> bool:
|
|
1421
|
+
return self._is_alias
|
|
1422
|
+
|
|
1328
1423
|
def add_column(self, name: str, definition: ColumnDefinition = ColumnDefinition()):
|
|
1329
1424
|
"""
|
|
1330
1425
|
Add column definition, accepts either ColumnDefinition or a string
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# Python 3.7 support
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
1
4
|
import argparse
|
|
2
5
|
import csv
|
|
3
6
|
import glob
|
|
@@ -5,17 +8,18 @@ import json
|
|
|
5
8
|
import logging
|
|
6
9
|
import os
|
|
7
10
|
import sys
|
|
11
|
+
import warnings
|
|
8
12
|
from datetime import datetime
|
|
9
13
|
from pathlib import Path
|
|
10
|
-
from typing import List, Dict, Optional, Union
|
|
14
|
+
from typing import List, Dict, Optional, Union, OrderedDict
|
|
11
15
|
|
|
12
16
|
from deprecated import deprecated
|
|
13
17
|
from pygelf import GelfUdpHandler, GelfTcpHandler
|
|
14
18
|
from pytz import utc
|
|
15
19
|
|
|
16
20
|
from . import dao
|
|
17
|
-
from .exceptions import UserException
|
|
18
21
|
from .dao import ColumnDefinition
|
|
22
|
+
from .exceptions import UserException
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
def register_csv_dialect():
|
|
@@ -331,7 +335,9 @@ class CommonInterface:
|
|
|
331
335
|
delimiter: str = ',',
|
|
332
336
|
delete_where: dict = None,
|
|
333
337
|
write_always: bool = False,
|
|
334
|
-
schema:
|
|
338
|
+
schema: Union[
|
|
339
|
+
OrderedDict[str, ColumnDefinition], list[str]] = None,
|
|
340
|
+
has_header: Optional[bool] = None) -> dao.TableDefinition:
|
|
335
341
|
"""
|
|
336
342
|
Helper method for dao.TableDefinition creation along with the "manifest".
|
|
337
343
|
It initializes path according to the storage_stage type.
|
|
@@ -361,6 +367,12 @@ class CommonInterface:
|
|
|
361
367
|
else:
|
|
362
368
|
raise ValueError(f'Invalid storage_stage value "{storage_stage}". Supported values are: "in" or "out"!')
|
|
363
369
|
|
|
370
|
+
# for transition period we need to force legacy mode for csv files w headers.
|
|
371
|
+
force_legacy_mode = False
|
|
372
|
+
if not schema and not columns and primary_key:
|
|
373
|
+
warnings.warn('Primary key is set but columns are not. Forcing legacy mode for CSV file.',
|
|
374
|
+
DeprecationWarning)
|
|
375
|
+
force_legacy_mode = True
|
|
364
376
|
return dao.TableDefinition(name=name,
|
|
365
377
|
full_path=full_path,
|
|
366
378
|
is_sliced=is_sliced,
|
|
@@ -374,7 +386,9 @@ class CommonInterface:
|
|
|
374
386
|
delete_where=delete_where,
|
|
375
387
|
stage=storage_stage,
|
|
376
388
|
write_always=write_always,
|
|
377
|
-
schema=schema
|
|
389
|
+
schema=schema,
|
|
390
|
+
has_header=has_header,
|
|
391
|
+
force_legacy_mode=force_legacy_mode)
|
|
378
392
|
|
|
379
393
|
def create_in_table_definition(self, name: str,
|
|
380
394
|
is_sliced: bool = False,
|
|
@@ -412,18 +426,22 @@ class CommonInterface:
|
|
|
412
426
|
delete_where=delete_where,
|
|
413
427
|
schema=schema)
|
|
414
428
|
|
|
429
|
+
SCHEMA_TYPE = Union[Dict[str, ColumnDefinition], OrderedDict[str, ColumnDefinition], List[str]]
|
|
430
|
+
|
|
415
431
|
def create_out_table_definition(self, name: str,
|
|
416
432
|
is_sliced: bool = False,
|
|
417
433
|
destination: str = '',
|
|
418
434
|
primary_key: List[str] = None,
|
|
419
|
-
|
|
435
|
+
schema: SCHEMA_TYPE = None,
|
|
420
436
|
incremental: bool = None,
|
|
421
437
|
table_metadata: dao.TableMetadata = None,
|
|
422
438
|
enclosure: str = '"',
|
|
423
439
|
delimiter: str = ',',
|
|
424
440
|
delete_where: dict = None,
|
|
425
441
|
write_always: bool = False,
|
|
426
|
-
|
|
442
|
+
has_header: Optional[bool] = None,
|
|
443
|
+
**kwargs
|
|
444
|
+
) -> dao.TableDefinition:
|
|
427
445
|
"""
|
|
428
446
|
Helper method for output dao.TableDefinition creation along with the "manifest".
|
|
429
447
|
It initializes path in data/tables/out/ folder.
|
|
@@ -433,7 +451,9 @@ class CommonInterface:
|
|
|
433
451
|
is_sliced: True if the full_path points to a folder with sliced tables
|
|
434
452
|
destination: String name of the table in Storage.
|
|
435
453
|
primary_key: List with names of columns used for primary key.
|
|
436
|
-
|
|
454
|
+
schema: List of columns or mapping of column names and ColumnDefinition objects.
|
|
455
|
+
if list of strings is provided, the columns will be created with default settings
|
|
456
|
+
(BaseType.string)
|
|
437
457
|
incremental: Set to true to enable incremental loading
|
|
438
458
|
table_metadata: <.dao.TableMetadata> object containing column and table metadata
|
|
439
459
|
enclosure: str: CSV enclosure, by default "
|
|
@@ -441,6 +461,8 @@ class CommonInterface:
|
|
|
441
461
|
delete_where: Dict with settings for deleting rows
|
|
442
462
|
write_always: Bool: If true, the table will be saved to Storage even when the job execution
|
|
443
463
|
fails.
|
|
464
|
+
has_header:Optional[bool] = flag whether the header is present in the file,
|
|
465
|
+
if None legacy method is used
|
|
444
466
|
"""
|
|
445
467
|
|
|
446
468
|
return self._create_table_definition(name=name,
|
|
@@ -448,14 +470,15 @@ class CommonInterface:
|
|
|
448
470
|
is_sliced=is_sliced,
|
|
449
471
|
destination=destination,
|
|
450
472
|
primary_key=primary_key,
|
|
451
|
-
columns=columns,
|
|
473
|
+
columns=kwargs.get('columns'),
|
|
452
474
|
incremental=incremental,
|
|
453
475
|
table_metadata=table_metadata,
|
|
454
476
|
enclosure=enclosure,
|
|
455
477
|
delimiter=delimiter,
|
|
456
478
|
delete_where=delete_where,
|
|
457
479
|
write_always=write_always,
|
|
458
|
-
schema=schema
|
|
480
|
+
schema=schema,
|
|
481
|
+
has_header=has_header)
|
|
459
482
|
|
|
460
483
|
# # File processing
|
|
461
484
|
|
|
@@ -927,7 +950,7 @@ class CommonInterface:
|
|
|
927
950
|
"""
|
|
928
951
|
|
|
929
952
|
if not legacy_manifest:
|
|
930
|
-
legacy_manifest = self.environment_variables.data_type_support not in ('authoritative', '
|
|
953
|
+
legacy_manifest = self.environment_variables.data_type_support not in ('authoritative', 'hints')
|
|
931
954
|
|
|
932
955
|
manifest = io_definition.get_manifest_dictionary(legacy_queue=self.is_legacy_queue,
|
|
933
956
|
legacy_manifest=legacy_manifest)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/requires.txt
RENAMED
|
File without changes
|
{keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/top_level.txt
RENAMED
|
File without changes
|