Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure and reorganization #53

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2018, Dan Ryan <[email protected]>
Copyright (c) 2018, Dan Ryan <[email protected]> and Tzu-ping Chung <[email protected]>

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
Expand Down
124 changes: 59 additions & 65 deletions Pipfile.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions news/53.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactored and restructured the internals for improved organization and separation of concerns.
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ install_requires =
resolvelib>=0.2.1,!=1.0.0.dev0
requirementslib>=1.1.1
six
virtualenv
vistir[spinner]>=0.1.4

[options.extras_require]
pack =
invoke
parver
tests =
cached-property
pytest-xdist
pytest-timeout
pytest-cov
Expand Down
Empty file added src/passa/actions/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions src/passa/actions/add.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import itertools
import sys


def add_packages(packages=[], editables=[], project=None, dev=False, sync=False, clean=False):
from passa.models.lockers import PinReuseLocker
from passa.operations.lock import lock

lines = list(itertools.chain(
packages,
("-e {}".format(e) for e in editables),
))

project = project
for line in lines:
try:
project.add_line_to_pipfile(line, develop=dev)
except (TypeError, ValueError) as e:
print("Cannot add {line!r} to Pipfile: {error}".format(
line=line, error=str(e),
), file=sys.stderr)
return 2

prev_lockfile = project.lockfile

locker = PinReuseLocker(project)
success = lock(locker)
if not success:
return 1

project._p.write()
project._l.write()
print("Written to project at", project.root)

if not sync:
return

from passa.models.synchronizers import Synchronizer
from passa.operations.sync import sync

lockfile_diff = project.difference_lockfile(prev_lockfile)
default = any(lockfile_diff.default)
develop = any(lockfile_diff.develop)

syncer = Synchronizer(
project, default=default, develop=develop,
clean_unneeded=clean
)
success = sync(syncer)
if not success:
return 1

print("Synchronized project at", project.root)
17 changes: 17 additions & 0 deletions src/passa/actions/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


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=default, develop=dev, sync=sync)

success = clean(cleaner)
if not success:
return 1

if sync:
print("Cleaned project at", project.root)
93 changes: 93 additions & 0 deletions src/passa/actions/freeze.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import contextlib
import io
import itertools
import sys

import vistir.misc


def _source_as_lines(source, extra):
url = source["url"]
if extra:
lines = ["--extra-index-url {}".format(url)]
else:
lines = ["--index-url {}".format(url)]
if not source.get("verify_ssl", True):
lines = ["--trusted-host {}".format(url)]
return lines


def _requirement_as_line(requirement, sources, include_hashes):
if requirement.index:
sources = sources
else:
sources = None
line = vistir.misc.to_text(
requirement.as_line(sources=sources, include_hashes=include_hashes)
)
return line


@contextlib.contextmanager
def open_for_output(filename):
if filename is None:
yield sys.stdout
return
with io.open(filename, "w", encoding="utf-8", newline="\n") as f:
yield f


def freeze(project=None, default=True, dev=True, include_hashes=None, target=None):
from requirementslib import Requirement

lockfile = project.lockfile
if not lockfile:
print("Pipfile.lock is required to export.", file=sys.stderr)
return 1

section_names = []
if default:
section_names.append("default")
if dev:
section_names.append("develop")
requirements = [
Requirement.from_pipfile(key, entry._data)
for key, entry in itertools.chain.from_iterable(
lockfile.get(name, {}).items()
for name in section_names
)
]

if include_hashes is None:
include_hashes = all(r.is_named for r in requirements)

sources = lockfile.meta.sources._data

source_lines = list(vistir.misc.dedup(itertools.chain(
itertools.chain.from_iterable(
_source_as_lines(source, False)
for source in sources[:1]
),
itertools.chain.from_iterable(
_source_as_lines(source, True)
for source in sources[1:]
),
)))

requirement_lines = sorted(vistir.misc.dedup(
_requirement_as_line(requirement, sources, include_hashes)
for requirement in requirements
))

with open_for_output(target) as f:
for line in source_lines:
f.write(line)
f.write("\n")
f.write("\n")
for line in requirement_lines:
f.write(line)
f.write("\n")
59 changes: 59 additions & 0 deletions src/passa/actions/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import io
import os
from pip_shims import Command as PipCommand, cmdoptions
import plette
import six
import vistir


class PipCmd(PipCommand):
name = "PipCmd"


def get_sources(urls, trusted_hosts):
trusted_hosts = [six.moves.urllib.parse.urlparse(url).netloc for url in trusted_hosts]
sources = []
for url in urls:
parsed_url = six.moves.urllib.parse.urlparse(url)
netloc = parsed_url.netloc
if '@' in netloc:
_, _, netloc = netloc.rpartition('@')
name, _, _ = netloc.partition('.') # Just use the domain name as the source name
verify_ssl = True
if netloc in trusted_hosts:
verify_ssl = False
sources.append({"url": url, "name": name, "verify_ssl": verify_ssl})
return sources


def init_project(root=None, python_version=None):
pipfile_path = os.path.join(root, "Pipfile")
if os.path.isfile(pipfile_path):
raise RuntimeError("{0!r} is already a Pipfile project".format(root))
if not os.path.exists(root):
vistir.path.mkdir_p(root, mode=0o755)
pip_command = PipCmd()
cmdoptions.make_option_group(cmdoptions.index_group, pip_command.parser)
parsed, _ = pip_command.parser.parse_args([])
index_urls = [parsed.index_url] + parsed.extra_index_urls
sources = get_sources(index_urls, parsed.trusted_hosts)
data = {
"source": sources,
"packages": {},
"dev-packages": {},
}
if python_version:
data["requires"] = {"python_version": python_version}
return create_project(pipfile_path=pipfile_path, data=data)


def create_project(pipfile_path, data={}):
pipfile = plette.pipfiles.Pipfile(data=data)
with io.open(pipfile_path, "w") as fh:
pipfile.dump(fh)
print("Successfully created new pipfile at {0!r}".format(pipfile_path))
return 0
32 changes: 32 additions & 0 deletions src/passa/actions/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


def install(project=None, check=True, dev=False, clean=True):
from passa.models.lockers import BasicLocker
from passa.operations.lock import lock

project = project

if not check or not project.is_synced():
locker = BasicLocker(project)
success = lock(locker)
if not success:
return 1
project._l.write()
print("Written to project at", project.root)

from passa.models.synchronizers import Synchronizer
from passa.operations.sync import sync

syncer = Synchronizer(
project, default=True, develop=dev,
clean_unneeded=clean,
)

success = sync(syncer)
if not success:
return 1

print("Synchronized project at", project.root)
17 changes: 17 additions & 0 deletions src/passa/actions/lock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


def lock(project=None):
from passa.models.lockers import BasicLocker
from passa.operations.lock import lock

project = project
locker = BasicLocker(project)
success = lock(locker)
if not success:
return

project._l.write()
print("Written to project at", project.root)
38 changes: 38 additions & 0 deletions src/passa/actions/remove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


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

default = (only != "dev")
develop = (only != "default")

project = project
project.remove_keys_from_pipfile(
packages, default=default, develop=develop,
)

locker = PinReuseLocker(project)
success = lock(locker)
if not success:
return 1

project._p.write()
project._l.write()
print("Written to project at", project.root)

if not clean:
return

from passa.models.synchronizers import Cleaner
from passa.operations.sync import clean

cleaner = Cleaner(project, default=True, develop=True)
success = clean(cleaner)
if not success:
return 1

print("Cleaned project at", project.root)
20 changes: 20 additions & 0 deletions src/passa/actions/sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


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

project = project
syncer = Synchronizer(
project, default=True, develop=dev,
clean_unneeded=clean,
)

success = sync(syncer)
if not success:
return 1

print("Synchronized project at", project.root)
Loading