Skip to content

Commit

Permalink
Add: support installing from http URL (#38)
Browse files Browse the repository at this point in the history
* Add: support http archive

* Fix: pass pylint

* Fix: you can't move venv

* comment for get_pkg_from_url

* Add: pkg_name arg to pip_api.install
  • Loading branch information
eight04 committed Jan 24, 2022
1 parent 4e1ba05 commit 0896249
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 5 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ classifiers =
Operating System :: Microsoft :: Windows :: Windows 7
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.10
Topic :: Software Development :: Build Tools

keywords = pip, pipm, venv, vex, virtualenv, pipenv
Expand Down
30 changes: 30 additions & 0 deletions vpip/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ def install_global(packages, upgrade=False, latest=False):
"""
from .. import venv, pip_api
for pkg in packages:
if pkg.startswith("http"):
install_global_url(pkg)
continue
vv = venv.get_global_pkg_venv(pkg)
if vv.exists() and not upgrade:
print("{} is already installed".format(pkg))
Expand All @@ -61,6 +64,33 @@ def install_global(packages, upgrade=False, latest=False):
vv.destroy()
raise

def get_pkg_from_url(url):
# FIXME: is there a faster way?
from .. import venv, pip_api
vv = venv.get_global_tmp_venv()
with vv.activate(auto_create=True):
pkg = pip_api.install(url, deps=False)
vv.destroy()
return pkg

def install_global_url(url):
from .. import venv, pip_api
pkg = get_pkg_from_url(url)
vv = venv.get_global_pkg_venv(pkg.name)
if vv.exists():
result = input(f"{pkg.name} has already been installed. Overwrite? (y/n) ")
if result in "yY":
vv.destroy()
else:
return
try:
with vv.activate(auto_create=True):
pip_api.install(url, pkg_name=pkg.name)
link_console_script(pkg.name)
except Exception:
vv.destroy()
raise

def install_local(packages, dev=False, **kwargs):
"""Install local packages and save to dependency.
Expand Down
29 changes: 24 additions & 5 deletions vpip/pip_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,28 @@

from .execute import execute

def install(package, install_scripts=None, upgrade=False, latest=False):
def install(package, install_scripts=None, upgrade=False, latest=False, deps=True, pkg_name=None):
"""Install a package and return the package info.
:arg str package: Package name. It may include the version specifier.
:arg str package: Package name. It may include the version specifier. It can also be a URL.
:arg str install_scripts: Install scripts to a different folder. It uses
the ``--install-option="--install-scripts=..."`` pip option.
:arg bool upgrade: Upgrade package.
:arg bool latest: Whether upgrade to the latest version. Otherwise upgrade
to the compatible version. This option has no effect if ``package``
includes specifiers.
:arg bool deps: Whether to install dependencies.
:arg str pkg_name: Package name. This is used when ``package`` is a URL. If not specified,
vpip parse installation output to find the installed package.
:return: Package information returned by :func:`show`.
:rtype: Namespace
"""
cmd = "install"
require = Requirement(package)
if package.startswith("http"):
require = None
else:
require = Requirement(package)
pkg_name = pkg_name or require.name
if install_scripts:
cmd += " --install-option \"--install-scripts={}\"".format(install_scripts)
if upgrade:
Expand All @@ -37,8 +44,20 @@ def install(package, install_scripts=None, upgrade=False, latest=False):
pass
else:
package = "{}~={}".format(require.name, get_compatible_version(version))
execute_pip("{} {}".format(cmd, package))
return show([require.name])[0]
if not deps:
cmd += " --no-deps"
cmd = f"{cmd} {package}"
if not pkg_name:
packages = []
for line in execute_pip(cmd, capture=True):
print(line)
match = re.match("Installing collected packages:(.+)", line, re.I)
if match:
packages = [p.strip() for p in match.group(1).split(",")]
pkg_name = packages[-1]
else:
execute_pip(cmd)
return show([pkg_name])[0]

def install_requirements(file="requirements.txt"):
"""Install ``requirements.txt`` file."""
Expand Down
7 changes: 7 additions & 0 deletions vpip/venv.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import os
import re
import shutil
Expand Down Expand Up @@ -81,6 +83,11 @@ def get_global_pkg_venv(pkg):
:rtype: Venv
"""
return Venv(get_global_folder(pkg))

def get_global_tmp_venv() -> Venv:
"""Get the :class:`Venv` instance with a temporary name."""
from time import time
return Venv(get_global_folder(f"tmp-{time()}"))

class Builder(venv.EnvBuilder):
"""An environment builder that could be used inside a venv.
Expand Down

0 comments on commit 0896249

Please sign in to comment.