| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  | # typed: strict | 
					
						
							| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  | module Language | 
					
						
							| 
									
										
										
										
											2020-08-17 19:01:02 +02:00
										 |  |  |   # Helper functions for Python formulae. | 
					
						
							|  |  |  |   # | 
					
						
							|  |  |  |   # @api public | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |   module Python | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |     sig { params(python: T.any(String, Pathname)).returns(T.nilable(Version)) } | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |     def self.major_minor_version(python) | 
					
						
							| 
									
										
										
										
											2023-04-29 23:39:26 +02:00
										 |  |  |       version = `#{python} --version 2>&1`.chomp[/(\d\.\d+)/, 1] | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |       return unless version | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-29 23:39:26 +02:00
										 |  |  |       Version.new(version) | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |     sig { params(python: T.any(String, Pathname)).returns(Pathname) } | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |     def self.homebrew_site_packages(python = "python3.7") | 
					
						
							|  |  |  |       HOMEBREW_PREFIX/site_packages(python) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |     sig { params(python: T.any(String, Pathname)).returns(String) } | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |     def self.site_packages(python = "python3.7") | 
					
						
							| 
									
										
										
										
											2019-03-11 20:14:03 +11:00
										 |  |  |       if (python == "pypy") || (python == "pypy3") | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |         "site-packages" | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         "lib/python#{major_minor_version python}/site-packages" | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  |     sig { | 
					
						
							|  |  |  |       params( | 
					
						
							|  |  |  |         build: T.any(BuildOptions, Tab), | 
					
						
							|  |  |  |         block: T.nilable(T.proc.params(python: String, version: T.nilable(Version)).void), | 
					
						
							|  |  |  |       ).void | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |     def self.each_python(build, &block) | 
					
						
							| 
									
										
										
										
											2022-05-30 04:37:09 +01:00
										 |  |  |       original_pythonpath = ENV.fetch("PYTHONPATH", nil) | 
					
						
							| 
									
										
										
										
											2019-03-11 20:14:03 +11:00
										 |  |  |       pythons = { "python@3" => "python3", | 
					
						
							|  |  |  |                   "pypy"     => "pypy", | 
					
						
							|  |  |  |                   "pypy3"    => "pypy3" } | 
					
						
							|  |  |  |       pythons.each do |python_formula, python| | 
					
						
							| 
									
										
										
										
											2018-02-28 03:20:14 -08:00
										 |  |  |         python_formula = Formulary.factory(python_formula) | 
					
						
							|  |  |  |         next if build.without? python_formula.to_s | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |         version = major_minor_version python | 
					
						
							| 
									
										
										
										
											2020-05-18 13:50:43 +01:00
										 |  |  |         ENV["PYTHONPATH"] = if python_formula.latest_version_installed? | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |           nil | 
					
						
							|  |  |  |         else | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |           homebrew_site_packages(python).to_s | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2017-09-24 19:24:46 +01:00
										 |  |  |         block&.call python, version | 
					
						
							| 
									
										
										
										
											2014-03-07 18:03:54 +00:00
										 |  |  |       end | 
					
						
							|  |  |  |       ENV["PYTHONPATH"] = original_pythonpath | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  |     sig { params(python: T.any(String, Pathname)).returns(T::Boolean) } | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |     def self.reads_brewed_pth_files?(python) | 
					
						
							| 
									
										
										
										
											2023-12-14 02:52:30 +00:00
										 |  |  |       return false unless homebrew_site_packages(python).directory? | 
					
						
							| 
									
										
										
										
											2024-03-27 06:26:32 +00:00
										 |  |  |       return false unless homebrew_site_packages(python).writable? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |       probe_file = homebrew_site_packages(python)/"homebrew-pth-probe.pth" | 
					
						
							| 
									
										
										
										
											2014-11-05 19:37:24 -08:00
										 |  |  |       begin | 
					
						
							|  |  |  |         probe_file.atomic_write("import site; site.homebrew_was_here = True") | 
					
						
							| 
									
										
										
										
											2017-12-13 06:06:07 +00:00
										 |  |  |         with_homebrew_path { quiet_system python, "-c", "import site; assert(site.homebrew_was_here)" } | 
					
						
							| 
									
										
										
										
											2014-11-05 19:37:24 -08:00
										 |  |  |       ensure | 
					
						
							|  |  |  |         probe_file.unlink if probe_file.exist? | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  |     sig { params(python: T.any(String, Pathname)).returns(Pathname) } | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |     def self.user_site_packages(python) | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  |       Pathname.new(`#{python} -c "import site; print(site.getusersitepackages())"`.chomp) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  |     sig { params(python: T.any(String, Pathname), path: T.any(String, Pathname)).returns(T::Boolean) } | 
					
						
							| 
									
										
										
										
											2015-08-03 13:09:07 +01:00
										 |  |  |     def self.in_sys_path?(python, path) | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |       script = <<~PYTHON | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  |         import os, sys | 
					
						
							|  |  |  |         [os.path.realpath(p) for p in sys.path].index(os.path.realpath("#{path}")) | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |       PYTHON | 
					
						
							| 
									
										
										
										
											2014-10-19 13:47:55 -07:00
										 |  |  |       quiet_system python, "-c", script | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2014-11-06 22:25:11 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |     sig { params(prefix: Pathname, python: T.any(String, Pathname)).returns(T::Array[String]) } | 
					
						
							| 
									
										
										
										
											2022-07-09 22:22:10 +08:00
										 |  |  |     def self.setup_install_args(prefix, python = "python3") | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |       shim = <<~PYTHON | 
					
						
							| 
									
										
										
										
											2014-12-09 23:17:11 -08:00
										 |  |  |         import setuptools, tokenize | 
					
						
							|  |  |  |         __file__ = 'setup.py' | 
					
						
							|  |  |  |         exec(compile(getattr(tokenize, 'open', open)(__file__).read() | 
					
						
							|  |  |  |           .replace('\\r\\n', '\\n'), __file__, 'exec')) | 
					
						
							| 
									
										
										
										
											2018-07-11 15:17:40 +02:00
										 |  |  |       PYTHON | 
					
						
							| 
									
										
										
										
											2014-12-09 23:17:11 -08:00
										 |  |  |       %W[
 | 
					
						
							|  |  |  |         -c | 
					
						
							|  |  |  |         #{shim} | 
					
						
							| 
									
										
										
										
											2015-01-08 16:43:40 -08:00
										 |  |  |         --no-user-cfg | 
					
						
							| 
									
										
										
										
											2014-12-09 23:17:11 -08:00
										 |  |  |         install | 
					
						
							|  |  |  |         --prefix=#{prefix} | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |         --install-scripts=#{prefix}/bin | 
					
						
							| 
									
										
										
										
											2022-07-09 22:22:10 +08:00
										 |  |  |         --install-lib=#{prefix/site_packages(python)} | 
					
						
							| 
									
										
										
										
											2014-12-09 23:17:11 -08:00
										 |  |  |         --single-version-externally-managed | 
					
						
							|  |  |  |         --record=installed.txt | 
					
						
							|  |  |  |       ] | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2015-03-02 21:44:35 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 15:40:43 +00:00
										 |  |  |     # Mixin module for {Formula} adding shebang rewrite features. | 
					
						
							|  |  |  |     module Shebang | 
					
						
							| 
									
										
										
										
											2023-04-18 06:17:22 -07:00
										 |  |  |       module_function | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 20:24:34 -04:00
										 |  |  |       # A regex to match potential shebang permutations. | 
					
						
							| 
									
										
										
										
											2024-01-18 22:18:42 +00:00
										 |  |  |       PYTHON_SHEBANG_REGEX = %r{^#! ?/usr/bin/(?:env )?python(?:[23](?:\.\d{1,2})?)?( |$)} | 
					
						
							| 
									
										
										
										
											2023-08-14 20:24:34 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # The length of the longest shebang matching `SHEBANG_REGEX`. | 
					
						
							| 
									
										
										
										
											2024-03-29 18:32:43 -04:00
										 |  |  |       PYTHON_SHEBANG_MAX_LENGTH = T.let("#! /usr/bin/env pythonx.yyy ".length, Integer) | 
					
						
							| 
									
										
										
										
											2023-08-14 20:24:34 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 15:40:43 +00:00
										 |  |  |       # @private | 
					
						
							| 
									
										
										
										
											2023-08-14 20:24:34 -04:00
										 |  |  |       sig { params(python_path: T.any(String, Pathname)).returns(Utils::Shebang::RewriteInfo) } | 
					
						
							| 
									
										
										
										
											2023-04-18 06:17:22 -07:00
										 |  |  |       def python_shebang_rewrite_info(python_path) | 
					
						
							| 
									
										
										
										
											2020-03-27 15:40:43 +00:00
										 |  |  |         Utils::Shebang::RewriteInfo.new( | 
					
						
							| 
									
										
										
										
											2023-08-14 20:24:34 -04:00
										 |  |  |           PYTHON_SHEBANG_REGEX, | 
					
						
							|  |  |  |           PYTHON_SHEBANG_MAX_LENGTH, | 
					
						
							| 
									
										
										
										
											2021-05-04 15:59:53 +01:00
										 |  |  |           "#{python_path}\\1", | 
					
						
							| 
									
										
										
										
											2020-03-27 15:40:43 +00:00
										 |  |  |         ) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |       sig { params(formula: Formula, use_python_from_path: T::Boolean).returns(Utils::Shebang::RewriteInfo) } | 
					
						
							|  |  |  |       def detected_python_shebang(formula = T.cast(self, Formula), use_python_from_path: false) | 
					
						
							| 
									
										
										
										
											2022-08-13 01:11:09 +08:00
										 |  |  |         python_path = if use_python_from_path | 
					
						
							|  |  |  |           "/usr/bin/env python3" | 
					
						
							|  |  |  |         else | 
					
						
							| 
									
										
										
										
											2024-08-09 14:08:29 +01:00
										 |  |  |           python_deps = formula.deps.select(&:required?).map(&:name).grep(/^python(@.+)?$/) | 
					
						
							| 
									
										
										
										
											2022-08-13 01:11:09 +08:00
										 |  |  |           raise ShebangDetectionError.new("Python", "formula does not depend on Python") if python_deps.empty? | 
					
						
							|  |  |  |           if python_deps.length > 1
 | 
					
						
							|  |  |  |             raise ShebangDetectionError.new("Python", "formula has multiple Python dependencies") | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2020-03-27 15:40:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 01:11:09 +08:00
										 |  |  |           python_dep = python_deps.first | 
					
						
							|  |  |  |           Formula[python_dep].opt_bin/python_dep.sub("@", "") | 
					
						
							| 
									
										
										
										
											2021-04-29 17:48:09 +01:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2020-02-17 21:29:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 01:11:09 +08:00
										 |  |  |         python_shebang_rewrite_info(python_path) | 
					
						
							| 
									
										
										
										
											2020-02-17 21:29:38 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |     # Mixin module for {Formula} adding virtualenv support features. | 
					
						
							|  |  |  |     module Virtualenv | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |       # Instantiates, creates and yields a {Virtualenv} object for use from | 
					
						
							| 
									
										
										
										
											2018-10-18 21:42:43 -04:00
										 |  |  |       # {Formula#install}, which provides helper methods for instantiating and | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |       # installing packages into a Python virtualenv. | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |       # | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |       # @param venv_root [Pathname, String] the path to the root of the virtualenv | 
					
						
							|  |  |  |       #   (often `libexec/"venv"`) | 
					
						
							| 
									
										
										
										
											2024-04-26 20:55:51 +02:00
										 |  |  |       # @param python [String, Pathname] which interpreter to use (e.g. `"python3"` | 
					
						
							|  |  |  |       #   or `"python3.x"`) | 
					
						
							| 
									
										
										
										
											2020-11-03 16:36:48 -05:00
										 |  |  |       # @param formula [Formula] the active {Formula} | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |       # @return [Virtualenv] a {Virtualenv} instance | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |       sig { | 
					
						
							|  |  |  |         params( | 
					
						
							|  |  |  |           venv_root:            T.any(String, Pathname), | 
					
						
							|  |  |  |           python:               T.any(String, Pathname), | 
					
						
							|  |  |  |           formula:              Formula, | 
					
						
							|  |  |  |           system_site_packages: T::Boolean, | 
					
						
							|  |  |  |           without_pip:          T::Boolean, | 
					
						
							|  |  |  |         ).returns(Virtualenv) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       def virtualenv_create(venv_root, python = "python", formula = T.cast(self, Formula), | 
					
						
							|  |  |  |                             system_site_packages: true, without_pip: true) | 
					
						
							| 
									
										
										
										
											2023-12-07 22:58:54 +00:00
										 |  |  |         # Limit deprecation to 3.12+ for now (or if we can't determine the version). | 
					
						
							| 
									
										
										
										
											2024-04-26 20:55:51 +02:00
										 |  |  |         # Some used this argument for `setuptools`, which we no longer bundle since 3.12. | 
					
						
							| 
									
										
										
										
											2023-12-07 22:58:54 +00:00
										 |  |  |         unless without_pip | 
					
						
							|  |  |  |           python_version = Language::Python.major_minor_version(python) | 
					
						
							|  |  |  |           if python_version.nil? || python_version.null? || python_version >= "3.12" | 
					
						
							|  |  |  |             raise ArgumentError, "virtualenv_create's without_pip is deprecated starting with Python 3.12" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 08:45:26 -07:00
										 |  |  |         ENV.refurbish_args | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         venv = Virtualenv.new formula, venv_root, python | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |         venv.create(system_site_packages:, without_pip:) | 
					
						
							| 
									
										
										
										
											2016-07-31 11:59:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # Find any Python bindings provided by recursive dependencies | 
					
						
							|  |  |  |         formula_deps = formula.recursive_dependencies | 
					
						
							| 
									
										
										
										
											2024-02-22 23:29:55 +00:00
										 |  |  |         pth_contents = formula_deps.filter_map do |d| | 
					
						
							| 
									
										
										
										
											2020-10-08 18:52:14 +02:00
										 |  |  |           next if d.build? || d.test? | 
					
						
							|  |  |  |           # Do not add the main site-package provided by the brewed | 
					
						
							|  |  |  |           # Python formula, to keep the virtual-env's site-package pristine | 
					
						
							|  |  |  |           next if python_names.include? d.name | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 23:01:52 -04:00
										 |  |  |           dep_site_packages = Formula[d.name].opt_prefix/Language::Python.site_packages(python) | 
					
						
							| 
									
										
										
										
											2016-07-31 11:59:30 -07:00
										 |  |  |           next unless dep_site_packages.exist? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-31 11:59:30 -07:00
										 |  |  |           "import site; site.addsitedir('#{dep_site_packages}')\n" | 
					
						
							| 
									
										
										
										
											2024-02-22 23:29:55 +00:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         (venv.site_packages/"homebrew_deps.pth").write pth_contents.join unless pth_contents.empty? | 
					
						
							| 
									
										
										
										
											2016-07-31 11:59:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         venv | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |       # Returns true if a formula option for the specified python is currently | 
					
						
							|  |  |  |       # active or if the specified python is required by the formula. Valid | 
					
						
							| 
									
										
										
										
											2024-04-26 20:55:51 +02:00
										 |  |  |       # inputs are `"python"`, `"python2"` and `:python3`. Note that | 
					
						
							|  |  |  |       # `"with-python"`, `"without-python"`, `"with-python@2"` and `"without-python@2"` | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |       # formula options are handled correctly even if not associated with any | 
					
						
							|  |  |  |       # corresponding depends_on statement. | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |       sig { params(python: String).returns(T::Boolean) } | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |       def needs_python?(python) | 
					
						
							|  |  |  |         return true if build.with?(python) | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-03 16:37:32 -04:00
										 |  |  |         (requirements.to_a | deps).any? { |r| r.name.split("/").last == python && r.required? } | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |       # Helper method for the common case of installing a Python application. | 
					
						
							|  |  |  |       # Creates a virtualenv in `libexec`, installs all `resource`s defined | 
					
						
							| 
									
										
										
										
											2024-04-30 11:10:23 +02:00
										 |  |  |       # on the formula and then installs the formula. An options hash may be | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |       # passed (e.g. `:using => "python"`) to override the default, guessed | 
					
						
							| 
									
										
										
										
											2020-02-10 22:54:11 +01:00
										 |  |  |       # formula preference for python or python@x.y, or to resolve an ambiguous | 
					
						
							|  |  |  |       # case where it's not clear whether python or python@x.y should be the | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |       # default guess. | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |       sig { | 
					
						
							|  |  |  |         params( | 
					
						
							|  |  |  |           using:                T.nilable(String), | 
					
						
							|  |  |  |           system_site_packages: T::Boolean, | 
					
						
							|  |  |  |           without_pip:          T::Boolean, | 
					
						
							|  |  |  |           link_manpages:        T::Boolean, | 
					
						
							| 
									
										
										
										
											2024-03-04 15:45:55 -05:00
										 |  |  |           without:              T.nilable(T.any(String, T::Array[String])), | 
					
						
							|  |  |  |           start_with:           T.nilable(T.any(String, T::Array[String])), | 
					
						
							|  |  |  |           end_with:             T.nilable(T.any(String, T::Array[String])), | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         ).returns(Virtualenv) | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-07-30 00:49:16 -07:00
										 |  |  |       def virtualenv_install_with_resources(using: nil, system_site_packages: true, without_pip: true, | 
					
						
							| 
									
										
										
										
											2024-03-04 15:45:55 -05:00
										 |  |  |                                             link_manpages: false, without: nil, start_with: nil, end_with: nil) | 
					
						
							| 
									
										
										
										
											2021-09-22 20:30:32 +08:00
										 |  |  |         python = using | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |         if python.nil? | 
					
						
							| 
									
										
										
										
											2020-10-08 18:52:14 +02:00
										 |  |  |           wanted = python_names.select { |py| needs_python?(py) } | 
					
						
							| 
									
										
										
										
											2020-07-09 15:43:05 +01:00
										 |  |  |           raise FormulaUnknownPythonError, self if wanted.empty? | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |           raise FormulaAmbiguousPythonError, self if wanted.size > 1
 | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |           python = T.must(wanted.first) | 
					
						
							| 
									
										
										
										
											2018-03-09 13:45:30 -08:00
										 |  |  |           python = "python3" if python == "python" | 
					
						
							| 
									
										
										
										
											2016-08-05 00:20:20 -07:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2024-03-04 15:45:55 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         venv_resources = if without.nil? && start_with.nil? && end_with.nil? | 
					
						
							|  |  |  |           resources | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           remaining_resources = resources.to_h { |resource| [resource.name, resource] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           slice_resources!(remaining_resources, Array(without)) | 
					
						
							|  |  |  |           start_with_resources = slice_resources!(remaining_resources, Array(start_with)) | 
					
						
							|  |  |  |           end_with_resources = slice_resources!(remaining_resources, Array(end_with)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           start_with_resources + remaining_resources.values + end_with_resources | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |         venv = virtualenv_create(libexec, python.delete("@"), system_site_packages:, | 
					
						
							|  |  |  |                                                               without_pip:) | 
					
						
							| 
									
										
										
										
											2024-03-04 15:45:55 -05:00
										 |  |  |         venv.pip_install venv_resources | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |         venv.pip_install_and_link(T.must(buildpath), link_manpages:) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         venv | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-20 12:03:48 +02:00
										 |  |  |       sig { returns(T::Array[String]) } | 
					
						
							| 
									
										
										
										
											2020-10-08 18:52:14 +02:00
										 |  |  |       def python_names | 
					
						
							|  |  |  |         %w[python python3 pypy pypy3] + Formula.names.select { |name| name.start_with? "python@" } | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-04 15:45:55 -05:00
										 |  |  |       private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       sig { | 
					
						
							|  |  |  |         params( | 
					
						
							|  |  |  |           resources_hash: T::Hash[String, Resource], | 
					
						
							|  |  |  |           resource_names: T::Array[String], | 
					
						
							|  |  |  |         ).returns(T::Array[Resource]) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       def slice_resources!(resources_hash, resource_names) | 
					
						
							|  |  |  |         resource_names.map do |resource_name| | 
					
						
							|  |  |  |           resources_hash.delete(resource_name) do | 
					
						
							|  |  |  |             raise ArgumentError, "Resource \"#{resource_name}\" is not defined in formula or is already used" | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |       # Convenience wrapper for creating and installing packages into Python | 
					
						
							|  |  |  |       # virtualenvs. | 
					
						
							|  |  |  |       class Virtualenv | 
					
						
							|  |  |  |         # Initializes a Virtualenv instance. This does not create the virtualenv | 
					
						
							|  |  |  |         # on disk; {#create} does that. | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |         # | 
					
						
							|  |  |  |         # @param formula [Formula] the active {Formula} | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         # @param venv_root [Pathname, String] the path to the root of the | 
					
						
							|  |  |  |         #   virtualenv | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         # @param python [String, Pathname] which interpreter to use, e.g. | 
					
						
							|  |  |  |         #   "python" or "python2" | 
					
						
							|  |  |  |         sig { params(formula: Formula, venv_root: T.any(String, Pathname), python: T.any(String, Pathname)).void } | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         def initialize(formula, venv_root, python) | 
					
						
							|  |  |  |           @formula = formula | 
					
						
							| 
									
										
										
										
											2024-04-01 08:09:42 +01:00
										 |  |  |           @venv_root = T.let(Pathname(venv_root), Pathname) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           @python = python | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         sig { returns(Pathname) } | 
					
						
							|  |  |  |         def root | 
					
						
							|  |  |  |           @venv_root | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sig { returns(Pathname) } | 
					
						
							|  |  |  |         def site_packages | 
					
						
							|  |  |  |           @venv_root/Language::Python.site_packages(@python) | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |         # Obtains a copy of the virtualenv library and creates a new virtualenv on disk. | 
					
						
							|  |  |  |         # | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         # @return [void] | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         sig { params(system_site_packages: T::Boolean, without_pip: T::Boolean).void } | 
					
						
							| 
									
										
										
										
											2023-07-30 00:49:16 -07:00
										 |  |  |         def create(system_site_packages: true, without_pip: true) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           return if (@venv_root/"bin/python").exist? | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-21 13:18:54 +08:00
										 |  |  |           args = ["-m", "venv"] | 
					
						
							|  |  |  |           args << "--system-site-packages" if system_site_packages | 
					
						
							| 
									
										
										
										
											2023-07-30 00:49:16 -07:00
										 |  |  |           args << "--without-pip" if without_pip | 
					
						
							| 
									
										
										
										
											2021-09-21 13:18:54 +08:00
										 |  |  |           @formula.system @python, *args, @venv_root | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-03 09:42:25 +00:00
										 |  |  |           # Robustify symlinks to survive python patch upgrades | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           @venv_root.find do |f| | 
					
						
							|  |  |  |             next unless f.symlink? | 
					
						
							| 
									
										
										
										
											2016-09-10 10:38:35 +01:00
										 |  |  |             next unless (rp = f.realpath.to_s).start_with? HOMEBREW_CELLAR | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:18:56 +01:00
										 |  |  |             version = rp.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}o | 
					
						
							| 
									
										
										
										
											2020-03-10 14:17:30 +00:00
										 |  |  |             version = "@#{version.captures.first}" unless version.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             new_target = rp.sub %r{#{HOMEBREW_CELLAR}/python#{version}/[^/]+}, Formula["python#{version}"].opt_prefix | 
					
						
							| 
									
										
										
										
											2016-09-10 10:38:35 +01:00
										 |  |  |             f.unlink | 
					
						
							|  |  |  |             f.make_symlink new_target | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-10-04 09:47:14 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |           Pathname.glob(@venv_root/"lib/python*/orig-prefix.txt").each do |prefix_file| | 
					
						
							|  |  |  |             prefix_path = prefix_file.read | 
					
						
							| 
									
										
										
										
											2020-03-10 14:17:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-16 22:18:56 +01:00
										 |  |  |             version = prefix_path.match %r{^#{HOMEBREW_CELLAR}/python@(.*?)/}o | 
					
						
							| 
									
										
										
										
											2020-03-10 14:17:30 +00:00
										 |  |  |             version = "@#{version.captures.first}" unless version.nil? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             prefix_path.sub! %r{^#{HOMEBREW_CELLAR}/python#{version}/[^/]+}, Formula["python#{version}"].opt_prefix | 
					
						
							| 
									
										
										
										
											2016-10-04 09:47:14 -07:00
										 |  |  |             prefix_file.atomic_write prefix_path | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2023-07-30 00:49:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |           # Remove unnecessary activate scripts | 
					
						
							|  |  |  |           (@venv_root/"bin").glob("[Aa]ctivate*").map(&:unlink) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Installs packages represented by `targets` into the virtualenv. | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |         # | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         # @param targets [String, Pathname, Resource, | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |         #   Array<String, Pathname, Resource>] (A) token(s) passed to `pip` | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         #   representing the object to be installed. This can be a directory | 
					
						
							|  |  |  |         #   containing a setup.py, a {Resource} which will be staged and | 
					
						
							|  |  |  |         #   installed, or a package identifier to be fetched from PyPI. | 
					
						
							|  |  |  |         #   Multiline strings are allowed and treated as though they represent | 
					
						
							|  |  |  |         #   the contents of a `requirements.txt`. | 
					
						
							|  |  |  |         # @return [void] | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             targets:         T.any(String, Pathname, Resource, T::Array[T.any(String, Pathname, Resource)]), | 
					
						
							|  |  |  |             build_isolation: T::Boolean, | 
					
						
							|  |  |  |           ).void | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-04-01 11:31:29 +02:00
										 |  |  |         def pip_install(targets, build_isolation: true) | 
					
						
							| 
									
										
										
										
											2020-07-13 22:48:53 +10:00
										 |  |  |           targets = Array(targets) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           targets.each do |t| | 
					
						
							| 
									
										
										
										
											2024-07-30 11:38:07 -04:00
										 |  |  |             if t.is_a?(Resource) | 
					
						
							|  |  |  |               t.stage do | 
					
						
							|  |  |  |                 target = Pathname.pwd | 
					
						
							|  |  |  |                 target /= t.downloader.basename if t.url&.end_with?("-none-any.whl") | 
					
						
							|  |  |  |                 do_install(target, build_isolation:) | 
					
						
							|  |  |  |               end | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |             else | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |               t = t.lines.map(&:strip) if t.is_a?(String) && t.include?("\n") | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |               do_install(t, build_isolation:) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |             end | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-02 22:37:15 +02:00
										 |  |  |         # Installs packages represented by `targets` into the virtualenv, but | 
					
						
							| 
									
										
										
										
											2020-11-05 17:17:03 -05:00
										 |  |  |         # unlike {#pip_install} also links new scripts to {Formula#bin}. | 
					
						
							|  |  |  |         # | 
					
						
							| 
									
										
										
										
											2016-08-02 22:37:15 +02:00
										 |  |  |         # @param (see #pip_install) | 
					
						
							|  |  |  |         # @return (see #pip_install) | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             targets:         T.any(String, Pathname, Resource, T::Array[T.any(String, Pathname, Resource)]), | 
					
						
							|  |  |  |             link_manpages:   T::Boolean, | 
					
						
							|  |  |  |             build_isolation: T::Boolean, | 
					
						
							|  |  |  |           ).void | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-04-01 11:31:29 +02:00
										 |  |  |         def pip_install_and_link(targets, link_manpages: false, build_isolation: true) | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           bin_before = Dir[@venv_root/"bin/*"].to_set | 
					
						
							| 
									
										
										
										
											2022-11-12 20:25:27 -08:00
										 |  |  |           man_before = Dir[@venv_root/"share/man/man*/*"].to_set if link_manpages | 
					
						
							| 
									
										
										
										
											2016-08-02 22:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |           pip_install(targets, build_isolation:) | 
					
						
							| 
									
										
										
										
											2016-08-02 22:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |           bin_after = Dir[@venv_root/"bin/*"].to_set | 
					
						
							| 
									
										
										
										
											2016-08-02 22:37:15 +02:00
										 |  |  |           bin_to_link = (bin_after - bin_before).to_a | 
					
						
							|  |  |  |           @formula.bin.install_symlink(bin_to_link) | 
					
						
							| 
									
										
										
										
											2022-11-12 20:25:27 -08:00
										 |  |  |           return unless link_manpages | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           man_after = Dir[@venv_root/"share/man/man*/*"].to_set | 
					
						
							|  |  |  |           man_to_link = (man_after - man_before).to_a | 
					
						
							|  |  |  |           man_to_link.each do |manpage| | 
					
						
							|  |  |  |             (@formula.man/Pathname.new(manpage).dirname.basename).install_symlink manpage | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         private | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-02 19:18:35 -05:00
										 |  |  |         sig { | 
					
						
							|  |  |  |           params( | 
					
						
							|  |  |  |             targets:         T.any(String, Pathname, T::Array[T.any(String, Pathname)]), | 
					
						
							|  |  |  |             build_isolation: T::Boolean, | 
					
						
							|  |  |  |           ).void | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-04-01 11:31:29 +02:00
										 |  |  |         def do_install(targets, build_isolation: true) | 
					
						
							| 
									
										
										
										
											2020-07-13 22:48:53 +10:00
										 |  |  |           targets = Array(targets) | 
					
						
							| 
									
										
										
										
											2024-03-07 16:20:20 +00:00
										 |  |  |           args = @formula.std_pip_args(prefix: false, build_isolation:) | 
					
						
							| 
									
										
										
										
											2023-07-30 00:49:16 -07:00
										 |  |  |           @formula.system @python, "-m", "pip", "--python=#{@venv_root}/bin/python", "install", *args, *targets | 
					
						
							| 
									
										
										
										
											2016-07-22 23:02:52 -07:00
										 |  |  |         end | 
					
						
							| 
									
										
										
										
											2017-10-21 19:52:43 +02:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |