-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
677 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from .bundler_manager import BundlerManager | ||
|
||
|
||
bundler_manager = BundlerManager() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from typing import TYPE_CHECKING | ||
|
||
|
||
if TYPE_CHECKING: | ||
from typing import Optional | ||
|
||
from clikit.api.io.io import IO | ||
from poetry.poetry import Poetry | ||
from poetry.utils._compat import Path | ||
|
||
|
||
class Bundler(object): | ||
@property | ||
def name(self): # type: () -> str | ||
raise NotImplementedError() | ||
|
||
def bundle( | ||
self, poetry, io, path, executable=None | ||
): # type: (Poetry, IO, Path, Optional[str]) -> None | ||
raise NotImplementedError() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from typing import Dict | ||
from typing import List | ||
|
||
from .bundler import Bundler | ||
from .exceptions import BundlerManagerError | ||
from .venv_bundler import VenvBundler | ||
|
||
|
||
class BundlerManager(object): | ||
def __init__(self): # type: () -> None | ||
self._bundlers = {} # type: Dict[str, Bundler] | ||
|
||
# Register default bundlers | ||
self.register_bundler(VenvBundler()) | ||
|
||
@property | ||
def bundlers(self): # type: () -> List[Bundler] | ||
return list(self._bundlers.values()) | ||
|
||
def bundler(self, name): # type: (str) -> Bundler | ||
if name.lower() not in self._bundlers: | ||
raise BundlerManagerError('The bundler "{}" does not exist.'.format(name)) | ||
|
||
return self._bundlers[name.lower()] | ||
|
||
def register_bundler(self, bundler): # type: (Bundler) -> BundlerManager | ||
if bundler.name.lower() in self._bundlers: | ||
raise BundlerManagerError( | ||
'A bundler with the name "{}" already exists.'.format(bundler.name) | ||
) | ||
|
||
self._bundlers[bundler.name.lower()] = bundler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
class BundlerManagerError(Exception): | ||
|
||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import sys | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
from .bundler import Bundler | ||
|
||
|
||
if TYPE_CHECKING: | ||
from typing import Optional | ||
|
||
from poetry.poetry import Poetry | ||
from clikit.api.io.io import IO | ||
from clikit.api.io.section_output import SectionOutput # noqa | ||
from poetry.utils._compat import Path | ||
|
||
|
||
class VenvBundler(Bundler): | ||
@property | ||
def name(self): # type: () -> str | ||
return "venv" | ||
|
||
def bundle( | ||
self, poetry, io, path, executable=None, remove=False | ||
): # type: (Poetry, IO, Path, Optional[str], bool) -> bool | ||
from poetry.core.masonry.builders.wheel import WheelBuilder | ||
from poetry.core.semver.version import Version | ||
from poetry.installation.installer import Installer | ||
from poetry.installation.operations.install import Install | ||
from poetry.io.null_io import NullIO | ||
from poetry.utils._compat import Path | ||
from poetry.utils.env import EnvManager | ||
from poetry.utils.env import SystemEnv | ||
from poetry.utils.env import VirtualEnv | ||
from poetry.utils.helpers import temporary_directory | ||
|
||
manager = EnvManager(poetry) | ||
if executable is not None: | ||
executable, python_version = manager.get_executable_info(executable) | ||
else: | ||
version_info = SystemEnv(Path(sys.prefix)).get_version_info() | ||
python_version = Version(*version_info[:3]) | ||
|
||
message = self._get_message(poetry, path) | ||
if io.supports_ansi() and not io.is_debug(): | ||
verbosity = io.verbosity | ||
io = io.section() | ||
io.set_verbosity(verbosity) | ||
|
||
io.write_line(message) | ||
|
||
if path.exists(): | ||
env = VirtualEnv(path) | ||
env_python_version = Version(*env.version_info[:3]) | ||
if not env.is_sane() or env_python_version != python_version or remove: | ||
self._write( | ||
io, message + ": <info>Removing existing virtual environment</info>" | ||
) | ||
|
||
manager.remove_venv(str(path)) | ||
|
||
self._write( | ||
io, | ||
message | ||
+ ": <info>Creating a virtual environment using Python <b>{}</b></info>".format( | ||
python_version | ||
), | ||
) | ||
|
||
manager.build_venv(str(path), executable=executable) | ||
else: | ||
self._write( | ||
io, | ||
message | ||
+ ": <info>Using existing virtual environment</info>".format( | ||
python_version | ||
), | ||
) | ||
else: | ||
self._write( | ||
io, | ||
message | ||
+ ": <info>Creating a virtual environment using Python <b>{}</b></info>".format( | ||
python_version | ||
), | ||
) | ||
|
||
manager.build_venv(str(path), executable=executable) | ||
|
||
env = VirtualEnv(path) | ||
|
||
self._write( | ||
io, | ||
message + ": <info>Installing dependencies</info>".format(python_version), | ||
) | ||
|
||
installer = Installer( | ||
NullIO() if not io.is_debug() else io, | ||
env, | ||
poetry.package, | ||
poetry.locker, | ||
poetry.pool, | ||
poetry.config, | ||
) | ||
installer.remove_untracked() | ||
installer.use_executor(poetry.config.get("experimental.new-installer", False)) | ||
|
||
return_code = installer.run() | ||
if return_code: | ||
self._write( | ||
io, | ||
self._get_message(poetry, path, error=True) | ||
+ ": <error>Failed</> at step <b>Installing dependencies</b>".format( | ||
python_version | ||
), | ||
) | ||
return False | ||
|
||
self._write( | ||
io, | ||
message | ||
+ ": <info>Installing <c1>{}</c1> (<b>{}</b>)</info>".format( | ||
poetry.package.pretty_name, poetry.package.pretty_version | ||
), | ||
) | ||
|
||
# Build a wheel of the project in a temporary directory | ||
# and install it in the newly create virtual environment | ||
with temporary_directory() as directory: | ||
wheel_name = WheelBuilder.make_in(poetry, directory=Path(directory)) | ||
wheel = Path(directory).joinpath(wheel_name) | ||
package = poetry.package.clone() | ||
package.source_type = "file" | ||
package.source_url = wheel | ||
installer.executor.execute([Install(package)]) | ||
|
||
self._write(io, self._get_message(poetry, path, done=True)) | ||
|
||
return True | ||
|
||
def _get_message( | ||
self, poetry, path, done=False, error=False | ||
): # type: (Poetry, Path, bool) -> str | ||
operation_color = "blue" | ||
|
||
if error: | ||
operation_color = "red" | ||
elif done: | ||
operation_color = "green" | ||
|
||
verb = "Bundling" | ||
if done: | ||
verb = "<success>Bundled</success>" | ||
|
||
return " <fg={};options=bold>•</> {} <c1>{}</c1> (<b>{}</b>) into <c2>{}</c2>".format( | ||
operation_color, | ||
verb, | ||
poetry.package.pretty_name, | ||
poetry.package.pretty_version, | ||
path, | ||
) | ||
|
||
def _write(self, io, message): # type: (IO, str) -> None | ||
from clikit.api.io.section_output import SectionOutput # noqa | ||
|
||
if ( | ||
io.is_debug() | ||
or not io.supports_ansi() | ||
or not isinstance(io.output, SectionOutput) | ||
): | ||
io.write_line(message) | ||
return | ||
|
||
io.output.clear() | ||
io.write(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .bundle import BundleCommand |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from typing import Optional | ||
|
||
from ..command import Command | ||
from .venv import BundleVenvCommand | ||
|
||
|
||
class BundleCommand(Command): | ||
|
||
name = "bundle" | ||
description = "Bundle the current project." | ||
|
||
commands = [BundleVenvCommand()] | ||
|
||
def handle(self): # type: () -> Optional[int] | ||
return self.call("help", self._config.name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from typing import Optional | ||
|
||
from cleo import argument | ||
from cleo import option | ||
|
||
from poetry.utils._compat import Path | ||
|
||
from ..command import Command | ||
|
||
|
||
class BundleVenvCommand(Command): | ||
|
||
name = "venv" | ||
description = "Bundle the current project into a virtual environment" | ||
|
||
arguments = [ | ||
argument("path", "The path to the virtual environment to bundle into."), | ||
] | ||
|
||
options = [ | ||
option( | ||
"python", | ||
"p", | ||
"The Python executable to use to create the virtual environment. " | ||
"Defaults to the current Python executable", | ||
flag=False, | ||
value_required=True, | ||
), | ||
option( | ||
"clear", | ||
None, | ||
"Clear the existing virtual environment if it exists. ", | ||
flag=True, | ||
), | ||
] | ||
|
||
def handle(self): # type: () -> Optional[int] | ||
from poetry.bundle import bundler_manager | ||
|
||
path = Path(self.argument("path")) | ||
executable = self.option("python") | ||
|
||
bundler = bundler_manager.bundler("venv") | ||
|
||
self.line("") | ||
|
||
return int( | ||
not bundler.bundle( | ||
self.poetry, | ||
self._io, | ||
path, | ||
executable=executable, | ||
remove=self.option("clear"), | ||
) | ||
) |
Oops, something went wrong.