Add `python setup.py make [alias]` (aka Makefile parsing in setup.py)

+ Tox check

Signed-off-by: Stephen L. <lrq3000@gmail.com>
This commit is contained in:
Stephen L 2015-10-15 15:47:02 +02:00
parent 2da04c5866
commit 7405a59ff2
5 changed files with 167 additions and 11 deletions

View File

@ -5,7 +5,8 @@ This file describes how to contribute changes to the project, and how to
upload to the pypi repository.
Most of the management commands have been directly placed inside
the Makefile, so you just have to run `make [alias]`.
the Makefile, so you just have to run `python setup.py make [alias]`, which
is equivalent to using `make [alias]` but without requiring `make`.
Note: to use the Makefile on Windows, you need to install make.exe,
for example by installing [MinGW MSYS](http://www.mingw.org/wiki/msys).
@ -46,6 +47,12 @@ The standard way to run the tests:
- `cd` to the root of the `tqdm` directory (in the same folder as this file)
- run the following command:
```
python setup.py make test
```
or
```
make test
```
@ -78,10 +85,10 @@ Alternatively, you can use `nose` to run the tests just for your Python version:
- run the following commands:
```
make alltests
python setup.py make alltests
```
Or if you don't have `make`:
Or if it doesn't work, you can do:
```
nosetests --with-coverage --cover-package=tqdm -v tqdm/

View File

@ -1,7 +1,28 @@
.PHONY: all flake8 test coverage
# IMPORTANT: to be compatible with `python setup.py make alias`, you must make
# sure that you only put one command per line, and ALWAYS put a line return
# after an alias and before a command, eg:
#
#```
#all:
# test
# install
#test:
# nosetest
#install:
# python setup.py install
# ```
alltests: testcoverage flake8 testsetup
all: alltests build
.PHONY:
alltests
alltests:
testcoverage
flake8
testsetup
all:
alltests
build
flake8:
flake8 --max-line-length=80 --count --statistics --exit-zero tqdm/
@ -15,6 +36,7 @@ testnose:
testsetup:
python setup.py check --restructuredtext --strict
python setup.py make none
testcoverage:
nosetests tqdm --with-coverage --cover-package=tqdm -v
@ -35,3 +57,6 @@ pypimeta:
pypi:
twine upload dist/*
none:
none # used for unit testing

12
RELEASE
View File

@ -5,7 +5,8 @@ This file is intended for the project's maintainers and it describes
how to update, build and upload a new release.
Most of the management commands have been directly placed inside
the Makefile, so you just have to run `make [alias]`.
the Makefile, so you just have to run `python setup.py make [alias]`, which
is equivalent to using `make [alias]` but without requiring `make`.
Note: to use the Makefile on Windows, you need to install make.exe,
for example by installing [MinGW MSYS](http://www.mingw.org/wiki/msys).
@ -40,7 +41,8 @@ python setup.py check --restructuredtext --strict
```
Also, if you happen to mistakenly upload a broken release to PyPi,
you can fix the metadata by using: `make pypimeta` or `python setup.py register`
you can fix the metadata by using: `python setup.py make pypimeta`
or `python setup.py register`.
BUILDING A RELEASE AND UPLOADING TO PYPI
@ -52,13 +54,13 @@ process and info that will be uploaded to [pypi](pypi.python.org).
Check the result by using the following commands:
```
make installdev
python setup.py make installdev
```
Secondly, build tqdm into a distributable python package:
```
make build
python setup.py make build
```
This will generate several builds in the `dist/` folder.
@ -67,7 +69,7 @@ Finally, we can upload everything to pypi. Uploading to pypi can be done
easily using the [twine](https://github.com/pypa/twine) module:
```
make pypi
python setup.py make pypi
```
NOTE:

121
setup.py
View File

@ -3,6 +3,112 @@
import os
from setuptools import setup
# For Makefile parsing
import ConfigParser
import StringIO
import sys, subprocess
### Makefile auxiliary functions ###
def parse_makefile_aliases(filepath):
'''Parse a makefile to find commands and substitute variables.
Note that this function is not a total replacement of make, it only
parse aliases.
Expects a makefile with only aliases and ALWAYS a line return between
each command, eg:
```
all:
test
install
test:
nosetest
install:
python setup.py install
```
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' + open(filepath, 'r').read()
ini_fp = StringIO.StringIO(ini_str)
# Parse it 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:
# strip the first line return, and then split by any line return
commands[alias] = config.get('root', alias).lstrip('\n').split('\n')
# -- Commands substitution
# We loop until we can substitute all aliases by their commands
# What we do is that we check each command of each alias, and
# if there is one command that is to be substituted by an alias,
# we try to do it right away, but if it's not possible because
# this alias himself points to other aliases, then we stop
# and put the current alias back in the queue, which we
# will process again later when we have substituted the
# other aliases.
# Create the queue of aliases to process
aliases_todo = commands.keys()
# Create the dict that will hold the substituted aliases by their 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]:
# If the alias points to itself, we pass
if cmd == alias:
pass
# If the alias points to a full command, we substitute
elif cmd in aliases and cmd in commands_new:
# Append all the commands referenced by the alias
commands_new[alias].extend(commands_new[cmd])
# If the alias points to another alias, we delay,
# 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]
# Put back into the queue
aliases_todo.append(alias)
# Break the loop for the current alias
break
# Else this is just a full command (no reference to an alias)
# so we just append it
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:
if verbose: print("Running command: %s" % cmd)
subprocess.check_call(cmd.split())
### Main setup.py config ###
# Get version from tqdm/_version.py
__version__ = None
version_file = os.path.join(os.path.dirname(__file__), 'tqdm', '_version.py')
@ -10,6 +116,21 @@ for line in open(version_file).readlines():
if (line.startswith('version_info') or line.startswith('__version__')):
exec(line.strip())
# Executing makefile commands if specified
if len(sys.argv) >= 3 and sys.argv[1].lower().strip() == 'make':
arg = sys.argv[-1]
fpath = 'Makefile'
commands = parse_makefile_aliases(fpath)
if arg == 'none': # unit testing, we do nothing (we just checked the makefile parsing)
sys.exit(0)
elif arg in commands.keys(): # else if the alias exists, we execute its commands
execute_makefile_commands(commands, arg, verbose=True)
else: # else the alias cannot be found
raise Exception("Provided argument cannot be found: make %s" % (arg))
# Stop the processing of setup.py here
sys.exit(0) # Important to avoid setup.py to spit an error because of the command not being standard
# Python package config
setup(
name='tqdm',
version=__version__,

View File

@ -28,3 +28,4 @@ deps =
pygments
commands =
python setup.py check --restructuredtext --metadata --strict
python setup.py make none