Commit 2464b15e by Sébastien Eustace

Read setup files if egg_info fails

parent 38ff6f33
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
## [0.12.5] - 2018-10-26 ## [0.12.5] - 2018-10-26
### 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
- Fixed installation of Poetry git dependencies with a build system. - Fixed installation of Poetry git dependencies with a build system.
......
import glob
import os import os
import pkginfo import pkginfo
...@@ -5,11 +6,13 @@ from pkginfo.distribution import HEADER_ATTRS ...@@ -5,11 +6,13 @@ from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0 from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.io import NullIO from poetry.io import NullIO
from poetry.utils._compat import PY35
from poetry.utils._compat import Path from poetry.utils._compat import Path
from poetry.utils._compat import decode
from poetry.utils.helpers import parse_requires from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
from poetry.utils.env import Env from poetry.utils.env import Env
from poetry.utils.env import EnvCommandError
from poetry.utils.setup_reader import SetupReader
from .dependency import Dependency from .dependency import Dependency
...@@ -80,37 +83,91 @@ class DirectoryDependency(Dependency): ...@@ -80,37 +83,91 @@ class DirectoryDependency(Dependency):
try: try:
cwd = base cwd = base
venv = Env.create_venv(NullIO(), cwd=cwd) venv = Env.get(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info") venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
result = SetupReader.read_from_directory(self._full_path)
if not result["name"]:
# The name could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package name for {}".format(path)
)
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the package version for {}".format(path)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
if python_requires is None:
python_requires = "*"
package_summary = ""
requires = ""
for dep in result["install_requires"]:
requires += dep + "\n"
if result["extras_require"]:
requires += "\n"
for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires)
else:
os.chdir(current_dir)
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(self._full_path), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(self._full_path.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
package_summary = meta.summary
python_requires = meta.requires_python
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())
finally: finally:
os.chdir(current_dir) os.chdir(current_dir)
egg_info = list(self._full_path.glob("*.egg-info"))[0] package = Package(package_name, package_version)
package.description = package_summary
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: for req in reqs:
package.requires.append(dependency_from_pep_508(req)) package.requires.append(dependency_from_pep_508(req))
if meta.requires_python: if python_requires:
package.python_versions = meta.requires_python package.python_versions = python_requires
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 = package
......
...@@ -32,6 +32,8 @@ from poetry.utils._compat import Path ...@@ -32,6 +32,8 @@ from poetry.utils._compat import Path
from poetry.utils.helpers import parse_requires from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
from poetry.utils.env import Env from poetry.utils.env import Env
from poetry.utils.env import EnvCommandError
from poetry.utils.setup_reader import SetupReader
from poetry.vcs.git import Git from poetry.vcs.git import Git
...@@ -213,36 +215,81 @@ class Provider: ...@@ -213,36 +215,81 @@ class Provider:
os.chdir(tmp_dir.as_posix()) os.chdir(tmp_dir.as_posix())
try: try:
venv.run("python", "setup.py", "egg_info") try:
venv.run("python", "setup.py", "egg_info")
# Sometimes pathlib will fail on recursive except EnvCommandError:
# symbolic links, so we need to workaround it # Most likely an error with the egg_info command
# and use the glob module instead. self.debug(
# Note that this does not happen with pathlib2 "<warning>Error executing the egg_info command. Reading setup files.</warning>"
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(tmp_dir), "**", "*.egg-info"),
recursive=True,
)
) )
else: result = SetupReader.read_from_directory(tmp_dir)
egg_info = next(tmp_dir.glob("**/*.egg-info")) if not result["name"]:
result["name"] = dependency.name
if not result["version"]:
# The version could not be determined
# so we raise an error since it is mandatory
raise RuntimeError(
"Unable to retrieve the version for {}".format(
dependency.name
)
)
meta = pkginfo.UnpackedSDist(str(egg_info)) package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
if meta.requires_dist: requires = ""
reqs = list(meta.requires_dist) for dep in result["install_requires"]:
else: requires += dep + "\n"
reqs = []
requires = egg_info / "requires.txt" if result["extras_require"]:
if requires.exists(): requires += "\n"
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(meta.name, meta.version) for extra_name, deps in result["extras_require"].items():
requires += "[{}]\n".format(extra_name)
for dep in deps:
requires += dep + "\n"
requires += "\n"
reqs = parse_requires(requires)
else:
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
# Note that this does not happen with pathlib2
# so it's safe to use it for Python < 3.4.
if PY35:
egg_info = next(
Path(p)
for p in glob.glob(
os.path.join(str(tmp_dir), "**", "*.egg-info"),
recursive=True,
)
)
else:
egg_info = next(tmp_dir.glob("**/*.egg-info"))
meta = pkginfo.UnpackedSDist(str(egg_info))
package_name = meta.name
package_version = meta.version
python_requires = meta.requires_python
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(package_name, package_version)
if python_requires:
package.python_versions = python_requires
for req in reqs: for req in reqs:
dep = dependency_from_pep_508(req) dep = dependency_from_pep_508(req)
......
...@@ -39,6 +39,7 @@ from poetry.utils._compat import to_str ...@@ -39,6 +39,7 @@ from poetry.utils._compat import to_str
from poetry.utils.helpers import parse_requires from poetry.utils.helpers import parse_requires
from poetry.utils.helpers import temporary_directory from poetry.utils.helpers import temporary_directory
from poetry.utils.env import Env from poetry.utils.env import Env
from poetry.utils.setup_reader import SetupReader
from poetry.version.markers import InvalidMarker from poetry.version.markers import InvalidMarker
from .exceptions import PackageNotFound from .exceptions import PackageNotFound
...@@ -502,45 +503,40 @@ class PyPiRepository(Repository): ...@@ -502,45 +503,40 @@ class PyPiRepository(Repository):
return info return info
# Still nothing, assume no dependencies # Still nothing, try reading (without executing it)
# We could probably get them by executing # the setup.py file.
# python setup.py egg-info but I don't feel try:
# confortable executing a file just for the sake return self._inspect_sdist_with_setup(sdist_dir)
# of getting dependencies. except Exception as e:
return info 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): def _inspect_sdist_with_setup(self, sdist_dir):
info = {"requires_python": None, "requires_dist": None} info = {"requires_python": None, "requires_dist": None}
setup = sdist_dir / "setup.py" result = SetupReader.read_from_directory(sdist_dir)
if not setup.exists(): requires = ""
return info for dep in result["install_requires"]:
requires += dep + "\n"
venv = Env.create_venv(NullIO())
current_dir = os.getcwd() if result["extras_require"]:
os.chdir(sdist_dir.as_posix()) requires += "\n"
try: for extra_name, deps in result["extras_require"].items():
venv.run("python", "setup.py", "egg_info") requires += "[{}]\n".format(extra_name)
egg_info = list(sdist_dir.glob("**/*.egg-info"))[0] for dep in deps:
requires += dep + "\n"
meta = pkginfo.UnpackedSDist(str(egg_info)) requires += "\n"
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
os.chdir(current_dir) info["requires_dist"] = parse_requires(requires)
info["requires_python"] = result["python_requires"]
return info return info
......
import ast
try:
from configparser import ConfigParser
except ImportError:
from ConfigParser import ConfigParser
from typing import Any
from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple
from typing import Union
from ._compat import PY35
from ._compat import basestring
from ._compat import Path
class SetupReader(object):
"""
Class that reads a setup.py file without executing it.
"""
DEFAULT = {
"name": None,
"version": None,
"install_requires": [],
"extras_require": {},
"python_requires": None,
}
FILES = ["setup.py", "setup.cfg"]
@classmethod
def read_from_directory(
cls, directory
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
if isinstance(directory, basestring):
directory = Path(directory)
for filename in cls.FILES:
filepath = directory / filename
if not filepath.exists():
continue
result = getattr(cls(), "read_{}".format(filename.replace(".", "_")))(
filepath
)
if not cls._is_empty_result(result):
return result
return cls.DEFAULT
@classmethod
def _is_empty_result(cls, result): # type: (Dict[str, Any]) -> bool
return (
not result["install_requires"]
and not result["extras_require"]
and not result["python_requires"]
)
def read_setup_py(
self, filepath
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
if not PY35:
return self.DEFAULT
if isinstance(filepath, basestring):
filepath = Path(filepath)
with filepath.open(encoding="utf-8") as f:
content = f.read()
result = {}
body = ast.parse(content).body
setup_call, body = self._find_setup_call(body)
if not setup_call:
return self.DEFAULT
# Inspecting keyword arguments
result["name"] = self._find_single_string(setup_call, body, "name")
result["version"] = self._find_single_string(setup_call, body, "version")
result["install_requires"] = self._find_install_requires(setup_call, body)
result["extras_require"] = self._find_extras_require(setup_call, body)
result["python_requires"] = self._find_single_string(
setup_call, body, "python_requires"
)
return result
def read_setup_cfg(
self, filepath
): # type: (Union[basestring, Path]) -> Dict[str, Union[List, Dict]]
parser = ConfigParser()
parser.read(str(filepath))
name = None
version = None
if parser.has_option("metadata", "name"):
name = parser.get("metadata", "name")
if parser.has_option("metadata", "version"):
version = parser.get("metadata", "version")
install_requires = []
extras_require = {}
python_requires = None
if parser.has_section("options"):
if parser.has_option("options", "install_requires"):
for dep in parser.get("options", "install_requires").split("\n"):
dep = dep.strip()
if not dep:
continue
install_requires.append(dep)
if parser.has_option("options", "python_requires"):
python_requires = parser.get("options", "python_requires")
if parser.has_section("options.extras_require"):
for group in parser.options("options.extras_require"):
extras_require[group] = []
deps = parser.get("options.extras_require", group)
for dep in deps.split("\n"):
dep = dep.strip()
if not dep:
continue
extras_require[group].append(dep)
return {
"name": name,
"version": version,
"install_requires": install_requires,
"extras_require": extras_require,
"python_requires": python_requires,
}
def _find_setup_call(
self, elements
): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
funcdefs = []
for element in elements:
if not isinstance(element, ast.Expr):
if isinstance(element, ast.FunctionDef):
funcdefs.append(element)
continue
value = element.value
if not isinstance(value, ast.Call):
continue
func = value.func
if not isinstance(func, ast.Name):
continue
if func.id != "setup":
continue
return value, elements
# Nothing, we inspect the function definitions
return self._find_sub_setup_call(funcdefs)
def _find_sub_setup_call(
self, elements
): # type: (List[Any]) -> Tuple[Optional[ast.Call], Optional[List[Any]]]
for element in elements:
if not isinstance(element, ast.FunctionDef):
continue
setup_call = self._find_setup_call(element.body)
if setup_call:
setup_call, body = setup_call
body = elements + body
return setup_call, body
def _find_install_requires(
self, call, body
): # type: (ast.Call, Iterable[Any]) -> List[str]
install_requires = []
value = self._find_in_call(call, "install_requires")
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return install_requires
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return install_requires
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return install_requires
if variable.func.id != "dict":
return install_requires
value = self._find_in_call(variable, "install_requires")
else:
value = self._find_in_dict(variable, "install_requires")
if value is None:
return install_requires
if isinstance(value, ast.List):
for el in value.elts:
install_requires.append(el.s)
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is not None and isinstance(variable, ast.List):
for el in variable.elts:
install_requires.append(el.s)
return install_requires
def _find_extras_require(
self, call, body
): # type: (ast.Call, Iterable[Any]) -> Dict[str, List]
extras_require = {}
value = self._find_in_call(call, "extras_require")
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return extras_require
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return extras_require
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return extras_require
if variable.func.id != "dict":
return extras_require
value = self._find_in_call(variable, "extras_require")
else:
value = self._find_in_dict(variable, "extras_require")
if value is None:
return extras_require
if isinstance(value, ast.Dict):
for key, val in zip(value.keys, value.values):
extras_require[key.s] = [e.s for e in val.elts]
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is None or not isinstance(variable, ast.Dict):
return extras_require
for key, val in zip(variable.keys, variable.values):
extras_require[key.s] = [e.s for e in val.elts]
return extras_require
def _find_single_string(
self, call, body, name
): # type: (ast.Call, List[Any], str) -> Optional[str]
value = self._find_in_call(call, name)
if value is None:
# Trying to find in kwargs
kwargs = self._find_call_kwargs(call)
if kwargs is None or not isinstance(kwargs, ast.Name):
return
variable = self._find_variable_in_body(body, kwargs.id)
if not isinstance(variable, (ast.Dict, ast.Call)):
return
if isinstance(variable, ast.Call):
if not isinstance(variable.func, ast.Name):
return
if variable.func.id != "dict":
return
value = self._find_in_call(variable, name)
else:
value = self._find_in_dict(variable, name)
if value is None:
return
if isinstance(value, ast.Str):
return value.s
elif isinstance(value, ast.Name):
variable = self._find_variable_in_body(body, value.id)
if variable is not None and isinstance(variable, ast.Str):
return variable.s
def _find_in_call(self, call, name): # type: (ast.Call, str) -> Optional[Any]
for keyword in call.keywords:
if keyword.arg == name:
return keyword.value
def _find_call_kwargs(self, call): # type: (ast.Call) -> Optional[Any]
kwargs = None
for keyword in call.keywords:
if keyword.arg is None:
kwargs = keyword.value
return kwargs
def _find_variable_in_body(
self, body, name
): # type: (Iterable[Any], str) -> Optional[Any]
found = None
for elem in body:
if found:
break
if not isinstance(elem, ast.Assign):
continue
for target in elem.targets:
if not isinstance(target, ast.Name):
continue
if target.id == name:
return elem.value
def _find_in_dict(self, dict_, name): # type: (ast.Call, str) -> Optional[Any]
for key, val in zip(dict_.keys, dict_.values):
if isinstance(key, ast.Str) and key.s == name:
return val
import pytest import pytest
import shutil
import tempfile import tempfile
try:
import urllib.parse as urlparse
except ImportError:
import urlparse
from poetry.config import Config from poetry.config import Config
from poetry.utils._compat import Path
from poetry.utils.toml_file import TomlFile from poetry.utils.toml_file import TomlFile
...@@ -11,3 +18,28 @@ def config(): # type: () -> Config ...@@ -11,3 +18,28 @@ def config(): # type: () -> Config
f.close() f.close()
return Config(TomlFile(f.name)) 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"
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from setuptools import setup from setuptools import setup
......
# -*- 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)
import pytest
from poetry.packages.directory_dependency import DirectoryDependency
from poetry.utils._compat import PY35
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_egg_info():
dependency = DirectoryDependency(DIST_PATH / "demo")
assert dependency.is_directory()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.2"
assert dependency.python_versions == "*"
package = dependency.package
assert package.name == "demo"
assert package.pretty_version == "0.1.2"
assert package.python_versions == "*"
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_directory_dependency_no_egg_info(mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = DirectoryDependency(DIST_PATH / "demo")
assert dependency.is_directory()
assert dependency.name == "demo"
assert dependency.pretty_constraint == "0.1.2"
assert dependency.python_versions == "*"
package = dependency.package
assert package.name == "demo"
assert package.pretty_version == "0.1.2"
assert package.python_versions == "*"
def test_directory_dependency_with_no_version_should_raise_an_error(mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
with pytest.raises(RuntimeError):
DirectoryDependency(DIST_PATH / "no-version")
import pytest
from poetry.io import NullIO
from poetry.packages import ProjectPackage
from poetry.packages.vcs_dependency import VCSDependency
from poetry.puzzle.provider import Provider
from poetry.repositories.pool import Pool
from poetry.repositories.repository import Repository
from poetry.utils._compat import PY35
from poetry.utils.env import EnvCommandError
from poetry.utils.env import MockEnv as BaseMockEnv
from tests.helpers import get_dependency
from subprocess import CalledProcessError
class MockEnv(BaseMockEnv):
def run(self, bin, *args):
raise EnvCommandError(CalledProcessError(1, "python", output=""))
@pytest.fixture
def root():
return ProjectPackage("root", "1.2.3")
@pytest.fixture
def repository():
return Repository()
@pytest.fixture
def pool(repository):
pool = Pool()
pool.add_repository(repository)
return pool
@pytest.fixture
def provider(root, pool):
return Provider(root, pool, NullIO())
def test_search_for_vcs_setup_egg_info(provider):
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {"foo": [get_dependency("cleo")]}
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4")
def test_search_for_vcs_read_setup(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/demo.git")
package = provider.search_for_vcs(dependency)[0]
assert package.name == "demo"
assert package.version.text == "0.1.2"
assert package.requires == [
get_dependency("pendulum", ">=1.4.4"),
get_dependency("cleo", optional=True),
]
assert package.extras == {"foo": [get_dependency("cleo")]}
def test_search_for_vcs_read_setup_raises_error_if_no_version(provider, mocker):
mocker.patch("poetry.utils.env.Env.get", return_value=MockEnv())
dependency = VCSDependency("demo", "git", "https://github.com/demo/no-version.git")
with pytest.raises(RuntimeError):
provider.search_for_vcs(dependency)
{
"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"
}
]
}
import json import json
import pytest
import shutil import shutil
from poetry.packages import Dependency
from poetry.repositories.pypi_repository import PyPiRepository from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import PY35
from poetry.utils._compat import Path from poetry.utils._compat import Path
...@@ -104,3 +107,25 @@ def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found(): ...@@ -104,3 +107,25 @@ def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found():
dep = package.requires[0] dep = package.requires[0]
assert dep.name == "futures" assert dep.name == "futures"
assert dep.python_versions == "~2.7" 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", "*")],
}
# 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
from __future__ import print_function
import json
import os
import os.path
import re
import sys
import warnings
from collections import defaultdict
from distutils.command.build_scripts import build_scripts as BuildScripts
from distutils.command.sdist import sdist as SDist
try:
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as BuildPy
from setuptools.command.install_lib import install_lib as InstallLib
from setuptools.command.install_scripts import install_scripts as InstallScripts
except ImportError:
print(
"Ansible now needs setuptools in order to build. Install it using"
" your package manager (usually python-setuptools) or via pip (pip"
" install setuptools).",
file=sys.stderr,
)
sys.exit(1)
sys.path.insert(0, os.path.abspath("lib"))
from ansible.release import __version__, __author__
SYMLINK_CACHE = "SYMLINK_CACHE.json"
def _find_symlinks(topdir, extension=""):
"""Find symlinks that should be maintained
Maintained symlinks exist in the bin dir or are modules which have
aliases. Our heuristic is that they are a link in a certain path which
point to a file in the same directory.
"""
symlinks = defaultdict(list)
for base_path, dirs, files in os.walk(topdir):
for filename in files:
filepath = os.path.join(base_path, filename)
if os.path.islink(filepath) and filename.endswith(extension):
target = os.readlink(filepath)
if os.path.dirname(target) == "":
link = filepath[len(topdir) :]
if link.startswith("/"):
link = link[1:]
symlinks[os.path.basename(target)].append(link)
return symlinks
def _cache_symlinks(symlink_data):
with open(SYMLINK_CACHE, "w") as f:
json.dump(symlink_data, f)
def _maintain_symlinks(symlink_type, base_path):
"""Switch a real file into a symlink"""
try:
# Try the cache first because going from git checkout to sdist is the
# only time we know that we're going to cache correctly
with open(SYMLINK_CACHE, "r") as f:
symlink_data = json.load(f)
except (IOError, OSError) as e:
# IOError on py2, OSError on py3. Both have errno
if e.errno == 2:
# SYMLINKS_CACHE doesn't exist. Fallback to trying to create the
# cache now. Will work if we're running directly from a git
# checkout or from an sdist created earlier.
symlink_data = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
# Sanity check that something we know should be a symlink was
# found. We'll take that to mean that the current directory
# structure properly reflects symlinks in the git repo
if "ansible-playbook" in symlink_data["script"]["ansible"]:
_cache_symlinks(symlink_data)
else:
raise
else:
raise
symlinks = symlink_data[symlink_type]
for source in symlinks:
for dest in symlinks[source]:
dest_path = os.path.join(base_path, dest)
if not os.path.islink(dest_path):
try:
os.unlink(dest_path)
except OSError as e:
if e.errno == 2:
# File does not exist which is all we wanted
pass
os.symlink(source, dest_path)
class BuildPyCommand(BuildPy):
def run(self):
BuildPy.run(self)
_maintain_symlinks("library", self.build_lib)
class BuildScriptsCommand(BuildScripts):
def run(self):
BuildScripts.run(self)
_maintain_symlinks("script", self.build_dir)
class InstallLibCommand(InstallLib):
def run(self):
InstallLib.run(self)
_maintain_symlinks("library", self.install_dir)
class InstallScriptsCommand(InstallScripts):
def run(self):
InstallScripts.run(self)
_maintain_symlinks("script", self.install_dir)
class SDistCommand(SDist):
def run(self):
# have to generate the cache of symlinks for release as sdist is the
# only command that has access to symlinks from the git repo
symlinks = {
"script": _find_symlinks("bin"),
"library": _find_symlinks("lib", ".py"),
}
_cache_symlinks(symlinks)
SDist.run(self)
def read_file(file_name):
"""Read file and return its contents."""
with open(file_name, "r") as f:
return f.read()
def read_requirements(file_name):
"""Read requirements file as a list."""
reqs = read_file(file_name).splitlines()
if not reqs:
raise RuntimeError(
"Unable to read requirements from the %s file"
"That indicates this copy of the source code is incomplete." % file_name
)
return reqs
PYCRYPTO_DIST = "pycrypto"
def get_crypto_req():
"""Detect custom crypto from ANSIBLE_CRYPTO_BACKEND env var.
pycrypto or cryptography. We choose a default but allow the user to
override it. This translates into pip install of the sdist deciding what
package to install and also the runtime dependencies that pkg_resources
knows about.
"""
crypto_backend = os.environ.get("ANSIBLE_CRYPTO_BACKEND", "").strip()
if crypto_backend == PYCRYPTO_DIST:
# Attempt to set version requirements
return "%s >= 2.6" % PYCRYPTO_DIST
return crypto_backend or None
def substitute_crypto_to_req(req):
"""Replace crypto requirements if customized."""
crypto_backend = get_crypto_req()
if crypto_backend is None:
return req
def is_not_crypto(r):
CRYPTO_LIBS = PYCRYPTO_DIST, "cryptography"
return not any(r.lower().startswith(c) for c in CRYPTO_LIBS)
return [r for r in req if is_not_crypto(r)] + [crypto_backend]
def read_extras():
"""Specify any extra requirements for installation."""
extras = dict()
extra_requirements_dir = "packaging/requirements"
for extra_requirements_filename in os.listdir(extra_requirements_dir):
filename_match = re.search(
r"^requirements-(\w*).txt$", extra_requirements_filename
)
if not filename_match:
continue
extra_req_file_path = os.path.join(
extra_requirements_dir, extra_requirements_filename
)
try:
extras[filename_match.group(1)] = read_file(
extra_req_file_path
).splitlines()
except RuntimeError:
pass
return extras
def get_dynamic_setup_params():
"""Add dynamically calculated setup params to static ones."""
return {
# Retrieve the long description from the README
"long_description": read_file("README.rst"),
"install_requires": substitute_crypto_to_req(
read_requirements("requirements.txt")
),
"extras_require": read_extras(),
}
static_setup_params = dict(
# Use the distutils SDist so that symlinks are not expanded
# Use a custom Build for the same reason
cmdclass={
"build_py": BuildPyCommand,
"build_scripts": BuildScriptsCommand,
"install_lib": InstallLibCommand,
"install_scripts": InstallScriptsCommand,
"sdist": SDistCommand,
},
name="ansible",
version=__version__,
description="Radically simple IT automation",
author=__author__,
author_email="info@ansible.com",
url="https://ansible.com/",
project_urls={
"Bug Tracker": "https://github.com/ansible/ansible/issues",
"CI: Shippable": "https://app.shippable.com/github/ansible/ansible",
"Code of Conduct": "https://docs.ansible.com/ansible/latest/community/code_of_conduct.html",
"Documentation": "https://docs.ansible.com/ansible/",
"Mailing lists": "https://docs.ansible.com/ansible/latest/community/communication.html#mailing-list-information",
"Source Code": "https://github.com/ansible/ansible",
},
license="GPLv3+",
# Ansible will also make use of a system copy of python-six and
# python-selectors2 if installed but use a Bundled copy if it's not.
python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
package_dir={"": "lib"},
packages=find_packages("lib"),
package_data={
"": [
"module_utils/powershell/*.psm1",
"module_utils/powershell/*/*.psm1",
"modules/windows/*.ps1",
"modules/windows/*/*.ps1",
"galaxy/data/*/*.*",
"galaxy/data/*/*/.*",
"galaxy/data/*/*/*.*",
"galaxy/data/*/tests/inventory",
"config/base.yml",
"config/module_defaults.yml",
]
},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Natural Language :: English",
"Operating System :: POSIX",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Topic :: System :: Installation/Setup",
"Topic :: System :: Systems Administration",
"Topic :: Utilities",
],
scripts=[
"bin/ansible",
"bin/ansible-playbook",
"bin/ansible-pull",
"bin/ansible-doc",
"bin/ansible-galaxy",
"bin/ansible-console",
"bin/ansible-connection",
"bin/ansible-vault",
"bin/ansible-config",
"bin/ansible-inventory",
],
data_files=[],
# Installing as zip files would break due to references to __file__
zip_safe=False,
)
def main():
"""Invoke installation process using setuptools."""
setup_params = dict(static_setup_params, **get_dynamic_setup_params())
ignore_warning_regex = (
r"Unknown distribution option: '(project_urls|python_requires)'"
)
warnings.filterwarnings(
"ignore",
message=ignore_warning_regex,
category=UserWarning,
module="distutils.dist",
)
setup(**setup_params)
warnings.resetwarnings()
if __name__ == "__main__":
main()
#!/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.",
)
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"]
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