shakeout 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- shakeout-0.1.0/.github/workflows/ci.yml +120 -0
- shakeout-0.1.0/.github/workflows/docs.yml +47 -0
- shakeout-0.1.0/.github/workflows/release.yml +77 -0
- shakeout-0.1.0/.gitignore +12 -0
- shakeout-0.1.0/.pre-commit-config.yaml +43 -0
- shakeout-0.1.0/CHANGELOG.md +17 -0
- shakeout-0.1.0/LICENSE +21 -0
- shakeout-0.1.0/PKG-INFO +72 -0
- shakeout-0.1.0/README.md +44 -0
- shakeout-0.1.0/docs/assets/logo.svg +8 -0
- shakeout-0.1.0/docs/concepts/how-it-works.md +35 -0
- shakeout-0.1.0/docs/concepts/import-rewriting.md +28 -0
- shakeout-0.1.0/docs/concepts/index.md +7 -0
- shakeout-0.1.0/docs/concepts/side-effects.md +47 -0
- shakeout-0.1.0/docs/concepts/soundness.md +27 -0
- shakeout-0.1.0/docs/concepts/targets.md +31 -0
- shakeout-0.1.0/docs/getting-started/features.md +27 -0
- shakeout-0.1.0/docs/getting-started/first-bundle.md +79 -0
- shakeout-0.1.0/docs/getting-started/index.md +7 -0
- shakeout-0.1.0/docs/getting-started/installation.md +36 -0
- shakeout-0.1.0/docs/guides/aws-lambda.md +55 -0
- shakeout-0.1.0/docs/guides/cli-tools.md +32 -0
- shakeout-0.1.0/docs/guides/dependencies.md +38 -0
- shakeout-0.1.0/docs/guides/dynamic-code.md +48 -0
- shakeout-0.1.0/docs/guides/index.md +9 -0
- shakeout-0.1.0/docs/guides/tracebacks.md +53 -0
- shakeout-0.1.0/docs/index.md +74 -0
- shakeout-0.1.0/docs/project/contributing.md +32 -0
- shakeout-0.1.0/docs/project/design.md +430 -0
- shakeout-0.1.0/docs/reference/cli.md +57 -0
- shakeout-0.1.0/docs/reference/configuration.md +39 -0
- shakeout-0.1.0/docs/reference/diagnostics.md +26 -0
- shakeout-0.1.0/docs/reference/index.md +7 -0
- shakeout-0.1.0/docs/reference/pragmas.md +22 -0
- shakeout-0.1.0/docs/reference/python-api.md +25 -0
- shakeout-0.1.0/docs/stylesheets/extra.css +69 -0
- shakeout-0.1.0/mkdocs.yml +99 -0
- shakeout-0.1.0/pyproject.toml +194 -0
- shakeout-0.1.0/scripts/check.sh +30 -0
- shakeout-0.1.0/scripts/matrix.sh +7 -0
- shakeout-0.1.0/scripts/mutation.sh +19 -0
- shakeout-0.1.0/src/shakeout/__init__.py +3 -0
- shakeout-0.1.0/src/shakeout/__main__.py +5 -0
- shakeout-0.1.0/src/shakeout/_compat.py +19 -0
- shakeout-0.1.0/src/shakeout/analysis/__init__.py +1 -0
- shakeout-0.1.0/src/shakeout/analysis/classes.py +115 -0
- shakeout-0.1.0/src/shakeout/analysis/effects.py +168 -0
- shakeout-0.1.0/src/shakeout/analysis/registry.py +220 -0
- shakeout-0.1.0/src/shakeout/analysis/scopes.py +515 -0
- shakeout-0.1.0/src/shakeout/api.py +65 -0
- shakeout-0.1.0/src/shakeout/cli.py +250 -0
- shakeout-0.1.0/src/shakeout/config.py +88 -0
- shakeout-0.1.0/src/shakeout/emit/__init__.py +188 -0
- shakeout-0.1.0/src/shakeout/emit/archive.py +141 -0
- shakeout-0.1.0/src/shakeout/emit/deps.py +98 -0
- shakeout-0.1.0/src/shakeout/emit/minify.py +105 -0
- shakeout-0.1.0/src/shakeout/emit/single.py +63 -0
- shakeout-0.1.0/src/shakeout/emit/tree.py +59 -0
- shakeout-0.1.0/src/shakeout/graph.py +249 -0
- shakeout-0.1.0/src/shakeout/model.py +136 -0
- shakeout-0.1.0/src/shakeout/options.py +58 -0
- shakeout-0.1.0/src/shakeout/project.py +545 -0
- shakeout-0.1.0/src/shakeout/py.typed +0 -0
- shakeout-0.1.0/src/shakeout/report.py +48 -0
- shakeout-0.1.0/src/shakeout/resolve.py +135 -0
- shakeout-0.1.0/src/shakeout/result.py +185 -0
- shakeout-0.1.0/src/shakeout/runtime.py +234 -0
- shakeout-0.1.0/src/shakeout/trace.py +94 -0
- shakeout-0.1.0/src/shakeout/uvstage.py +95 -0
- shakeout-0.1.0/tests/__init__.py +0 -0
- shakeout-0.1.0/tests/cases/basics/annotations.case +20 -0
- shakeout-0.1.0/tests/cases/basics/annotations.golden +9 -0
- shakeout-0.1.0/tests/cases/basics/argv_and_exit.case +9 -0
- shakeout-0.1.0/tests/cases/basics/argv_and_exit.golden +5 -0
- shakeout-0.1.0/tests/cases/basics/closure_globals.case +20 -0
- shakeout-0.1.0/tests/cases/basics/closure_globals.golden +8 -0
- shakeout-0.1.0/tests/cases/basics/comments_preserved.case +15 -0
- shakeout-0.1.0/tests/cases/basics/comments_preserved.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/default_args.case +15 -0
- shakeout-0.1.0/tests/cases/basics/default_args.golden +7 -0
- shakeout-0.1.0/tests/cases/basics/docstring_entry.case +9 -0
- shakeout-0.1.0/tests/cases/basics/docstring_entry.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/empty_entry.case +3 -0
- shakeout-0.1.0/tests/cases/basics/empty_entry.golden +1 -0
- shakeout-0.1.0/tests/cases/basics/future_annotations.case +19 -0
- shakeout-0.1.0/tests/cases/basics/future_annotations.golden +9 -0
- shakeout-0.1.0/tests/cases/basics/main_guard_entry.case +10 -0
- shakeout-0.1.0/tests/cases/basics/main_guard_entry.golden +3 -0
- shakeout-0.1.0/tests/cases/basics/main_guard_library.case +20 -0
- shakeout-0.1.0/tests/cases/basics/main_guard_library.golden +7 -0
- shakeout-0.1.0/tests/cases/basics/recursion.case +27 -0
- shakeout-0.1.0/tests/cases/basics/recursion.golden +9 -0
- shakeout-0.1.0/tests/cases/basics/redefinition.case +14 -0
- shakeout-0.1.0/tests/cases/basics/redefinition.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/same_line_statements.case +11 -0
- shakeout-0.1.0/tests/cases/basics/same_line_statements.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/transitive.case +27 -0
- shakeout-0.1.0/tests/cases/basics/transitive.golden +9 -0
- shakeout-0.1.0/tests/cases/basics/unused_class.case +17 -0
- shakeout-0.1.0/tests/cases/basics/unused_class.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/unused_constants.case +14 -0
- shakeout-0.1.0/tests/cases/basics/unused_constants.golden +9 -0
- shakeout-0.1.0/tests/cases/basics/unused_function.case +15 -0
- shakeout-0.1.0/tests/cases/basics/unused_function.golden +6 -0
- shakeout-0.1.0/tests/cases/basics/unused_in_entry.case +17 -0
- shakeout-0.1.0/tests/cases/basics/unused_in_entry.golden +5 -0
- shakeout-0.1.0/tests/cases/classes/assume_pure_bases_option.case +16 -0
- shakeout-0.1.0/tests/cases/classes/assume_pure_bases_option.golden +8 -0
- shakeout-0.1.0/tests/cases/classes/base_from_unknown_external.case +11 -0
- shakeout-0.1.0/tests/cases/classes/base_from_unknown_external.golden +5 -0
- shakeout-0.1.0/tests/cases/classes/class_attribute_reference.case +15 -0
- shakeout-0.1.0/tests/cases/classes/class_attribute_reference.golden +7 -0
- shakeout-0.1.0/tests/cases/classes/class_body_side_effect.case +10 -0
- shakeout-0.1.0/tests/cases/classes/class_body_side_effect.golden +5 -0
- shakeout-0.1.0/tests/cases/classes/class_decorator_registry.case +19 -0
- shakeout-0.1.0/tests/cases/classes/class_decorator_registry.golden +7 -0
- shakeout-0.1.0/tests/cases/classes/class_keyword_args.case +18 -0
- shakeout-0.1.0/tests/cases/classes/class_keyword_args.golden +7 -0
- shakeout-0.1.0/tests/cases/classes/class_scope_not_visible_in_methods.case +16 -0
- shakeout-0.1.0/tests/cases/classes/class_scope_not_visible_in_methods.golden +6 -0
- shakeout-0.1.0/tests/cases/classes/dataclass_fields.case +21 -0
- shakeout-0.1.0/tests/cases/classes/dataclass_fields.golden +8 -0
- shakeout-0.1.0/tests/cases/classes/init_subclass_inherited_two_levels.case +24 -0
- shakeout-0.1.0/tests/cases/classes/init_subclass_inherited_two_levels.golden +10 -0
- shakeout-0.1.0/tests/cases/classes/init_subclass_registry.case +25 -0
- shakeout-0.1.0/tests/cases/classes/init_subclass_registry.golden +10 -0
- shakeout-0.1.0/tests/cases/classes/metaclass_registry.case +31 -0
- shakeout-0.1.0/tests/cases/classes/metaclass_registry.golden +11 -0
- shakeout-0.1.0/tests/cases/classes/method_references.case +22 -0
- shakeout-0.1.0/tests/cases/classes/method_references.golden +7 -0
- shakeout-0.1.0/tests/cases/classes/plain_inheritance_removed.case +24 -0
- shakeout-0.1.0/tests/cases/classes/plain_inheritance_removed.golden +8 -0
- shakeout-0.1.0/tests/cases/classes/stdlib_bases_removed.case +39 -0
- shakeout-0.1.0/tests/cases/classes/stdlib_bases_removed.golden +15 -0
- shakeout-0.1.0/tests/cases/dynamic/dunder_import_constant.case +8 -0
- shakeout-0.1.0/tests/cases/dynamic/dunder_import_constant.golden +5 -0
- shakeout-0.1.0/tests/cases/dynamic/eval_exec.case +14 -0
- shakeout-0.1.0/tests/cases/dynamic/eval_exec.golden +7 -0
- shakeout-0.1.0/tests/cases/dynamic/getattr_on_module.case +17 -0
- shakeout-0.1.0/tests/cases/dynamic/getattr_on_module.golden +8 -0
- shakeout-0.1.0/tests/cases/dynamic/globals_lookup.case +21 -0
- shakeout-0.1.0/tests/cases/dynamic/globals_lookup.golden +10 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_computed_with_keep.case +13 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_computed_with_keep.golden +8 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_constant.case +17 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_constant.golden +9 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_fully_computed.case +16 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_fully_computed.golden +6 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_prefix_glob.case +17 -0
- shakeout-0.1.0/tests/cases/dynamic/import_module_prefix_glob.golden +11 -0
- shakeout-0.1.0/tests/cases/dynamic/keep_symbol_option.case +16 -0
- shakeout-0.1.0/tests/cases/dynamic/keep_symbol_option.golden +6 -0
- shakeout-0.1.0/tests/cases/dynamic/module_passed_as_value.case +19 -0
- shakeout-0.1.0/tests/cases/dynamic/module_passed_as_value.golden +7 -0
- shakeout-0.1.0/tests/cases/dynamic/star_import.case +14 -0
- shakeout-0.1.0/tests/cases/dynamic/star_import.golden +6 -0
- shakeout-0.1.0/tests/cases/dynamic/star_import_respects_all.case +16 -0
- shakeout-0.1.0/tests/cases/dynamic/star_import_respects_all.golden +7 -0
- shakeout-0.1.0/tests/cases/dynamic/sys_modules_self.case +21 -0
- shakeout-0.1.0/tests/cases/dynamic/sys_modules_self.golden +10 -0
- shakeout-0.1.0/tests/cases/dynamic/vars_module.case +11 -0
- shakeout-0.1.0/tests/cases/dynamic/vars_module.golden +8 -0
- shakeout-0.1.0/tests/cases/effects/augmented_assignment.case +9 -0
- shakeout-0.1.0/tests/cases/effects/augmented_assignment.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/conditional_definitions.case +15 -0
- shakeout-0.1.0/tests/cases/effects/conditional_definitions.golden +7 -0
- shakeout-0.1.0/tests/cases/effects/decorator_registry.case +28 -0
- shakeout-0.1.0/tests/cases/effects/decorator_registry.golden +12 -0
- shakeout-0.1.0/tests/cases/effects/decorator_with_args_registry.case +27 -0
- shakeout-0.1.0/tests/cases/effects/decorator_with_args_registry.golden +8 -0
- shakeout-0.1.0/tests/cases/effects/global_created_by_function.case +12 -0
- shakeout-0.1.0/tests/cases/effects/global_created_by_function.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/global_written_by_function.case +15 -0
- shakeout-0.1.0/tests/cases/effects/global_written_by_function.golden +7 -0
- shakeout-0.1.0/tests/cases/effects/impure_initializer_kept.case +14 -0
- shakeout-0.1.0/tests/cases/effects/impure_initializer_kept.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/keep_pragma.case +10 -0
- shakeout-0.1.0/tests/cases/effects/keep_pragma.golden +5 -0
- shakeout-0.1.0/tests/cases/effects/module_mutates_other_module.case +13 -0
- shakeout-0.1.0/tests/cases/effects/module_mutates_other_module.golden +9 -0
- shakeout-0.1.0/tests/cases/effects/mutation_of_global_container.case +10 -0
- shakeout-0.1.0/tests/cases/effects/mutation_of_global_container.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/pure_call_initializers.case +19 -0
- shakeout-0.1.0/tests/cases/effects/pure_call_initializers.golden +15 -0
- shakeout-0.1.0/tests/cases/effects/pure_call_option.case +14 -0
- shakeout-0.1.0/tests/cases/effects/pure_call_option.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/pure_decorators_removed.case +36 -0
- shakeout-0.1.0/tests/cases/effects/pure_decorators_removed.golden +13 -0
- shakeout-0.1.0/tests/cases/effects/pure_pragma.case +13 -0
- shakeout-0.1.0/tests/cases/effects/pure_pragma.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/sorted_with_callback_is_impure.case +14 -0
- shakeout-0.1.0/tests/cases/effects/sorted_with_callback_is_impure.golden +6 -0
- shakeout-0.1.0/tests/cases/effects/stdlib_registration.case +15 -0
- shakeout-0.1.0/tests/cases/effects/stdlib_registration.golden +8 -0
- shakeout-0.1.0/tests/cases/effects/third_party_import_is_effect.case +12 -0
- shakeout-0.1.0/tests/cases/effects/third_party_import_is_effect.golden +6 -0
- shakeout-0.1.0/tests/cases/errors/chained_exceptions.case +16 -0
- shakeout-0.1.0/tests/cases/errors/chained_exceptions.golden +6 -0
- shakeout-0.1.0/tests/cases/errors/error_at_import_time.case +11 -0
- shakeout-0.1.0/tests/cases/errors/error_at_import_time.golden +6 -0
- shakeout-0.1.0/tests/cases/errors/format_exc_has_source.case +15 -0
- shakeout-0.1.0/tests/cases/errors/format_exc_has_source.golden +7 -0
- shakeout-0.1.0/tests/cases/errors/inspect_getsource.case +15 -0
- shakeout-0.1.0/tests/cases/errors/inspect_getsource.golden +8 -0
- shakeout-0.1.0/tests/cases/errors/removed_name_imported_by_generated_code.case +20 -0
- shakeout-0.1.0/tests/cases/errors/removed_name_imported_by_generated_code.golden +8 -0
- shakeout-0.1.0/tests/cases/errors/removed_symbol_attribute_error.case +22 -0
- shakeout-0.1.0/tests/cases/errors/removed_symbol_attribute_error.golden +9 -0
- shakeout-0.1.0/tests/cases/errors/traceback_through_modules.case +33 -0
- shakeout-0.1.0/tests/cases/errors/traceback_through_modules.golden +14 -0
- shakeout-0.1.0/tests/cases/errors/warnings_location.case +19 -0
- shakeout-0.1.0/tests/cases/errors/warnings_location.golden +10 -0
- shakeout-0.1.0/tests/cases/fuzz/kept_import_requires_names.case +43 -0
- shakeout-0.1.0/tests/cases/fuzz/kept_import_requires_names.golden +16 -0
- shakeout-0.1.0/tests/cases/fuzz/whole_module_imports.case +74 -0
- shakeout-0.1.0/tests/cases/fuzz/whole_module_imports.golden +21 -0
- shakeout-0.1.0/tests/cases/imports/alias.case +20 -0
- shakeout-0.1.0/tests/cases/imports/alias.golden +8 -0
- shakeout-0.1.0/tests/cases/imports/attribute_access.case +15 -0
- shakeout-0.1.0/tests/cases/imports/attribute_access.golden +6 -0
- shakeout-0.1.0/tests/cases/imports/circular.case +28 -0
- shakeout-0.1.0/tests/cases/imports/circular.golden +11 -0
- shakeout-0.1.0/tests/cases/imports/circular_from_import.case +18 -0
- shakeout-0.1.0/tests/cases/imports/circular_from_import.golden +10 -0
- shakeout-0.1.0/tests/cases/imports/dotted_import.case +17 -0
- shakeout-0.1.0/tests/cases/imports/dotted_import.golden +8 -0
- shakeout-0.1.0/tests/cases/imports/dotted_import_as.case +16 -0
- shakeout-0.1.0/tests/cases/imports/dotted_import_as.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/entry_imported_by_name.case +6 -0
- shakeout-0.1.0/tests/cases/imports/entry_imported_by_name.golden +6 -0
- shakeout-0.1.0/tests/cases/imports/from_package_import_submodule.case +16 -0
- shakeout-0.1.0/tests/cases/imports/from_package_import_submodule.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/import_unused_but_module_needed_elsewhere.case +17 -0
- shakeout-0.1.0/tests/cases/imports/import_unused_but_module_needed_elsewhere.golden +9 -0
- shakeout-0.1.0/tests/cases/imports/local_import.case +25 -0
- shakeout-0.1.0/tests/cases/imports/local_import.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/local_import_shadows_global.case +19 -0
- shakeout-0.1.0/tests/cases/imports/local_import_shadows_global.golden +6 -0
- shakeout-0.1.0/tests/cases/imports/missing_external_dependency.case +6 -0
- shakeout-0.1.0/tests/cases/imports/missing_external_dependency.golden +4 -0
- shakeout-0.1.0/tests/cases/imports/reexport_chain.case +24 -0
- shakeout-0.1.0/tests/cases/imports/reexport_chain.golden +13 -0
- shakeout-0.1.0/tests/cases/imports/relative_levels.case +24 -0
- shakeout-0.1.0/tests/cases/imports/relative_levels.golden +17 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_all_aliases_unused.case +20 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_all_aliases_unused.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_relative_and_submodule.case +22 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_relative_and_submodule.golden +12 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_unused_aliases.case +18 -0
- shakeout-0.1.0/tests/cases/imports/rewrite_unused_aliases.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/same_name_different_packages.case +25 -0
- shakeout-0.1.0/tests/cases/imports/same_name_different_packages.golden +12 -0
- shakeout-0.1.0/tests/cases/imports/side_effect_module_kept.case +13 -0
- shakeout-0.1.0/tests/cases/imports/side_effect_module_kept.golden +6 -0
- shakeout-0.1.0/tests/cases/imports/stdlib_external.case +18 -0
- shakeout-0.1.0/tests/cases/imports/stdlib_external.golden +9 -0
- shakeout-0.1.0/tests/cases/imports/submodule_attribute_after_import.case +12 -0
- shakeout-0.1.0/tests/cases/imports/submodule_attribute_after_import.golden +7 -0
- shakeout-0.1.0/tests/cases/imports/try_except_import.case +12 -0
- shakeout-0.1.0/tests/cases/imports/try_except_import.golden +6 -0
- shakeout-0.1.0/tests/cases/imports/type_checking_block.case +21 -0
- shakeout-0.1.0/tests/cases/imports/type_checking_block.golden +9 -0
- shakeout-0.1.0/tests/cases/imports/unused_import_pure_module_dropped.case +11 -0
- shakeout-0.1.0/tests/cases/imports/unused_import_pure_module_dropped.golden +5 -0
- shakeout-0.1.0/tests/cases/packages/data_file_via_file.case +22 -0
- shakeout-0.1.0/tests/cases/packages/data_file_via_file.golden +9 -0
- shakeout-0.1.0/tests/cases/packages/dunder_all_kept.case +14 -0
- shakeout-0.1.0/tests/cases/packages/dunder_all_kept.golden +7 -0
- shakeout-0.1.0/tests/cases/packages/init_side_effects_order.case +14 -0
- shakeout-0.1.0/tests/cases/packages/init_side_effects_order.golden +8 -0
- shakeout-0.1.0/tests/cases/packages/module_getattr.case +10 -0
- shakeout-0.1.0/tests/cases/packages/module_getattr.golden +5 -0
- shakeout-0.1.0/tests/cases/packages/name_and_file.case +12 -0
- shakeout-0.1.0/tests/cases/packages/name_and_file.golden +10 -0
- shakeout-0.1.0/tests/cases/packages/namespace_package.case +15 -0
- shakeout-0.1.0/tests/cases/packages/namespace_package.golden +7 -0
- shakeout-0.1.0/tests/cases/packages/pickle_roundtrip.case +15 -0
- shakeout-0.1.0/tests/cases/packages/pickle_roundtrip.golden +8 -0
- shakeout-0.1.0/tests/cases/packages/pure_parent_kept_for_child.case +14 -0
- shakeout-0.1.0/tests/cases/packages/pure_parent_kept_for_child.golden +7 -0
- shakeout-0.1.0/tests/cases/scoping/class_in_function.case +17 -0
- shakeout-0.1.0/tests/cases/scoping/class_in_function.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/comprehension_first_iter_is_outer.case +13 -0
- shakeout-0.1.0/tests/cases/scoping/comprehension_first_iter_is_outer.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/comprehension_target_vs_global.case +14 -0
- shakeout-0.1.0/tests/cases/scoping/comprehension_target_vs_global.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/del_local.case +15 -0
- shakeout-0.1.0/tests/cases/scoping/del_local.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/except_as_and_with_as.case +20 -0
- shakeout-0.1.0/tests/cases/scoping/except_as_and_with_as.golden +9 -0
- shakeout-0.1.0/tests/cases/scoping/global_in_nested_function.case +19 -0
- shakeout-0.1.0/tests/cases/scoping/global_in_nested_function.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/global_read_after_local_branch.case +13 -0
- shakeout-0.1.0/tests/cases/scoping/global_read_after_local_branch.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/local_shadows_global.case +15 -0
- shakeout-0.1.0/tests/cases/scoping/local_shadows_global.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/nonlocal.case +22 -0
- shakeout-0.1.0/tests/cases/scoping/nonlocal.golden +8 -0
- shakeout-0.1.0/tests/cases/scoping/param_shadows_global.case +13 -0
- shakeout-0.1.0/tests/cases/scoping/param_shadows_global.golden +6 -0
- shakeout-0.1.0/tests/cases/scoping/walrus_in_comprehension.case +14 -0
- shakeout-0.1.0/tests/cases/scoping/walrus_in_comprehension.golden +6 -0
- shakeout-0.1.0/tests/cases/syntax/async_code.case +22 -0
- shakeout-0.1.0/tests/cases/syntax/async_code.golden +9 -0
- shakeout-0.1.0/tests/cases/syntax/crlf_source.case +22 -0
- shakeout-0.1.0/tests/cases/syntax/crlf_source.golden +7 -0
- shakeout-0.1.0/tests/cases/syntax/decorated_with_lambda_default.case +13 -0
- shakeout-0.1.0/tests/cases/syntax/decorated_with_lambda_default.golden +8 -0
- shakeout-0.1.0/tests/cases/syntax/encoding_and_unicode.case +15 -0
- shakeout-0.1.0/tests/cases/syntax/encoding_and_unicode.golden +6 -0
- shakeout-0.1.0/tests/cases/syntax/except_star.case +13 -0
- shakeout-0.1.0/tests/cases/syntax/except_star.golden +5 -0
- shakeout-0.1.0/tests/cases/syntax/form_feed_line_numbers.case +15 -0
- shakeout-0.1.0/tests/cases/syntax/form_feed_line_numbers.golden +6 -0
- shakeout-0.1.0/tests/cases/syntax/fstring_nested.case +15 -0
- shakeout-0.1.0/tests/cases/syntax/fstring_nested.golden +7 -0
- shakeout-0.1.0/tests/cases/syntax/match_statement.case +21 -0
- shakeout-0.1.0/tests/cases/syntax/match_statement.golden +7 -0
- shakeout-0.1.0/tests/cases/syntax/pep695_generics.case +22 -0
- shakeout-0.1.0/tests/cases/syntax/pep695_generics.golden +9 -0
- shakeout-0.1.0/tests/cases/syntax/template_strings.case +14 -0
- shakeout-0.1.0/tests/cases/syntax/template_strings.golden +6 -0
- shakeout-0.1.0/tests/conftest.py +17 -0
- shakeout-0.1.0/tests/corpus/drivers/attrs_driver.py +21 -0
- shakeout-0.1.0/tests/corpus/drivers/click_driver.py +21 -0
- shakeout-0.1.0/tests/corpus/drivers/dateutil_driver.py +7 -0
- shakeout-0.1.0/tests/corpus/drivers/idna_driver.py +8 -0
- shakeout-0.1.0/tests/corpus/drivers/jinja_driver.py +8 -0
- shakeout-0.1.0/tests/corpus/drivers/packaging_driver.py +8 -0
- shakeout-0.1.0/tests/corpus/drivers/rich_driver.py +12 -0
- shakeout-0.1.0/tests/corpus/drivers/six_driver.py +4 -0
- shakeout-0.1.0/tests/corpus/drivers/tomli_driver.py +8 -0
- shakeout-0.1.0/tests/corpus/metrics.json +74 -0
- shakeout-0.1.0/tests/corpus/overrides.toml +6 -0
- shakeout-0.1.0/tests/corpus/requirements.txt +15 -0
- shakeout-0.1.0/tests/corpus/test_corpus.py +128 -0
- shakeout-0.1.0/tests/fuzz/__init__.py +0 -0
- shakeout-0.1.0/tests/fuzz/generator.py +207 -0
- shakeout-0.1.0/tests/fuzz/test_fuzz.py +77 -0
- shakeout-0.1.0/tests/harness/__init__.py +0 -0
- shakeout-0.1.0/tests/harness/cases.py +171 -0
- shakeout-0.1.0/tests/harness/project.py +22 -0
- shakeout-0.1.0/tests/harness/run.py +69 -0
- shakeout-0.1.0/tests/harness/soundness.py +82 -0
- shakeout-0.1.0/tests/harness/tracebacks.py +85 -0
- shakeout-0.1.0/tests/harness/tracer_main.py +107 -0
- shakeout-0.1.0/tests/test_cases.py +194 -0
- shakeout-0.1.0/tests/test_cli.py +103 -0
- shakeout-0.1.0/tests/test_docs.py +61 -0
- shakeout-0.1.0/tests/test_features.py +356 -0
- shakeout-0.1.0/tests/test_harness.py +197 -0
- shakeout-0.1.0/tests/test_self_hosting.py +86 -0
- shakeout-0.1.0/tests/unit/__init__.py +0 -0
- shakeout-0.1.0/tests/unit/helpers.py +25 -0
- shakeout-0.1.0/tests/unit/test_classes.py +89 -0
- shakeout-0.1.0/tests/unit/test_config_and_cli.py +192 -0
- shakeout-0.1.0/tests/unit/test_effects.py +180 -0
- shakeout-0.1.0/tests/unit/test_emit_properties.py +143 -0
- shakeout-0.1.0/tests/unit/test_minify.py +132 -0
- shakeout-0.1.0/tests/unit/test_runtime.py +183 -0
- shakeout-0.1.0/tests/unit/test_scopes.py +130 -0
- shakeout-0.1.0/tests/unit/test_scopes_symtable.py +136 -0
- shakeout-0.1.0/uv.lock +3244 -0
- shakeout-0.1.0/vulture_whitelist.py +39 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
- cron: "17 4 * * *" # nightly tier
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
lint:
|
|
20
|
+
name: lint and types
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 15
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
28
|
+
- run: uv lock --check
|
|
29
|
+
- run: uv sync --locked --python 3.14
|
|
30
|
+
- run: uv run ruff format --check .
|
|
31
|
+
- run: uv run ruff check --output-format=github .
|
|
32
|
+
- run: uv run pyright
|
|
33
|
+
- run: uv run mypy
|
|
34
|
+
- run: uv run codespell src tests/*.py tests/harness tests/unit tests/fuzz/*.py docs README.md
|
|
35
|
+
- run: uv run vulture src vulture_whitelist.py --min-confidence 60
|
|
36
|
+
- run: uv run validate-pyproject pyproject.toml
|
|
37
|
+
- run: uv run actionlint
|
|
38
|
+
- run: uv run zizmor --persona=pedantic --quiet .github/workflows
|
|
39
|
+
|
|
40
|
+
test:
|
|
41
|
+
name: test (${{ matrix.os }}, ${{ matrix.python }})
|
|
42
|
+
runs-on: ${{ matrix.os }}
|
|
43
|
+
timeout-minutes: 30
|
|
44
|
+
strategy:
|
|
45
|
+
fail-fast: false
|
|
46
|
+
matrix:
|
|
47
|
+
os: [ubuntu-latest, macos-latest]
|
|
48
|
+
python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
51
|
+
with:
|
|
52
|
+
persist-credentials: false
|
|
53
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
54
|
+
- run: uv sync --locked --python "$PYTHON"
|
|
55
|
+
env:
|
|
56
|
+
PYTHON: ${{ matrix.python }}
|
|
57
|
+
- name: tests (branch coverage gate on one interpreter)
|
|
58
|
+
env:
|
|
59
|
+
PYTHON: ${{ matrix.python }}
|
|
60
|
+
OS: ${{ matrix.os }}
|
|
61
|
+
run: |
|
|
62
|
+
if [ "$PYTHON" = "3.14" ] && [ "$OS" = "ubuntu-latest" ]; then
|
|
63
|
+
uv run pytest -n auto --cov --cov-report=term-missing:skip-covered
|
|
64
|
+
else
|
|
65
|
+
uv run pytest -n auto
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
package:
|
|
69
|
+
name: package and docs
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
timeout-minutes: 15
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
74
|
+
with:
|
|
75
|
+
persist-credentials: false
|
|
76
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
77
|
+
- run: uv sync --locked --python 3.14 --group docs
|
|
78
|
+
- run: uv run mkdocs build --strict --site-dir build/site
|
|
79
|
+
- run: uv build
|
|
80
|
+
- run: uv run twine check --strict dist/*
|
|
81
|
+
- run: uv run check-wheel-contents dist/*.whl
|
|
82
|
+
- run: uv run --isolated --no-project --with dist/*.whl shakeout --version
|
|
83
|
+
|
|
84
|
+
audit:
|
|
85
|
+
name: dependency audit
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
timeout-minutes: 10
|
|
88
|
+
steps:
|
|
89
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
90
|
+
with:
|
|
91
|
+
persist-credentials: false
|
|
92
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
93
|
+
- run: uv sync --locked --python 3.14
|
|
94
|
+
- run: uv export --frozen --all-groups --no-hashes --no-emit-project -o requirements.txt
|
|
95
|
+
- run: uv run pip-audit --strict --progress-spinner off -r requirements.txt
|
|
96
|
+
|
|
97
|
+
nightly:
|
|
98
|
+
name: nightly (corpus, long fuzz, mutation)
|
|
99
|
+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
timeout-minutes: 120
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
104
|
+
with:
|
|
105
|
+
persist-credentials: false
|
|
106
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
107
|
+
- run: uv sync --locked --python 3.14
|
|
108
|
+
- name: corpus and 1500-program fuzz campaign
|
|
109
|
+
run: uv run pytest -n auto -m nightly
|
|
110
|
+
- name: mutation testing of the analysis core
|
|
111
|
+
run: scripts/mutation.sh
|
|
112
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
113
|
+
if: always()
|
|
114
|
+
with:
|
|
115
|
+
name: nightly-results
|
|
116
|
+
path: |
|
|
117
|
+
tests/corpus/metrics.json
|
|
118
|
+
tests/fuzz/failures/
|
|
119
|
+
mutants/
|
|
120
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths: ["docs/**", "mkdocs.yml", ".github/workflows/docs.yml"]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions: {}
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: pages
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
name: build site
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 10
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: false
|
|
29
|
+
- run: uv run --locked --group docs mkdocs build --strict --site-dir build/site
|
|
30
|
+
- uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
|
|
31
|
+
with:
|
|
32
|
+
path: build/site
|
|
33
|
+
|
|
34
|
+
deploy:
|
|
35
|
+
name: deploy to GitHub Pages
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 10
|
|
39
|
+
environment:
|
|
40
|
+
name: github-pages
|
|
41
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
42
|
+
permissions:
|
|
43
|
+
pages: write # publish the site
|
|
44
|
+
id-token: write # prove the deployment's origin to Pages
|
|
45
|
+
steps:
|
|
46
|
+
- id: deployment
|
|
47
|
+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Push a tag like v0.1.0 to publish. PyPI trusted publishing (OIDC): no API tokens are stored.
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
tags: ["v*"]
|
|
7
|
+
|
|
8
|
+
permissions: {}
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false # never interrupt a publish
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build:
|
|
16
|
+
name: build distributions
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 10
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
with:
|
|
24
|
+
persist-credentials: false
|
|
25
|
+
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: false # never restore caches into a release build
|
|
28
|
+
- name: tag matches the package version
|
|
29
|
+
run: |
|
|
30
|
+
version=$(uv version --short)
|
|
31
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
32
|
+
echo "tag $GITHUB_REF_NAME does not match version $version" >&2
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
- run: uv build
|
|
36
|
+
- run: uvx twine check --strict dist/*
|
|
37
|
+
- run: uvx check-wheel-contents dist/*.whl
|
|
38
|
+
- run: uv run --isolated --no-project --with dist/*.whl shakeout --version
|
|
39
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
if-no-files-found: error
|
|
44
|
+
|
|
45
|
+
pypi:
|
|
46
|
+
name: publish to PyPI
|
|
47
|
+
needs: build
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 10
|
|
50
|
+
environment:
|
|
51
|
+
name: pypi
|
|
52
|
+
url: https://pypi.org/project/shakeout/
|
|
53
|
+
permissions:
|
|
54
|
+
id-token: write # trusted publishing
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
57
|
+
with:
|
|
58
|
+
name: dist
|
|
59
|
+
path: dist/
|
|
60
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
61
|
+
|
|
62
|
+
github-release:
|
|
63
|
+
name: GitHub release
|
|
64
|
+
needs: pypi
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
timeout-minutes: 10
|
|
67
|
+
permissions:
|
|
68
|
+
contents: write # create the GitHub release
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
71
|
+
with:
|
|
72
|
+
name: dist
|
|
73
|
+
path: dist/
|
|
74
|
+
- name: create release with the distributions attached
|
|
75
|
+
env:
|
|
76
|
+
GH_TOKEN: ${{ github.token }}
|
|
77
|
+
run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --generate-notes --verify-tag
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Uses the tool versions pinned in uv.lock. Install: uv run pre-commit install
|
|
2
|
+
repos:
|
|
3
|
+
- repo: local
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-format
|
|
6
|
+
name: ruff format
|
|
7
|
+
entry: uv run ruff format
|
|
8
|
+
language: system
|
|
9
|
+
types_or: [python, pyi]
|
|
10
|
+
- id: ruff
|
|
11
|
+
name: ruff check
|
|
12
|
+
entry: uv run ruff check --fix
|
|
13
|
+
language: system
|
|
14
|
+
types_or: [python, pyi]
|
|
15
|
+
- id: pyright
|
|
16
|
+
name: pyright
|
|
17
|
+
entry: uv run pyright
|
|
18
|
+
language: system
|
|
19
|
+
pass_filenames: false
|
|
20
|
+
types: [python]
|
|
21
|
+
- id: mypy
|
|
22
|
+
name: mypy
|
|
23
|
+
entry: uv run mypy
|
|
24
|
+
language: system
|
|
25
|
+
pass_filenames: false
|
|
26
|
+
types: [python]
|
|
27
|
+
- id: codespell
|
|
28
|
+
name: codespell
|
|
29
|
+
entry: uv run codespell
|
|
30
|
+
language: system
|
|
31
|
+
types_or: [python, markdown]
|
|
32
|
+
exclude: ^tests/cases/
|
|
33
|
+
- id: uv-lock
|
|
34
|
+
name: uv lock --check
|
|
35
|
+
entry: uv lock --check
|
|
36
|
+
language: system
|
|
37
|
+
pass_filenames: false
|
|
38
|
+
files: ^(pyproject\.toml|uv\.lock)$
|
|
39
|
+
- id: actionlint
|
|
40
|
+
name: actionlint
|
|
41
|
+
entry: uv run actionlint
|
|
42
|
+
language: system
|
|
43
|
+
files: ^\.github/workflows/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-11-08)
|
|
4
|
+
|
|
5
|
+
First release.
|
|
6
|
+
|
|
7
|
+
- Static, sound tree shaking of first-party code (and opted-in third-party packages) across
|
|
8
|
+
module boundaries, with esbuild-style rewriting of kept imports.
|
|
9
|
+
- Targets: `single` (one runnable or importable `.py`), `pyz` (zipapp), `tree`, and `lambda`
|
|
10
|
+
(reproducible AWS Lambda zip with `--export` handlers).
|
|
11
|
+
- Exact tracebacks from bundles: original file names, line numbers and source lines; runtime notes
|
|
12
|
+
that name the `keep` setting when removed code is reached; `shakeout trace`.
|
|
13
|
+
- Dependencies: vendoring of unshaken distributions, `include` to shake them, `--uv` staging from
|
|
14
|
+
`uv.lock` for a target Python and platform.
|
|
15
|
+
- `--minify` with exact line numbers, `--strip-docstrings`.
|
|
16
|
+
- CLI: `build`, `report`, `why`, `trace`; configuration in `[tool.shakeout]`.
|
|
17
|
+
- CPython 3.10 to 3.15.
|
shakeout-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mischa Spiegelmock
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
shakeout-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: shakeout
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Static tree-shaking bundler for Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/revmischa/shakeout
|
|
6
|
+
Project-URL: Documentation, https://revmischa.github.io/shakeout/
|
|
7
|
+
Project-URL: Repository, https://github.com/revmischa/shakeout
|
|
8
|
+
Project-URL: Issues, https://github.com/revmischa/shakeout/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/revmischa/shakeout/blob/main/CHANGELOG.md
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: aws-lambda,bundler,dead-code,tree-shaking,zipapp
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: tomli>=2; python_version < '3.11'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# shakeout
|
|
30
|
+
|
|
31
|
+
A static, tree-shaking bundler for Python. Give it an entry script: it finds every module the
|
|
32
|
+
program can import, removes top-level code nothing can reach, and writes a self-contained
|
|
33
|
+
artifact. That can be one runnable `.py`, a zipapp, a pruned directory or a reproducible AWS
|
|
34
|
+
Lambda zip. Tracebacks from bundles name the original files and lines.
|
|
35
|
+
|
|
36
|
+
```console
|
|
37
|
+
$ uvx shakeout build app/main.py -o dist # one runnable file
|
|
38
|
+
$ uvx shakeout build handler.py -t lambda --export handler # Lambda package
|
|
39
|
+
$ uvx shakeout report app/main.py # what is kept, and why
|
|
40
|
+
$ uvx shakeout why app/main.py pkg.core:helper # the chain that keeps a symbol
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Documentation:** https://revmischa.github.io/shakeout/ (sources in [`docs/`](docs/); the
|
|
44
|
+
design and roadmap are in [docs/project/design.md](docs/project/design.md)).
|
|
45
|
+
|
|
46
|
+
## Guarantees
|
|
47
|
+
|
|
48
|
+
- **Sound by default.** Anything the analysis can't prove unused is kept: registration
|
|
49
|
+
decorators, `__init_subclass__` and metaclass registries, modules that use `globals()`, `eval`
|
|
50
|
+
or `sys.modules`, module objects passed around as values, star imports.
|
|
51
|
+
- **Exact tracebacks without source maps.** Modules keep their original file names and line
|
|
52
|
+
numbers, and their sources are registered with `linecache`. Tracebacks, `warnings`,
|
|
53
|
+
`inspect.getsource` and error trackers show real files, lines, carets and code, even in
|
|
54
|
+
Lambda.
|
|
55
|
+
- **Self-explaining failures.** Reaching a removed name at runtime raises the usual exception
|
|
56
|
+
with a note that names the `keep` setting to add.
|
|
57
|
+
|
|
58
|
+
## How it is tested
|
|
59
|
+
|
|
60
|
+
| Tier | What it checks |
|
|
61
|
+
|---|---|
|
|
62
|
+
| Case catalog (`tests/cases/**/*.case`) | 126 multi-file programs: expected removals, behavioral equivalence of every target (stdout, exit code, traceback frames), a no-shake build, reviewed golden reports |
|
|
63
|
+
| Soundness oracle | runs the original under `sys.monitoring`; fails if any function that executed was removed |
|
|
64
|
+
| Differential checks | scope analysis vs. CPython's `symtable`, and minified vs. original bytecode, over the whole stdlib |
|
|
65
|
+
| Fuzzing | Hypothesis-generated multi-module programs; equivalence + soundness |
|
|
66
|
+
| Mutation testing | 100% of mutants in the effects rules, class analysis and shaker are killed (floor 99%) |
|
|
67
|
+
| Real-world corpus (nightly) | attrs, click, dateutil, idna, jinja2, packaging, rich, six, tomli |
|
|
68
|
+
| Self-hosting | shakeout bundles itself byte-identically and contains no dead code |
|
|
69
|
+
|
|
70
|
+
Every gate CI runs is in `scripts/check.sh`: `ruff` with all rules, strict `pyright` and `mypy`,
|
|
71
|
+
96% branch coverage, vulture, codespell, packaging checks, pip-audit, zizmor and actionlint, and
|
|
72
|
+
a strict docs build. CPython 3.10 to 3.15 are supported (`scripts/matrix.sh`).
|
shakeout-0.1.0/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# shakeout
|
|
2
|
+
|
|
3
|
+
A static, tree-shaking bundler for Python. Give it an entry script: it finds every module the
|
|
4
|
+
program can import, removes top-level code nothing can reach, and writes a self-contained
|
|
5
|
+
artifact. That can be one runnable `.py`, a zipapp, a pruned directory or a reproducible AWS
|
|
6
|
+
Lambda zip. Tracebacks from bundles name the original files and lines.
|
|
7
|
+
|
|
8
|
+
```console
|
|
9
|
+
$ uvx shakeout build app/main.py -o dist # one runnable file
|
|
10
|
+
$ uvx shakeout build handler.py -t lambda --export handler # Lambda package
|
|
11
|
+
$ uvx shakeout report app/main.py # what is kept, and why
|
|
12
|
+
$ uvx shakeout why app/main.py pkg.core:helper # the chain that keeps a symbol
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Documentation:** https://revmischa.github.io/shakeout/ (sources in [`docs/`](docs/); the
|
|
16
|
+
design and roadmap are in [docs/project/design.md](docs/project/design.md)).
|
|
17
|
+
|
|
18
|
+
## Guarantees
|
|
19
|
+
|
|
20
|
+
- **Sound by default.** Anything the analysis can't prove unused is kept: registration
|
|
21
|
+
decorators, `__init_subclass__` and metaclass registries, modules that use `globals()`, `eval`
|
|
22
|
+
or `sys.modules`, module objects passed around as values, star imports.
|
|
23
|
+
- **Exact tracebacks without source maps.** Modules keep their original file names and line
|
|
24
|
+
numbers, and their sources are registered with `linecache`. Tracebacks, `warnings`,
|
|
25
|
+
`inspect.getsource` and error trackers show real files, lines, carets and code, even in
|
|
26
|
+
Lambda.
|
|
27
|
+
- **Self-explaining failures.** Reaching a removed name at runtime raises the usual exception
|
|
28
|
+
with a note that names the `keep` setting to add.
|
|
29
|
+
|
|
30
|
+
## How it is tested
|
|
31
|
+
|
|
32
|
+
| Tier | What it checks |
|
|
33
|
+
|---|---|
|
|
34
|
+
| Case catalog (`tests/cases/**/*.case`) | 126 multi-file programs: expected removals, behavioral equivalence of every target (stdout, exit code, traceback frames), a no-shake build, reviewed golden reports |
|
|
35
|
+
| Soundness oracle | runs the original under `sys.monitoring`; fails if any function that executed was removed |
|
|
36
|
+
| Differential checks | scope analysis vs. CPython's `symtable`, and minified vs. original bytecode, over the whole stdlib |
|
|
37
|
+
| Fuzzing | Hypothesis-generated multi-module programs; equivalence + soundness |
|
|
38
|
+
| Mutation testing | 100% of mutants in the effects rules, class analysis and shaker are killed (floor 99%) |
|
|
39
|
+
| Real-world corpus (nightly) | attrs, click, dateutil, idna, jinja2, packaging, rich, six, tomli |
|
|
40
|
+
| Self-hosting | shakeout bundles itself byte-identically and contains no dead code |
|
|
41
|
+
|
|
42
|
+
Every gate CI runs is in `scripts/check.sh`: `ruff` with all rules, strict `pyright` and `mypy`,
|
|
43
|
+
96% branch coverage, vulture, codespell, packaging checks, pip-audit, zizmor and actionlint, and
|
|
44
|
+
a strict docs build. CPython 3.10 to 3.15 are supported (`scripts/matrix.sh`).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none" aria-label="shakeout">
|
|
2
|
+
<path d="M32 6 L54 44 H10 Z" fill="currentColor" opacity="0.18"/>
|
|
3
|
+
<path d="M32 6 L54 44 H10 Z" stroke="currentColor" stroke-width="4" stroke-linejoin="round"/>
|
|
4
|
+
<path d="M32 44 V58" stroke="currentColor" stroke-width="4" stroke-linecap="round"/>
|
|
5
|
+
<circle cx="19" cy="54" r="3" fill="currentColor" opacity="0.55"/>
|
|
6
|
+
<circle cx="46" cy="56" r="2.5" fill="currentColor" opacity="0.4"/>
|
|
7
|
+
<circle cx="40" cy="50" r="2" fill="currentColor" opacity="0.3"/>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# How shaking works
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
resolve ─► parse ─► analyze ─► link ─► shake ─► emit
|
|
5
|
+
modules AST scopes, cross- reach- chunks with
|
|
6
|
+
(no exec) effects module ability original lines
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
1. **Resolve.** Starting from the entry script, shakeout follows every `import`, including ones
|
|
10
|
+
inside functions, and locates modules the way Python would, without executing anything.
|
|
11
|
+
Modules under your source roots are bundled. The standard library and other packages are
|
|
12
|
+
external.
|
|
13
|
+
2. **Parse and analyze.** Each module is split into top-level statements. For each one,
|
|
14
|
+
shakeout records the names it binds, the names it reads, using exact Python scoping rules,
|
|
15
|
+
the modules it may import, and whether running it has [side effects](side-effects.md).
|
|
16
|
+
3. **Link.** References like `pkg.sub.func` are followed through imports and re-exports to the
|
|
17
|
+
statement that defines them.
|
|
18
|
+
4. **Shake.** Starting from the entry point, every statement with side effects in an imported
|
|
19
|
+
module, and every export or keep, is marked live, then everything live code references.
|
|
20
|
+
A module that nothing live imports is dropped entirely.
|
|
21
|
+
5. **Emit.** Kept statements are written with their original line numbers, and kept imports are
|
|
22
|
+
[rewritten](import-rewriting.md) to drop unused names. Removed names get a small
|
|
23
|
+
`__getattr__` that explains them if they are reached at runtime.
|
|
24
|
+
|
|
25
|
+
## Per-module isolation
|
|
26
|
+
|
|
27
|
+
Unlike JavaScript bundlers, shakeout doesn't merge modules into one namespace ("scope
|
|
28
|
+
hoisting"). Every module keeps its own globals, `__name__`, import order and circular-import
|
|
29
|
+
behavior. That costs a little size and buys exact semantics and honest tracebacks.
|
|
30
|
+
|
|
31
|
+
## What is shaken
|
|
32
|
+
|
|
33
|
+
The unit of removal is a top-level statement: a function, class, assignment or import.
|
|
34
|
+
Methods inside a kept class stay. Whether an instance's method is called can't be decided
|
|
35
|
+
statically.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Import rewriting
|
|
2
|
+
|
|
3
|
+
A kept `from m import a, b` fails at runtime if `b` no longer exists. Keeping `b` just for
|
|
4
|
+
that would keep most of a library's public API alive. Instead, shakeout rewrites kept imports,
|
|
5
|
+
like esbuild does:
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from lib import used, unused # before
|
|
9
|
+
from lib import used # shakeout: was 'from lib import used, unused' # after
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
If none of the names are used but the import is kept for its side effects, it becomes a plain
|
|
13
|
+
import:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import lib as _shakeout_import_1 # shakeout: was 'from lib import a, b'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The rewritten line occupies the same number of lines as the original, and it carries a comment
|
|
20
|
+
saying what it was, so a traceback through it explains itself.
|
|
21
|
+
|
|
22
|
+
## Not rewritten
|
|
23
|
+
|
|
24
|
+
- Imports of modules outside the bundle: a missing name must still raise `ImportError`.
|
|
25
|
+
- Imports in modules kept whole (for example because they call `globals()`).
|
|
26
|
+
- Plain `import x`. It never requires attributes to exist, so only names of side-effect-free
|
|
27
|
+
bundled modules are dropped, and traceback text stays identical.
|
|
28
|
+
- A `from pkg import sub` where `sub` is a submodule with side effects.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
- [How shaking works](how-it-works.md): the pipeline from entry point to artifact.
|
|
4
|
+
- [Side effects](side-effects.md): which statements may be removed, and why most can't.
|
|
5
|
+
- [Import rewriting](import-rewriting.md): how kept imports shed unused names.
|
|
6
|
+
- [Targets](targets.md): single file, zipapp, tree and Lambda.
|
|
7
|
+
- [Soundness and testing](soundness.md): what shakeout guarantees and how that is checked.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Side effects
|
|
2
|
+
|
|
3
|
+
Importing a Python module runs it. A statement may only be removed if running it does nothing
|
|
4
|
+
observable, which means it only creates bindings in its own module, and if nothing reachable uses
|
|
5
|
+
those bindings.
|
|
6
|
+
|
|
7
|
+
## Removable when unused
|
|
8
|
+
|
|
9
|
+
| Statement | Condition |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `def` / `async def` | No decorators, or only known-pure ones (`dataclass`, `lru_cache`, `property`, `contextmanager`, ...), and pure default values |
|
|
12
|
+
| `class` | Pure decorators, no class keywords, a pure body, and bases that don't register subclasses |
|
|
13
|
+
| `x = ...` | Plain name targets and a pure value: literals, names, operators, lambdas, and calls to known-pure constructors like `TypeVar`, `namedtuple`, `re.compile`, `frozenset`, `field` |
|
|
14
|
+
| `import` | Every module it loads is side-effect free: the standard library, or bundled modules with no side effects |
|
|
15
|
+
| `if TYPE_CHECKING:` | Always |
|
|
16
|
+
|
|
17
|
+
## Always kept
|
|
18
|
+
|
|
19
|
+
Calls at module level, loops, `try`, `with`, attribute or item stores (`config.X = 1`), augmented
|
|
20
|
+
assignment, `del`, `__future__` imports, module docstrings, dunders like `__all__` and
|
|
21
|
+
`__getattr__`, and anything marked `# shakeout: keep`.
|
|
22
|
+
|
|
23
|
+
## Classes that register
|
|
24
|
+
|
|
25
|
+
Defining a subclass runs `__init_subclass__` and metaclass code, which is how plugin registries
|
|
26
|
+
work. A class is *registering* if it defines `__init_subclass__`, has a metaclass, or inherits from
|
|
27
|
+
a registering class. Subclasses of registering classes are always kept. Bases imported from
|
|
28
|
+
packages outside the bundle are assumed to register too, unless they are known to be safe
|
|
29
|
+
(`Exception`, `Enum`, `Protocol`, `NamedTuple`, `ABC`, the `collections.abc` types, ...).
|
|
30
|
+
`assume-pure-bases = true` trusts all unknown bases.
|
|
31
|
+
|
|
32
|
+
## Effects travel through imports
|
|
33
|
+
|
|
34
|
+
A module has side effects if it runs a side-effecting statement or imports a module that has
|
|
35
|
+
them. An `import` of such a module is kept even when its names are unused, because removing it
|
|
36
|
+
would skip those effects.
|
|
37
|
+
|
|
38
|
+
## Assumptions
|
|
39
|
+
|
|
40
|
+
These are documented so you can judge them:
|
|
41
|
+
|
|
42
|
+
- Operators, attribute reads and subscripts on module-level values don't call user code with
|
|
43
|
+
side effects.
|
|
44
|
+
- The known-pure callables don't call back into user code. Builtins that do, like `sorted(...,
|
|
45
|
+
key=f)` and `map`, are not on the list.
|
|
46
|
+
- Importing standard-library modules has no observable effects, except for a short denylist
|
|
47
|
+
such as `this`, `antigravity` and `turtle`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Soundness and testing
|
|
2
|
+
|
|
3
|
+
**Sound** means: if the original program would run a piece of code, the bundle still contains it.
|
|
4
|
+
A bundler that removes too much breaks programs silently, so shakeout's test suite exists
|
|
5
|
+
mainly to make that impossible to miss.
|
|
6
|
+
|
|
7
|
+
| Check | What it catches |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Case catalog (126 multi-file programs) | Expected removals, plus byte-identical stdout, exit codes and traceback frames for every target, with and without shaking and minification |
|
|
10
|
+
| Soundness oracle | Runs the *original* program under `sys.monitoring` and fails if any function that executed was removed, even if output happened to match |
|
|
11
|
+
| `symtable` differential | Scope analysis against CPython's own symbol table on every standard-library file |
|
|
12
|
+
| Minify equivalence | Minified standard-library sources compile to identical instructions on identical lines |
|
|
13
|
+
| Fuzzing | Hypothesis generates multi-module programs with registries, plugins, cycles, local imports and `globals()`, and checks equivalence and soundness |
|
|
14
|
+
| Real-world corpus | attrs, click, dateutil, idna, jinja2, packaging, rich, six and tomli, shaken and compared |
|
|
15
|
+
| Mutation testing | Mutants of the effects rules, class analysis and reachability engine must be caught. The current kill rate is 100% |
|
|
16
|
+
| Self-hosting | shakeout bundles itself and the bundle produces byte-identical output. Shaking shakeout from its API removes nothing |
|
|
17
|
+
|
|
18
|
+
Every bug these found became a permanent regression case. The list is in the
|
|
19
|
+
[design document](../project/design.md).
|
|
20
|
+
|
|
21
|
+
## Known limits
|
|
22
|
+
|
|
23
|
+
- Code reached only through strings (settings files, entry points, generated code) needs `keep`.
|
|
24
|
+
- Data files next to modules are only shipped in vendored distributions. Reading `__file__` in
|
|
25
|
+
your own modules produces a warning.
|
|
26
|
+
- Dropping a side-effect-free module can change the order in which other modules initialize.
|
|
27
|
+
This matters only for order-dependent circular imports.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Targets
|
|
2
|
+
|
|
3
|
+
| Target | Output | Runs as | Contains third-party code |
|
|
4
|
+
|---|---|---|---|
|
|
5
|
+
| `single` | `<entry>.py` (+ `<entry>.manifest.json`) | `python main.py`, or `import` it with `--export` | Only `include`d pure-Python packages |
|
|
6
|
+
| `pyz` | `<entry>.pyz` | `python main.pyz` or `./main.pyz` | Yes, vendored |
|
|
7
|
+
| `tree` | a directory, one file per module | `python dist/main.py` | Yes, vendored |
|
|
8
|
+
| `lambda` | `<entry>.zip` and `package/` | AWS Lambda handler `<entry>.<export>` | Yes, vendored |
|
|
9
|
+
|
|
10
|
+
## single
|
|
11
|
+
|
|
12
|
+
A small runtime (about 200 lines) installs an in-memory importer and runs the entry in the real
|
|
13
|
+
`__main__` namespace. The program sees exactly the globals it would see unbundled, and `pickle`
|
|
14
|
+
of classes defined in the entry works. Each module is stored as chunks tagged with their original
|
|
15
|
+
start line and padded back to the original line numbers when compiled.
|
|
16
|
+
|
|
17
|
+
## tree
|
|
18
|
+
|
|
19
|
+
Every module is written to its import path with removed statements replaced by blank lines. No
|
|
20
|
+
runtime is involved: tracebacks name real files.
|
|
21
|
+
|
|
22
|
+
## pyz
|
|
23
|
+
|
|
24
|
+
A `tree` packed into a reproducible zipapp. Its `__main__.py` registers archived sources with
|
|
25
|
+
`linecache` (CPython's zipimport only does this lazily for tracebacks and splits lines
|
|
26
|
+
incorrectly when files contain form feeds) and runs the entry with `runpy`.
|
|
27
|
+
|
|
28
|
+
## lambda
|
|
29
|
+
|
|
30
|
+
A `tree` with vendored dependencies, zipped reproducibly (fixed timestamps, sorted entries). It
|
|
31
|
+
requires `exports`, and the manifest lists the handler names.
|