mint-sdk 1.2.2__tar.gz → 1.2.4__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.
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/PKG-INFO +1 -1
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_version.py +2 -2
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/bundled/frontend.json +11 -2
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/filesystem.py +232 -38
- mint_sdk-1.2.4/tests/test_filesystem.py +915 -0
- mint_sdk-1.2.2/tests/test_filesystem.py +0 -485
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/.gitignore +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/README.md +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/pyproject.toml +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_discover.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_envelope.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_files.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_optional.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_prompt.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_staging_base.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/_transport.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/actors.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_artifact.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_endpoint.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_hook.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_migration.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_schema.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_service.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_setting.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_backend_wiring.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_backend_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_assay_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_core_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_helpers.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_lab_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_pack_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_panel_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_frontend_workflow_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_data_template_router_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_field_specs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_frontend_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_frontend_routes.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_naming.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_project.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_r_analysis_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_r_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_source_edits.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/add_template_catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ai_generated_instructions.md.template +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ai_instructions.md.template +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/analysis.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/api_errors.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/app.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_build.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_build_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/_utils.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/add_scaffold_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/add_templates_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/admin_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/auth_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/debug_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/experiment_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/init_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/plugin_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/project_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/status_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/status_formatting.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_commands/update_cmd.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_data_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/cli_entry.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_config.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_exceptions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_http.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/_types.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/client.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/admin.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/auth.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/experiments.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/objects.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/plugins.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/projects.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/client/resources/updates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/compat.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/config.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/context.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract_ts_types.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/contract_typescript.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/data_store.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/db_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dependency_locks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dependency_policy.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deploy_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_api_catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_apis.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_destructure_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_doc_text_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_expression_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_import_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deprecated_frontend_tag_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/deps_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/design_validation.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dev_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/dev_proxy_config.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/cache.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/cli_docs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/contract_docs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/formatter.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_component_catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_component_chooser.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_css_formatters.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_detail_formatters.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/frontend_extractor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/manifest_lookup.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_detail_formatters.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_extractor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/python_introspection.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_docs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_guides.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/reference_search.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/search.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/suggestions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_data.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_docs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_search.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/template_usage.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/terminal_style.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs/topic_routes.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/docs_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_boot.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_database_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_decorator_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_api_usage.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_frontend.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_deprecated_python.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_fixes.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_component_guidance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_component_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_guidance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_import_guidance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sdk_catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sdk_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_sidebar_guidance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_frontend_source_utils.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_guidance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_import_rules.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_models.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_output.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_platform_constraints.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_plugin_metadata.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_r_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_scan_config.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_sdk_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_source_files.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/doctor_workspace.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/endpoint.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/ephemeral_results.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/events_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/exceptions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export_common.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/export_tree.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/frontend_assets.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/generated_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/info_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_ai.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_backend_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_files.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_frontend_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_mode_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_naming.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_post_scaffold.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_script_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_versions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/init_workflow_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/instrument.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/integrations.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_manager.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_notifications.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_profiles.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_sessions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_staging.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/job_stream.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/jobs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/lcms.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/link_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/local_database.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/logging.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/logs_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic/env.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic/script.py.mako +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/_alembic_runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/base.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/conformance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/errors.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/locking.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/ops.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/runner.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/migrations/runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/models.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/package_metadata.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/path_staging.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/payloads.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/permissions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/platform_daemon.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/platform_daemon_supervisor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_database.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_decorators.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_exports.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_frontend.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_lifecycle.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_persistence.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/plugin_settings.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/project_layout.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/project_python.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/py.typed +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/pyproject_edit.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/r.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/remote_context.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/repositories.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/result_schema.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/runtime_dependencies.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/s3.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/schema.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/sdk_generate_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/settings_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/BaseModal-CJrCXCfY.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/ExperimentPopover-IJzKM7P3.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/ExperimentSelectorModal-N0ANhxH5.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/SettingsModal-Bg6ixjmN.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/Skeleton-BdmBYpaw.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/chunk-5LNFb4hk.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/assets/plotly.min-DHWXmmi3.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/generated-ui.css +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/generated-ui.js +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/static/generated-ui/index.html +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/temp_byte_quota.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/assay_matrix.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/base.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/calibration_curve.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/dose_response.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/flow_cytometry_panel.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/instrument_run.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/packs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/plate_map.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/preset_catalog.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/preset_helpers.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/presets.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/protocol_steps.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/qpcr_plate.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/reagent_list.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/sample_prep.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/sample_sheet.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/templates/time_course.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/harness.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/plugins.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/recording_context.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/testing/subprocess.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/token_auth.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/update_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/verify_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/src/mint_sdk/wire.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/conftest.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/scaffold_helpers.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_artifact.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_endpoint.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_hook.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_migration.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_schema.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_service.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_backend_setting.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_add_r_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_metadata.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime_concurrency.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_alembic_runtime_safety.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_api_errors.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_app.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_calendar_contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_command_utils.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_extras.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_status_formatting.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_typer.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_cli_utils.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/__init__.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_admin_plugin_update_api.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_analysis_artifacts_api.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_auth_api.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_client.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_config.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_exceptions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_experiments_api.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_http.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_client/test_objects_api.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_compat.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_config_runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_data_store.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_db_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dependency_locks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dependency_policy.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_deploy_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_deprecated_frontend_patterns.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_design_validation.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_dev_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_discover.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_manifest_lookup.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_detail_formatters.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_extractor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_python_introspection.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_docs_template_search.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_decorator_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_fixes_versions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_import_rules.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_output.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_platform_constraints.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_r_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_sdk_checks.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_doctor_workspace.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_endpoint_host_integration.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_endpoint_routing.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_ephemeral_results.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_error_envelope.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_events_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_exceptions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_experiment_endpoint_scope.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_export.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_foreground_daemon.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_frontend_assets.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_generated_ui_example.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_generated_ui_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_info_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_modes.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_init_versions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_instrument.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_definition.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_manager.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_manager_activity.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_notification_bridge.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_results.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_staging.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_job_stream.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_jobs.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_lazy_exports.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_lcms.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_link_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_local_database.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_logs_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_base.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_conformance.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_errors.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_locking.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_ops.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migration_runner.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_migrations_lazy_import.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_models.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_models_color.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_notification_contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_path_staging.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_permission_decorators.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_permissions.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_cli_commands.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_daemon.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_platform_daemon_supervisor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_actor.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_database.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_decorators.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_save_load.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_settings_model.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_plugin_test_harness.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_project_layout.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_project_layout_integration.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_prompt.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_r_bridge.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_recording_context.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_calendar_dispatch.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_context.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_notification_dispatch.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_remote_transport.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_repository_contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_runtime_dependencies.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_schema.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_dependency_ownership.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_documentation.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_sdk_generate_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_security_boundaries.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_settings_atomic_patch.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_settings_router.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_staged_job_runtime.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_temp_byte_quota.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_templates.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_testing.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_token_auth.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_typed_actor_contract.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_update_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_verify_command.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/tests/test_wire.py +0 -0
- {mint_sdk-1.2.2 → mint_sdk-1.2.4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mint-sdk
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.4
|
|
4
4
|
Summary: MINT Plugin SDK - Build analysis plugins for the MINT (Mass-spec INtegrated Toolkit) platform
|
|
5
5
|
Project-URL: Homepage, https://github.com/MorscherLab/MINT
|
|
6
6
|
Project-URL: Documentation, https://github.com/MorscherLab/MINT/tree/main/packages/sdk-python#readme
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 2,
|
|
21
|
+
__version__ = version = '1.2.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 2, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"sdk": "frontend",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"categories": {
|
|
5
5
|
"components": [
|
|
6
6
|
{
|
|
@@ -15274,7 +15274,7 @@
|
|
|
15274
15274
|
"params": [
|
|
15275
15275
|
{
|
|
15276
15276
|
"name": "options",
|
|
15277
|
-
"type": "{ apiBaseUrl?: string }"
|
|
15277
|
+
"type": "FilePickerAdapterOptions & { apiBaseUrl?: string }"
|
|
15278
15278
|
}
|
|
15279
15279
|
],
|
|
15280
15280
|
"returnType": "PickerAdapter",
|
|
@@ -18426,6 +18426,12 @@
|
|
|
18426
18426
|
{
|
|
18427
18427
|
"name": "FilePicker"
|
|
18428
18428
|
},
|
|
18429
|
+
{
|
|
18430
|
+
"name": "FilePickerAdapterOptions"
|
|
18431
|
+
},
|
|
18432
|
+
{
|
|
18433
|
+
"name": "FilePickerTransport"
|
|
18434
|
+
},
|
|
18429
18435
|
{
|
|
18430
18436
|
"name": "FileSelection"
|
|
18431
18437
|
},
|
|
@@ -20262,6 +20268,9 @@
|
|
|
20262
20268
|
{
|
|
20263
20269
|
"name": "createElisaAssayCollection"
|
|
20264
20270
|
},
|
|
20271
|
+
{
|
|
20272
|
+
"name": "createFilePickerAdapter"
|
|
20273
|
+
},
|
|
20265
20274
|
{
|
|
20266
20275
|
"name": "createFlowCytometryAssayCollection"
|
|
20267
20276
|
},
|
|
@@ -12,11 +12,17 @@ that do not match without the listing ever misreporting what is on disk.
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import os
|
|
15
|
-
|
|
16
|
-
from
|
|
15
|
+
import stat
|
|
16
|
+
from collections import OrderedDict
|
|
17
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
18
|
+
from concurrent.futures import Future
|
|
19
|
+
from dataclasses import dataclass, field
|
|
17
20
|
from datetime import UTC, datetime
|
|
18
21
|
from enum import StrEnum
|
|
22
|
+
from math import isfinite
|
|
19
23
|
from pathlib import Path, PurePosixPath
|
|
24
|
+
from threading import Lock
|
|
25
|
+
from time import monotonic
|
|
20
26
|
|
|
21
27
|
from pydantic import BaseModel, ConfigDict
|
|
22
28
|
|
|
@@ -130,6 +136,18 @@ class DirectoryListing(BaseModel):
|
|
|
130
136
|
truncated: bool = False
|
|
131
137
|
|
|
132
138
|
|
|
139
|
+
@dataclass
|
|
140
|
+
class _DirectorySnapshot:
|
|
141
|
+
entries: list[DirectoryEntry]
|
|
142
|
+
signature: tuple[int, ...]
|
|
143
|
+
timestamp: float
|
|
144
|
+
child_counts: dict[str, int | None] = field(default_factory=dict)
|
|
145
|
+
count_lock: Lock = field(default_factory=Lock)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
_CacheKey = tuple[str, Path, Path, PurePosixPath, bool]
|
|
149
|
+
|
|
150
|
+
|
|
133
151
|
def normalize_type_rules(rules: Iterable[str] | None) -> frozenset[str]:
|
|
134
152
|
"""Normalize user-facing extensions to lowercase, dot-prefixed suffixes.
|
|
135
153
|
|
|
@@ -168,6 +186,8 @@ def _relative_parts(path: str) -> tuple[str, ...]:
|
|
|
168
186
|
if not cleaned:
|
|
169
187
|
return ()
|
|
170
188
|
parts = tuple(part for part in cleaned.split("/") if part not in {"", "."})
|
|
189
|
+
if any(part[1:2] == ":" and part[0].lower() in "abcdefghijklmnopqrstuvwxyz" for part in parts):
|
|
190
|
+
raise MountPathError("A mount path must not contain a Windows drive")
|
|
171
191
|
if any(part == ".." for part in parts):
|
|
172
192
|
raise MountPathError("A mount path must not escape its mount root")
|
|
173
193
|
return parts
|
|
@@ -177,7 +197,7 @@ def _resolved_root(mount: ServerMount) -> Path:
|
|
|
177
197
|
"""Resolve a mount root, treating an absent or unreadable one as offline."""
|
|
178
198
|
try:
|
|
179
199
|
root = mount.root.resolve(strict=True)
|
|
180
|
-
except OSError as exc:
|
|
200
|
+
except (OSError, RuntimeError) as exc:
|
|
181
201
|
raise MountPathNotFoundError(f"Mount {mount.id!r} is not available") from exc
|
|
182
202
|
if not root.is_dir():
|
|
183
203
|
raise MountPathNotFoundError(f"Mount {mount.id!r} is not available")
|
|
@@ -187,23 +207,10 @@ def _resolved_root(mount: ServerMount) -> Path:
|
|
|
187
207
|
def _is_available(mount: ServerMount) -> bool:
|
|
188
208
|
try:
|
|
189
209
|
return mount.root.is_dir()
|
|
190
|
-
except OSError:
|
|
210
|
+
except (OSError, RuntimeError):
|
|
191
211
|
return False
|
|
192
212
|
|
|
193
213
|
|
|
194
|
-
def _child_count(path: Path, *, include_hidden: bool) -> int | None:
|
|
195
|
-
"""Count a directory's entries, or None when it cannot be read.
|
|
196
|
-
|
|
197
|
-
Hidden entries are counted only when the listing itself would show them, so
|
|
198
|
-
the number beside a folder agrees with what opening it displays.
|
|
199
|
-
"""
|
|
200
|
-
try:
|
|
201
|
-
with os.scandir(path) as entries:
|
|
202
|
-
return sum(1 for item in entries if include_hidden or not item.name.startswith("."))
|
|
203
|
-
except OSError:
|
|
204
|
-
return None
|
|
205
|
-
|
|
206
|
-
|
|
207
214
|
def _matches_rules(name: str, extension: str, rules: frozenset[str]) -> bool:
|
|
208
215
|
"""Test a filename against the type rules.
|
|
209
216
|
|
|
@@ -225,6 +232,13 @@ class FileBrowser:
|
|
|
225
232
|
The mount list is deployment configuration supplied by the host platform.
|
|
226
233
|
Everything a caller passes afterwards — mount id, path, type rules — is
|
|
227
234
|
treated as untrusted input.
|
|
235
|
+
|
|
236
|
+
Directory metadata is cached for ``cache_ttl`` seconds (zero disables
|
|
237
|
+
retention), bounded by ``cache_size`` directories. Every request resolves
|
|
238
|
+
its path and checks the directory signature before reusing metadata.
|
|
239
|
+
``refresh=True`` or ``clear_cache`` also invalidates descendant listings.
|
|
240
|
+
Optional ``child_count_type_rules`` maps entry kinds to countable suffixes;
|
|
241
|
+
missing kinds or empty rules count nothing. ``None`` counts every visible entry.
|
|
228
242
|
"""
|
|
229
243
|
|
|
230
244
|
def __init__(
|
|
@@ -232,14 +246,47 @@ class FileBrowser:
|
|
|
232
246
|
mounts: Sequence[ServerMount],
|
|
233
247
|
*,
|
|
234
248
|
type_rules: Iterable[str] | None = None,
|
|
235
|
-
max_entries: int = 2000,
|
|
249
|
+
max_entries: int | None = 2000,
|
|
236
250
|
child_count_limit: int = 250,
|
|
251
|
+
child_count_type_rules: Mapping[EntryKind, Iterable[str]] | None = None,
|
|
252
|
+
cache_ttl: float = 300,
|
|
253
|
+
cache_size: int = 128,
|
|
254
|
+
restrict_symlinks_to_directory: bool = False,
|
|
237
255
|
) -> None:
|
|
256
|
+
if not isfinite(cache_ttl) or cache_ttl < 0:
|
|
257
|
+
raise ValueError("cache_ttl must be finite and non-negative")
|
|
258
|
+
if not isinstance(cache_size, int) or isinstance(cache_size, bool) or cache_size < 1:
|
|
259
|
+
raise ValueError("cache_size must be a positive integer")
|
|
260
|
+
if max_entries is not None and (not isinstance(max_entries, int) or max_entries < 1):
|
|
261
|
+
raise ValueError("max_entries must be a positive integer or None")
|
|
262
|
+
if child_count_limit < 0:
|
|
263
|
+
raise ValueError("child_count_limit must be non-negative")
|
|
238
264
|
self._mounts = tuple(mounts)
|
|
239
265
|
self._by_id = {mount.id: mount for mount in self._mounts}
|
|
240
266
|
self._default_type_rules = normalize_type_rules(type_rules)
|
|
241
267
|
self._max_entries = max_entries
|
|
242
268
|
self._child_count_limit = child_count_limit
|
|
269
|
+
self._child_count_type_rules = (
|
|
270
|
+
{EntryKind(kind): normalize_type_rules(rules) for kind, rules in child_count_type_rules.items()}
|
|
271
|
+
if child_count_type_rules is not None else None
|
|
272
|
+
)
|
|
273
|
+
self._cache_ttl = cache_ttl
|
|
274
|
+
self._cache_size = cache_size
|
|
275
|
+
self._restrict_symlinks_to_directory = restrict_symlinks_to_directory
|
|
276
|
+
# ponytail: bounded by directory count; add an entry budget if very wide mounts exceed memory limits.
|
|
277
|
+
self._cache: OrderedDict[_CacheKey, _DirectorySnapshot] = OrderedDict()
|
|
278
|
+
self._pending: dict[_CacheKey, tuple[tuple[int, ...], Future[_DirectorySnapshot]]] = {}
|
|
279
|
+
self._cache_lock = Lock()
|
|
280
|
+
|
|
281
|
+
def clear_cache(self, mount_id: str | None = None) -> None:
|
|
282
|
+
"""Invalidate listings and detach in-flight scans so they cannot refill the cache."""
|
|
283
|
+
if mount_id is not None:
|
|
284
|
+
self._mount(mount_id)
|
|
285
|
+
with self._cache_lock:
|
|
286
|
+
for mapping in (self._cache, self._pending):
|
|
287
|
+
for key in list(mapping):
|
|
288
|
+
if mount_id is None or key[0] == mount_id:
|
|
289
|
+
del mapping[key]
|
|
243
290
|
|
|
244
291
|
def list_mounts(self) -> list[MountInfo]:
|
|
245
292
|
"""Describe every configured mount, flagging the ones that are offline."""
|
|
@@ -265,12 +312,13 @@ class FileBrowser:
|
|
|
265
312
|
candidate = root.joinpath(*parts)
|
|
266
313
|
try:
|
|
267
314
|
resolved = candidate.resolve(strict=True)
|
|
268
|
-
except OSError as exc:
|
|
315
|
+
except (OSError, RuntimeError) as exc:
|
|
269
316
|
raise MountPathNotFoundError(f"{path!r} does not exist in mount {mount_id!r}") from exc
|
|
270
317
|
# Resolve-then-compare: `parts` is already free of "..", so anything
|
|
271
318
|
# landing outside the root got there through a symlink.
|
|
272
319
|
if resolved != root and not resolved.is_relative_to(root):
|
|
273
320
|
raise MountPathError(f"{path!r} would escape mount {mount_id!r}")
|
|
321
|
+
_relative_parts(resolved.relative_to(root).as_posix())
|
|
274
322
|
return resolved
|
|
275
323
|
|
|
276
324
|
def list_directory(
|
|
@@ -283,6 +331,8 @@ class FileBrowser:
|
|
|
283
331
|
sort: SortKey = SortKey.NAME,
|
|
284
332
|
descending: bool = False,
|
|
285
333
|
include_hidden: bool = False,
|
|
334
|
+
refresh: bool = False,
|
|
335
|
+
count_children: bool = True,
|
|
286
336
|
) -> DirectoryListing:
|
|
287
337
|
"""List one directory inside a mount.
|
|
288
338
|
|
|
@@ -291,15 +341,39 @@ class FileBrowser:
|
|
|
291
341
|
a caller can honestly report "N items, M shown".
|
|
292
342
|
"""
|
|
293
343
|
mount = self._mount(mount_id)
|
|
344
|
+
parts = _relative_parts(path)
|
|
345
|
+
if not include_hidden and any(part.startswith(".") for part in parts):
|
|
346
|
+
raise MountPathError("Hidden directories are not included")
|
|
294
347
|
directory = self.resolve(mount_id, path)
|
|
295
|
-
|
|
348
|
+
root = _resolved_root(mount)
|
|
349
|
+
if not directory.is_relative_to(root):
|
|
350
|
+
raise MountPathError(f"{path!r} would escape mount {mount_id!r}")
|
|
351
|
+
if not include_hidden and any(part.startswith(".") for part in directory.relative_to(root).parts):
|
|
352
|
+
raise MountPathError("Hidden directories are not included")
|
|
353
|
+
try:
|
|
354
|
+
directory_stat = directory.stat(follow_symlinks=False)
|
|
355
|
+
except (OSError, RuntimeError) as exc:
|
|
356
|
+
raise MountPathNotFoundError(f"{path!r} does not exist in mount {mount_id!r}") from exc
|
|
357
|
+
if not stat.S_ISDIR(directory_stat.st_mode):
|
|
296
358
|
raise MountPathError(f"{path!r} is not a directory in mount {mount_id!r}")
|
|
297
359
|
|
|
298
360
|
rules = self._default_type_rules if type_rules is None else normalize_type_rules(type_rules)
|
|
299
|
-
base = PurePosixPath(*
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
361
|
+
base = PurePosixPath(*parts)
|
|
362
|
+
if refresh:
|
|
363
|
+
self.clear_cache(mount_id)
|
|
364
|
+
key = (mount_id, root, directory, base, include_hidden)
|
|
365
|
+
signature = (
|
|
366
|
+
directory_stat.st_dev, directory_stat.st_ino,
|
|
367
|
+
directory_stat.st_mtime_ns, directory_stat.st_ctime_ns, directory_stat.st_size,
|
|
368
|
+
)
|
|
369
|
+
snapshot = self._directory_snapshot(key, signature)
|
|
370
|
+
entries = [
|
|
371
|
+
entry.model_copy(update={
|
|
372
|
+
"matches_type_rules": entry.kind is EntryKind.DIRECTORY
|
|
373
|
+
or _matches_rules(entry.name, entry.extension, rules),
|
|
374
|
+
})
|
|
375
|
+
for entry in snapshot.entries
|
|
376
|
+
]
|
|
303
377
|
total_count = len(entries)
|
|
304
378
|
match_count = sum(1 for entry in entries if entry.matches_type_rules)
|
|
305
379
|
|
|
@@ -314,11 +388,12 @@ class FileBrowser:
|
|
|
314
388
|
|
|
315
389
|
# Cap after sorting so the cut is deterministic, and say so rather than
|
|
316
390
|
# silently returning a short listing.
|
|
317
|
-
truncated = len(entries) > self._max_entries
|
|
391
|
+
truncated = self._max_entries is not None and len(entries) > self._max_entries
|
|
318
392
|
if truncated:
|
|
319
393
|
entries = entries[: self._max_entries]
|
|
320
394
|
|
|
321
|
-
|
|
395
|
+
if count_children:
|
|
396
|
+
entries = self._count_children(directory, entries, snapshot, root=root, include_hidden=include_hidden)
|
|
322
397
|
|
|
323
398
|
return DirectoryListing(
|
|
324
399
|
mount_id=mount_id,
|
|
@@ -338,12 +413,53 @@ class FileBrowser:
|
|
|
338
413
|
except KeyError as exc:
|
|
339
414
|
raise MountNotFoundError(f"unknown mount {mount_id!r}") from exc
|
|
340
415
|
|
|
416
|
+
def _directory_snapshot(self, key: _CacheKey, signature: tuple[int, ...]) -> _DirectorySnapshot:
|
|
417
|
+
_, root, directory, base, include_hidden = key
|
|
418
|
+
with self._cache_lock:
|
|
419
|
+
cached = self._cache.get(key)
|
|
420
|
+
if (
|
|
421
|
+
cached is not None
|
|
422
|
+
and cached.signature == signature
|
|
423
|
+
and monotonic() - cached.timestamp < self._cache_ttl
|
|
424
|
+
):
|
|
425
|
+
self._cache.move_to_end(key)
|
|
426
|
+
return cached
|
|
427
|
+
existing = self._pending.get(key)
|
|
428
|
+
owner = existing is None or existing[0] != signature
|
|
429
|
+
if owner:
|
|
430
|
+
future: Future[_DirectorySnapshot] = Future()
|
|
431
|
+
self._pending[key] = (signature, future)
|
|
432
|
+
else:
|
|
433
|
+
future = existing[1]
|
|
434
|
+
if not owner:
|
|
435
|
+
return future.result()
|
|
436
|
+
try:
|
|
437
|
+
snapshot = _DirectorySnapshot(
|
|
438
|
+
self._scan(directory, root, base, include_hidden=include_hidden),
|
|
439
|
+
signature, monotonic(),
|
|
440
|
+
)
|
|
441
|
+
with self._cache_lock:
|
|
442
|
+
if self._pending.get(key) == (signature, future):
|
|
443
|
+
if self._cache_ttl:
|
|
444
|
+
self._cache[key] = snapshot
|
|
445
|
+
self._cache.move_to_end(key)
|
|
446
|
+
while len(self._cache) > self._cache_size:
|
|
447
|
+
self._cache.popitem(last=False)
|
|
448
|
+
del self._pending[key]
|
|
449
|
+
future.set_result(snapshot)
|
|
450
|
+
return snapshot
|
|
451
|
+
except BaseException as exc:
|
|
452
|
+
with self._cache_lock:
|
|
453
|
+
if self._pending.get(key) == (signature, future):
|
|
454
|
+
del self._pending[key]
|
|
455
|
+
future.set_exception(exc)
|
|
456
|
+
raise
|
|
457
|
+
|
|
341
458
|
def _scan(
|
|
342
459
|
self,
|
|
343
460
|
directory: Path,
|
|
344
461
|
root: Path,
|
|
345
462
|
base: PurePosixPath,
|
|
346
|
-
rules: frozenset[str],
|
|
347
463
|
*,
|
|
348
464
|
include_hidden: bool,
|
|
349
465
|
) -> list[DirectoryEntry]:
|
|
@@ -353,7 +469,7 @@ class FileBrowser:
|
|
|
353
469
|
for item in scan:
|
|
354
470
|
if not include_hidden and item.name.startswith("."):
|
|
355
471
|
continue
|
|
356
|
-
entry = self._describe(item, root, base,
|
|
472
|
+
entry = self._describe(item, root, base, directory=directory, include_hidden=include_hidden)
|
|
357
473
|
if entry is not None:
|
|
358
474
|
entries.append(entry)
|
|
359
475
|
except OSError as exc:
|
|
@@ -369,7 +485,9 @@ class FileBrowser:
|
|
|
369
485
|
self,
|
|
370
486
|
directory: Path,
|
|
371
487
|
entries: list[DirectoryEntry],
|
|
488
|
+
snapshot: _DirectorySnapshot,
|
|
372
489
|
*,
|
|
490
|
+
root: Path,
|
|
373
491
|
include_hidden: bool,
|
|
374
492
|
) -> list[DirectoryEntry]:
|
|
375
493
|
"""Fill in child counts, but only when the directory is small enough.
|
|
@@ -382,10 +500,23 @@ class FileBrowser:
|
|
|
382
500
|
directories = [entry for entry in entries if entry.kind is EntryKind.DIRECTORY]
|
|
383
501
|
if len(directories) > self._child_count_limit:
|
|
384
502
|
return entries
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
for entry in directories
|
|
388
|
-
|
|
503
|
+
# Only this directory's count work is serialized; unrelated scans stay concurrent.
|
|
504
|
+
with snapshot.count_lock:
|
|
505
|
+
for entry in directories:
|
|
506
|
+
if entry.path in snapshot.child_counts:
|
|
507
|
+
continue
|
|
508
|
+
count = None
|
|
509
|
+
try:
|
|
510
|
+
target = (directory / entry.name).resolve(strict=True)
|
|
511
|
+
boundary = directory if self._restrict_symlinks_to_directory else root
|
|
512
|
+
if target.is_relative_to(boundary) and (
|
|
513
|
+
include_hidden or not any(part.startswith(".") for part in target.relative_to(root).parts)
|
|
514
|
+
):
|
|
515
|
+
count = self._child_count(target, root=root, include_hidden=include_hidden)
|
|
516
|
+
except (OSError, RuntimeError):
|
|
517
|
+
pass
|
|
518
|
+
snapshot.child_counts[entry.path] = count
|
|
519
|
+
counted = dict(snapshot.child_counts)
|
|
389
520
|
return [
|
|
390
521
|
entry.model_copy(update={"child_count": counted[entry.path]})
|
|
391
522
|
if entry.path in counted
|
|
@@ -393,26 +524,89 @@ class FileBrowser:
|
|
|
393
524
|
for entry in entries
|
|
394
525
|
]
|
|
395
526
|
|
|
527
|
+
def _child_count(self, directory: Path, *, root: Path, include_hidden: bool) -> int | None:
|
|
528
|
+
"""Count matching children without retaining another directory snapshot."""
|
|
529
|
+
rules_by_kind = self._child_count_type_rules
|
|
530
|
+
count = 0
|
|
531
|
+
try:
|
|
532
|
+
with os.scandir(directory) as items:
|
|
533
|
+
for item in items:
|
|
534
|
+
try:
|
|
535
|
+
if not include_hidden and item.name.startswith("."):
|
|
536
|
+
continue
|
|
537
|
+
if _relative_parts(item.name) != (item.name,):
|
|
538
|
+
continue
|
|
539
|
+
lowered = item.name.lower()
|
|
540
|
+
if rules_by_kind is not None and not any(
|
|
541
|
+
lowered.endswith(rule) for rules in rules_by_kind.values() for rule in rules
|
|
542
|
+
):
|
|
543
|
+
continue
|
|
544
|
+
if item.is_symlink() or item.is_junction():
|
|
545
|
+
entry = self._describe(
|
|
546
|
+
item, root, PurePosixPath(), directory=directory, include_hidden=include_hidden,
|
|
547
|
+
)
|
|
548
|
+
if entry is None:
|
|
549
|
+
continue
|
|
550
|
+
kind = entry.kind
|
|
551
|
+
elif rules_by_kind is None:
|
|
552
|
+
count += 1
|
|
553
|
+
continue
|
|
554
|
+
elif item.is_dir(follow_symlinks=False):
|
|
555
|
+
kind = EntryKind.DIRECTORY
|
|
556
|
+
elif item.is_file(follow_symlinks=False):
|
|
557
|
+
kind = EntryKind.FILE
|
|
558
|
+
else:
|
|
559
|
+
continue
|
|
560
|
+
if rules_by_kind is None or any(
|
|
561
|
+
lowered.endswith(rule) for rule in rules_by_kind.get(kind, ())
|
|
562
|
+
):
|
|
563
|
+
count += 1
|
|
564
|
+
except (OSError, RuntimeError, ValueError):
|
|
565
|
+
continue
|
|
566
|
+
return count
|
|
567
|
+
except OSError:
|
|
568
|
+
return None
|
|
569
|
+
|
|
396
570
|
def _describe(
|
|
397
571
|
self,
|
|
398
572
|
item: os.DirEntry[str],
|
|
399
573
|
root: Path,
|
|
400
574
|
base: PurePosixPath,
|
|
401
|
-
|
|
575
|
+
*,
|
|
576
|
+
directory: Path,
|
|
577
|
+
include_hidden: bool,
|
|
402
578
|
) -> DirectoryEntry | None:
|
|
403
579
|
"""Build one entry, or None when it is unreadable or points outside."""
|
|
404
580
|
try:
|
|
581
|
+
if _relative_parts(item.name) != (item.name,):
|
|
582
|
+
return None
|
|
405
583
|
# A symlink out of the mount is not part of the mount. Drop it
|
|
406
584
|
# rather than listing something the caller could never open.
|
|
407
|
-
if item.is_symlink()
|
|
585
|
+
if item.is_symlink() or item.is_junction():
|
|
586
|
+
target = Path(item.path).resolve(strict=True)
|
|
587
|
+
boundary = directory if self._restrict_symlinks_to_directory else root
|
|
588
|
+
if not target.is_relative_to(boundary):
|
|
589
|
+
return None
|
|
590
|
+
target_parts = _relative_parts(target.relative_to(root).as_posix())
|
|
591
|
+
if not include_hidden and any(part.startswith(".") for part in target_parts):
|
|
592
|
+
return None
|
|
593
|
+
stat_result = target.stat(follow_symlinks=False)
|
|
594
|
+
else:
|
|
595
|
+
# Do not follow an entry that was replaced with a link after scandir.
|
|
596
|
+
stat_result = item.stat(follow_symlinks=False)
|
|
597
|
+
if hasattr(stat_result, "st_reparse_tag") and stat_result.st_reparse_tag in (
|
|
598
|
+
stat.IO_REPARSE_TAG_MOUNT_POINT, stat.IO_REPARSE_TAG_SYMLINK,
|
|
599
|
+
):
|
|
600
|
+
# A resolved target or ordinary entry may have become a junction meanwhile.
|
|
601
|
+
return None
|
|
602
|
+
is_dir = stat.S_ISDIR(stat_result.st_mode)
|
|
603
|
+
if not is_dir and not stat.S_ISREG(stat_result.st_mode):
|
|
408
604
|
return None
|
|
409
|
-
is_dir = item.is_dir()
|
|
410
|
-
stat_result = item.stat()
|
|
411
605
|
# Inside the guard: an out-of-range mtime raises OSError too, and
|
|
412
606
|
# one corrupt entry should be skipped like any other unreadable
|
|
413
607
|
# one rather than failing the whole directory.
|
|
414
608
|
modified_at = datetime.fromtimestamp(stat_result.st_mtime, tz=UTC)
|
|
415
|
-
except (OSError, ValueError, OverflowError):
|
|
609
|
+
except (OSError, RuntimeError, ValueError, OverflowError):
|
|
416
610
|
return None
|
|
417
611
|
|
|
418
612
|
kind = EntryKind.DIRECTORY if is_dir else EntryKind.FILE
|
|
@@ -426,7 +620,7 @@ class FileBrowser:
|
|
|
426
620
|
extension=extension,
|
|
427
621
|
# A directory is always reachable: type rules describe what you can
|
|
428
622
|
# pick, not where you can go.
|
|
429
|
-
matches_type_rules=
|
|
623
|
+
matches_type_rules=True,
|
|
430
624
|
child_count=None,
|
|
431
625
|
)
|
|
432
626
|
|