Skip to content
2 changes: 1 addition & 1 deletion src/passa/actions/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def add_packages(packages=[], editables=[], project=None, dev=False, sync=False,

syncer = Synchronizer(
project, default=default, develop=develop,
clean_unneeded=clean,
clean_unneeded=clean
)
success = sync(syncer)
if not success:
Expand Down
4 changes: 2 additions & 2 deletions src/passa/actions/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from __future__ import absolute_import, print_function, unicode_literals


def clean(project, dev=False):
def clean(project, default=True, dev=False, sync=True):
from passa.models.synchronizers import Cleaner
from passa.operations.sync import clean

cleaner = Cleaner(project, default=True, develop=dev)
cleaner = Cleaner(project, default=default, develop=dev, sync=sync)

success = clean(cleaner)
if not success:
Expand Down
2 changes: 1 addition & 1 deletion src/passa/actions/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import absolute_import, print_function, unicode_literals


def remove(project=None, only="default", packages=[], clean=True):
def remove(project=None, only="default", packages=[], clean=True, sync=False):
from passa.models.lockers import PinReuseLocker
from passa.operations.lock import lock

Expand Down
7 changes: 4 additions & 3 deletions src/passa/cli/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

from ..actions.add import add_packages
from ._base import BaseCommand
from .options import package_group
from .options import package_group, clean_group


class Command(BaseCommand):

name = "add"
description = "Add packages to project."
arguments = [package_group]
arguments = [package_group, clean_group]

def run(self, options):
if not options.editables and not options.packages:
Expand All @@ -20,7 +20,8 @@ def run(self, options):
packages=options.packages,
editables=options.editables,
project=options.project,
dev=options.dev
dev=options.dev,
clean=options.clean
)


Expand Down
9 changes: 6 additions & 3 deletions src/passa/cli/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@

from ..actions.clean import clean
from ._base import BaseCommand
from .options import dev, no_default
from .options import dev, no_default, sync_group


class Command(BaseCommand):

name = "clean"
description = "Uninstall unlisted packages from the environment."
arguments = [dev, no_default]
arguments = [dev, no_default, sync_group]

def run(self, options):
return clean(project=options.project, default=options.default, dev=options.dev)
return clean(
project=options.project, default=options.default, dev=options.dev,
sync=options.sync
)


if __name__ == "__main__":
Expand Down
77 changes: 69 additions & 8 deletions src/passa/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
from __future__ import absolute_import

import argparse
import inspect
import os
import sys

import six
import tomlkit.exceptions

import passa.models.projects
import passa.models.virtualenv
import vistir


Expand All @@ -20,23 +23,67 @@ def __init__(self, root, *args, **kwargs):
pipfile = root.joinpath("Pipfile")
if not pipfile.is_file():
raise argparse.ArgumentError(
"{0!r} is not a Pipfile project".format(root),
project, "{0!r} is not a Pipfile project".format(root.as_posix()),
)
self.venv = self.get_venv(root)
try:
super(Project, self).__init__(root.as_posix(), *args, **kwargs)
super(Project, self).__init__(root.as_posix(), env_prefix=self.venv.venv_dir,
*args, **kwargs)
except tomlkit.exceptions.ParseError as e:
raise argparse.ArgumentError(
"failed to parse Pipfile: {0!r}".format(str(e)),
project, "failed to parse Pipfile: {0!r}".format(str(e)),
)

def get_venv(self, root):
if 'VIRTUAL_ENV' in os.environ:
return passa.models.virtualenv.VirtualEnv(os.environ['VIRTUAL_ENV'])
return passa.models.virtualenv.VirtualEnv.from_project_path(root)

def __name__(self):
return "Project Root"


class OptionMeta(type):

@property
def action_map(self):
action_map = getattr(self, '_action_map', None)
if not action_map:
self.action_map = {
name.strip("_").replace("Action", ""): obj
for name, obj in inspect.getmembers(argparse)
if name.startswith('_') and name.endswith('Action')
}
return self._action_map

@action_map.setter
def action_map(self, action_map):
self._action_map = action_map


@six.add_metaclass(OptionMeta)
class Option(object):
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
self.args = list(args)
self.kwargs = kwargs.copy()
if "dest" not in kwargs and not args[0].startswith("-"):
dest = list(args).pop(0)
else:
dest = kwargs.pop("dest", args[0].lstrip("-").replace("-", "_"))
action = kwargs.pop("action", None)
if not action:
if 'const' in kwargs:
action = 'store_const'
else:
action = 'store'
self.action = self.get_option(action, args, dest, **kwargs)

@classmethod
def get_option(cls, action, option_strings, dest, *args, **kwargs):
if action:
action = action.title().replace("_", "")
return cls.action_map[action](list(option_strings), dest, *args, **kwargs)
return

def add_to_parser(self, parser):
parser.add_argument(*self.args, **self.kwargs)
Expand All @@ -46,7 +93,7 @@ def add_to_group(self, group):


class ArgumentGroup(object):
def __init__(self, name, parser=None, is_mutually_exclusive=False, required=None, options=[]):
def __init__(self, name, parser=None, is_mutually_exclusive=False, required=False, options=[]):
self.name = name
self.options = options
self.parser = parser
Expand All @@ -65,6 +112,9 @@ def add_to_parser(self, parser):
self.argument_group = group
self.parser = parser

def add_to_group(self, group):
self.add_to_parser(group)


project = Option(
"--project", metavar="project", default=os.getcwd(), type=Project,
Expand All @@ -77,7 +127,7 @@ def add_to_parser(self, parser):
)

python_version = Option(
"--py-version", "--python-version", "--requires-python", metavar="python-version",
"--py-version", "--python-version", "--requires-python", metavar="python_version",
dest="python_version", default=PYTHON_VERSION, type=str,
help="required minor python version for the project"
)
Expand All @@ -102,6 +152,11 @@ def add_to_parser(self, parser):
help="do not synchronize the environment",
)

sync = Option(
"--sync", dest="sync", action="store_true", help="synchronize the environment",
default=False
)

target = Option(
"-t", "--target", default=None,
help="file to export into (default is to print to stdout)"
Expand Down Expand Up @@ -132,6 +187,10 @@ def add_to_parser(self, parser):
help="do not remove packages not specified in Pipfile.lock",
)

clean = Option(
"--clean", dest="clean", action="store_true", default=False,
help="remove packages not specified in Pipfile.lock",
)
dev_only = Option(
"--dev", dest="only", action="store_const", const="dev",
help="only try to modify [dev-packages]",
Expand All @@ -149,5 +208,7 @@ def add_to_parser(self, parser):

include_hashes_group = ArgumentGroup("include_hashes", is_mutually_exclusive=True, options=[include_hashes, no_include_hashes])
dev_group = ArgumentGroup("dev", is_mutually_exclusive="True", options=[dev_only, default_only])
package_group = ArgumentGroup("packages", options=[packages, editable, dev, no_sync])
new_project_group = ArgumentGroup("new-project", options=[new_project, python_version])
clean_group = ArgumentGroup("clean", is_mutually_exclusive=True, options=[clean, no_clean])
sync_group = ArgumentGroup("sync", is_mutually_exclusive=True, options=[sync, no_sync])
package_group = ArgumentGroup("packages", options=[packages, editable, dev, sync_group])
6 changes: 3 additions & 3 deletions src/passa/cli/remove.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

from ..actions.remove import remove
from ._base import BaseCommand
from .options import dev_group, no_clean, packages
from .options import dev_group, clean_group, sync_group, packages


class Command(BaseCommand):

name = "remove"
description = "Remove packages from project."
arguments = [dev_group, no_clean, packages]
arguments = [dev_group, clean_group, sync_group, packages]

def run(self, options):
return remove(project=options.project, only=options.only,
packages=options.packages, clean=options.clean)
packages=options.packages, clean=options.clean, sync=options.sync)


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions src/passa/cli/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

from ..actions.sync import sync
from ._base import BaseCommand
from .options import dev, no_clean
from .options import dev, clean_group


class Command(BaseCommand):

name = "sync"
description = "Install Pipfile.lock into the environment."
arguments = [dev, no_clean]
arguments = [dev, clean_group]

def run(self, options):
return sync(project=options.project, dev=options.dev, clean=options.clean)
Expand Down
4 changes: 2 additions & 2 deletions src/passa/cli/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

from ..actions.upgrade import upgrade
from ._base import BaseCommand
from .options import no_clean, no_sync, packages, strategy
from .options import clean_group, sync_group, packages, strategy


class Command(BaseCommand):

name = "upgrade"
description = "Upgrade packages in project."
arguments = [packages, strategy, no_clean, no_sync]
arguments = [packages, strategy, clean_group, sync_group]

def run(self, options):
return upgrade(project=options.project, strategy=options.strategy,
Expand Down
1 change: 1 addition & 0 deletions src/passa/models/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def dumps(self):
class Project(object):

root = attr.ib()
env_prefix = attr.ib(default=None)
_p = attr.ib(init=False)
_l = attr.ib(init=False)

Expand Down
Loading