Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
Merge pull request #308 from dephell/setuppy-again
Browse files Browse the repository at this point in the history
Integrate dephell_setuptools
  • Loading branch information
orsinium committed Nov 19, 2019
2 parents 08c85d3 + 34093fa commit ec3a87a
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 133 deletions.
3 changes: 3 additions & 0 deletions dephell/controllers/_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ def _resolve(self, debug: bool, silent: bool, level: Optional[int], spinner) ->
dep.group = group

def apply_envs(self, envs: set) -> None:
if not any(root.dependencies for root in self.graph.get_layer(0)):
logger.debug('no dependencies, nothing to filter')
return
layer = self.graph.get_layer(1)

# Unapply deps that we don't need
Expand Down
108 changes: 18 additions & 90 deletions dephell/converters/setuppy.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
# built-in
import sys
from collections import defaultdict
from distutils.core import run_setup
from json import dumps as json_dumps
from logging import getLogger
from pathlib import Path
from re import sub
from typing import Optional

# external
from dephell_discover import Root as PackageRoot
from dephell_links import DirLink, FileLink, URLLink, VCSLink, parse_link
from dephell_setuptools import read_setup
from dephell_specifier import RangeSpecifier
from packaging.requirements import Requirement
from setuptools.dist import Distribution

# app
from ..context_tools import chdir
from ..controllers import DependencyMaker, Readme
from ..models import Author, EntryPoint, RootDependency
from .base import BaseConverter
Expand Down Expand Up @@ -77,65 +73,56 @@ def load(self, path) -> RootDependency:
path = self._make_source_path_absolute(path)
self._resolve_path = path.parent

old_sys_path = sys.path
sys.path.append(str(path.parent))

info = self._execute(path=path)
if info is None:
with chdir(path.parent):
info = run_setup(path.name)

sys.path = old_sys_path

data = read_setup(path=path, error_handler=logger.debug)
root = RootDependency(
raw_name=self._get(info, 'name'),
version=self._get(info, 'version') or '0.0.0',
raw_name=data['name'],
version=data.get('version', '0.0.0'),
package=PackageRoot(
path=self.project_path or Path(),
name=self._get(info, 'name') or None,
name=data['name'],
),

description=self._get(info, 'description'),
license=self._get(info, 'license'),
description=data.get('description'),
license=data.get('license'),

keywords=tuple(self._get_list(info, 'keywords')),
classifiers=tuple(self._get_list(info, 'classifiers')),
platforms=tuple(self._get_list(info, 'platforms')),
keywords=tuple(data.get('keywords', ())),
classifiers=tuple(data.get('classifiers', ())),
platforms=tuple(data.get('platforms', ())),

python=RangeSpecifier(self._get(info, 'python_requires')),
python=RangeSpecifier(data.get('python_requires')),
readme=Readme.from_code(path=path),
)

# links
for key, name in (('home', 'url'), ('download', 'download_url')):
link = self._get(info, name)
link = data.get(name)
if link:
root.links[key] = link

# authors
for name in ('author', 'maintainer'):
author = self._get(info, name)
author = data.get(name)
if author:
root.authors += (
Author(name=author, mail=self._get(info, name + '_email')),
Author(name=author, mail=data.get(name + '_email')),
)

# entrypoints
entrypoints = []
for group, content in (getattr(info, 'entry_points', {}) or {}).items():
for group, content in data.get('entry_points', {}).items():
for entrypoint in content:
entrypoints.append(EntryPoint.parse(text=entrypoint, group=group))
root.entrypoints = tuple(entrypoints)

# dependency_links
urls = dict()
for url in self._get_list(info, 'dependency_links'):
for url in data.get('dependency_links', ()):
parsed = parse_link(url)
name = parsed.name.split('-')[0]
urls[name] = url

# dependencies
for req in self._get_list(info, 'install_requires'):
for req in data.get('install_requires', ()):
req = Requirement(req)
root.attach_dependencies(DependencyMaker.from_requirement(
source=root,
Expand All @@ -144,7 +131,7 @@ def load(self, path) -> RootDependency:
))

# extras
for extra, reqs in getattr(info, 'extras_require', {}).items():
for extra, reqs in data.get('extras_require', {}).items():
extra, marker = self._split_extra_and_marker(extra)
envs = {extra} if extra == 'dev' else {'main', extra}
for req in reqs:
Expand Down Expand Up @@ -260,65 +247,6 @@ def dumps(self, reqs, project: RootDependency, content=None) -> str:

# private methods

@staticmethod
def _execute(path: Path):
source = path.read_text('utf-8')
# Remove any dotted module names
new_source = sub(r'[a-z][a-z0-9.]*\.setup\(', 'setup(', source)
# Remove return
new_source = new_source.replace('return setup(', 'setup(')
# Rename functions that end with setup
new_source = sub(r'([_a-z][a-z0-9._]*)setup\(', r'setup\1(', new_source)
# Ensure _dist is global
new_source = new_source.replace('setup(', 'global _dist; _dist = dict(')
if new_source == source:
logger.error('cannot modify source')
return None

globe = {
'__file__': str(path),
'__name__': '__main__',
}
with chdir(path.parent):
try:
exec(compile(new_source, path.name, 'exec'), globe)
except Exception as e:
logger.exception('_execute {}: {}'.format(type(e).__name__, str(e)))

dist = globe.get('_dist')
if dist is None:
logger.error('distribution was not called')
return None
return Distribution(dist)

@staticmethod
def _get(msg, name: str) -> str:
value = getattr(msg.metadata, name, None)
if not value:
value = getattr(msg, name, None)
if not value:
return ''
value = str(value)
if value == 'UNKNOWN':
return ''
return value.strip()

@staticmethod
def _get_list(msg, name: str) -> tuple:
if name == 'keywords':
return ' '.join(msg.get_keywords()).split()

getter = getattr(msg, 'get_' + name, None)
if getter:
values = getter()
else:
values = getattr(msg, name, None)
if type(values) is str:
values = values.split()
if not values:
return ()
return tuple(value for value in values if value != 'UNKNOWN' and value.strip())

@staticmethod
def _format_req(req) -> str:
line = req.raw_name
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ dephell-licenses = ">=0.1.6"
dephell-links = ">=0.1.4"
dephell-markers = ">=1.0.0"
dephell-pythons = ">=0.1.11"
dephell-setuptools = ">=0.2.1"
dephell-shells = ">=0.1.3"
dephell-specifier = ">=0.1.7"
dephell-venvs = ">=0.1.16"
Expand Down
33 changes: 18 additions & 15 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
description='Dependency resolution for Python',
python_requires='>=3.5',
project_urls={
'documentation': 'https://dephell.org/docs/',
'homepage': 'https://dephell.org/',
'repository': 'https://github.com/dephell/dephell'
"documentation": "https://dephell.org/docs/",
"homepage": "https://dephell.org/",
"repository": "https://github.com/dephell/dephell"
},
author='Gram',
author_email='master_fess@mail.ru',
Expand All @@ -41,37 +41,40 @@
'Topic :: Software Development :: Build Tools',
'Topic :: Software Development :: Libraries :: Python Modules'
],
entry_points={'console_scripts': ['dephell = dephell.cli:entrypoint']},
entry_points={"console_scripts": ["dephell = dephell.cli:entrypoint"]},
packages=[
'dephell', 'dephell.actions', 'dephell.commands', 'dephell.config',
'dephell.controllers', 'dephell.converters', 'dephell.models',
'dephell.repositories', 'dephell.repositories._conda',
'dephell.repositories._git', 'dephell.repositories._warehouse'
],
package_data={'dephell': ['templates/*.j2', 'templates/*.sh']},
package_dir={"": "."},
package_data={"dephell": ["templates/*.j2", "templates/*.sh"]},
install_requires=[
'aiohttp', 'appdirs', 'attrs>=19.2.0',
'bowler; python_version >= "3.6"',
'bowler-py35>=0.9.1; python_version < "3.6"', 'cerberus>=1.3',
'dephell-archive>=0.1.5', 'dephell-discover>=0.2.6',
'dephell-licenses>=0.1.6', 'dephell-links>=0.1.4',
'dephell-markers>=1.0.0', 'dephell-pythons>=0.1.11',
'dephell-shells>=0.1.3', 'dephell-specifier>=0.1.7',
'dephell-venvs>=0.1.16', 'dephell-versioning', 'docker', 'dockerpty',
'dephell-setuptools>=0.2.1', 'dephell-shells>=0.1.3',
'dephell-specifier>=0.1.7', 'dephell-venvs>=0.1.16',
'dephell-versioning', 'docker', 'dockerpty',
'fissix; python_version >= "3.6"',
'fissix-py35; python_version < "3.6"', 'flatdict', 'html5lib', 'jinja2',
'm2r', 'packaging', 'pip>=18.0', 'pygments', 'requests', 'ruamel.yaml',
'setuptools', 'tabulate', 'tomlkit', 'yaspin'
],
extras_require={
'dev': [
'aioresponses', 'alabaster', 'flake8-isort', 'isort[pyproject]',
'pygments-github-lexers', 'pytest', 'recommonmark', 'requests-mock',
'sphinx'
"dev": [
"aioresponses", "alabaster", "flake8-isort", "isort[pyproject]",
"pygments-github-lexers", "pytest", "recommonmark", "requests-mock",
"sphinx"
],
'docs':
['alabaster', 'pygments-github-lexers', 'recommonmark', 'sphinx'],
'full': ['aiofiles', 'autopep8', 'colorama', 'graphviz', 'yapf'],
'tests': ['aioresponses', 'pytest', 'requests-mock']
"docs": [
"alabaster", "pygments-github-lexers", "recommonmark", "sphinx"
],
"full": ["aiofiles", "autopep8", "colorama", "graphviz", "yapf"],
"tests": ["aioresponses", "pytest", "requests-mock"]
},
)
41 changes: 13 additions & 28 deletions tests/test_converters/test_setuppy.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,67 +32,52 @@ def test_load_metadata():

def test_dotted_setup_call(temp_path: Path):
path = temp_path / 'setup.py'
with open(str(path), 'w') as f:
f.write(dedent("""
path.write_text(dedent("""
import setuptools
setuptools.setup(name='foo')
"""))

dist = SetupPyConverter()._execute(path)

assert dist.get_name() == 'foo'
root = SetupPyConverter().load(path)
assert root.name == 'foo'


def test_return_setup_call(temp_path: Path):
path = temp_path / 'setup.py'
with open(str(path), 'w') as f:
f.write(dedent("""
path.write_text(dedent("""
from setuptools import setup
def main():
return setup(name='foo')
if __name__ == '__main__':
main()
"""))

dist = SetupPyConverter()._execute(path)

assert dist.get_name() == 'foo'
root = SetupPyConverter().load(path)
assert root.name == 'foo'


def test_run_setup_function(temp_path: Path):
path = temp_path / 'setup.py'
with open(str(path), 'w') as f:
f.write(dedent("""
path.write_text(dedent("""
from setuptools import setup
def run_setup():
return setup(name='foo')
if __name__ == '__main__':
run_setup()
"""))

dist = SetupPyConverter()._execute(path)

assert dist.get_name() == 'foo'
root = SetupPyConverter().load(path)
assert root.name == 'foo'


def test_import(temp_path: Path):
with open(str(temp_path / 'local_module.py'), 'w') as f:
f.write(dedent("""
name = 'imported'
"""))
path = temp_path / 'local_module.py'
path.write_text(dedent('name = "imported"'))
path = temp_path / 'setup.py'
with open(str(path), 'w') as f:
f.write(dedent("""
import sys
print(sys.path)
path.write_text(dedent("""
from setuptools import setup
import local_module
setup(name=local_module.name)
"""))

root = SetupPyConverter().load(path)

assert root.name == 'imported'


Expand Down

0 comments on commit ec3a87a

Please sign in to comment.