diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 0000000000..02200b4aad --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,3 @@ +exclude_paths: + - 'versioneer.py' + - 'arch/_version.py' diff --git a/ci/performance.py b/ci/performance.py index 2ddc801598..63b6cc649d 100644 --- a/ci/performance.py +++ b/ci/performance.py @@ -6,11 +6,11 @@ from arch.tests.univariate.test_recursions import TestRecursions -if __name__ == '__main__': +if __name__ == "__main__": try: import numba # noqa: F401 except ImportError: - print('numba not available -- skipping performance tests') + print("numba not available -- skipping performance tests") sys.exit(0) t = TestRecursions() diff --git a/doc/source/conf.py b/doc/source/conf.py index 6b24fa1821..8283584a74 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -71,7 +71,7 @@ "sphinx.ext.napoleon", "IPython.sphinxext.ipython_console_highlighting", "IPython.sphinxext.ipython_directive", - "nbsphinx" + "nbsphinx", ] try: @@ -133,24 +133,24 @@ # sphinx_material theme options (see theme.conf for more information) html_theme_options = { - 'base_url': 'http://bashtage.github.io/arch/', - 'repo_url': 'https://github.com/bashtage/arch/', - 'repo_name': 'ARCH', + "base_url": "http://bashtage.github.io/arch/", + "repo_url": "https://github.com/bashtage/arch/", + "repo_name": "ARCH", # Set the name of the project to appear in the sidebar - "nav_title": project + u" " + version, - 'globaltoc_depth': 2, - 'globaltoc_collapse': True, - 'globaltoc_includehidden': True, - 'theme_color': '#2196f3', - 'color_primary': 'blue ', - 'color_accent': 'indigo', - 'html_minify': True, - 'css_minify': True, - 'master_doc': False + "nav_title": project + " " + version, + "globaltoc_depth": 2, + "globaltoc_collapse": True, + "globaltoc_includehidden": True, + "theme_color": "#2196f3", + "color_primary": "blue ", + "color_accent": "indigo", + "html_minify": True, + "css_minify": True, + "master_doc": False, } -html_favicon = 'images/favicon.ico' -html_logo = 'images/bw-logo.svg' +html_favicon = "images/favicon.ico" +html_logo = "images/bw-logo.svg" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. @@ -174,8 +174,7 @@ # html_sidebars = {} if not on_rtd: html_sidebars = { - "**": ["logo-text.html", "globaltoc.html", "searchbox.html", - "localtoc.html"] + "**": ["logo-text.html", "globaltoc.html", "searchbox.html", "localtoc.html"] } # If false, no module index is generated. @@ -192,15 +191,12 @@ # The paper size ("letterpaper" or "a4paper"). # # "papersize": "letterpaper", - # The font size ("10pt", "11pt" or "12pt"). # # "pointsize": "10pt", - # Additional stuff for the LaTeX preamble. # # "preamble": '', - # Latex figure (float) alignment # # "figure_align": "htbp", @@ -210,18 +206,14 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, "arch.tex", "arch Documentation", - "Kevin Sheppard", "manual"), + (master_doc, "arch.tex", "arch Documentation", "Kevin Sheppard", "manual"), ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, "arch", "arch Documentation", - [author], 1) -] +man_pages = [(master_doc, "arch", "arch Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -229,9 +221,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, "arch", "arch Documentation", - author, "arch", "ARCH models in Python", - "Finance/Econometrics"), + ( + master_doc, + "arch", + "arch Documentation", + author, + "arch", + "ARCH models in Python", + "Finance/Econometrics", + ), ] # -- Options for Epub output ------------------------------------------------- @@ -274,4 +272,4 @@ napoleon_use_admonition_for_references = True autosummary_generate = True -autoclass_content = 'class' +autoclass_content = "class" diff --git a/doc/source/images/favicon.py b/doc/source/images/favicon.py index 44393e9fcf..dfd7f74aea 100644 --- a/doc/source/images/favicon.py +++ b/doc/source/images/favicon.py @@ -6,68 +6,66 @@ import pandas_datareader as pdr import seaborn as sns -colors = sns.color_palette('muted') +colors = sns.color_palette("muted") NBINS = 7 -plt.rcParams['figure.figsize'] = (10, 10) +plt.rcParams["figure.figsize"] = (10, 10) start = datetime.datetime(1980, 1, 1) end = datetime.datetime(2020, 1, 1) -data = pdr.get_data_yahoo('^GSPC', start, end) -price = data['Adj Close'] -rets = 100 * price.resample('M').last().pct_change() +data = pdr.get_data_yahoo("^GSPC", start, end) +price = data["Adj Close"] +rets = 100 * price.resample("M").last().pct_change() -l, u = rets.quantile([.01, .99]) +l, u = rets.quantile([0.01, 0.99]) bins = np.linspace(l, u, NBINS) fig = plt.figure(frameon=False) fig.set_size_inches(8, 8) -ax = fig.add_subplot('111') +ax = fig.add_subplot("111") rwidth = np.diff(bins).mean() * 0.22 -_, _, patches = ax.hist(rets, bins=bins, rwidth=rwidth, - align='mid') # '#2196f3') +_, _, patches = ax.hist(rets, bins=bins, rwidth=rwidth, align="mid") # '#2196f3') for i, patch in enumerate(patches): patch.set_facecolor(colors[i]) ax.set_xticks([]) ax.set_yticks([]) -ax.set_ylabel('') +ax.set_ylabel("") sns.despine(left=True, bottom=True) fig.tight_layout(pad=1.0) -fig.savefig('favicon.svg', transparent=True, bbox_inches=0) -fig.savefig('favicon.png', transparent=True) +fig.savefig("favicon.svg", transparent=True, bbox_inches=0) +fig.savefig("favicon.png", transparent=True) fig = plt.figure(frameon=False) fig.set_size_inches(8, 8) -ax = fig.add_subplot('111') +ax = fig.add_subplot("111") rwidth = np.diff(bins).mean() * 0.22 -ax.hist(rets, bins=bins, rwidth=rwidth, align='mid', color='#ffffff') +ax.hist(rets, bins=bins, rwidth=rwidth, align="mid", color="#ffffff") ax.set_xticks([]) ax.set_yticks([]) -ax.set_ylabel('') +ax.set_ylabel("") sns.despine(left=True, bottom=True) fig.tight_layout(pad=1.0) -fig.savefig('logo.svg', transparent=True, bbox_inches=0) +fig.savefig("logo.svg", transparent=True, bbox_inches=0) -prop = matplotlib.font_manager.FontProperties('Roboto') +prop = matplotlib.font_manager.FontProperties("Roboto") prop.set_size(216) fig = plt.figure(frameon=False, figsize=(12, 4)) -ax = fig.add_subplot('111') +ax = fig.add_subplot("111") rwidth = np.diff(bins).mean() * 0.22 -_, _, patches = ax.hist(rets, bins=bins, rwidth=rwidth, - align='mid') +_, _, patches = ax.hist(rets, bins=bins, rwidth=rwidth, align="mid") for i, patch in enumerate(patches): patch.set_facecolor(colors[i]) ax.set_xticks([]) ax.set_yticks([]) -text = ax.text(11, 0, 'arch') +text = ax.text(11, 0, "arch") text.set_fontproperties(prop) text.set_fontsize(216) -text.set_color('#757575') +text.set_color("#757575") ax.set_ylim(0, 180) sns.despine(left=True, bottom=True) fig.tight_layout(pad=1.0) -fig.savefig('color-logo.png', transparent=True) -fig.savefig('color-logo.svg', transparent=True) +fig.savefig("color-logo.png", transparent=True) +fig.savefig("color-logo.svg", transparent=True) diff --git a/doc/source/images/hero.py b/doc/source/images/hero.py index a724809177..61b3018e6b 100644 --- a/doc/source/images/hero.py +++ b/doc/source/images/hero.py @@ -8,18 +8,18 @@ from arch import arch_model import arch.data.sp500 -warnings.simplefilter('ignore') -sns.set_style('whitegrid') -sns.mpl.rcParams['figure.figsize'] = (12, 3) +warnings.simplefilter("ignore") +sns.set_style("whitegrid") +sns.mpl.rcParams["figure.figsize"] = (12, 3) data = arch.data.sp500.load() -market = data['Adj Close'] +market = data["Adj Close"] returns = 100 * market.pct_change().dropna() am = arch_model(returns) res = am.fit(update_freq=5) -prop = matplotlib.font_manager.FontProperties('Roboto') +prop = matplotlib.font_manager.FontProperties("Roboto") def _set_tight_x(axis, index): @@ -32,9 +32,9 @@ def _set_tight_x(axis, index): fig = figure() ax = fig.add_subplot(1, 1, 1) vol = res.conditional_volatility -title = 'S&P 500 Annualized Conditional Volatility' -scales = {'D': 252, 'W': 52, 'M': 12} -vol = vol * np.sqrt(scales['D']) +title = "S&P 500 Annualized Conditional Volatility" +scales = {"D": 252, "W": 52, "M": 12} +vol = vol * np.sqrt(scales["D"]) ax.plot(res._index.values, vol) _set_tight_x(ax, res._index) @@ -43,13 +43,13 @@ def _set_tight_x(axis, index): title = ax.get_children()[7] title.set_fontproperties(prop) title.set_fontsize(26) -title.set_fontweight('ultralight') -title.set_fontstretch('ultra-condensed') -title.set_color('#757575') -ax.xaxis.label.set_color('#757575') -ax.yaxis.label.set_color('#757575') -ax.tick_params(axis='x', colors='#757575') -ax.tick_params(axis='y', colors='#757575') +title.set_fontweight("ultralight") +title.set_fontstretch("ultra-condensed") +title.set_color("#757575") +ax.xaxis.label.set_color("#757575") +ax.yaxis.label.set_color("#757575") +ax.tick_params(axis="x", colors="#757575") +ax.tick_params(axis="y", colors="#757575") fig.tight_layout(pad=1.0) -fig.savefig('hero.svg', transparent=True) -fig.savefig('hero.png', transparent=True) +fig.savefig("hero.svg", transparent=True) +fig.savefig("hero.png", transparent=True) diff --git a/setup.py b/setup.py index d19b5b04a6..0c00a972d7 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ from collections import defaultdict -from distutils.errors import (CCompilerError, DistutilsExecError, - DistutilsPlatformError) +from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError import fnmatch import os import sys @@ -11,10 +10,12 @@ import versioneer -CYTHON_COVERAGE = os.environ.get('ARCH_CYTHON_COVERAGE', '0') in ('true', '1', 'True') +CYTHON_COVERAGE = os.environ.get("ARCH_CYTHON_COVERAGE", "0") in ("true", "1", "True") if CYTHON_COVERAGE: - print('Building with coverage for cython modules, ARCH_CYTHON_COVERAGE=' + - os.environ['ARCH_CYTHON_COVERAGE']) + print( + "Building with coverage for cython modules, ARCH_CYTHON_COVERAGE=" + + os.environ["ARCH_CYTHON_COVERAGE"] + ) try: from Cython.Build import cythonize @@ -24,8 +25,9 @@ except ImportError: CYTHON_INSTALLED = False if CYTHON_COVERAGE: - raise ImportError('cython is required for cython coverage. Unset ' - 'ARCH_CYTHON_COVERAGE') + raise ImportError( + "cython is required for cython coverage. Unset " "ARCH_CYTHON_COVERAGE" + ) from setuptools.command.build_ext import build_ext as _build_ext @@ -50,23 +52,26 @@ # numpy is installed class build_ext(_build_ext): def build_extensions(self): - numpy_incl = pkg_resources.resource_filename('numpy', 'core/include') + numpy_incl = pkg_resources.resource_filename("numpy", "core/include") for ext in self.extensions: - if (hasattr(ext, 'include_dirs') and - numpy_incl not in ext.include_dirs): + if hasattr(ext, "include_dirs") and numpy_incl not in ext.include_dirs: ext.include_dirs.append(numpy_incl) _build_ext.build_extensions(self) -SETUP_REQUIREMENTS = {'numpy': '1.14', 'cython': '0.29.14'} +SETUP_REQUIREMENTS = {"numpy": "1.14", "cython": "0.29.14"} INSTALL_REQUIREMENTS = SETUP_REQUIREMENTS.copy() -INSTALL_REQUIREMENTS.update({'scipy': '1.0.1', - 'pandas': '0.23', - 'statsmodels': '0.9', - 'property_cached': '1.6.3'}) +INSTALL_REQUIREMENTS.update( + { + "scipy": "1.0.1", + "pandas": "0.23", + "statsmodels": "0.9", + "property_cached": "1.6.3", + } +) -cmdclass['build_ext'] = build_ext +cmdclass["build_ext"] = build_ext class BinaryDistribution(Distribution): @@ -78,7 +83,7 @@ class CleanCommand(Command): user_options = [] def run(self): - raise NotImplementedError('Use git clean -xfd instead') + raise NotImplementedError("Use git clean -xfd instead") def initialize_options(self): pass @@ -87,118 +92,149 @@ def finalize_options(self): pass -cmdclass['clean'] = CleanCommand +cmdclass["clean"] = CleanCommand try: - markdown = os.stat('README.md').st_mtime - if os.path.exists('README.rst'): - rst = os.stat('README.rst').st_mtime + markdown = os.stat("README.md").st_mtime + if os.path.exists("README.rst"): + rst = os.stat("README.rst").st_mtime else: rst = markdown - 1 if rst >= markdown: - with open('README.rst', 'r') as rst: + with open("README.rst", "r") as rst: description = rst.read() else: import pypandoc - osx_line_ending = '\r' - windows_line_ending = '\r\n' - linux_line_ending = '\n' + osx_line_ending = "\r" + windows_line_ending = "\r\n" + linux_line_ending = "\n" - description = pypandoc.convert_file('README.md', 'rst+smart') + description = pypandoc.convert_file("README.md", "rst+smart") description = description.replace(windows_line_ending, linux_line_ending) description = description.replace(osx_line_ending, linux_line_ending) - with open('README.rst', 'w') as rst: + with open("README.rst", "w") as rst: rst.write(description) except (ImportError, OSError): import warnings warnings.warn("Unable to convert README.md to README.rst", UserWarning) - description = open('README.md').read() + description = open("README.md").read() package_data = defaultdict(list) -filetypes = ['*.csv', '*.csv.gz'] -for root, _, filenames in os.walk(os.path.join(os.getcwd(), 'arch')): # noqa: E501 +filetypes = ["*.csv", "*.csv.gz"] +for root, _, filenames in os.walk(os.path.join(os.getcwd(), "arch")): # noqa: E501 matches = [] for filetype in filetypes: for filename in fnmatch.filter(filenames, filetype): matches.append(filename) if matches: - package_data['.'.join(os.path.relpath(root).split(os.path.sep))] = filetypes + package_data[".".join(os.path.relpath(root).split(os.path.sep))] = filetypes def run_setup(binary=True): if not binary: extensions = [] else: - directives = {'linetrace': CYTHON_COVERAGE} - macros = [('NPY_NO_DEPRECATED_API', '1'), - ('NPY_1_7_API_VERSION', '1')] + directives = {"linetrace": CYTHON_COVERAGE} + macros = [("NPY_NO_DEPRECATED_API", "1"), ("NPY_1_7_API_VERSION", "1")] if CYTHON_COVERAGE: - macros.append(('CYTHON_TRACE', '1')) + macros.append(("CYTHON_TRACE", "1")) ext_modules = [] - ext_modules.append(Extension("arch.univariate.recursions", - ["./arch/univariate/recursions.pyx"], - define_macros=macros)) - ext_modules.append(Extension("arch.bootstrap._samplers", - ["./arch/bootstrap/_samplers.pyx"], - define_macros=macros)) - extensions = cythonize(ext_modules, - force=CYTHON_COVERAGE, - compiler_directives=directives) - - setup(name='arch', - license='NCSA', - version=versioneer.get_version(), - description='ARCH for Python', - long_description=description, - author='Kevin Sheppard', - author_email='kevin.sheppard@economics.ox.ac.uk', - url='http://github.com/bashtage/arch', - packages=find_packages(), - ext_modules=extensions, - package_dir={'arch': './arch'}, - cmdclass=cmdclass, - keywords=['arch', 'ARCH', 'variance', 'econometrics', 'volatility', - 'finance', 'GARCH', 'bootstrap', 'random walk', 'unit root', - 'Dickey Fuller', 'time series', 'confidence intervals', - 'multiple comparisons', 'Reality Check', 'SPA', 'StepM'], - zip_safe=False, - include_package_data=False, - package_data=package_data, - distclass=BinaryDistribution, - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Financial and Insurance Industry', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'License :: OSI Approved', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Programming Language :: Python', - 'Programming Language :: Cython', - 'Topic :: Scientific/Engineering', - ], - install_requires=[key + '>=' + INSTALL_REQUIREMENTS[key] - for key in INSTALL_REQUIREMENTS], - setup_requires=[key + '>=' + SETUP_REQUIREMENTS[key] - for key in SETUP_REQUIREMENTS], - python_requires='>=3.6', - ) + ext_modules.append( + Extension( + "arch.univariate.recursions", + ["./arch/univariate/recursions.pyx"], + define_macros=macros, + ) + ) + ext_modules.append( + Extension( + "arch.bootstrap._samplers", + ["./arch/bootstrap/_samplers.pyx"], + define_macros=macros, + ) + ) + extensions = cythonize( + ext_modules, force=CYTHON_COVERAGE, compiler_directives=directives + ) + + setup( + name="arch", + license="NCSA", + version=versioneer.get_version(), + description="ARCH for Python", + long_description=description, + author="Kevin Sheppard", + author_email="kevin.sheppard@economics.ox.ac.uk", + url="http://github.com/bashtage/arch", + packages=find_packages(), + ext_modules=extensions, + package_dir={"arch": "./arch"}, + cmdclass=cmdclass, + keywords=[ + "arch", + "ARCH", + "variance", + "econometrics", + "volatility", + "finance", + "GARCH", + "bootstrap", + "random walk", + "unit root", + "Dickey Fuller", + "time series", + "confidence intervals", + "multiple comparisons", + "Reality Check", + "SPA", + "StepM", + ], + zip_safe=False, + include_package_data=False, + package_data=package_data, + distclass=BinaryDistribution, + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: End Users/Desktop", + "Intended Audience :: Financial and Insurance Industry", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "License :: OSI Approved", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX", + "Programming Language :: Python", + "Programming Language :: Cython", + "Topic :: Scientific/Engineering", + ], + install_requires=[ + key + ">=" + INSTALL_REQUIREMENTS[key] for key in INSTALL_REQUIREMENTS + ], + setup_requires=[ + key + ">=" + SETUP_REQUIREMENTS[key] for key in SETUP_REQUIREMENTS + ], + python_requires=">=3.6", + ) try: - build_binary = '--no-binary' not in sys.argv and CYTHON_INSTALLED - if '--no-binary' in sys.argv: - sys.argv.remove('--no-binary') + build_binary = "--no-binary" not in sys.argv and CYTHON_INSTALLED + if "--no-binary" in sys.argv: + sys.argv.remove("--no-binary") run_setup(binary=build_binary) -except (CCompilerError, DistutilsExecError, DistutilsPlatformError, IOError, ValueError): +except ( + CCompilerError, + DistutilsExecError, + DistutilsPlatformError, + IOError, + ValueError, +): run_setup(binary=False) import warnings diff --git a/versioneer.py b/versioneer.py index c816d30204..eb50b742d6 100644 --- a/versioneer.py +++ b/versioneer.py @@ -1,4 +1,3 @@ - # Version: 0.18 """The Versioneer - like a rocketeer, but for versions. @@ -306,11 +305,13 @@ def get_root(): setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") + err = ( + "Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND')." + ) raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools @@ -323,8 +324,10 @@ def get_root(): me_dir = os.path.normcase(os.path.splitext(me)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) if me_dir != vsr_dir: - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(me), versioneer_py)) + print( + "Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py) + ) except NameError: pass return root @@ -346,6 +349,7 @@ def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None + cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" @@ -370,17 +374,18 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None @@ -388,10 +393,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -416,7 +424,9 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, return stdout, p.returncode -LONG_VERSION_PY['git'] = ''' +LONG_VERSION_PY[ + "git" +] = ''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -991,7 +1001,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -1000,7 +1010,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -1008,19 +1018,26 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -1035,8 +1052,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -1044,10 +1060,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -1070,17 +1095,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out return pieces # tag @@ -1089,10 +1113,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -1103,13 +1129,13 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces @@ -1165,16 +1191,22 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -1203,11 +1235,13 @@ def versions_from_file(filename): contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) if not mo: - mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S + ) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) @@ -1216,8 +1250,7 @@ def versions_from_file(filename): def write_to_version_file(filename, versions): """Write the given version number to the given _version.py file.""" os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) + contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) @@ -1249,8 +1282,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -1364,11 +1396,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -1388,9 +1422,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } class VersioneerBadRootError(Exception): @@ -1413,8 +1451,9 @@ def get_versions(verbose=False): handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" + assert ( + cfg.versionfile_source is not None + ), "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) @@ -1468,9 +1507,13 @@ def get_versions(verbose=False): if verbose: print("unable to compute version") - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } def get_version(): @@ -1519,6 +1562,7 @@ def run(self): print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools @@ -1551,14 +1595,15 @@ def run(self): # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) + target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. # setup(console=[{ @@ -1579,17 +1624,21 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["build_exe"] = cmd_build_exe del cmds["build_py"] - if 'py2exe' in sys.modules: # py2exe enabled? + if "py2exe" in sys.modules: # py2exe enabled? try: from py2exe.distutils_buildexe import py2exe as _py2exe # py3 except ImportError: @@ -1608,13 +1657,17 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["py2exe"] = cmd_py2exe # we override different "sdist" commands for both environments @@ -1641,8 +1694,10 @@ def make_release_tree(self, base_dir, files): # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) + write_to_version_file( + target_versionfile, self._versioneer_generated_versions + ) + cmds["sdist"] = cmd_sdist return cmds @@ -1697,11 +1752,13 @@ def do_setup(): root = get_root() try: cfg = get_config_from_root(root) - except (EnvironmentError, configparser.NoSectionError, - configparser.NoOptionError) as e: + except ( + EnvironmentError, + configparser.NoSectionError, + configparser.NoOptionError, + ) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) + print("Adding sample versioneer config to setup.cfg", file=sys.stderr) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) @@ -1710,15 +1767,18 @@ def do_setup(): print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: @@ -1760,8 +1820,10 @@ def do_setup(): else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: - print(" appending versionfile_source ('%s') to MANIFEST.in" % - cfg.versionfile_source) + print( + " appending versionfile_source ('%s') to MANIFEST.in" + % cfg.versionfile_source + ) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: