nextmv 1.7.2__tar.gz → 1.7.3.dev0__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.
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/PKG-INFO +1 -1
- nextmv-1.7.3.dev0/nextmv/__about__.py +1 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/init.py +22 -5
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/package.py +2 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/executor.py +19 -14
- nextmv-1.7.3.dev0/tests/cli/test_init_cli_call.py +109 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/test_executor.py +115 -0
- nextmv-1.7.2/nextmv/__about__.py +0 -1
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/.python-version +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/CONTRIBUTING.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/LICENSE +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/__entrypoint__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/_serialization.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/account.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/base_model.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/acceptance/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/account/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/account/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/account/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/account/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/account/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/exists.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/push.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/app/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/batch/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/data/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/data/upload.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/ensemble/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/input_set/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/exists.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/instance/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/managed_input/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/app/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/app/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/app/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/app/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/app/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/subscription/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/subscription/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/subscription/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/subscription/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/subscription/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/version/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/version/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/version/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/version/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/marketplace/version/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/cancel.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/logs.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/run/track.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/scenario/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/secrets/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/start.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/stop.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/shadow/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/disable.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/domain/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/domain/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/enable.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/sso/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/start.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/stop.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/switchback/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/upload/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/upload/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/exists.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/cloud/version/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/community/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/community/clone.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/community/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/configuration/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/configuration/config.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/configuration/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/configuration/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/configuration/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/delete.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/register.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/registered.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/sync.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/app/update.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/create.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/get.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/list.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/logs.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/metadata.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/local/run/visuals.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/manifest/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/manifest/init.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/manifest/validate.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/serve.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/server.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/WORKFLOW.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/_helpers.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/acceptance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/account.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/app.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/batch.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/community.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/ensemble.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/guide.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/input_set.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/instance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/local.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/managed_input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/profile.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/run.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/scenario.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/secrets.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/shadow.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/sso.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/switchback.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/mcp/tools/version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/message.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/options.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cli/version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/account.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_acceptance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_batch_scenario.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_ensemble.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_input_set.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_instance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_managed_input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_run.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_secrets.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_shadow.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_switchback.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_utils.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/application/_version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/assets.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/client.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/community.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/input_set.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/instance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/integration.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/marketplace.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/scenario.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/secrets.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/shadow.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/sso.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/switchback.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/url.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/cloud/version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/content_format.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/deprecated.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/application.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/local.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/registry.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/runner.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/local/uv_handler.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/logger.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/manifest.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/model.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/options.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/output.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/polling.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/run.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/safe.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/status.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/binary_json_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/binary_multi-file_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/go.mod +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_json_hello-world/main.go +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/go.mod +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/go_multi-file_hello-world/main.go +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/Main.java +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_json_hello-world/pom.xml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/Main.java +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/java_multi-file_hello-world/pom.xml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_class-assign/visualizations.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/allocation/visuals.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_demand-alloc/workflow/visuals.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_json_hello-world/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/inputs/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_class-assign/visualizations.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/allocation/visuals.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/inputs/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_demand-alloc/workflow/visuals.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/README.md +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/nextmv/templates/python_multi-file_hello-world/requirements.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/pyproject.toml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cli/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cli/test_configuration.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cli/test_main.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cli/test_mcp.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cli/test_version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/app.yaml +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/test_client.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/test_instance.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/test_package.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/cloud/test_scenario.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/integration/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/integration/cloud/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/integration/cloud/test_integration_cloud.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/integration/cloud/test_integration_marketplace.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/test_application.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/test_registry.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/test_runner.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/local/test_uv_handler.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options1.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options2.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options3.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options4.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options5.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options6.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/scripts/options7.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_base_model.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_input.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_inputs/test_data.json +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_logger.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_manifest.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_model.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_options.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_output.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_polling.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_run.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_safe.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_serialization.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/tests/test_version.py +0 -0
- {nextmv-1.7.2 → nextmv-1.7.3.dev0}/uv.lock +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v1.7.3.dev0"
|
|
@@ -927,26 +927,38 @@ def _cli_call(command: list[str]) -> subprocess.CompletedProcess:
|
|
|
927
927
|
"""
|
|
928
928
|
|
|
929
929
|
try:
|
|
930
|
+
# Use binary mode so we can apply different decoding strategies to each
|
|
931
|
+
# stream: stdout is machine-readable (JSON) and must be decoded strictly
|
|
932
|
+
# so corruption surfaces as a clear UnicodeDecodeError rather than a
|
|
933
|
+
# confusing JSONDecodeError; stderr is human-facing and uses
|
|
934
|
+
# errors="replace" so stray non-UTF-8 bytes (common on Windows with
|
|
935
|
+
# codepage cp1252) never crash the thread.
|
|
930
936
|
process = subprocess.Popen(
|
|
931
937
|
command,
|
|
932
938
|
env=os.environ,
|
|
933
|
-
text=True,
|
|
934
939
|
stdout=subprocess.PIPE,
|
|
935
940
|
stderr=subprocess.PIPE,
|
|
936
941
|
)
|
|
937
942
|
|
|
938
943
|
stdout_lines = []
|
|
939
944
|
stderr_lines = []
|
|
945
|
+
stdout_exc: list[BaseException] = []
|
|
940
946
|
|
|
941
947
|
def stream_stderr():
|
|
942
|
-
for
|
|
948
|
+
for raw_line in process.stderr:
|
|
949
|
+
line = raw_line.decode("utf-8", errors="replace")
|
|
943
950
|
rich.print(line, end="", file=sys.stderr)
|
|
944
951
|
stderr_lines.append(line)
|
|
945
952
|
|
|
946
953
|
def stream_stdout():
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
954
|
+
try:
|
|
955
|
+
for raw_line in process.stdout:
|
|
956
|
+
line = raw_line.decode("utf-8")
|
|
957
|
+
rich.print(line, end="")
|
|
958
|
+
stdout_lines.append(line)
|
|
959
|
+
except Exception as exc:
|
|
960
|
+
# Capture so we can re-raise on the main thread after joining.
|
|
961
|
+
stdout_exc.append(exc)
|
|
950
962
|
|
|
951
963
|
# Use threads to read both streams concurrently without deadlocking
|
|
952
964
|
stderr_thread = threading.Thread(target=stream_stderr)
|
|
@@ -959,6 +971,11 @@ def _cli_call(command: list[str]) -> subprocess.CompletedProcess:
|
|
|
959
971
|
stdout_thread.join()
|
|
960
972
|
process.wait()
|
|
961
973
|
|
|
974
|
+
# Re-raise any decoding error from stdout on the calling thread so it
|
|
975
|
+
# surfaces as a clear UnicodeDecodeError rather than being swallowed.
|
|
976
|
+
if stdout_exc:
|
|
977
|
+
raise stdout_exc[0]
|
|
978
|
+
|
|
962
979
|
result = subprocess.CompletedProcess(
|
|
963
980
|
args=command,
|
|
964
981
|
returncode=process.returncode,
|
|
@@ -325,6 +325,8 @@ def __run_command(binary: str, dir: str, redirect_out_err: bool, *arguments: str
|
|
|
325
325
|
stdout=subprocess.PIPE if redirect_out_err else None,
|
|
326
326
|
stderr=subprocess.PIPE if redirect_out_err else None,
|
|
327
327
|
text=True,
|
|
328
|
+
encoding="utf-8",
|
|
329
|
+
errors="replace",
|
|
328
330
|
)
|
|
329
331
|
|
|
330
332
|
if redirect_out_err:
|
|
@@ -146,7 +146,7 @@ def execute_run(
|
|
|
146
146
|
|
|
147
147
|
# Set the run status to running.
|
|
148
148
|
info_file = os.path.join(run_dir, f"{run_id}.json")
|
|
149
|
-
with open(info_file, "r+") as f:
|
|
149
|
+
with open(info_file, "r+", encoding="utf-8") as f:
|
|
150
150
|
info = json.load(f)
|
|
151
151
|
info["metadata"]["status_v2"] = "running"
|
|
152
152
|
f.seek(0)
|
|
@@ -173,9 +173,12 @@ def execute_run(
|
|
|
173
173
|
stderr_lines: list[str] = []
|
|
174
174
|
|
|
175
175
|
# Force unbuffered stdout/stderr in child processes so both
|
|
176
|
-
# streams are written to the log file in real time.
|
|
176
|
+
# streams are written to the log file in real time. Also force
|
|
177
|
+
# UTF-8 encoding for all stdio so Windows codepage mismatches
|
|
178
|
+
# (e.g. cp1252) do not cause UnicodeDecodeError.
|
|
177
179
|
child_env = os.environ.copy()
|
|
178
180
|
child_env["PYTHONUNBUFFERED"] = "1"
|
|
181
|
+
child_env["PYTHONIOENCODING"] = "utf-8"
|
|
179
182
|
|
|
180
183
|
# This is the process that actually executes the entrypoint script.
|
|
181
184
|
# We use subprocess.Popen instead of subprocess.run because we need
|
|
@@ -185,6 +188,8 @@ def execute_run(
|
|
|
185
188
|
args,
|
|
186
189
|
env=child_env,
|
|
187
190
|
text=True,
|
|
191
|
+
encoding="utf-8",
|
|
192
|
+
errors="replace",
|
|
188
193
|
stdin=subprocess.PIPE,
|
|
189
194
|
stdout=subprocess.PIPE,
|
|
190
195
|
stderr=subprocess.PIPE,
|
|
@@ -199,7 +204,7 @@ def execute_run(
|
|
|
199
204
|
# A lock to serialize writes from the stdout and stderr threads so log
|
|
200
205
|
# lines are never interleaved in the log file.
|
|
201
206
|
log_lock = threading.Lock()
|
|
202
|
-
log_f = open(log_file_path, "a")
|
|
207
|
+
log_f = open(log_file_path, "a", encoding="utf-8")
|
|
203
208
|
|
|
204
209
|
def stream_stderr() -> None:
|
|
205
210
|
# Stderr is always streamed live to the log file so errors appear
|
|
@@ -263,12 +268,12 @@ def execute_run(
|
|
|
263
268
|
|
|
264
269
|
except Exception as e:
|
|
265
270
|
# If we encounter an exception, we log it to the stderr log file.
|
|
266
|
-
with open(os.path.join(logs_dir, LOGS_FILE), "a") as f:
|
|
271
|
+
with open(os.path.join(logs_dir, LOGS_FILE), "a", encoding="utf-8") as f:
|
|
267
272
|
f.write(f"\nException during run execution: {str(e)}\n")
|
|
268
273
|
|
|
269
274
|
# Also, we update the run information file to set the status to failed.
|
|
270
275
|
info_file = os.path.join(run_dir, f"{run_id}.json")
|
|
271
|
-
with open(info_file, "r+") as f:
|
|
276
|
+
with open(info_file, "r+", encoding="utf-8") as f:
|
|
272
277
|
info = json.load(f)
|
|
273
278
|
info["metadata"]["status_v2"] = "failed"
|
|
274
279
|
info["metadata"]["error"] = "Run failed, please check logs for details."
|
|
@@ -483,7 +488,7 @@ def _process_run_information(run_id: str, run_dir: str, result: subprocess.Compl
|
|
|
483
488
|
|
|
484
489
|
info_file = os.path.join(run_dir, f"{run_id}.json")
|
|
485
490
|
|
|
486
|
-
with open(info_file) as f:
|
|
491
|
+
with open(info_file, encoding="utf-8") as f:
|
|
487
492
|
info = json.load(f)
|
|
488
493
|
|
|
489
494
|
# Calculate duration.
|
|
@@ -505,7 +510,7 @@ def _process_run_information(run_id: str, run_dir: str, result: subprocess.Compl
|
|
|
505
510
|
info["metadata"]["status_v2"] = status
|
|
506
511
|
info["metadata"]["error"] = error
|
|
507
512
|
|
|
508
|
-
with open(info_file, "w") as f:
|
|
513
|
+
with open(info_file, "w", encoding="utf-8") as f:
|
|
509
514
|
json.dump(info, f, indent=2)
|
|
510
515
|
|
|
511
516
|
|
|
@@ -569,7 +574,7 @@ def _process_run_metrics(
|
|
|
569
574
|
if METRICS_KEY not in stdout_output:
|
|
570
575
|
return
|
|
571
576
|
|
|
572
|
-
with open(metrics_dst, "w") as f:
|
|
577
|
+
with open(metrics_dst, "w", encoding="utf-8") as f:
|
|
573
578
|
metrics = {METRICS_KEY: stdout_output[METRICS_KEY]}
|
|
574
579
|
json.dump(metrics, f, indent=2)
|
|
575
580
|
|
|
@@ -637,7 +642,7 @@ def _process_run_statistics(
|
|
|
637
642
|
if STATISTICS_KEY not in stdout_output:
|
|
638
643
|
return
|
|
639
644
|
|
|
640
|
-
with open(stats_dst, "w") as f:
|
|
645
|
+
with open(stats_dst, "w", encoding="utf-8") as f:
|
|
641
646
|
stats = {STATISTICS_KEY: stdout_output[STATISTICS_KEY]}
|
|
642
647
|
json.dump(stats, f, indent=2)
|
|
643
648
|
|
|
@@ -702,7 +707,7 @@ def _process_run_assets(
|
|
|
702
707
|
if ASSETS_KEY not in stdout_output:
|
|
703
708
|
return
|
|
704
709
|
|
|
705
|
-
with open(assets_dst, "w") as f:
|
|
710
|
+
with open(assets_dst, "w", encoding="utf-8") as f:
|
|
706
711
|
assets = {ASSETS_KEY: stdout_output[ASSETS_KEY]}
|
|
707
712
|
json.dump(assets, f, indent=2)
|
|
708
713
|
|
|
@@ -753,7 +758,7 @@ def _process_run_solutions(
|
|
|
753
758
|
|
|
754
759
|
info_file = os.path.join(run_dir, f"{run_id}.json")
|
|
755
760
|
|
|
756
|
-
with open(info_file) as f:
|
|
761
|
+
with open(info_file, encoding="utf-8") as f:
|
|
757
762
|
info = json.load(f)
|
|
758
763
|
|
|
759
764
|
solutions_dst = os.path.join(outputs_dir, SOLUTIONS_KEY)
|
|
@@ -786,7 +791,7 @@ def _process_run_solutions(
|
|
|
786
791
|
)
|
|
787
792
|
else:
|
|
788
793
|
if bool(stdout_output):
|
|
789
|
-
with open(os.path.join(solutions_dst, DEFAULT_OUTPUT_JSON_FILE), "w") as f:
|
|
794
|
+
with open(os.path.join(solutions_dst, DEFAULT_OUTPUT_JSON_FILE), "w", encoding="utf-8") as f:
|
|
790
795
|
if isinstance(stdout_output, dict):
|
|
791
796
|
json.dump(stdout_output, f, indent=2)
|
|
792
797
|
elif isinstance(stdout_output, str):
|
|
@@ -795,7 +800,7 @@ def _process_run_solutions(
|
|
|
795
800
|
# Update the run information file with the output size and type.
|
|
796
801
|
calculate_files_size(run_dir, run_id, solutions_dst, metadata_key="output_size")
|
|
797
802
|
info["metadata"]["format"]["output"] = {"type": output_format.value}
|
|
798
|
-
with open(info_file, "w") as f:
|
|
803
|
+
with open(info_file, "w", encoding="utf-8") as f:
|
|
799
804
|
json.dump(info, f, indent=2)
|
|
800
805
|
|
|
801
806
|
|
|
@@ -824,7 +829,7 @@ def _process_run_visuals(run_dir: str, outputs_dir: str) -> None:
|
|
|
824
829
|
else:
|
|
825
830
|
return
|
|
826
831
|
|
|
827
|
-
with open(assets_file) as f:
|
|
832
|
+
with open(assets_file, encoding="utf-8") as f:
|
|
828
833
|
assets = json.load(f)
|
|
829
834
|
|
|
830
835
|
# Create visuals directory.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the encoding behaviour of _cli_call in nextmv.cli.init.
|
|
3
|
+
|
|
4
|
+
On Windows, subprocess pipes default to the system codepage (e.g. cp1252).
|
|
5
|
+
Byte 0x8f is undefined in cp1252, so without an explicit encoding this raises
|
|
6
|
+
UnicodeDecodeError. The fix decodes each stream independently:
|
|
7
|
+
|
|
8
|
+
- stderr → utf-8 with errors="replace" (human-facing, replacement char is OK)
|
|
9
|
+
- stdout → utf-8 strict (machine-readable JSON; corruption must
|
|
10
|
+
surface as a clear UnicodeDecodeError,
|
|
11
|
+
not a silent JSONDecodeError later)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import subprocess
|
|
15
|
+
import unittest
|
|
16
|
+
from io import BytesIO
|
|
17
|
+
from unittest.mock import MagicMock, patch
|
|
18
|
+
|
|
19
|
+
import typer
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _make_pipe(raw_bytes: bytes):
|
|
23
|
+
"""Return a mock binary pipe whose iteration yields lines from *raw_bytes*."""
|
|
24
|
+
bio = BytesIO(raw_bytes)
|
|
25
|
+
mock_pipe = MagicMock()
|
|
26
|
+
mock_pipe.__iter__ = lambda self: iter(bio)
|
|
27
|
+
return mock_pipe
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _make_process(stdout_bytes: bytes = b"", stderr_bytes: bytes = b"", returncode: int = 0):
|
|
31
|
+
mock = MagicMock()
|
|
32
|
+
mock.stdout = _make_pipe(stdout_bytes)
|
|
33
|
+
mock.stderr = _make_pipe(stderr_bytes)
|
|
34
|
+
mock.returncode = returncode
|
|
35
|
+
mock.wait = MagicMock()
|
|
36
|
+
return mock
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestCliCallEncoding(unittest.TestCase):
|
|
40
|
+
"""Verify that _cli_call survives non-UTF-8 bytes and applies the correct
|
|
41
|
+
decoding strategy to each stream."""
|
|
42
|
+
|
|
43
|
+
def _invoke(self, stdout_bytes: bytes, stderr_bytes: bytes) -> subprocess.CompletedProcess:
|
|
44
|
+
"""Call _cli_call with a mocked Popen and return its result."""
|
|
45
|
+
from nextmv.cli.init import _cli_call
|
|
46
|
+
|
|
47
|
+
mock_process = _make_process(stdout_bytes, stderr_bytes)
|
|
48
|
+
|
|
49
|
+
with patch("nextmv.cli.init.subprocess.Popen", return_value=mock_process):
|
|
50
|
+
return _cli_call(["echo", "test"])
|
|
51
|
+
|
|
52
|
+
def test_non_utf8_in_stderr_replaced_not_raised(self):
|
|
53
|
+
"""Byte 0x8f (undefined in cp1252) in stderr must not raise; it becomes a replacement char."""
|
|
54
|
+
result = self._invoke(
|
|
55
|
+
stdout_bytes=b"",
|
|
56
|
+
stderr_bytes=b"warning: bad byte \x8f here\n",
|
|
57
|
+
)
|
|
58
|
+
# The replacement character U+FFFD (or '?') appears; no exception was raised.
|
|
59
|
+
self.assertIn("warning: bad byte", result.stderr)
|
|
60
|
+
|
|
61
|
+
def test_valid_utf8_stderr_preserved(self):
|
|
62
|
+
"""Normal UTF-8 stderr (including non-ASCII) must be preserved exactly."""
|
|
63
|
+
result = self._invoke(
|
|
64
|
+
stdout_bytes=b"",
|
|
65
|
+
stderr_bytes="info: résumé 日本語\n".encode(),
|
|
66
|
+
)
|
|
67
|
+
self.assertIn("résumé", result.stderr)
|
|
68
|
+
self.assertIn("日本語", result.stderr)
|
|
69
|
+
|
|
70
|
+
def test_valid_utf8_stdout_preserved(self):
|
|
71
|
+
"""Normal UTF-8 stdout is decoded and returned intact."""
|
|
72
|
+
result = self._invoke(
|
|
73
|
+
stdout_bytes=b'{"key": "value"}\n',
|
|
74
|
+
stderr_bytes=b"",
|
|
75
|
+
)
|
|
76
|
+
self.assertIn('"key"', result.stdout)
|
|
77
|
+
|
|
78
|
+
def test_non_utf8_in_stdout_raises_unicode_error(self):
|
|
79
|
+
"""Non-UTF-8 bytes in stdout must not be silently swallowed.
|
|
80
|
+
|
|
81
|
+
The UnicodeDecodeError is caught by _cli_call's outer handler, which
|
|
82
|
+
prints a human-readable message and raises typer.Exit(code=1). That is
|
|
83
|
+
the correct production behaviour: the caller sees a clear failure rather
|
|
84
|
+
than a confusing JSONDecodeError or silently corrupted data.
|
|
85
|
+
"""
|
|
86
|
+
with self.assertRaises((UnicodeDecodeError, typer.Exit)):
|
|
87
|
+
self._invoke(
|
|
88
|
+
stdout_bytes=b'{"key": "\x8f"}\n',
|
|
89
|
+
stderr_bytes=b"",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def test_popen_called_in_binary_mode(self):
|
|
93
|
+
"""_cli_call must open the subprocess in binary mode (no text=True / encoding kwarg)
|
|
94
|
+
so that each stream can be decoded with its own strategy."""
|
|
95
|
+
from nextmv.cli.init import _cli_call
|
|
96
|
+
|
|
97
|
+
mock_process = _make_process(b'{"ok": true}\n', b"")
|
|
98
|
+
|
|
99
|
+
with patch("nextmv.cli.init.subprocess.Popen", return_value=mock_process) as mock_popen:
|
|
100
|
+
_cli_call(["echo", "test"])
|
|
101
|
+
|
|
102
|
+
call_kwargs = mock_popen.call_args[1]
|
|
103
|
+
# Must NOT pass text=True or a global encoding – binary mode is required.
|
|
104
|
+
self.assertNotIn("encoding", call_kwargs)
|
|
105
|
+
self.assertNotIn("text", call_kwargs)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
unittest.main()
|
|
@@ -1203,5 +1203,120 @@ class TestCopyNewOrModifiedFiles(unittest.TestCase):
|
|
|
1203
1203
|
self._assert_file_not_exists(os.path.join(self.dst_dir, "regular.pyc"))
|
|
1204
1204
|
|
|
1205
1205
|
|
|
1206
|
+
class TestExecutorEncoding(unittest.TestCase):
|
|
1207
|
+
"""Tests that execute_run handles non-UTF-8 bytes in subprocess output without crashing."""
|
|
1208
|
+
|
|
1209
|
+
def setUp(self):
|
|
1210
|
+
self.test_dir = tempfile.mkdtemp()
|
|
1211
|
+
self.run_dir = os.path.join(self.test_dir, "run_dir")
|
|
1212
|
+
os.makedirs(self.run_dir)
|
|
1213
|
+
|
|
1214
|
+
def tearDown(self):
|
|
1215
|
+
shutil.rmtree(self.test_dir)
|
|
1216
|
+
|
|
1217
|
+
def _make_info_file(self, run_id: str) -> None:
|
|
1218
|
+
info_file = os.path.join(self.run_dir, f"{run_id}.json")
|
|
1219
|
+
with open(info_file, "w", encoding="utf-8") as f:
|
|
1220
|
+
json.dump(
|
|
1221
|
+
{
|
|
1222
|
+
"metadata": {
|
|
1223
|
+
"created_at": "2023-01-01T00:00:00Z",
|
|
1224
|
+
"status_v2": "pending",
|
|
1225
|
+
"format": {"output": {"type": "json"}},
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
f,
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
def _run_execute_run(self, stdout_lines, stderr_lines, run_id="test_encoding_run"):
|
|
1232
|
+
"""Helper that patches Popen and runs execute_run, returning normally."""
|
|
1233
|
+
self._make_info_file(run_id)
|
|
1234
|
+
|
|
1235
|
+
mock_process = Mock()
|
|
1236
|
+
mock_process.returncode = 0
|
|
1237
|
+
mock_process.stdin = Mock()
|
|
1238
|
+
mock_process.stdout = iter(stdout_lines)
|
|
1239
|
+
mock_process.stderr = iter(stderr_lines)
|
|
1240
|
+
|
|
1241
|
+
with (
|
|
1242
|
+
patch("nextmv.local.executor._copy_files_from_manifest"),
|
|
1243
|
+
patch("nextmv.local.executor.process_run_output"),
|
|
1244
|
+
patch("nextmv.local.executor.subprocess.Popen", return_value=mock_process),
|
|
1245
|
+
):
|
|
1246
|
+
execute_run(
|
|
1247
|
+
run_id=run_id,
|
|
1248
|
+
src="/test/src",
|
|
1249
|
+
manifest_dict={"execution": {"entrypoint": "main.py"}, "type": "python", "files": ["main.py"]},
|
|
1250
|
+
run_dir=self.run_dir,
|
|
1251
|
+
run_config={"format": {"input": {"type": "json"}}},
|
|
1252
|
+
input_data={"test": "data"},
|
|
1253
|
+
)
|
|
1254
|
+
|
|
1255
|
+
def test_non_utf8_bytes_in_stderr_do_not_crash(self):
|
|
1256
|
+
"""Non-UTF-8 bytes in stderr must not raise UnicodeDecodeError.
|
|
1257
|
+
|
|
1258
|
+
On Windows the subprocess pipes default to the system codepage (e.g.
|
|
1259
|
+
cp1252). Byte 0x8f is undefined in cp1252, so without an explicit
|
|
1260
|
+
encoding this would raise UnicodeDecodeError. The fix is to open the
|
|
1261
|
+
Popen with encoding='utf-8' and errors='replace'.
|
|
1262
|
+
"""
|
|
1263
|
+
# Simulate a line that contains the byte 0x8f – undefined in cp1252.
|
|
1264
|
+
# We use a plain string here because the mock iter already yields str;
|
|
1265
|
+
# the important thing is that the pipeline doesn't blow up when such
|
|
1266
|
+
# a replacement character reaches the log file writer.
|
|
1267
|
+
bad_line = "progress: \ufffd done\n" # U+FFFD = replacement character
|
|
1268
|
+
self._run_execute_run(stdout_lines=[], stderr_lines=[bad_line])
|
|
1269
|
+
|
|
1270
|
+
logs_file = os.path.join(self.run_dir, LOGS_KEY, LOGS_FILE)
|
|
1271
|
+
self.assertTrue(os.path.exists(logs_file))
|
|
1272
|
+
with open(logs_file, encoding="utf-8") as f:
|
|
1273
|
+
content = f.read()
|
|
1274
|
+
self.assertIn("progress:", content)
|
|
1275
|
+
|
|
1276
|
+
def test_non_utf8_bytes_in_stdout_do_not_crash(self):
|
|
1277
|
+
"""Non-UTF-8 replacement characters in stdout are handled gracefully."""
|
|
1278
|
+
bad_line = '{"result": "\ufffd"}\n'
|
|
1279
|
+
self._run_execute_run(stdout_lines=[bad_line], stderr_lines=[])
|
|
1280
|
+
|
|
1281
|
+
def test_log_file_written_as_utf8(self):
|
|
1282
|
+
"""The logs file must be written with UTF-8 encoding so non-ASCII characters survive."""
|
|
1283
|
+
unicode_line = "info: résumé café naïve 日本語\n"
|
|
1284
|
+
self._run_execute_run(stdout_lines=[], stderr_lines=[unicode_line])
|
|
1285
|
+
|
|
1286
|
+
logs_file = os.path.join(self.run_dir, LOGS_KEY, LOGS_FILE)
|
|
1287
|
+
with open(logs_file, encoding="utf-8") as f:
|
|
1288
|
+
content = f.read()
|
|
1289
|
+
self.assertIn("résumé", content)
|
|
1290
|
+
self.assertIn("日本語", content)
|
|
1291
|
+
|
|
1292
|
+
def test_popen_called_with_utf8_encoding(self):
|
|
1293
|
+
"""subprocess.Popen must be called with encoding='utf-8' and errors='replace'."""
|
|
1294
|
+
self._make_info_file("enc_check")
|
|
1295
|
+
|
|
1296
|
+
mock_process = Mock()
|
|
1297
|
+
mock_process.returncode = 0
|
|
1298
|
+
mock_process.stdin = Mock()
|
|
1299
|
+
mock_process.stdout = iter([])
|
|
1300
|
+
mock_process.stderr = iter([])
|
|
1301
|
+
|
|
1302
|
+
with (
|
|
1303
|
+
patch("nextmv.local.executor._copy_files_from_manifest"),
|
|
1304
|
+
patch("nextmv.local.executor.process_run_output"),
|
|
1305
|
+
patch("nextmv.local.executor.subprocess.Popen", return_value=mock_process) as mock_popen,
|
|
1306
|
+
):
|
|
1307
|
+
execute_run(
|
|
1308
|
+
run_id="enc_check",
|
|
1309
|
+
src="/test/src",
|
|
1310
|
+
manifest_dict={"execution": {"entrypoint": "main.py"}, "type": "python", "files": ["main.py"]},
|
|
1311
|
+
run_dir=self.run_dir,
|
|
1312
|
+
run_config={"format": {"input": {"type": "json"}}},
|
|
1313
|
+
input_data={"test": "data"},
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
call_kwargs = mock_popen.call_args[1]
|
|
1317
|
+
self.assertEqual(call_kwargs.get("encoding"), "utf-8")
|
|
1318
|
+
self.assertEqual(call_kwargs.get("errors"), "replace")
|
|
1319
|
+
|
|
1320
|
+
|
|
1206
1321
|
if __name__ == "__main__":
|
|
1207
1322
|
unittest.main()
|
nextmv-1.7.2/nextmv/__about__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "v1.7.2"
|
|
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
|