Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7844350
Restructure and reorganization
techalchemy Sep 14, 2018
cadad15
Add forgotten __future__ imports
techalchemy Sep 14, 2018
edc7734
drop unused import
techalchemy Sep 14, 2018
9afcc4e
Fix missed import
techalchemy Sep 14, 2018
76ca23e
sigh
techalchemy Sep 14, 2018
702853d
Update license, add news
techalchemy Sep 17, 2018
ec156a5
Add initial tests
techalchemy Sep 17, 2018
4a52ed8
Clean up options and add virtualenv module
techalchemy Sep 18, 2018
ad79981
Add tests (clean is still failing)
techalchemy Sep 18, 2018
ab37932
Wheel installation into virtualenv works
techalchemy Sep 18, 2018
2c7b235
Update setup.cfg
techalchemy Sep 18, 2018
9239d7b
Implement working versions of clean and install inside virtualenvs
techalchemy Sep 19, 2018
766d73e
Fix cleaning working set and messaging
techalchemy Sep 20, 2018
bf16fde
Split into separate libraries
techalchemy Sep 21, 2018
432c6a5
Fix package task
techalchemy Sep 21, 2018
27ca19d
Update lockfile and setup.cfg
techalchemy Sep 22, 2018
1c708f2
Add LRU cache for cacheable function calls
techalchemy Sep 22, 2018
b416213
Drop markers from editable requirements
techalchemy Sep 22, 2018
cbddbee
Add PySpec object to handle comparison and consolidation
techalchemy Sep 22, 2018
2ccbada
Cleanup
techalchemy Sep 23, 2018
fc59b47
Test fixes
techalchemy Sep 23, 2018
9586ce0
Fix specifier dedup logic
techalchemy Sep 23, 2018
d167d00
Fix import
techalchemy Sep 23, 2018
f3f6c26
Fix tox
techalchemy Sep 23, 2018
56523a8
Use working set from environment instead of real prefix
techalchemy Sep 23, 2018
6b7a9af
Fix virtualenv usage and Set inheritance
techalchemy Sep 23, 2018
704c954
Add set operations and comparison methods to metasets
techalchemy Sep 24, 2018
e50f898
Add abstract extraction methods for marker cleanup
techalchemy Sep 26, 2018
9e51f06
Add intersection method for PySpecs and smarter unions and creation
techalchemy Sep 26, 2018
7ea23ee
Simplify and cleanup metadata implementation
techalchemy Sep 26, 2018
f65c6ef
update lockfile?
techalchemy Sep 26, 2018
a9b8bfb
Finalize marker intersection logic
techalchemy Sep 27, 2018
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
2 changes: 1 addition & 1 deletion Pipfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[packages]
passa = { editable = true, path = '.' }
passa = { editable = true, path = '.', extras = ['virtualenv'] }

[dev-packages]
black = '*'
Expand Down
456 changes: 318 additions & 138 deletions Pipfile.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
sphinx
sphinx_rtd_theme
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.
7 changes: 7 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,27 @@ python_requires = >=2.7,!=3.0,!=3.1,!=3.2,!=3.3
setup_requires = setuptools>=36.2.2
install_requires =
appdirs
cached-property
distlib
installer
packaging
packagebuilder
pip-shims>=0.1.2
plette[validation]>=0.2.2
requests
resolvelib>=0.2.1,!=1.0.0.dev0
backports.functools_lru_cache; python_version<='2.7'
requirementslib>=1.1.1
six
virtualenv
vistir[spinner]>=0.1.4

[options.extras_require]
pack =
invoke
parver
virtualenv =
mork
tests =
pytest-xdist
pytest-timeout
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 installer.synchronizer import Synchronizer
from installer.operations 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 installer.synchronizer import Cleaner
from installer.operations 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")
54 changes: 54 additions & 0 deletions src/passa/actions/init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# -*- coding=utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import io
import os

import six

import packagebuilder

import plette
import vistir


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)
sources = packagebuilder.get_sources()
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 installer.synchronizer import Synchronizer
from installer.operations 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 installer.synchronizer import Cleaner
from installer.operations 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 installer.synchronizers import Synchronizer
from installer.operations 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