Commit 2464b15e by Sébastien Eustace

Read setup files if egg_info fails

parent 38ff6f33
......@@ -2,6 +2,10 @@
## [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 installation of Poetry git dependencies with a build system.
......
import glob
import os
import pkginfo
......@@ -5,11 +6,13 @@ from pkginfo.distribution import HEADER_ATTRS
from pkginfo.distribution import HEADER_ATTRS_2_0
from poetry.io import NullIO
from poetry.utils._compat import PY35
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 poetry.utils.env import EnvCommandError
from poetry.utils.setup_reader import SetupReader
from .dependency import Dependency
......@@ -80,14 +83,71 @@ class DirectoryDependency(Dependency):
try:
cwd = base
venv = Env.create_venv(NullIO(), cwd=cwd)
venv = Env.get(NullIO(), cwd=cwd)
venv.run("python", "setup.py", "egg_info")
finally:
os.chdir(current_dir)
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)
egg_info = list(self._full_path.glob("*.egg-info"))[0]
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)
......@@ -97,20 +157,17 @@ class DirectoryDependency(Dependency):
if requires.exists():
with requires.open() as f:
reqs = parse_requires(f.read())
finally:
os.chdir(current_dir)
package = Package(meta.name, meta.version)
package.description = meta.summary
package = Package(package_name, package_version)
package.description = package_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)
if python_requires:
package.python_versions = python_requires
self._package = package
......
......@@ -32,6 +32,8 @@ from poetry.utils._compat import Path
from poetry.utils.helpers import parse_requires
from poetry.utils.toml_file import TomlFile
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
......@@ -213,8 +215,47 @@ class Provider:
os.chdir(tmp_dir.as_posix())
try:
try:
venv.run("python", "setup.py", "egg_info")
except EnvCommandError:
# Most likely an error with the egg_info command
self.debug(
"<warning>Error executing the egg_info command. Reading setup files.</warning>"
)
result = SetupReader.read_from_directory(tmp_dir)
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
)
)
package_name = result["name"]
package_version = result["version"]
python_requires = result["python_requires"]
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:
# Sometimes pathlib will fail on recursive
# symbolic links, so we need to workaround it
# and use the glob module instead.
......@@ -233,6 +274,10 @@ class Provider:
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:
......@@ -242,7 +287,9 @@ class Provider:
with requires.open() as f:
reqs = parse_requires(f.read())
package = Package(meta.name, meta.version)
package = Package(package_name, package_version)
if python_requires:
package.python_versions = python_requires
for req in reqs:
dep = dependency_from_pep_508(req)
......
......@@ -39,6 +39,7 @@ 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
......@@ -502,45 +503,40 @@ 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.
# Still nothing, try reading (without executing it)
# the setup.py file.
try:
return self._inspect_sdist_with_setup(sdist_dir)
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")
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
for dep in deps:
requires += dep + "\n"
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 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"
# -*- coding: utf-8 -*-
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 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,25 @@ 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", "*")],
}
# 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.",
)
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