src-connectors 0.1.0__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.
common/.gitkeep ADDED
File without changes
common/__init__.py ADDED
File without changes
@@ -0,0 +1,18 @@
1
+ import sys
2
+ from typing import Any
3
+
4
+ __all__ = ["SQLServerConnector", "SparkConnector"]
5
+
6
+
7
+ def __getattr__(name: str) -> Any:
8
+ if name == "SQLServerConnector":
9
+ from src_connectors.src_sqlserver.connector import SQLServerConnector
10
+
11
+ return SQLServerConnector
12
+
13
+ if name == "SparkConnector":
14
+ from src_connectors.src_spark.connector import SparkConnector
15
+
16
+ return SparkConnector
17
+
18
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@@ -0,0 +1,3 @@
1
+ from .connector import BaseConnector
2
+
3
+ __all__ = ["BaseConnector"]
@@ -0,0 +1,31 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Optional
3
+
4
+ class BaseConnector(ABC):
5
+ """
6
+ Abstract base class for database connectors.
7
+ """
8
+
9
+ @abstractmethod
10
+ def connect(self) -> Any:
11
+ """Establish a connection to the database."""
12
+ pass
13
+
14
+ @abstractmethod
15
+ def close(self) -> None:
16
+ """Close the database connection."""
17
+ pass
18
+
19
+ @abstractmethod
20
+ def execute_query(
21
+ self,
22
+ query: str,
23
+ params: Optional[tuple] = None,
24
+ stream: bool = False,
25
+ batch_size: int = 1000,
26
+ output_type: str = "list",
27
+ **kwargs: Any
28
+ ) -> Any:
29
+ """Execute a query and return the results."""
30
+ pass
31
+
@@ -0,0 +1,6 @@
1
+ from src_connectors.src_spark.connector import SparkConnector
2
+ from src_connectors.src_spark.configs.core_config import SparkCoreConfig
3
+ from src_connectors.src_spark.configs.iceberg import SparkIcebergConfig
4
+ from src_connectors.src_spark.configs.jdbc import SparkJdbcConfig
5
+
6
+ __all__ = ["SparkConnector", "SparkCoreConfig", "SparkIcebergConfig", "SparkJdbcConfig"]
@@ -0,0 +1,6 @@
1
+ from src_connectors.src_spark.configs.core_config import SparkCoreConfig
2
+ from src_connectors.src_spark.configs.iceberg import SparkIcebergConfig
3
+ from src_connectors.src_spark.configs.jdbc import SparkJdbcConfig
4
+ from src_connectors.src_spark.configs.base import SparkBaseComponent
5
+
6
+ __all__ = ["SparkCoreConfig", "SparkIcebergConfig", "SparkJdbcConfig", "SparkBaseComponent"]
@@ -0,0 +1,30 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List, Dict
3
+
4
+ class SparkBaseComponent(ABC):
5
+ """
6
+ Abstract base class for Spark components.
7
+
8
+ This class defines the interface for components that require
9
+ specific Spark configurations and external packages.
10
+ """
11
+
12
+ @abstractmethod
13
+ def get_spark_config(self) -> Dict:
14
+ """
15
+ Return Spark configuration settings.
16
+
17
+ Returns:
18
+ dict: A dictionary of Spark configuration keys and values.
19
+ """
20
+ pass
21
+
22
+ @abstractmethod
23
+ def get_required_spark_packages(self) -> List:
24
+ """
25
+ Return a list of required Spark packages.
26
+
27
+ Returns:
28
+ list: A list of Maven coordinates or package names.
29
+ """
30
+ pass
@@ -0,0 +1,138 @@
1
+ from typing import Dict, List, Any, Optional
2
+ from pydantic import BaseModel, Field
3
+
4
+ from src_connectors.src_spark.configs.base import SparkBaseComponent
5
+ from variables.spark import SparkVariables
6
+
7
+ # Load environment-based defaults
8
+ _spark_defaults = SparkVariables.get_spark_base_config()
9
+
10
+
11
+ class SparkCoreConfig(BaseModel, SparkBaseComponent):
12
+ """
13
+ Configuration model for core Apache Spark engine settings.
14
+
15
+ This class manages Spark core parameters including resource allocation (memory, cores),
16
+ cluster connectivity, and runtime dependencies. Defaults are initialized from
17
+ environment variables.
18
+ """
19
+
20
+ # Cluster & Application Settings
21
+ spark_master: str = Field(
22
+ default=_spark_defaults.get("SPARK_MASTER") or "local[*]",
23
+ description="The Spark master URL to connect to."
24
+ )
25
+ spark_driver_host: str = Field(
26
+ default=_spark_defaults.get("SPARK_DRIVER_HOST") or "127.0.0.1",
27
+ description="Hostname or IP address for the driver to listen on."
28
+ )
29
+ spark_driver_bind_address: str = Field(
30
+ default=_spark_defaults.get("SPARK_DRIVER_BIND_ADDRESS") or "127.0.0.1",
31
+ description="Address to bind to for the driver."
32
+ )
33
+ spark_app_name: str = Field(
34
+ default=_spark_defaults.get("SPARK_APP_NAME") or "SparkApp",
35
+ description="The name of the Spark application."
36
+ )
37
+
38
+ # Resource Management
39
+ spark_executor_memory: str = Field(
40
+ default=_spark_defaults.get("SPARK_EXECUTOR_MEMORY") or "2g",
41
+ description="Amount of memory to use per executor process."
42
+ )
43
+ spark_executor_cores: int = Field(
44
+ default=int(_spark_defaults.get("SPARK_EXECUTOR_CORES") or 2),
45
+ description="The number of cores to use on each executor."
46
+ )
47
+ spark_driver_memory: str = Field(
48
+ default=_spark_defaults.get("SPARK_DRIVER_MEMORY") or "4g",
49
+ description="Amount of memory to use for the driver process."
50
+ )
51
+ spark_driver_cores: int = Field(
52
+ default=int(_spark_defaults.get("SPARK_DRIVER_CORES") or 2),
53
+ description="Number of cores to use for the driver process."
54
+ )
55
+
56
+ # Dependencies & Runtime
57
+ spark_jars: Optional[str] = Field(
58
+ default=_spark_defaults.get("SPARK_LOCAL_JARS") or "",
59
+ description="Comma-separated local jar paths."
60
+ )
61
+ spark_jars_packages: Optional[str] = Field(
62
+ default=_spark_defaults.get("SPARK_JARS_PACKAGES") or "",
63
+ description="Comma-separated Maven coordinates of jars to include."
64
+ )
65
+ spark_jars_ivy: str = Field(
66
+ default=_spark_defaults.get("SPARK_JARS_IVY") or "/tmp/.ivy2",
67
+ description="Path to local Ivy repository for dependency resolution."
68
+ )
69
+
70
+ # Environment & Versioning
71
+ spark_version: str = Field(
72
+ default=_spark_defaults.get("SPARK_VERSION") or "3.0.0",
73
+ description="Target Spark version."
74
+ )
75
+ spark_minor_version: str = Field(
76
+ default=_spark_defaults.get("SPARK_MINOR_VERSION") or "3.0",
77
+ description="Target Spark minor version."
78
+ )
79
+ spark_local_dir: str = Field(
80
+ default=_spark_defaults.get("SPARK_LOCAL_DIR") or "/tmp/spark",
81
+ description="Directory for Spark 'scratch' space (map outputs, RDD spills)."
82
+ )
83
+
84
+ def get_spark_config(self) -> Dict[str, Any]:
85
+ """
86
+ Generates a dictionary of Spark configuration properties.
87
+
88
+ Returns:
89
+ Dict[str, Any]: Flat dictionary of 'spark.*' configuration keys and values.
90
+ """
91
+ config = {
92
+ "spark.master": self.spark_master,
93
+ "spark.app.name": self.spark_app_name,
94
+ "spark.executor.memory": self.spark_executor_memory,
95
+ "spark.executor.cores": self.spark_executor_cores,
96
+ "spark.driver.memory": self.spark_driver_memory,
97
+ "spark.driver.cores": self.spark_driver_cores,
98
+ "spark.jars.ivy": self.spark_jars_ivy,
99
+ "spark.local.dir": self.spark_local_dir,
100
+ }
101
+
102
+ # Handle Driver Host logic:
103
+ # Force 127.0.0.1 ONLY if running in local mode to avoid RPC errors on dev machines.
104
+ # In cluster mode (K8s/YARN), we let Spark/Cluster Manager handle it or use provided host.
105
+ is_local = self.spark_master.startswith("local")
106
+ if is_local:
107
+ config["spark.driver.host"] = self.spark_driver_host
108
+ config["spark.driver.bindAddress"] = self.spark_driver_bind_address
109
+ else:
110
+ # In cluster mode, only apply if explicitly provided (not default 127.0.0.1)
111
+ if self.spark_driver_host != "127.0.0.1":
112
+ config["spark.driver.host"] = self.spark_driver_host
113
+ if self.spark_driver_bind_address != "127.0.0.1":
114
+ config["spark.driver.bindAddress"] = self.spark_driver_bind_address
115
+
116
+ # Handle Jars and Packages logic priority
117
+ if self.spark_jars:
118
+ config["spark.jars"] = self.spark_jars
119
+ elif self.spark_jars_packages:
120
+ config["spark.jars.packages"] = self.spark_jars_packages
121
+
122
+ return config
123
+
124
+ def get_required_spark_packages(self) -> List[str]:
125
+ """
126
+ Returns a list of required Spark packages.
127
+ If local jars are provided, packages are usually skipped to avoid conflicts.
128
+
129
+ Returns:
130
+ List[str]: A list of cleaned Maven package coordinates.
131
+ """
132
+ if self.spark_jars:
133
+ return []
134
+
135
+ if not self.spark_jars_packages:
136
+ return []
137
+
138
+ return [pkg.strip() for pkg in self.spark_jars_packages.split(",") if pkg.strip()]
@@ -0,0 +1,205 @@
1
+ from typing import Dict, Any, Optional, List, Union
2
+ from pydantic import BaseModel, Field, ConfigDict
3
+
4
+ from src_connectors.src_spark.configs.base import SparkBaseComponent
5
+ from variables.spark import SparkVariables
6
+
7
+ # Load environment-based defaults from centralized variables
8
+ _iceberg_defaults = SparkVariables.get_iceberg_config()
9
+ _minio_defaults = SparkVariables.get_minio_config()
10
+ _spark_defaults = SparkVariables.get_spark_base_config()
11
+
12
+
13
+ class SparkIcebergConfig(BaseModel, SparkBaseComponent):
14
+ """
15
+ Configuration model for Apache Iceberg integration with Spark.
16
+
17
+ This class manages Iceberg catalog settings, including connection URIs,
18
+ authentication credentials, and warehouse locations. It constructs the
19
+ necessary 'spark.sql.catalog' properties required by the Iceberg Spark runtime.
20
+ """
21
+ model_config = ConfigDict(extra='allow')
22
+
23
+ iceberg_catalog_uri: str = Field(
24
+ default=_iceberg_defaults.get("SPARK_ICEBERG_CATALOG_URI") or "",
25
+ description="The URI for the Iceberg catalog (REST/Hive/Glue endpoint)."
26
+ )
27
+ iceberg_catalog_type: str = Field(
28
+ default=_iceberg_defaults.get("SPARK_ICEBERG_CATALOG_TYPE") or "hadoop",
29
+ description="The type of Iceberg catalog (e.g., 'rest', 'hive', 'hadoop')."
30
+ )
31
+ iceberg_oauth2_server_uri: str = Field(
32
+ default=_iceberg_defaults.get("SPARK_ICEBERG_OAUTH2_SERVER_URI") or "",
33
+ description="OAuth2 server URI for REST catalog authentication."
34
+ )
35
+ iceberg_credential: str = Field(
36
+ default=_iceberg_defaults.get("SPARK_ICEBERG_CREDENTIAL") or "",
37
+ description="Credentials for Iceberg catalog (e.g., client_id:client_secret)."
38
+ )
39
+ iceberg_scope: str = Field(
40
+ default=_iceberg_defaults.get("SPARK_ICEBERG_SCOPE") or "",
41
+ description="OAuth2 scope for catalog authentication."
42
+ )
43
+ iceberg_warehouse: Union[str, List[str]] = Field(
44
+ default=_iceberg_defaults.get("SPARK_ICEBERG_WAREHOUSE") or "",
45
+ description="The warehouse location/name used for the Spark SQL catalog prefix."
46
+ )
47
+ iceberg_version: str = Field(
48
+ default=_iceberg_defaults.get("SPARK_ICEBERG_VERSION") or "1.4.2",
49
+ description="The Version of Iceberg runtime packages to load."
50
+ )
51
+ spark_jars: Optional[str] = Field(
52
+ default=_spark_defaults.get("SPARK_LOCAL_JARS") or "",
53
+ description="Local jar file paths to include in the Spark session."
54
+ )
55
+ spark_jars_packages: Optional[str] = Field(
56
+ default=_spark_defaults.get("SPARK_JARS_PACKAGES") or "",
57
+ description="Override for Spark dependency packages."
58
+ )
59
+
60
+ # Storage Settings (MinIO / S3)
61
+ spark_minio_access_key: str = Field(
62
+ default=_minio_defaults.get("SPARK_MINIO_ACCESS_KEY_ID") or "",
63
+ description="Access key for MinIO or S3 storage."
64
+ )
65
+ spark_minio_secret_key: str = Field(
66
+ default=_minio_defaults.get("SPARK_MINIO_SECRET_ACCESS_KEY") or "",
67
+ description="Secret key for MinIO or S3 storage."
68
+ )
69
+ spark_minio_endpoint: str = Field(
70
+ default=_minio_defaults.get("SPARK_MINIO_ENDPOINT_URL") or "",
71
+ description="Endpoint URL for MinIO or S3 storage (e.g., http://localhost:9000)."
72
+ )
73
+
74
+ def get_spark_config(self) -> Dict[str, Any]:
75
+ """
76
+ Generates Spark configuration for the Iceberg catalog.
77
+
78
+ Includes SQL extensions, catalog-specific properties, and Hadoop S3A settings
79
+ for cloud storage connectivity.
80
+
81
+ Returns:
82
+ Dict[str, Any]: A flat dictionary of Spark configuration properties.
83
+ """
84
+ config = {
85
+ "spark.sql.extensions": "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions",
86
+ }
87
+
88
+ # Determine warehouses to configure (handles single string or list)
89
+ warehouses = [self.iceberg_warehouse] if isinstance(self.iceberg_warehouse, str) else self.iceberg_warehouse
90
+
91
+ for wh in warehouses:
92
+ # Using warehouse name as the catalog identifier prefix
93
+ prefix = f"spark.sql.catalog.{wh}"
94
+ config[prefix] = "org.apache.iceberg.spark.SparkCatalog"
95
+
96
+ # For non-REST catalogs, type can be used as shorthand (hive, hadoop, etc.)
97
+ # For REST catalogs, catalog-impl is preferred and setting both causes conflicts.
98
+ if self.iceberg_catalog_type != "rest":
99
+ config[f"{prefix}.type"] = self.iceberg_catalog_type
100
+
101
+ # Catalog URI and basic I/O settings
102
+ if self.iceberg_catalog_uri:
103
+ config[f"{prefix}.uri"] = self.iceberg_catalog_uri
104
+ config[f"{prefix}.classloader.isolation.enabled"] = "false"
105
+
106
+ # Use S3FileIO if S3/MinIO is likely being used
107
+ if any(s in wh.lower() or s in self.iceberg_catalog_uri.lower() for s in ["s3", "minio"]):
108
+ config[f"{prefix}.io-impl"] = "org.apache.iceberg.aws.s3.S3FileIO"
109
+
110
+ if wh:
111
+ config[f"{prefix}.warehouse"] = wh
112
+
113
+ # REST Catalog specialized authentication settings
114
+ if self.iceberg_catalog_type == "rest":
115
+ self._apply_rest_catalog_config(config, prefix)
116
+
117
+ # Handle Spark Jars and Packages logic: Priority to local jars
118
+ self._apply_dependency_config(config)
119
+
120
+ # Apply Hadoop S3A storage configurations if credentials are provided
121
+ if self.spark_minio_access_key and self.spark_minio_secret_key:
122
+ config.update(self._get_s3_hadoop_config())
123
+
124
+ return config
125
+
126
+ def _apply_rest_catalog_config(self, config: Dict[str, Any], prefix: str) -> None:
127
+ """Applies REST catalog specific authentication and refresh settings."""
128
+ config[f"{prefix}.catalog-impl"] = "org.apache.iceberg.rest.RESTCatalog"
129
+ if self.iceberg_oauth2_server_uri:
130
+ config[f"{prefix}.oauth2-server-uri"] = self.iceberg_oauth2_server_uri
131
+ if self.iceberg_credential:
132
+ config[f"{prefix}.credential"] = self.iceberg_credential
133
+ if self.iceberg_scope:
134
+ config[f"{prefix}.scope"] = self.iceberg_scope
135
+
136
+ config[f"{prefix}.rest.auth.type"] = "oauth2"
137
+ config[f"{prefix}.token-refresh-enabled"] = "true"
138
+ config[f"{prefix}.token-refresh-min-validity-time"] = "60s"
139
+
140
+ def _apply_dependency_config(self, config: Dict[str, Any]) -> None:
141
+ """Handles the logic for Spark JARs and Packages with local priority."""
142
+ if self.spark_jars:
143
+ config["spark.jars"] = self.spark_jars
144
+ else:
145
+ if self.spark_jars_packages:
146
+ config["spark.jars.packages"] = self.spark_jars_packages
147
+ else:
148
+ spark_minor_version = _spark_defaults.get("SPARK_MINOR_VERSION", "3.0")
149
+ config["spark.jars.packages"] = (
150
+ f"org.apache.iceberg:iceberg-spark-runtime-{spark_minor_version}_2.12:{self.iceberg_version},"
151
+ f"org.apache.iceberg:iceberg-azure-bundle:{self.iceberg_version},"
152
+ f"org.apache.iceberg:iceberg-aws-bundle:{self.iceberg_version},"
153
+ f"org.apache.iceberg:iceberg-gcp-bundle:{self.iceberg_version}"
154
+ )
155
+
156
+ def _get_s3_hadoop_config(self) -> Dict[str, str]:
157
+ """
158
+ Generates Hadoop S3A configurations for MinIO/S3 compatible storage.
159
+
160
+ Returns:
161
+ Dict[str, str]: Hadoop filesystem properties.
162
+ """
163
+ hadoop_conf = {
164
+ "spark.hadoop.fs.s3a.access.key": self.spark_minio_access_key,
165
+ "spark.hadoop.fs.s3a.secret.key": self.spark_minio_secret_key,
166
+ "spark.hadoop.fs.s3a.impl": "org.apache.hadoop.fs.s3a.S3AFileSystem",
167
+ "spark.hadoop.fs.s3a.path.style.access": "true",
168
+ "spark.hadoop.fs.s3a.fast.upload": "true",
169
+ "spark.hadoop.fs.s3a.connection.ssl.enabled": "false" if "http://" in self.spark_minio_endpoint.lower() else "true"
170
+ }
171
+
172
+ if self.spark_minio_endpoint:
173
+ hadoop_conf["spark.hadoop.fs.s3a.endpoint"] = self.spark_minio_endpoint
174
+
175
+ # When keys are provided, we use the SimpleAWSCredentialsProvider
176
+ hadoop_conf["spark.hadoop.fs.s3a.aws.credentials.provider"] = (
177
+ "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider"
178
+ )
179
+
180
+ # Compatibility with libraries using 's3' instead of 's3a'
181
+ hadoop_conf["spark.hadoop.fs.s3.impl"] = "org.apache.hadoop.fs.s3a.S3AFileSystem"
182
+
183
+ return hadoop_conf
184
+
185
+ def get_required_spark_packages(self) -> List[str]:
186
+ """
187
+ Returns a list of required Spark packages for Iceberg.
188
+
189
+ Returns:
190
+ List[str]: A list of Maven coordinates for Iceberg and related bundles.
191
+ """
192
+ if self.spark_jars:
193
+ return []
194
+
195
+ if self.spark_jars_packages:
196
+ return [pkg.strip() for pkg in self.spark_jars_packages.split(",") if pkg.strip()]
197
+
198
+ # Default Iceberg packages if nothing is provided
199
+ spark_minor_version = _spark_defaults.get("SPARK_MINOR_VERSION", "3.0")
200
+ return [
201
+ f"org.apache.iceberg:iceberg-spark-runtime-{spark_minor_version}_2.12:{self.iceberg_version}",
202
+ f"org.apache.iceberg:iceberg-azure-bundle:{self.iceberg_version}",
203
+ f"org.apache.iceberg:iceberg-aws-bundle:{self.iceberg_version}",
204
+ f"org.apache.iceberg:iceberg-gcp-bundle:{self.iceberg_version}"
205
+ ]
@@ -0,0 +1,81 @@
1
+ from typing import Dict, Any, List
2
+ from pydantic import BaseModel, Field, ConfigDict
3
+
4
+ from src_connectors.src_spark.configs.base import SparkBaseComponent
5
+ from variables.spark import SparkVariables
6
+
7
+ # Load environment-based defaults from centralized variables
8
+ _jdbc_defaults = SparkVariables.get_jdbc_config()
9
+
10
+
11
+ class SparkJdbcConfig(BaseModel, SparkBaseComponent):
12
+ """
13
+ Configuration model for Spark JDBC connections.
14
+
15
+ This class manages JDBC connectivity parameters, including driver details,
16
+ connection URIs, and authentication credentials. It provides both descriptive
17
+ metadata and functional options for Spark data source operations.
18
+ """
19
+ model_config = ConfigDict(extra='allow')
20
+
21
+ jdbc_url: str = Field(
22
+ default=_jdbc_defaults.get("SPARK_JDBC_URL") or "",
23
+ description="The JDBC connection URL (e.g., 'jdbc:postgresql://localhost:5432/db')."
24
+ )
25
+ jdbc_driver: str = Field(
26
+ default=_jdbc_defaults.get("SPARK_JDBC_DRIVER") or "",
27
+ description="The fully qualified class name of the JDBC driver (e.g., 'org.postgresql.Driver')."
28
+ )
29
+ jdbc_username: str = Field(
30
+ default=_jdbc_defaults.get("SPARK_JDBC_USERNAME") or "",
31
+ description="The database username for the JDBC connection."
32
+ )
33
+ jdbc_password: str = Field(
34
+ default=_jdbc_defaults.get("SPARK_JDBC_PASSWORD") or "",
35
+ description="The database password for the JDBC connection."
36
+ )
37
+
38
+ def get_spark_config(self) -> Dict[str, Any]:
39
+ """
40
+ Returns a descriptive overview of the JDBC configuration.
41
+
42
+ Security Note: Sensitive credentials (password) are masked in the output.
43
+ Note: JDBC parameters are typically utilized in .read.format("jdbc").options(...)
44
+ rather than being part of the global SparkConf.
45
+
46
+ Returns:
47
+ Dict[str, Any]: A dictionary containing JDBC connection metadata.
48
+ """
49
+ return {
50
+ "spark_jdbc_url": self.jdbc_url,
51
+ "spark_jdbc_driver": self.jdbc_driver,
52
+ "spark_jdbc_user": self.jdbc_username,
53
+ "spark_jdbc_password_provided": bool(self.jdbc_password)
54
+ }
55
+
56
+ def get_jdbc_options(self) -> Dict[str, str]:
57
+ """
58
+ Generates connection options for Spark's JDBC data source.
59
+
60
+ Suitable for use with spark.read.format("jdbc").options(**config.get_jdbc_options()).load().
61
+
62
+ Returns:
63
+ Dict[str, str]: A dictionary of key-value pairs representing JDBC options.
64
+ """
65
+ options = {
66
+ "url": self.jdbc_url,
67
+ "driver": self.jdbc_driver,
68
+ "user": self.jdbc_username,
69
+ "password": self.jdbc_password,
70
+ }
71
+ # Filter out empty options and ensure all values are strings for Spark compatibility
72
+ return {k: str(v) for k, v in options.items() if v}
73
+
74
+ def get_required_spark_packages(self) -> List[str]:
75
+ """
76
+ Returns a list of required Spark packages for JDBC.
77
+
78
+ Returns:
79
+ List[str]: Usually an empty list as JDBC drivers are managed via JARs.
80
+ """
81
+ return []