ingestr 0.13.65__py3-none-any.whl → 0.13.67__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.
ingestr/src/buildinfo.py CHANGED
@@ -1 +1 @@
1
- version = "v0.13.65"
1
+ version = "v0.13.67"
@@ -18,14 +18,6 @@ from dlt.common.storages.configuration import FileSystemCredentials
18
18
  from dlt.destinations.impl.clickhouse.configuration import (
19
19
  ClickHouseCredentials,
20
20
  )
21
- from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration
22
- from dlt.destinations.impl.mssql.mssql import (
23
- HINT_TO_MSSQL_ATTR,
24
- MsSqlJobClient,
25
- )
26
- from dlt.destinations.impl.mssql.sql_client import (
27
- PyOdbcMsSqlClient,
28
- )
29
21
 
30
22
  from ingestr.src.errors import MissingValueError
31
23
  from ingestr.src.loader import load_dlt_file
@@ -172,72 +164,90 @@ def handle_datetimeoffset(dto_value: bytes) -> datetime.datetime:
172
164
  )
173
165
 
174
166
 
175
- class OdbcMsSqlClient(PyOdbcMsSqlClient):
176
- SQL_COPT_SS_ACCESS_TOKEN = 1256
177
- SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"}
167
+ # MSSQL_COPT_SS_ACCESS_TOKEN is a connection attribute used to pass
168
+ # an Azure Active Directory access token to the SQL Server ODBC driver.
169
+ MSSQL_COPT_SS_ACCESS_TOKEN = 1256
178
170
 
179
- def open_connection(self):
180
- cfg = self.credentials._get_odbc_dsn_dict()
181
- if (
182
- cfg.get("AUTHENTICATION", "").strip().lower()
183
- != "activedirectoryaccesstoken"
184
- ):
185
- return super().open_connection()
186
171
 
187
- import pyodbc # type: ignore
172
+ def serialize_azure_token(token):
173
+ # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723
174
+ encoded = token.encode("utf_16_le")
175
+ return struct.pack("<i", len(encoded)) + encoded
188
176
 
189
- dsn = ";".join(
190
- [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS]
191
- )
192
177
 
193
- self._conn = pyodbc.connect(
194
- dsn,
195
- timeout=self.credentials.connect_timeout,
196
- attrs_before={
197
- self.SQL_COPT_SS_ACCESS_TOKEN: self.serialize_token(cfg["PWD"]),
198
- },
199
- )
178
+ def build_mssql_dest():
179
+ # https://github.com/bruin-data/ingestr/issues/293
200
180
 
201
- # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
202
- self._conn.add_output_converter(-155, handle_datetimeoffset)
203
- self._conn.autocommit = True
204
- return self._conn
205
-
206
- def serialize_token(self, token):
207
- # https://github.com/mkleehammer/pyodbc/issues/228#issuecomment-494773723
208
- encoded = token.encode("utf_16_le")
209
- return struct.pack("<i", len(encoded)) + encoded
210
-
211
-
212
- class MsSqlClient(MsSqlJobClient):
213
- def __init__(
214
- self,
215
- schema: Schema,
216
- config: MsSqlClientConfiguration,
217
- capabilities: DestinationCapabilitiesContext,
218
- ) -> None:
219
- sql_client = OdbcMsSqlClient(
220
- config.normalize_dataset_name(schema),
221
- config.normalize_staging_dataset_name(schema),
222
- config.credentials,
223
- capabilities,
224
- )
225
- super(MsSqlJobClient, self).__init__(schema, config, sql_client)
226
- self.config: MsSqlClientConfiguration = config
227
- self.sql_client = sql_client
228
- self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {}
229
- self.type_mapper = capabilities.get_type_mapper()
181
+ from dlt.destinations.impl.mssql.configuration import MsSqlClientConfiguration
182
+ from dlt.destinations.impl.mssql.mssql import (
183
+ HINT_TO_MSSQL_ATTR,
184
+ MsSqlJobClient,
185
+ )
186
+ from dlt.destinations.impl.mssql.sql_client import (
187
+ PyOdbcMsSqlClient,
188
+ )
230
189
 
190
+ class OdbcMsSqlClient(PyOdbcMsSqlClient):
191
+ SKIP_CREDENTIALS = {"PWD", "AUTHENTICATION", "UID"}
231
192
 
232
- class MsSqlDestImpl(dlt.destinations.mssql):
233
- @property
234
- def client_class(self):
235
- return MsSqlClient
193
+ def open_connection(self):
194
+ cfg = self.credentials._get_odbc_dsn_dict()
195
+ if (
196
+ cfg.get("AUTHENTICATION", "").strip().lower()
197
+ != "activedirectoryaccesstoken"
198
+ ):
199
+ return super().open_connection()
200
+
201
+ import pyodbc # type: ignore
202
+
203
+ dsn = ";".join(
204
+ [f"{k}={v}" for k, v in cfg.items() if k not in self.SKIP_CREDENTIALS]
205
+ )
206
+
207
+ self._conn = pyodbc.connect(
208
+ dsn,
209
+ timeout=self.credentials.connect_timeout,
210
+ attrs_before={
211
+ MSSQL_COPT_SS_ACCESS_TOKEN: serialize_azure_token(cfg["PWD"]),
212
+ },
213
+ )
214
+
215
+ # https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
216
+ self._conn.add_output_converter(-155, handle_datetimeoffset)
217
+ self._conn.autocommit = True
218
+ return self._conn
219
+
220
+ class MsSqlClient(MsSqlJobClient):
221
+ def __init__(
222
+ self,
223
+ schema: Schema,
224
+ config: MsSqlClientConfiguration,
225
+ capabilities: DestinationCapabilitiesContext,
226
+ ) -> None:
227
+ sql_client = OdbcMsSqlClient(
228
+ config.normalize_dataset_name(schema),
229
+ config.normalize_staging_dataset_name(schema),
230
+ config.credentials,
231
+ capabilities,
232
+ )
233
+ super(MsSqlJobClient, self).__init__(schema, config, sql_client)
234
+ self.config: MsSqlClientConfiguration = config
235
+ self.sql_client = sql_client
236
+ self.active_hints = HINT_TO_MSSQL_ATTR if self.config.create_indexes else {}
237
+ self.type_mapper = capabilities.get_type_mapper()
238
+
239
+ class MsSqlDestImpl(dlt.destinations.mssql):
240
+ @property
241
+ def client_class(self):
242
+ return MsSqlClient
243
+
244
+ return MsSqlDestImpl
236
245
 
237
246
 
238
247
  class MsSQLDestination(GenericSqlDestination):
239
248
  def dlt_dest(self, uri: str, **kwargs):
240
- return MsSqlDestImpl(credentials=uri, **kwargs)
249
+ cls = build_mssql_dest()
250
+ return cls(credentials=uri, **kwargs)
241
251
 
242
252
 
243
253
  class DatabricksDestination(GenericSqlDestination):
ingestr/src/sources.py CHANGED
@@ -268,8 +268,9 @@ class SqlSource:
268
268
  from sqlalchemy import create_engine
269
269
 
270
270
  from ingestr.src.destinations import (
271
- OdbcMsSqlClient,
271
+ MSSQL_COPT_SS_ACCESS_TOKEN,
272
272
  handle_datetimeoffset,
273
+ serialize_azure_token,
273
274
  )
274
275
 
275
276
  cfg = {
@@ -283,7 +284,7 @@ class SqlSource:
283
284
  if k.lower() not in ["driver", "authentication", "connect_timeout"]:
284
285
  cfg[k.upper()] = v[0]
285
286
 
286
- token = OdbcMsSqlClient.serialize_token(None, parsed_uri.password) # type: ignore[arg-type]
287
+ token = serialize_azure_token(parsed_uri.password)
287
288
  dsn = ";".join([f"{k}={v}" for k, v in cfg.items()])
288
289
 
289
290
  def creator():
@@ -292,7 +293,7 @@ class SqlSource:
292
293
  autocommit=True,
293
294
  timeout=kwargs.get("connect_timeout", 30),
294
295
  attrs_before={
295
- OdbcMsSqlClient.SQL_COPT_SS_ACCESS_TOKEN: token,
296
+ MSSQL_COPT_SS_ACCESS_TOKEN: token,
296
297
  },
297
298
  )
298
299
  connection.add_output_converter(-155, handle_datetimeoffset)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ingestr
3
- Version: 0.13.65
3
+ Version: 0.13.67
4
4
  Summary: ingestr is a command-line application that ingests data from various sources and stores them in any database.
5
5
  Project-URL: Homepage, https://github.com/bruin-data/ingestr
6
6
  Project-URL: Issues, https://github.com/bruin-data/ingestr/issues
@@ -179,7 +179,7 @@ Requires-Dist: snowflake-connector-python==3.14.0
179
179
  Requires-Dist: snowflake-sqlalchemy==1.6.1
180
180
  Requires-Dist: sortedcontainers==2.4.0
181
181
  Requires-Dist: sqlalchemy-bigquery==1.12.1
182
- Requires-Dist: sqlalchemy-cratedb==0.42.0.dev2
182
+ Requires-Dist: sqlalchemy-cratedb==0.41.0
183
183
  Requires-Dist: sqlalchemy-hana==2.0.0
184
184
  Requires-Dist: sqlalchemy-redshift==0.8.14
185
185
  Requires-Dist: sqlalchemy-spanner==1.11.0
@@ -202,7 +202,7 @@ Requires-Dist: tzdata==2025.1
202
202
  Requires-Dist: tzlocal==5.3
203
203
  Requires-Dist: uritemplate==4.1.1
204
204
  Requires-Dist: urllib3==2.3.0
205
- Requires-Dist: verlib2==0.3.1
205
+ Requires-Dist: verlib2==0.2.0
206
206
  Requires-Dist: wrapt==1.17.2
207
207
  Requires-Dist: yarl==1.18.3
208
208
  Requires-Dist: zeep==4.3.1
@@ -2,8 +2,8 @@ ingestr/conftest.py,sha256=Q03FIJIZpLBbpj55cfCHIKEjc1FCvWJhMF2cidUJKQU,1748
2
2
  ingestr/main.py,sha256=taDyHyaVSpB17iNLl8zA0gmr4CqDO-MSTQX1CaRBB9U,26364
3
3
  ingestr/src/.gitignore,sha256=8cX1AZTSI0TcdZFGTmS_oyBjpfCzhOEt0DdAo2dFIY8,203
4
4
  ingestr/src/blob.py,sha256=UUWMjHUuoR9xP1XZQ6UANQmnMVyDx3d0X4-2FQC271I,2138
5
- ingestr/src/buildinfo.py,sha256=RDAMEy23q-LmXSYODsQMAghvn5syzLPD4mQO_GpxC0c,21
6
- ingestr/src/destinations.py,sha256=ZJTbTn1K9oXinL19dTGQDUrft5C9fjrpSlTw1CLQhuM,21749
5
+ ingestr/src/buildinfo.py,sha256=sLhX7e2Ae9uo6JOAie2f4CiqQivKOIkv83SFKQF-YU8,21
6
+ ingestr/src/destinations.py,sha256=rNKtrfWlH7tk4kXZlEpzIgZm1tG8fV-BimBlJ4CJG4E,22216
7
7
  ingestr/src/errors.py,sha256=Ufs4_DfE77_E3vnA1fOQdi6cmuLVNm7_SbFLkL1XPGk,686
8
8
  ingestr/src/factory.py,sha256=AJCvlK4M1sIpAAks1K-xsR_uxziIxru74mj572zixhg,6546
9
9
  ingestr/src/filters.py,sha256=LLecXe9QkLFkFLUZ92OXNdcANr1a8edDxrflc2ko_KA,1452
@@ -11,7 +11,7 @@ ingestr/src/http_client.py,sha256=bxqsk6nJNXCo-79gW04B53DQO-yr25vaSsqP0AKtjx4,73
11
11
  ingestr/src/loader.py,sha256=9NaWAyfkXdqAZSS-N72Iwo36Lbx4PyqIfaaH1dNdkFs,1712
12
12
  ingestr/src/partition.py,sha256=BrIP6wFJvyR7Nus_3ElnfxknUXeCipK_E_bB8kZowfc,969
13
13
  ingestr/src/resource.py,sha256=ZqmZxFQVGlF8rFPhBiUB08HES0yoTj8sZ--jKfaaVps,1164
14
- ingestr/src/sources.py,sha256=K554sV-RW29vj0c84r_rR0yZTl5HVvQ9vqbQOkJ9E7M,103386
14
+ ingestr/src/sources.py,sha256=gNayvEcWHppb5SLs2QFMxwXxPlUpbmoJTX6cwl9_dkk,103384
15
15
  ingestr/src/table_definition.py,sha256=REbAbqdlmUMUuRh8nEQRreWjPVOQ5ZcfqGkScKdCrmk,390
16
16
  ingestr/src/time.py,sha256=H_Fk2J4ShXyUM-EMY7MqCLZQhlnZMZvO952bmZPc4yE,254
17
17
  ingestr/src/version.py,sha256=J_2xgZ0mKlvuHcjdKCx2nlioneLH0I47JiU_Slr_Nwc,189
@@ -147,8 +147,8 @@ ingestr/testdata/merge_expected.csv,sha256=DReHqWGnQMsf2PBv_Q2pfjsgvikYFnf1zYcQZ
147
147
  ingestr/testdata/merge_part1.csv,sha256=Pw8Z9IDKcNU0qQHx1z6BUf4rF_-SxKGFOvymCt4OY9I,185
148
148
  ingestr/testdata/merge_part2.csv,sha256=T_GiWxA81SN63_tMOIuemcvboEFeAmbKc7xRXvL9esw,287
149
149
  ingestr/tests/unit/test_smartsheets.py,sha256=eiC2CCO4iNJcuN36ONvqmEDryCA1bA1REpayHpu42lk,5058
150
- ingestr-0.13.65.dist-info/METADATA,sha256=PWjju7xvb3O9Ya0IRwj-zti34_sN6sGSY3YbROP3KKs,15027
151
- ingestr-0.13.65.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
152
- ingestr-0.13.65.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
153
- ingestr-0.13.65.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
154
- ingestr-0.13.65.dist-info/RECORD,,
150
+ ingestr-0.13.67.dist-info/METADATA,sha256=97obKQ7ltYvn_6SfwmBV3QrSH5mit8GbE9uQiJenqCY,15022
151
+ ingestr-0.13.67.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
152
+ ingestr-0.13.67.dist-info/entry_points.txt,sha256=oPJy0KBnPWYjDtP1k8qwAihcTLHSZokSQvRAw_wtfJM,46
153
+ ingestr-0.13.67.dist-info/licenses/LICENSE.md,sha256=cW8wIhn8HFE-KLStDF9jHQ1O_ARWP3kTpk_-eOccL24,1075
154
+ ingestr-0.13.67.dist-info/RECORD,,