maxc-cli 0.4.0__tar.gz → 0.4.2__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.
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/PKG-INFO +1 -1
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/setup.py +1 -1
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/app.py +12 -3
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/data.py +31 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/cli.py +16 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/PKG-INFO +1 -1
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/MANIFEST.in +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/README.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/pyproject.toml +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/scripts/regression_test.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/setup.cfg +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/__main__.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/_samples.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/agent_platforms.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/auth_providers.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/__init__.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/auth.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/catalog.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/job.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/meta.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/odps.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/backend/query.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/cache.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/catalog_bootstrap.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/config.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/exceptions.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/help_format.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/helpers.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/masking.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/models.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/output.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/setting_parser.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/SKILL.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/bootstrap-flow.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/command-patterns.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/json-output-format.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/migrate-from-odpscmd.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/partition-guide.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/red-lines.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/setup-install.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli/utils.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/SOURCES.txt +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_agent_hints_and_cli.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_agent_platforms.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_agent_skill_commands.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_agent_skill_commands_context.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_backend_data.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_backend_data_serialization.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_backend_meta.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_build_release_script.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_cache.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_catalog.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_catalog_bootstrap.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_cli_arg_validation.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_cli_mock.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_cli_query_parse_and_sanitize.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_compat.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_envelope_shape.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_error_translation.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_exit_codes.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_external_auth.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_flag_hoist.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_help_format.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_help_version_e2e.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_helpers.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_helpers_csv.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_integration.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_integration_real.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_job_improvements.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_masking.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_meta_schema_and_partition_cols.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_phase1_improvements.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_pyinstaller_bundle.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_query_auto_promote.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_query_result_csv_fallback.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_setting_parser.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_skill_cli_consistency.py +0 -0
- {maxc_cli-0.4.0 → maxc_cli-0.4.2}/tests/test_skill_renderer.py +0 -0
|
@@ -9,7 +9,7 @@ README = ROOT / "README.md"
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name="maxc-cli",
|
|
12
|
-
version="0.4.
|
|
12
|
+
version="0.4.2",
|
|
13
13
|
description="Agent-native MaxCompute CLI for external coding agents",
|
|
14
14
|
long_description=README.read_text(encoding="utf-8"),
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
@@ -607,6 +607,7 @@ class MaxCApp:
|
|
|
607
607
|
cost_check: 'float | None' = None,
|
|
608
608
|
idempotency_key: 'str | None' = None,
|
|
609
609
|
force: 'bool' = False,
|
|
610
|
+
dry_run: 'bool' = False,
|
|
610
611
|
) -> 'Envelope':
|
|
611
612
|
return self.query(
|
|
612
613
|
command="job.submit",
|
|
@@ -617,6 +618,7 @@ class MaxCApp:
|
|
|
617
618
|
cost_check=cost_check,
|
|
618
619
|
idempotency_key=idempotency_key,
|
|
619
620
|
force=force,
|
|
621
|
+
dry_run=dry_run,
|
|
620
622
|
)
|
|
621
623
|
|
|
622
624
|
def job_status(self, job_id: 'str') -> 'Envelope':
|
|
@@ -2465,6 +2467,7 @@ class MaxCApp:
|
|
|
2465
2467
|
block_size: 'int' = 10000,
|
|
2466
2468
|
project: 'str | None' = None,
|
|
2467
2469
|
schema: 'str | None' = None,
|
|
2470
|
+
dry_run: 'bool' = False,
|
|
2468
2471
|
) -> 'Envelope':
|
|
2469
2472
|
target_project = project or self.config.default_project
|
|
2470
2473
|
result = self.backend.upload_table(
|
|
@@ -2473,6 +2476,7 @@ class MaxCApp:
|
|
|
2473
2476
|
delimiter=delimiter, has_header=has_header,
|
|
2474
2477
|
null_marker=null_marker, block_size=block_size,
|
|
2475
2478
|
project=project, schema=schema,
|
|
2479
|
+
dry_run=dry_run,
|
|
2476
2480
|
)
|
|
2477
2481
|
metadata = {
|
|
2478
2482
|
"project": target_project,
|
|
@@ -2480,16 +2484,21 @@ class MaxCApp:
|
|
|
2480
2484
|
"delimiter": delimiter,
|
|
2481
2485
|
"block_size": block_size,
|
|
2482
2486
|
}
|
|
2487
|
+
if dry_run:
|
|
2488
|
+
actions = [action("data.upload", data=result, metadata=metadata)]
|
|
2489
|
+
insights = ["Dry-run validated table schema and CSV file. Re-run without --dry-run to upload."]
|
|
2490
|
+
else:
|
|
2491
|
+
actions = [action("data.sample", data=result, metadata=metadata)]
|
|
2492
|
+
insights = []
|
|
2483
2493
|
envelope = Envelope(
|
|
2484
2494
|
command="data.upload",
|
|
2485
2495
|
status="success",
|
|
2486
2496
|
data=result,
|
|
2487
2497
|
metadata=metadata,
|
|
2488
2498
|
agent_hints=AgentHints(
|
|
2489
|
-
actions=
|
|
2490
|
-
action("data.sample", data=result, metadata=metadata),
|
|
2491
|
-
],
|
|
2499
|
+
actions=actions,
|
|
2492
2500
|
warnings=result.get("warnings", []),
|
|
2501
|
+
insights=insights,
|
|
2493
2502
|
),
|
|
2494
2503
|
)
|
|
2495
2504
|
self.log("data.upload", envelope.status, envelope.metadata)
|
|
@@ -228,6 +228,7 @@ class DataMixin:
|
|
|
228
228
|
block_size: 'int' = 10000,
|
|
229
229
|
project: 'str | None' = None,
|
|
230
230
|
schema: 'str | None' = None,
|
|
231
|
+
dry_run: 'bool' = False,
|
|
231
232
|
) -> 'dict[str, Any]':
|
|
232
233
|
"""Upload a CSV/TSV file into an existing table or partition via Tunnel.
|
|
233
234
|
|
|
@@ -295,6 +296,36 @@ class DataMixin:
|
|
|
295
296
|
)
|
|
296
297
|
|
|
297
298
|
bytes_read = os.path.getsize(file_path)
|
|
299
|
+
|
|
300
|
+
if dry_run:
|
|
301
|
+
warnings: list[str] = []
|
|
302
|
+
rows_found = 0
|
|
303
|
+
with open(file_path, encoding="utf-8", newline="") as fh:
|
|
304
|
+
reader = csv.reader(fh, delimiter=delimiter)
|
|
305
|
+
if has_header:
|
|
306
|
+
try:
|
|
307
|
+
header = next(reader)
|
|
308
|
+
except StopIteration:
|
|
309
|
+
header = []
|
|
310
|
+
column_mapping = _resolve_header_mapping(header, data_columns, warnings)
|
|
311
|
+
else:
|
|
312
|
+
column_mapping = [c.name for c in data_columns]
|
|
313
|
+
for _ in reader:
|
|
314
|
+
rows_found += 1
|
|
315
|
+
warnings.append("Dry-run: file and table schema validated, no data was uploaded.")
|
|
316
|
+
return {
|
|
317
|
+
"table": definition.name,
|
|
318
|
+
"applied_partition": partition,
|
|
319
|
+
"rows_written": 0,
|
|
320
|
+
"rows_found": rows_found,
|
|
321
|
+
"bytes_read": bytes_read,
|
|
322
|
+
"column_mapping": column_mapping,
|
|
323
|
+
"blocks": 0,
|
|
324
|
+
"overwrite": overwrite,
|
|
325
|
+
"dry_run": True,
|
|
326
|
+
"warnings": warnings,
|
|
327
|
+
}
|
|
328
|
+
|
|
298
329
|
block_ids: list[int] = []
|
|
299
330
|
rows_written = 0
|
|
300
331
|
warnings: list[str] = []
|
|
@@ -250,6 +250,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
250
250
|
job_submit.add_argument("--max-rows", type=positive_int, default=100, help="Maximum rows to return (default: 100)")
|
|
251
251
|
job_submit.add_argument("--cost-check", type=float, help="Abort if estimated cost exceeds threshold (CU)")
|
|
252
252
|
job_submit.add_argument("--idempotency-key", help="Deduplication key for retries")
|
|
253
|
+
job_submit.add_argument("--dry-run", action="store_true", help="Estimate cost without submitting")
|
|
253
254
|
job_submit.add_argument("--force", action="store_true", default=False, help=argparse.SUPPRESS)
|
|
254
255
|
job_submit.set_defaults(handler=_handle_job_submit)
|
|
255
256
|
|
|
@@ -453,6 +454,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
453
454
|
data_upload.add_argument("--schema", help="Schema name (overrides session default)")
|
|
454
455
|
data_upload.add_argument("--block-size", type=positive_int, default=10000,
|
|
455
456
|
help="Rows per Tunnel block (default: 10000)")
|
|
457
|
+
data_upload.add_argument("--dry-run", action="store_true", help="Validate table and CSV without uploading")
|
|
456
458
|
data_upload.add_argument("--project", help="Target MaxCompute project")
|
|
457
459
|
data_upload.add_argument("--json", action="store_true", help="Output as JSON envelope")
|
|
458
460
|
data_upload.set_defaults(handler=_handle_data_upload)
|
|
@@ -775,8 +777,20 @@ def _configure_stdio_encoding() -> None:
|
|
|
775
777
|
pass
|
|
776
778
|
|
|
777
779
|
|
|
780
|
+
def _configure_user_agent() -> None:
|
|
781
|
+
from . import __version__
|
|
782
|
+
try:
|
|
783
|
+
from odps import options
|
|
784
|
+
except ImportError:
|
|
785
|
+
return
|
|
786
|
+
options.user_agent_pattern = (
|
|
787
|
+
f"maxc-cli/{__version__} $pyodps_version $python_version $os_version"
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
|
|
778
791
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
779
792
|
_configure_stdio_encoding()
|
|
793
|
+
_configure_user_agent()
|
|
780
794
|
return run(argv=argv)
|
|
781
795
|
|
|
782
796
|
|
|
@@ -1088,6 +1102,7 @@ def _handle_job_submit(app: MaxCApp, args: argparse.Namespace, stdout: TextIO) -
|
|
|
1088
1102
|
cost_check=args.cost_check,
|
|
1089
1103
|
idempotency_key=args.idempotency_key,
|
|
1090
1104
|
force=args.force,
|
|
1105
|
+
dry_run=args.dry_run,
|
|
1091
1106
|
)
|
|
1092
1107
|
_emit_envelope(envelope, args=args, stdout=stdout, default_format="json")
|
|
1093
1108
|
|
|
@@ -1360,6 +1375,7 @@ def _handle_data_upload(app: MaxCApp, args: argparse.Namespace, stdout: TextIO)
|
|
|
1360
1375
|
block_size=args.block_size,
|
|
1361
1376
|
project=args.project,
|
|
1362
1377
|
schema=getattr(args, "schema", None),
|
|
1378
|
+
dry_run=args.dry_run,
|
|
1363
1379
|
)
|
|
1364
1380
|
_emit_envelope(envelope, args=args, stdout=stdout, default_format="json")
|
|
1365
1381
|
|
|
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
|
|
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
|