nextmv 1.4.2.dev3__tar.gz → 1.5.0__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.4.2.dev3 → nextmv-1.5.0}/PKG-INFO +1 -1
- nextmv-1.5.0/nextmv/__about__.py +1 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/logs.py +5 -6
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/create.py +51 -1
- nextmv-1.5.0/nextmv/cli/local/run/logs.py +173 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_run.py +2 -2
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/application.py +93 -1
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/executor.py +90 -51
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/polling.py +2 -2
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/local/test_executor.py +110 -47
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/uv.lock +1037 -366
- nextmv-1.4.2.dev3/nextmv/__about__.py +0 -1
- nextmv-1.4.2.dev3/nextmv/cli/local/run/logs.py +0 -69
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/.python-version +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/CONTRIBUTING.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/LICENSE +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/__entrypoint__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/_serialization.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/account.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/base_model.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/acceptance/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/account/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/account/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/account/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/account/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/account/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/exists.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/push.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/app/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/batch/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/data/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/data/upload.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/ensemble/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/input_set/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/exists.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/instance/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/managed_input/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/app/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/app/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/app/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/app/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/app/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/subscription/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/subscription/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/subscription/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/subscription/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/subscription/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/version/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/version/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/version/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/version/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/marketplace/version/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/cancel.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/run/track.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/scenario/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/secrets/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/start.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/stop.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/shadow/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/disable.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/domain/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/domain/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/enable.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/sso/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/start.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/stop.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/switchback/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/upload/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/upload/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/exists.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/cloud/version/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/community/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/community/clone.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/community/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/configuration/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/configuration/config.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/configuration/create.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/configuration/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/configuration/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/init.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/delete.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/register.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/registered.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/sync.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/app/update.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/get.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/list.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/metadata.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/local/run/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/manifest/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/manifest/init.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/manifest/validate.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/serve.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/server.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/WORKFLOW.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/_helpers.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/acceptance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/account.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/app.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/batch.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/community.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/ensemble.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/guide.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/input_set.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/instance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/local.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/managed_input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/profile.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/run.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/scenario.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/secrets.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/shadow.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/sso.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/switchback.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/mcp/tools/version.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/message.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/options.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cli/version.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/acceptance_test.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/account.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_acceptance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_batch_scenario.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_ensemble.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_input_set.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_instance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_managed_input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_secrets.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_shadow.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_switchback.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_utils.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/application/_version.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/assets.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/batch_experiment.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/client.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/community.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/ensemble.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/input_set.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/instance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/integration.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/marketplace.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/package.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/scenario.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/secrets.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/shadow.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/sso.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/switchback.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/url.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/cloud/version.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/content_format.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/src/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/default_app/src/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/deprecated.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/geojson_handler.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/local.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/plotly_handler.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/registry.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/local/runner.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/logger.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/manifest.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/model.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/options.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/output.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/run.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/safe.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/status.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/binary_json_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/binary_multi-file_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/go.mod +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_json_hello-world/main.go +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/go.mod +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/go_multi-file_hello-world/main.go +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/Main.java +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_json_hello-world/pom.xml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/Main.java +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/java_multi-file_hello-world/pom.xml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_class-assign/visualizations.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/allocation/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_demand-alloc/workflow/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_json_hello-world/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/inputs/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_class-assign/visualizations.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/allocation/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/inputs/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_demand-alloc/workflow/visuals.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/.gitignore +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/README.md +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/inputs/input.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/nextmv/templates/python_multi-file_hello-world/requirements.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/pyproject.toml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cli/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cli/test_configuration.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cli/test_main.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cli/test_mcp.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cli/test_version.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/app.yaml +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/test_client.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/test_instance.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/test_package.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/cloud/test_scenario.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/integration/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/integration/cloud/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/integration/cloud/test_integration_cloud.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/integration/cloud/test_integration_marketplace.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/local/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/local/test_application.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/local/test_registry.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/local/test_runner.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options1.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options2.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options3.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options4.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options5.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options6.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/scripts/options7.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_base_model.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_entrypoint/__init__.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_entrypoint/test_entrypoint.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_input.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_inputs/test_data.csv +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_inputs/test_data.json +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_inputs/test_data.txt +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_logger.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_manifest.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_model.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_options.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_output.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_polling.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_run.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_safe.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_serialization.py +0 -0
- {nextmv-1.4.2.dev3 → nextmv-1.5.0}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "v1.5.0"
|
|
@@ -108,14 +108,13 @@ def handle_logs(
|
|
|
108
108
|
"""
|
|
109
109
|
Handle retrieving and outputting logs from a run.
|
|
110
110
|
|
|
111
|
-
If neither `tail` is True nor `logs` is specified, this function
|
|
112
|
-
returns early without doing anything. Otherwise, logs are retrieved and
|
|
113
|
-
optionally written to a file.
|
|
114
|
-
|
|
115
111
|
When `tail` is True, logs are streamed in real-time to stderr as the run
|
|
116
112
|
executes. When `logs` is specified (without tailing), the function waits
|
|
117
|
-
for the run to complete and then fetches all logs at once.
|
|
118
|
-
|
|
113
|
+
for the run to complete and then fetches all logs at once. When neither
|
|
114
|
+
`tail` is True nor `file_output` is set, logs are fetched immediately and
|
|
115
|
+
printed to stderr without waiting for the run to complete. In all cases
|
|
116
|
+
where `tail` is True or `logs` is specified, if a `logs` file path is
|
|
117
|
+
provided, the logs are persisted to that file.
|
|
119
118
|
|
|
120
119
|
Parameters
|
|
121
120
|
----------
|
|
@@ -11,6 +11,7 @@ import typer
|
|
|
11
11
|
|
|
12
12
|
from nextmv.cli.configuration.config import build_local_app
|
|
13
13
|
from nextmv.cli.local.run.get import handle_outputs
|
|
14
|
+
from nextmv.cli.local.run.logs import handle_logs
|
|
14
15
|
from nextmv.cli.message import enum_values, error, print_json, success
|
|
15
16
|
from nextmv.cli.options import LocalAppIDOption, LocalAppSrcOption
|
|
16
17
|
from nextmv.input import InputFormat
|
|
@@ -39,6 +40,16 @@ def create(
|
|
|
39
40
|
),
|
|
40
41
|
] = None,
|
|
41
42
|
# Options for controlling output.
|
|
43
|
+
logs: Annotated[
|
|
44
|
+
str | None,
|
|
45
|
+
typer.Option(
|
|
46
|
+
"--logs",
|
|
47
|
+
"-l",
|
|
48
|
+
help="Waits for the run to complete and saves the logs to this location.",
|
|
49
|
+
metavar="LOGS_PATH",
|
|
50
|
+
rich_help_panel="Output control",
|
|
51
|
+
),
|
|
52
|
+
] = None,
|
|
42
53
|
output: Annotated[
|
|
43
54
|
str | None,
|
|
44
55
|
typer.Option(
|
|
@@ -50,6 +61,16 @@ def create(
|
|
|
50
61
|
rich_help_panel="Output control",
|
|
51
62
|
),
|
|
52
63
|
] = None,
|
|
64
|
+
tail: Annotated[
|
|
65
|
+
bool,
|
|
66
|
+
typer.Option(
|
|
67
|
+
"--tail",
|
|
68
|
+
"-t",
|
|
69
|
+
help="Tail the logs until the run completes. Logs are streamed to [magenta]stderr[/magenta]. "
|
|
70
|
+
"Specify log output location with --logs.",
|
|
71
|
+
rich_help_panel="Output control",
|
|
72
|
+
),
|
|
73
|
+
] = False,
|
|
53
74
|
wait: Annotated[
|
|
54
75
|
bool,
|
|
55
76
|
typer.Option(
|
|
@@ -134,6 +155,10 @@ def create(
|
|
|
134
155
|
specify a destination (file or dir) for the output, depending on the
|
|
135
156
|
content type.
|
|
136
157
|
|
|
158
|
+
Use the --tail flag to stream logs to [magenta]stderr[/magenta] until the
|
|
159
|
+
run completes. Using the --logs flag will also activate waiting, and allows
|
|
160
|
+
you to specify a file to write the logs to.
|
|
161
|
+
|
|
137
162
|
[bold][underline]Examples[/underline][/bold]
|
|
138
163
|
|
|
139
164
|
- Read a [magenta]json[/magenta] input via [magenta]stdin[/magenta], from an [magenta]input.json[/magenta] file,
|
|
@@ -149,11 +174,28 @@ def create(
|
|
|
149
174
|
Wait for the run to complete and print the result to [magenta]stdout[/magenta].
|
|
150
175
|
$ [dim]nextmv local run create --app-src ./my-app --input input.json --wait[/dim]
|
|
151
176
|
|
|
177
|
+
- Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
|
|
178
|
+
create a run for an app at path [magenta]./my-app[/magenta].
|
|
179
|
+
Tail the run's logs, streaming to [magenta]stderr[/magenta].
|
|
180
|
+
$ [dim]nextmv local run create --app-src ./my-app --input input.json --tail[/dim]
|
|
181
|
+
|
|
152
182
|
- Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
|
|
153
183
|
create a run for an app with ID [magenta]hare-app[/magenta].
|
|
154
184
|
Wait for the run to complete and write the result to an [magenta]output.json[/magenta] file.
|
|
155
185
|
$ [dim]nextmv local run create --app-id hare-app --input input.json --output output.json[/dim]
|
|
156
186
|
|
|
187
|
+
- Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and
|
|
188
|
+
create a run for an app with ID [magenta]hare-app[/magenta].
|
|
189
|
+
Wait for the run to complete, and write the logs to a [magenta]logs.log[/magenta] file.
|
|
190
|
+
$ [dim]nextmv local run create --app-id hare-app --input input.json --logs logs.log[/dim]
|
|
191
|
+
|
|
192
|
+
- Read a [magenta]json[/magenta] input from an [magenta]input.json[/magenta] file, and create a run for an app at
|
|
193
|
+
path [magenta]./my-app[/magenta]. Wait for the run to complete. Tail the run's logs, streaming to
|
|
194
|
+
[magenta]stderr[/magenta]. Write the logs to a [magenta]logs.log[/magenta] file. Write the result to an
|
|
195
|
+
[magenta]output.json[/magenta] file.
|
|
196
|
+
$ [dim]nextmv local run create --app-src ./my-app --input input.json --tail --logs logs.log \\
|
|
197
|
+
--output output.json[/dim]
|
|
198
|
+
|
|
157
199
|
- Read a [magenta]multi-file[/magenta] input from an [magenta]inputs[/magenta] directory, and
|
|
158
200
|
create a run for an app at path [magenta]./my-app[/magenta].
|
|
159
201
|
$ [dim]nextmv local run create --app-src ./my-app --input inputs --content-format multi-file[/dim]
|
|
@@ -196,7 +238,7 @@ def create(
|
|
|
196
238
|
)
|
|
197
239
|
|
|
198
240
|
# If we don't need to poll at all we are done.
|
|
199
|
-
if not wait and output is None:
|
|
241
|
+
if not wait and not tail and output is None and logs is None:
|
|
200
242
|
print_json({"run_id": run_id})
|
|
201
243
|
|
|
202
244
|
return
|
|
@@ -209,6 +251,14 @@ def create(
|
|
|
209
251
|
|
|
210
252
|
# Handle what happens after the run is created for logging and result
|
|
211
253
|
# retrieval.
|
|
254
|
+
handle_logs(
|
|
255
|
+
local_app=local_app,
|
|
256
|
+
run_id=run_id,
|
|
257
|
+
tail=tail,
|
|
258
|
+
logs=logs,
|
|
259
|
+
polling_options=polling_options,
|
|
260
|
+
file_output=True,
|
|
261
|
+
)
|
|
212
262
|
handle_outputs(
|
|
213
263
|
local_app=local_app,
|
|
214
264
|
run_id=run_id,
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the local run logs command for the Nextmv CLI.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import rich
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from nextmv.cli.configuration.config import build_local_app
|
|
13
|
+
from nextmv.cli.message import in_progress, success
|
|
14
|
+
from nextmv.cli.options import LocalAppIDOption, LocalAppSrcOption, RunIDOption
|
|
15
|
+
from nextmv.local.application import Application
|
|
16
|
+
from nextmv.polling import PollingOptions, default_polling_options
|
|
17
|
+
|
|
18
|
+
# Set up subcommand application.
|
|
19
|
+
app = typer.Typer()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.command()
|
|
23
|
+
def logs(
|
|
24
|
+
run_id: RunIDOption,
|
|
25
|
+
app_id: LocalAppIDOption = None,
|
|
26
|
+
app_src: LocalAppSrcOption = ".",
|
|
27
|
+
output: Annotated[
|
|
28
|
+
str | None,
|
|
29
|
+
typer.Option(
|
|
30
|
+
"--output",
|
|
31
|
+
"-o",
|
|
32
|
+
help="Waits for the run to complete and saves the logs to this location.",
|
|
33
|
+
metavar="OUTPUT_PATH",
|
|
34
|
+
),
|
|
35
|
+
] = None,
|
|
36
|
+
tail: Annotated[
|
|
37
|
+
bool,
|
|
38
|
+
typer.Option(
|
|
39
|
+
"--tail",
|
|
40
|
+
"-t",
|
|
41
|
+
help="Tail the logs until the run completes. Logs are streamed to [magenta]stderr[/magenta]. "
|
|
42
|
+
"Specify log output location with --output.",
|
|
43
|
+
),
|
|
44
|
+
] = False,
|
|
45
|
+
timeout: Annotated[
|
|
46
|
+
int,
|
|
47
|
+
typer.Option(
|
|
48
|
+
help="The maximum time in seconds to wait for results when polling. Poll indefinitely if not set.",
|
|
49
|
+
metavar="TIMEOUT_SECONDS",
|
|
50
|
+
),
|
|
51
|
+
] = -1,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Get the logs of a local application run.
|
|
55
|
+
|
|
56
|
+
You may identify the app by using --app-src, or --app-id if it has been
|
|
57
|
+
registered. If the app is not already registered, this command will
|
|
58
|
+
register it.
|
|
59
|
+
|
|
60
|
+
By default, the logs are fetched and printed to [magenta]stderr[/magenta].
|
|
61
|
+
Use the --tail flag to stream logs to [magenta]stderr[/magenta] until the
|
|
62
|
+
run completes. Using the --output flag will also activate waiting, and
|
|
63
|
+
allows you to specify a file to write the logs to.
|
|
64
|
+
|
|
65
|
+
[bold][underline]Examples[/underline][/bold]
|
|
66
|
+
|
|
67
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with ID
|
|
68
|
+
[magenta]hare-app[/magenta]. Logs are printed to [magenta]stderr[/magenta].
|
|
69
|
+
$ [dim]nextmv local run logs --app-id hare-app --run-id burrow-123[/dim]
|
|
70
|
+
|
|
71
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with ID
|
|
72
|
+
[magenta]hare-app[/magenta]. Tail the logs until the run completes.
|
|
73
|
+
$ [dim]nextmv local run logs --app-id hare-app --run-id burrow-123 --tail[/dim]
|
|
74
|
+
|
|
75
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with ID
|
|
76
|
+
[magenta]hare-app[/magenta]. Save the logs to a [magenta]logs.log[/magenta] file.
|
|
77
|
+
$ [dim]nextmv local run logs --app-id hare-app --run-id burrow-123 --output logs.log[/dim]
|
|
78
|
+
|
|
79
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with ID
|
|
80
|
+
[magenta]hare-app[/magenta]. Tail the logs and save them to a [magenta]logs.log[/magenta] file.
|
|
81
|
+
$ [dim]nextmv local run logs --app-id hare-app --run-id burrow-123 --tail --output logs.log[/dim]
|
|
82
|
+
|
|
83
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with source path
|
|
84
|
+
[magenta]./my-app[/magenta].
|
|
85
|
+
$ [dim]nextmv local run logs --app-src ./my-app --run-id burrow-123[/dim]
|
|
86
|
+
|
|
87
|
+
- Get the logs of a run with ID [magenta]burrow-123[/magenta], belonging to an app with source path
|
|
88
|
+
[magenta]./my-app[/magenta]. Set a timeout of [magenta]60[/magenta] seconds.
|
|
89
|
+
$ [dim]nextmv local run logs --app-src ./my-app --run-id burrow-123 --timeout 60[/dim]
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
local_app = build_local_app(app_src, app_id)
|
|
93
|
+
|
|
94
|
+
# Build the polling options.
|
|
95
|
+
polling_options = default_polling_options()
|
|
96
|
+
polling_options.max_duration = timeout
|
|
97
|
+
|
|
98
|
+
handle_logs(
|
|
99
|
+
local_app=local_app,
|
|
100
|
+
run_id=run_id,
|
|
101
|
+
tail=tail,
|
|
102
|
+
logs=output,
|
|
103
|
+
polling_options=polling_options,
|
|
104
|
+
file_output=output is not None,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def handle_logs(
|
|
109
|
+
local_app: Application,
|
|
110
|
+
run_id: str,
|
|
111
|
+
tail: bool,
|
|
112
|
+
logs: str | None,
|
|
113
|
+
polling_options: PollingOptions,
|
|
114
|
+
file_output: bool,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Handle retrieving and outputting logs from a run.
|
|
118
|
+
|
|
119
|
+
When `tail` is True, logs are streamed in real-time to stderr as the run
|
|
120
|
+
executes. When `logs` is specified (without tailing), the function waits
|
|
121
|
+
for the run to complete and then fetches all logs at once. When neither
|
|
122
|
+
`tail` is True nor `file_output` is set, logs are fetched immediately and
|
|
123
|
+
printed to stderr without waiting for the run to complete. In all cases
|
|
124
|
+
where `tail` is True or `logs` is specified, if a `logs` file path is
|
|
125
|
+
provided, the logs are persisted to that file.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
local_app : Application
|
|
130
|
+
The local application instance used to interact with the Nextmv Local
|
|
131
|
+
API.
|
|
132
|
+
run_id : str
|
|
133
|
+
The unique identifier of the run to retrieve logs for.
|
|
134
|
+
tail : bool
|
|
135
|
+
If True, streams logs in real-time to stderr as the run executes.
|
|
136
|
+
logs : str | None
|
|
137
|
+
The file path where logs should be written. If None, logs are only
|
|
138
|
+
displayed to stderr (when tailing) and not persisted to a file.
|
|
139
|
+
polling_options : PollingOptions
|
|
140
|
+
Configuration options for polling behavior, including timeout and
|
|
141
|
+
interval settings.
|
|
142
|
+
file_output : bool
|
|
143
|
+
Indicates whether logs should be written to a file. If False, logs are
|
|
144
|
+
only printed to stderr, no matter the status of the run.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
if tail:
|
|
148
|
+
in_progress(msg="Tailing logs...")
|
|
149
|
+
fetched_logs = local_app.run_logs_with_polling(
|
|
150
|
+
run_id=run_id,
|
|
151
|
+
polling_options=polling_options,
|
|
152
|
+
verbose=True,
|
|
153
|
+
rich_print=True,
|
|
154
|
+
)
|
|
155
|
+
if logs is None:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
log_content = "\n".join(log_entry for log_entry in fetched_logs)
|
|
159
|
+
elif logs is not None and logs != "" and file_output:
|
|
160
|
+
in_progress(msg="Getting run logs...")
|
|
161
|
+
local_app.run_result_with_polling(run_id=run_id, polling_options=polling_options)
|
|
162
|
+
run_logs = local_app.run_logs(run_id=run_id)
|
|
163
|
+
log_content = run_logs
|
|
164
|
+
elif not file_output:
|
|
165
|
+
in_progress(msg="Getting run logs...")
|
|
166
|
+
run_logs = local_app.run_logs(run_id=run_id)
|
|
167
|
+
rich.print(run_logs, file=sys.stderr)
|
|
168
|
+
return
|
|
169
|
+
else:
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
Path(logs).write_text(log_content)
|
|
173
|
+
success(f"Run logs written to [magenta]{logs}[/magenta].")
|
|
@@ -824,7 +824,7 @@ class ApplicationRunMixin:
|
|
|
824
824
|
|
|
825
825
|
Examples
|
|
826
826
|
--------
|
|
827
|
-
>>> from nextmv
|
|
827
|
+
>>> from nextmv import PollingOptions
|
|
828
828
|
>>> # Create custom polling options
|
|
829
829
|
>>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
|
|
830
830
|
>>> # Get run logs with polling
|
|
@@ -1058,7 +1058,7 @@ class ApplicationRunMixin:
|
|
|
1058
1058
|
|
|
1059
1059
|
Examples
|
|
1060
1060
|
--------
|
|
1061
|
-
>>> from nextmv
|
|
1061
|
+
>>> from nextmv import PollingOptions
|
|
1062
1062
|
>>> # Create custom polling options
|
|
1063
1063
|
>>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
|
|
1064
1064
|
>>> # Get run result with polling
|
|
@@ -16,6 +16,7 @@ import shutil
|
|
|
16
16
|
import sys
|
|
17
17
|
import tempfile
|
|
18
18
|
import webbrowser
|
|
19
|
+
from collections.abc import Callable
|
|
19
20
|
from datetime import datetime, timezone
|
|
20
21
|
from typing import Any
|
|
21
22
|
|
|
@@ -853,6 +854,97 @@ class Application(BaseModel):
|
|
|
853
854
|
|
|
854
855
|
return logs
|
|
855
856
|
|
|
857
|
+
def run_logs_with_polling(
|
|
858
|
+
self,
|
|
859
|
+
run_id: str,
|
|
860
|
+
verbose: bool = False,
|
|
861
|
+
rich_print: bool = False,
|
|
862
|
+
polling_options: PollingOptions = DEFAULT_POLLING_OPTIONS,
|
|
863
|
+
log_func: Callable[[str], None] | None = None,
|
|
864
|
+
) -> list[str]:
|
|
865
|
+
"""
|
|
866
|
+
Get the logs of a local run with polling.
|
|
867
|
+
|
|
868
|
+
Retrieves the logs of a run. This method polls for the logs until the
|
|
869
|
+
run finishes executing or the polling strategy is exhausted. It is the
|
|
870
|
+
"real-time" equivalent of the `run_logs` method. After the polling is
|
|
871
|
+
done, all the logs are returned as a list of strings (one per line).
|
|
872
|
+
You can use the `verbose` parameter to print the logs as they are
|
|
873
|
+
obtained during the polling process. You can also use the `rich_print`
|
|
874
|
+
parameter to enable rich printing for better formatting of the logs.
|
|
875
|
+
|
|
876
|
+
Parameters
|
|
877
|
+
----------
|
|
878
|
+
run_id : str
|
|
879
|
+
ID of the run to retrieve the logs for.
|
|
880
|
+
verbose : bool, default=False
|
|
881
|
+
Whether to print the logs as they are obtained during the polling
|
|
882
|
+
process.
|
|
883
|
+
rich_print : bool, default=False
|
|
884
|
+
Whether to use rich printing for better formatting of the logs.
|
|
885
|
+
polling_options : PollingOptions, default=DEFAULT_POLLING_OPTIONS
|
|
886
|
+
Options to use when polling for the run logs.
|
|
887
|
+
log_func : Optional[Callable[[str], None]], default=None
|
|
888
|
+
Optional custom logging function callback. This function is invoked
|
|
889
|
+
independently of the `verbose` flag. It needs to take a `str` as
|
|
890
|
+
its argument and return `None`.
|
|
891
|
+
|
|
892
|
+
Returns
|
|
893
|
+
-------
|
|
894
|
+
list[str]
|
|
895
|
+
List of log lines of the run.
|
|
896
|
+
|
|
897
|
+
Raises
|
|
898
|
+
------
|
|
899
|
+
TimeoutError
|
|
900
|
+
If the run does not complete after the polling strategy is
|
|
901
|
+
exhausted based on time duration.
|
|
902
|
+
RuntimeError
|
|
903
|
+
If the run does not complete after the polling strategy is
|
|
904
|
+
exhausted based on number of tries.
|
|
905
|
+
|
|
906
|
+
Examples
|
|
907
|
+
--------
|
|
908
|
+
>>> from nextmv import PollingOptions
|
|
909
|
+
>>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
|
|
910
|
+
>>> logs = app.run_logs_with_polling("run-123", polling_opts)
|
|
911
|
+
>>> for line in logs:
|
|
912
|
+
... print(line)
|
|
913
|
+
Starting optimization...
|
|
914
|
+
Found initial solution
|
|
915
|
+
"""
|
|
916
|
+
|
|
917
|
+
all_logs: list[str] = []
|
|
918
|
+
lines_seen = 0
|
|
919
|
+
|
|
920
|
+
def polling_func() -> tuple[Any, bool]:
|
|
921
|
+
nonlocal lines_seen
|
|
922
|
+
|
|
923
|
+
# Check if the run has finalized. If so, all logs are guaranteed
|
|
924
|
+
# to have been flushed to the file before this check returned.
|
|
925
|
+
run_information = self.run_metadata(run_id=run_id)
|
|
926
|
+
is_done = run_information.metadata.run_is_finalized()
|
|
927
|
+
|
|
928
|
+
# Read the current log file contents and emit only new lines.
|
|
929
|
+
log_contents = self.run_logs(run_id)
|
|
930
|
+
current_lines = log_contents.splitlines() if log_contents else []
|
|
931
|
+
for line in current_lines[lines_seen:]:
|
|
932
|
+
if log_func is not None:
|
|
933
|
+
log_func(line)
|
|
934
|
+
if verbose:
|
|
935
|
+
if rich_print:
|
|
936
|
+
rich.print(line, file=sys.stderr)
|
|
937
|
+
else:
|
|
938
|
+
log(line)
|
|
939
|
+
all_logs.append(line)
|
|
940
|
+
lines_seen = len(current_lines)
|
|
941
|
+
|
|
942
|
+
return all_logs, is_done
|
|
943
|
+
|
|
944
|
+
all_logs = poll(polling_options=polling_options, polling_func=polling_func)
|
|
945
|
+
|
|
946
|
+
return all_logs
|
|
947
|
+
|
|
856
948
|
def run_metadata(self, run_id: str) -> RunInformation:
|
|
857
949
|
"""
|
|
858
950
|
Get the metadata of a local run.
|
|
@@ -1002,7 +1094,7 @@ class Application(BaseModel):
|
|
|
1002
1094
|
|
|
1003
1095
|
Examples
|
|
1004
1096
|
--------
|
|
1005
|
-
>>> from nextmv
|
|
1097
|
+
>>> from nextmv import PollingOptions
|
|
1006
1098
|
>>> # Create custom polling options
|
|
1007
1099
|
>>> polling_opts = PollingOptions(max_tries=50, max_duration=600)
|
|
1008
1100
|
>>> # Get run result with polling
|
|
@@ -20,8 +20,6 @@ resolve_output_format
|
|
|
20
20
|
Function to determine the output format from manifest or directory structure.
|
|
21
21
|
process_run_information
|
|
22
22
|
Function to update run metadata including duration and status.
|
|
23
|
-
process_run_logs
|
|
24
|
-
Function to process and save run logs.
|
|
25
23
|
process_run_metrics
|
|
26
24
|
Function to process and save run metrics.
|
|
27
25
|
process_run_statistics
|
|
@@ -43,6 +41,7 @@ import shutil
|
|
|
43
41
|
import subprocess
|
|
44
42
|
import sys
|
|
45
43
|
import tempfile
|
|
44
|
+
import threading
|
|
46
45
|
from datetime import datetime, timezone
|
|
47
46
|
from typing import Any
|
|
48
47
|
|
|
@@ -159,16 +158,100 @@ def execute_run(
|
|
|
159
158
|
cwd = __determine_cwd(manifest, default=temp_src)
|
|
160
159
|
args = __determine_command(manifest) + [entrypoint] + options_args(options)
|
|
161
160
|
|
|
162
|
-
|
|
161
|
+
# Determine whether stdout should be streamed live to the log file.
|
|
162
|
+
# For MULTI_FILE format, stdout carries log output and must be streamed
|
|
163
|
+
# immediately. For other formats (JSON, CSV_ARCHIVE), stdout carries
|
|
164
|
+
# structured output consumed after the process completes.
|
|
165
|
+
is_multi_file = (
|
|
166
|
+
manifest.configuration is not None
|
|
167
|
+
and manifest.configuration.content is not None
|
|
168
|
+
and manifest.configuration.content.format == InputFormat.MULTI_FILE
|
|
169
|
+
) or run_config["format"]["input"]["type"] == InputFormat.MULTI_FILE.value
|
|
170
|
+
|
|
171
|
+
log_file_path = os.path.join(logs_dir, LOGS_FILE)
|
|
172
|
+
stdout_lines: list[str] = []
|
|
173
|
+
stderr_lines: list[str] = []
|
|
174
|
+
|
|
175
|
+
# Force unbuffered stdout/stderr in child processes so both
|
|
176
|
+
# streams are written to the log file in real time.
|
|
177
|
+
child_env = os.environ.copy()
|
|
178
|
+
child_env["PYTHONUNBUFFERED"] = "1"
|
|
179
|
+
|
|
180
|
+
# This is the process that actually executes the entrypoint script.
|
|
181
|
+
# We use subprocess.Popen instead of subprocess.run because we need
|
|
182
|
+
# to stream the output in real time, which is not possible with
|
|
183
|
+
# subprocess.run.
|
|
184
|
+
process = subprocess.Popen(
|
|
163
185
|
args,
|
|
164
|
-
env=
|
|
165
|
-
check=False,
|
|
186
|
+
env=child_env,
|
|
166
187
|
text=True,
|
|
167
|
-
|
|
168
|
-
|
|
188
|
+
stdin=subprocess.PIPE,
|
|
189
|
+
stdout=subprocess.PIPE,
|
|
190
|
+
stderr=subprocess.PIPE,
|
|
169
191
|
cwd=cwd,
|
|
170
192
|
)
|
|
171
193
|
|
|
194
|
+
def write_stdin() -> None:
|
|
195
|
+
# Write all input at once then close so the child process receives EOF.
|
|
196
|
+
process.stdin.write(stdin_input)
|
|
197
|
+
process.stdin.close()
|
|
198
|
+
|
|
199
|
+
# A lock to serialize writes from the stdout and stderr threads so log
|
|
200
|
+
# lines are never interleaved in the log file.
|
|
201
|
+
log_lock = threading.Lock()
|
|
202
|
+
log_f = open(log_file_path, "a")
|
|
203
|
+
|
|
204
|
+
def stream_stderr() -> None:
|
|
205
|
+
# Stderr is always streamed live to the log file so errors appear
|
|
206
|
+
# immediately even if the process is still running.
|
|
207
|
+
for line in process.stderr:
|
|
208
|
+
with log_lock:
|
|
209
|
+
log_f.write(line)
|
|
210
|
+
log_f.flush()
|
|
211
|
+
stderr_lines.append(line)
|
|
212
|
+
|
|
213
|
+
def stream_stdout() -> None:
|
|
214
|
+
# For multi-file runs, stdout carries log output, so mirror it to
|
|
215
|
+
# the log file in real time just like stderr. For other formats,
|
|
216
|
+
# stdout carries the structured result and is only buffered.
|
|
217
|
+
if is_multi_file:
|
|
218
|
+
for line in process.stdout:
|
|
219
|
+
with log_lock:
|
|
220
|
+
log_f.write(line)
|
|
221
|
+
log_f.flush()
|
|
222
|
+
stdout_lines.append(line)
|
|
223
|
+
else:
|
|
224
|
+
for line in process.stdout:
|
|
225
|
+
stdout_lines.append(line)
|
|
226
|
+
|
|
227
|
+
# Run stdin, stdout, and stderr on separate threads so no single pipe
|
|
228
|
+
# can fill its OS buffer and block the process while another pipe waits,
|
|
229
|
+
# which would cause a deadlock.
|
|
230
|
+
stdin_thread = threading.Thread(target=write_stdin)
|
|
231
|
+
stderr_thread = threading.Thread(target=stream_stderr)
|
|
232
|
+
stdout_thread = threading.Thread(target=stream_stdout)
|
|
233
|
+
|
|
234
|
+
stdin_thread.start()
|
|
235
|
+
stderr_thread.start()
|
|
236
|
+
stdout_thread.start()
|
|
237
|
+
|
|
238
|
+
# Wait for all streams to be fully consumed before calling process.wait()
|
|
239
|
+
# to ensure no output is lost.
|
|
240
|
+
stdin_thread.join()
|
|
241
|
+
stderr_thread.join()
|
|
242
|
+
stdout_thread.join()
|
|
243
|
+
process.wait()
|
|
244
|
+
log_f.close()
|
|
245
|
+
|
|
246
|
+
# Assemble a CompletedProcess so the rest of the pipeline can treat this
|
|
247
|
+
# the same as a synchronous subprocess.run() result.
|
|
248
|
+
result = subprocess.CompletedProcess(
|
|
249
|
+
args=args,
|
|
250
|
+
returncode=process.returncode,
|
|
251
|
+
stdout="".join(stdout_lines),
|
|
252
|
+
stderr="".join(stderr_lines),
|
|
253
|
+
)
|
|
254
|
+
|
|
172
255
|
process_run_output(
|
|
173
256
|
manifest=manifest,
|
|
174
257
|
run_id=run_id,
|
|
@@ -344,12 +427,6 @@ def process_run_output(
|
|
|
344
427
|
run_dir=run_dir,
|
|
345
428
|
result=result,
|
|
346
429
|
)
|
|
347
|
-
process_run_logs(
|
|
348
|
-
output_format=output_format,
|
|
349
|
-
run_dir=run_dir,
|
|
350
|
-
result=result,
|
|
351
|
-
stdout_output=stdout_output,
|
|
352
|
-
)
|
|
353
430
|
process_run_metrics(
|
|
354
431
|
temp_run_outputs_dir=temp_run_outputs_dir,
|
|
355
432
|
outputs_dir=outputs_dir,
|
|
@@ -472,44 +549,6 @@ def process_run_information(run_id: str, run_dir: str, result: subprocess.Comple
|
|
|
472
549
|
json.dump(info, f, indent=2)
|
|
473
550
|
|
|
474
551
|
|
|
475
|
-
def process_run_logs(
|
|
476
|
-
output_format: OutputFormat,
|
|
477
|
-
run_dir: str,
|
|
478
|
-
result: subprocess.CompletedProcess[str],
|
|
479
|
-
stdout_output: str | dict[str, Any],
|
|
480
|
-
) -> None:
|
|
481
|
-
"""
|
|
482
|
-
Processes the logs of the run. Writes the logs to a logs directory.
|
|
483
|
-
For multi-file format, stdout is written to logs if present.
|
|
484
|
-
|
|
485
|
-
Parameters
|
|
486
|
-
----------
|
|
487
|
-
output_format : OutputFormat
|
|
488
|
-
The output format of the run (JSON, CSV_ARCHIVE, or MULTI_FILE).
|
|
489
|
-
run_dir : str
|
|
490
|
-
The path to the run directory where logs will be stored.
|
|
491
|
-
result : subprocess.CompletedProcess[str]
|
|
492
|
-
The result of the subprocess run containing stderr output.
|
|
493
|
-
stdout_output : Union[str, dict[str, Any]]
|
|
494
|
-
The stdout output of the run, either as raw string or parsed dictionary.
|
|
495
|
-
"""
|
|
496
|
-
|
|
497
|
-
logs_dir = os.path.join(run_dir, LOGS_KEY)
|
|
498
|
-
os.makedirs(logs_dir, exist_ok=True)
|
|
499
|
-
std_err = result.stderr
|
|
500
|
-
with open(os.path.join(logs_dir, LOGS_FILE), "w") as f:
|
|
501
|
-
if output_format == OutputFormat.MULTI_FILE and bool(stdout_output):
|
|
502
|
-
if isinstance(stdout_output, dict):
|
|
503
|
-
f.write(json.dumps(stdout_output))
|
|
504
|
-
elif isinstance(stdout_output, str):
|
|
505
|
-
f.write(stdout_output)
|
|
506
|
-
|
|
507
|
-
if std_err:
|
|
508
|
-
f.write("\n")
|
|
509
|
-
|
|
510
|
-
f.write(std_err)
|
|
511
|
-
|
|
512
|
-
|
|
513
552
|
def process_run_metrics(
|
|
514
553
|
temp_run_outputs_dir: str,
|
|
515
554
|
outputs_dir: str,
|
|
@@ -85,7 +85,7 @@ class PollingOptions:
|
|
|
85
85
|
|
|
86
86
|
Examples
|
|
87
87
|
--------
|
|
88
|
-
>>> from nextmv
|
|
88
|
+
>>> from nextmv import PollingOptions
|
|
89
89
|
>>> # Create polling options with custom settings
|
|
90
90
|
>>> polling_options = PollingOptions(
|
|
91
91
|
... max_tries=50,
|
|
@@ -218,7 +218,7 @@ def poll( # noqa: C901
|
|
|
218
218
|
|
|
219
219
|
Examples
|
|
220
220
|
--------
|
|
221
|
-
>>> from nextmv
|
|
221
|
+
>>> from nextmv import PollingOptions, poll
|
|
222
222
|
>>> import time
|
|
223
223
|
>>>
|
|
224
224
|
>>> # Define a polling function that succeeds after 3 tries
|