Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 <dan@danryan.co>
Copyright (c) 2018, Dan Ryan <dan@danryan.co> and Tzu-ping Chung <uranusjr@gmail.com>

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.
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)
16 changes: 16 additions & 0 deletions src/passa/actions/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals


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

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

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

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 = {
"sources": 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):
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