|
1
|
|
|
#!/usr/bin/env python |
|
2
|
|
|
# -*- coding: UTF-8 -*- |
|
3
|
|
|
|
|
4
|
|
|
import sys |
|
5
|
|
|
import subprocess |
|
6
|
|
|
import pkg_resources |
|
7
|
|
|
|
|
8
|
|
|
|
|
9
|
|
|
class SetupHelper (object): |
|
10
|
|
|
""" |
|
11
|
|
|
SetupHelper streamlines the population of setup() function calls with |
|
12
|
|
|
contents from __init__.py and README.md. |
|
13
|
|
|
""" |
|
14
|
|
|
|
|
15
|
|
|
def __init__(self, initfile="__init__.py", readmefile="README.md"): |
|
16
|
|
|
self.author, self.email, self.license, self.version = \ |
|
17
|
|
|
self.get_init(initfile) |
|
18
|
|
|
self.author = ", ".join(self.author) |
|
19
|
|
|
self.long_description = self.get_long_description(readmefile) |
|
20
|
|
|
|
|
21
|
|
|
def check_version(self, name, majorv=2, minorv=7): |
|
22
|
|
|
""" Make sure the package runs on the supported Python version |
|
23
|
|
|
""" |
|
24
|
|
|
if sys.version_info.major == majorv and sys.version_info.minor != minorv: |
|
25
|
|
|
sys.stderr.write("ERROR: %s is only for >= Python %d.%d but you are running %d.%d\n" %\ |
|
26
|
|
|
(name, majorv, minorv, sys.version_info.major, sys.version_info.minor)) |
|
27
|
|
|
sys.exit(1) |
|
28
|
|
|
|
|
29
|
|
|
def get_init(self, filename="__init__.py"): |
|
30
|
|
|
""" Get various info from the package without importing them |
|
31
|
|
|
""" |
|
32
|
|
|
import ast |
|
33
|
|
|
|
|
34
|
|
|
with open(filename) as init_file: |
|
35
|
|
|
module = ast.parse(init_file.read()) |
|
36
|
|
|
|
|
37
|
|
|
itr = lambda x: (ast.literal_eval(node.value) for node in ast.walk(module) \ |
|
38
|
|
|
if isinstance(node, ast.Assign) and node.targets[0].id == x) |
|
39
|
|
|
|
|
40
|
|
|
try: |
|
41
|
|
|
return next(itr("__author__")), \ |
|
42
|
|
|
next(itr("__email__")), \ |
|
43
|
|
|
next(itr("__license__")), \ |
|
44
|
|
|
next(itr("__version__")) |
|
45
|
|
|
except StopIteration: |
|
46
|
|
|
raise ValueError("One of author, email, license, or version" |
|
47
|
|
|
" cannot be found in {}".format(filename)) |
|
48
|
|
|
|
|
49
|
|
|
def missing_requirements(self, specifiers): |
|
50
|
|
|
""" Find what's missing |
|
51
|
|
|
""" |
|
52
|
|
|
for specifier in specifiers: |
|
53
|
|
|
try: |
|
54
|
|
|
pkg_resources.require(specifier) |
|
55
|
|
|
except pkg_resources.DistributionNotFound: |
|
56
|
|
|
yield specifier |
|
57
|
|
|
|
|
58
|
|
|
def install_requirements(self, requires): |
|
59
|
|
|
""" Install the listed requirements |
|
60
|
|
|
""" |
|
61
|
|
|
# Temporarily install dependencies required by setup.py before trying to import them. |
|
62
|
|
|
sys.path[0:0] = ['setup-requires'] |
|
63
|
|
|
pkg_resources.working_set.add_entry('setup-requires') |
|
64
|
|
|
|
|
65
|
|
|
to_install = list(self.missing_requirements(requires)) |
|
66
|
|
|
if to_install: |
|
67
|
|
|
cmd = [sys.executable, "-m", "pip", "install", |
|
68
|
|
|
"-t", "setup-requires"] + to_install |
|
69
|
|
|
subprocess.call(cmd) |
|
70
|
|
|
|
|
71
|
|
|
def get_long_description(self, filename='README.md'): |
|
72
|
|
|
""" I really prefer Markdown to reStructuredText. PyPi does not. |
|
73
|
|
|
""" |
|
74
|
|
|
try: |
|
75
|
|
|
import pypandoc |
|
76
|
|
|
description = pypandoc.convert_file('README.md', 'rst', 'md') |
|
77
|
|
|
except (IOError, ImportError): |
|
78
|
|
|
description = open("README.md").read() |
|
79
|
|
|
return description |
|
80
|
|
|
|