code-sandboxes 1.9.30__tar.gz → 1.9.31__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.
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/PKG-INFO +1 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/__version__.py +1 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/conformance.py +37 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/resolve.py +50 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/environments/specification.mdx +1 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/daytona.mdx +27 -9
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/e2b.mdx +1 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/modal.mdx +1 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_resolve.py +87 -1
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/build.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/environments-live.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/py-code-style.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/py-tests.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/py-typing.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.github/workflows/reusable-python.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.gitignore +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.licenserc.yaml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.pre-commit-config.yaml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/.vscode/settings.json +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/CHANGELOG.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/Dockerfile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/LICENSE +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/Makefile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/README.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/__init__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/base.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/bridge_mount.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/builds.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/cli.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/client.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/cloudflare_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/commands.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/console.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/contents.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/coreweave_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/daytona_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/docker_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/e2b_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/__init__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/__main__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/accounts.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/__init__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/datalayer.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/daytona.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/e2b.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/managed.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/modal.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/attest.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/bases.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/breaker.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/build_secrets.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/builders.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/canonical.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/constraints/wheelhouse/jupyter_server-2.21.0+datalayer.1-py3-none-any.whl +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/contract.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/doctor/__init__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/doctor/build.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/doctor/report.schema.json +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/errors.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/files.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/image_import.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/lifecycle.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/policy.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/redact.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/resolve_conda.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/schema.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/spec.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/eval_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/exceptions.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/filesystem.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/google_colab.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/google_colab_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/interfaces.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/jupyter_ingress.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/jupyter_server_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/kaggle.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/kaggle_execute.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/kaggle_live.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/kaggle_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/lifecycle.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/manage.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/modal_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/models.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/monty_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/provider_ingress.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/providers.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docker/Dockerfile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/.gitignore +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/LICENSE +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/Makefile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/README.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/babel.config.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/api-reference/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/api-reference/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/cli/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/cli/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/contribute/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/contribute/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/environments/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/environments/contract.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/environments/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/environments/spike-2026-09.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/examples/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/examples/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/guide/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/guide/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/install/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/install/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/provider-ingress.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/_category_.yml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/cloudflare.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/coreweave.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/datalayer.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/docker.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/eval.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/google-colab.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/index.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/jupyter-server.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/kaggle.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docs/providers/monty.mdx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/docusaurus.config.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/package.json +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/sidebars.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow1.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow1.module.css +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow2.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow2.module.css +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow3.js +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/components/HomepageRow3.module.css +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/css/custom.css +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/pages/index.module.css +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/pages/markdown-page.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/theme/CustomDocItem.tsx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/src/theme/IconsTitle.tsx +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/bricks.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/building-construction.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/cloud-sun.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/cloud.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/collaboration.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/copy-left.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/logo.png +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/logo.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/personas/business.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/personas/devops.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/datalayer/project/project-line.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/favicon.ico +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/jupyter.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/open-source.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/react-js.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/react-js2.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/ringed-planet.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/robot.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/rocket.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/shield.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/sparkles.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/streamlit.gif +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/super-hero.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/target.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/docs/static/img/thumbs-up.svg +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/README.md +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/Makefile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/exec/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/Makefile +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/cloudflare_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/coreweave_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/datalayer_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/daytona_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/docker_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/e2b_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/eval_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/google_colab_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/jupyter_server_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/kaggle_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/modal_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/examples/repl/monty_sandbox_example.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/pyproject.toml +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/schemas/environment-v1alpha1.json +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/__init__.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/conftest.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_bridge_mount.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_builds.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_cli_exec.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_cli_repl.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_client.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_client_contents.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_client_files.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_client_outcome_error_output.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_cloudflare.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_commands_envelope.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_console.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_contents_contract.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_contents_live_matrix.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_coreweave.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_datalayer_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_daytona.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_e2b.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_accounts.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_attest.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_bases.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_breaker.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_build_secrets.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_builders.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_canonical.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_conformance.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_constraints.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_contract.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_cross_variant_report.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_datalayer_builder.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_daytona_builder.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_dockerfile_source.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_doctor.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_e2b_builder.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_errors.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_image_import.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_lifecycle.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_live_matrix.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_managed_builders.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_modal_builder.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_organization_policy.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_redact.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_resolve_conda.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_spec.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_environment_spec_organization_policy.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_eval.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_factory.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_google_colab.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_integration.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_jupyter_ingress.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_jupyter_server.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_jupyter_server_streaming.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_kaggle.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_kaggle_execute.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_kaggle_live.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_kaggle_session.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_kernel_client_compatibility.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_lifecycle_vocabulary.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_manage.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_manage_datalayer.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_modal_google_colab_sandbox.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_modal_session.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_models.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_output_line_fidelity.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_provider_catalog_names.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_provider_ingress.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_providers.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_sandboxes.py +0 -0
- {code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/tests/test_the_kill_relaunch_drill.py +0 -0
|
@@ -487,7 +487,7 @@ def expected_packages(environment: Any, lock_text: str) -> dict[str, str]:
|
|
|
487
487
|
return conda_expected_packages(dependency_file.content, lock_text or "")
|
|
488
488
|
pinned = locked_versions(lock_text) if lock_text else {}
|
|
489
489
|
names: list[str] = []
|
|
490
|
-
for text in environment
|
|
490
|
+
for text in _declared_dependencies(environment):
|
|
491
491
|
try:
|
|
492
492
|
names.append(canonicalize_name(Requirement(text).name))
|
|
493
493
|
except InvalidRequirement:
|
|
@@ -495,6 +495,42 @@ def expected_packages(environment: Any, lock_text: str) -> dict[str, str]:
|
|
|
495
495
|
return {name: pinned[name] for name in names if name in pinned}
|
|
496
496
|
|
|
497
497
|
|
|
498
|
+
def _declared_dependencies(environment: Any) -> list[str]:
|
|
499
|
+
"""The requirements a person declared, wherever the source keeps them.
|
|
500
|
+
|
|
501
|
+
A `dependencyFile` source names its packages in the file and leaves
|
|
502
|
+
`packages.python` empty, which is what the resolver reads too (E3-01).
|
|
503
|
+
Read from `packages.python` alone, check 5 was handed nothing for a
|
|
504
|
+
`requirements.txt` and passed for it, found live on 2026-09-18 (E3-08).
|
|
505
|
+
"""
|
|
506
|
+
from .spec import parse_requirements_txt
|
|
507
|
+
|
|
508
|
+
build = environment.spec.build
|
|
509
|
+
dependency_file = build.dependency_file
|
|
510
|
+
if build.source == "dependencyFile" and dependency_file is not None:
|
|
511
|
+
if dependency_file.source_format == "requirements":
|
|
512
|
+
return parse_requirements_txt(dependency_file.content)
|
|
513
|
+
if dependency_file.source_format == "pyproject":
|
|
514
|
+
return _pyproject_dependencies(dependency_file.content)
|
|
515
|
+
return list(environment.spec.packages.python.dependencies)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _pyproject_dependencies(text: str) -> list[str]:
|
|
519
|
+
"""`[project].dependencies` of a `pyproject.toml`, or none when it cannot be read."""
|
|
520
|
+
try:
|
|
521
|
+
import tomllib as toml
|
|
522
|
+
except ImportError: # Python 3.10
|
|
523
|
+
try:
|
|
524
|
+
import tomli as toml # type: ignore[no-redef]
|
|
525
|
+
except ImportError:
|
|
526
|
+
return []
|
|
527
|
+
try:
|
|
528
|
+
project = toml.loads(text).get("project") or {}
|
|
529
|
+
except toml.TOMLDecodeError:
|
|
530
|
+
return []
|
|
531
|
+
return [str(item) for item in project.get("dependencies") or []]
|
|
532
|
+
|
|
533
|
+
|
|
498
534
|
def run_core_tier(
|
|
499
535
|
sandbox: Sandbox,
|
|
500
536
|
*,
|
|
@@ -39,6 +39,7 @@ Where the solve runs is the :class:`ResolveRunner`'s business:
|
|
|
39
39
|
from __future__ import annotations
|
|
40
40
|
|
|
41
41
|
import hashlib
|
|
42
|
+
import os
|
|
42
43
|
import re
|
|
43
44
|
import shlex
|
|
44
45
|
import shutil
|
|
@@ -860,9 +861,38 @@ def locked_versions(lock_text: str) -> dict[str, str]:
|
|
|
860
861
|
]
|
|
861
862
|
if len(pinned) == 1:
|
|
862
863
|
versions[canonicalize_name(requirement.name)] = pinned[0]
|
|
864
|
+
elif requirement.url:
|
|
865
|
+
wheel = _wheel_version(requirement.name, requirement.url)
|
|
866
|
+
if wheel:
|
|
867
|
+
versions[canonicalize_name(requirement.name)] = wheel
|
|
863
868
|
return versions
|
|
864
869
|
|
|
865
870
|
|
|
871
|
+
def _wheel_version(name: str, url: str) -> str | None:
|
|
872
|
+
"""The version of a direct reference to a wheel, read from its file name.
|
|
873
|
+
|
|
874
|
+
How a `pyproject` lock brings Datalayer's `jupyter-server` fork, which no
|
|
875
|
+
index serves (E3-08): `uv` records a hash only for a wheel it fetched by
|
|
876
|
+
URL, so the export pins it as `jupyter-server @ https://…whl`, with no
|
|
877
|
+
`==` for the version. The wheel's name carries one, and nothing else here
|
|
878
|
+
does. A reference that is not a wheel of the same distribution pins none.
|
|
879
|
+
"""
|
|
880
|
+
from urllib.parse import unquote, urlsplit
|
|
881
|
+
|
|
882
|
+
from packaging.utils import InvalidWheelFilename, canonicalize_name, parse_wheel_filename
|
|
883
|
+
|
|
884
|
+
filename = unquote(urlsplit(url).path.rsplit("/", 1)[-1])
|
|
885
|
+
if not filename.endswith(".whl"):
|
|
886
|
+
return None
|
|
887
|
+
try:
|
|
888
|
+
wheel_name, version, _build, _tags = parse_wheel_filename(filename)
|
|
889
|
+
except InvalidWheelFilename:
|
|
890
|
+
return None
|
|
891
|
+
if wheel_name != canonicalize_name(name):
|
|
892
|
+
return None
|
|
893
|
+
return str(version)
|
|
894
|
+
|
|
895
|
+
|
|
866
896
|
def apt_pins_in(lock_text: str) -> dict[str, str]:
|
|
867
897
|
"""The apt versions a lock records, by package.
|
|
868
898
|
|
|
@@ -1058,10 +1088,12 @@ def _verified_pyproject_lock(
|
|
|
1058
1088
|
root = Path(directory)
|
|
1059
1089
|
(root / "pyproject.toml").write_text(dependency_file.content, encoding="utf-8")
|
|
1060
1090
|
(root / "uv.lock").write_text(dependency_file.lock_content, encoding="utf-8")
|
|
1091
|
+
environment = _uv_environment(root)
|
|
1061
1092
|
try:
|
|
1062
1093
|
checked = invoke(
|
|
1063
1094
|
[resolved_uv, "lock", "--dry-run"],
|
|
1064
1095
|
cwd=root,
|
|
1096
|
+
env=environment,
|
|
1065
1097
|
capture_output=True,
|
|
1066
1098
|
text=True,
|
|
1067
1099
|
timeout=timeout,
|
|
@@ -1109,6 +1141,7 @@ def _verified_pyproject_lock(
|
|
|
1109
1141
|
exported = invoke(
|
|
1110
1142
|
[resolved_uv, "export", "--locked", "--format", "requirements.txt"],
|
|
1111
1143
|
cwd=root,
|
|
1144
|
+
env=environment,
|
|
1112
1145
|
capture_output=True,
|
|
1113
1146
|
text=True,
|
|
1114
1147
|
timeout=timeout,
|
|
@@ -1138,6 +1171,23 @@ def _verified_pyproject_lock(
|
|
|
1138
1171
|
}
|
|
1139
1172
|
|
|
1140
1173
|
|
|
1174
|
+
def _uv_environment(root: Path) -> dict[str, str]:
|
|
1175
|
+
"""What `uv` checks a lock with: its cache in the scratch directory, and no downloads.
|
|
1176
|
+
|
|
1177
|
+
The durable worker runs as a user with no home, so `uv`'s default cache
|
|
1178
|
+
under `~/.cache` could not be made, and neither could the interpreter it
|
|
1179
|
+
downloads when none satisfies `requires-python` (found on 2026-09-18, E3-08).
|
|
1180
|
+
The interpreter comes from the worker's image instead, where the base
|
|
1181
|
+
channel's own Python is installed: a check that fetched one per resolve would
|
|
1182
|
+
be a check that depends on the network in a way nothing records.
|
|
1183
|
+
"""
|
|
1184
|
+
return {
|
|
1185
|
+
**os.environ,
|
|
1186
|
+
"UV_CACHE_DIR": str(root / ".uv-cache"),
|
|
1187
|
+
"UV_PYTHON_DOWNLOADS": "never",
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
|
|
1141
1191
|
def _refuse_unless_protected_pins_are_locked(packages: Mapping[str, str]) -> None:
|
|
1142
1192
|
"""A `pyproject` source is never merged with Datalayer's pins (E3-01):
|
|
1143
1193
|
bringing your own lock is the point, so nothing is force-injected the
|
|
@@ -42,7 +42,7 @@ A document is refused with `DL_ENV_SPEC_INVALID` when a field is wrong, and with
|
|
|
42
42
|
| `compatibility.variants.required` | `[datalayer]` | At least one of `datalayer`, `e2b`, `daytona`, `modal`. |
|
|
43
43
|
| `compatibility.variants.optional` | none | The same variants, none of them also required. |
|
|
44
44
|
| `compatibility.regions` | none | Region names. |
|
|
45
|
-
| `build.source` | `packages` | `packages
|
|
45
|
+
| `build.source` | `packages` | `packages`; `dependencyFile` (a `requirements.txt`; a `pyproject.toml` whose own `uv.lock` is checked against Datalayer's protected pins and exported rather than re-resolved; or a conda `environment.yml`, resolved in its own `micromamba` solve into an explicit lock with the protected pins merged over its `pip:` layer); `dockerfile` (`build.dockerfile.content`, inline: its `FROM` names an approved base with its channel as the tag, which is what resolves, and what its own instructions install is not locked, as the lock's header says); and `image` (an existing image from a bootstrap registry allowlist, replacing the approved base). `packages`, a `requirements.txt`, a conda `environment.yml` and `dockerfile` build on the Datalayer and Daytona variants. A `pyproject.toml` does not build yet: its `uv.lock` has to lock Datalayer's `jupyter-server` fork, which no package index serves. `image` resolves but does not yet start as a sandbox, because the imported image has no Datalayer runtime layer; `image` from a private registry, and a `COPY` from a Dockerfile's build context, are refused. |
|
|
46
46
|
|
|
47
47
|
## The spec digest
|
|
48
48
|
|
|
@@ -229,15 +229,33 @@ sandbox.files.read_bytes("/tmp/data.parquet")
|
|
|
229
229
|
|
|
230
230
|
## Environments
|
|
231
231
|
|
|
232
|
-
An [Environment](/
|
|
233
|
-
built from the approved Datalayer base,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
An [Environment](/environments) built for Daytona is a **snapshot** —
|
|
233
|
+
built from the approved Datalayer base, the resolved lock, your files and
|
|
234
|
+
`postInstall` commands, and an explicit `tini -- sleep infinity` entrypoint
|
|
235
|
+
(Daytona's own default, otherwise, is a bare `sleep infinity`, with no init
|
|
236
|
+
to reap orphans). The base already carries `uv`, the doctor and the sandbox
|
|
237
|
+
contract's user and locale, so none of that is reinstalled — only the lock is
|
|
238
|
+
genuinely per-build. The snapshot's own `id` is the artifact; a build also
|
|
239
|
+
names it, for operability, but a launch never uses the name.
|
|
240
|
+
|
|
241
|
+
How the lock is installed depends on where the environment came from:
|
|
242
|
+
|
|
243
|
+
| Source | Installed with |
|
|
244
|
+
|---|---|
|
|
245
|
+
| A package list, a `requirements.txt`, a `pyproject.toml` and its `uv.lock` | `uv pip sync --require-hashes`: the snapshot holds exactly the lock |
|
|
246
|
+
| A conda `environment.yml` | `micromamba install` of the explicit lock into the base's own interpreter, then the pip layer the solve recorded |
|
|
247
|
+
| A Dockerfile | Daytona's `Image.from_dockerfile`, its approved `FROM` pinned to the resolved digest, then `uv pip install --require-hashes` of the lock — `install`, not `sync`, so what the Dockerfile installed stays |
|
|
248
|
+
|
|
249
|
+
**Deleted by id, and never stranded.** Retention deletes a snapshot nothing
|
|
250
|
+
keeps any more, by its `id` — a name is reused once its snapshot is gone —
|
|
251
|
+
and a snapshot already gone counts as deleted. A build that fails *after*
|
|
252
|
+
Daytona built its snapshot, at the core tier or at the record, deletes that
|
|
253
|
+
snapshot before it reports the failure, so nothing is left in your account
|
|
254
|
+
that no version names.
|
|
255
|
+
|
|
256
|
+
`make env-build-daytona`, in the `environments` folder of the
|
|
257
|
+
[examples repository](https://github.com/datalayer/examples), builds a
|
|
258
|
+
version for Daytona from the command line.
|
|
241
259
|
|
|
242
260
|
**Bring your own account:** a build runs with the environment owner's own
|
|
243
261
|
Daytona organization, not this package's ambient credentials — either
|
|
@@ -190,7 +190,7 @@ sandbox.files.read_bytes("/tmp/data.parquet")
|
|
|
190
190
|
|
|
191
191
|
## Environments
|
|
192
192
|
|
|
193
|
-
An [Environment](/
|
|
193
|
+
An [Environment](/environments) built for E2B is a **template**, not
|
|
194
194
|
built from the Datalayer base the other three variants share: E2B's own
|
|
195
195
|
code-interpreter server (the FastAPI service `run_code` and contexts
|
|
196
196
|
actually talk to) is proprietary and ships baked into E2B's own
|
|
@@ -74,7 +74,7 @@ with Sandbox.create(
|
|
|
74
74
|
|
|
75
75
|
## Environments
|
|
76
76
|
|
|
77
|
-
An [Environment](/
|
|
77
|
+
An [Environment](/environments) built for Modal is an **image id**,
|
|
78
78
|
`im-…` — pulled `from_aws_ecr` off the approved Datalayer base, with your
|
|
79
79
|
lock installed by `uv pip sync --require-hashes` on top. A published name
|
|
80
80
|
is worth having for operability, but a launch always pins the id: each
|
|
@@ -591,6 +591,19 @@ class TestARequirementsFile:
|
|
|
591
591
|
assert from_packages["content"] == from_file["content"]
|
|
592
592
|
assert from_packages["digest"] == from_file["digest"]
|
|
593
593
|
|
|
594
|
+
def test_check_five_is_handed_what_the_file_names(self) -> None:
|
|
595
|
+
"""Read from `packages.python`, which a file source leaves empty, it
|
|
596
|
+
was handed nothing and check 5 passed for having nothing to check
|
|
597
|
+
(found live on 2026-09-18, E3-08)."""
|
|
598
|
+
from code_sandboxes.environments.conformance import expected_packages
|
|
599
|
+
from code_sandboxes.environments.spec import validate_environment
|
|
600
|
+
|
|
601
|
+
environment = validate_environment(
|
|
602
|
+
a_dependency_file_spec(content="geopandas==1.1.1 # inline\n-r other.txt\nsix>=1\n")
|
|
603
|
+
)
|
|
604
|
+
lock = "geopandas==1.1.1\nsix==1.16.0\nshapely==2.1.2\n"
|
|
605
|
+
assert expected_packages(environment, lock) == {"geopandas": "1.1.1", "six": "1.16.0"}
|
|
606
|
+
|
|
594
607
|
|
|
595
608
|
# -- A pyproject.toml and its uv.lock (E3-01) ----------------------------------
|
|
596
609
|
|
|
@@ -606,9 +619,11 @@ class FakeUv:
|
|
|
606
619
|
def __init__(self, *answers: tuple[int, str, str]) -> None:
|
|
607
620
|
self.answers = list(answers)
|
|
608
621
|
self.calls: list[list[str]] = []
|
|
622
|
+
self.environments: list[dict[str, str]] = []
|
|
609
623
|
|
|
610
|
-
def __call__(self, argv, **
|
|
624
|
+
def __call__(self, argv, **kwargs):
|
|
611
625
|
self.calls.append(list(argv))
|
|
626
|
+
self.environments.append(dict(kwargs.get("env") or {}))
|
|
612
627
|
code, out, err = self.answers[min(len(self.calls), len(self.answers)) - 1]
|
|
613
628
|
return subprocess.CompletedProcess(list(argv), code, out, err)
|
|
614
629
|
|
|
@@ -645,6 +660,13 @@ EXPORTED = (
|
|
|
645
660
|
)
|
|
646
661
|
|
|
647
662
|
|
|
663
|
+
#: Where a `pyproject` author locks Datalayer's `jupyter-server` fork from.
|
|
664
|
+
FORK_WHEEL_URL = (
|
|
665
|
+
"https://github.com/datalayer-externals/jupyter-server/releases/download/"
|
|
666
|
+
"v2.21.0-datalayer.1/jupyter_server-2.21.0%2Bdatalayer.1-py3-none-any.whl"
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
|
|
648
670
|
class TestAPyprojectFile:
|
|
649
671
|
def test_a_current_lock_is_exported_rather_than_resolved(self) -> None:
|
|
650
672
|
uv = FakeUv((0, "", "Resolved 2 packages in 1ms\n"), (0, EXPORTED, ""))
|
|
@@ -791,6 +813,70 @@ class TestAPyprojectFile:
|
|
|
791
813
|
)
|
|
792
814
|
assert raised.value.code.code == "DL_ENV_PROVIDER_ERROR"
|
|
793
815
|
|
|
816
|
+
def test_check_five_is_handed_the_projects_own_dependencies(self) -> None:
|
|
817
|
+
"""`[project].dependencies`, not every package the export pins: most of
|
|
818
|
+
a lock is transitive, and check 5 imports what it is handed."""
|
|
819
|
+
from code_sandboxes.environments.conformance import expected_packages
|
|
820
|
+
from code_sandboxes.environments.spec import validate_environment
|
|
821
|
+
|
|
822
|
+
environment = validate_environment(a_pyproject_spec())
|
|
823
|
+
assert expected_packages(environment, EXPORTED) == {"six": "1.16.0"}
|
|
824
|
+
|
|
825
|
+
def test_the_fork_brought_by_url_is_a_locked_protected_pin(self) -> None:
|
|
826
|
+
"""No index serves `2.21.0+datalayer.1`, and `uv` hashes a wheel only
|
|
827
|
+
when it fetched it by URL: so a real author's export names the fork
|
|
828
|
+
`jupyter-server @ https://…whl`, with no `==` (E3-08). Its version is
|
|
829
|
+
in the wheel's name, and that is what the pin is checked against."""
|
|
830
|
+
by_url = EXPORTED.replace(
|
|
831
|
+
"jupyter-server==2.21.0+datalayer.1 \\\n",
|
|
832
|
+
f"jupyter-server @ {FORK_WHEEL_URL} \\\n",
|
|
833
|
+
)
|
|
834
|
+
uv = FakeUv((0, "", "Resolved 7 packages in 1ms\n"), (0, by_url, ""))
|
|
835
|
+
answer = resolve_environment(
|
|
836
|
+
spec=a_pyproject_spec(),
|
|
837
|
+
variants=["datalayer"],
|
|
838
|
+
bases=BASES,
|
|
839
|
+
uv="/usr/bin/uv",
|
|
840
|
+
pyproject_run=uv,
|
|
841
|
+
)
|
|
842
|
+
assert answer["content"] == by_url
|
|
843
|
+
assert locked_versions(by_url)["jupyter-server"] == "2.21.0+datalayer.1"
|
|
844
|
+
|
|
845
|
+
def test_uv_keeps_its_cache_in_the_scratch_directory_and_downloads_no_python(self) -> None:
|
|
846
|
+
"""The durable worker's user has no home: `uv`'s default cache could not
|
|
847
|
+
be made, nor the interpreter it would download (E3-08)."""
|
|
848
|
+
uv = FakeUv((0, "", "Resolved 7 packages in 1ms\n"), (0, EXPORTED, ""))
|
|
849
|
+
resolve_environment(
|
|
850
|
+
spec=a_pyproject_spec(),
|
|
851
|
+
variants=["datalayer"],
|
|
852
|
+
bases=BASES,
|
|
853
|
+
uv="/usr/bin/uv",
|
|
854
|
+
pyproject_run=uv,
|
|
855
|
+
)
|
|
856
|
+
assert len(uv.environments) == 2
|
|
857
|
+
for environment in uv.environments:
|
|
858
|
+
assert environment["UV_PYTHON_DOWNLOADS"] == "never"
|
|
859
|
+
assert environment["UV_CACHE_DIR"].endswith("/.uv-cache")
|
|
860
|
+
assert "/dl-pyproject-" in environment["UV_CACHE_DIR"]
|
|
861
|
+
assert environment["PATH"]
|
|
862
|
+
|
|
863
|
+
def test_a_url_that_is_not_that_distributions_wheel_pins_nothing(self) -> None:
|
|
864
|
+
assert (
|
|
865
|
+
locked_versions(
|
|
866
|
+
"jupyter-server @ https://example.org/jupyter_server-2.21.0.tar.gz\n"
|
|
867
|
+
f"ipykernel @ {FORK_WHEEL_URL}\n"
|
|
868
|
+
"six @ https://example.org/not-a-wheel-name.whl\n"
|
|
869
|
+
)
|
|
870
|
+
== {}
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
def test_a_pyproject_check_five_cannot_read_hands_it_nothing(self) -> None:
|
|
874
|
+
from code_sandboxes.environments.conformance import expected_packages
|
|
875
|
+
from code_sandboxes.environments.spec import validate_environment
|
|
876
|
+
|
|
877
|
+
environment = validate_environment(a_pyproject_spec(content="[project\n"))
|
|
878
|
+
assert expected_packages(environment, EXPORTED) == {}
|
|
879
|
+
|
|
794
880
|
|
|
795
881
|
# -- An imported image (E3-04) -------------------------------------------------
|
|
796
882
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/__init__.py
RENAMED
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/datalayer.py
RENAMED
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/daytona.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/managed.py
RENAMED
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/adapters/modal.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/build_secrets.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/doctor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_sandboxes-1.9.30 → code_sandboxes-1.9.31}/code_sandboxes/environments/resolve_conda.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|