forked from tqdm/tqdm
-
Notifications
You must be signed in to change notification settings - Fork 1
/
setup.py
executable file
·217 lines (191 loc) · 7.92 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
import sys
from subprocess import check_call
from io import open as io_open
# For Makefile parsing
import shlex
try: # pragma: no cover
import ConfigParser
import StringIO
except ImportError: # pragma: no cover
import configparser as ConfigParser
import io as StringIO
import re
# Makefile auxiliary functions #
RE_MAKE_CMD = re.compile('^\t(@\+?)(make)?', flags=re.M)
def parse_makefile_aliases(filepath):
'''
Parse a makefile to find commands and substitute variables. Expects a
makefile with only aliases and a line return between each command.
Returns a dict, with a list of commands for each alias.
'''
# -- Parsing the Makefile using ConfigParser
# Adding a fake section to make the Makefile a valid Ini file
ini_str = '[root]\n'
with io_open(filepath, mode='r') as fd:
ini_str = ini_str + RE_MAKE_CMD.sub('\t', fd.read())
ini_fp = StringIO.StringIO(ini_str)
# Parse using ConfigParser
config = ConfigParser.RawConfigParser()
config.readfp(ini_fp)
# Fetch the list of aliases
aliases = config.options('root')
# -- Extracting commands for each alias
commands = {}
for alias in aliases:
if alias.lower() in ['.phony']:
continue
# strip the first line return, and then split by any line return
commands[alias] = config.get('root', alias).lstrip('\n').split('\n')
# -- Commands substitution
# Loop until all aliases are substituted by their commands:
# Check each command of each alias, and if there is one command that is to
# be substituted by an alias, try to do it right away. If this is not
# possible because this alias itself points to other aliases , then stop
# and put the current alias back in the queue to be processed again later.
# Create the queue of aliases to process
aliases_todo = list(commands.keys())
# Create the dict that will hold the full commands
commands_new = {}
# Loop until we have processed all aliases
while aliases_todo:
# Pick the first alias in the queue
alias = aliases_todo.pop(0)
# Create a new entry in the resulting dict
commands_new[alias] = []
# For each command of this alias
for cmd in commands[alias]:
# Ignore self-referencing (alias points to itself)
if cmd == alias:
pass
# Substitute full command
elif cmd in aliases and cmd in commands_new:
# Append all the commands referenced by the alias
commands_new[alias].extend(commands_new[cmd])
# Delay substituting another alias, waiting for the other alias to
# be substituted first
elif cmd in aliases and cmd not in commands_new:
# Delete the current entry to avoid other aliases
# to reference this one wrongly (as it is empty)
del commands_new[alias]
aliases_todo.append(alias)
break
# Full command (no aliases)
else:
commands_new[alias].append(cmd)
commands = commands_new
del commands_new
# -- Prepending prefix to avoid conflicts with standard setup.py commands
# for alias in commands.keys():
# commands['make_'+alias] = commands[alias]
# del commands[alias]
return commands
def execute_makefile_commands(commands, alias, verbose=False):
cmds = commands[alias]
for cmd in cmds:
# Parse string in a shell-like fashion
# (incl quoted strings and comments)
parsed_cmd = shlex.split(cmd, comments=True)
# Execute command if not empty (ie, not just a comment)
if parsed_cmd:
if verbose:
print("Running command: " + cmd)
# Launch the command and wait to finish (synchronized call)
check_call(parsed_cmd, cwd=os.path.dirname(os.path.abspath(__file__)))
# Main setup.py config #
# Get version from tqdm/_version.py
__version__ = None
version_file = os.path.join(os.path.dirname(__file__), 'tqdm', '_version.py')
with io_open(version_file, mode='r') as fd:
exec(fd.read())
# Executing makefile commands if specified
if sys.argv[1].lower().strip() == 'make':
# Filename of the makefile
fpath = os.path.join(os.path.dirname(__file__), 'Makefile')
# Parse the makefile, substitute the aliases and extract the commands
commands = parse_makefile_aliases(fpath)
# If no alias (only `python setup.py make`), print the list of aliases
if len(sys.argv) < 3 or sys.argv[-1] == '--help':
print("Shortcut to use commands via aliases. List of aliases:")
print('\n'.join(alias for alias in sorted(commands.keys())))
# Else process the commands for this alias
else:
arg = sys.argv[-1]
# if unit testing, we do nothing (we just checked the makefile parsing)
if arg == 'none':
sys.exit(0)
# else if the alias exists, we execute its commands
elif arg in commands.keys():
execute_makefile_commands(commands, arg, verbose=True)
# else the alias cannot be found
else:
raise Exception("Provided alias cannot be found: make " + arg)
# Stop the processing of setup.py here:
# It's important to avoid setup.py raising an error because of the command
# not being standard
sys.exit(0)
# Python package config #
README_rst = ''
fndoc = os.path.join(os.path.dirname(__file__), 'README.rst')
with io_open(fndoc, mode='r', encoding='utf-8') as fd:
README_rst = fd.read()
setup(
name='tqdm',
version=__version__,
description='Fast, Extensible Progress Meter',
license='MPLv2.0, MIT Licences',
author='Noam Yorav-Raphael',
author_email='noamraph@gmail.com',
url='https://github.com/tqdm/tqdm',
maintainer='tqdm developers',
maintainer_email='python.tqdm@gmail.com',
platforms=['any'],
packages=['tqdm'],
entry_points={'console_scripts': ['tqdm=tqdm._main:main'], },
long_description=README_rst,
classifiers=[
# Trove classifiers
# (https://pypi.python.org/pypi?%3Aaction=list_classifiers)
'Development Status :: 5 - Production/Stable',
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
'License :: OSI Approved :: MIT License',
'Environment :: Console',
'Framework :: IPython',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Operating System :: POSIX',
'Operating System :: POSIX :: Linux',
'Operating System :: POSIX :: BSD',
'Operating System :: POSIX :: BSD :: FreeBSD',
'Operating System :: POSIX :: SunOS/Solaris',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: Implementation :: PyPy',
'Programming Language :: Python :: Implementation :: IronPython',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Software Development :: User Interfaces',
'Topic :: System :: Monitoring',
'Topic :: Terminals',
'Topic :: Utilities',
'Intended Audience :: Developers',
],
keywords='progressbar progressmeter progress bar meter'
' rate eta console terminal time',
test_suite='nose.collector',
tests_require=['nose', 'flake8', 'coverage'],
)