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.
Files changed (18) hide show
  1. {keboola.component-1.6.0/src/keboola.component.egg-info → keboola.component-1.6.1}/PKG-INFO +1 -1
  2. {keboola.component-1.6.0 → keboola.component-1.6.1}/setup.py +1 -1
  3. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/dao.py +165 -70
  4. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/interface.py +33 -10
  5. {keboola.component-1.6.0 → keboola.component-1.6.1/src/keboola.component.egg-info}/PKG-INFO +1 -1
  6. {keboola.component-1.6.0 → keboola.component-1.6.1}/LICENSE +0 -0
  7. {keboola.component-1.6.0 → keboola.component-1.6.1}/README.md +0 -0
  8. {keboola.component-1.6.0 → keboola.component-1.6.1}/setup.cfg +0 -0
  9. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/__init__.py +0 -0
  10. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/base.py +0 -0
  11. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/exceptions.py +0 -0
  12. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/sync_actions.py +0 -0
  13. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola/component/table_schema.py +0 -0
  14. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/SOURCES.txt +0 -0
  15. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/dependency_links.txt +0 -0
  16. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/not-zip-safe +0 -0
  17. {keboola.component-1.6.0 → keboola.component-1.6.1}/src/keboola.component.egg-info/requires.txt +0 -0
  18. {keboola.component-1.6.0 → keboola.component-1.6.1}/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.0
3
+ Version: 1.6.1
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.0",
16
+ version="1.6.1",
17
17
  author="Keboola KDS Team",
18
18
  project_urls=project_urls,
19
19
  setup_requires=['pytest-runner', 'flake8'],
@@ -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
- from collections import OrderedDict
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(dict):
445
+ class DataType:
424
446
  dtype: str
425
- length: Optional[int] = None
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(DataType):
434
- def __init__(self, dtype: SupportedDataTypes = SupportedDataTypes.STRING, length: Optional[int] = None,
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
- columns: Optional[List[str]] = None,
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] = 'in',
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
- uri: Optional[str] = None,
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.columns = columns
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] = None, legacy_queue: bool = False,
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.get_column_metadata_for_manifest(),
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.columns and not self.stage == 'in':
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
- self._schema = value
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="Columns can be set by add_columns method")
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
- if val:
1244
- if not isinstance(val, list):
1245
- raise TypeError("Columns must be a list")
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
- self.schema = OrderedDict()
1248
- for col in val:
1249
- self.schema[col] = ColumnDefinition()
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 isinstance(self.schema, OrderedDict):
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
- for col in primary_key:
1289
- if col in self.schema:
1290
- self.schema[col].primary_key = True
1291
- else:
1292
- raise UserException(f"Primary key column {col} not found in columns")
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.get_column_metadata_for_manifest().items():
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: List[ColumnDefinition] = None) -> dao.TableDefinition:
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
- columns: List[str] = None,
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
- schema: List[ColumnDefinition] = False) -> dao.TableDefinition:
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
- columns: List of columns for headless CSV files
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', 'hint')
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: keboola.component
3
- Version: 1.6.0
3
+ Version: 1.6.1
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