satisfactoscript 0.6.1__tar.gz → 0.6.3__tar.gz

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.
Files changed (32) hide show
  1. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/PKG-INFO +1 -1
  2. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/pyproject.toml +1 -1
  3. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/core/core.py +54 -6
  4. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript.egg-info/PKG-INFO +1 -1
  5. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/README.md +0 -0
  6. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/setup.cfg +0 -0
  7. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/__init__.py +0 -0
  8. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/agentic/__init__.py +0 -0
  9. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/agentic/agent.py +0 -0
  10. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/core/__init__.py +0 -0
  11. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/core/config.py +0 -0
  12. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/core/loaders.py +0 -0
  13. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/core/registry.py +0 -0
  14. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/registry.py +0 -0
  15. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/semantic/__init__.py +0 -0
  16. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/semantic/semantic.py +0 -0
  17. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript/utils.py +0 -0
  18. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript.egg-info/SOURCES.txt +0 -0
  19. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript.egg-info/dependency_links.txt +0 -0
  20. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript.egg-info/requires.txt +0 -0
  21. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/src/satisfactoscript.egg-info/top_level.txt +0 -0
  22. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_config.py +0 -0
  23. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_core.py +0 -0
  24. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_core_connect_patch.py +0 -0
  25. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_core_env_detection.py +0 -0
  26. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_core_join.py +0 -0
  27. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_core_username.py +0 -0
  28. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_dummy.py +0 -0
  29. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_loaders.py +0 -0
  30. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_registry.py +0 -0
  31. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_registry_import_paths.py +0 -0
  32. {satisfactoscript-0.6.1 → satisfactoscript-0.6.3}/tests/test_utils_safe_columns.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: satisfactoscript
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: An Enterprise-Ready, Declarative Data Engineering Framework for Databricks Lakehouse.
5
5
  Author: julhouba
6
6
  Classifier: Programming Language :: Python :: 3
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "satisfactoscript"
7
- version = "0.6.1"
7
+ version = "0.6.3"
8
8
  description = "An Enterprise-Ready, Declarative Data Engineering Framework for Databricks Lakehouse."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -210,6 +210,7 @@ class SatisfactoEngine:
210
210
  raise RuntimeError("CRITICAL: Failed to initialize Spark Session.")
211
211
 
212
212
  self._patch_connect_debugging()
213
+ self._patch_connect_user_context()
213
214
  self.dbutils = DBUtils(self.spark)
214
215
 
215
216
  # ======================================================================
@@ -279,6 +280,42 @@ class SatisfactoEngine:
279
280
  except Exception:
280
281
  pass
281
282
 
283
+ def _patch_connect_user_context(self):
284
+ """
285
+ Patches SparkConnectClient._user_id when it is empty or missing.
286
+
287
+ Root cause: on Databricks Connect v2 from local environments (Windows/PyCharm),
288
+ the gRPC session is created successfully but _user_id is not populated from the
289
+ SDK config. PySpark's execute_command only injects user_context.user_id when
290
+ self._user_id is truthy — so every gRPC execution call (saveAsTable, collect,
291
+ execute_command for DDL) fails immediately with:
292
+ 'Missing required field UserContext'
293
+
294
+ Fix: after session creation, if _user_id is absent on the SparkConnectClient,
295
+ fetch the current user's email via the Databricks SDK REST API (which is not
296
+ affected by the gRPC issue) and inject it directly into the client object.
297
+ This is a one-time init patch with no functional side effects.
298
+ """
299
+ try:
300
+ client = getattr(self.spark, 'client', None)
301
+ if client is None:
302
+ return # Standard SparkSession (not a Connect session) — nothing to patch
303
+
304
+ if getattr(client, '_user_id', None):
305
+ return # Already properly set — no patch needed
306
+
307
+ from databricks.sdk import WorkspaceClient
308
+ host = os.getenv("DATABRICKS_HOST")
309
+ token = os.getenv("DATABRICKS_TOKEN")
310
+ if host and token:
311
+ w = WorkspaceClient(host=host, token=token)
312
+ email = w.current_user.me().user_name
313
+ if email:
314
+ client._user_id = email
315
+ print(f" [Init] ⚙️ Applied UserContext patch: user_id injected ('{email}').")
316
+ except Exception:
317
+ pass
318
+
282
319
  def _find_file_upwards(self, filename):
283
320
  """
284
321
  Searches for a file starting from current working directory and moving up.
@@ -546,10 +583,15 @@ class SatisfactoEngine:
546
583
  fqn (str): The Fully Qualified Name of the target table.
547
584
  label (str): A label to display in the logging output.
548
585
  """
549
- if df.isEmpty():
550
- print(f" -> [Write] WARNING: Dataframe for {label} is empty. Skipping write.")
551
- return
552
-
586
+ try:
587
+ if df.isEmpty():
588
+ print(f" -> [Write] WARNING: Dataframe for {label} is empty. Skipping write.")
589
+ return
590
+ except Exception:
591
+ # isEmpty() triggers collect() via gRPC, which fails with UserContext on
592
+ # Databricks Connect v2 locally. Skip the check and proceed with the write.
593
+ pass
594
+
553
595
  print(f" -> [Write] Writing data to: {fqn} ...")
554
596
  df.write.format("delta").mode("overwrite").option("overwriteSchema", "true").saveAsTable(fqn)
555
597
  print(f" -> [Success] Table {label} written successfully.")
@@ -846,5 +888,11 @@ class SatisfactoEngine:
846
888
  print(f" -> [Success] Table {target_table_name} optimized successfully.")
847
889
 
848
890
  except Exception as e:
849
- print(f" -> [Error] Optimization failed on {full_target_fqn}: {e}")
850
- raise e
891
+ # OPTIMIZE is a Databricks DDL command with no SDK REST equivalent.
892
+ # When running locally via Databricks Connect v2, gRPC DDL calls fail with
893
+ # 'Missing required field UserContext'. In that case, skip silently.
894
+ if "UserContext" in str(e) or "user_context" in str(e).lower():
895
+ print(f" -> ⚠️ [Optimize] Skipped: OPTIMIZE is not supported via Databricks Connect v2 locally.")
896
+ else:
897
+ print(f" -> ❌ [Error] Optimization failed on {full_target_fqn}: {e}")
898
+ raise e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: satisfactoscript
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: An Enterprise-Ready, Declarative Data Engineering Framework for Databricks Lakehouse.
5
5
  Author: julhouba
6
6
  Classifier: Programming Language :: Python :: 3