Improve package loadable (#16237)
* Add caching to package loadable * Fix tests * Improve package loadable * Lint * Typing
This commit is contained in:
parent
12709ceaa3
commit
09dc4d663d
4 changed files with 166 additions and 144 deletions
|
@ -4,17 +4,11 @@ import logging
|
|||
import os
|
||||
from subprocess import PIPE, Popen
|
||||
import sys
|
||||
import threading
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import pkg_resources
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
INSTALL_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def is_virtual_env() -> bool:
|
||||
"""Return if we run in a virtual environtment."""
|
||||
|
@ -31,58 +25,30 @@ def install_package(package: str, upgrade: bool = True,
|
|||
Return boolean if install successful.
|
||||
"""
|
||||
# Not using 'import pip; pip.main([])' because it breaks the logger
|
||||
with INSTALL_LOCK:
|
||||
if package_loadable(package):
|
||||
return True
|
||||
_LOGGER.info('Attempting install of %s', package)
|
||||
env = os.environ.copy()
|
||||
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
|
||||
if upgrade:
|
||||
args.append('--upgrade')
|
||||
if constraints is not None:
|
||||
args += ['--constraint', constraints]
|
||||
if target:
|
||||
assert not is_virtual_env()
|
||||
# This only works if not running in venv
|
||||
args += ['--user']
|
||||
env['PYTHONUSERBASE'] = os.path.abspath(target)
|
||||
if sys.platform != 'win32':
|
||||
# Workaround for incompatible prefix setting
|
||||
# See http://stackoverflow.com/a/4495175
|
||||
args += ['--prefix=']
|
||||
process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
|
||||
_, stderr = process.communicate()
|
||||
if process.returncode != 0:
|
||||
_LOGGER.error("Unable to install package %s: %s",
|
||||
package, stderr.decode('utf-8').lstrip().strip())
|
||||
return False
|
||||
|
||||
_LOGGER.info('Attempting install of %s', package)
|
||||
env = os.environ.copy()
|
||||
args = [sys.executable, '-m', 'pip', 'install', '--quiet', package]
|
||||
if upgrade:
|
||||
args.append('--upgrade')
|
||||
if constraints is not None:
|
||||
args += ['--constraint', constraints]
|
||||
if target:
|
||||
assert not is_virtual_env()
|
||||
# This only works if not running in venv
|
||||
args += ['--user']
|
||||
env['PYTHONUSERBASE'] = os.path.abspath(target)
|
||||
if sys.platform != 'win32':
|
||||
# Workaround for incompatible prefix setting
|
||||
# See http://stackoverflow.com/a/4495175
|
||||
args += ['--prefix=']
|
||||
process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=env)
|
||||
_, stderr = process.communicate()
|
||||
if process.returncode != 0:
|
||||
_LOGGER.error("Unable to install package %s: %s",
|
||||
package, stderr.decode('utf-8').lstrip().strip())
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def package_loadable(package: str) -> bool:
|
||||
"""Check if a package is what will be loaded when we import it.
|
||||
|
||||
Returns True when the requirement is met.
|
||||
Returns False when the package is not installed or doesn't meet req.
|
||||
"""
|
||||
try:
|
||||
req = pkg_resources.Requirement.parse(package)
|
||||
except ValueError:
|
||||
# This is a zip file
|
||||
req = pkg_resources.Requirement.parse(urlparse(package).fragment)
|
||||
|
||||
req_proj_name = req.project_name.lower()
|
||||
|
||||
for path in sys.path:
|
||||
for dist in pkg_resources.find_distributions(path):
|
||||
# If the project name is the same, it will be the one that is
|
||||
# loaded when we import it.
|
||||
if dist.project_name.lower() == req_proj_name:
|
||||
return dist in req
|
||||
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
async def async_get_user_site(deps_dir: str) -> str:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue