diff --git a/poetry.lock b/poetry.lock index 91a4e599bf2..9de654f830c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -669,6 +669,17 @@ optional = false python-versions = "*" version = "1.0" +[[package]] +category = "main" +description = "Pexpect allows easy control of interactive console applications." +name = "pexpect" +optional = false +python-versions = "*" +version = "4.7.0" + +[package.dependencies] +ptyprocess = ">=0.5" + [[package]] category = "main" description = "Query metadatdata from sdists / bdists / installed packages." @@ -723,6 +734,14 @@ version = "*" python = "<3.7" version = "*" +[[package]] +category = "main" +description = "Run a subprocess in a pseudo terminal" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + [[package]] category = "dev" description = "library with cross-python path, ini-parsing, io, code, log facilities" @@ -1185,7 +1204,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pathlib2", "contextlib2", "unittest2"] [metadata] -content-hash = "ce3f588df2abd05fc628ef84a79d494d60ab722745effd758765870c91d42b5e" +content-hash = "fef0a14e6eed6d3fd20670fe4c048b1f17bc12a08a8b8a5482dfcff6f569cb1d" python-versions = "~2.7 || ^3.4" [metadata.hashes] @@ -1240,9 +1259,11 @@ packaging = ["28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", pastel = ["a904e1659512cc9880a028f66de77cc813a4c32f7ceb68725cbc8afad57ef7ef", "bf3b1901b2442ea0d8ab9a390594e5b0c9584709d543a3113506fe8b28cbace3"] pathlib2 = ["2156525d6576d21c4dcaddfa427fae887ef89a7a9de5cbfe0728b3aafa78427e", "446014523bb9be5c28128c4d2a10ad6bb60769e78bd85658fe44a450674e0ef8"] pep562 = ["58cb1cc9ee63d93e62b4905a50357618d526d289919814bea1f0da8f53b79395", "d2a48b178ebf5f8dd31709cc26a19808ef794561fa2fe50ea01ea2bad4d667ef"] +pexpect = ["2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", "9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"] pkginfo = ["7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", "a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"] pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"] pre-commit = ["1d3c0587bda7c4e537a46c27f2c84aa006acc18facf9970bf947df596ce91f3f", "fa78ff96e8e9ac94c748388597693f18b041a181c94a4f039ad20f45287ba44a"] +ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] pycparser = ["a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"] pygments = ["5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", "e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d", "71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] diff --git a/poetry/console/commands/shell.py b/poetry/console/commands/shell.py index 14cbf2f3485..bdc8c0b1401 100644 --- a/poetry/console/commands/shell.py +++ b/poetry/console/commands/shell.py @@ -36,5 +36,5 @@ def handle(self): # Setting this to avoid spawning unnecessary nested shells environ["POETRY_ACTIVE"] = "1" shell = Shell.get() - self.env.execute(shell.path) + shell.activate(self.env) environ.pop("POETRY_ACTIVE") diff --git a/poetry/utils/shell.py b/poetry/utils/shell.py index c040e904888..a8bc69ed65a 100644 --- a/poetry/utils/shell.py +++ b/poetry/utils/shell.py @@ -1,8 +1,16 @@ import os +import signal +import sys +import pexpect + +from clikit.utils.terminal import Terminal from shellingham import detect_shell from shellingham import ShellDetectionFailure +from ._compat import WINDOWS +from .env import VirtualEnv + class Shell: """ @@ -40,5 +48,51 @@ def get(cls): # type: () -> Shell return cls._shell + def activate(self, env): # type: (VirtualEnv) -> None + if WINDOWS: + return env.execute(self.path) + + terminal = Terminal() + with env.temp_environ(): + c = pexpect.spawn( + self._path, ["-i"], dimensions=(terminal.height, terminal.width) + ) + + c.setecho(False) + activate_script = self._get_activate_script() + bin_dir = "Scripts" if WINDOWS else "bin" + activate_path = env.path / bin_dir / activate_script + c.sendline("{} {}".format(self._get_source_command(), activate_path)) + + def resize(sig, data): + terminal = Terminal() + c.setwinsize(terminal.height, terminal.width) + + signal.signal(signal.SIGWINCH, resize) + + # Interact with the new shell. + c.interact(escape_character=None) + c.close() + + sys.exit(c.exitstatus) + + def _get_activate_script(self): + if "fish" == self._name: + suffix = ".fish" + elif "csh" == self._name: + suffix = ".csh" + else: + suffix = "" + + return "activate" + suffix + + def _get_source_command(self): + if "fish" == self._name: + return "source" + elif "csh" == self._name: + return "source" + + return "." + def __repr__(self): # type: () -> str return '{}("{}", "{}")'.format(self.__class__.__name__, self._name, self._path) diff --git a/pyproject.toml b/pyproject.toml index 8486e47217d..8f0e6c706b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,6 +35,7 @@ pkginfo = "^1.4" html5lib = "^1.0" shellingham = "^1.1" tomlkit = "^0.5.5" +pexpect = "^4.7.0" # The typing module is not in the stdlib in Python 2.7 and 3.4 typing = { version = "^3.6", python = "~2.7 || ~3.4" }