aio-sf 0.1.0b10__tar.gz → 0.1.0b12__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.
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/PKG-INFO +1 -1
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/batch.py +13 -18
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/exporter/parquet_writer.py +18 -32
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.cursor/rules/api-structure.mdc +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.cursor/rules/async-patterns.mdc +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.cursor/rules/project-tooling.mdc +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.github/workflows/publish.yml +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.github/workflows/test.yml +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/.gitignore +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/LICENSE +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/README.md +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/RELEASE.md +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/pyproject.toml +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/pytest.ini +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/base.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/client_credentials.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/refresh_token.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/sfdx_cli.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/auth/static_token.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/bulk_v2/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/bulk_v2/client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/bulk_v2/types.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/records.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/retry.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/collections/types.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/describe/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/describe/client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/describe/types.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/query/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/query/client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/query/types.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/api/types.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/exporter/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/src/aio_sf/exporter/bulk_export.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/__init__.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/conftest.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/test_api_clients.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/test_auth.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/test_client.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/tests/test_retry_and_batch.py +0 -0
- {aio_sf-0.1.0b10 → aio_sf-0.1.0b12}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aio-sf
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0b12
|
|
4
4
|
Summary: Async Salesforce library for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/callawaycloud/aio-salesforce
|
|
6
6
|
Project-URL: Repository, https://github.com/callawaycloud/aio-salesforce
|
|
@@ -126,29 +126,24 @@ async def process_batches_concurrently(
|
|
|
126
126
|
else:
|
|
127
127
|
errors.append(item)
|
|
128
128
|
|
|
129
|
-
#
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if r is not None and not r.get("success", False)
|
|
140
|
-
)
|
|
141
|
-
records_completed = records_succeeded + records_failed
|
|
142
|
-
else:
|
|
143
|
-
records_succeeded = records_failed = records_completed = 0
|
|
129
|
+
# Update progress_state cumulatively for per-batch reporting
|
|
130
|
+
# We only count successes as completed here (errors remain pending until final)
|
|
131
|
+
progress_state["records_succeeded"] += len(successes)
|
|
132
|
+
progress_state["records_completed"] += len(successes)
|
|
133
|
+
progress_state["records_pending"] = progress_state[
|
|
134
|
+
"total_records"
|
|
135
|
+
] - (
|
|
136
|
+
progress_state["records_succeeded"]
|
|
137
|
+
+ progress_state["records_failed"]
|
|
138
|
+
)
|
|
144
139
|
|
|
145
140
|
result_info: ResultInfo = {
|
|
146
141
|
"successes": successes,
|
|
147
142
|
"errors": errors,
|
|
148
143
|
"total_records": progress_state["total_records"],
|
|
149
|
-
"records_completed": records_completed,
|
|
150
|
-
"records_succeeded": records_succeeded,
|
|
151
|
-
"records_failed": records_failed,
|
|
144
|
+
"records_completed": progress_state["records_completed"],
|
|
145
|
+
"records_succeeded": progress_state["records_succeeded"],
|
|
146
|
+
"records_failed": progress_state["records_failed"],
|
|
152
147
|
"records_pending": progress_state["records_pending"],
|
|
153
148
|
"current_attempt": progress_state["current_attempt"],
|
|
154
149
|
"current_batch_size": progress_state["current_batch_size"],
|
|
@@ -227,17 +227,11 @@ class ParquetWriter:
|
|
|
227
227
|
|
|
228
228
|
# Apply type-specific conversions
|
|
229
229
|
if pa.types.is_boolean(field.type):
|
|
230
|
-
# Convert string 'true'/'false' to boolean
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
{"true": True, "false": False,
|
|
230
|
+
# Convert string 'true'/'false' to boolean
|
|
231
|
+
# Use map directly - unmapped values become NaN which is fine for nullable boolean
|
|
232
|
+
df[field_name] = df[field_name].map(
|
|
233
|
+
{"true": True, "false": False, "True": True, "False": False}
|
|
234
234
|
)
|
|
235
|
-
# For values that weren't mapped, keep the original values
|
|
236
|
-
# This avoids the fillna FutureWarning by using boolean indexing instead
|
|
237
|
-
mask = mapped_series.notna()
|
|
238
|
-
result_series = original_series.copy()
|
|
239
|
-
result_series.loc[mask] = mapped_series.loc[mask]
|
|
240
|
-
df[field_name] = result_series
|
|
241
235
|
elif pa.types.is_integer(field.type):
|
|
242
236
|
df[field_name] = pd.to_numeric(df[field_name], errors="coerce").astype(
|
|
243
237
|
"Int64"
|
|
@@ -256,6 +250,12 @@ class ParquetWriter:
|
|
|
256
250
|
date_series = df[field_name]
|
|
257
251
|
if isinstance(date_series, pd.Series):
|
|
258
252
|
df[field_name] = self._convert_date_strings_to_dates(date_series)
|
|
253
|
+
elif pa.types.is_string(field.type):
|
|
254
|
+
# Ensure string columns contain only strings or None
|
|
255
|
+
# This handles edge cases where non-string values might be present
|
|
256
|
+
df[field_name] = df[field_name].apply(
|
|
257
|
+
lambda x: None if pd.isna(x) else str(x)
|
|
258
|
+
)
|
|
259
259
|
|
|
260
260
|
# Replace empty strings with None for non-string fields
|
|
261
261
|
if not pa.types.is_string(field.type):
|
|
@@ -300,33 +300,19 @@ class ParquetWriter:
|
|
|
300
300
|
|
|
301
301
|
def _convert_date_strings_to_dates(self, series: pd.Series) -> pd.Series:
|
|
302
302
|
"""
|
|
303
|
-
Convert Salesforce ISO date strings to pandas
|
|
303
|
+
Convert Salesforce ISO date strings to pandas datetime objects.
|
|
304
304
|
|
|
305
305
|
Salesforce returns date in ISO format like '2025-10-01'.
|
|
306
|
+
PyArrow will handle conversion from datetime64 to date32.
|
|
306
307
|
"""
|
|
308
|
+
# Replace empty strings with None first
|
|
309
|
+
series = series.replace({"": None})
|
|
307
310
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
try:
|
|
313
|
-
# Handle Salesforce date format (YYYY-MM-DD)
|
|
314
|
-
date_str = str(date_str).strip()
|
|
315
|
-
|
|
316
|
-
# Use pandas to_datetime for date parsing, then convert to date
|
|
317
|
-
return pd.to_datetime(date_str, format="%Y-%m-%d").date()
|
|
318
|
-
|
|
319
|
-
except (ValueError, TypeError) as e:
|
|
320
|
-
logging.warning(f"Failed to parse date string '{date_str}': {e}")
|
|
321
|
-
return pd.NaT
|
|
311
|
+
# Use pandas to_datetime for vectorized conversion
|
|
312
|
+
# This returns datetime64[ns] which PyArrow can convert to date32
|
|
313
|
+
result = pd.to_datetime(series, format="%Y-%m-%d", errors="coerce")
|
|
322
314
|
|
|
323
|
-
|
|
324
|
-
result = series.apply(parse_sf_date)
|
|
325
|
-
if isinstance(result, pd.Series):
|
|
326
|
-
return result
|
|
327
|
-
else:
|
|
328
|
-
# This shouldn't happen, but handle it gracefully
|
|
329
|
-
return pd.Series(result, index=series.index)
|
|
315
|
+
return result
|
|
330
316
|
|
|
331
317
|
def close(self) -> None:
|
|
332
318
|
"""Close the parquet writer."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|