Commit 329b334b by Sébastien Eustace

Merge branch 'master' into develop

parents 55668d76 f597d1fc
......@@ -25,7 +25,7 @@ pip-log.txt
/test.py
/test_*.*
setup.cfg
/setup.cfg
MANIFEST.in
/setup.py
/docs/site/*
......
......@@ -11,6 +11,55 @@
- Fixed transitive extra dependencies being removed when updating a specific dependency.
## [0.12.8] - 2018-11-13
### Fixed
- Fixed permission errors when adding/removing git dependencies on Windows.
- Fixed `Pool` not raising an exception when no package could be found.
- Fixed reading `bz2` source distribution.
- Fixed handling of arbitrary equals in `InstalledRepository`.
## [0.12.7] - 2018-11-08
### Fixed
- Fixed reading of some `setup.py` files.
- Fixed a `KeyError` when getting information for packages which require reading setup files.
- Fixed the building of wheels with C extensions and an `src` layout.
- Fixed extras being selected when resolving dependencies even when not required.
- Fixed performance issues when packaging projects if a lot of files were excluded.
- Fixed installation of files.
- Fixed extras not being retrieved for legacy repositories.
- Fixed invalid transitive constraints raising an error for legacy repositories.
## [0.12.6] - 2018-11-05
### Changed
- Poetry will now try to read, without executing, setup files (`setup.py` and/or `setup.cfg`) if the `egg_info` command fails when resolving dependencies.
### Fixed
- Fixed installation of directory dependencies.
- Fixed handling of dependencies with a `not in` marker operator.
- Fixed support for VCS dependencies.
- Fixed the `exclude` property not being respected if no VCS was available.
## [0.12.5] - 2018-10-26
### Fixed
- Fixed installation of Poetry git dependencies with a build system.
- Fixed possible errors when resolving dependencies for specific packages.
- Fixed handling of Python versions compatibility.
- Fixed the dependency resolver picking up unnecessary dependencies due to not using the `python_full_version` marker.
- Fixed the `Python-Requires` metadata being invalid for single Python versions.
## [0.12.4] - 2018-10-21
### Fixed
......@@ -548,7 +597,11 @@ Initial release
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.4...develop
[Unreleased]: https://github.com/sdispater/poetry/compare/0.12.8...master
[0.12.8]: https://github.com/sdispater/poetry/releases/tag/0.12.8
[0.12.7]: https://github.com/sdispater/poetry/releases/tag/0.12.7
[0.12.6]: https://github.com/sdispater/poetry/releases/tag/0.12.6
[0.12.5]: https://github.com/sdispater/poetry/releases/tag/0.12.5
[0.12.4]: https://github.com/sdispater/poetry/releases/tag/0.12.4
[0.12.3]: https://github.com/sdispater/poetry/releases/tag/0.12.3
[0.12.2]: https://github.com/sdispater/poetry/releases/tag/0.12.2
......
......@@ -153,7 +153,7 @@ There are some things we can notice here:
`poetry` will also detect if you are inside a virtualenv and install the packages accordingly.
So, `poetry` can be installed globally and used everywhere.
`poetry` also comes with a full fledged dependency resolution library, inspired by [Molinillo](https://github.com/CocoaPods/Molinillo).
`poetry` also comes with a full fledged dependency resolution library.
## Why?
......
......@@ -35,18 +35,26 @@ The `^` operator works very well with libraries following [semantic versioning](
## Is tox supported?
For now, you can use Poetry with [tox](https://tox.readthedocs.io/en/latest/) by using something similar to what is done in the [Pendulum](https://github.com/sdispater/pendulum/blob/master/tox.ini) package.
Yes. By using the [isolated builds](https://tox.readthedocs.io/en/latest/config.html#conf-isolated_build) `tox` provides,
you can use it in combination with the PEP 517 compliant build system provided by Poetry.
Minimal viable `tox.ini` configuration file looks like this:
So, in your `pyproject.toml` file add this section if does not already exists:
```toml
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```
And use a `tox.ini` configuration file similar to this:
```INI
[tox]
skipsdist = True
isolated_build = true
envlist = py27, py36
[testenv]
whitelist_externals = poetry
skip_install = true
commands =
poetry install -v
poetry run pytest tests/
......
......@@ -412,7 +412,7 @@ class Installer:
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
print("You may simply press the Enter key to leave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
......@@ -434,17 +434,6 @@ class Installer:
return True
def customize_install(self):
if not self._accept_all:
print("Before we start, please answer the following questions.")
print("You may simple press the Enter key to keave unchanged.")
modify_path = input("Modify PATH variable? ([y]/n) ") or "y"
if modify_path.lower() in {"n", "no"}:
self._modify_path = False
print("")
def ensure_home(self):
"""
Ensures that $POETRY_HOME exists or create it.
......@@ -701,7 +690,34 @@ class Installer:
self.set_windows_path_var(path)
def remove_from_unix_path(self):
pass
# Updating any profile we can on UNIX systems
export_string = self.get_export_string()
addition = "{}\n".format(export_string)
profiles = self.get_unix_profiles()
for profile in profiles:
if not os.path.exists(profile):
continue
with open(profile, "r") as f:
content = f.readlines()
if addition not in content:
continue
new_content = []
for line in content:
if line == addition:
if new_content and not new_content[-1].strip():
new_content = new_content[:-1]
continue
new_content.append(line)
with open(profile, "w") as f:
f.writelines(new_content)
def get_export_string(self):
path = POETRY_BIN.replace(os.getenv("HOME", ""), "$HOME")
......
__version__ = "0.12.4"
__version__ = "0.12.8"
......@@ -31,19 +31,22 @@ class DebugInfoCommand(Command):
env_python_version = ".".join(str(s) for s in env.version_info[:3])
self.output.title("Virtualenv")
self.output.listing(
[
"<info>Python</info>: <comment>{}</>".format(
env_python_version
),
"<info>Implementation</info>: <comment>{}</>".format(
env.python_implementation
),
"<info>Path</info>: <comment>{}</>".format(
env.path if env.is_venv() else "NA"
),
]
)
listing = [
"<info>Python</info>: <comment>{}</>".format(env_python_version),
"<info>Implementation</info>: <comment>{}</>".format(
env.python_implementation
),
"<info>Path</info>: <comment>{}</>".format(
env.path if env.is_venv() else "NA"
),
]
if env.is_venv():
listing.append(
"<info>Valid</info>: <{tag}>{is_valid}</{tag}>".format(
tag="comment" if env.is_sane() else "error", is_valid=env.is_sane()
)
)
self.output.listing(listing)
self.line("")
......
......@@ -8,10 +8,27 @@ class EnvCommand(Command):
super(EnvCommand, self).__init__()
def initialize(self, i, o):
from poetry.semver import parse_constraint
from poetry.utils.env import Env
super(EnvCommand, self).initialize(i, o)
# Checking compatibility of the current environment with
# the python dependency specified in pyproject.toml
current_env = Env.get()
supported_python = self.poetry.package.python_constraint
current_python = parse_constraint(
".".join(str(v) for v in current_env.version_info[:3])
)
if not supported_python.allows(current_python):
raise RuntimeError(
"The current Python version ({}) is not supported by the project ({})\n"
"Please activate a compatible Python version.".format(
current_python, self.poetry.package.python_versions
)
)
self._env = Env.create_venv(
o, self.poetry.package.name, cwd=self.poetry.file.parent
)
......
......@@ -37,6 +37,7 @@ The <info>init</info> command creates a basic <comment>pyproject.toml</> file in
def handle(self):
from poetry.layouts import layout
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.vcs.git import GitConfig
if (Path.cwd() / "pyproject.toml").exists():
......@@ -101,7 +102,16 @@ The <info>init</info> command creates a basic <comment>pyproject.toml</> file in
question.validator = self._validate_license
license = self.ask(question)
question = self.create_question("Compatible Python versions [*]: ", default="*")
current_env = Env.get()
default_python = "^{}".format(
".".join(str(v) for v in current_env.version_info[:2])
)
question = self.create_question(
"Compatible Python versions [<comment>{}</comment>]: ".format(
default_python
),
default=default_python,
)
python = self.ask(question)
self.line("")
......
......@@ -14,6 +14,7 @@ class NewCommand(Command):
def handle(self):
from poetry.layouts import layout
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.vcs.git import GitConfig
if self.option("src"):
......@@ -44,7 +45,17 @@ class NewCommand(Command):
if author_email:
author += " <{}>".format(author_email)
layout_ = layout_(name, "0.1.0", author=author, readme_format=readme_format)
current_env = Env.get()
default_python = "^{}".format(
".".join(str(v) for v in current_env.version_info[:2])
)
layout_ = layout_(
name,
"0.1.0",
author=author,
readme_format=readme_format,
python=default_python,
)
layout_.create(path)
self.line(
......
......@@ -24,12 +24,8 @@ lists all packages available."""
colors = ["green", "yellow", "cyan", "magenta", "blue"]
def handle(self):
from poetry.packages.constraints import (
parse_constraint as parse_generic_constraint,
)
from poetry.repositories.installed_repository import InstalledRepository
from poetry.semver import Version
from poetry.semver import parse_constraint
package = self.argument("package")
......@@ -172,7 +168,7 @@ lists all packages available."""
color = "yellow"
line += " <fg={}>{:{}}</>".format(
color, latest.pretty_version, latest_length
color, latest.full_pretty_version, latest_length
)
if self.option("outdated") and update_status == "up-to-date":
continue
......@@ -291,11 +287,17 @@ lists all packages available."""
self.set_style(color, color)
def find_latest_package(self, package):
from poetry.io import NullIO
from poetry.puzzle.provider import Provider
from poetry.version.version_selector import VersionSelector
# find the latest version allowed in this pool
if package.source_type == "git":
return
for dep in self.poetry.package.requires:
if dep.name == package.name and dep.is_vcs():
return Provider(
self.poetry.package, self.poetry.pool, NullIO()
).search_for_vcs(dep)[0]
name = package.name
selector = VersionSelector(self.poetry.pool)
......
import os
import shutil
import tempfile
from subprocess import CalledProcessError
from poetry.config import Config
from poetry.utils.helpers import get_http_basic_auth
from poetry.utils.helpers import safe_rmtree
try:
......@@ -25,7 +27,7 @@ class PipInstaller(BaseInstaller):
def install(self, package, update=False):
if package.source_type == "directory":
self.install_directory(package, update=update)
self.install_directory(package)
return
......@@ -92,6 +94,12 @@ class PipInstaller(BaseInstaller):
self.install(target, update=True)
def remove(self, package):
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
try:
self.run("uninstall", package.name, "-y")
except CalledProcessError as e:
......@@ -119,7 +127,7 @@ class PipInstaller(BaseInstaller):
else:
req = os.path.realpath(package.source_url)
if package.develop:
if package.develop and package.source_type == "directory":
req = ["-e", req]
return req
......@@ -143,7 +151,7 @@ class PipInstaller(BaseInstaller):
return name
def install_directory(self, package, update=False):
def install_directory(self, package):
from poetry.io import NullIO
from poetry.masonry.builder import SdistBuilder
from poetry.poetry import Poetry
......@@ -161,12 +169,16 @@ class PipInstaller(BaseInstaller):
pyproject = TomlFile(os.path.join(req, "pyproject.toml"))
has_poetry = False
has_build_system = False
if pyproject.exists():
pyproject_content = pyproject.read()
has_poetry = (
"tool" in pyproject_content and "poetry" in pyproject_content["tool"]
)
has_build_system = "build-system" in pyproject_content
# Even if there is a build system specified
# pip as of right now does not support it fully
# TODO: Check for pip version when proper PEP-517 support lands
# has_build_system = ("build-system" in pyproject_content)
setup = os.path.join(req, "setup.py")
has_setup = os.path.exists(setup)
......@@ -193,21 +205,22 @@ class PipInstaller(BaseInstaller):
def install_git(self, package):
from poetry.packages import Package
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
from poetry.vcs import Git
with temporary_directory() as tmp_dir:
tmp_dir = Path(tmp_dir)
src_dir = self._env.path / "src" / package.name
if src_dir.exists():
safe_rmtree(str(src_dir))
src_dir.parent.mkdir(exist_ok=True)
git = Git()
git.clone(package.source_url, tmp_dir)
git.checkout(package.source_reference, tmp_dir)
git = Git()
git.clone(package.source_url, src_dir)
git.checkout(package.source_reference, src_dir)
# Now we just need to install from the temporary directory
pkg = Package(package.name, package.version)
pkg.source_type = "directory"
pkg.source_url = str(tmp_dir)
pkg.develop = False
# Now we just need to install from the source directory
pkg = Package(package.name, package.version)
pkg.source_type = "directory"
pkg.source_url = str(src_dir)
pkg.develop = True
self.install_directory(pkg)
self.install_directory(pkg)
# -*- coding: utf-8 -*-
import os
import re
import shutil
import tempfile
from collections import defaultdict
from contextlib import contextmanager
from typing import Set
from typing import Union
from poetry.utils._compat import Path
from poetry.utils._compat import basestring
from poetry.utils._compat import lru_cache
from poetry.vcs import get_vcs
from ..metadata import Metadata
......@@ -39,35 +42,40 @@ class Builder(object):
def build(self):
raise NotImplementedError()
def find_excluded_files(self): # type: () -> list
@lru_cache(maxsize=None)
def find_excluded_files(self): # type: () -> Set[str]
# Checking VCS
vcs = get_vcs(self._path)
if not vcs:
return []
vcs_ignored_files = set()
else:
vcs_ignored_files = set(vcs.get_ignored_files())
explicitely_excluded = []
explicitely_excluded = set()
for excluded_glob in self._package.exclude:
for excluded in self._path.glob(excluded_glob):
explicitely_excluded.append(excluded)
explicitely_excluded.add(excluded.relative_to(self._path).as_posix())
ignored = vcs.get_ignored_files() + explicitely_excluded
result = []
ignored = vcs_ignored_files | explicitely_excluded
result = set()
for file in ignored:
try:
file = Path(file).absolute().relative_to(self._path)
except ValueError:
# Should only happen in tests
continue
result.append(file)
result.add(file)
# The list of excluded files might be big and we will do a lot
# containment check (x in excluded).
# Returning a set make those tests much much faster.
return result
def find_files_to_add(self, exclude_build=True): # type: () -> list
def is_excluded(self, filepath): # type: (Union[str, Path]) -> bool
if not isinstance(filepath, basestring):
filepath = filepath.as_posix()
return filepath in self.find_excluded_files()
def find_files_to_add(self, exclude_build=True): # type: (bool) -> list
"""
Finds all files to add to the tarball
"""
excluded = self.find_excluded_files()
to_add = []
for include in self._module.includes:
......@@ -80,7 +88,7 @@ class Builder(object):
file = file.relative_to(self._path)
if file in excluded and isinstance(include, PackageInclude):
if self.is_excluded(file) and isinstance(include, PackageInclude):
continue
if file.suffix == ".pyc":
......
......@@ -278,7 +278,7 @@ class SdistBuilder(Builder):
if not f.is_dir()
]
data = [e for e in data_elements if e not in excluded_files]
data = [e for e in data_elements if not self.is_excluded(e)]
if not data:
continue
......
......@@ -11,10 +11,10 @@ import zipfile
from base64 import urlsafe_b64encode
from io import StringIO
from typing import Set
from poetry.__version__ import __version__
from poetry.semver import parse_constraint
from poetry.utils._compat import Path
from ..utils.helpers import normalize_file_permissions
from ..utils.package_include import PackageInclude
......@@ -67,8 +67,8 @@ class WheelBuilder(Builder):
with zipfile.ZipFile(
os.fdopen(fd, "w+b"), mode="w", compression=zipfile.ZIP_DEFLATED
) as zip_file:
self._build()
self._copy_module(zip_file)
self._build(zip_file)
self._write_metadata(zip_file)
self._write_record(zip_file)
......@@ -79,7 +79,7 @@ class WheelBuilder(Builder):
self._io.writeln(" - Built <fg=cyan>{}</>".format(self.wheel_filename))
def _build(self):
def _build(self, wheel):
if self._package.build:
setup = self._path / "setup.py"
......@@ -103,9 +103,22 @@ class WheelBuilder(Builder):
return
lib = lib[0]
for pkg in lib.glob("*"):
shutil.rmtree(str(self._path / pkg.name))
shutil.copytree(str(pkg), str(self._path / pkg.name))
excluded = self.find_excluded_files()
for pkg in lib.glob("**/*"):
if pkg.is_dir() or pkg in excluded:
continue
rel_path = str(pkg.relative_to(lib))
if rel_path in wheel.namelist():
continue
self._io.writeln(
" - Adding: <comment>{}</comment>".format(rel_path),
verbosity=self._io.VERBOSITY_VERY_VERBOSE,
)
self._add_file(wheel, pkg, rel_path)
def _copy_module(self, wheel):
excluded = self.find_excluded_files()
......@@ -173,9 +186,9 @@ class WheelBuilder(Builder):
# RECORD itself is recorded with no hash or size
f.write(self.dist_info + "/RECORD,,\n")
def find_excluded_files(self): # type: () -> list
def find_excluded_files(self): # type: () -> Set
# Checking VCS
return []
return set()
@property
def dist_info(self): # type: () -> str
......
......@@ -39,7 +39,7 @@ class _Writer:
if required_python_version is not None:
buffer.append(
"The current supported Python versions are {}".format(
"The current project must support the following Python versions: {}".format(
required_python_version
)
)
......
......@@ -105,20 +105,21 @@ def dependency_from_pep_508(name):
op = ""
elif op == "!=":
version += ".*"
elif op == "in":
elif op in ("in", "not in"):
versions = []
for v in re.split("[ ,]+", version):
split = v.split(".")
if len(split) in [1, 2]:
split.append("*")
op = ""
op_ = "" if op == "in" else "!="
else:
op = "=="
op_ = "==" if op == "in" else "!="
versions.append(op + ".".join(split))
versions.append(op_ + ".".join(split))
glue = " || " if op == "in" else ", "
if versions:
ands.append(" || ".join(versions))
ands.append(glue.join(versions))
continue
......
import os
import pkginfo
from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.io import NullIO
from poetry.utils._compat import Path
from poetry.utils._compat import decode
from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile
from poetry.utils.env import Env
from .dependency import Dependency
......@@ -22,15 +15,13 @@ HEADER_ATTRS.update(
class DirectoryDependency(Dependency):
def __init__(
self,
name,
path, # type: Path
category="main", # type: str
optional=False, # type: bool
base=None, # type: Path
develop=True, # type: bool
):
from . import dependency_from_pep_508
from .package import Package
self._path = path
self._base = base
self._full_path = path
......@@ -46,7 +37,7 @@ class DirectoryDependency(Dependency):
if self._full_path.is_file():
raise ValueError("{} is a file, expected a directory".format(self._path))
# Checking content to dertermine actions
# Checking content to determine actions
setup = self._full_path / "setup.py"
pyproject = TomlFile(self._full_path / "pyproject.toml")
if pyproject.exists():
......@@ -62,67 +53,8 @@ class DirectoryDependency(Dependency):
)
)
if self._supports_poetry:
from poetry.poetry import Poetry
poetry = Poetry.create(self._full_path)
package = poetry.package
self._package = Package(package.pretty_name, package.version)
self._package.requires += package.requires
self._package.dev_requires += package.dev_requires
self._package.extras = package.extras
self._package.python_versions = package.python_versions
else:
# Execute egg_info
current_dir = os.getcwd()
os.chdir(str(self._full_path))
try:
cwd = base
venv = Env.create_venv(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
finally:
os.chdir(current_dir)
egg_info = list(self._full_path.glob("*.egg-info"))[0]
meta = pkginfo.UnpackedSDist(str(egg_info))
if meta.requires_dist:
reqs = list(meta.requires_dist)
else:
reqs = []
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(meta.name, meta.version)
package.description = meta.summary
for req in reqs:
package.requires.append(dependency_from_pep_508(req))
if meta.requires_python:
package.python_versions = meta.requires_python
if meta.platforms:
platforms = [p for p in meta.platforms if p.lower() != "unknown"]
if platforms:
package.platform = " || ".join(platforms)
self._package = package
self._package.source_type = "directory"
self._package.source_url = self._path.as_posix()
super(DirectoryDependency, self).__init__(
self._package.name,
self._package.version,
category=category,
optional=optional,
allows_prereleases=True,
name, "*", category=category, optional=optional, allows_prereleases=True
)
@property
......@@ -134,8 +66,8 @@ class DirectoryDependency(Dependency):
return self._full_path.resolve()
@property
def package(self):
return self._package
def base(self):
return self._base
@property
def develop(self):
......
import hashlib
import io
import pkginfo
from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
......@@ -19,6 +17,7 @@ HEADER_ATTRS.update(
class FileDependency(Dependency):
def __init__(
self,
name,
path, # type: Path
category="main", # type: str
optional=False, # type: bool
......@@ -37,18 +36,8 @@ class FileDependency(Dependency):
if self._full_path.is_dir():
raise ValueError("{} is a directory, expected a file".format(self._path))
if self._path.suffix == ".whl":
self._meta = pkginfo.Wheel(str(self._full_path))
else:
# Assume sdist
self._meta = pkginfo.SDist(str(self._full_path))
super(FileDependency, self).__init__(
self._meta.name,
self._meta.version,
category=category,
optional=optional,
allows_prereleases=True,
name, "*", category=category, optional=optional, allows_prereleases=True
)
@property
......@@ -59,10 +48,6 @@ class FileDependency(Dependency):
def full_path(self):
return self._full_path.resolve()
@property
def metadata(self):
return self._meta
def is_file(self):
return True
......
......@@ -73,6 +73,7 @@ class Package(object):
self._python_constraint = parse_constraint("*")
self._python_marker = AnyMarker()
self.platform = None
self.marker = AnyMarker()
self.root_dir = None
......@@ -168,19 +169,6 @@ class Package(object):
return self._python_marker
@property
def platform(self): # type: () -> str
return self._platform
@platform.setter
def platform(self, value): # type: (str) -> None
self._platform = value
self._platform_constraint = parse_generic_constraint(value)
@property
def platform_constraint(self):
return self._platform_constraint
@property
def license(self):
return self._license
......@@ -273,7 +261,7 @@ class Package(object):
file_path = Path(constraint["file"])
dependency = FileDependency(
file_path, category=category, base=self.root_dir
name, file_path, category=category, base=self.root_dir
)
elif "path" in constraint:
path = Path(constraint["path"])
......@@ -285,10 +273,15 @@ class Package(object):
if is_file:
dependency = FileDependency(
path, category=category, optional=optional, base=self.root_dir
name,
path,
category=category,
optional=optional,
base=self.root_dir,
)
else:
dependency = DirectoryDependency(
name,
path,
category=category,
optional=optional,
......
......@@ -146,6 +146,12 @@ def convert_markers(marker):
else:
variable, op, value = group
group_name = str(variable)
# python_full_version is equivalent to python_version
# for Poetry so we merge them
if group_name == "python_full_version":
group_name = "python_version"
if group_name not in requirements:
requirements[group_name] = []
......
......@@ -7,6 +7,7 @@ from typing import List
from poetry.mixology import resolve_version
from poetry.mixology.failure import SolveFailure
from poetry.packages import DependencyPackage
from poetry.packages import Package
from poetry.semver import parse_constraint
from poetry.version.markers import AnyMarker
......@@ -55,8 +56,33 @@ class Solver:
for pkg in self._installed.packages:
if package.name == pkg.name:
installed = True
# Checking version
if package.version != pkg.version:
if pkg.source_type == "git" and package.source_type == "git":
# Trying to find the currently installed version
for locked in self._locked.packages:
if (
locked.name == pkg.name
and locked.source_type == pkg.source_type
and locked.source_url == pkg.source_url
and locked.source_reference == pkg.source_reference
):
pkg = Package(pkg.name, locked.version)
pkg.source_type = "git"
pkg.source_url = locked.source_url
pkg.source_reference = locked.source_reference
break
if (
pkg.source_url != package.source_url
or pkg.source_reference != package.source_reference
):
operations.append(Update(pkg, package))
else:
operations.append(
Install(package).skip("Already installed")
)
elif package.version != pkg.version:
# Checking version
operations.append(Update(pkg, package))
else:
operations.append(Install(package).skip("Already installed"))
......
class RepositoryError(Exception):
pass
class PackageNotFound(Exception):
pass
import re
from poetry.packages import Package
from poetry.utils.env import Env
......@@ -17,7 +19,26 @@ class InstalledRepository(Repository):
freeze_output = env.run("pip", "freeze")
for line in freeze_output.split("\n"):
if "==" in line:
name, version = line.split("==")
name, version = re.split("={2,3}", line)
repo.add_package(Package(name, version, version))
elif line.startswith("-e "):
line = line[3:].strip()
if line.startswith("git+"):
url = line.lstrip("git+")
if "@" in url:
url, rev = url.rsplit("@", 1)
else:
rev = "master"
name = url.split("/")[-1].rstrip(".git")
if "#egg=" in rev:
rev, name = rev.split("#egg=")
package = Package(name, "0.0.0")
package.source_type = "git"
package.source_url = url
package.source_reference = rev
repo.add_package(package)
return repo
......@@ -41,6 +41,7 @@ from poetry.utils._compat import Path
from poetry.utils.helpers import canonicalize_name, get_http_basic_auth
from poetry.version.markers import InvalidMarker
from .exceptions import PackageNotFound
from .pypi_repository import PyPiRepository
......@@ -265,9 +266,17 @@ class LegacyRepository(PyPiRepository):
req = req.split(";")[0]
dependency = dependency_from_pep_508(req)
except ValueError:
# Likely unable to parse constraint so we skip it
self._log(
"Invalid constraint ({}) found in {}-{} dependencies, "
"skipping".format(req, package.name, package.version),
level="debug",
)
continue
if dependency.extras:
for extra in dependency.extras:
if dependency.in_extras:
for extra in dependency.in_extras:
if extra not in package.extras:
package.extras[extra] = []
......@@ -297,7 +306,7 @@ class LegacyRepository(PyPiRepository):
def _get_release_info(self, name, version): # type: (str, str) -> dict
page = self._get("/{}/".format(canonicalize_name(name).replace(".", "-")))
if page is None:
raise ValueError('No package named "{}"'.format(name))
raise PackageNotFound('No package named "{}"'.format(name))
data = {
"name": name,
......@@ -310,7 +319,7 @@ class LegacyRepository(PyPiRepository):
links = list(page.links_for_version(Version.parse(version)))
if not links:
raise ValueError(
raise PackageNotFound(
'No valid distribution links found for package: "{}" version: "{}"'.format(
name, version
)
......
from typing import List
from typing import Union
import poetry.packages
from .base_repository import BaseRepository
from .exceptions import PackageNotFound
from .repository import Repository
......@@ -65,7 +65,7 @@ class Pool(BaseRepository):
for repository in self._repositories:
try:
package = repository.package(name, version, extras=extras)
except ValueError:
except PackageNotFound:
continue
if package:
......@@ -73,7 +73,7 @@ class Pool(BaseRepository):
return package
raise PackageNotFound("Package [{}] not found.".format(name))
raise PackageNotFound("Package {} ({}) not found.".format(name, version))
def find_packages(
self, name, constraint=None, extras=None, allow_prereleases=False
......
......@@ -39,8 +39,10 @@ from poetry.utils._compat import to_str
from poetry.utils.helpers import parse_requires
from poetry.utils.helpers import temporary_directory
from poetry.utils.env import Env
from poetry.utils.setup_reader import SetupReader
from poetry.version.markers import InvalidMarker
from .exceptions import PackageNotFound
from .repository import Repository
......@@ -225,7 +227,7 @@ class PyPiRepository(Repository):
def _get_package_info(self, name): # type: (str) -> dict
data = self._get("pypi/{}/json".format(name))
if data is None:
raise ValueError("Package [{}] not found.".format(name))
raise PackageNotFound("Package [{}] not found.".format(name))
return data
......@@ -261,7 +263,7 @@ class PyPiRepository(Repository):
json_data = self._get("pypi/{}/{}/json".format(name, version))
if json_data is None:
raise ValueError("Package [{}] not found.".format(name))
raise PackageNotFound("Package [{}] not found.".format(name))
info = json_data["info"]
data = {
......@@ -460,6 +462,9 @@ class PyPiRepository(Repository):
else:
if suffix == ".bz2":
gz = BZ2File(str(filepath))
suffixes = filepath.suffixes
if len(suffixes) > 1 and suffixes[-2] == ".tar":
suffix = ".tar.bz2"
else:
gz = GzipFile(str(filepath))
suffix = ".tar.gz"
......@@ -501,45 +506,42 @@ class PyPiRepository(Repository):
return info
# Still nothing, assume no dependencies
# We could probably get them by executing
# python setup.py egg-info but I don't feel
# confortable executing a file just for the sake
# of getting dependencies.
return info
# Still nothing, try reading (without executing it)
# the setup.py file.
try:
info.update(self._inspect_sdist_with_setup(sdist_dir))
return info
except Exception as e:
self._log(
"An error occurred when reading setup.py or setup.cfg: {}".format(
str(e)
),
"warning",
)
return info
def _inspect_sdist_with_setup(self, sdist_dir):
info = {"requires_python": None, "requires_dist": None}
setup = sdist_dir / "setup.py"
if not setup.exists():
return info
result = SetupReader.read_from_directory(sdist_dir)
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
venv = Env.create_venv(NullIO())
if result["extras_require"]:
requires += "\n"
current_dir = os.getcwd()
os.chdir(sdist_dir.as_posix())
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
try:
venv.run("python", "setup.py", "egg_info")
for dep in deps:
requires += dep + "\n"
egg_info = list(sdist_dir.glob("**/*.egg-info"))[0]
meta = pkginfo.UnpackedSDist(str(egg_info))
if meta.requires_python:
info["requires_python"] = meta.requires_python
if meta.requires_dist:
info["requires_dist"] = list(meta.requires_dist)
else:
requires = egg_info / "requires.txt"
if requires.exists():
with requires.open() as f:
info["requires_dist"] = parse_requires(f.read())
except Exception:
pass
requires += "\n"
os.chdir(current_dir)
info["requires_dist"] = parse_requires(requires)
info["requires_python"] = result["python_requires"]
return info
......
import sys
try:
import pathlib2
from pathlib2 import Path
from functools32 import lru_cache
except ImportError:
from pathlib import Path
from functools import lru_cache
try: # Python 2
long = long
......@@ -21,6 +20,12 @@ PY35 = sys.version_info >= (3, 5)
PY36 = sys.version_info >= (3, 6)
if PY35:
from pathlib import Path
else:
from pathlib2 import Path
def decode(string, encodings=None):
if not PY2 and not isinstance(string, bytes):
return string
......
......@@ -302,8 +302,6 @@ class Env(object):
Return path to the given executable.
"""
bin_path = (self._bin_dir / bin).with_suffix(".exe" if self._is_windows else "")
if not bin_path.exists():
return bin
return str(bin_path)
......@@ -516,3 +514,19 @@ class NullEnv(SystemEnv):
def _bin(self, bin):
return bin
class MockEnv(NullEnv):
def __init__(self, version_info=(3, 7, 0), python_implementation="cpython"):
super(MockEnv, self).__init__()
self._version_info = version_info
self._python_implementation = python_implementation
@property
def version_info(self): # type: () -> Tuple[int]
return self._version_info
@property
def python_implementation(self): # type: () -> str
return self._python_implementation
import os
import re
import shutil
import stat
import tempfile
from contextlib import contextmanager
......@@ -89,3 +91,12 @@ def get_http_basic_auth(
return repo_auth["username"], repo_auth.get("password")
return None
def _on_rm_error(func, path, exc_info):
os.chmod(path, stat.S_IWRITE)
func(path)
def safe_rmtree(path):
shutil.rmtree(path, onerror=_on_rm_error)
......@@ -21,7 +21,10 @@ def format_python_constraint(constraint):
This helper will help in transforming
disjunctive constraint into proper constraint.
"""
if isinstance(constraint, Version) and constraint.precision < 3:
if isinstance(constraint, Version):
if constraint.precision >= 3:
return "=={}".format(str(constraint))
# Transform 3.6 or 3
if constraint.precision == 2:
# 3.6
......
[tool.poetry]
name = "poetry"
version = "0.12.4"
version = "0.12.8"
description = "Python dependency management and packaging made easy."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
......@@ -34,7 +34,7 @@ cachecontrol = { version = "^0.12.4", extras = ["filecache"] }
pkginfo = "^1.4"
html5lib = "^1.0"
shellingham = "^1.1"
tomlkit = "^0.4.4"
tomlkit = "^0.5.1"
# The typing module is not in the stdlib in Python 2.7 and 3.4
typing = { version = "^3.6", python = "~2.7 || ~3.4" }
......@@ -43,6 +43,8 @@ typing = { version = "^3.6", python = "~2.7 || ~3.4" }
pathlib2 = { version = "^2.3", python = "~2.7 || ~3.4" }
# Use virtualenv for Python 2.7 since venv does not exist
virtualenv = { version = "^16.0", python = "~2.7" }
# functools32 is needed for Python 2.7
functools32 = { version = "^3.2.3", python = "~2.7" }
[tool.poetry.dev-dependencies]
pytest = "^3.4"
......@@ -55,7 +57,7 @@ pygments-github-lexers = "^0.0.5"
black = { version = "^18.3-alpha.0", python = "^3.6" }
pre-commit = "^1.10"
tox = "^3.0"
pytest-sugar = "^0.9.1"
pytest-sugar = "^0.9.2"
[tool.poetry.scripts]
......
......@@ -51,6 +51,7 @@ class MakeReleaseCommand(Command):
from poetry.repositories.repository import Repository
from poetry.utils._compat import Path
from poetry.utils.helpers import temporary_directory
from poetry.vcs import get_vcs
project = Poetry.create(Path.cwd())
package = project.package
......@@ -60,11 +61,21 @@ class MakeReleaseCommand(Command):
pool = Pool()
pool.add_repository(project.locker.locked_repository(with_dev_reqs=True))
vcs = get_vcs(Path(__file__).parent)
if vcs:
vcs_excluded = [str(f) for f in vcs.get_ignored_files()]
else:
vcs_excluded = []
with temporary_directory() as tmp_dir:
# Copy poetry to tmp dir
poetry_dir = os.path.join(tmp_dir, "poetry")
shutil.copytree(
os.path.join(os.path.dirname(__file__), "poetry"), poetry_dir
os.path.join(os.path.dirname(__file__), "poetry"),
poetry_dir,
ignore=lambda dir_, names: set(vcs_excluded).intersection(
set([os.path.join(dir_, name) for name in names])
),
)
for version, python in sorted(pythons.items()):
self.line(
......@@ -102,9 +113,14 @@ class MakeReleaseCommand(Command):
continue
path = os.path.join(os.path.realpath(root), f)
relpath = os.path.relpath(
path, os.path.realpath(tmp_dir)
)
if relpath in vcs_excluded:
continue
tar_info = tar.gettarinfo(str(path), arcname=relpath)
if tar_info.isreg():
......
import pytest
import shutil
import tempfile
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from poetry.config import Config
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile
......@@ -11,3 +18,28 @@ def config(): # type: () -> Config
f.close()
return Config(TomlFile(f.name))
def mock_clone(_, source, dest):
# Checking source to determine which folder we need to copy
parts = urlparse.urlparse(source)
folder = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ parts.netloc
/ parts.path.lstrip("/").rstrip(".git")
)
shutil.rmtree(str(dest))
shutil.copytree(str(folder), str(dest))
@pytest.fixture(autouse=True)
def git_mock(mocker):
# Patch git module to not actually clone projects
mocker.patch("poetry.vcs.git.Git.clone", new=mock_clone)
mocker.patch("poetry.vcs.git.Git.checkout", new=lambda *_: None)
p = mocker.patch("poetry.vcs.git.Git.rev_parse")
p.return_value = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
import sys
from cleo.testers import CommandTester
from tests.helpers import get_dependency
from tests.helpers import get_package
......@@ -78,7 +75,6 @@ Resolving dependencies...
Resolution results:
- pendulum (2.0.3)
- cleo (0.6.5)
- demo (0.1.2)
"""
......
......@@ -148,7 +148,7 @@ description = ""
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "*"
python = "^3.7"
[tool.poetry.dev-dependencies]
"""
......
......@@ -17,6 +17,8 @@ from poetry.packages import Locker as BaseLocker
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.utils._compat import Path
from poetry.utils.env import Env
from poetry.utils.env import MockEnv
from poetry.utils.toml_file import TomlFile
......@@ -48,6 +50,8 @@ def installed():
@pytest.fixture(autouse=True)
def setup(mocker, installer, installed):
Env._env = MockEnv()
# Set Installer's installer
p = mocker.patch("poetry.installation.installer.Installer._get_installer")
p.return_value = installer
......@@ -74,6 +78,7 @@ def setup(mocker, installer, installed):
os.environ.clear()
os.environ.update(environ)
Env._env = None
class Application(BaseApplication):
......
# -*- coding: utf-8 -*-
from setuptools import setup
......@@ -13,7 +12,7 @@ kwargs = dict(
url="https://github.com/demo/demo",
packages=["demo"],
install_requires=["pendulum>=1.4.4"],
extras_require={"foo": ["cleo"]},
extras_require={"foo": ["cleo"], "bar": ["tomlkit"]},
)
......
# -*- coding: utf-8 -*-
import ast
import os
from setuptools import setup
def read_version():
with open(os.path.join(os.path.dirname(__file__), "demo", "__init__.py")) as f:
for line in f:
if line.startswith("__version__ = "):
return ast.literal_eval(line[len("__version__ = ") :].strip())
kwargs = dict(
name="demo",
license="MIT",
version=read_version(),
description="Demo project.",
author="Sébastien Eustace",
author_email="sebastien@eustace.io",
url="https://github.com/demo/demo",
packages=["demo"],
install_requires=["pendulum>=1.4.4"],
extras_require={"foo": ["cleo"]},
)
setup(**kwargs)
[[package]]
description = ""
category = "dev"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "dev"
name = "pendulum"
optional = true
python-versions = "*"
version = "1.4.4"
[[package]]
category = "main"
description = ""
name = "project-with-extras"
......@@ -40,7 +24,7 @@ python-versions = "*"
version = "1.2.3"
[package.dependencies]
project-with-extras = "1.2.3"
project-with-extras = "*"
[package.source]
reference = ""
......@@ -52,7 +36,5 @@ content-hash = "123456789"
python-versions = "*"
[metadata.hashes]
cachy = []
project-with-extras = []
project-with-transitive-directory-dependencies = []
pendulum = []
[[package]]
description = ""
category = "main"
name = "cachy"
optional = true
python-versions = "*"
version = "0.2.0"
[[package]]
description = ""
category = "main"
name = "pendulum"
optional = false
python-versions = "*"
......@@ -39,6 +31,5 @@ content-hash = "123456789"
python-versions = "*"
[metadata.hashes]
cachy = []
project-with-extras = []
pendulum = []
[[package]]
name = "demo"
version = "0.1.0"
description = "Description"
description = ""
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
......@@ -12,7 +12,7 @@ reference = ""
url = "tests/fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
[package.dependencies]
pendulum = ">=1.4.0.0,<2.0.0.0"
pendulum = ">=1.4.4"
[[package]]
name = "pendulum"
......@@ -27,5 +27,5 @@ python-versions = "*"
content-hash = "123456789"
[metadata.hashes]
demo = ["373a7e4bb99653541f35e52738f1b8a8e889c12e5f8b93a88c757d948b0cbe89"]
demo = ["70e704135718fffbcbf61ed1fc45933cfd86951a744b681000eaaa75da31f17a"]
pendulum = []
......@@ -77,21 +77,12 @@ class Locker(BaseLocker):
self._written_data = data
@pytest.fixture(autouse=True)
def setup():
# Mock python version and platform to get reliable tests
original_platform = sys.platform
sys.platform = "darwin"
yield
sys.platform = original_platform
@pytest.fixture()
def package():
return ProjectPackage("root", "1.0")
p = ProjectPackage("root", "1.0")
p.root_dir = Path.cwd()
return p
@pytest.fixture()
......@@ -472,8 +463,10 @@ def test_run_with_optional_and_python_restricted_dependencies(
def test_run_with_optional_and_platform_restricted_dependencies(
installer, locker, repo, package
installer, locker, repo, package, mocker
):
mocker.patch("sys.platform", "darwin")
package_a = get_package("A", "1.0")
package_b = get_package("B", "1.1")
package_c12 = get_package("C", "1.2")
......@@ -678,11 +671,12 @@ def test_run_installs_with_local_file(installer, locker, repo, package):
def test_run_installs_with_local_poetry_directory_and_extras(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_extras/")
package.add_dependency("demo", {"path": str(file_path), "extras": ["extras_a"]})
file_path = Path("tests/fixtures/project_with_extras")
package.add_dependency(
"project-with-extras", {"path": str(file_path), "extras": ["extras_a"]}
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
installer.run()
......@@ -699,7 +693,9 @@ def test_run_installs_with_local_poetry_directory_transitive(
file_path = Path(
"tests/fixtures/directory/project_with_transitive_directory_dependencies/"
)
package.add_dependency("demo", {"path": str(file_path)})
package.add_dependency(
"project-with-transitive-directory-dependencies", {"path": str(file_path)}
)
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
......@@ -717,7 +713,7 @@ def test_run_installs_with_local_setuptools_directory(
installer, locker, repo, package, tmpdir
):
file_path = Path("tests/fixtures/project_with_setup/")
package.add_dependency("demo", {"path": str(file_path)})
package.add_dependency("my-package", {"path": str(file_path)})
repo.add_package(get_package("pendulum", "1.4.4"))
repo.add_package(get_package("cachy", "0.2.0"))
......
......@@ -20,6 +20,10 @@ classifiers = [
"Topic :: Software Development :: Libraries :: Python Modules"
]
exclude = [
"**/*.xml"
]
# Requirements
[tool.poetry.dependencies]
python = "^3.6"
......
[tool.poetry]
name = "single-python"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
[tool.poetry.dependencies]
python = "2.7.15"
from distutils.core import Extension
extensions = [Extension("extended.extended", ["src/extended/extended.c"])]
def build(setup_kwargs):
setup_kwargs.update({"ext_modules": extensions})
[tool.poetry]
name = "extended"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.rst"
homepage = "https://poetry.eustace.io/"
build = "build.py"
#include <Python.h>
static PyObject *hello(PyObject *self) {
return PyUnicode_FromString("Hello");
}
static PyMethodDef module_methods[] = {
{
"hello",
(PyCFunction) hello,
NULL,
PyDoc_STR("Say hello.")
},
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"extended",
NULL,
-1,
module_methods,
NULL,
NULL,
NULL,
NULL,
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_extended(void)
#else
init_extended(void)
#endif
{
PyObject *module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule3("extended", module_methods, NULL);
#endif
if (module == NULL)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
......@@ -7,6 +7,7 @@ import shutil
import sys
import tarfile
import zipfile
import tempfile
from poetry import __version__
from poetry.io import NullIO
......@@ -83,6 +84,67 @@ $""".format(
)
is not None
)
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
finally:
zip.close()
@pytest.mark.skipif(
sys.platform == "win32" and sys.version_info <= (3, 4),
reason="Disable test on Windows for Python <=3.4",
)
def test_wheel_c_extension_src_layout():
module_path = fixtures_dir / "src_extended"
builder = CompleteBuilder(
Poetry.create(module_path), NullEnv(execute=True), NullIO()
)
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
whl = list((module_path / "dist").glob("extended-0.1-cp*-cp*-*.whl"))[0]
assert whl.exists()
zip = zipfile.ZipFile(str(whl))
has_compiled_extension = False
for name in zip.namelist():
if name.startswith("extended/extended") and name.endswith((".so", ".pyd")):
has_compiled_extension = True
assert has_compiled_extension
try:
wheel_data = decode(zip.read("extended-0.1.dist-info/WHEEL"))
assert (
re.match(
"""(?m)^\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: false
Tag: cp[23]\\d-cp[23]\\dmu?-.+
$""".format(
__version__
),
wheel_data,
)
is not None
)
records = decode(zip.read("extended-0.1.dist-info/RECORD"))
assert re.search(r"\s+extended/extended.*\.(so|pyd)", records) is not None
finally:
zip.close()
......@@ -164,6 +226,105 @@ My Package
zip.close()
def test_complete_no_vcs():
# Copy the complete fixtures dir to a temporary directory
module_path = fixtures_dir / "complete"
temporary_dir = Path(tempfile.mkdtemp()) / "complete"
shutil.copytree(module_path.as_posix(), temporary_dir.as_posix())
builder = CompleteBuilder(
Poetry.create(temporary_dir), NullEnv(execute=True), NullIO()
)
builder.build()
whl = temporary_dir / "dist" / "my_package-1.2.3-py3-none-any.whl"
assert whl.exists()
zip = zipfile.ZipFile(str(whl))
# Check the zipped file to be sure that included and excluded files are
# correctly taken account of without vcs
expected_name_list = [
"my_package/__init__.py",
"my_package/data1/test.json",
"my_package/sub_pkg1/__init__.py",
"my_package/sub_pkg2/__init__.py",
"my_package/sub_pkg2/data2/data.json",
"my_package-1.2.3.dist-info/entry_points.txt",
"my_package-1.2.3.dist-info/LICENSE",
"my_package-1.2.3.dist-info/WHEEL",
"my_package-1.2.3.dist-info/METADATA",
"my_package-1.2.3.dist-info/RECORD",
]
assert sorted(zip.namelist()) == sorted(expected_name_list)
try:
entry_points = zip.read("my_package-1.2.3.dist-info/entry_points.txt")
assert (
decode(entry_points.decode())
== """\
[console_scripts]
extra-script=my_package.extra:main[time]
my-2nd-script=my_package:main2
my-script=my_package:main
"""
)
wheel_data = decode(zip.read("my_package-1.2.3.dist-info/WHEEL"))
assert (
wheel_data
== """\
Wheel-Version: 1.0
Generator: poetry {}
Root-Is-Purelib: true
Tag: py3-none-any
""".format(
__version__
)
)
wheel_data = decode(zip.read("my_package-1.2.3.dist-info/METADATA"))
assert (
wheel_data
== """\
Metadata-Version: 2.1
Name: my-package
Version: 1.2.3
Summary: Some description.
Home-page: https://poetry.eustace.io/
License: MIT
Keywords: packaging,dependency,poetry
Author: Sébastien Eustace
Author-email: sebastien@eustace.io
Requires-Python: >=3.6,<4.0
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Provides-Extra: time
Requires-Dist: cachy[msgpack] (>=0.2.0,<0.3.0)
Requires-Dist: cleo (>=0.6,<0.7)
Requires-Dist: pendulum (>=1.4,<2.0); extra == "time"
Project-URL: Documentation, https://poetry.eustace.io/docs
Project-URL: Repository, https://github.com/sdispater/poetry
Description-Content-Type: text/x-rst
My Package
==========
"""
)
finally:
zip.close()
def test_module_src():
module_path = fixtures_dir / "source_file"
builder = CompleteBuilder(
......
......@@ -309,6 +309,21 @@ def test_with_c_extensions():
assert "extended-0.1/extended/extended.c" in tar.getnames()
def test_with_c_extensions_src_layout():
poetry = Poetry.create(project("src_extended"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
builder.build()
sdist = fixtures_dir / "src_extended" / "dist" / "extended-0.1.tar.gz"
assert sdist.exists()
with tarfile.open(str(sdist), "r") as tar:
assert "extended-0.1/build.py" in tar.getnames()
assert "extended-0.1/src/extended/extended.c" in tar.getnames()
def test_with_src_module_file():
poetry = Poetry.create(project("source_file"))
......@@ -423,14 +438,18 @@ def test_default_with_excluded_data(mocker):
# Patch git module to return specific excluded files
p = mocker.patch("poetry.vcs.git.Git.get_ignored_files")
p.return_value = [
str(
Path(__file__).parent
/ "fixtures"
/ "default_with_excluded_data"
/ "my_package"
/ "data"
/ "sub_data"
/ "data2.txt"
(
(
Path(__file__).parent
/ "fixtures"
/ "default_with_excluded_data"
/ "my_package"
/ "data"
/ "sub_data"
/ "data2.txt"
)
.relative_to(project("default_with_excluded_data"))
.as_posix()
)
]
poetry = Poetry.create(project("default_with_excluded_data"))
......@@ -471,7 +490,7 @@ def test_default_with_excluded_data(mocker):
assert "my-package-1.2.3/PKG-INFO" in names
def test_proper_python_requires_if_single_version_specified():
def test_proper_python_requires_if_two_digits_precision_version_specified():
poetry = Poetry.create(project("simple_version"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
......@@ -480,3 +499,14 @@ def test_proper_python_requires_if_single_version_specified():
parsed = p.parsestr(to_str(pkg_info))
assert parsed["Requires-Python"] == ">=3.6,<3.7"
def test_proper_python_requires_if_three_digits_precision_version_specified():
poetry = Poetry.create(project("single_python"))
builder = SdistBuilder(poetry, NullEnv(), NullIO())
pkg_info = builder.build_pkg_info()
p = Parser()
parsed = p.parsestr(to_str(pkg_info))
assert parsed["Requires-Python"] == "==2.7.15"
......@@ -8,7 +8,7 @@ def test_dependency_does_not_match_root_python_constraint(root, provider, repo):
add_to_repo(repo, "foo", "1.0.0", python="<3.5")
error = """The current supported Python versions are ^3.6
error = """The current project must support the following Python versions: ^3.6
Because no versions of foo match !=1.0.0
and foo (1.0.0) requires Python <3.5, foo is forbidden.
......
import pytest
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.utils._compat import Path
from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
from subprocess import CalledProcessError
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
raise EnvCommandError(CalledProcessError(1, "python", output=""))
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "git" / "github.com" / "demo"
def test_directory_dependency_must_exist():
with pytest.raises(ValueError):
DirectoryDependency("demo", DIST_PATH / "invalid")
......@@ -6,35 +6,11 @@ from poetry.utils._compat import Path
DIST_PATH = Path(__file__).parent.parent / "fixtures" / "distributions"
def test_file_dependency_wheel():
dependency = FileDependency(DIST_PATH / "demo-0.1.0-py2.py3-none-any.whl")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_sdist():
dependency = FileDependency(DIST_PATH / "demo-0.1.0.tar.gz")
assert dependency.is_file()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.0"
assert dependency.python_versions == "*"
meta = dependency.metadata
assert meta.requires_dist == ["pendulum (>=1.4.0.0,<2.0.0.0)"]
def test_file_dependency_wrong_path():
with pytest.raises(ValueError):
FileDependency(DIST_PATH / "demo-0.2.0.tar.gz")
FileDependency("demo", DIST_PATH / "demo-0.2.0.tar.gz")
def test_file_dependency_dir():
with pytest.raises(ValueError):
FileDependency(DIST_PATH)
FileDependency("demo", DIST_PATH)
......@@ -152,3 +152,20 @@ def test_dependency_from_pep_508_with_python_version_union_of_multi():
'python_version >= "2.7" and python_version < "2.8" '
'or python_version >= "3.4" and python_version < "3.5"'
)
def test_dependency_from_pep_508_with_not_in_op_marker():
name = (
"jinja2 (>=2.7,<2.8)"
'; python_version not in "3.0,3.1,3.2" and extra == "export"'
)
dep = dependency_from_pep_508(name)
assert dep.name == "jinja2"
assert str(dep.constraint) == ">=2.7,<2.8"
assert dep.in_extras == ["export"]
assert dep.python_versions == "!=3.0.*, !=3.1.*, !=3.2.*"
assert (
str(dep.marker) == 'python_version not in "3.0,3.1,3.2" and extra == "export"'
)
......@@ -10,6 +10,7 @@ from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.puzzle import Solver
from poetry.puzzle.exceptions import SolverProblemError
from poetry.utils._compat import Path
from poetry.version.markers import parse_marker
from tests.helpers import get_dependency
......@@ -913,7 +914,6 @@ def test_solver_can_resolve_git_dependencies(solver, repo, package):
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": cleo},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
......@@ -1192,3 +1192,299 @@ def test_solver_should_not_resolve_prerelease_version_if_not_requested(
with pytest.raises(SolverProblemError):
solver.solve()
def test_solver_ignores_dependencies_with_incompatible_python_full_version_marker(
solver, repo, package
):
package.python_versions = "^3.6"
package.add_dependency("A", "^1.0")
package.add_dependency("B", "^2.0")
package_a = get_package("A", "1.0.0")
package_a.requires.append(
dependency_from_pep_508(
'B (<2.0); platform_python_implementation == "PyPy" and python_full_version < "2.7.9"'
)
)
package_b200 = get_package("B", "2.0.0")
package_b100 = get_package("B", "1.0.0")
repo.add_package(package_a)
repo.add_package(package_b100)
repo.add_package(package_b200)
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": package_a},
{"job": "install", "package": package_b200},
],
)
def test_solver_git_dependencies_update(solver, repo, package, installed):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
demo = get_package("demo", "0.1.2")
demo.source_type = "git"
demo.source_url = "https://github.com/demo/demo.git"
demo.source_reference = "123456"
installed.add_package(demo)
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{
"job": "update",
"from": get_package("demo", "0.1.2"),
"to": get_package("demo", "0.1.2"),
},
],
)
op = ops[1]
assert op.job_type == "update"
assert op.package.source_type == "git"
assert op.package.source_reference.startswith("9cf87a2")
assert op.initial_package.source_reference == "123456"
def test_solver_git_dependencies_update_skipped(solver, repo, package, installed):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
demo = get_package("demo", "0.1.2")
demo.source_type = "git"
demo.source_url = "https://github.com/demo/demo.git"
demo.source_reference = "9cf87a285a2d3fbb0b9fa621997b3acc3631ed24"
installed.add_package(demo)
package.add_dependency("demo", {"git": "https://github.com/demo/demo.git"})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{
"job": "install",
"package": get_package("demo", "0.1.2"),
"skipped": True,
},
],
)
def test_solver_can_resolve_directory_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_directory_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "git"
/ "github.com"
/ "demo"
/ "demo"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.2")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.2"
assert op.package.source_type == "directory"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_sdist_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0.tar.gz"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
repo.add_package(pendulum)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
).as_posix()
package.add_dependency("demo", {"path": path})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[1]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
def test_solver_can_resolve_wheel_dependencies_with_extras(solver, repo, package):
pendulum = get_package("pendulum", "2.0.3")
cleo = get_package("cleo", "1.0.0")
repo.add_package(pendulum)
repo.add_package(cleo)
path = (
Path(__file__).parent.parent
/ "fixtures"
/ "distributions"
/ "demo-0.1.0-py2.py3-none-any.whl"
).as_posix()
package.add_dependency("demo", {"path": path, "extras": ["foo"]})
ops = solver.solve()
check_solver_result(
ops,
[
{"job": "install", "package": cleo},
{"job": "install", "package": pendulum},
{"job": "install", "package": get_package("demo", "0.1.0")},
],
)
op = ops[2]
assert op.package.name == "demo"
assert op.package.version.text == "0.1.0"
assert op.package.source_type == "file"
assert op.package.source_url == path
<!DOCTYPE html>
<html>
<head>
<title>Links for jupyter</title>
</head>
<body>
<h1>Links for jupyter</h1>
<a href="https://files.pythonhosted.org/packages/c9/a9/371d0b8fe37dd231cf4b2cff0a9f0f25e98f3a73c3771742444be27f2944/jupyter-1.0.0.tar.gz#sha256=d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f">jupyter-1.0.0.tar.gz</a><br/>
</body>
</html>
<!--SERIAL 1673841-->
<!DOCTYPE html>
<html>
<head>
<title>Links for python-language-server</title>
</head>
<body>
<h1>Links for python-language-server</h1>
<a href="https://files.pythonhosted.org/packages/9f/1d/2817b5dc2dd77f897410a11c1c9e2a6d96b3273c53d4219dd9edab7882af/python-language-server-0.21.2.tar.gz#sha256=fa9162acb1402b807132d7288b7f521db2bd666d63505d8a4d9464d4b8488c52">python-language-server-0.21.2.tar.gz</a><br/>
</body>
</html>
<!--SERIAL 4245719-->
{
"info": {
"author": "Mike Bayer",
"author_email": "mike_mp@zzzcomputing.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Database :: Front-Ends"
],
"description": "",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://www.sqlalchemy.org",
"keywords": "",
"license": "MIT License",
"maintainer": "",
"maintainer_email": "",
"name": "SQLAlchemy",
"package_url": "https://pypi.org/project/SQLAlchemy/",
"platform": "",
"project_url": "https://pypi.org/project/SQLAlchemy/",
"project_urls": {
"Homepage": "http://www.sqlalchemy.org"
},
"release_url": "https://pypi.org/project/SQLAlchemy/1.2.12/",
"requires_dist": null,
"requires_python": "",
"summary": "Database Abstraction Library",
"version": "1.2.12"
},
"last_serial": 4289618,
"releases": {
"1.2.12": [
{
"comment_text": "",
"digests": {
"md5": "3baca105a1e49798d6bc99eb2738cb3b",
"sha256": "c5951d9ef1d5404ed04bae5a16b60a0779087378928f997a294d1229c6ca4d3e"
},
"downloads": -1,
"filename": "SQLAlchemy-1.2.12.tar.gz",
"has_sig": true,
"md5_digest": "3baca105a1e49798d6bc99eb2738cb3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5634807,
"upload_time": "2018-09-19T18:14:55",
"url": "https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "3baca105a1e49798d6bc99eb2738cb3b",
"sha256": "c5951d9ef1d5404ed04bae5a16b60a0779087378928f997a294d1229c6ca4d3e"
},
"downloads": -1,
"filename": "SQLAlchemy-1.2.12.tar.gz",
"has_sig": true,
"md5_digest": "3baca105a1e49798d6bc99eb2738cb3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 5634807,
"upload_time": "2018-09-19T18:14:55",
"url": "https://files.pythonhosted.org/packages/25/c9/b0552098cee325425a61efdf380c51b5c721e459081c85bbb860f501c091/SQLAlchemy-1.2.12.tar.gz"
}
]
}
{
"info": {
"author": "Twisted Matrix Laboratories",
"author_email": "twisted-python@twistedmatrix.com",
"bugtrack_url": null,
"classifiers": [
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7"
],
"description": "description",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://twistedmatrix.com/",
"keywords": "",
"license": "MIT",
"maintainer": "Glyph Lefkowitz",
"maintainer_email": "glyph@twistedmatrix.com",
"name": "Twisted",
"package_url": "https://pypi.org/project/Twisted/",
"platform": "",
"project_url": "https://pypi.org/project/Twisted/",
"project_urls": {
"Homepage": "http://twistedmatrix.com/"
},
"release_url": "https://pypi.org/project/Twisted/18.9.0/",
"requires_dist": null,
"requires_python": "",
"summary": "An asynchronous networking framework written in Python",
"version": "18.9.0"
},
"last_serial": 4376865,
"releases": {
"18.9.0": [
{
"comment_text": "",
"digests": {
"md5": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"sha256": "294be2c6bf84ae776df2fc98e7af7d6537e1c5e60a46d33c3ce2a197677da395"
},
"downloads": -1,
"filename": "Twisted-18.9.0.tar.bz2",
"has_sig": false,
"md5_digest": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3088398,
"upload_time": "2018-10-15T09:11:22",
"url": "https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"sha256": "294be2c6bf84ae776df2fc98e7af7d6537e1c5e60a46d33c3ce2a197677da395"
},
"downloads": -1,
"filename": "Twisted-18.9.0.tar.bz2",
"has_sig": false,
"md5_digest": "20fe2ec156e6e45b0b0d2ff06d9e828f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3088398,
"upload_time": "2018-10-15T09:11:22",
"url": "https://files.pythonhosted.org/packages/5d/0e/a72d85a55761c2c3ff1cb968143a2fd5f360220779ed90e0fadf4106d4f2/Twisted-18.9.0.tar.bz2"
}
]
}
from poetry.repositories.installed_repository import InstalledRepository
from poetry.utils.env import MockEnv as BaseMockEnv
FREEZE_RESULTS = """cleo==0.6.8
-e git+https://github.com/sdispater/pendulum.git@bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6#egg=pendulum
orator===0.9.8
"""
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
if bin == "pip" and args[0] == "freeze":
return FREEZE_RESULTS
super(MockEnv, self).run(bin, *args)
def test_load():
repository = InstalledRepository.load(MockEnv())
assert len(repository.packages) == 3
cleo = repository.packages[0]
assert cleo.name == "cleo"
assert cleo.version.text == "0.6.8"
pendulum = repository.packages[1]
assert pendulum.name == "pendulum"
assert pendulum.version.text == "0.0.0"
assert pendulum.source_type == "git"
assert pendulum.source_url == "https://github.com/sdispater/pendulum.git"
assert pendulum.source_reference == "bb058f6b78b2d28ef5d9a5e759cfa179a1a713d6"
orator = repository.packages[2]
assert orator.name == "orator"
assert orator.version.text == "0.9.8"
import pytest
import shutil
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from poetry.packages import Dependency
from poetry.repositories.exceptions import PackageNotFound
from poetry.repositories.legacy_repository import LegacyRepository
from poetry.repositories.legacy_repository import Page
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
......@@ -23,6 +32,12 @@ class MockRepository(LegacyRepository):
with fixture.open() as f:
return Page(self._url + endpoint, f.read(), {})
def _download(self, url, dest):
filename = urlparse.urlparse(url).path.rsplit("/")[-1]
filepath = self.FIXTURES.parent / "pypi.org" / "dists" / filename
shutil.copyfile(str(filepath), dest)
def test_page_relative_links_path_are_correct():
repo = MockRepository()
......@@ -52,8 +67,64 @@ def test_sdist_format_support():
assert bz2_links[0].filename == "poetry-0.1.1.tar.bz2"
def test_missing_version(mocker):
def test_missing_version():
repo = MockRepository()
with pytest.raises(ValueError):
with pytest.raises(PackageNotFound):
repo._get_release_info("missing_version", "1.1.0")
def test_get_package_information_fallback_read_setup():
repo = MockRepository()
package = repo.package("jupyter", "1.0.0")
assert package.name == "jupyter"
assert package.version.text == "1.0.0"
assert (
package.description
== "Jupyter metapackage. Install all the Jupyter components in one go."
)
if PY35:
assert package.requires == [
Dependency("notebook", "*"),
Dependency("qtconsole", "*"),
Dependency("jupyter-console", "*"),
Dependency("nbconvert", "*"),
Dependency("ipykernel", "*"),
Dependency("ipywidgets", "*"),
]
def test_get_package_information_skips_dependencies_with_invalid_constraints():
repo = MockRepository()
package = repo.package("python-language-server", "0.21.2")
assert package.name == "python-language-server"
assert package.version.text == "0.21.2"
assert (
package.description == "Python Language Server for the Language Server Protocol"
)
assert sorted(package.requires, key=lambda r: r.name) == [
Dependency("configparser", "*"),
Dependency("future", ">=0.14.0"),
Dependency("futures", "*"),
Dependency("jedi", ">=0.12"),
Dependency("pluggy", "*"),
Dependency("python-jsonrpc-server", "*"),
]
all_extra = package.extras["all"]
# rope>-0.10.5 should be discarded
assert sorted(all_extra, key=lambda r: r.name) == [
Dependency("autopep8", "*"),
Dependency("mccabe", "*"),
Dependency("pycodestyle", "*"),
Dependency("pydocstyle", ">=2.0.0"),
Dependency("pyflakes", ">=1.6.0"),
Dependency("yapf", "*"),
]
import pytest
from poetry.repositories import Pool
from poetry.repositories import Repository
from poetry.repositories.exceptions import PackageNotFound
def test_pool_raises_package_not_found_when_no_package_is_found():
pool = Pool()
pool.add_repository(Repository())
with pytest.raises(PackageNotFound):
pool.package("foo", "1.0.0")
import json
import pytest
import shutil
from poetry.packages import Dependency
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import PY35
from poetry.utils._compat import Path
......@@ -104,3 +107,62 @@ def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found():
dep = package.requires[0]
assert dep.name == "futures"
assert dep.python_versions == "~2.7"
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_fallback_can_read_setup_to_get_dependencies():
repo = MockRepository(fallback=True)
package = repo.package("sqlalchemy", "1.2.12")
assert package.name == "sqlalchemy"
assert len(package.requires) == 0
assert package.extras == {
"mssql_pymssql": [Dependency("pymssql", "*")],
"mssql_pyodbc": [Dependency("pyodbc", "*")],
"mysql": [Dependency("mysqlclient", "*")],
"oracle": [Dependency("cx_oracle", "*")],
"postgresql": [Dependency("psycopg2", "*")],
"postgresql_pg8000": [Dependency("pg8000", "*")],
"postgresql_psycopg2binary": [Dependency("psycopg2-binary", "*")],
"postgresql_psycopg2cffi": [Dependency("psycopg2cffi", "*")],
"pymysql": [Dependency("pymysql", "*")],
}
def test_pypi_repository_supports_reading_bz2_files():
repo = MockRepository(fallback=True)
package = repo.package("twisted", "18.9.0")
assert package.name == "twisted"
assert sorted(package.requires, key=lambda r: r.name) == [
Dependency("attrs", ">=17.4.0"),
Dependency("Automat", ">=0.3.0"),
Dependency("constantly", ">=15.1"),
Dependency("hyperlink", ">=17.1.1"),
Dependency("incremental", ">=16.10.1"),
Dependency("PyHamcrest", ">=1.9.0"),
Dependency("zope.interface", ">=4.4.2"),
]
expected_extras = {
"all_non_platform": [
Dependency("appdirs", ">=1.4.0"),
Dependency("cryptography", ">=1.5"),
Dependency("h2", ">=3.0,<4.0"),
Dependency("idna", ">=0.6,!=2.3"),
Dependency("priority", ">=1.1.0,<2.0"),
Dependency("pyasn1", "*"),
Dependency("pyopenssl", ">=16.0.0"),
Dependency("pyserial", ">=3.0"),
Dependency("service_identity", "*"),
Dependency("soappy", "*"),
]
}
for name, deps in expected_extras.items():
assert expected_extras[name] == sorted(
package.extras[name], key=lambda r: r.name
)
......@@ -64,25 +64,21 @@ def test_poetry():
assert demo.is_file()
assert not demo.is_vcs()
assert demo.name == "demo"
assert demo.pretty_constraint == "0.1.0"
assert demo.pretty_constraint == "*"
demo = dependencies["my-package"]
assert not demo.is_file()
assert demo.is_directory()
assert not demo.is_vcs()
assert demo.name == "my-package"
assert demo.pretty_constraint == "0.1.2"
assert demo.package.requires[0].name == "pendulum"
assert demo.package.requires[1].name == "cachy"
assert demo.package.requires[1].extras == ["msgpack"]
assert demo.pretty_constraint == "*"
simple_project = dependencies["simple-project"]
assert not simple_project.is_file()
assert simple_project.is_directory()
assert not simple_project.is_vcs()
assert simple_project.name == "simple-project"
assert simple_project.pretty_constraint == "1.2.3"
assert simple_project.package.requires == []
assert simple_project.pretty_constraint == "*"
assert "db" in package.extras
......
# Note: this requirements.txt file is used to specify what dependencies are
# needed to make the package run rather than for deployment of a tested set of
# packages. Thus, this should be the loosest set possible (only required
# packages, not optional ones, and with the widest range of versions that could
# be suitable)
jinja2
PyYAML
paramiko
cryptography
setuptools
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
import re
from collections import OrderedDict
from setuptools import setup
with io.open("README.rst", "rt", encoding="utf8") as f:
readme = f.read()
with io.open("flask/__init__.py", "rt", encoding="utf8") as f:
version = re.search(r"__version__ = \'(.*?)\'", f.read()).group(1)
setup(
name="Flask",
version=version,
url="https://www.palletsprojects.com/p/flask/",
project_urls=OrderedDict(
(
("Documentation", "http://flask.pocoo.org/docs/"),
("Code", "https://github.com/pallets/flask"),
("Issue tracker", "https://github.com/pallets/flask/issues"),
)
),
license="BSD",
author="Armin Ronacher",
author_email="armin.ronacher@active-4.com",
maintainer="Pallets team",
maintainer_email="contact@palletsprojects.com",
description="A simple framework for building complex web applications.",
long_description=readme,
packages=["flask", "flask.json"],
include_package_data=True,
zip_safe=False,
platforms="any",
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*",
install_requires=[
"Werkzeug>=0.14",
"Jinja2>=2.10",
"itsdangerous>=0.24",
"click>=5.1",
],
extras_require={
"dotenv": ["python-dotenv"],
"dev": [
"pytest>=3",
"coverage",
"tox",
"sphinx",
"pallets-sphinx-themes",
"sphinxcontrib-log-cabinet",
],
"docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"],
},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Flask",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
"Topic :: Software Development :: Libraries :: Application Frameworks",
"Topic :: Software Development :: Libraries :: Python Modules",
],
entry_points={"console_scripts": ["flask = flask.cli:main"]},
)
# -*- coding: utf-8 -*-
from distutils.core import setup
packages = [
"pendulum",
"pendulum._extensions",
"pendulum.formatting",
"pendulum.locales",
"pendulum.locales.da",
"pendulum.locales.de",
"pendulum.locales.en",
"pendulum.locales.es",
"pendulum.locales.fa",
"pendulum.locales.fo",
"pendulum.locales.fr",
"pendulum.locales.ko",
"pendulum.locales.lt",
"pendulum.locales.pt_br",
"pendulum.locales.zh",
"pendulum.mixins",
"pendulum.parsing",
"pendulum.parsing.exceptions",
"pendulum.tz",
"pendulum.tz.data",
"pendulum.tz.zoneinfo",
"pendulum.utils",
]
package_data = {"": ["*"]}
install_requires = ["python-dateutil>=2.6,<3.0", "pytzdata>=2018.3"]
extras_require = {':python_version < "3.5"': ["typing>=3.6,<4.0"]}
setup_kwargs = {
"name": "pendulum",
"version": "2.0.4",
"description": "Python datetimes made easy",
"author": "Sébastien Eustace",
"author_email": "sebastien@eustace.io",
"url": "https://pendulum.eustace.io",
"packages": packages,
"package_data": package_data,
"install_requires": install_requires,
"extras_require": extras_require,
"python_requires": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
}
from build import *
build(setup_kwargs)
setup(**setup_kwargs)
#!/usr/bin/env python
# Learn more: https://github.com/kennethreitz/setup.py
import os
import re
import sys
from codecs import open
from setuptools import setup
from setuptools.command.test import test as TestCommand
here = os.path.abspath(os.path.dirname(__file__))
class PyTest(TestCommand):
user_options = [("pytest-args=", "a", "Arguments to pass into py.test")]
def initialize_options(self):
TestCommand.initialize_options(self)
try:
from multiprocessing import cpu_count
self.pytest_args = ["-n", str(cpu_count()), "--boxed"]
except (ImportError, NotImplementedError):
self.pytest_args = ["-n", "1", "--boxed"]
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import pytest
errno = pytest.main(self.pytest_args)
sys.exit(errno)
# 'setup.py publish' shortcut.
if sys.argv[-1] == "publish":
os.system("python setup.py sdist bdist_wheel")
os.system("twine upload dist/*")
sys.exit()
packages = ["requests"]
requires = [
"chardet>=3.0.2,<3.1.0",
"idna>=2.5,<2.8",
"urllib3>=1.21.1,<1.25",
"certifi>=2017.4.17",
]
test_requirements = [
"pytest-httpbin==0.0.7",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"PySocks>=1.5.6, !=1.5.7",
"pytest>=2.8.0",
]
about = {}
with open(os.path.join(here, "requests", "__version__.py"), "r", "utf-8") as f:
exec(f.read(), about)
with open("README.md", "r", "utf-8") as f:
readme = f.read()
with open("HISTORY.md", "r", "utf-8") as f:
history = f.read()
setup(
name=about["__title__"],
version=about["__version__"],
description=about["__description__"],
long_description=readme,
long_description_content_type="text/markdown",
author=about["__author__"],
author_email=about["__author_email__"],
url=about["__url__"],
packages=packages,
package_data={"": ["LICENSE", "NOTICE"], "requests": ["*.pem"]},
package_dir={"requests": "requests"},
include_package_data=True,
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
install_requires=requires,
license=about["__license__"],
zip_safe=False,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Natural Language :: English",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],
cmdclass={"test": PyTest},
tests_require=test_requirements,
extras_require={
"security": ["pyOpenSSL >= 0.14", "cryptography>=1.3.4", "idna>=2.0.0"],
"socks": ["PySocks>=1.5.6, !=1.5.7"],
'socks:sys_platform == "win32" and python_version == "2.7"': ["win_inet_pton"],
},
)
import os
import platform
import re
import sys
from distutils.command.build_ext import build_ext
from distutils.errors import CCompilerError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
from setuptools import Distribution as _Distribution, Extension
from setuptools import setup
from setuptools import find_packages
from setuptools.command.test import test as TestCommand
cmdclass = {}
if sys.version_info < (2, 7):
raise Exception("SQLAlchemy requires Python 2.7 or higher.")
cpython = platform.python_implementation() == "CPython"
ext_modules = [
Extension(
"sqlalchemy.cprocessors", sources=["lib/sqlalchemy/cextension/processors.c"]
),
Extension(
"sqlalchemy.cresultproxy", sources=["lib/sqlalchemy/cextension/resultproxy.c"]
),
Extension("sqlalchemy.cutils", sources=["lib/sqlalchemy/cextension/utils.c"]),
]
ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
if sys.platform == "win32":
# 2.6's distutils.msvc9compiler can raise an IOError when failing to
# find the compiler
ext_errors += (IOError,)
class BuildFailed(Exception):
def __init__(self):
self.cause = sys.exc_info()[1] # work around py 2/3 different syntax
class ve_build_ext(build_ext):
# This class allows C extension building to fail.
def run(self):
try:
build_ext.run(self)
except DistutilsPlatformError:
raise BuildFailed()
def build_extension(self, ext):
try:
build_ext.build_extension(self, ext)
except ext_errors:
raise BuildFailed()
except ValueError:
# this can happen on Windows 64 bit, see Python issue 7511
if "'path'" in str(sys.exc_info()[1]): # works with both py 2/3
raise BuildFailed()
raise
cmdclass["build_ext"] = ve_build_ext
class Distribution(_Distribution):
def has_ext_modules(self):
# We want to always claim that we have ext_modules. This will be fine
# if we don't actually have them (such as on PyPy) because nothing
# will get built, however we don't want to provide an overally broad
# Wheel package when building a wheel without C support. This will
# ensure that Wheel knows to treat us as if the build output is
# platform specific.
return True
class PyTest(TestCommand):
# from http://pytest.org/latest/goodpractices.html\
# #integrating-with-setuptools-python-setup-py-test-pytest-runner
# TODO: prefer pytest-runner package at some point, however it was
# not working at the time of this comment.
user_options = [("pytest-args=", "a", "Arguments to pass to py.test")]
default_options = ["-n", "4", "-q", "--nomemory"]
def initialize_options(self):
TestCommand.initialize_options(self)
self.pytest_args = ""
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import shlex
# import here, cause outside the eggs aren't loaded
import pytest
errno = pytest.main(self.default_options + shlex.split(self.pytest_args))
sys.exit(errno)
cmdclass["test"] = PyTest
def status_msgs(*msgs):
print("*" * 75)
for msg in msgs:
print(msg)
print("*" * 75)
with open(
os.path.join(os.path.dirname(__file__), "lib", "sqlalchemy", "__init__.py")
) as v_file:
VERSION = re.compile(r".*__version__ = '(.*?)'", re.S).match(v_file.read()).group(1)
with open(os.path.join(os.path.dirname(__file__), "README.rst")) as r_file:
readme = r_file.read()
def run_setup(with_cext):
kwargs = {}
if with_cext:
kwargs["ext_modules"] = ext_modules
else:
kwargs["ext_modules"] = []
setup(
name="SQLAlchemy",
version=VERSION,
description="Database Abstraction Library",
author="Mike Bayer",
author_email="mike_mp@zzzcomputing.com",
url="http://www.sqlalchemy.org",
packages=find_packages("lib"),
package_dir={"": "lib"},
license="MIT License",
cmdclass=cmdclass,
tests_require=["pytest >= 2.5.2", "mock", "pytest-xdist"],
long_description=readme,
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Database :: Front-Ends",
"Operating System :: OS Independent",
],
distclass=Distribution,
extras_require={
"mysql": ["mysqlclient"],
"pymysql": ["pymysql"],
"postgresql": ["psycopg2"],
"postgresql_pg8000": ["pg8000"],
"postgresql_psycopg2cffi": ["psycopg2cffi"],
"oracle": ["cx_oracle"],
"mssql_pyodbc": ["pyodbc"],
"mssql_pymssql": ["pymssql"],
},
**kwargs
)
if not cpython:
run_setup(False)
status_msgs(
"WARNING: C extensions are not supported on "
+ "this Python platform, speedups are not enabled.",
"Plain-Python build succeeded.",
)
elif os.environ.get("DISABLE_SQLALCHEMY_CEXT"):
run_setup(False)
status_msgs(
"DISABLE_SQLALCHEMY_CEXT is set; " + "not attempting to build C extensions.",
"Plain-Python build succeeded.",
)
else:
try:
run_setup(True)
except BuildFailed as exc:
status_msgs(
exc.cause,
"WARNING: The C extension could not be compiled, "
+ "speedups are not enabled.",
"Failure information, if any, is above.",
"Retrying the build without the C extension now.",
)
run_setup(False)
status_msgs(
"WARNING: The C extension could not be compiled, "
+ "speedups are not enabled.",
"Plain-Python build succeeded.",
)
[metadata]
name = with-setup-cfg
version = 1.2.3
[options]
zip_safe = true
python_requires = >=2.6,!=3.0,!=3.1,!=3.2,!=3.3
setup_requires = setuptools>=36.2.2
install_requires =
six
tomlkit
[options.extras_require]
validation =
cerberus
tests =
pytest
pytest-xdist
pytest-cov
from setuptools import setup
setup()
import os
import pytest
from poetry.utils._compat import PY35
from poetry.utils.setup_reader import SetupReader
@pytest.fixture()
def setup():
def _setup(name):
return os.path.join(os.path.dirname(__file__), "fixtures", "setups", name)
return _setup
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_first_level_setup_call_with_direct_types(setup):
result = SetupReader.read_from_directory(setup("flask"))
expected_name = "Flask"
expected_version = None
expected_install_requires = [
"Werkzeug>=0.14",
"Jinja2>=2.10",
"itsdangerous>=0.24",
"click>=5.1",
]
expected_extras_require = {
"dotenv": ["python-dotenv"],
"dev": [
"pytest>=3",
"coverage",
"tox",
"sphinx",
"pallets-sphinx-themes",
"sphinxcontrib-log-cabinet",
],
"docs": ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet"],
}
expected_python_requires = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_first_level_setup_call_with_variables(setup):
result = SetupReader.read_from_directory(setup("requests"))
expected_name = None
expected_version = None
expected_install_requires = [
"chardet>=3.0.2,<3.1.0",
"idna>=2.5,<2.8",
"urllib3>=1.21.1,<1.25",
"certifi>=2017.4.17",
]
expected_extras_require = {
"security": ["pyOpenSSL >= 0.14", "cryptography>=1.3.4", "idna>=2.0.0"],
"socks": ["PySocks>=1.5.6, !=1.5.7"],
'socks:sys_platform == "win32" and python_version == "2.7"': ["win_inet_pton"],
}
expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_sub_level_setup_call_with_direct_types(setup):
result = SetupReader.read_from_directory(setup("sqlalchemy"))
expected_name = "SQLAlchemy"
expected_version = None
expected_install_requires = []
expected_extras_require = {
"mysql": ["mysqlclient"],
"pymysql": ["pymysql"],
"postgresql": ["psycopg2"],
"postgresql_pg8000": ["pg8000"],
"postgresql_psycopg2cffi": ["psycopg2cffi"],
"oracle": ["cx_oracle"],
"mssql_pyodbc": ["pyodbc"],
"mssql_pymssql": ["pymssql"],
}
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert result["python_requires"] is None
def test_setup_reader_read_setup_cfg(setup):
result = SetupReader.read_from_directory(setup("with-setup-cfg"))
expected_name = "with-setup-cfg"
expected_version = "1.2.3"
expected_install_requires = ["six", "tomlkit"]
expected_extras_require = {
"validation": ["cerberus"],
"tests": ["pytest", "pytest-xdist", "pytest-cov"],
}
expected_python_requires = ">=2.6,!=3.0,!=3.1,!=3.2,!=3.3"
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_setup_kwargs(setup):
result = SetupReader.read_from_directory(setup("pendulum"))
expected_name = "pendulum"
expected_version = "2.0.4"
expected_install_requires = ["python-dateutil>=2.6,<3.0", "pytzdata>=2018.3"]
expected_extras_require = {':python_version < "3.5"': ["typing>=3.6,<4.0"]}
expected_python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_setup_reader_read_setup_call_in_main(setup):
result = SetupReader.read_from_directory(setup("pyyaml"))
expected_name = "PyYAML"
expected_version = "3.13"
expected_install_requires = []
expected_extras_require = {}
expected_python_requires = None
assert expected_name == result["name"]
assert expected_version == result["version"]
assert expected_install_requires == result["install_requires"]
assert expected_extras_require == result["extras_require"]
assert expected_python_requires == result["python_requires"]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment