cleanup: handle Python site-packages directories

Inside a given `site-packages` directory, *.pyc files live in
`__pycache__` directories. These are created by the Python interpreter
when a module is imported in order to speed up future access to the
imported module.

These can be left behind when formulae are uninstalled, which results in
erroneous success in importing the modules they were originally compiled
for.

Let's fix that by cleaning these up. In the top-level `__pycache__`
directory, we use the `Pathname#prune?` extension in order to determine
whether a `*.pyc` file is old enough to clean up. In other directories,
we clean them up when they are all that remains in a given module's
tree.

Removing these `*.pyc` files will make `python3` regenerate them when
required, so deleting them is relatively safe. The worst consequence is
slightly slower module import times.

Closes #13701.
This commit is contained in:
Carlo Cabrera 2022-08-18 21:09:12 +08:00
parent fcb30c1ead
commit 0b48261f79
No known key found for this signature in database
GPG Key ID: C74D447FC549A1D0

View File

@ -235,6 +235,7 @@ module Homebrew
cleanup_cache
cleanup_logs
cleanup_lockfiles
cleanup_python_site_packages
prune_prefix_symlinks_and_directories
unless dry_run?
@ -489,6 +490,54 @@ module Homebrew
end
end
def cleanup_python_site_packages
pyc_files = Hash.new { |h, k| h[k] = [] }
seen_non_pyc_file = Hash.new { |h, k| h[k] = false }
unused_pyc_files = []
HOMEBREW_PREFIX.glob("lib/python*/site-packages").each do |site_packages|
site_packages.each_child do |child|
next unless child.directory?
# TODO: Work out a sensible way to clean up pip's, setuptools', and wheel's
# {dist,site}-info directories. Alternatively, consider always removing
# all `-info` directories, because we may not be making use of them.
next if child.basename.to_s.end_with?("-info")
# Clean up old *.pyc files in the top-level __pycache__.
if child.basename.to_s == "__pycache__"
child.find do |path|
next unless path.extname == ".pyc"
next unless path.prune?(days)
unused_pyc_files << path
end
next
end
# Look for directories that contain only *.pyc files.
child.find do |path|
next if path.directory?
if path.extname == ".pyc"
pyc_files[child] << path
else
seen_non_pyc_file[child] = true
break
end
end
end
end
unused_pyc_files += pyc_files.reject { |k,| seen_non_pyc_file[k] }
.values
return if unused_pyc_files.blank?
unused_pyc_files.each do |pyc|
cleanup_path(pyc) { pyc.unlink }
end
end
def prune_prefix_symlinks_and_directories
ObserverPathnameExtension.reset_counts!