fivetran-connector-sdk 1.3.1__py3-none-any.whl → 1.3.3__py3-none-any.whl

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.
@@ -17,6 +17,7 @@ import time
17
17
  import traceback
18
18
  import re
19
19
  import socket
20
+ import ast
20
21
 
21
22
  from concurrent import futures
22
23
  from datetime import datetime
@@ -31,13 +32,13 @@ from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
31
32
 
32
33
  # Version format: <major_version>.<minor_version>.<patch_version>
33
34
  # (where Major Version = 1 for GA, Minor Version is incremental MM from Jan 25 onwards, Patch Version is incremental within a month)
34
- __version__ = "1.3.1"
35
+ __version__ = "1.3.3"
35
36
 
36
37
  MAC_OS = "mac"
37
38
  WIN_OS = "windows"
38
39
  LINUX_OS = "linux"
39
40
 
40
- TESTER_VERSION = "0.25.0411.001"
41
+ TESTER_VERSION = "0.25.0423.001"
41
42
  TESTER_FILENAME = "run_sdk_tester.jar"
42
43
  VERSION_FILENAME = "version.txt"
43
44
  UPLOAD_FILENAME = "code.zip"
@@ -52,6 +53,7 @@ MAX_RETRIES = 3
52
53
  LOGGING_PREFIX = "Fivetran-Connector-SDK"
53
54
  LOGGING_DELIMITER = ": "
54
55
  VIRTUAL_ENV_CONFIG = "pyvenv.cfg"
56
+ ROOT_FILENAME = "connector.py"
55
57
 
56
58
  # Compile patterns used in the implementation
57
59
  WORD_DASH_DOT_PATTERN = re.compile(r'^[\w.-]*$')
@@ -85,6 +87,7 @@ MAX_CONFIG_FIELDS = 100
85
87
  SUPPORTED_PYTHON_VERSIONS = ["3.12.8", "3.11.11", "3.10.16", "3.9.21"]
86
88
  DEFAULT_PYTHON_VERSION = "3.12.8"
87
89
  FIVETRAN_HD_AGENT_ID = "FIVETRAN_HD_AGENT_ID"
90
+ UTF_8 = "utf-8"
88
91
 
89
92
 
90
93
  class Logging:
@@ -144,15 +147,23 @@ class Logging:
144
147
  Logging.__log(Logging.Level.WARNING, message)
145
148
 
146
149
  @staticmethod
147
- def severe(message: str):
150
+ def severe(message: str, exception: Exception = None):
148
151
  """Logs a severe-level message.
149
152
 
150
153
  Args:
151
154
  message (str): The message to log.
155
+ exception (Exception, optional): Exception to be logged if provided.
152
156
  """
153
157
  if Logging.LOG_LEVEL <= Logging.Level.SEVERE:
154
158
  Logging.__log(Logging.Level.SEVERE, message)
155
159
 
160
+ if exception:
161
+ exc_type, exc_value, exc_traceback = type(exception), exception, exception.__traceback__
162
+ tb_str = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback, limit=1))
163
+
164
+ for error in tb_str.split("\n"):
165
+ Logging.__log(Logging.Level.SEVERE, error)
166
+
156
167
 
157
168
  class Operations:
158
169
  @staticmethod
@@ -288,7 +299,7 @@ def check_newer_version():
288
299
 
289
300
  if os.path.isfile(last_check_file_path):
290
301
  # Is it time to check again?
291
- with open(last_check_file_path, 'r') as f_in:
302
+ with open(last_check_file_path, 'r', encoding=UTF_8) as f_in:
292
303
  timestamp = int(f_in.read())
293
304
  if (int(time.time()) - timestamp) < ONE_DAY_IN_SEC:
294
305
  return
@@ -304,7 +315,7 @@ def check_newer_version():
304
315
  print_library_log(f"[notice] A new release of 'fivetran-connector-sdk' is available: {latest_version}\n" +
305
316
  f"[notice] To update, run: pip install --upgrade fivetran-connector-sdk\n")
306
317
 
307
- with open(last_check_file_path, 'w') as f_out:
318
+ with open(last_check_file_path, 'w', encoding=UTF_8) as f_out:
308
319
  f_out.write(f"{int(time.time())}")
309
320
  break
310
321
  except Exception:
@@ -354,7 +365,6 @@ def _map_data_to_columns(data: dict, columns: dict) -> dict:
354
365
  map_defined_data_type(columns, key, mapped_data, v)
355
366
  else:
356
367
  map_inferred_data_type(key, mapped_data, v)
357
-
358
368
  return mapped_data
359
369
 
360
370
 
@@ -426,6 +436,36 @@ def map_defined_data_type(columns, k, mapped_data, v):
426
436
  else:
427
437
  raise ValueError(f"Unsupported data type encountered: {columns[k].type}. Please use valid data types.")
428
438
 
439
+ def _warn_exit_usage(filename, line_no, func):
440
+ print_library_log(f"Avoid using {func} to exit from the Python code as this can cause the connector to become stuck. Throw a error if required " +
441
+ f"at: {filename}:{line_no}. See the Technical Reference for details: https://fivetran.com/docs/connector-sdk/technical-reference#handlingexceptions",
442
+ Logging.Level.WARNING)
443
+
444
+ def _exit_check(project_path):
445
+ """Checks for the presence of 'exit()' in the calling code.
446
+ Args:
447
+ project_path: The absolute project_path to check exit in the connector.py file in the project.
448
+ """
449
+ # We expect the connector.py to catch errors or throw exceptions
450
+ # This is a warning shown to let the customer know that we expect either the yield call or error thrown
451
+ # exit() or sys.exit() in between some yields can cause the connector to be stuck without processing further upsert calls
452
+
453
+ filepath = os.path.join(project_path, ROOT_FILENAME)
454
+ with open(filepath, "r", encoding=UTF_8) as f:
455
+ try:
456
+ tree = ast.parse(f.read())
457
+ for node in ast.walk(tree):
458
+ if isinstance(node, ast.Call):
459
+ if isinstance(node.func, ast.Name) and node.func.id == "exit":
460
+ _warn_exit_usage(ROOT_FILENAME, node.lineno, "exit()")
461
+ elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
462
+ if node.func.attr == "_exit" and node.func.value.id == "os":
463
+ _warn_exit_usage(ROOT_FILENAME, node.lineno, "os._exit()")
464
+ if node.func.attr == "exit" and node.func.value.id == "sys":
465
+ _warn_exit_usage(ROOT_FILENAME, node.lineno, "sys.exit()")
466
+ except SyntaxError as e:
467
+ print_library_log(f"SyntaxError in {ROOT_FILENAME}: {e}", Logging.Level.SEVERE)
468
+
429
469
 
430
470
  def _parse_datetime_str(dt):
431
471
  return datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S.%f%z" if '.' in dt else "%Y-%m-%dT%H:%M:%S%z")
@@ -555,7 +595,7 @@ def get_available_port():
555
595
  def update_base_url_if_required():
556
596
  config_file_path = os.path.join(_tester_root_dir(), CONFIG_FILE)
557
597
  if os.path.isfile(config_file_path):
558
- with open(config_file_path, 'r') as f:
598
+ with open(config_file_path, 'r', encoding=UTF_8) as f:
559
599
  data = json.load(f)
560
600
  base_url = data.get('production_base_url')
561
601
  if base_url is not None:
@@ -705,7 +745,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
705
745
  Returns:
706
746
  list[str]: A list of dependencies as strings.
707
747
  """
708
- with open(file_path, 'r') as f:
748
+ with open(file_path, 'r', encoding=UTF_8) as f:
709
749
  return f.read().splitlines()
710
750
 
711
751
  @staticmethod
@@ -836,7 +876,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
836
876
  if force:
837
877
  requirements = tmp_requirements
838
878
 
839
- with open(REQUIREMENTS_TXT, "w") as file:
879
+ with open(REQUIREMENTS_TXT, "w", encoding=UTF_8) as file:
840
880
  file.write("\n".join(requirements.values()))
841
881
  print_library_log(f"`{REQUIREMENTS_TXT}` has been updated successfully")
842
882
 
@@ -869,7 +909,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
869
909
  if os.path.exists(REQUIREMENTS_TXT):
870
910
  requirements = self.fetch_requirements_as_dict(self, os.path.join(project_path, 'requirements.txt'))
871
911
  else:
872
- with open(REQUIREMENTS_TXT, 'w'):
912
+ with open(REQUIREMENTS_TXT, 'w', encoding=UTF_8):
873
913
  pass
874
914
  requirements = {}
875
915
  print_library_log("Adding `requirements.txt` file to your project folder.", Logging.Level.WARNING)
@@ -904,8 +944,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
904
944
  connection_config = {
905
945
  "schema": connection,
906
946
  "secrets_list": secrets_list,
907
- "sync_method": "DIRECT",
908
- "custom_payloads": []
947
+ "sync_method": "DIRECT"
909
948
  }
910
949
 
911
950
  # args.python_version is already validated in validate_deploy_parameters - so its safe to add in connection_config
@@ -1373,7 +1412,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1373
1412
  version_file = os.path.join(tester_root_dir, VERSION_FILENAME)
1374
1413
  if os.path.isfile(version_file):
1375
1414
  # Check version number & update if different
1376
- with open(version_file, 'r') as fi:
1415
+ with open(version_file, 'r', encoding=UTF_8) as fi:
1377
1416
  current_version = fi.readline()
1378
1417
 
1379
1418
  if current_version != TESTER_VERSION:
@@ -1420,6 +1459,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1420
1459
  self.validate_requirements_file(project_path, False)
1421
1460
  print_library_log(f"Debugging connector at: {project_path}")
1422
1461
  available_port = get_available_port()
1462
+ _exit_check(project_path)
1423
1463
 
1424
1464
  if available_port is None:
1425
1465
  raise RuntimeError("SEVERE: Unable to allocate a port in the range 50051-50061. "
@@ -1871,7 +1911,7 @@ def validate_and_load_configuration(args, configuration):
1871
1911
  if configuration:
1872
1912
  json_filepath = os.path.join(args.project_path, args.configuration)
1873
1913
  if os.path.isfile(json_filepath):
1874
- with open(json_filepath, 'r') as fi:
1914
+ with open(json_filepath, 'r', encoding=UTF_8) as fi:
1875
1915
  configuration = json.load(fi)
1876
1916
  if len(configuration) > MAX_CONFIG_FIELDS:
1877
1917
  raise ValueError(f"Configuration field count exceeds maximum of {MAX_CONFIG_FIELDS}. Reduce the field count.")
@@ -1890,8 +1930,12 @@ def validate_and_load_configuration(args, configuration):
1890
1930
  def validate_and_load_state(args, state):
1891
1931
  if state:
1892
1932
  json_filepath = os.path.join(args.project_path, args.state)
1933
+ else:
1934
+ json_filepath = os.path.join(args.project_path, "files", "state.json")
1935
+
1936
+ if os.path.exists(json_filepath):
1893
1937
  if os.path.isfile(json_filepath):
1894
- with open(json_filepath, 'r') as fi:
1938
+ with open(json_filepath, 'r', encoding=UTF_8) as fi:
1895
1939
  state = json.load(fi)
1896
1940
  elif state.lstrip().startswith("{"):
1897
1941
  state = json.loads(state)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fivetran_connector_sdk
3
- Version: 1.3.1
3
+ Version: 1.3.3
4
4
  Summary: Build custom connectors on Fivetran platform
5
5
  Author-email: Fivetran <developers@fivetran.com>
6
6
  Project-URL: Homepage, https://fivetran.com/docs/connectors/connector-sdk
@@ -1,4 +1,4 @@
1
- fivetran_connector_sdk/__init__.py,sha256=4ovEZ7G46t5JFEAUmyGNksa0RVs9x5eKsYfzmyBDuNs,81121
1
+ fivetran_connector_sdk/__init__.py,sha256=sltQUs9T18U5aCkagxVdfT6-BO4d81FWWyaNEBj5NJ8,83830
2
2
  fivetran_connector_sdk/protos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  fivetran_connector_sdk/protos/common_pb2.py,sha256=kUwVcyZHgLigNR-KnHZn7dHrlxaMnUXqzprsRx6T72M,6831
4
4
  fivetran_connector_sdk/protos/common_pb2.pyi,sha256=S0hdIzoXyyOKD5cjiGeDDLYpQ9J3LjAvu4rCj1JvJWE,9038
@@ -6,8 +6,8 @@ fivetran_connector_sdk/protos/common_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXH
6
6
  fivetran_connector_sdk/protos/connector_sdk_pb2.py,sha256=9Ke_Ti1s0vAeXapfXT-EryrT2-TSGQb8mhs4gxTpUMk,7732
7
7
  fivetran_connector_sdk/protos/connector_sdk_pb2.pyi,sha256=FWYxRgshEF3QDYAE0TM_mv4N2gGvkxCH_uPpxnMc4oA,8406
8
8
  fivetran_connector_sdk/protos/connector_sdk_pb2_grpc.py,sha256=ZfJLp4DW7uP4pFOZ74s_wQ6tD3eIPi-08UfnLwe4tzo,7163
9
- fivetran_connector_sdk-1.3.1.dist-info/METADATA,sha256=qEnx2X-ac7iQhdJ7bx0p9yKhliRUAz_cxhRRgX3P-Jk,2967
10
- fivetran_connector_sdk-1.3.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
11
- fivetran_connector_sdk-1.3.1.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
- fivetran_connector_sdk-1.3.1.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
- fivetran_connector_sdk-1.3.1.dist-info/RECORD,,
9
+ fivetran_connector_sdk-1.3.3.dist-info/METADATA,sha256=6T4trCMBQS0wnx2-iMOyX01xpU8yo3R9VqRI-d5uwhQ,2967
10
+ fivetran_connector_sdk-1.3.3.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
11
+ fivetran_connector_sdk-1.3.3.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
+ fivetran_connector_sdk-1.3.3.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
+ fivetran_connector_sdk-1.3.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5