diff --git a/.gitignore b/.gitignore index 6ec8d9ecb..6ce278987 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ *.py[cod] + +# C extensions *.so -/wiki/ -/docs/ -/feedstock/ # Packages tqdm.egg-info @@ -13,6 +12,7 @@ dist/ .tox/ .coverage __pycache__ +nosetests.xml # Translations *.mo @@ -31,3 +31,8 @@ __pycache__ # asv .asv/ benchmarks/*.py[co] + +# Sumbodules +/wiki/ +/docs/ +/feedstock/ diff --git a/.tqdm.1.md b/.tqdm.1.md index 2f88ad598..69ea3b17d 100644 --- a/.tqdm.1.md +++ b/.tqdm.1.md @@ -1,6 +1,6 @@ % TQDM(1) tqdm User Manuals % tqdm developers -% 2015-2018 +% 2015-2019 # NAME diff --git a/.travis.yml b/.travis.yml index 35037afe3..6afe47609 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -# sudo: required -# dist: trusty language: python matrix: include: @@ -46,10 +44,7 @@ before_install: # - sudo ln -s /run/shm /dev/shm - git fetch --tags install: - # Install tox first, before dependencies (to get per-env deps) - pip install tox - # install this package (tqdm) into the environment - pip install . -# run tests script: - tox diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0d2f12b7..9afe4b714 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -82,7 +82,7 @@ how to build and upload a new release. Once again, `[python setup.py] make []` will help. -## SEMANTIC VERSIONING +## Semantic Versioning The tqdm repository managers should: @@ -97,7 +97,7 @@ Note: tools can be used to automate this process, such as [python-semanticversion](https://github.com/rbarrois/python-semanticversion/). -## CHECKING SETUP.PY +## Checking setup.py To check that the `setup.py` file is compliant with PyPi requirements (e.g. version number; reStructuredText in README.rst) use: @@ -114,7 +114,7 @@ to PyPi, use: ``` -## MERGING PULL REQUESTS +## Merging Pull Requests This section describes how to cleanly merge PRs. @@ -164,7 +164,7 @@ git merge --no-ff pr-branch-name ### 5 Version -Modify tqdm/_version.py and amend the last (merge) commit: +Modify `tqdm/_version.py` and amend the last (merge) commit: ``` git add tqdm/_version.py @@ -178,7 +178,7 @@ git push origin master ``` -## BUILDING A RELEASE AND UPLOADING TO PYPI +## Building a Release and Uploading to PyPI Formally publishing requires additional steps: testing and tagging. @@ -199,7 +199,7 @@ display as `v{major}.{minor}.{patch}-{commit_hash}`. ### Upload -Build tqdm into a distributable python package: +Build `tqdm` into a distributable python package: ``` [python setup.py] make build @@ -233,21 +233,20 @@ cannot re-upload another with the same version number updating just the metadata is possible: `[python setup.py] make pypimeta` -## UPDATING GH-PAGES +## Updating Websites -The most important file is README.rst, which sould always be kept up-to-date +The most important file is `README.rst`, which sould always be kept up-to-date and in sync with the in-line source documentation. This will affect all of the following: - The [main repository site](https://github.com/tqdm/tqdm) which automatically - serves the latest README.rst as well as links to all of github's features. - This is the preferred online referral link for tqdm. + serves the latest `README.rst` as well as links to all of github's features. + This is the preferred online referral link for `tqdm`. - The [PyPi mirror](https://pypi.org/project/tqdm) which automatically - serves the latest release built from README.rst as well as links to past + serves the latest release built from `README.rst` as well as links to past releases. - Many external web crawlers. - Additionally (less maintained), there exists: - A [wiki] which is publicly editable. @@ -276,7 +275,7 @@ For experienced devs, once happy with local master: b) `twine upload -s -i $(git config user.signingkey) dist/tqdm-*` 10. create new release on https://github.com/tqdm/tqdm/releases a) add helpful release notes - b) attach dist/tqdm-* binaries (usually only *.whl*) + b) attach `dist/tqdm-*` binaries (usually only `*.whl*`) 11. run `make` in the `wiki` submodule to update release notes 12. run `make deploy` in the `docs` submodule to update website 13. accept the automated PR in the `feedstock` submodule to update conda diff --git a/LICENCE b/LICENCE index 7700869d8..0c4f76d13 100644 --- a/LICENCE +++ b/LICENCE @@ -7,7 +7,7 @@ Exceptions or notable authors are listed below in reverse chronological order: * files: * - MPLv2.0 2015-2018 (c) Casper da Costa-Luis + MPLv2.0 2015-2019 (c) Casper da Costa-Luis [casperdcl](https://github.com/casperdcl). * files: tqdm/_tqdm.py MIT 2016 (c) [PR #96] on behalf of Google Inc. @@ -20,8 +20,10 @@ in reverse chronological order: Mozilla Public Licence (MPL) v. 2.0 - Exhibit A ----------------------------------------------- -This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. +This Source Code Form is subject to the terms of the +Mozilla Public License, v. 2.0. +If a copy of the MPL was not distributed with this file, +You can obtain one at https://mozilla.org/MPL/2.0/. MIT License (MIT) diff --git a/MANIFEST.in b/MANIFEST.in index c77b2ff5e..488c5fae5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,6 +9,7 @@ include tox.ini # Test suite recursive-include tqdm/tests *.py +include requirements-dev.txt # Sub-packages recursive-include tqdm/autonotebook *.py diff --git a/Makefile b/Makefile index b3e6ed6cc..4e897f4a4 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,7 @@ # IMPORTANT: for compatibility with `python setup.py make [alias]`, ensure: # 1. Every alias is preceded by @[+]make (eg: @make alias) # 2. A maximum of one @make alias or command per line -# -# Sample makefile compatible with `python setup.py make`: -#``` -#all: -# @make test -# @make install -#test: -# nosetest -#install: -# python setup.py \ -# install -#``` +# see: https://github.com/tqdm/py-make/issues/1 .PHONY: alltests @@ -28,14 +17,17 @@ coverclean prebuildclean clean + toxclean installdev install build + buildupload pypi + help none help: - @python setup.py make + @python setup.py make -p alltests: @+make testcoverage @@ -48,7 +40,7 @@ all: @+make build flake8: - @+flake8 --max-line-length=80 --exclude .asv,.tox,.ipynb_checkpoints \ + @+flake8 --max-line-length=80 --exclude .asv,.tox,.ipynb_checkpoints,build \ -j 8 --count --statistics --exit-zero . test: diff --git a/README.rst b/README.rst index f10172839..101f6c629 100644 --- a/README.rst +++ b/README.rst @@ -870,7 +870,7 @@ There are also many |GitHub-Contributions| which we are grateful for. .. |Branch-Coverage-Status| image:: https://codecov.io/gh/tqdm/tqdm/branch/master/graph/badge.svg :target: https://codecov.io/gh/tqdm/tqdm .. |Codacy-Grade| image:: https://api.codacy.com/project/badge/Grade/3f965571598f44549c7818f29cdcf177 - :target: https://www.codacy.com/app/tqdm/tqdm?utm_source=github.com&utm_medium=referral&utm_content=tqdm/tqdm&utm_campaign=Badge_Grade + :target: https://www.codacy.com/app/tqdm/tqdm/dashboard .. |GitHub-Status| image:: https://img.shields.io/github/tag/tqdm/tqdm.svg?maxAge=86400&logo=github&logoColor=white :target: https://github.com/tqdm/tqdm/releases .. |GitHub-Forks| image:: https://img.shields.io/github/forks/tqdm/tqdm.svg?logo=github&logoColor=white @@ -905,8 +905,8 @@ There are also many |GitHub-Contributions| which we are grateful for. :target: https://www.openhub.net/p/tqdm?ref=Thin+badge .. |LICENCE| image:: https://img.shields.io/pypi/l/tqdm.svg :target: https://raw.githubusercontent.com/tqdm/tqdm/master/LICENCE -.. |DOI-URI| image:: https://zenodo.org/badge/21637/tqdm/tqdm.svg - :target: https://zenodo.org/badge/latestdoi/21637/tqdm/tqdm +.. |DOI-URI| image:: https://img.shields.io/badge/DOI-10.5281/zenodo.595120-blue.svg + :target: https://doi.org/10.5281/zenodo.595120 .. |interactive-demo| image:: https://img.shields.io/badge/demo-interactive-orange.svg?logo=jupyter :target: https://notebooks.rmotr.com/demo/gh/tqdm/tqdm .. |Screenshot-Jupyter1| image:: https://raw.githubusercontent.com/tqdm/tqdm/master/images/tqdm-jupyter-1.gif diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 000000000..fc129715e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,4 @@ +py-make>=0.1.0 # setup.py make/pymake +twine # pymake pypi +argopt # cd wiki && pymake +pydoc-markdown # cd docs && pymake diff --git a/setup.py b/setup.py index de4b434b5..132669b7a 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - import os try: from setuptools import setup, find_packages @@ -24,116 +23,28 @@ def find_packages(where='.'): # Executing makefile commands if specified if sys.argv[1].lower().strip() == 'make': - import shlex - from subprocess import check_call - try: # pragma: no cover - import ConfigParser - import StringIO - except ImportError: # pragma: no cover - # Python 3 compatibility - import configparser as ConfigParser - import io as StringIO - import re - - 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. - """ - # Parse Makefile using ConfigParser - ini_str = '[root]\n' # fake section to resemble valid *.ini - 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) - config = ConfigParser.RawConfigParser() - config.readfp(ini_fp) - aliases = config.options('root') - - # Extract commands for each alias - commands = {} - for alias in aliases: - if alias.lower() in ['.phony']: - continue - commands[alias] = config.get('root', alias)\ - .lstrip('\n').replace('\\\n', '').split('\n') - - # Command substitution (depth-first). - # If this is not possible because an alias points to another alias, - # then stop and put the current alias back in the queue to be - # processed again later (bottom-up). - - aliases_todo = list(commands.keys()) - commands_new = {} - while aliases_todo: - alias = aliases_todo.pop() - commands_new[alias] = [] - for cmd in commands[alias]: - # Ignore self-referencing (alias points to itself) - if cmd == alias: - pass - elif cmd in aliases: - # Append substituted full commands - if cmd in commands_new: - commands_new[alias].extend(commands_new[cmd]) - # Delay substituting another alias until it is substituted - else: - del commands_new[alias] - aliases_todo.insert(0, alias) - break - # Full command (no aliases) - else: - commands_new[alias].append(cmd) - commands = commands_new - # Prepending prefix to avoid conflicts with standard setup.py commands - # for alias in list(commands.keys()): - # commands['make_'+alias] = commands.pop(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/comment - if parsed_cmd: - if verbose: - print("Running command: " + cmd) - check_call(parsed_cmd, cwd=src_dir) - + import pymake # Filename of the makefile fpath = os.path.join(src_dir, '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 - args = sys.argv[2:] - invalid = set(args).difference(commands.keys()) - if not args or '--help' in args or invalid: - print("Shortcut to use commands via aliases. List of aliases:") - print(' '.join(alias for alias in sorted(commands.keys()))) - if invalid: - raise Exception("Cannot find: " + ' '.join(invalid)) - # Else process the commands for this alias - else: - for arg in args: - execute_makefile_commands(commands, arg, verbose=True) + pymake.main(['-f', fpath] + sys.argv[2:]) # Stop to avoid setup.py raising non-standard command error sys.exit(0) +extras_require = {} +requirements_dev = os.path.join(src_dir, 'requirements-dev.txt') +with io_open(requirements_dev, mode='r') as fd: + extras_require['dev'] = [i.strip().split('#', 1)[0].strip() + for i in fd.read().strip().split('\n')] + README_rst = '' fndoc = os.path.join(src_dir, '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', + long_description=README_rst, license='MPLv2.0, MIT Licences', author='Noam Yorav-Raphael', author_email='noamraph@gmail.com', @@ -142,10 +53,11 @@ def execute_makefile_commands(commands, alias, verbose=False): maintainer_email='python.tqdm@gmail.com', platforms=['any'], packages=['tqdm'] + ['tqdm.' + i for i in find_packages('tqdm')], + provides=['tqdm'], + extras_require=extras_require, entry_points={'console_scripts': ['tqdm=tqdm._main:main'], }, package_data={'tqdm': ['CONTRIBUTING.md', 'LICENCE', 'examples/*.py', - 'tqdm.1']}, - long_description=README_rst, + 'tqdm.1', 'requirements-dev.txt']}, python_requires='>=2.6, !=3.0.*, !=3.1.*', classifiers=[ # Trove classifiers @@ -186,12 +98,16 @@ def execute_makefile_commands(commands, alias, verbose=False): 'Programming Language :: Python :: Implementation :: IronPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Desktop Environment', + 'Topic :: Education :: Computer Aided Instruction (CAI)', 'Topic :: Education :: Testing', 'Topic :: Office/Business', 'Topic :: Other/Nonlisted Topic', + 'Topic :: Software Development :: Build Tools', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Software Development :: Pre-processors', 'Topic :: Software Development :: User Interfaces', + 'Topic :: System :: Installation/Setup', 'Topic :: System :: Logging', 'Topic :: System :: Monitoring', 'Topic :: System :: Shells', diff --git a/tox.ini b/tox.ini index f4e4e4c8a..fef7be2e3 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ deps = coverage coveralls commands = - nosetests --with-coverage --cover-package=tqdm --ignore-files="tests_p(erf|andas)\.py" -d -v tqdm/ + nosetests --with-coverage --cover-package=tqdm --ignore-files="tests_perf\.py" -d -v tqdm/ - coveralls [extra] @@ -22,22 +22,18 @@ deps = nose-timer codecov commands = - nosetests --with-coverage --with-timer --cover-package=tqdm --ignore-files="tests_p(erf|andas)\.py" -d -v tqdm/ + nosetests --with-coverage --with-timer --cover-package=tqdm --ignore-files="tests_perf\.py" -d -v tqdm/ - coveralls codecov [testenv] -# default tests (most things) -passenv = CI TRAVIS TRAVIS_* +passenv = CI TRAVIS TRAVIS_* TOXENV CODECOV_* deps = {[extra]deps} cython numpy pandas -commands = - nosetests --with-coverage --with-timer --cover-package=tqdm --ignore-files="tests_perf\.py" -d -v tqdm/ - - coveralls - codecov +commands = {[extra]commands} # no cython/numpy/pandas for py{py,py3,26,33,34} @@ -47,9 +43,12 @@ deps = nose coverage coveralls==1.2.0 + codecov pycparser==2.18 idna==2.7 -commands = {[coverage]commands} +commands = + {[coverage]commands} + codecov [testenv:pypy] deps = {[extra]deps}