Skip to content

Commit

Permalink
Add pew-style project management
Browse files Browse the repository at this point in the history
  • Loading branch information
yggi49 committed Apr 28, 2019
1 parent 5a2a3e6 commit 8b189dd
Show file tree
Hide file tree
Showing 10 changed files with 273 additions and 0 deletions.
4 changes: 4 additions & 0 deletions poetry/console/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

from .config import ApplicationConfig

from .commands.alias import AliasCommand
from .commands.env import EnvCommand


Expand Down Expand Up @@ -91,6 +92,9 @@ def get_default_commands(self): # type: () -> list
# Debug command
commands += [DebugCommand()]

# Alias command
commands += [AliasCommand()]

# Env command
commands += [EnvCommand()]

Expand Down
1 change: 1 addition & 0 deletions poetry/console/commands/alias/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .alias import AliasCommand
26 changes: 26 additions & 0 deletions poetry/console/commands/alias/alias.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from ..command import Command

from .go import AliasGoCommand
from .list import AliasListCommand
from .rm import AliasRmCommand
from .set import AliasSetCommand
from .show import AliasShowCommand


class AliasCommand(Command):
"""
Work with Poetry's project aliases.
alias
"""

commands = [
AliasGoCommand(),
AliasListCommand(),
AliasRmCommand(),
AliasSetCommand(),
AliasShowCommand(),
]

def handle(self): # type: () -> int
return self.call("help", self._config.name)
40 changes: 40 additions & 0 deletions poetry/console/commands/alias/go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import sys

from os import chdir, environ
from distutils.util import strtobool

from poetry.utils._compat import Path
from poetry.utils.alias import AliasManager

from ..command import Command


class AliasGoCommand(Command):
"""
Activate a project's virtualenv and change to its source directory.
go
{name : The alias of the project to activate.}
"""

def handle(self): # type: () -> None
# Don't do anything if we are already inside a virtualenv
active_venv = environ.get("VIRTUAL_ENV", None)

if active_venv is not None:
self.line(
"Virtual environment already activated: "
"<info>{}</>".format(active_venv)
)
return

# Determine the project path for the given alias
manager = AliasManager()
name = self.argument("name")
project_path = manager.get_project(name)
project_dirname = str(project_path)

# Change directory and activate the virtualenv
chdir(project_dirname)
environ["PWD"] = project_dirname
self.call("shell")
18 changes: 18 additions & 0 deletions poetry/console/commands/alias/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from poetry.utils.alias import AliasManager

from ..command import Command


class AliasListCommand(Command):
"""
List all defined project aliases.
list
"""

def handle(self): # type: () -> None
manager = AliasManager()
aliases = manager.list()

for name in sorted(aliases):
self.line("<info>{}</info>: {}".format(name, aliases[name]))
23 changes: 23 additions & 0 deletions poetry/console/commands/alias/rm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from poetry.utils.alias import AliasManager

from ..command import Command


class AliasRmCommand(Command):
"""
Remove the alias for the current project.
rm
{alias? : The alias to remove. If omitted, remove the current project's alias.}
"""

def handle(self): # type: () -> None
manager = AliasManager()
alias = self.argument("alias")

if alias:
project_path = manager.get_project(alias)
else:
project_path = self.poetry.file.parent

manager.remove(project_path)
18 changes: 18 additions & 0 deletions poetry/console/commands/alias/set.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from poetry.utils.alias import AliasManager

from ..command import Command


class AliasSetCommand(Command):
"""
Set an alias for the current project.
set
{name : The alias to set for the current project.}
"""

def handle(self): # type: () -> None
manager = AliasManager(self.poetry.config)
project_path = self.poetry.file.parent
name = self.argument("name")
manager.set(project_path, name)
17 changes: 17 additions & 0 deletions poetry/console/commands/alias/show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from poetry.utils.alias import AliasManager

from ..command import Command


class AliasShowCommand(Command):
"""
Show the alias for the current project.
show
"""

def handle(self): # type: () -> None
manager = AliasManager(self.poetry.config)
project_path = self.poetry.file.parent
name = manager.get_alias(project_path)
self.line(name)
29 changes: 29 additions & 0 deletions poetry/console/commands/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

from .command import Command
from .env_command import EnvCommand
from ...utils.alias import AliasCommandError, AliasManager


class InitCommand(Command):
Expand All @@ -23,6 +24,7 @@ class InitCommand(Command):
{--dev-dependency=* : Package to require for development with an optional version constraint,
e.g. requests:^2.10.0 or requests=2.11.1}
{--l|license= : License of the package}
{--alias= : Project alias}
"""

help = """\
Expand All @@ -33,6 +35,7 @@ def __init__(self):
super(InitCommand, self).__init__()

self._pool = None
self._alias_manager = AliasManager()

def handle(self):
from poetry.layouts import layout
Expand Down Expand Up @@ -112,6 +115,15 @@ def handle(self):
)
python = self.ask(question)

alias = self.option("alias")
if not alias:
question = self.create_question(
"Project alias [<comment>{}</comment>, . to skip]: ".format(name),
default=name,
)
question.set_validator(self._validate_alias)
alias = self.ask(question)

self.line("")

requirements = {}
Expand Down Expand Up @@ -163,6 +175,9 @@ def handle(self):
with (Path.cwd() / "pyproject.toml").open("w") as f:
f.write(content)

if alias:
self._alias_manager.set(Path.cwd(), alias)

def _determine_requirements(
self, requires, allow_prereleases=False # type: List[str] # type: bool
): # type: (...) -> List[str]
Expand Down Expand Up @@ -318,6 +333,20 @@ def _validate_license(self, license):

return license

def _validate_alias(self, alias):
if alias == ".":
return

try:
existing_project = self._alias_manager.get_project(alias)
except AliasCommandError:
existing_project = None

if existing_project is not None:
raise ValueError("Alias already exists")

return alias

def _get_pool(self):
from poetry.repositories import Pool
from poetry.repositories.pypi_repository import PyPiRepository
Expand Down
97 changes: 97 additions & 0 deletions poetry/utils/alias.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import tomlkit

from typing import Dict

from poetry.config import Config
from poetry.locations import CACHE_DIR
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile


class AliasManager(object):
"""
Project aliases manager.
"""

def __init__(self, config=None): # type: (Config) -> None
if config is None:
config = Config.create("config.toml")

venv_path = config.setting("settings.virtualenvs.path")
if venv_path is None:
venv_path = Path(CACHE_DIR) / "virtualenvs"
else:
venv_path = Path(venv_path)

self.aliases_file = TomlFile(venv_path / "aliases.toml")
self.aliases = tomlkit.document()

if self.aliases_file.exists():
self.aliases = self.aliases_file.read()

def list(self): # type: () -> Dict[str, str]
"""
Return a list of all project alias definitions.
"""
return {name: project for name, project in self.aliases.items()}

def get_alias(self, project_path): # type: (Path) -> str
"""
Get the alias for a given project.
"""
project_dirname = str(project_path)

for n, p in self.aliases.items():
if p == project_dirname:
return n

raise AliasCommandError("No alias defined for the project")

def get_project(self, name): # type: (str) -> Path
"""
Get the project path for a given alias.
"""
try:
project_dirname = self.aliases[name]
except KeyError:
raise AliasCommandError("Unknown project alias: {}".format(name))

return Path(project_dirname)

def remove(self, project_path): # type: (Path) -> None
"""
Remove aliase(es) for a project.
"""
project_dirname = str(project_path)

for n, p in self.aliases.items():
if p == project_dirname:
del self.aliases[n]

self.aliases_file.write(self.aliases)

def set(self, project_path, name): # type: (Path, str) -> None
"""
Set an alias for a project.
"""
project_dirname = str(project_path)

# Check the the alias isn't already used for a different project
existing_project = self.aliases.get(name, None)

if existing_project is not None and existing_project != project_dirname:
raise AliasCommandError(
'Alias "{}" already exists for project {}'.format(
name, existing_project
)
)

# Add alias to the aliases file
self.remove(project_path)
self.aliases[name] = project_dirname
self.aliases_file.write(self.aliases)


class AliasCommandError(Exception):

pass

0 comments on commit 8b189dd

Please sign in to comment.