fivetran-connector-sdk 1.3.4__py3-none-any.whl → 1.4.1__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.
@@ -32,7 +32,7 @@ from fivetran_connector_sdk.protos import connector_sdk_pb2_grpc
32
32
 
33
33
  # Version format: <major_version>.<minor_version>.<patch_version>
34
34
  # (where Major Version = 1 for GA, Minor Version is incremental MM from Jan 25 onwards, Patch Version is incremental within a month)
35
- __version__ = "1.3.4"
35
+ __version__ = "1.4.1"
36
36
 
37
37
  MAC_OS = "mac"
38
38
  WIN_OS = "windows"
@@ -110,12 +110,24 @@ class Logging:
110
110
  if DEBUGGING:
111
111
  current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
112
112
  escaped_message = json.dumps(message).strip('"')
113
- print(f"{current_time} {level.name}: {escaped_message}")
113
+ print(f"{Logging._get_color(level)}{current_time} {level.name}: {escaped_message} {Logging._reset_color()}")
114
114
  else:
115
115
  escaped_message = json.dumps(message)
116
116
  log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "connector_sdk"}}'
117
117
  print(log_message)
118
118
 
119
+ @staticmethod
120
+ def _get_color(level):
121
+ if level == Logging.Level.WARNING:
122
+ return "\033[93m" # Yellow
123
+ elif level == Logging.Level.SEVERE:
124
+ return "\033[91m" # Red
125
+ return ""
126
+
127
+ @staticmethod
128
+ def _reset_color():
129
+ return "\033[0m"
130
+
119
131
  @staticmethod
120
132
  def fine(message: str):
121
133
  """Logs a fine-level message.
@@ -313,7 +325,7 @@ def check_newer_version():
313
325
  latest_version = data["info"]["version"]
314
326
  if __version__ < latest_version:
315
327
  print_library_log(f"[notice] A new release of 'fivetran-connector-sdk' is available: {latest_version}\n" +
316
- f"[notice] To update, run: pip install --upgrade fivetran-connector-sdk\n")
328
+ "[notice] To update, run: pip install --upgrade fivetran-connector-sdk\n")
317
329
 
318
330
  with open(last_check_file_path, 'w', encoding=UTF_8) as f_out:
319
331
  f_out.write(f"{int(time.time())}")
@@ -573,7 +585,7 @@ def print_library_log(message: str, level: Logging.Level = Logging.Level.INFO):
573
585
  if DEBUGGING or EXECUTED_VIA_CLI:
574
586
  current_time = datetime.now().strftime("%b %d, %Y %I:%M:%S %p")
575
587
  escaped_message = json.dumps(message).strip('"')
576
- print(f"{current_time} {level.name} {LOGGING_PREFIX}: {escaped_message}")
588
+ print(f"{Logging._get_color(level)}{current_time} {level.name} {LOGGING_PREFIX}: {escaped_message} {Logging._reset_color()}")
577
589
  else:
578
590
  escaped_message = json.dumps(LOGGING_PREFIX + LOGGING_DELIMITER + message)
579
591
  log_message = f'{{"level":"{level.name}", "message": {escaped_message}, "message_origin": "library"}}'
@@ -760,6 +772,8 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
760
772
  values are the full dependency strings.
761
773
  """
762
774
  requirements_dict = {}
775
+ if not os.path.exists(file_path):
776
+ return requirements_dict
763
777
  for requirement in self.fetch_requirements_from_file(file_path):
764
778
  requirement = requirement.strip()
765
779
  if not requirement or requirement.startswith("#"): # Skip empty lines and comments
@@ -793,7 +807,10 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
793
807
  ignored_dirs = EXCLUDED_PIPREQS_DIRS + venv_dirs if venv_dirs else EXCLUDED_PIPREQS_DIRS
794
808
 
795
809
  # tmp_requirements is only generated when pipreqs command is successful
810
+ requirements_file_path = os.path.join(project_path, REQUIREMENTS_TXT)
796
811
  tmp_requirements_file_path = os.path.join(project_path, 'tmp_requirements.txt')
812
+ # copying packages of requirements file to tmp file to handle pipreqs fail use-case
813
+ self.copy_requirements_file_to_tmp_requirements_file(os.path.join(project_path, REQUIREMENTS_TXT), tmp_requirements_file_path)
797
814
  # Run the pipreqs command and capture stderr
798
815
  attempt = 0
799
816
  while attempt < MAX_RETRIES:
@@ -815,13 +832,11 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
815
832
  time.sleep(retry_after)
816
833
  else:
817
834
  print_library_log(f"pipreqs failed after {MAX_RETRIES} attempts with:", Logging.Level.SEVERE)
818
- print(result.stderr)
819
- sys.exit(1)
835
+ print_library_log(result.stderr, Logging.Level.SEVERE)
836
+ print_library_log(f"Skipping validation of requirements.txt due to error connecting to PyPI (Python Package Index) APIs. Continuing with {'deploy' if is_deploy else 'debug'}...", Logging.Level.SEVERE)
820
837
 
821
838
  tmp_requirements = self.fetch_requirements_as_dict(self, tmp_requirements_file_path)
822
- tmp_requirements.pop("fivetran_connector_sdk")
823
- if tmp_requirements.get('requests') is not None:
824
- tmp_requirements.pop("requests")
839
+ self.remove_unwanted_packages(tmp_requirements)
825
840
  os.remove(tmp_requirements_file_path)
826
841
 
827
842
  # remove corrupt requirements listed by pipreqs
@@ -830,7 +845,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
830
845
  del tmp_requirements[requirement]
831
846
 
832
847
  if len(tmp_requirements) > 0:
833
- requirements = self.load_or_add_requirements_file(project_path)
848
+ requirements = self.load_or_add_requirements_file(requirements_file_path)
834
849
 
835
850
  version_mismatch_deps = {key: tmp_requirements[key] for key in
836
851
  (requirements.keys() & tmp_requirements.keys())
@@ -876,15 +891,18 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
876
891
  if force:
877
892
  requirements = tmp_requirements
878
893
 
879
- with open(REQUIREMENTS_TXT, "w", encoding=UTF_8) as file:
894
+ with open(requirements_file_path, "w", encoding=UTF_8) as file:
880
895
  file.write("\n".join(requirements.values()))
881
896
  print_library_log(f"`{REQUIREMENTS_TXT}` has been updated successfully")
882
897
 
883
898
  else:
884
- if os.path.exists(REQUIREMENTS_TXT):
899
+ if os.path.exists(requirements_file_path):
885
900
  print_library_log("`requirements.txt` is not required as no additional "
886
901
  "Python libraries are required or all required libraries for "
887
902
  "your code are pre-installed.", Logging.Level.WARNING)
903
+ with open(requirements_file_path, 'w') as file:
904
+ file.write("")
905
+
888
906
 
889
907
  if is_deploy: print_library_log("Successful validation of requirements.txt")
890
908
 
@@ -894,7 +912,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
894
912
  if 'requests' in unused_deps:
895
913
  log_unused_deps_error("requests", "2.32.3")
896
914
  print_library_log("The following dependencies are not needed, "
897
- f"they are not used or already installed. Please remove them from {REQUIREMENTS_TXT}:")
915
+ f"they are not used or already installed. Please remove them from {REQUIREMENTS_TXT}:", Logging.Level.WARNING)
898
916
  print(*unused_deps)
899
917
 
900
918
  def handle_missing_deps(self, missing_deps):
@@ -905,16 +923,30 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
905
923
  "#workingwithrequirementstxtfile", Logging.Level.SEVERE)
906
924
  print(*list(missing_deps.values()))
907
925
 
908
- def load_or_add_requirements_file(self, project_path):
909
- if os.path.exists(REQUIREMENTS_TXT):
910
- requirements = self.fetch_requirements_as_dict(self, os.path.join(project_path, 'requirements.txt'))
926
+ def load_or_add_requirements_file(self, requirements_file_path):
927
+ if os.path.exists(requirements_file_path):
928
+ requirements = self.fetch_requirements_as_dict(self, requirements_file_path)
911
929
  else:
912
- with open(REQUIREMENTS_TXT, 'w', encoding=UTF_8):
930
+ with open(requirements_file_path, 'w', encoding=UTF_8):
913
931
  pass
914
932
  requirements = {}
915
933
  print_library_log("Adding `requirements.txt` file to your project folder.", Logging.Level.WARNING)
916
934
  return requirements
917
935
 
936
+ def copy_requirements_file_to_tmp_requirements_file(self, requirements_file_path: str, tmp_requirements_file_path):
937
+ if os.path.exists(requirements_file_path):
938
+ requirements_file_content = self.fetch_requirements_from_file(requirements_file_path)
939
+ with open(tmp_requirements_file_path, 'w') as file:
940
+ file.write("\n".join(requirements_file_content))
941
+
942
+ @staticmethod
943
+ def remove_unwanted_packages(requirements: dict):
944
+ # remove the `fivetran_connector_sdk` and `requests` packages from requirements as we already pre-installed them.
945
+ if requirements.get("fivetran_connector_sdk") is not None:
946
+ requirements.pop("fivetran_connector_sdk")
947
+ if requirements.get('requests') is not None:
948
+ requirements.pop("requests")
949
+
918
950
  # Call this method to deploy the connector to Fivetran platform
919
951
  def deploy(self, args: dict, deploy_key: str, group: str, connection: str, hd_agent_id: str, configuration: dict = None):
920
952
  """Deploys the connector to the Fivetran platform.
@@ -1188,7 +1220,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1188
1220
  if INSTALLATION_SCRIPT in files:
1189
1221
  custom_driver_installation_script_exists = True
1190
1222
  for file in files:
1191
- if file == "connector.py":
1223
+ if file == ROOT_FILENAME:
1192
1224
  connector_file_exists = True
1193
1225
  file_path = os.path.join(root, file)
1194
1226
  arcname = os.path.relpath(file_path, project_path)
@@ -1506,7 +1538,7 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1506
1538
  Yields:
1507
1539
  str: Each line from the stream after replacing the matched pattern with the replacement string.
1508
1540
  """
1509
- pattern = r'com\.fivetran\.fivetran_sdk.*\.tools\.testers\.\S+'
1541
+ pattern = r'com\.fivetran\.partner_sdk.*\.tools\.testers\.\S+'
1510
1542
 
1511
1543
  for line in iter(stream.readline, ""):
1512
1544
  if not re.search(pattern, line):
@@ -1542,10 +1574,10 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1542
1574
 
1543
1575
  popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
1544
1576
  for line in Connector.process_stream(popen.stderr):
1545
- yield line
1577
+ yield Connector._maybe_colorize_jar_output(line)
1546
1578
 
1547
1579
  for line in Connector.process_stream(popen.stdout):
1548
- yield line
1580
+ yield Connector._maybe_colorize_jar_output(line)
1549
1581
  popen.stdout.close()
1550
1582
  return_code = popen.wait()
1551
1583
  if return_code:
@@ -1726,19 +1758,30 @@ class Connector(connector_sdk_pb2_grpc.ConnectorServicer):
1726
1758
  print_library_log(error_message, Logging.Level.SEVERE)
1727
1759
  raise RuntimeError(error_message) from e
1728
1760
 
1761
+ @staticmethod
1762
+ def _maybe_colorize_jar_output(line: str) -> str:
1763
+ if not DEBUGGING:
1764
+ return line
1765
+
1766
+ if "SEVERE" in line or "ERROR" in line or "Exception" in line or "FAILED" in line:
1767
+ return f"\033[91m{line}\033[0m" # Red
1768
+ elif "WARN" in line or "WARNING" in line:
1769
+ return f"\033[93m{line}\033[0m" # Yellow
1770
+ return line
1771
+
1729
1772
 
1730
- def find_connector_object(project_path) -> Connector:
1773
+ def find_connector_object(project_path) -> Optional[Connector]:
1731
1774
  """Finds the connector object in the given project path.
1732
1775
  Args:
1733
1776
  project_path (str): The path to the project.
1734
1777
 
1735
1778
  Returns:
1736
- object: The connector object.
1779
+ Optional[Connector]: The connector object or None if not found.
1737
1780
  """
1738
1781
 
1739
1782
  sys.path.append(project_path) # Allows python interpreter to search for modules in this path
1740
1783
  module_name = "connector_connector_code"
1741
- connector_py = os.path.join(project_path, "connector.py")
1784
+ connector_py = os.path.join(project_path, ROOT_FILENAME)
1742
1785
  try:
1743
1786
  spec = importlib.util.spec_from_file_location(module_name, connector_py)
1744
1787
  module = importlib.util.module_from_spec(spec)
@@ -1752,11 +1795,11 @@ def find_connector_object(project_path) -> Connector:
1752
1795
  except FileNotFoundError:
1753
1796
  print_library_log(
1754
1797
  "The connector object is missing in the current directory. Please ensure that you are running the command from correct directory or that you have defined a connector object using the correct syntax in your `connector.py` file. Reference: https://fivetran.com/docs/connectors/connector-sdk/technical-reference#technicaldetailsrequiredobjectconnector", Logging.Level.SEVERE)
1755
- return
1798
+ return None
1756
1799
 
1757
1800
  print_library_log(
1758
1801
  "The connector object is missing. Please ensure that you have defined a connector object using the correct syntax in your `connector.py` file. Reference: https://fivetran.com/docs/connectors/connector-sdk/technical-reference#technicaldetailsrequiredobjectconnector", Logging.Level.SEVERE)
1759
- return
1802
+ return None
1760
1803
 
1761
1804
 
1762
1805
  def suggest_correct_command(input_command: str) -> bool:
@@ -1782,9 +1825,9 @@ def suggest_correct_command(input_command: str) -> bool:
1782
1825
 
1783
1826
 
1784
1827
  def print_suggested_command_message(valid_command: str, input_command: str) -> None:
1785
- print(f"`fivetran {input_command}` is not a valid command.")
1786
- print(f"Did you mean `fivetran {valid_command}`?")
1787
- print("Use `fivetran --help` for more details.")
1828
+ print_library_log(f"`fivetran {input_command}` is not a valid command.", Logging.Level.SEVERE)
1829
+ print_library_log(f"Did you mean `fivetran {valid_command}`?", Logging.Level.SEVERE)
1830
+ print_library_log("Use `fivetran --help` for more details.", Logging.Level.SEVERE)
1788
1831
 
1789
1832
 
1790
1833
  def edit_distance(first_string: str, second_string: str) -> int:
@@ -1829,7 +1872,7 @@ def get_input_from_cli(prompt : str, default_value: str) -> str:
1829
1872
 
1830
1873
  if not value:
1831
1874
  raise ValueError("Missing required input: Expected a value but received None")
1832
- return value or None
1875
+ return value
1833
1876
 
1834
1877
 
1835
1878
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fivetran_connector_sdk
3
- Version: 1.3.4
3
+ Version: 1.4.1
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=GsNVJZJiFWlhdxH0mFzrdByWyHhsBT4tGWvb7RdAar0,83938
1
+ fivetran_connector_sdk/__init__.py,sha256=22ATq-tnC_JYy2LUWh4yLWaTopg2v_gOah4k_10WoIQ,86407
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.4.dist-info/METADATA,sha256=UYYWBmVMvJl5SWnE1Gh2yPWvWzo32Grr4NMb0cXgTcs,2967
10
- fivetran_connector_sdk-1.3.4.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
11
- fivetran_connector_sdk-1.3.4.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
- fivetran_connector_sdk-1.3.4.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
- fivetran_connector_sdk-1.3.4.dist-info/RECORD,,
9
+ fivetran_connector_sdk-1.4.1.dist-info/METADATA,sha256=F5N5tcF6rl-23TOZohrKyk8uELa5kadAYPE8XUFy7s0,2967
10
+ fivetran_connector_sdk-1.4.1.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
11
+ fivetran_connector_sdk-1.4.1.dist-info/entry_points.txt,sha256=uQn0KPnFlQmXJfxlk0tifdNsSXWfVlnAFzNqjXZM_xM,57
12
+ fivetran_connector_sdk-1.4.1.dist-info/top_level.txt,sha256=-_xk2MFY4psIh7jw1lJePMzFb5-vask8_ZtX-UzYWUI,23
13
+ fivetran_connector_sdk-1.4.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5