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.
- fivetran_connector_sdk/__init__.py +73 -30
- {fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/METADATA +1 -1
- {fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/RECORD +6 -6
- {fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/WHEEL +1 -1
- {fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/entry_points.txt +0 -0
- {fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/top_level.txt +0 -0
@@ -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.
|
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
|
-
|
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
|
-
|
819
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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,
|
909
|
-
if os.path.exists(
|
910
|
-
requirements = self.fetch_requirements_as_dict(self,
|
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(
|
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 ==
|
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\.
|
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
|
-
|
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,
|
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
|
-
|
1786
|
-
|
1787
|
-
|
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
|
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
|
+
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=
|
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.
|
10
|
-
fivetran_connector_sdk-1.
|
11
|
-
fivetran_connector_sdk-1.
|
12
|
-
fivetran_connector_sdk-1.
|
13
|
-
fivetran_connector_sdk-1.
|
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,,
|
{fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/entry_points.txt
RENAMED
File without changes
|
{fivetran_connector_sdk-1.3.4.dist-info → fivetran_connector_sdk-1.4.1.dist-info}/top_level.txt
RENAMED
File without changes
|