maxc-cli 0.5.0__tar.gz → 0.5.1__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.5.0 → maxc_cli-0.5.1}/PKG-INFO +1 -1
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/__init__.py +1 -1
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/state_permissions.py +11 -3
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/PKG-INFO +1 -1
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_setting_parser.py +26 -2
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_state_permissions.py +31 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/MANIFEST.in +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/README.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/pyproject.toml +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/scripts/pyinstaller_entry.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/scripts/regression_test.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/setup.cfg +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/setup.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/__main__.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/_samples.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/agent_platforms.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/app.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/audit.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/auth_continuation.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/auth_providers.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/__init__.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/auth.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/catalog.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/data.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/job.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/meta.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/odps.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/backend/query.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/cache.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/catalog_bootstrap.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/cli.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/config.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/exceptions.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/help_format.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/helpers.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/job_ids.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/masking.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/models.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/oauth.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/odps_runtime.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/output.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/setting_parser.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/SKILL.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/agents/openai.yaml +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/bootstrap-auth.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/bootstrap-flow.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/command-patterns.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/json-output-format.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/maxcompute-select-guide.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/maxcompute-sql-notes.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/partition-guide.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/red-lines.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/setup-install.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/sql-common-errors.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/sql-query-patterns.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/skills/references/text2sql-principles.md +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/store.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli/utils.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/SOURCES.txt +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/dependency_links.txt +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/entry_points.txt +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/requires.txt +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/src/maxc_cli.egg-info/top_level.txt +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_agent_hints_and_cli.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_agent_platforms.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_agent_skill_commands.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_agent_skill_commands_context.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_ai_native_contract_regressions.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_auth_logout.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_backend_auth.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_backend_data.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_backend_data_serialization.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_backend_meta.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_build_release_archive_compat.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_build_release_script.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_cache.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_catalog.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_catalog_bootstrap.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_cli_arg_validation.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_cli_mock.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_cli_query_parse_and_sanitize.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_compat.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_config_atomic_write.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_e2e_smoke.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_envelope_shape.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_error_self_correction.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_error_translation.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_exit_codes.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_external_auth.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_flag_hoist.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_help_format.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_help_version_e2e.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_helpers.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_helpers_csv.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_installer_contracts.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_integration.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_integration_real.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_job_improvements.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_job_store_durability.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_manifest_runtime_contract.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_masking.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_meta_schema_and_partition_cols.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_oauth.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_odps_runtime.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_output_action_safety.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_output_format_contract.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_packaging_metadata.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_phase1_improvements.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_pyinstaller_bundle.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_python39_compat.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_query_auto_promote.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_query_result_csv_fallback.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_semantic_scope.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_skill_cli_consistency.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_skill_eval.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_skill_renderer.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_startup_imports.py +0 -0
- {maxc_cli-0.5.0 → maxc_cli-0.5.1}/tests/test_state_portability.py +0 -0
|
@@ -135,10 +135,18 @@ def open_private_directory(
|
|
|
135
135
|
except FileNotFoundError:
|
|
136
136
|
if not create:
|
|
137
137
|
raise
|
|
138
|
-
|
|
138
|
+
try:
|
|
139
|
+
os.mkdir(component, PRIVATE_DIRECTORY_MODE, dir_fd=descriptor)
|
|
140
|
+
except FileExistsError:
|
|
141
|
+
# Another process may have created the same component
|
|
142
|
+
# after our open() observed it missing. Re-open it with
|
|
143
|
+
# O_DIRECTORY/O_NOFOLLOW below so the usual ownership and
|
|
144
|
+
# link checks still apply.
|
|
145
|
+
pass
|
|
146
|
+
else:
|
|
147
|
+
if is_leaf:
|
|
148
|
+
created_leaf = True
|
|
139
149
|
next_descriptor = os.open(component, flags, dir_fd=descriptor)
|
|
140
|
-
if is_leaf:
|
|
141
|
-
created_leaf = True
|
|
142
150
|
os.close(descriptor)
|
|
143
151
|
descriptor = next_descriptor
|
|
144
152
|
|
|
@@ -366,17 +366,37 @@ def test_translate_odps_error_type_error_not_readonly():
|
|
|
366
366
|
assert not isinstance(result, ReadOnlyError)
|
|
367
367
|
|
|
368
368
|
|
|
369
|
-
def test_cli_readonly_error_has_agent_hints():
|
|
369
|
+
def test_cli_readonly_error_has_agent_hints(tmp_path):
|
|
370
370
|
"""Verify that client-side write detection returns WRITE_OPERATION_REQUIRES_FORCE with hints."""
|
|
371
371
|
import io
|
|
372
372
|
import json
|
|
373
373
|
|
|
374
374
|
from maxc_cli.cli import run
|
|
375
375
|
|
|
376
|
+
config_path = tmp_path / "config.yaml"
|
|
377
|
+
config_path.write_text(
|
|
378
|
+
"default_project: test_project\n"
|
|
379
|
+
f"state_dir: {tmp_path / 'state'}\n"
|
|
380
|
+
f"cache_dir: {tmp_path / 'cache'}\n"
|
|
381
|
+
"auth:\n"
|
|
382
|
+
" provider: access_key\n"
|
|
383
|
+
" access_id: test_access_id\n"
|
|
384
|
+
" secret_access_key: test_secret\n"
|
|
385
|
+
" project: test_project\n"
|
|
386
|
+
" endpoint: https://service.example.invalid/api\n",
|
|
387
|
+
encoding="utf-8",
|
|
388
|
+
)
|
|
376
389
|
stdout = io.StringIO()
|
|
377
390
|
|
|
378
391
|
exit_code = run(
|
|
379
|
-
[
|
|
392
|
+
[
|
|
393
|
+
"--config",
|
|
394
|
+
str(config_path),
|
|
395
|
+
"query",
|
|
396
|
+
"CREATE TABLE t (id BIGINT)",
|
|
397
|
+
"--json",
|
|
398
|
+
],
|
|
399
|
+
cwd=tmp_path,
|
|
380
400
|
stdout=stdout,
|
|
381
401
|
)
|
|
382
402
|
|
|
@@ -386,3 +406,7 @@ def test_cli_readonly_error_has_agent_hints():
|
|
|
386
406
|
assert output["error"]["recoverable"] is False
|
|
387
407
|
assert "--force" not in output["error"].get("suggestion", "")
|
|
388
408
|
assert "outside" in output["error"].get("suggestion", "")
|
|
409
|
+
assert any(
|
|
410
|
+
"SELECT-only" in warning
|
|
411
|
+
for warning in output["agent_hints"].get("warnings", [])
|
|
412
|
+
)
|
|
@@ -11,6 +11,7 @@ import pytest
|
|
|
11
11
|
|
|
12
12
|
import maxc_cli.audit as audit_module
|
|
13
13
|
import maxc_cli.cache as cache_module
|
|
14
|
+
import maxc_cli.state_permissions as state_permissions
|
|
14
15
|
from maxc_cli.app import MaxCApp
|
|
15
16
|
from maxc_cli.audit import AuditLogger
|
|
16
17
|
from maxc_cli.cache import LocalCache
|
|
@@ -161,6 +162,36 @@ def test_new_cache_and_state_directories_are_private(tmp_path: Path) -> None:
|
|
|
161
162
|
assert _mode(state_dir / "jobs.json") == 0o600
|
|
162
163
|
|
|
163
164
|
|
|
165
|
+
@pytest.mark.skipif(
|
|
166
|
+
not state_permissions._USE_DIRECTORY_FDS,
|
|
167
|
+
reason="exercises descriptor-relative directory creation",
|
|
168
|
+
)
|
|
169
|
+
def test_private_directory_accepts_a_concurrently_created_component(
|
|
170
|
+
tmp_path: Path,
|
|
171
|
+
monkeypatch,
|
|
172
|
+
) -> None:
|
|
173
|
+
target = tmp_path / "state"
|
|
174
|
+
original_mkdir = state_permissions.os.mkdir
|
|
175
|
+
raced = False
|
|
176
|
+
|
|
177
|
+
def racing_mkdir(path, mode=0o777, *, dir_fd=None):
|
|
178
|
+
nonlocal raced
|
|
179
|
+
if path == target.name and dir_fd is not None and not raced:
|
|
180
|
+
raced = True
|
|
181
|
+
original_mkdir(path, mode, dir_fd=dir_fd)
|
|
182
|
+
raise FileExistsError(path)
|
|
183
|
+
return original_mkdir(path, mode, dir_fd=dir_fd)
|
|
184
|
+
|
|
185
|
+
monkeypatch.setattr(state_permissions.os, "mkdir", racing_mkdir)
|
|
186
|
+
|
|
187
|
+
directory = state_permissions.open_private_directory(target)
|
|
188
|
+
state_permissions.close_private_directory(directory)
|
|
189
|
+
|
|
190
|
+
assert raced is True
|
|
191
|
+
assert target.is_dir()
|
|
192
|
+
assert _mode(target) == 0o700
|
|
193
|
+
|
|
194
|
+
|
|
164
195
|
def test_custom_audit_path_does_not_chmod_existing_shared_parent(tmp_path: Path) -> None:
|
|
165
196
|
shared_dir = tmp_path / "shared"
|
|
166
197
|
shared_dir.mkdir(mode=0o755)
|
|
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
|
|
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
|