-
datacustomcode zipanddatacustomcode deployno longer package symlinks.Symlinks found inside the payload directory are skipped instead of being followed. A symlinked file logs a
Skipping symlink file <path>warning and is left out ofdeployment.zip.Why: Following a symlink packaged the bytes of its target into the archive, even when the target was outside the payload (for example
payload/notes.txt -> ~/.ssh/id_rsa).Migration: Replace any symlink in the payload with a real copy of the file or directory it points to:
# Before ln -s ../shared/helpers.py payload/helpers.py # After cp ../shared/helpers.py payload/helpers.py
StreamingClient.write_dmo_deltas(name, dataframe)– start a streaming query that writes each micro-batch to the target DMO and return theStreamingQueryhandle. It is the DMO counterpart towrite_dlo_deltasand pairs withread_dmo_deltas().
-
StreamingClientfor BYOC streaming (delta) transforms.A dedicated
StreamingClient(alongside the batchClient) lets an entry point process a Data Lake Object's Change Data Feed continuously instead of reading a bounded snapshot.read_dlo_deltas()/read_dmo_deltas()– return a streaming DataFrame over the object's change feed.write_dlo_deltas(name, dataframe)– start a streaming query that writes each micro-batch to the target DLO and return theStreamingQueryhandle.
The shared functions (
find_file_path,llm_gateway_generate_text,einstein_predict) are available on bothClientandStreamingClient.from datacustomcode import StreamingClient client = StreamingClient() deltas = client.read_dlo_deltas() transformed = deltas.withColumn("description__c", upper(col("description__c"))) query = client.write_dlo_deltas("Output__dll", transformed) query.awaitTermination()
These methods run only inside the Data Cloud streaming (
DELTA_SYNC) runtime; locally they raiseNotImplementedError. See theexamples/streaming_deltas/entrypoint.pyexample and the "Streaming (delta) transforms" section of the README.
-
WriteMode.OVERWRITE_PARTITIONSis deprecated and now raises an error if used.WriteMode.OVERWRITE_PARTITIONSremains in the enum for backward compatibility but will raise aValueErrorat runtime.Migration: Use
WriteMode.OVERWRITEto replace all records, orWriteMode.MERGEfor targeted updates by primary key:# Before client.write_to_dlo("MyTable__dll", df, write_mode=WriteMode.OVERWRITE_PARTITIONS) # After (full replacement) client.write_to_dlo("MyTable__dll", df, write_mode=WriteMode.OVERWRITE) # After (update specific rows by primary key) client.write_to_dlo("MyTable__dll", df, write_mode=WriteMode.MERGE)
-
**Added
runtime: datacustomcode.runtime.function.Runtimeto function contract for codeTypefunction.Function now mandates runtime as arguments.
Why:
runTimeallows access to resources ( llm_gateway / file ) available during function execution.Migration: use function(request: dict, runTime: Runtime) instead od function(request: dict)
# Before def function(request: dict): pass # After def function(request: dict, runTime: Runtime): pass
-
Removed the
row_limitparameter fromread_dlo()andread_dmo().These methods no longer accept a
row_limitargument. When running locally, reads are automatically capped at 1000 rows to prevent accidentally fetching large datasets during development. When deployed to Data Cloud, no limit is applied and all records are returned.Why: The
row_limitparameter duplicated PySpark's built-in.limit()and created a behavioral difference between local and deployed environments. The 1000-row safety net is now handled internally via thedefault_row_limitsetting inconfig.yaml, and deployed environments naturally omit it.Migration: Remove any
row_limitarguments from yourread_dlo()andread_dmo()calls. If you need a specific number of rows, use PySpark's.limit()on the returned DataFrame:# Before df = client.read_dlo("MyObject__dll", row_limit=500) # After df = client.read_dlo("MyObject__dll").limit(500)
-
read_dlo()andread_dmo()now return DataFrames with all-lowercase column names.Column names returned by both
QueryAPIDataCloudReaderandSFCLIDataCloudReaderare now lowercased to match the column names produced by the deployed Data Cloud environment (e.g.,unitprice__cinstead ofUnitPrice__c).Why: In the deployed environment, column names are normalized to lowercase by the underlying Iceberg metadata layer. The local SDK previously returned the original API casing, causing "column does not exist" errors when scripts were deployed. This change aligns local behavior with the cloud.
Migration: Update any column references in your local scripts to use lowercase:
# Before df.withColumn("Description__c", upper(col("Description__c"))) df.drop("KQ_Id__c") df["UnitPrice__c"] # After df.withColumn("description__c", upper(col("description__c"))) df.drop("kq_id__c") df["unitprice__c"]
Scripts already running in Data Cloud are unaffected — the cloud always returned lowercase column names.