From cb768ec25970dca813e06454ef73af602c3d53d0 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Tue, 19 Feb 2019 12:53:38 -0500 Subject: [PATCH 01/22] Renaming and switching to using autover --- .gitattributes | 1 - .gitignore | 14 + LICENSE.txt | 4 +- MANIFEST.in | 2 - conda.recipe/meta.yaml | 32 - pyproject.toml | 5 + setup.cfg | 8 - setup.py | 53 +- sphinx_ioam_theme/__init__.py | 9 - sphinx_ioam_theme/_version.py | 520 ----- sphinx_pyviz_theme/__init__.py | 12 + .../includes/ga.html | 0 .../includes/searchbox.html | 0 .../layout.html | 0 .../search.html | 0 .../static/css/main.css | 0 .../static/images/favicon.ico | Bin .../static/images/logo.png | Bin .../static/js/main.js | 0 .../theme.conf | 0 versioneer.py | 1822 ----------------- 21 files changed, 75 insertions(+), 2407 deletions(-) delete mode 100644 .gitattributes create mode 100644 .gitignore delete mode 100644 conda.recipe/meta.yaml create mode 100644 pyproject.toml delete mode 100644 sphinx_ioam_theme/__init__.py delete mode 100644 sphinx_ioam_theme/_version.py create mode 100644 sphinx_pyviz_theme/__init__.py rename {sphinx_ioam_theme => sphinx_pyviz_theme}/includes/ga.html (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/includes/searchbox.html (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/layout.html (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/search.html (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/static/css/main.css (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/static/images/favicon.ico (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/static/images/logo.png (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/static/js/main.js (100%) rename {sphinx_ioam_theme => sphinx_pyviz_theme}/theme.conf (100%) delete mode 100644 versioneer.py diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index cb250ef..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -sphinx_ioam_theme/_version.py export-subst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6836c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*~ +*.pyc +/dist +*.egg-info +pip-wheel-metadata +.pytest_cache +*__pycache__ + +# pyct +.doit.db +.tox + +# autover +*/.version diff --git a/LICENSE.txt b/LICENSE.txt index 4b56bb0..2905029 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017-2019, IOAM (ioam.github.com) +Copyright (c) 2017-2019, PyViz (pyviz.org) All rights reserved. Redistribution and use in source and binary forms, with or without @@ -13,7 +13,7 @@ met: documentation and/or other materials provided with the distribution. - * Neither the name of IOAM nor the names of its contributors + * Neither the name of PyViz nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/MANIFEST.in b/MANIFEST.in index bd29599..42eb410 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1 @@ -include versioneer.py -include sphinx_ioam_theme/_version.py include LICENSE.txt diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml deleted file mode 100644 index a87c3a2..0000000 --- a/conda.recipe/meta.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{% set sdata = load_setup_py_data() %} - -package: - name: sphinx_ioam_theme - version: {{ sdata['version'] | replace('v','') }} - -source: - path: .. - -build: - noarch: python - script: python setup.py install --single-version-externally-managed --record=record.txt - -requirements: - build: - - python - run: - - python {{ sdata['python_requires'] }} - {% for dep in sdata.get('install_requires',{}) %} - - {{ dep }} - {% endfor %} - - {% for option in sdata.get('extras_require',{}) %} - {% for dep in sdata['extras_require'][option] %} - - {{ dep }} - {% endfor %} - {% endfor %} - -about: - home: {{ sdata['url'] }} - summary: {{ sdata['description'] }} - license: {{ sdata['license'] }} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..506538c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[build-system] +requires = [ + "param >=1.7.0", + "setuptools >=30.3.0" +] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 3ef2145..5f465ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,11 +3,3 @@ universal = 1 [metadata] license_file = LICENSE.txt - -[versioneer] -VCS = git -versionfile_source = sphinx_ioam_theme/_version.py -versionfile_build = sphinx_ioam_theme/_version.py -tag_prefix = -parentdir_prefix = sphinx_ioam_theme- -style = pep440-pre diff --git a/setup.py b/setup.py index 8e3fea5..2364365 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,41 @@ from setuptools import setup -import versioneer +NAME = 'sphinx_pyviz_theme' +DESCRIPTION = "PyViz theme for use with nbsite" + +# duplicated from pyct.build.get_setup_version until pyct[build] >0.4.6 lands +def get_setup_version(root, reponame): + """ + Helper to get the current version from either git describe or the + .version file (if available) - allows for param to not be available. + Normally used in setup.py as follows: + >>> from pyct.build import get_setup_version + >>> version = get_setup_version(__file__, reponame) # noqa + """ + import os + import json + + filepath = os.path.abspath(os.path.dirname(root)) + version_file_path = os.path.join(filepath, reponame, '.version') + try: + from param import version + except: + version = None + if version is not None: + return version.Version.setup_version(filepath, reponame, archive_commit="$Format:%h$") + else: + print("WARNING: param>=1.6.0 unavailable. If you are installing a package, this warning can safely be ignored. If you are creating a package or otherwise operating in a git repository, you should install param>=1.6.0.") + return json.load(open(version_file_path, 'r'))['version_string'] setup_args = dict( - name='sphinx_ioam_theme', - version=versioneer.get_version(), - url="https://github.com/ioam/sphinx_ioam_theme", - description="For nbsite", + name=NAME, + version=get_setup_version(__file__, NAME), + url="https://github.com/pyviz-dev/sphinx_pyviz_theme", + description=DESCRIPTION, license="BSD-3", zip_safe=False, - packages=['sphinx_ioam_theme'], - package_data={'sphinx_ioam_theme': [ + packages=[NAME], + package_data={NAME: [ 'theme.conf', '*.html', 'includes/*.html', @@ -21,14 +46,20 @@ include_package_data=True, entry_points = { 'sphinx.html_themes': [ - 'sphinx_ioam_theme = sphinx_ioam_theme', + 'sphinx_pyviz_theme = sphinx_pyviz_theme', ] }, python_requires = ">=2.7", - install_requires =[ - "sphinx" - ] + install_requires = [ + "sphinx", + ], + extras_require = { + 'build': [ + "param >=1.7.0", + "setuptools", + ] + } ) if __name__=="__main__": diff --git a/sphinx_ioam_theme/__init__.py b/sphinx_ioam_theme/__init__.py deleted file mode 100644 index c821ff2..0000000 --- a/sphinx_ioam_theme/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from os import path - -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions - -def setup(app): - app.add_html_theme('sphinx_ioam_theme', path.abspath(path.dirname(__file__))) - diff --git a/sphinx_ioam_theme/_version.py b/sphinx_ioam_theme/_version.py deleted file mode 100644 index 2a895d1..0000000 --- a/sphinx_ioam_theme/_version.py +++ /dev/null @@ -1,520 +0,0 @@ - -# 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 -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.18 (https://github.com/warner/python-versioneer) - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "$Format:%d$" - git_full = "$Format:%H$" - git_date = "$Format:%ci$" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "pep440-pre" - cfg.tag_prefix = "" - cfg.parentdir_prefix = "sphinx_ioam_theme-" - cfg.versionfile_source = "sphinx_ioam_theme/_version.py" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -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): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - 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)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = p.communicate()[0].strip() - if sys.version_info[0] >= 3: - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, p.returncode - return stdout, p.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - 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} - 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)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # 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)]) - 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 - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # 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)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - 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):] - if verbose: - print("picking %s" % r) - 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} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - 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) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # 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) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if 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) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - 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)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - 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() - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Eexceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -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} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} diff --git a/sphinx_pyviz_theme/__init__.py b/sphinx_pyviz_theme/__init__.py new file mode 100644 index 0000000..e22efef --- /dev/null +++ b/sphinx_pyviz_theme/__init__.py @@ -0,0 +1,12 @@ +import os.path +import param + +NAME = "sphinx_pyviz_theme" + +# version comes from git if available, otherwise from .version file +__version__ = str(param.version.Version(fpath=__file__, archive_commit="$Format:%h$", + reponame=NAME)) + +def setup(app): + app.add_html_theme('sphinx_pyviz_theme', os.path.abspath(os.path.dirname(__file__))) + diff --git a/sphinx_ioam_theme/includes/ga.html b/sphinx_pyviz_theme/includes/ga.html similarity index 100% rename from sphinx_ioam_theme/includes/ga.html rename to sphinx_pyviz_theme/includes/ga.html diff --git a/sphinx_ioam_theme/includes/searchbox.html b/sphinx_pyviz_theme/includes/searchbox.html similarity index 100% rename from sphinx_ioam_theme/includes/searchbox.html rename to sphinx_pyviz_theme/includes/searchbox.html diff --git a/sphinx_ioam_theme/layout.html b/sphinx_pyviz_theme/layout.html similarity index 100% rename from sphinx_ioam_theme/layout.html rename to sphinx_pyviz_theme/layout.html diff --git a/sphinx_ioam_theme/search.html b/sphinx_pyviz_theme/search.html similarity index 100% rename from sphinx_ioam_theme/search.html rename to sphinx_pyviz_theme/search.html diff --git a/sphinx_ioam_theme/static/css/main.css b/sphinx_pyviz_theme/static/css/main.css similarity index 100% rename from sphinx_ioam_theme/static/css/main.css rename to sphinx_pyviz_theme/static/css/main.css diff --git a/sphinx_ioam_theme/static/images/favicon.ico b/sphinx_pyviz_theme/static/images/favicon.ico similarity index 100% rename from sphinx_ioam_theme/static/images/favicon.ico rename to sphinx_pyviz_theme/static/images/favicon.ico diff --git a/sphinx_ioam_theme/static/images/logo.png b/sphinx_pyviz_theme/static/images/logo.png similarity index 100% rename from sphinx_ioam_theme/static/images/logo.png rename to sphinx_pyviz_theme/static/images/logo.png diff --git a/sphinx_ioam_theme/static/js/main.js b/sphinx_pyviz_theme/static/js/main.js similarity index 100% rename from sphinx_ioam_theme/static/js/main.js rename to sphinx_pyviz_theme/static/js/main.js diff --git a/sphinx_ioam_theme/theme.conf b/sphinx_pyviz_theme/theme.conf similarity index 100% rename from sphinx_ioam_theme/theme.conf rename to sphinx_pyviz_theme/theme.conf diff --git a/versioneer.py b/versioneer.py deleted file mode 100644 index 64fea1c..0000000 --- a/versioneer.py +++ /dev/null @@ -1,1822 +0,0 @@ - -# Version: 0.18 - -"""The Versioneer - like a rocketeer, but for versions. - -The Versioneer -============== - -* like a rocketeer, but for versions! -* https://github.com/warner/python-versioneer -* Brian Warner -* License: Public Domain -* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy -* [![Latest Version] -(https://pypip.in/version/versioneer/badge.svg?style=flat) -](https://pypi.python.org/pypi/versioneer/) -* [![Build Status] -(https://travis-ci.org/warner/python-versioneer.png?branch=master) -](https://travis-ci.org/warner/python-versioneer) - -This is a tool for managing a recorded version number in distutils-based -python projects. The goal is to remove the tedious and error-prone "update -the embedded version string" step from your release process. Making a new -release should be as easy as recording a new tag in your version-control -system, and maybe making new tarballs. - - -## Quick Install - -* `pip install versioneer` to somewhere to your $PATH -* add a `[versioneer]` section to your setup.cfg (see below) -* run `versioneer install` in your source tree, commit the results - -## Version Identifiers - -Source trees come from a variety of places: - -* a version-control system checkout (mostly used by developers) -* a nightly tarball, produced by build automation -* a snapshot tarball, produced by a web-based VCS browser, like github's - "tarball from tag" feature -* a release tarball, produced by "setup.py sdist", distributed through PyPI - -Within each source tree, the version identifier (either a string or a number, -this tool is format-agnostic) can come from a variety of places: - -* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows - about recent "tags" and an absolute revision-id -* the name of the directory into which the tarball was unpacked -* an expanded VCS keyword ($Id$, etc) -* a `_version.py` created by some earlier build step - -For released software, the version identifier is closely related to a VCS -tag. Some projects use tag names that include more than just the version -string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool -needs to strip the tag prefix to extract the version identifier. For -unreleased software (between tags), the version identifier should provide -enough information to help developers recreate the same tree, while also -giving them an idea of roughly how old the tree is (after version 1.2, before -version 1.3). Many VCS systems can report a description that captures this, -for example `git describe --tags --dirty --always` reports things like -"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the -0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has -uncommitted changes. - -The version identifier is used for multiple purposes: - -* to allow the module to self-identify its version: `myproject.__version__` -* to choose a name and prefix for a 'setup.py sdist' tarball - -## Theory of Operation - -Versioneer works by adding a special `_version.py` file into your source -tree, where your `__init__.py` can import it. This `_version.py` knows how to -dynamically ask the VCS tool for version information at import time. - -`_version.py` also contains `$Revision$` markers, and the installation -process marks `_version.py` to have this marker rewritten with a tag name -during the `git archive` command. As a result, generated tarballs will -contain enough information to get the proper version. - -To allow `setup.py` to compute a version too, a `versioneer.py` is added to -the top level of your source tree, next to `setup.py` and the `setup.cfg` -that configures it. This overrides several distutils/setuptools commands to -compute the version when invoked, and changes `setup.py build` and `setup.py -sdist` to replace `_version.py` with a small static file that contains just -the generated version data. - -## Installation - -See [INSTALL.md](./INSTALL.md) for detailed installation instructions. - -## Version-String Flavors - -Code which uses Versioneer can learn about its version string at runtime by -importing `_version` from your main `__init__.py` file and running the -`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can -import the top-level `versioneer.py` and run `get_versions()`. - -Both functions return a dictionary with different flavors of version -information: - -* `['version']`: A condensed version string, rendered using the selected - style. This is the most commonly used value for the project's version - string. The default "pep440" style yields strings like `0.11`, - `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section - below for alternative styles. - -* `['full-revisionid']`: detailed revision identifier. For Git, this is the - full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". - -* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the - commit date in ISO 8601 format. This will be None if the date is not - available. - -* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that - this is only accurate if run in a VCS checkout, otherwise it is likely to - be False or None - -* `['error']`: if the version string could not be computed, this will be set - to a string describing the problem, otherwise it will be None. It may be - useful to throw an exception in setup.py if this is set, to avoid e.g. - creating tarballs with a version string of "unknown". - -Some variants are more useful than others. Including `full-revisionid` in a -bug report should allow developers to reconstruct the exact code being tested -(or indicate the presence of local changes that should be shared with the -developers). `version` is suitable for display in an "about" box or a CLI -`--version` output: it can be easily compared against release notes and lists -of bugs fixed in various releases. - -The installer adds the following text to your `__init__.py` to place a basic -version in `YOURPROJECT.__version__`: - - from ._version import get_versions - __version__ = get_versions()['version'] - del get_versions - -## Styles - -The setup.cfg `style=` configuration controls how the VCS information is -rendered into a version string. - -The default style, "pep440", produces a PEP440-compliant string, equal to the -un-prefixed tag name for actual releases, and containing an additional "local -version" section with more detail for in-between builds. For Git, this is -TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags ---dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the -tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and -that this commit is two revisions ("+2") beyond the "0.11" tag. For released -software (exactly equal to a known tag), the identifier will only contain the -stripped tag, e.g. "0.11". - -Other styles are available. See [details.md](details.md) in the Versioneer -source tree for descriptions. - -## Debugging - -Versioneer tries to avoid fatal errors: if something goes wrong, it will tend -to return a version of "0+unknown". To investigate the problem, run `setup.py -version`, which will run the version-lookup code in a verbose mode, and will -display the full contents of `get_versions()` (including the `error` string, -which may help identify what went wrong). - -## Known Limitations - -Some situations are known to cause problems for Versioneer. This details the -most significant ones. More can be found on Github -[issues page](https://github.com/warner/python-versioneer/issues). - -### Subprojects - -Versioneer has limited support for source trees in which `setup.py` is not in -the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are -two common reasons why `setup.py` might not be in the root: - -* Source trees which contain multiple subprojects, such as - [Buildbot](https://github.com/buildbot/buildbot), which contains both - "master" and "slave" subprojects, each with their own `setup.py`, - `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI - distributions (and upload multiple independently-installable tarballs). -* Source trees whose main purpose is to contain a C library, but which also - provide bindings to Python (and perhaps other langauges) in subdirectories. - -Versioneer will look for `.git` in parent directories, and most operations -should get the right version string. However `pip` and `setuptools` have bugs -and implementation details which frequently cause `pip install .` from a -subproject directory to fail to find a correct version string (so it usually -defaults to `0+unknown`). - -`pip install --editable .` should work correctly. `setup.py install` might -work too. - -Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in -some later version. - -[Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking -this issue. The discussion in -[PR #61](https://github.com/warner/python-versioneer/pull/61) describes the -issue from the Versioneer side in more detail. -[pip PR#3176](https://github.com/pypa/pip/pull/3176) and -[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve -pip to let Versioneer work correctly. - -Versioneer-0.16 and earlier only looked for a `.git` directory next to the -`setup.cfg`, so subprojects were completely unsupported with those releases. - -### Editable installs with setuptools <= 18.5 - -`setup.py develop` and `pip install --editable .` allow you to install a -project into a virtualenv once, then continue editing the source code (and -test) without re-installing after every change. - -"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a -convenient way to specify executable scripts that should be installed along -with the python package. - -These both work as expected when using modern setuptools. When using -setuptools-18.5 or earlier, however, certain operations will cause -`pkg_resources.DistributionNotFound` errors when running the entrypoint -script, which must be resolved by re-installing the package. This happens -when the install happens with one version, then the egg_info data is -regenerated while a different version is checked out. Many setup.py commands -cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into -a different virtualenv), so this can be surprising. - -[Bug #83](https://github.com/warner/python-versioneer/issues/83) describes -this one, but upgrading to a newer version of setuptools should probably -resolve it. - -### Unicode version strings - -While Versioneer works (and is continually tested) with both Python 2 and -Python 3, it is not entirely consistent with bytes-vs-unicode distinctions. -Newer releases probably generate unicode version strings on py2. It's not -clear that this is wrong, but it may be surprising for applications when then -write these strings to a network connection or include them in bytes-oriented -APIs like cryptographic checksums. - -[Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates -this question. - - -## Updating Versioneer - -To upgrade your project to a new release of Versioneer, do the following: - -* install the new Versioneer (`pip install -U versioneer` or equivalent) -* edit `setup.cfg`, if necessary, to include any new configuration settings - indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. -* re-run `versioneer install` in your source tree, to replace - `SRC/_version.py` -* commit any changed files - -## Future Directions - -This tool is designed to make it easily extended to other version-control -systems: all VCS-specific components are in separate directories like -src/git/ . The top-level `versioneer.py` script is assembled from these -components by running make-versioneer.py . In the future, make-versioneer.py -will take a VCS name as an argument, and will construct a version of -`versioneer.py` that is specific to the given VCS. It might also take the -configuration arguments that are currently provided manually during -installation by editing setup.py . Alternatively, it might go the other -direction and include code from all supported VCS systems, reducing the -number of intermediate scripts. - - -## License - -To make Versioneer easier to embed, all its code is dedicated to the public -domain. The `_version.py` that it creates is also in the public domain. -Specifically, both are released under the Creative Commons "Public Domain -Dedication" license (CC0-1.0), as described in -https://creativecommons.org/publicdomain/zero/1.0/ . - -""" - -from __future__ import print_function -try: - import configparser -except ImportError: - import ConfigParser as configparser -import errno -import json -import os -import re -import subprocess -import sys - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_root(): - """Get the project root directory. - - We require that all commands are run from the project root, i.e. the - directory that contains setup.py, setup.cfg, and versioneer.py . - """ - root = os.path.realpath(os.path.abspath(os.getcwd())) - 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)): - # allow 'python path/to/setup.py COMMAND' - root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) - 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').") - raise VersioneerBadRootError(err) - try: - # Certain runtime workflows (setup.py install/develop in a setuptools - # tree) execute all dependencies in a single python process, so - # "versioneer" may be imported multiple times, and python's shared - # module-import table will cache the first one. So we can't use - # os.path.dirname(__file__), as that will find whichever - # versioneer.py was first imported, even in later projects. - me = os.path.realpath(os.path.abspath(__file__)) - 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)) - except NameError: - pass - return root - - -def get_config_from_root(root): - """Read the project setup.cfg file to determine Versioneer config.""" - # This might raise EnvironmentError (if setup.cfg is missing), or - # configparser.NoSectionError (if it lacks a [versioneer] section), or - # configparser.NoOptionError (if it lacks "VCS="). See the docstring at - # the top of versioneer.py for instructions on writing your setup.cfg . - setup_cfg = os.path.join(root, "setup.cfg") - parser = configparser.SafeConfigParser() - with open(setup_cfg, "r") as f: - parser.readfp(f) - VCS = parser.get("versioneer", "VCS") # mandatory - - 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 "" - cfg.versionfile_source = get(parser, "versionfile_source") - cfg.versionfile_build = get(parser, "versionfile_build") - cfg.tag_prefix = get(parser, "tag_prefix") - if cfg.tag_prefix in ("''", '""'): - cfg.tag_prefix = "" - cfg.parentdir_prefix = get(parser, "parentdir_prefix") - cfg.verbose = get(parser, "verbose") - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -# these dictionaries contain VCS-specific tools -LONG_VERSION_PY = {} -HANDLERS = {} - - -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): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - 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)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %s" % dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %s" % (commands,)) - return None, None - stdout = p.communicate()[0].strip() - if sys.version_info[0] >= 3: - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %s (error)" % dispcmd) - print("stdout was %s" % stdout) - return None, p.returncode - return stdout, p.returncode - - -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 -# directories (produced by setup.py build) will contain a much shorter file -# that just contains the computed version number. - -# This file is released into the public domain. Generated by -# versioneer-0.18 (https://github.com/warner/python-versioneer) - -"""Git implementation of _version.py.""" - -import errno -import os -import re -import subprocess -import sys - - -def get_keywords(): - """Get the keywords needed to look up the version information.""" - # these strings will be replaced by git during git-archive. - # setup.py/versioneer.py will grep for the variable names, so they must - # each be defined on a line of their own. _version.py will just call - # get_keywords(). - git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" - git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" - git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" - keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} - return keywords - - -class VersioneerConfig: - """Container for Versioneer configuration parameters.""" - - -def get_config(): - """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py - cfg = VersioneerConfig() - cfg.VCS = "git" - cfg.style = "%(STYLE)s" - cfg.tag_prefix = "%(TAG_PREFIX)s" - cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" - cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" - cfg.verbose = False - return cfg - - -class NotThisMethod(Exception): - """Exception raised if a method is not valid for the current scenario.""" - - -LONG_VERSION_PY = {} -HANDLERS = {} - - -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): - """Call the given command(s).""" - assert isinstance(commands, list) - p = None - for c in commands: - 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)) - break - except EnvironmentError: - e = sys.exc_info()[1] - if e.errno == errno.ENOENT: - continue - if verbose: - print("unable to run %%s" %% dispcmd) - print(e) - return None, None - else: - if verbose: - print("unable to find command, tried %%s" %% (commands,)) - return None, None - stdout = p.communicate()[0].strip() - if sys.version_info[0] >= 3: - stdout = stdout.decode() - if p.returncode != 0: - if verbose: - print("unable to run %%s (error)" %% dispcmd) - print("stdout was %%s" %% stdout) - return None, p.returncode - return stdout, p.returncode - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - 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} - 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)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # 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)]) - 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 - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # 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)]) - if verbose: - print("discarding '%%s', no digits" %% ",".join(refs - tags)) - if verbose: - print("likely tags: %%s" %% ",".join(sorted(tags))) - 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):] - if verbose: - print("picking %%s" %% r) - 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} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - 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) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # 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) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if 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) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%%s'" - %% describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - 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)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - 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() - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post.dev%%d" %% pieces["distance"] - else: - # exception #1 - rendered = "0.post.dev%%d" %% pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%%s" %% pieces["short"] - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%%s" %% pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Eexceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%%d" %% pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -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} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%%s'" %% style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -def get_versions(): - """Get version information or return default if unable to do so.""" - # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have - # __file__, we can work backwards from there to the root. Some - # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which - # case we can only use expanded keywords. - - cfg = get_config() - verbose = cfg.verbose - - try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) - except NotThisMethod: - pass - - try: - root = os.path.realpath(__file__) - # versionfile_source is the relative path from the top of the source - # tree (where the .git directory might live) to this file. Invert - # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): - root = os.path.dirname(root) - except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} - - try: - pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) - return render(pieces, cfg.style) - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - except NotThisMethod: - pass - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} -''' - - -@register_vcs_handler("git", "get_keywords") -def git_get_keywords(versionfile_abs): - """Extract version information from the given file.""" - # the code embedded in _version.py can just fetch the value of these - # keywords. When used from setup.py, we don't want to import _version.py, - # so we do it with a regexp instead. This function is not used from - # _version.py. - keywords = {} - try: - f = open(versionfile_abs, "r") - for line in f.readlines(): - if line.strip().startswith("git_refnames ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["refnames"] = mo.group(1) - if line.strip().startswith("git_full ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["full"] = mo.group(1) - if line.strip().startswith("git_date ="): - mo = re.search(r'=\s*"(.*)"', line) - if mo: - keywords["date"] = mo.group(1) - f.close() - except EnvironmentError: - pass - return keywords - - -@register_vcs_handler("git", "keywords") -def git_versions_from_keywords(keywords, tag_prefix, verbose): - """Get version information from git keywords.""" - if not keywords: - raise NotThisMethod("no keywords at all, weird") - date = keywords.get("date") - if date is not None: - # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant - # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 - # -like" string, which we must then edit to make compliant), because - # it's been around since git-1.5.3, and it's too difficult to - # discover which version we're using, or to work around using an - # older one. - date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - refnames = keywords["refnames"].strip() - if refnames.startswith("$Format"): - if verbose: - print("keywords are unexpanded, not using") - raise NotThisMethod("unexpanded keywords, not a git-archive tarball") - refs = set([r.strip() for r in refnames.strip("()").split(",")]) - # 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)]) - 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 - # expansion behaves like git log --decorate=short and strips out the - # refs/heads/ and refs/tags/ prefixes that would let us distinguish - # 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)]) - if verbose: - print("discarding '%s', no digits" % ",".join(refs - tags)) - if verbose: - print("likely tags: %s" % ",".join(sorted(tags))) - 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):] - if verbose: - print("picking %s" % r) - 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} - - -@register_vcs_handler("git", "pieces_from_vcs") -def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): - """Get version from 'git describe' in the root of the source tree. - - This only gets called if the git-archive 'subst' keywords were *not* - expanded, and _version.py hasn't already been rewritten with a short - version string, meaning we're inside a checked out source tree. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - - 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) - raise NotThisMethod("'git rev-parse --git-dir' returned error") - - # 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) - # --long was added in git-1.5.5 - if describe_out is None: - raise NotThisMethod("'git describe' failed") - describe_out = describe_out.strip() - full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) - if full_out is None: - raise NotThisMethod("'git rev-parse' failed") - full_out = full_out.strip() - - pieces = {} - pieces["long"] = full_out - pieces["short"] = full_out[:7] # maybe improved later - pieces["error"] = None - - # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] - # TAG might have hyphens. - git_describe = describe_out - - # look for -dirty suffix - dirty = git_describe.endswith("-dirty") - pieces["dirty"] = dirty - if 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) - if not mo: - # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) - return pieces - - # tag - full_tag = mo.group(1) - if not full_tag.startswith(tag_prefix): - 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)) - return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] - - # distance: number of commits since tag - pieces["distance"] = int(mo.group(2)) - - # commit: short hex revision ID - pieces["short"] = mo.group(3) - - else: - # HEX: no tags - pieces["closest-tag"] = None - 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() - pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) - - return pieces - - -def do_vcs_install(manifest_in, versionfile_source, ipy): - """Git-specific installation logic for Versioneer. - - For Git, this means creating/changing .gitattributes to mark _version.py - for export-subst keyword substitution. - """ - GITS = ["git"] - if sys.platform == "win32": - GITS = ["git.cmd", "git.exe"] - files = [manifest_in, versionfile_source] - if ipy: - files.append(ipy) - try: - me = __file__ - if me.endswith(".pyc") or me.endswith(".pyo"): - me = os.path.splitext(me)[0] + ".py" - versioneer_file = os.path.relpath(me) - except NameError: - versioneer_file = "versioneer.py" - files.append(versioneer_file) - present = False - try: - f = open(".gitattributes", "r") - for line in f.readlines(): - if line.strip().startswith(versionfile_source): - if "export-subst" in line.strip().split()[1:]: - present = True - f.close() - except EnvironmentError: - pass - if not present: - f = open(".gitattributes", "a+") - f.write("%s export-subst\n" % versionfile_source) - f.close() - files.append(".gitattributes") - run_command(GITS, ["add", "--"] + files) - - -def versions_from_parentdir(parentdir_prefix, root, verbose): - """Try to determine the version from the parent directory name. - - Source tarballs conventionally unpack into a directory that includes both - the project name and a version string. We will also support searching up - two directory levels for an appropriately named parent directory - """ - rootdirs = [] - - 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} - 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)) - raise NotThisMethod("rootdir doesn't start with parentdir_prefix") - - -SHORT_VERSION_PY = """ -# This file was generated by 'versioneer.py' (0.18) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -%s -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) -""" - - -def versions_from_file(filename): - """Try to determine the version from _version.py if present.""" - try: - with open(filename) as f: - 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) - if not mo: - 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)) - - -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=(",", ": ")) - with open(filename, "w") as f: - f.write(SHORT_VERSION_PY % contents) - - print("set %s to '%s'" % (filename, versions["version"])) - - -def plus_or_dot(pieces): - """Return a + if we don't already have one, else return a .""" - if "+" in pieces.get("closest-tag", ""): - return "." - return "+" - - -def render_pep440(pieces): - """Build up version string, with post-release "local version identifier". - - Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you - get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty - - Exceptions: - 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += plus_or_dot(pieces) - rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - else: - # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) - if pieces["dirty"]: - rendered += ".dirty" - return rendered - - -def render_pep440_pre(pieces): - """TAG[.post.devDISTANCE] -- No -dirty. - - Exceptions: - 1: no tags. 0.post.devDISTANCE - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += ".post.dev%d" % pieces["distance"] - else: - # exception #1 - rendered = "0.post.dev%d" % pieces["distance"] - return rendered - - -def render_pep440_post(pieces): - """TAG[.postDISTANCE[.dev0]+gHEX] . - - The ".dev0" means dirty. Note that .dev0 sorts backwards - (a dirty tree will appear "older" than the corresponding clean one), - but you shouldn't be releasing software with -dirty anyways. - - Exceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += plus_or_dot(pieces) - rendered += "g%s" % pieces["short"] - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - rendered += "+g%s" % pieces["short"] - return rendered - - -def render_pep440_old(pieces): - """TAG[.postDISTANCE[.dev0]] . - - The ".dev0" means dirty. - - Eexceptions: - 1: no tags. 0.postDISTANCE[.dev0] - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"] or pieces["dirty"]: - rendered += ".post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - else: - # exception #1 - rendered = "0.post%d" % pieces["distance"] - if pieces["dirty"]: - rendered += ".dev0" - return rendered - - -def render_git_describe(pieces): - """TAG[-DISTANCE-gHEX][-dirty]. - - Like 'git describe --tags --dirty --always'. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - if pieces["distance"]: - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -def render_git_describe_long(pieces): - """TAG-DISTANCE-gHEX[-dirty]. - - Like 'git describe --tags --dirty --always -long'. - The distance/hash is unconditional. - - Exceptions: - 1: no tags. HEX[-dirty] (note: no 'g' prefix) - """ - if pieces["closest-tag"]: - rendered = pieces["closest-tag"] - rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) - else: - # exception #1 - rendered = pieces["short"] - if pieces["dirty"]: - rendered += "-dirty" - return rendered - - -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} - - if not style or style == "default": - style = "pep440" # the default - - if style == "pep440": - rendered = render_pep440(pieces) - elif style == "pep440-pre": - rendered = render_pep440_pre(pieces) - elif style == "pep440-post": - rendered = render_pep440_post(pieces) - elif style == "pep440-old": - rendered = render_pep440_old(pieces) - elif style == "git-describe": - rendered = render_git_describe(pieces) - elif style == "git-describe-long": - rendered = render_git_describe_long(pieces) - else: - raise ValueError("unknown style '%s'" % style) - - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} - - -class VersioneerBadRootError(Exception): - """The project root directory is unknown or missing key files.""" - - -def get_versions(verbose=False): - """Get the project version from whatever source is available. - - Returns dict with two keys: 'version' and 'full'. - """ - if "versioneer" in sys.modules: - # see the discussion in cmdclass.py:get_cmdclass() - del sys.modules["versioneer"] - - root = get_root() - cfg = get_config_from_root(root) - - assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" - 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.tag_prefix is not None, "please set versioneer.tag_prefix" - - versionfile_abs = os.path.join(root, cfg.versionfile_source) - - # extract version from first of: _version.py, VCS command (e.g. 'git - # describe'), parentdir. This is meant to work for developers using a - # source checkout, for users of a tarball created by 'setup.py sdist', - # and for users of a tarball/zipball created by 'git archive' or github's - # download-from-tag feature or the equivalent in other VCSes. - - get_keywords_f = handlers.get("get_keywords") - from_keywords_f = handlers.get("keywords") - if get_keywords_f and from_keywords_f: - try: - keywords = get_keywords_f(versionfile_abs) - ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) - if verbose: - print("got version from expanded keyword %s" % ver) - return ver - except NotThisMethod: - pass - - try: - ver = versions_from_file(versionfile_abs) - if verbose: - print("got version from file %s %s" % (versionfile_abs, ver)) - return ver - except NotThisMethod: - pass - - from_vcs_f = handlers.get("pieces_from_vcs") - if from_vcs_f: - try: - pieces = from_vcs_f(cfg.tag_prefix, root, verbose) - ver = render(pieces, cfg.style) - if verbose: - print("got version from VCS %s" % ver) - return ver - except NotThisMethod: - pass - - try: - if cfg.parentdir_prefix: - ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) - if verbose: - print("got version from parentdir %s" % ver) - return ver - except NotThisMethod: - pass - - if verbose: - print("unable to compute version") - - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} - - -def get_version(): - """Get the short version string for this project.""" - return get_versions()["version"] - - -def get_cmdclass(): - """Get the custom setuptools/distutils subclasses used by Versioneer.""" - if "versioneer" in sys.modules: - del sys.modules["versioneer"] - # this fixes the "python setup.py develop" case (also 'install' and - # 'easy_install .'), in which subdependencies of the main project are - # built (using setup.py bdist_egg) in the same python process. Assume - # a main project A and a dependency B, which use different versions - # of Versioneer. A's setup.py imports A's Versioneer, leaving it in - # sys.modules by the time B's setup.py is executed, causing B to run - # with the wrong versioneer. Setuptools wraps the sub-dep builds in a - # sandbox that restores sys.modules to it's pre-build state, so the - # parent is protected against the child's "import versioneer". By - # removing ourselves from sys.modules here, before the child build - # happens, we protect the child from the parent's versioneer too. - # Also see https://github.com/warner/python-versioneer/issues/52 - - cmds = {} - - # we add "version" to both distutils and setuptools - from distutils.core import Command - - class cmd_version(Command): - description = "report generated version string" - user_options = [] - boolean_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - vers = get_versions(verbose=True) - print("Version: %s" % vers["version"]) - print(" full-revisionid: %s" % vers.get("full-revisionid")) - print(" dirty: %s" % vers.get("dirty")) - 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 - # - # most invocation pathways end up running build_py: - # distutils/build -> build_py - # distutils/install -> distutils/build ->.. - # setuptools/bdist_wheel -> distutils/install ->.. - # setuptools/bdist_egg -> distutils/install_lib -> build_py - # setuptools/install -> bdist_egg ->.. - # setuptools/develop -> ? - # pip install: - # copies source tree to a tempdir before running egg_info/etc - # if .git isn't copied too, 'git describe' will fail - # then does setup.py bdist_wheel, or sometimes setup.py install - # setup.py egg_info -> ? - - # we override different "build_py" commands for both environments - if "setuptools" in sys.modules: - from setuptools.command.build_py import build_py as _build_py - else: - from distutils.command.build_py import build_py as _build_py - - class cmd_build_py(_build_py): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - _build_py.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) - 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=[{ - # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION - # "product_version": versioneer.get_version(), - # ... - - class cmd_build_exe(_build_exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _build_exe.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, - }) - cmds["build_exe"] = cmd_build_exe - del cmds["build_py"] - - if 'py2exe' in sys.modules: # py2exe enabled? - try: - from py2exe.distutils_buildexe import py2exe as _py2exe # py3 - except ImportError: - from py2exe.build_exe import py2exe as _py2exe # py2 - - class cmd_py2exe(_py2exe): - def run(self): - root = get_root() - cfg = get_config_from_root(root) - versions = get_versions() - target_versionfile = cfg.versionfile_source - print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, versions) - - _py2exe.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, - }) - cmds["py2exe"] = cmd_py2exe - - # we override different "sdist" commands for both environments - if "setuptools" in sys.modules: - from setuptools.command.sdist import sdist as _sdist - else: - from distutils.command.sdist import sdist as _sdist - - class cmd_sdist(_sdist): - def run(self): - versions = get_versions() - self._versioneer_generated_versions = versions - # unless we update this, the command will keep using the old - # version - self.distribution.metadata.version = versions["version"] - return _sdist.run(self) - - def make_release_tree(self, base_dir, files): - root = get_root() - cfg = get_config_from_root(root) - _sdist.make_release_tree(self, base_dir, files) - # now locate _version.py in the new base_dir directory - # (remembering that it may be a hardlink) and replace it with an - # 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) - cmds["sdist"] = cmd_sdist - - return cmds - - -CONFIG_ERROR = """ -setup.cfg is missing the necessary Versioneer configuration. You need -a section like: - - [versioneer] - VCS = git - style = pep440 - versionfile_source = src/myproject/_version.py - versionfile_build = myproject/_version.py - tag_prefix = - parentdir_prefix = myproject- - -You will also need to edit your setup.py to use the results: - - import versioneer - setup(version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), ...) - -Please read the docstring in ./versioneer.py for configuration instructions, -edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. -""" - -SAMPLE_CONFIG = """ -# See the docstring in versioneer.py for instructions. Note that you must -# re-run 'versioneer.py setup' after changing this section, and commit the -# resulting files. - -[versioneer] -#VCS = git -#style = pep440 -#versionfile_source = -#versionfile_build = -#tag_prefix = -#parentdir_prefix = - -""" - -INIT_PY_SNIPPET = """ -from ._version import get_versions -__version__ = get_versions()['version'] -del get_versions -""" - - -def do_setup(): - """Main VCS-independent setup function for installing Versioneer.""" - root = get_root() - try: - cfg = get_config_from_root(root) - 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) - with open(os.path.join(root, "setup.cfg"), "a") as f: - f.write(SAMPLE_CONFIG) - print(CONFIG_ERROR, file=sys.stderr) - return 1 - - 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") - if os.path.exists(ipy): - try: - with open(ipy, "r") as f: - old = f.read() - except EnvironmentError: - old = "" - if INIT_PY_SNIPPET not in old: - print(" appending to %s" % ipy) - with open(ipy, "a") as f: - f.write(INIT_PY_SNIPPET) - else: - print(" %s unmodified" % ipy) - else: - print(" %s doesn't exist, ok" % ipy) - ipy = None - - # Make sure both the top-level "versioneer.py" and versionfile_source - # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so - # they'll be copied into source distributions. Pip won't be able to - # install the package without this. - manifest_in = os.path.join(root, "MANIFEST.in") - simple_includes = set() - try: - with open(manifest_in, "r") as f: - for line in f: - if line.startswith("include "): - for include in line.split()[1:]: - simple_includes.add(include) - except EnvironmentError: - pass - # That doesn't cover everything MANIFEST.in can do - # (http://docs.python.org/2/distutils/sourcedist.html#commands), so - # it might give some false negatives. Appending redundant 'include' - # lines is safe, though. - if "versioneer.py" not in simple_includes: - print(" appending 'versioneer.py' to MANIFEST.in") - with open(manifest_in, "a") as f: - f.write("include versioneer.py\n") - 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) - with open(manifest_in, "a") as f: - f.write("include %s\n" % cfg.versionfile_source) - else: - print(" versionfile_source already in MANIFEST.in") - - # Make VCS-specific changes. For git, this means creating/changing - # .gitattributes to mark _version.py for export-subst keyword - # substitution. - do_vcs_install(manifest_in, cfg.versionfile_source, ipy) - return 0 - - -def scan_setup_py(): - """Validate the contents of setup.py against Versioneer's expectations.""" - found = set() - setters = False - errors = 0 - with open("setup.py", "r") as f: - for line in f.readlines(): - if "import versioneer" in line: - found.add("import") - if "versioneer.get_cmdclass()" in line: - found.add("cmdclass") - if "versioneer.get_version()" in line: - found.add("get_version") - if "versioneer.VCS" in line: - setters = True - if "versioneer.versionfile_source" in line: - setters = True - if len(found) != 3: - print("") - print("Your setup.py appears to be missing some important items") - print("(but I might be wrong). Please make sure it has something") - print("roughly like the following:") - print("") - print(" import versioneer") - print(" setup( version=versioneer.get_version(),") - print(" cmdclass=versioneer.get_cmdclass(), ...)") - print("") - errors += 1 - if setters: - print("You should remove lines like 'versioneer.VCS = ' and") - print("'versioneer.versionfile_source = ' . This configuration") - print("now lives in setup.cfg, and should be removed from setup.py") - print("") - errors += 1 - return errors - - -if __name__ == "__main__": - cmd = sys.argv[1] - if cmd == "setup": - errors = do_setup() - errors += scan_setup_py() - if errors: - sys.exit(1) From 8d73205fe15f3f0889b55cff40659775a6986b85 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Thu, 9 May 2019 16:23:47 -0400 Subject: [PATCH 02/22] Added option to enable two-level nav bar (#2) --- sphinx_pyviz_theme/layout.html | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/sphinx_pyviz_theme/layout.html b/sphinx_pyviz_theme/layout.html index 384dab0..8887288 100644 --- a/sphinx_pyviz_theme/layout.html +++ b/sphinx_pyviz_theme/layout.html @@ -71,15 +71,40 @@ Menu - + {% if second_nav %} +
+ + +
+ {% endif %}
From 032f8f007876ad6f08e8ebaeaaa58f1c45d798bf Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 12:57:13 -0400 Subject: [PATCH 03/22] Unminifying css and using sphinx templating (#5) * Removing liga and making lists show numbers * Unminifying and using templating * Making input a darker color * Adding theme option to control second_nav --- sphinx_pyviz_theme/layout.html | 35 +- sphinx_pyviz_theme/static/css/main.css | 1 - sphinx_pyviz_theme/static/css/main.css_t | 2232 ++++++++++++++++++++++ sphinx_pyviz_theme/theme.conf | 4 + 4 files changed, 2252 insertions(+), 20 deletions(-) delete mode 100644 sphinx_pyviz_theme/static/css/main.css create mode 100644 sphinx_pyviz_theme/static/css/main.css_t diff --git a/sphinx_pyviz_theme/layout.html b/sphinx_pyviz_theme/layout.html index 8887288..e7a7a0d 100644 --- a/sphinx_pyviz_theme/layout.html +++ b/sphinx_pyviz_theme/layout.html @@ -8,7 +8,7 @@ {% if title == "<no title>" %} {{ shorttitle|striptags|e }} {% else %} - {{ title|striptags|e }} — {{ shorttitle|striptags|e }} + {{ title|striptags|e }} — {{ shorttitle|striptags|e }} {% endif %} @@ -38,7 +38,7 @@ {% if theme_custom_css %} {% endif %} - + @@ -71,38 +71,35 @@ Menu
- {% if second_nav %} + {% if theme_second_nav|tobool %}
-
{% endif %} diff --git a/sphinx_pyviz_theme/static/css/main.css b/sphinx_pyviz_theme/static/css/main.css deleted file mode 100644 index 5f09088..0000000 --- a/sphinx_pyviz_theme/static/css/main.css +++ /dev/null @@ -1 +0,0 @@ -button,input[type="button"],input[type="reset"],input[type="submit"]{appearance:none;background-color:#F16A25;border:0;border-radius:0px;color:#fff;cursor:pointer;display:inline-block;font-family:"Noto Sans",sans-serif;font-size:14px;-webkit-font-smoothing:antialiased;font-weight:600;line-height:1;padding:.75em 1.5em;text-decoration:none;transition:background-color 150ms ease;user-select:none;vertical-align:middle;white-space:nowrap}button:hover,button:focus,input[type="button"]:hover,input[type="button"]:focus,input[type="reset"]:hover,input[type="reset"]:focus,input[type="submit"]:hover,input[type="submit"]:focus{background-color:#c1551e;color:#fff}button:disabled,input[type="button"]:disabled,input[type="reset"]:disabled,input[type="submit"]:disabled{cursor:not-allowed;opacity:0.5}button:disabled:hover,input[type="button"]:disabled:hover,input[type="reset"]:disabled:hover,input[type="submit"]:disabled:hover{background-color:#F16A25}fieldset{background-color:#e8e8e7;border:1px solid #e8e8e7;margin:0 0 .75em;padding:1.5em}input,label,select{display:block;font-family:"Noto Sans",sans-serif;font-size:14px}label{font-weight:600;margin-bottom:.375em}label.required::after{content:"*"}label abbr{display:none}input[type="color"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="email"],input[type="month"],input[type="number"],input[type="password"],input[type="search"],input[type="tel"],input[type="text"],input[type="time"],input[type="url"],input[type="week"],input:not([type]),textarea,select[multiple=multiple]{background-color:#fff;border:1px solid #e8e8e7;border-radius:0px;box-shadow:inset 0 1px 3px rgba(0,0,0,0.06);box-sizing:border-box;font-family:"Noto Sans",sans-serif;font-size:14px;margin-bottom:.75em;padding:.5em;transition:border-color 150ms ease;width:100%}input[type="color"]:hover,input[type="date"]:hover,input[type="datetime"]:hover,input[type="datetime-local"]:hover,input[type="email"]:hover,input[type="month"]:hover,input[type="number"]:hover,input[type="password"]:hover,input[type="search"]:hover,input[type="tel"]:hover,input[type="text"]:hover,input[type="time"]:hover,input[type="url"]:hover,input[type="week"]:hover,input:not([type]):hover,textarea:hover,select[multiple=multiple]:hover{border-color:#babab9}input[type="color"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="email"]:focus,input[type="month"]:focus,input[type="number"]:focus,input[type="password"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="text"]:focus,input[type="time"]:focus,input[type="url"]:focus,input[type="week"]:focus,input:not([type]):focus,textarea:focus,select[multiple=multiple]:focus{border-color:#F16A25;box-shadow:inset 0 1px 3px rgba(0,0,0,0.06),0 0 5px rgba(237,90,15,0.7);outline:none}input[type="color"]:disabled,input[type="date"]:disabled,input[type="datetime"]:disabled,input[type="datetime-local"]:disabled,input[type="email"]:disabled,input[type="month"]:disabled,input[type="number"]:disabled,input[type="password"]:disabled,input[type="search"]:disabled,input[type="tel"]:disabled,input[type="text"]:disabled,input[type="time"]:disabled,input[type="url"]:disabled,input[type="week"]:disabled,input:not([type]):disabled,textarea:disabled,select[multiple=multiple]:disabled{background-color:#f2f2f2;cursor:not-allowed}input[type="color"]:disabled:hover,input[type="date"]:disabled:hover,input[type="datetime"]:disabled:hover,input[type="datetime-local"]:disabled:hover,input[type="email"]:disabled:hover,input[type="month"]:disabled:hover,input[type="number"]:disabled:hover,input[type="password"]:disabled:hover,input[type="search"]:disabled:hover,input[type="tel"]:disabled:hover,input[type="text"]:disabled:hover,input[type="time"]:disabled:hover,input[type="url"]:disabled:hover,input[type="week"]:disabled:hover,input:not([type]):disabled:hover,textarea:disabled:hover,select[multiple=multiple]:disabled:hover{border:1px solid #e8e8e7}textarea{resize:vertical}input[type="search"]{appearance:none}input[type="checkbox"],input[type="radio"]{display:inline;margin-right:.375em}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block}input[type="file"]{margin-bottom:.75em;width:100%}select{margin-bottom:1.5em;max-width:100%;width:auto}dl{margin-bottom:.75em}dl dt{margin-top:.75em;font-weight:600;color:#4c4c4c}body{color:#2f2f2f;font-family:"Noto Sans",sans-serif;font-feature-settings:"kern", "liga", "pnum";font-size:14px;line-height:1.5}h1,h2,h3,h4,h5,h6{font-family:"Source Sans Pro",sans-serif;font-size:14px;line-height:1.2}p{margin:0 0 .75em}a{color:#F16A25;text-decoration:none;transition:color 150ms ease}a:active,a:focus,a:hover{color:#b5501c;text-decoration:underline}hr{border-bottom:1px solid #e8e8e7;border-left:0;border-right:0;border-top:0;margin:1.5em 0}img,picture{margin:0;max-width:100%}code,pre{color:#902040;background-color:#fff}html{box-sizing:border-box}*,*::after,*::before{box-sizing:inherit}body{margin:0em;padding:0em;color:#2f2f2f;font-family:"Noto Sans",sans-serif;font-size:12px}@media screen and (min-width: 900px){body{font-size:14px}}.wrapper,.section .content{max-width:68em;margin-left:auto;margin-right:auto;max-width:none;position:relative;z-index:9999;padding-left:2em;padding-right:2em}.wrapper::after,.section .content::after{clear:both;content:"";display:table}.wrapper::after,.section .content::after{clear:both;content:"";display:table}.title,.section h1,.section.docs .content h1{color:#F16A25;font-family:"Noto Sans",sans-serif;font-weight:400;font-size:2.4285714286em}.section .content{padding:0.5em 24px 2em 24px}@media screen and (min-width: 640px){.section .content{padding:0.5em 60px 2em 60px}}.section .content p{margin:1em 0;line-height:1.4em}.section .content p+ul{margin-top:-0.5em}.section .content li{list-style-type:disc;list-style-position:inherit}.section h3{text-transform:uppercase;color:#F16A25;font-family:"Source Sans Pro",sans-serif;font-weight:600;font-size:1.2857142857em}.section h4{font-size:1.1428571429em}.section .small-title-with-sub .sub{font-weight:400;text-transform:none;float:right;font-size:14px}.section img{border-color:#2f2f2f;border-style:solid;border-width:1px}.section .caption{margin-top:0.25em;font-family:"Source Sans Pro",sans-serif;font-weight:400}.section.dark{background-color:#e8e8e7}.section.light h3{margin-bottom:1.5em}.navigation{background-color:#2f2f2f;border-bottom:1px solid #161616;min-height:40px;width:100%;z-index:999;font-family:"Source Sans Pro",sans-serif}.navigation ul,.navigation ol{list-style-type:none;margin:0;padding:0}.navigation .logo{float:left;max-height:40px;padding-left:0.2em 0;padding-right:2em;margin-left:-5px}.navigation .logo img{max-height:40px;padding:0.2em 0}.navigation .logo-text{font-family:"HelveticaNeue-Light","Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;color:#c4c3c3;display:inline-block;text-decoration:none;font-size:1.1428571429em;position:absolute;left:60px;top:8px}.navigation .navigation-menu-button{color:#F5C33C;display:block;float:right;line-height:40px;margin:0;text-decoration:none;text-transform:uppercase;font-size:.8571428571em}@media screen and (min-width: 640px){.navigation .navigation-menu-button{display:none}}.navigation .navigation-menu-button:focus,.navigation .navigation-menu-button:hover{color:#e8e8e7}.navigation nav{float:none;z-index:9999999}@media screen and (min-width: 640px){.navigation nav{float:right}}.navigation ul.navigation-menu{clear:both;display:none;margin:-1em auto 1em auto;overflow:visible;padding:0;width:100%;z-index:9999}.navigation ul.navigation-menu.show{display:block}@media screen and (min-width: 640px){.navigation ul.navigation-menu{display:inline;margin:0;padding:0}}.navigation ul li.nav-link{font-size:.8571428571em;background:#2f2f2f;display:block;line-height:1.5;overflow:hidden;padding-right:0.8em;text-align:right;width:100%;z-index:9999}@media screen and (min-width: 640px){.navigation ul li.nav-link{background:transparent;display:inline;line-height:40px;text-decoration:none;width:auto}.navigation ul li.nav-link:last-child{padding-right:0}.navigation ul li.nav-link:last-child>a{padding-right:0}}.navigation ul li.nav-link a{color:#F5C33C;display:inline-block;text-decoration:none;text-transform:uppercase}@media screen and (min-width: 640px){.navigation ul li.nav-link a{padding-right:1em}}.navigation ul li.nav-link a:focus,.navigation ul li.nav-link a:hover{color:#e8e8e7}.navigation .active-nav-item a{border-bottom:1px solid #e8e8e7;padding-bottom:3px}.second-nav{background-color:#902040;min-height:1.2em;text-transform:uppercase;padding:0.5em 24px}.second-nav::after{clear:both;content:"";display:table}@media screen and (min-width: 640px){.second-nav{padding:0.5em 60px}}.second-nav ul,.second-nav ol{list-style-type:none;margin:0;padding:0}.second-nav ul li.nav-link{background:inherit;display:inline;line-height:1.5em;padding-right:2em}.second-nav ul li.nav-link a{font-size:.8571428571em;font-family:"Source Sans Pro",sans-serif;color:#e8e8e7}.second-nav ul li.nav-link a:focus,.second-nav ul li.nav-link a:hover{text-decoration:underline}.second-nav ul li.nav-link a.current{color:#F5C33C}.second-nav .navigation-menu{float:left}.second-nav .navigation-tools{clear:both;display:block;padding-top:0.4em}.second-nav .navigation-tools::after{clear:both;content:"";display:table}@media screen and (min-width: 640px){.second-nav .navigation-tools{background:transparent;clear:none;float:right;padding-top:0}}.second-nav li.more.nav-link{padding-right:0}@media screen and (min-width: 640px){.second-nav li.more.nav-link{padding-right:1em}}.second-nav li.more.nav-link>ul>li:first-child a{padding-top:1em}.second-nav li.more.nav-link a{margin-right:1em}.second-nav li.more.nav-link>a{padding-right:0.6em}.second-nav li.more.nav-link>a:after{position:absolute;top:auto;right:-.4em;bottom:auto;left:auto;content:'\25BE';color:#e8e8e7}.second-nav li.more{overflow:visible;padding-right:0}.second-nav li.more a{padding-right:0.8em}.second-nav li.more>a{padding-right:1.6em;position:relative}@media screen and (min-width: 640px){.second-nav li.more>a{margin-right:1em}}.second-nav li.more>a:after{content:'›';font-size:1.2em;position:absolute;right:.5em}.second-nav li.more:focus>.submenu,.second-nav li.more:hover>.submenu{display:block}@media screen and (min-width: 640px){.second-nav li.more{padding-right:0.8em;position:relative}}.second-nav ul.submenu{display:none;z-index:10;background-color:#902040}@media screen and (min-width: 640px){.second-nav ul.submenu{padding:1em;position:absolute;top:1.5em}}@media screen and (min-width: 640px){.second-nav ul.submenu .submenu{left:11.8em;top:0}}.footer{background:#4c4c4c;font-family:"Source Sans Pro",sans-serif;padding:2em 24px}@media screen and (min-width: 640px){.footer{padding:2em 60px}}.footer .footer-links{max-width:68em;margin-left:auto;margin-right:auto;max-width:none;-webkit-box-pack:left;-moz-box-pack:left;box-pack:left;-webkit-justify-content:left;-moz-justify-content:left;-ms-justify-content:left;-o-justify-content:left;justify-content:left;-ms-flex-pack:left}.footer .footer-links::after{clear:both;content:"";display:table}.footer ul{float:left;display:block;margin-right:2.3576515979%;width:31.7615656014%;padding:0}.footer ul:last-child{margin-right:0}@media screen and (min-width: 640px){.footer ul{float:left;display:block;margin-right:2.3576515979%;width:14.7019570017%}.footer ul:last-child{margin-right:0}}.footer .copyright{margin-right:0}@media screen and (min-width: 640px){.footer .copyright{float:left;display:block;margin-right:2.3576515979%;width:48.821174201%}.footer .copyright:last-child{margin-right:0}}.footer .copyright>li{color:#e8e8e7;font-size:.8571428571em;float:left}@media screen and (min-width: 640px){.footer .copyright>li{float:right}}.footer li{list-style:none;text-align:left;font-size:.8571428571em}.footer li>a{color:#e8e8e7;text-decoration:none}.footer li>a:focus,.footer li>a:hover{color:#F16A25}.footer li>.footer-title{color:#F5C33C;font-size:1em;margin-bottom:0.4em;text-transform:uppercase;font-weight:400}.content.title-content{padding-top:0.5em;padding-bottom:2.5em;margin-bottom:0}.content.title-content .title,.content.title-content .section h1,.section .content.title-content h1{margin:0.5em 0 0 0;line-height:1em;padding:0}.content.title-content .subtitle{color:#4c4c4c;font-family:"Noto Sans",sans-serif;font-weight:400}.mosaic-backdrop{line-height:0;position:relative;width:100%;overflow:hidden}.mosaic-backdrop ul{list-style-type:none;margin:0;padding:0;width:125%;overflow:hidden;height:75px}.mosaic-backdrop ul li{float:left;width:100px}.mosaic-backdrop ul li img{border:none;height:auto;left:0;position:relative;top:0;width:100%}.mosaic-backdrop .copy{z-index:999}.mosaic-backdrop .overlay{background-color:transparent;background-image:-webkit-linear-gradient(top, transparent,#fff);background-image:linear-gradient(to bottom,transparent,#fff);display:block;position:absolute;top:40px;right:0px;bottom:0px;left:0px}.getting-started{float:left;display:block;margin-right:2.3576515979%;width:31.7615656014%;padding-right:1em}.getting-started:last-child{margin-right:0}.examples{float:left;display:block;margin-right:2.3576515979%;width:48.821174201%;padding-right:2em}.examples:last-child{margin-right:0}.value-prop .lead{color:#F16A25;font-size:1.5714285714em}.value-prop:first-child{padding-top:1.5em}.value-prop:last-child{padding-bottom:1.3em}.highlight code,.highlight pre{color:#087d88;background-color:#f4f4f4;padding:0.5em 1em;font-size:.8571428571em;border-top-left-radius:5px;border-top-right-radius:5px;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-bottom-left-radius:5px;border-top-left-radius:5px;border-bottom-right-radius:5px;border-top-right-radius:5px}.highlight .hll{background-color:#ffffcc}.highlight .c{color:#408080;font-style:italic}.highlight .err{border:1px solid #FF0000}.highlight .k{color:#008000;font-weight:bold}.highlight .o{color:#666666}.highlight .cm{color:#408080;font-style:italic}.highlight .cp{color:#BC7A00}.highlight .c1{color:#408080;font-style:italic}.highlight .cs{color:#408080;font-style:italic}.highlight .gd{color:#A00000}.highlight .ge{font-style:italic}.highlight .gr{color:#FF0000}.highlight .gh{color:#000080;font-weight:bold}.highlight .gi{color:#00A000}.highlight .go{color:#808080}.highlight .gp{color:#000080;font-weight:bold}.highlight .gs{font-weight:bold}.highlight .gu{color:#800080;font-weight:bold}.highlight .gt{color:#0040D0}.highlight .kc{color:#008000;font-weight:bold}.highlight .kd{color:#008000;font-weight:bold}.highlight .kn{color:#008000;font-weight:bold}.highlight .kp{color:#008000}.highlight .kr{color:#008000;font-weight:bold}.highlight .kt{color:#B00040}.highlight .m{color:#666666}.highlight .s{color:#BA2121}.highlight .na{color:#7D9029}.highlight .nb{color:#008000}.highlight .nc{color:#0000FF;font-weight:bold}.highlight .no{color:#880000}.highlight .nd{color:#AA22FF}.highlight .ni{color:#999999;font-weight:bold}.highlight .ne{color:#D2413A;font-weight:bold}.highlight .nf{color:#0000FF}.highlight .nl{color:#A0A000}.highlight .nn{color:#0000FF;font-weight:bold}.highlight .nt{color:#008000;font-weight:bold}.highlight .nv{color:#19177C}.highlight .ow{color:#AA22FF;font-weight:bold}.highlight .w{color:#bbbbbb}.highlight .mf{color:#666666}.highlight .mh{color:#666666}.highlight .mi{color:#666666}.highlight .mo{color:#666666}.highlight .sb{color:#BA2121}.highlight .sc{color:#BA2121}.highlight .sd{color:#BA2121;font-style:italic}.highlight .s2{color:#BA2121}.highlight .se{color:#BB6622;font-weight:bold}.highlight .sh{color:#BA2121}.highlight .si{color:#BB6688;font-weight:bold}.highlight .sx{color:#008000}.highlight .sr{color:#BB6688}.highlight .s1{color:#BA2121}.highlight .ss{color:#19177C}.highlight .bp{color:#008000}.highlight .vc{color:#19177C}.highlight .vg{color:#19177C}.highlight .vi{color:#19177C}.highlight .il{color:#666666}div.clearer{clear:both}div.related{width:100%;font-size:90%}div.related h3{display:none}div.related ul{margin:0;padding:0 0 0 10px;list-style:none}div.related li{display:inline}div.related li.right{float:right;margin-right:5px}div.sphinxsidebarwrapper{padding:10px 5px 0 10px}div.sphinxsidebar{float:left;width:230px;margin-left:-100%;font-size:90%}div.sphinxsidebar ul{list-style:none}div.sphinxsidebar ul ul,div.sphinxsidebar ul.want-points{margin-left:20px;list-style:square}div.sphinxsidebar ul ul{margin-top:0;margin-bottom:0}div.sphinxsidebar form{margin-top:10px}div.sphinxsidebar input{border:1px solid #98dbcc;font-family:sans-serif;font-size:1em}div.sphinxsidebar #searchbox input[type="text"]{width:170px}div.sphinxsidebar #searchbox input[type="submit"]{width:30px}img{border:0;max-width:100%}ul.search{margin:10px 0 0 20px;padding:0}ul.search li{padding:5px 0 5px 20px;background-image:url(file.png);background-repeat:no-repeat;background-position:0 7px}ul.search li a{font-weight:bold}ul.search li div.context{color:#888;margin:2px 0 0 30px;text-align:left}ul.keywordmatches li.goodmatch a{font-weight:bold}table.contentstable{width:90%}table.contentstable p.biglink{line-height:150%}a.biglink{font-size:1.3em}span.linkdescr{font-style:italic;padding-top:5px;font-size:90%}table.indextable{width:100%}table.indextable td{text-align:left;vertical-align:top}table.indextable dl,table.indextable dd{margin-top:0;margin-bottom:0}table.indextable tr.pcap{height:10px}table.indextable tr.cap{margin-top:10px;background-color:#f2f2f2}img.toggler{margin-right:3px;margin-top:3px;cursor:pointer}div.modindex-jumpbox{border-top:1px solid #ddd;border-bottom:1px solid #ddd;margin:1em 0 1em 0;padding:0.4em}div.genindex-jumpbox{border-top:1px solid #ddd;border-bottom:1px solid #ddd;margin:1em 0 1em 0;padding:0.4em}a.headerlink{visibility:hidden}h1:hover>a.headerlink,h2:hover>a.headerlink,h3:hover>a.headerlink,h4:hover>a.headerlink,h5:hover>a.headerlink,h6:hover>a.headerlink,dt:hover>a.headerlink,caption:hover>a.headerlink,p.caption:hover>a.headerlink,div.code-block-caption:hover>a.headerlink{visibility:visible}div.body p.caption{text-align:inherit}div.body td{text-align:left}.field-list ul{padding-left:1em}.first{margin-top:0 !important}p.rubric{margin-top:30px;font-weight:bold}img.align-left,.figure.align-left,object.align-left{clear:left;float:left;margin-right:1em}img.align-right,.figure.align-right,object.align-right{clear:right;float:right;margin-left:1em}img.align-center,.figure.align-center,object.align-center{display:block;margin-left:auto;margin-right:auto}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}div.sidebar{margin:0 0 0.5em 1em;border:1px solid #ddb;padding:7px 7px 0 7px;background-color:#ffe;width:40%;float:right}p.sidebar-title{font-weight:bold}div.topic{border:1px solid #ccc;padding:7px 7px 0 7px;margin:10px 0 10px 0}p.topic-title{font-size:1.1em;font-weight:bold;margin-top:10px}table.docutils{border:0;border-collapse:collapse}table caption span.caption-number{font-style:italic}table.docutils td,table.docutils th{padding:1px 8px 1px 5px;border-top:0;border-left:0;border-right:0;border-bottom:1px solid #aaa}table.field-list td,table.field-list th{border:0 !important}table.footnote td,table.footnote th{border:0 !important}th{text-align:left;padding-right:5px}table.citation{border-left:solid 1px gray;margin-left:1px}table.citation td{border-bottom:none}div.figure{margin:0.5em;padding:0.5em}div.figure p.caption{padding:0.3em}div.figure p.caption span.caption-number{font-style:italic}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}dl{margin-bottom:15px}dd p{margin-top:0px}dd ul,dd table{margin-bottom:10px}dd{margin-top:3px;margin-bottom:10px;margin-left:30px}dt:target,.highlighted{background-color:#fbe54e}dl.glossary dt{font-weight:bold;font-size:1.1em}.field-list ul{margin:0;padding-left:1em}.field-list p{margin:0}.optional{font-size:1.3em}.sig-paren{font-size:larger}.versionmodified{font-style:italic}.system-message{background-color:#fda;padding:5px;border:3px solid red}.footnote:target{background-color:#ffa}.line-block{display:block;margin-top:1em;margin-bottom:1em}.line-block .line-block{margin-top:0;margin-bottom:0;margin-left:1.5em}.guilabel,.menuselection{font-family:sans-serif}.accelerator{text-decoration:underline}.classifier{font-style:oblique}abbr,acronym{border-bottom:dotted 1px;cursor:help}pre{overflow:auto;overflow-y:hidden}td.linenos pre{padding:5px 0px;border:0;background-color:transparent;color:#aaa}table.highlighttable{margin-left:0.5em}table.highlighttable td{padding:0 0.5em 0 0.5em}div.code-block-caption{padding:2px 5px;font-size:small}div.code-block-caption code{background-color:transparent}div.code-block-caption+div>div.highlight>pre{margin-top:0}div.code-block-caption span.caption-number{padding:0.1em 0.3em;font-style:italic}div.literal-block-wrapper{padding:1em 1em 0}div.literal-block-wrapper div.highlight{margin:0}code.descname{background-color:transparent;font-weight:bold;font-size:1.2em}code.descclassname{background-color:transparent}code.xref,a code{background-color:transparent;font-weight:bold}h1 code,h2 code,h3 code,h4 code,h5 code,h6 code{background-color:transparent}.viewcode-link{float:right}.viewcode-back{float:right;font-family:sans-serif}div.viewcode-block:target{margin:-1px -10px;padding:0 10px}img.math{vertical-align:middle}div.body div.math p{text-align:center}span.eqno{float:right}@media print{div.document,div.documentwrapper,div.bodywrapper{margin:0 !important;width:100%}div.sphinxsidebar,div.related,div.footer,#top-link{display:none}}.section.docs{max-width:68em;margin-left:auto;margin-right:auto;max-width:none}.section.docs h2,.section.docs h3{margin-top:1em;font-size:1.3em;font-weight:400;color:#2f2f2f;text-transform:unset;font-weight:600}.section.docs h3{font-size:1.2em;color:#6f6f6f;margin-bottom:0em}.section.docs .content .section{overflow-x:auto}.section.docs::after{clear:both;content:"";display:table}.section.docs .content{float:left;display:block;margin-right:2.3576515979%;width:74.4105871005%;margin-right:0;padding-left:0.5em}.section.docs .content:last-child{margin-right:0}.section.docs img{border:none;vertical-align:middle}.highlight-python{margin-bottom:20px}a.headerlink{visibility:visible;color:#e8e8e7;padding-left:0.2em;margin-top:-0.2em;vertical-align:text-bottom}a.headerlink:hover{color:#F16A25;text-decoration:none}dt a.headerlink{vertical-align:inherit}.simple{display:inline-block;margin-bottom:.75em;padding-left:1.5em}.simple ~ p{clear:both}.mosaic.docs-home{margin-top:2em}.mosaic.docs-home td,.mosaic.docs-home th{padding:0;background-color:#2f2f2f}.mosaic.docs-home td a:hover,.mosaic.docs-home th a:hover{opacity:0.5}.field-list .field-name{white-space:nowrap}#tab-panes pre{clear:both}.toc{float:left;display:block;margin-right:2.3576515979%;width:23.2317613015%;font-family:"Source Sans Pro",sans-serif;margin-top:3em;padding:2em 1em;background-color:#e8e8e7}.toc:last-child{margin-right:0}.toc .hide{display:none}.toc.obfuscate{background-color:white;width:7%}.toc code{font-weight:400;font-size:0.9em}.toc ul{list-style-type:none;margin:0;padding:0}.toc li.divide-top{border-top:solid 1px #404040}.toc li.divide-bottom{border-bottom:solid 1px #404040}.toc li.current{background:#fff}.toc li.current a{color:#2f2f2f;padding:.4045em 2.427em}.toc li.current a:hover{background-color:#fff;text-decoration:none}.toc li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.toc li.on a,.toc li.current>a{color:#F16A25;padding:.809em 1.618em;font-weight:600;position:relative;background:#f4f4f4;border:none;padding-left:1.618em -4px}.toc li.on a:hover,.toc li.current>a:hover{background-color:#fff}.toc li.toctree-l2 a:hover{text-decoration:underline}.toc li.toctree-l2.current>a{background:#fff;padding:.4045em 2.427em}.toc li.toctree-l2.current li.toctree-l3>a{display:block;background:#fff;padding:.4045em 4.045em}.toc li.toctree-l3>a{background:#fff;padding:.4045em 4.045em}.toc li.toctree-l3 li.toctree-l4>a{display:block;background:#fff;padding:.4045em 5.663em;border-top:none;border-bottom:none}.toc li.current ul{display:block;overflow-x:auto}.toc li ul{margin-bottom:0;display:none}.toc .local-toc li ul{display:block}.toc li ul li a{margin-bottom:0;color:#2f2f2f;font-weight:normal}.toc a{display:inline-block;line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#2f2f2f}.toc a:hover{background-color:#fff;text-decoration:none}.toctree-wrapper{display:none}.class,.section{clear:both}.class{margin-top:20px;padding-top:10px;border-top:1px #e8e8e7 solid}.class>dt{color:#F16A25}dt:target,.highlighted{background-color:#EEEEDD}.class>dd{padding-top:10px;clear:both}.attribute>dt>code:before{content:' attr ';font-style:italic;color:#902040;font-size:0.8em}.method>dt>code:before{content:' method ';font-style:italic;color:#902040;font-size:0.8em}.classmethod>dt>.property{font-style:italic;color:#902040;font-size:0.9em}.classmethod>dd>table,.method>dd>table,.attribute>dd>table{margin-left:40px;margin-bottom:0}.class ~ .panel-group{float:left}.xref .pre{font-weight:normal}.section .content .admonition-title{font-family:"Source Sans Pro",sans-serif;font-weight:400;color:#F5C33C;font-size:1.3em}.section .content .admonition{margin:1em 0;border-left:0.5em #2f2f2f solid;padding:0em 3em 0.5em 1em}.section .content .admonition p{margin:0.2em 0}.section .content .admonition.note{border-color:#3bbcb5}.section .content .admonition.note .admonition-title{color:#3bbcb5}.section .content .admonition.error{border-color:red}.section .content .admonition.error .admonition-title{color:red}.section .content .admonition.warning{border-color:#F16A25}.section .content .admonition.warning .admonition-title{color:#F16A25}.section .content .admonition.alert{border-color:#902040}.section .content .admonition.alert .admonition-title{color:#902040}.section .content ul.gallery li{list-style-type:none;float:left}.section .content img.gallery{width:200px;height:200px}.search-bar{float:left;width:100%;position:relative}@media screen and (min-width: 640px){.search-bar{width:60%}}.search-bar input[type=search]{box-sizing:border-box;background:#f4f4f4;border:1px solid #dcdcda;color:#902040;font-size:0.85em;margin:0;padding:0.25em 0.4em;width:100%}@media screen and (min-width: 640px){.search-bar{width:100%}}.search-bar button[type=submit]{background:#f4f4f4;border:none;bottom:0.1em;left:auto;outline:none;padding:0 9px;position:absolute;right:0.1em;top:0.2em}.search-bar button[type=submit] img{height:12px;opacity:0.7;padding:1px}#search-results .cse .gsc-control-cse,#search-results .gsc-control-cse{padding:1em 0 0 0}#search-results .gsc-selected-option-container{min-width:100px}#search-results .gsc-tabsArea{border-color:#4c4c4c}#search-results .gsc-tabHeader.gsc-tabhActive{border-color:#4c4c4c #4c4c4c #fff;height:29px}@media screen and (min-width: 640px){display:inline-block;position:relative;width:16em;input{box-sizing:border-box;display:block}}.expander .expander-trigger{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;color:#F16A25;cursor:pointer;display:block;font-size:1em;margin-bottom:1em;padding-bottom:.25em;text-decoration:none}.expander .expander-trigger:before{font-size:.7em;content:"\25BC";margin-right:0.5em}.expander .expander-hidden:before{font-size:.7em;content:"\25BA"}.expander .expander-hidden+.expander-content{display:none} diff --git a/sphinx_pyviz_theme/static/css/main.css_t b/sphinx_pyviz_theme/static/css/main.css_t new file mode 100644 index 0000000..304f8f4 --- /dev/null +++ b/sphinx_pyviz_theme/static/css/main.css_t @@ -0,0 +1,2232 @@ +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + appearance: none; + background-color: {{theme_primary_color}}; + border: 0; + border-radius: 0px; + color: #fff; + cursor: pointer; + display: inline-block; + font-family: "Ubuntu", sans-serif; + font-size: 14px; + -webkit-font-smoothing: antialiased; + font-weight: 600; + line-height: 1; + padding: .75em 1.5em; + text-decoration: none; + transition: background-color 150ms ease; + user-select: none; + vertical-align: middle; + white-space: nowrap +} + +button:hover, +button:focus, +input[type="button"]:hover, +input[type="button"]:focus, +input[type="reset"]:hover, +input[type="reset"]:focus, +input[type="submit"]:hover, +input[type="submit"]:focus { + background-color: #1e507a; + color: #fff +} + +button:disabled, +input[type="button"]:disabled, +input[type="reset"]:disabled, +input[type="submit"]:disabled { + cursor: not-allowed; + opacity: 0.5 +} + +button:disabled:hover, +input[type="button"]:disabled:hover, +input[type="reset"]:disabled:hover, +input[type="submit"]:disabled:hover { + background-color: {{theme_primary_color}} +} + +fieldset { + background-color: #e8e8e7; + border: 1px solid #e8e8e7; + margin: 0 0 .75em; + padding: 1.5em +} + +input, +label, +select { + display: block; + font-family: "Ubuntu", sans-serif; + font-size: 14px +} + +label { + font-weight: 600; + margin-bottom: .375em +} + +label.required::after { + content: "*" +} + +label abbr { + display: none +} + +input[type="color"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="email"], +input[type="month"], +input[type="number"], +input[type="password"], +input[type="search"], +input[type="tel"], +input[type="text"], +input[type="time"], +input[type="url"], +input[type="week"], +input:not([type]), +textarea, +select[multiple=multiple] { + background-color: #fff; + border: 1px solid #e8e8e7; + border-radius: 0px; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06); + box-sizing: border-box; + font-family: "Ubuntu", sans-serif; + font-size: 14px; + margin-bottom: .75em; + padding: .5em; + transition: border-color 150ms ease; + width: 100% +} + +input[type="color"]:hover, +input[type="date"]:hover, +input[type="datetime"]:hover, +input[type="datetime-local"]:hover, +input[type="email"]:hover, +input[type="month"]:hover, +input[type="number"]:hover, +input[type="password"]:hover, +input[type="search"]:hover, +input[type="tel"]:hover, +input[type="text"]:hover, +input[type="time"]:hover, +input[type="url"]:hover, +input[type="week"]:hover, +input:not([type]):hover, +textarea:hover, +select[multiple=multiple]:hover { + border-color: #babab9 +} + +input[type="color"]:focus, +input[type="date"]:focus, +input[type="datetime"]:focus, +input[type="datetime-local"]:focus, +input[type="email"]:focus, +input[type="month"]:focus, +input[type="number"]:focus, +input[type="password"]:focus, +input[type="search"]:focus, +input[type="tel"]:focus, +input[type="text"]:focus, +input[type="time"]:focus, +input[type="url"]:focus, +input[type="week"]:focus, +input:not([type]):focus, +textarea:focus, +select[multiple=multiple]:focus { + border-color: {{theme_primary_color}}; + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.06), 0 0 5px rgba(33, 87, 132, 0.7); + outline: none +} + +input[type="color"]:disabled, +input[type="date"]:disabled, +input[type="datetime"]:disabled, +input[type="datetime-local"]:disabled, +input[type="email"]:disabled, +input[type="month"]:disabled, +input[type="number"]:disabled, +input[type="password"]:disabled, +input[type="search"]:disabled, +input[type="tel"]:disabled, +input[type="text"]:disabled, +input[type="time"]:disabled, +input[type="url"]:disabled, +input[type="week"]:disabled, +input:not([type]):disabled, +textarea:disabled, +select[multiple=multiple]:disabled { + background-color: #f2f2f2; + cursor: not-allowed +} + +input[type="color"]:disabled:hover, +input[type="date"]:disabled:hover, +input[type="datetime"]:disabled:hover, +input[type="datetime-local"]:disabled:hover, +input[type="email"]:disabled:hover, +input[type="month"]:disabled:hover, +input[type="number"]:disabled:hover, +input[type="password"]:disabled:hover, +input[type="search"]:disabled:hover, +input[type="tel"]:disabled:hover, +input[type="text"]:disabled:hover, +input[type="time"]:disabled:hover, +input[type="url"]:disabled:hover, +input[type="week"]:disabled:hover, +input:not([type]):disabled:hover, +textarea:disabled:hover, +select[multiple=multiple]:disabled:hover { + border: 1px solid #e8e8e7 +} + +textarea { + resize: vertical +} + +input[type="search"] { + appearance: none +} + +input[type="checkbox"], +input[type="radio"] { + display: inline; + margin-right: .375em +} + +input[type="checkbox"]+label, +input[type="radio"]+label { + display: inline-block +} + +input[type="file"] { + margin-bottom: .75em; + width: 100% +} + +select { + margin-bottom: 1.5em; + max-width: 100%; + width: auto +} + +dl { + margin-bottom: .75em +} + +dl dt { + margin-top: .75em; + font-weight: 600; + color: #4c4c4c +} + +body { + color: #2f2f2f; + font-family: "Ubuntu", sans-serif; + font-feature-settings: "kern", "pnum"; + font-size: 14px; + line-height: 1.5 +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Source Sans Pro", sans-serif; + font-size: 14px; + line-height: 1.2 +} + +p { + margin: 0 0 .75em +} + +a { + color: {{theme_primary_color}}; + text-decoration: none; + transition: color 150ms ease +} + +a:active, +a:focus, +a:hover { + color: {{theme_primary_color_dark}}; + text-decoration: underline +} + +hr { + border-bottom: 1px solid #e8e8e7; + border-left: 0; + border-right: 0; + border-top: 0; + margin: 1.5em 0 +} + +img, +picture { + margin: 0; + max-width: 100% +} + +code, +pre { + color: {{theme_primary_color_dark}}; + background-color: #fff +} + +html { + box-sizing: border-box +} + +*, +*::after, +*::before { + box-sizing: inherit +} + +body { + margin: 0em; + padding: 0em; + color: #2f2f2f; + font-family: "Ubuntu", sans-serif; + font-size: 12px +} + +@media screen and (min-width: 900px) { + body { + font-size: 14px + } +} + +.wrapper, +.section .content { + max-width: 68em; + margin-left: auto; + margin-right: auto; + max-width: none; + position: relative; + z-index: 9999; + padding-left: 2em; + padding-right: 2em +} + +.wrapper::after, +.section .content::after { + clear: both; + content: ""; + display: table +} + +.wrapper::after, +.section .content::after { + clear: both; + content: ""; + display: table +} + +.title, +.section h1, +.section.docs .content h1 { + color: {{theme_primary_color}}; + font-family: "Ubuntu", sans-serif; + font-weight: 400; + font-size: 2.4285714286em +} + +.section .content { + padding: 0.5em 24px 2em 24px +} + +@media screen and (min-width: 640px) { + .section .content { + padding: 0.5em 60px 2em 60px + } +} + +.section .content p { + margin: 1em 0; + line-height: 1.4em +} + +.section .content p+ul { + margin-top: -0.5em +} + +.section .content li { + list-style-type: inherit; + list-style-position: inherit +} + +.section h3 { + text-transform: uppercase; + color: {{theme_secondary_color}}; + font-family: "Source Sans Pro", sans-serif; + font-weight: 600; + font-size: 1.2857142857em +} + +.section h4 { + font-size: 1.1428571429em +} + +.section .small-title-with-sub .sub { + font-weight: 400; + text-transform: none; + float: right; + font-size: 14px +} + +.section img { + border-color: #2f2f2f; + border-style: solid; + border-width: 1px +} + +.section .caption { + margin-top: 0.25em; + font-family: "Source Sans Pro", sans-serif; + font-weight: 400 +} + +.section.dark { + background-color: #e8e8e7 +} + +.section.light h3 { + margin-bottom: 1.5em +} + +.navigation { + background-color: #2f2f2f; + border-bottom: 1px solid #161616; + min-height: 40px; + width: 100%; + z-index: 999; + font-family: "Source Sans Pro", sans-serif +} + +.navigation ul, +.navigation ol { + list-style-type: none; + margin: 0; + padding: 0 +} + +.navigation .logo { + float: left; + max-height: 40px; + padding-left: 0.2em 0; + padding-right: 2em; + margin-left: -5px +} + +.navigation .logo img { + max-height: 40px; + padding: 0.2em 0 +} + +.navigation .logo-text { + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; + color: #c4c3c3; + display: inline-block; + text-decoration: none; + font-size: 1.1428571429em; + position: absolute; + left: 60px; + top: 8px +} + +.navigation .navigation-menu-button { + color: {{theme_secondary_color}}; + display: block; + float: right; + line-height: 40px; + margin: 0; + text-decoration: none; + text-transform: uppercase; + font-size: .8571428571em +} + +@media screen and (min-width: 640px) { + .navigation .navigation-menu-button { + display: none + } +} + +.navigation .navigation-menu-button:focus, +.navigation .navigation-menu-button:hover { + color: #e8e8e7 +} + +.navigation nav { + float: none; + z-index: 9999999 +} + +@media screen and (min-width: 640px) { + .navigation nav { + float: right + } +} + +.navigation ul.navigation-menu { + clear: both; + display: none; + margin: -1em auto 1em auto; + overflow: visible; + padding: 0; + width: 100%; + z-index: 9999 +} + +.navigation ul.navigation-menu.show { + display: block +} + +@media screen and (min-width: 640px) { + .navigation ul.navigation-menu { + display: inline; + margin: 0; + padding: 0 + } +} + +.navigation ul li.nav-link { + font-size: .8571428571em; + background: #2f2f2f; + display: block; + line-height: 1.5; + overflow: hidden; + padding-right: 0.8em; + text-align: right; + width: 100%; + z-index: 9999 +} + +@media screen and (min-width: 640px) { + .navigation ul li.nav-link { + background: transparent; + display: inline; + line-height: 40px; + text-decoration: none; + width: auto + } + .navigation ul li.nav-link:last-child { + padding-right: 0 + } + .navigation ul li.nav-link:last-child>a { + padding-right: 0 + } +} + +.navigation ul li.nav-link a { + color: {{theme_secondary_color}}; + display: inline-block; + text-decoration: none; + text-transform: uppercase +} + +@media screen and (min-width: 640px) { + .navigation ul li.nav-link a { + padding-right: 1em + } +} + +.navigation ul li.nav-link a:focus, +.navigation ul li.nav-link a:hover { + color: #e8e8e7 +} + +.navigation .active-nav-item a { + border-bottom: 1px solid #e8e8e7; + padding-bottom: 3px +} + +.second-nav { + background-color: {{theme_primary_color}}; + min-height: 1.2em; + text-transform: uppercase; + padding: 0.5em 24px +} + +.second-nav::after { + clear: both; + content: ""; + display: table +} + +@media screen and (min-width: 640px) { + .second-nav { + padding: 0.5em 60px + } +} + +.second-nav ul, +.second-nav ol { + list-style-type: none; + margin: 0; + padding: 0 +} + +.second-nav ul li.nav-link { + background: inherit; + display: inline; + line-height: 1.5em; + padding-right: 2em +} + +.second-nav ul li.nav-link a { + font-size: .8571428571em; + font-family: "Source Sans Pro", sans-serif; + color: #FFFFFF +} + +.second-nav ul li.nav-link a:focus, +.second-nav ul li.nav-link a:hover { + text-decoration: underline +} + +.second-nav ul li.nav-link a.current { + color: {{theme_secondary_color}} +} + +.second-nav .navigation-menu { + float: left +} + +.second-nav .navigation-tools { + clear: both; + display: block; + padding-top: 0.4em +} + +.second-nav .navigation-tools::after { + clear: both; + content: ""; + display: table +} + +@media screen and (min-width: 640px) { + .second-nav .navigation-tools { + background: transparent; + clear: none; + float: right; + padding-top: 0 + } +} + +.second-nav li.more.nav-link { + padding-right: 0 +} + +@media screen and (min-width: 640px) { + .second-nav li.more.nav-link { + padding-right: 1em + } +} + +.second-nav li.more.nav-link>ul>li:first-child a { + padding-top: 1em +} + +.second-nav li.more.nav-link a { + margin-right: 1em +} + +.second-nav li.more.nav-link>a { + padding-right: 0.6em +} + +.second-nav li.more.nav-link>a:after { + position: absolute; + top: auto; + right: -.4em; + bottom: auto; + left: auto; + content: '\25BE'; + color: #e8e8e7 +} + +.second-nav li.more { + overflow: visible; + padding-right: 0 +} + +.second-nav li.more a { + padding-right: 0.8em +} + +.second-nav li.more>a { + padding-right: 1.6em; + position: relative +} + +@media screen and (min-width: 640px) { + .second-nav li.more>a { + margin-right: 1em + } +} + +.second-nav li.more>a:after { + content: '›'; + font-size: 1.2em; + position: absolute; + right: .5em +} + +.second-nav li.more:focus>.submenu, +.second-nav li.more:hover>.submenu { + display: block +} + +@media screen and (min-width: 640px) { + .second-nav li.more { + padding-right: 0.8em; + position: relative + } +} + +.second-nav ul.submenu { + display: none; + z-index: 10; + background-color: {{theme_primary_color}} +} + +@media screen and (min-width: 640px) { + .second-nav ul.submenu { + padding: 1em; + position: absolute; + top: 1.5em + } +} + +@media screen and (min-width: 640px) { + .second-nav ul.submenu .submenu { + left: 11.8em; + top: 0 + } +} + +.footer { + background: #4c4c4c; + font-family: "Source Sans Pro", sans-serif; + padding: 2em 24px +} + +@media screen and (min-width: 640px) { + .footer { + padding: 2em 60px + } +} + +.footer .footer-links { + max-width: 68em; + margin-left: auto; + margin-right: auto; + max-width: none; + -webkit-box-pack: left; + -moz-box-pack: left; + box-pack: left; + -webkit-justify-content: left; + -moz-justify-content: left; + -ms-justify-content: left; + -o-justify-content: left; + justify-content: left; + -ms-flex-pack: left +} + +.footer .footer-links::after { + clear: both; + content: ""; + display: table +} + +.footer ul { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 31.7615656014%; + padding: 0 +} + +.footer ul:last-child { + margin-right: 0 +} + +@media screen and (min-width: 640px) { + .footer ul { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 14.7019570017% + } + .footer ul:last-child { + margin-right: 0 + } +} + +.footer .copyright { + margin-right: 0 +} + +@media screen and (min-width: 640px) { + .footer .copyright { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 48.821174201% + } + .footer .copyright:last-child { + margin-right: 0 + } +} + +.footer .copyright>li { + color: #e8e8e7; + font-size: .8571428571em; + float: left +} + +@media screen and (min-width: 640px) { + .footer .copyright>li { + float: right + } +} + +.footer li { + list-style: none; + text-align: left; + font-size: .8571428571em +} + +.footer li>a { + color: #e8e8e7; + text-decoration: none +} + +.footer li>a:focus, +.footer li>a:hover { + color: {{theme_primary_color}} +} + +.footer li>.footer-title { + color: {{theme_secondary_color}}; + font-size: 1em; + margin-bottom: 0.4em; + text-transform: uppercase; + font-weight: 400 +} + +.content.title-content { + padding-top: 0.5em; + padding-bottom: 2.5em; + margin-bottom: 0 +} + +.content.title-content .title, +.content.title-content .section h1, +.section .content.title-content h1 { + margin: 0.5em 0 0 0; + line-height: 1em; + padding: 0 +} + +.content.title-content .subtitle { + color: #4c4c4c; + font-family: "Ubuntu", sans-serif; + font-weight: 400 +} + +.mosaic-backdrop { + line-height: 0; + position: relative; + width: 100%; + overflow: hidden +} + +.mosaic-backdrop ul { + list-style-type: none; + margin: 0; + padding: 0; + width: 125%; + overflow: hidden; + height: 75px +} + +.mosaic-backdrop ul li { + float: left; + width: 100px +} + +.mosaic-backdrop ul li img { + border: none; + height: auto; + left: 0; + position: relative; + top: 0; + width: 100% +} + +.mosaic-backdrop .copy { + z-index: 999 +} + +.mosaic-backdrop .overlay { + background-color: transparent; + background-image: -webkit-linear-gradient(top, transparent, #fff); + background-image: linear-gradient(to bottom, transparent, #fff); + display: block; + position: absolute; + top: 40px; + right: 0px; + bottom: 0px; + left: 0px +} + +.getting-started { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 31.7615656014%; + padding-right: 1em +} + +.getting-started:last-child { + margin-right: 0 +} + +.examples { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 48.821174201%; + padding-right: 2em +} + +.examples:last-child { + margin-right: 0 +} + +.value-prop .lead { + color: {{theme_primary_color}}; + font-size: 1.5714285714em +} + +.value-prop:first-child { + padding-top: 1.5em +} + +.value-prop:last-child { + padding-bottom: 1.3em +} + +.highlight code, +.highlight pre { + color: #087d88; + background-color: #f4f4f4; + padding: 0.5em 1em; + font-size: .8571428571em; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + border-bottom-right-radius: 5px; + border-top-right-radius: 5px +} + +.highlight .hll { + background-color: #ffffcc +} + +.highlight .c { + color: #408080; + font-style: italic +} + +.highlight .err { + border: 1px solid #FF0000 +} + +.highlight .k { + color: #008000; + font-weight: bold +} + +.highlight .o { + color: #666666 +} + +.highlight .cm { + color: #408080; + font-style: italic +} + +.highlight .cp { + color: #BC7A00 +} + +.highlight .c1 { + color: #408080; + font-style: italic +} + +.highlight .cs { + color: #408080; + font-style: italic +} + +.highlight .gd { + color: #A00000 +} + +.highlight .ge { + font-style: italic +} + +.highlight .gr { + color: #FF0000 +} + +.highlight .gh { + color: #000080; + font-weight: bold +} + +.highlight .gi { + color: #00A000 +} + +.highlight .go { + color: #808080 +} + +.highlight .gp { + color: #000080; + font-weight: bold +} + +.highlight .gs { + font-weight: bold +} + +.highlight .gu { + color: #800080; + font-weight: bold +} + +.highlight .gt { + color: #0040D0 +} + +.highlight .kc { + color: #008000; + font-weight: bold +} + +.highlight .kd { + color: #008000; + font-weight: bold +} + +.highlight .kn { + color: #008000; + font-weight: bold +} + +.highlight .kp { + color: #008000 +} + +.highlight .kr { + color: #008000; + font-weight: bold +} + +.highlight .kt { + color: #B00040 +} + +.highlight .m { + color: #666666 +} + +.highlight .s { + color: #BA2121 +} + +.highlight .na { + color: #7D9029 +} + +.highlight .nb { + color: #008000 +} + +.highlight .nc { + color: #0000FF; + font-weight: bold +} + +.highlight .no { + color: #880000 +} + +.highlight .nd { + color: #AA22FF +} + +.highlight .ni { + color: #999999; + font-weight: bold +} + +.highlight .ne { + color: #D2413A; + font-weight: bold +} + +.highlight .nf { + color: #0000FF +} + +.highlight .nl { + color: #A0A000 +} + +.highlight .nn { + color: #0000FF; + font-weight: bold +} + +.highlight .nt { + color: #008000; + font-weight: bold +} + +.highlight .nv { + color: #19177C +} + +.highlight .ow { + color: #AA22FF; + font-weight: bold +} + +.highlight .w { + color: #bbbbbb +} + +.highlight .mf { + color: #666666 +} + +.highlight .mh { + color: #666666 +} + +.highlight .mi { + color: #666666 +} + +.highlight .mo { + color: #666666 +} + +.highlight .sb { + color: #BA2121 +} + +.highlight .sc { + color: #BA2121 +} + +.highlight .sd { + color: #BA2121; + font-style: italic +} + +.highlight .s2 { + color: #BA2121 +} + +.highlight .se { + color: #BB6622; + font-weight: bold +} + +.highlight .sh { + color: #BA2121 +} + +.highlight .si { + color: #BB6688; + font-weight: bold +} + +.highlight .sx { + color: #008000 +} + +.highlight .sr { + color: #BB6688 +} + +.highlight .s1 { + color: #BA2121 +} + +.highlight .ss { + color: #19177C +} + +.highlight .bp { + color: #008000 +} + +.highlight .vc { + color: #19177C +} + +.highlight .vg { + color: #19177C +} + +.highlight .vi { + color: #19177C +} + +.highlight .il { + color: #666666 +} + +div.clearer { + clear: both +} + +div.related { + width: 100%; + font-size: 90% +} + +div.related h3 { + display: none +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none +} + +div.related li { + display: inline +} + +div.related li.right { + float: right; + margin-right: 5px +} + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90% +} + +div.sphinxsidebar ul { + list-style: none +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0 +} + +div.sphinxsidebar form { + margin-top: 10px +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px +} + +div.sphinxsidebar #searchbox input[type="submit"] { + width: 30px +} + +img { + border: 0; + max-width: 100% +} + +ul.search { + margin: 10px 0 0 20px; + padding: 0 +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px +} + +ul.search li a { + font-weight: bold +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold +} + +table.contentstable { + width: 90% +} + +table.contentstable p.biglink { + line-height: 150% +} + +a.biglink { + font-size: 1.3em +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90% +} + +table.indextable { + width: 100% +} + +table.indextable td { + text-align: left; + vertical-align: top +} + +table.indextable dl, +table.indextable dd { + margin-top: 0; + margin-bottom: 0 +} + +table.indextable tr.pcap { + height: 10px +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2 +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em +} + +a.headerlink { + visibility: hidden +} + +h1:hover>a.headerlink, +h2:hover>a.headerlink, +h3:hover>a.headerlink, +h4:hover>a.headerlink, +h5:hover>a.headerlink, +h6:hover>a.headerlink, +dt:hover>a.headerlink, +caption:hover>a.headerlink, +p.caption:hover>a.headerlink, +div.code-block-caption:hover>a.headerlink { + visibility: visible +} + +div.body p.caption { + text-align: inherit +} + +div.body td { + text-align: left +} + +.field-list ul { + padding-left: 1em +} + +.first { + margin-top: 0 !important +} + +p.rubric { + margin-top: 30px; + font-weight: bold +} + +img.align-left, +.figure.align-left, +object.align-left { + clear: left; + float: left; + margin-right: 1em +} + +img.align-right, +.figure.align-right, +object.align-right { + clear: right; + float: right; + margin-left: 1em +} + +img.align-center, +.figure.align-center, +object.align-center { + display: block; + margin-left: auto; + margin-right: auto +} + +.align-left { + text-align: left +} + +.align-center { + text-align: center +} + +.align-right { + text-align: right +} + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right +} + +p.sidebar-title { + font-weight: bold +} + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0 +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px +} + +table.docutils { + border: 0; + border-collapse: collapse +} + +table caption span.caption-number { + font-style: italic +} + +table.docutils td, +table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa +} + +table.field-list td, +table.field-list th { + border: 0 !important +} + +table.footnote td, +table.footnote th { + border: 0 !important +} + +th { + text-align: left; + padding-right: 5px +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px +} + +table.citation td { + border-bottom: none +} + +div.figure { + margin: 0.5em; + padding: 0.5em +} + +div.figure p.caption { + padding: 0.3em +} + +div.figure p.caption span.caption-number { + font-style: italic +} + +ol.arabic { + list-style: decimal +} + +ol.loweralpha { + list-style: lower-alpha +} + +ol.upperalpha { + list-style: upper-alpha +} + +ol.lowerroman { + list-style: lower-roman +} + +ol.upperroman { + list-style: upper-roman +} + +dl { + margin-bottom: 15px +} + +dd p { + margin-top: 0px +} + +dd ul, +dd table { + margin-bottom: 10px +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px +} + +dt:target, +.highlighted { + background-color: #fbe54e +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em +} + +.field-list ul { + margin: 0; + padding-left: 1em +} + +.field-list p { + margin: 0 +} + +.optional { + font-size: 1.3em +} + +.sig-paren { + font-size: larger +} + +.versionmodified { + font-style: italic +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red +} + +.footnote:target { + background-color: #ffa +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em +} + +.guilabel, +.menuselection { + font-family: sans-serif +} + +.accelerator { + text-decoration: underline +} + +.classifier { + font-style: oblique +} + +abbr, +acronym { + border-bottom: dotted 1px; + cursor: help +} + +pre { + overflow: auto; + overflow-y: hidden +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa +} + +table.highlighttable { + margin-left: 0.5em +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small +} + +div.code-block-caption code { + background-color: transparent +} + +div.code-block-caption+div>div.highlight>pre { + margin-top: 0 +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic +} + +div.literal-block-wrapper { + padding: 1em 1em 0 +} + +div.literal-block-wrapper div.highlight { + margin: 0 +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em +} + +code.descclassname { + background-color: transparent +} + +code.xref, +a code { + background-color: transparent; + font-weight: bold +} + +h1 code, +h2 code, +h3 code, +h4 code, +h5 code, +h6 code { + background-color: transparent +} + +.viewcode-link { + float: right +} + +.viewcode-back { + float: right; + font-family: sans-serif +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px +} + +img.math { + vertical-align: middle +} + +div.body div.math p { + text-align: center +} + +span.eqno { + float: right +} + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100% + } + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none + } +} + +.section.docs { + max-width: 68em; + margin-left: auto; + margin-right: auto; + max-width: none +} + +.section.docs h2, +.section.docs h3 { + margin-top: 1em; + font-size: 1.3em; + font-weight: 400; + color: #2f2f2f; + text-transform: unset; + font-weight: 600 +} + +.section.docs h3 { + font-size: 1.2em; + color: #6f6f6f; + margin-bottom: 0em +} + +.section.docs .content .section { + overflow-x: auto +} + +.section.docs::after { + clear: both; + content: ""; + display: table +} + +.section.docs .content { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 74.4105871005%; + margin-right: 0; + padding-left: 0.5em +} + +.section.docs .content:last-child { + margin-right: 0 +} + +.section.docs img { + border: none; + vertical-align: middle +} + +.highlight-python { + margin-bottom: 20px +} + +a.headerlink { + visibility: visible; + color: #e8e8e7; + padding-left: 0.2em; + margin-top: -0.2em; + vertical-align: text-bottom +} + +a.headerlink:hover { + color: {{theme_secondary_color}}; + text-decoration: none +} + +dt a.headerlink { + vertical-align: inherit +} + +.simple { + display: inline-block; + margin-bottom: .75em; + padding-left: 1.5em +} + +.simple ~ p { + clear: both +} + +.mosaic.docs-home { + margin-top: 2em +} + +.mosaic.docs-home td, +.mosaic.docs-home th { + padding: 0; + background-color: #2f2f2f +} + +.mosaic.docs-home td a:hover, +.mosaic.docs-home th a:hover { + opacity: 0.5 +} + +.field-list .field-name { + white-space: nowrap +} + +#tab-panes pre { + clear: both +} + +.toc { + float: left; + display: block; + margin-right: 2.3576515979%; + width: 23.2317613015%; + font-family: "Source Sans Pro", sans-serif; + margin-top: 3em; + padding: 2em 1em; + background-color: #fff +} + +.toc:last-child { + margin-right: 0 +} + +.toc .hide { + display: none +} + +.toc.obfuscate { + background-color: white; + width: 7% +} + +.toc code { + font-weight: 400; + font-size: 0.9em +} + +.toc ul { + list-style-type: none; + margin: 0; + padding: 0 +} + +.toc li.divide-top { + border-top: solid 1px #404040 +} + +.toc li.divide-bottom { + border-bottom: solid 1px #404040 +} + +.toc li.current { + background: #fafafa +} + +.toc li.current a { + color: #2f2f2f; + padding: .4045em 2.427em +} + +.toc li.current a:hover { + background-color: lightgray; + text-decoration: none +} + +.toc li code { + border: none; + background: inherit; + color: inherit; + padding-left: 0; + padding-right: 0 +} + +.toc li.on a, +.toc li.current>a { + color: black; + padding: .809em 1.618em; + position: relative; + background: lightgray; + border: none; + padding-left: 1.618em -4px +} + +.toc li.on a:hover, +.toc li.current>a:hover { + background-color: #f4f4f4 +} + +.toc li.toctree-l2 a:hover { + text-decoration: underline; + background: #f4f4f4 +} + +.toc li.toctree-l2.current>a { + background: #f4f4f4; + padding: .4045em 2.427em +} + +.toc li.toctree-l2.current li.toctree-l3>a { + display: block; + background: #f4f4f4; + padding: .4045em 4.045em +} + +.toc li.toctree-l3>a { + background: #f4f4f4; + padding: .4045em 4.045em +} + +.toc li.toctree-l3 li.toctree-l4>a { + display: block; + background: #f4f4f4; + padding: .4045em 5.663em; + border-top: none; + border-bottom: none +} + +.toc li.current ul { + display: block; + overflow-x: auto +} + +.toc li ul { + margin-bottom: 0; + display: none +} + +.toc .local-toc li ul { + display: block +} + +.toc li ul li a { + margin-bottom: 0; + color: #2f2f2f; + font-weight: normal +} + +.toc a { + display: inline-block; + line-height: 18px; + padding: .4045em 1.618em; + display: block; + position: relative; + font-size: 90%; + color: #2f2f2f +} + +.toc a:hover { + background-color: #f4f4f4; + text-decoration: none; +} + +.toctree-wrapper { + display: none +} + +.class, +.section { + clear: both +} + +.class { + margin-top: 20px; + padding-top: 10px; + border-top: 1px #e8e8e7 solid +} + +.class>dt { + color: {{theme_primary_color}} +} + +dt:target, +.highlighted { + background-color: #EEEEDD +} + +.class>dd { + padding-top: 10px; + clear: both +} + +.attribute>dt>code:before { + content: ' attr '; + font-style: italic; + color: {{theme_secondary_color}}; + font-size: 0.8em +} + +.method>dt>code:before { + content: ' method '; + font-style: italic; + color: {{theme_secondary_color}}; + font-size: 0.8em +} + +.classmethod>dt>.property { + font-style: italic; + color: {{theme_secondary_color}}; + font-size: 0.9em +} + +.classmethod>dd>table, +.method>dd>table, +.attribute>dd>table { + margin-left: 40px; + margin-bottom: 0 +} + +.class ~ .panel-group { + float: left +} + +.xref .pre { + font-weight: normal +} + +.section .content .admonition-title { + font-family: "Source Sans Pro", sans-serif; + font-weight: 400; + color: {{theme_secondary_color}}; + font-size: 1.3em +} + +.section .content .admonition { + margin: 1em 0; + border-left: 0.5em #2f2f2f solid; + padding: 0em 3em 0.5em 1em +} + +.section .content .admonition p { + margin: 0.2em 0 +} + +.section .content .admonition.note { + border-color: #3bbcb5 +} + +.section .content .admonition.note .admonition-title { + color: #3bbcb5 +} + +.section .content .admonition.error { + border-color: red +} + +.section .content .admonition.error .admonition-title { + color: red +} + +.section .content .admonition.warning { + border-color: {{theme_primary_color}} +} + +.section .content .admonition.warning .admonition-title { + color: {{theme_primary_color}} +} + +.section .content .admonition.alert { + border-color: {{theme_secondary_color}} +} + +.section .content .admonition.alert .admonition-title { + color: {{theme_primary_color}} +} + +.section .content ul.gallery li { + list-style-type: none; + float: left +} + +.section .content img.gallery { + width: 200px; + height: 200px +} + +.search-bar { + float: left; + width: 100%; + position: relative +} + +@media screen and (min-width: 640px) { + .search-bar { + width: 60% + } +} + +.search-bar input[type=search] { + box-sizing: border-box; + background: #f4f4f4; + border: 1px solid #dcdcda; + color: {{theme_primary_color_dark}}; + font-size: 0.85em; + margin: 0; + padding: 0.25em 0.4em; + width: 100% +} + +@media screen and (min-width: 640px) { + .search-bar { + width: 100% + } +} + +.search-bar button[type=submit] { + background: #f4f4f4; + border: none; + bottom: 0.1em; + left: auto; + outline: none; + padding: 0 9px; + position: absolute; + right: 0.1em; + top: 0.2em +} + +.search-bar button[type=submit] img { + height: 12px; + opacity: 0.7; + padding: 1px +} + +#search-results .cse .gsc-control-cse, +#search-results .gsc-control-cse { + padding: 1em 0 0 0 +} + +#search-results .gsc-selected-option-container { + min-width: 100px +} + +#search-results .gsc-tabsArea { + border-color: #4c4c4c +} + +#search-results .gsc-tabHeader.gsc-tabhActive { + border-color: #4c4c4c #4c4c4c #fff; + height: 29px +} + +@media screen and (min-width: 640px) { + display: inline-block; + position: relative; + width: 16em; + input { + box-sizing: border-box; + display: block + } +} + +.expander .expander-trigger { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + color: {{theme_primary_color}}; + cursor: pointer; + display: block; + font-size: 1em; + margin-bottom: 1em; + padding-bottom: .25em; + text-decoration: none +} + +.expander .expander-trigger:before { + font-size: .7em; + content: "\25BC"; + margin-right: 0.5em +} + +.expander .expander-hidden:before { + font-size: .7em; + content: "\25BA" +} + +.expander .expander-hidden+.expander-content { + display: none +} \ No newline at end of file diff --git a/sphinx_pyviz_theme/theme.conf b/sphinx_pyviz_theme/theme.conf index b474733..fd01774 100644 --- a/sphinx_pyviz_theme/theme.conf +++ b/sphinx_pyviz_theme/theme.conf @@ -7,3 +7,7 @@ css = css/main.css custom_css = favicon = images/favicon.ico logo = images/logo.png +primary_color = coral +primary_color_dark = sienna +secondary_color = gold +second_nav = false From 4269d3498131539d4d0822e763f1f352f6710530 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 13:35:33 -0400 Subject: [PATCH 04/22] Setting up release infrastructure (#6) * Fixing up conda recipe * Updating travis with env vars for pypi --- .travis.yml | 5 ++--- conda.recipe/meta.yaml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 conda.recipe/meta.yaml diff --git a/.travis.yml b/.travis.yml index 6a4e947..31c22b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,9 +26,8 @@ jobs: distributions: "sdist bdist_wheel" on: tags: true - user: ceball - password: - secure: VJpGRSoKsuSZSIuBwXmd/XtAviateO7xlU4LYVzz9wrfI1xzd7TQkrRLbL/JQIzPFPop79nrtWvREZC7p+e/o8llNGNWeS8GiZHjDZxaONWhvrZqJKGRu9SNfWH08dxut6gQ9XLZ1+jilAG4KMuEhD/dww7uT+rjVLUrXcKQTMzmMt6+tWlCiVc56rQ6ZZLEqTC0eVq2Ctd2GnKqpRqUbzb1ajp3Y1nqt7M8Nr/XEwTGswt5/kFZpAi7QUz2tW39W4LlPnlzGqx9fkeRxzZ0ws1vkAhB//oJOSh+ao07i3GdneGjydjWlTaqFGUlB+W/Qol/bQpMvI0PlTiGEKqRLsoqXTVyq+8yuLtFRj+C74Bbg6/heKmHhF4lRNZXbeYbQ1Z2yVhxr3i81+DTjT02+EExvJ//96iO08KJmPnjVxxFCY5DC16YMDzGZ04s1MR+Fe3CohWKkgigfnAsZukmyFpZT/Qc9MvRUEDX4/7hEptedUULs/TaQP7yMoDf1V2c7F3jDDqB5Fjkgedqmh2LRJ2YGSFl7ciJ9hpSOCg3FibOOxDX3RPMqUpxUbd9cNyYXykPBwTg2vAe6aFuRf0lahwR6b6hRp4Kbs5sFmpFcQrJjSS9ty1TbriVoeG7MEwmhLRo7L1FYXkcSHngwooxcziU1IntwWoJMhfUZ8cx+dw= + user: $PPU + password: $PPP - stage: conda_package install: diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml new file mode 100644 index 0000000..2b3ed93 --- /dev/null +++ b/conda.recipe/meta.yaml @@ -0,0 +1,39 @@ +{% set sdata = load_setup_py_data() %} + +package: + name: sphinx_pyviz_theme + version: {{ sdata['version'] }} + +source: + path: .. + +build: + noarch: python + script: python setup.py install --single-version-externally-managed --record=record.txt + entry_points: + {% for group,epoints in sdata.get("entry_points",{}).items() %} + {% for entry_point in epoints %} + - {{ entry_point }} + {% endfor %} + {% endfor %} + +requirements: + build: + - python {{ sdata['python_requires'] }} + {% for dep in sdata['extras_require'].get('build', {}) %} + - {{ dep }} + {% endfor %} + run: + - python {{ sdata['python_requires'] }} + {% for dep in sdata.get('install_requires',{}) %} + - {{ dep }} + {% endfor %} + +test: + imports: + - sphinx_pyviz_theme + +about: + home: {{ sdata['url'] }} + summary: {{ sdata['description'] }} + license: {{ sdata['license'] }} From c3eae59367e8351bdd1a97091ca0a85f03254332 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 13:55:57 -0400 Subject: [PATCH 05/22] Updating conda recipe --- conda.recipe/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index 2b3ed93..e0e148a 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -20,7 +20,7 @@ build: requirements: build: - python {{ sdata['python_requires'] }} - {% for dep in sdata['extras_require'].get('build', {}) %} + {% for dep in sdata.get('extras_require', {}).get('build', {}) %} - {{ dep }} {% endfor %} run: From dc87708a47f4316a666327719ac359118452ded8 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 14:32:54 -0400 Subject: [PATCH 06/22] Cleaning up conda packaging (#7) * Removing entry_points * Adding param to run requirements --- conda.recipe/meta.yaml | 8 +------- setup.py | 1 + 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index e0e148a..4549beb 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -10,17 +10,11 @@ source: build: noarch: python script: python setup.py install --single-version-externally-managed --record=record.txt - entry_points: - {% for group,epoints in sdata.get("entry_points",{}).items() %} - {% for entry_point in epoints %} - - {{ entry_point }} - {% endfor %} - {% endfor %} requirements: build: - python {{ sdata['python_requires'] }} - {% for dep in sdata.get('extras_require', {}).get('build', {}) %} + {% for dep in sdata.get('extras_require', {}).get('build', []) %} - {{ dep }} {% endfor %} run: diff --git a/setup.py b/setup.py index 2364365..d420c85 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,7 @@ def get_setup_version(root, reponame): install_requires = [ "sphinx", + "param >=1.7.0", ], extras_require = { 'build': [ From fd31d15d305d943e74ad710c2bae311a457c316a Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 14:57:26 -0400 Subject: [PATCH 07/22] Trying to get release working (#8) * Fixing version * Fixing up indentation * Last fix to init --- .gitattributes | 1 + MANIFEST.in | 2 + pyproject.toml | 5 - setup.cfg | 8 + setup.py | 50 +- sphinx_pyviz_theme/__init__.py | 15 +- sphinx_pyviz_theme/_version.py | 520 +++++++++ versioneer.py | 1822 ++++++++++++++++++++++++++++++++ 8 files changed, 2367 insertions(+), 56 deletions(-) create mode 100644 .gitattributes delete mode 100644 pyproject.toml create mode 100644 sphinx_pyviz_theme/_version.py create mode 100644 versioneer.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f741aec --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +sphinx_pyviz_theme/_version.py export-subst diff --git a/MANIFEST.in b/MANIFEST.in index 42eb410..a072ecd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,3 @@ +include versioneer.py +include sphinx_pyviz_theme/_version.py include LICENSE.txt diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 506538c..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,5 +0,0 @@ -[build-system] -requires = [ - "param >=1.7.0", - "setuptools >=30.3.0" -] \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 5f465ef..c85111e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,11 @@ universal = 1 [metadata] license_file = LICENSE.txt + +[versioneer] +VCS = git +versionfile_source = sphinx_pyviz_theme/_version.py +versionfile_build = sphinx_pyviz_theme/_version.py +tag_prefix = +parentdir_prefix = sphinx_pyviz_theme- +style = pep440-pre diff --git a/setup.py b/setup.py index d420c85..151f528 100644 --- a/setup.py +++ b/setup.py @@ -1,41 +1,16 @@ from setuptools import setup -NAME = 'sphinx_pyviz_theme' -DESCRIPTION = "PyViz theme for use with nbsite" - -# duplicated from pyct.build.get_setup_version until pyct[build] >0.4.6 lands -def get_setup_version(root, reponame): - """ - Helper to get the current version from either git describe or the - .version file (if available) - allows for param to not be available. - Normally used in setup.py as follows: - >>> from pyct.build import get_setup_version - >>> version = get_setup_version(__file__, reponame) # noqa - """ - import os - import json - - filepath = os.path.abspath(os.path.dirname(root)) - version_file_path = os.path.join(filepath, reponame, '.version') - try: - from param import version - except: - version = None - if version is not None: - return version.Version.setup_version(filepath, reponame, archive_commit="$Format:%h$") - else: - print("WARNING: param>=1.6.0 unavailable. If you are installing a package, this warning can safely be ignored. If you are creating a package or otherwise operating in a git repository, you should install param>=1.6.0.") - return json.load(open(version_file_path, 'r'))['version_string'] +import versioneer setup_args = dict( - name=NAME, - version=get_setup_version(__file__, NAME), + name='sphinx_pyviz_theme', + version=versioneer.get_version(), url="https://github.com/pyviz-dev/sphinx_pyviz_theme", - description=DESCRIPTION, + description="For nbsite", license="BSD-3", zip_safe=False, - packages=[NAME], - package_data={NAME: [ + packages=['sphinx_pyviz_theme'], + package_data={'sphinx_pyviz_theme': [ 'theme.conf', '*.html', 'includes/*.html', @@ -51,16 +26,9 @@ def get_setup_version(root, reponame): }, python_requires = ">=2.7", - install_requires = [ - "sphinx", - "param >=1.7.0", - ], - extras_require = { - 'build': [ - "param >=1.7.0", - "setuptools", - ] - } + install_requires =[ + "sphinx" + ] ) if __name__=="__main__": diff --git a/sphinx_pyviz_theme/__init__.py b/sphinx_pyviz_theme/__init__.py index e22efef..a49d6b4 100644 --- a/sphinx_pyviz_theme/__init__.py +++ b/sphinx_pyviz_theme/__init__.py @@ -1,12 +1,7 @@ -import os.path -import param - -NAME = "sphinx_pyviz_theme" - -# version comes from git if available, otherwise from .version file -__version__ = str(param.version.Version(fpath=__file__, archive_commit="$Format:%h$", - reponame=NAME)) +import os +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions def setup(app): - app.add_html_theme('sphinx_pyviz_theme', os.path.abspath(os.path.dirname(__file__))) - + app.add_html_theme('sphinx_pyviz_theme', os.path.abspath(os.path.dirname(__file__))) diff --git a/sphinx_pyviz_theme/_version.py b/sphinx_pyviz_theme/_version.py new file mode 100644 index 0000000..ace8a7a --- /dev/null +++ b/sphinx_pyviz_theme/_version.py @@ -0,0 +1,520 @@ + +# 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 +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.18 (https://github.com/warner/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "$Format:%d$" + git_full = "$Format:%H$" + git_date = "$Format:%ci$" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "pep440-pre" + cfg.tag_prefix = "" + cfg.parentdir_prefix = "sphinx_pyviz_theme-" + cfg.versionfile_source = "sphinx_pyviz_theme/_version.py" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +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): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + 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)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip() + if sys.version_info[0] >= 3: + stdout = stdout.decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + 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} + 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)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # 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)]) + 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 + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # 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)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + 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):] + if verbose: + print("picking %s" % r) + 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} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + 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) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # 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) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if 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) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + 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)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + 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() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +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} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} diff --git a/versioneer.py b/versioneer.py new file mode 100644 index 0000000..64fea1c --- /dev/null +++ b/versioneer.py @@ -0,0 +1,1822 @@ + +# Version: 0.18 + +"""The Versioneer - like a rocketeer, but for versions. + +The Versioneer +============== + +* like a rocketeer, but for versions! +* https://github.com/warner/python-versioneer +* Brian Warner +* License: Public Domain +* Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy +* [![Latest Version] +(https://pypip.in/version/versioneer/badge.svg?style=flat) +](https://pypi.python.org/pypi/versioneer/) +* [![Build Status] +(https://travis-ci.org/warner/python-versioneer.png?branch=master) +](https://travis-ci.org/warner/python-versioneer) + +This is a tool for managing a recorded version number in distutils-based +python projects. The goal is to remove the tedious and error-prone "update +the embedded version string" step from your release process. Making a new +release should be as easy as recording a new tag in your version-control +system, and maybe making new tarballs. + + +## Quick Install + +* `pip install versioneer` to somewhere to your $PATH +* add a `[versioneer]` section to your setup.cfg (see below) +* run `versioneer install` in your source tree, commit the results + +## Version Identifiers + +Source trees come from a variety of places: + +* a version-control system checkout (mostly used by developers) +* a nightly tarball, produced by build automation +* a snapshot tarball, produced by a web-based VCS browser, like github's + "tarball from tag" feature +* a release tarball, produced by "setup.py sdist", distributed through PyPI + +Within each source tree, the version identifier (either a string or a number, +this tool is format-agnostic) can come from a variety of places: + +* ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows + about recent "tags" and an absolute revision-id +* the name of the directory into which the tarball was unpacked +* an expanded VCS keyword ($Id$, etc) +* a `_version.py` created by some earlier build step + +For released software, the version identifier is closely related to a VCS +tag. Some projects use tag names that include more than just the version +string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool +needs to strip the tag prefix to extract the version identifier. For +unreleased software (between tags), the version identifier should provide +enough information to help developers recreate the same tree, while also +giving them an idea of roughly how old the tree is (after version 1.2, before +version 1.3). Many VCS systems can report a description that captures this, +for example `git describe --tags --dirty --always` reports things like +"0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the +0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has +uncommitted changes. + +The version identifier is used for multiple purposes: + +* to allow the module to self-identify its version: `myproject.__version__` +* to choose a name and prefix for a 'setup.py sdist' tarball + +## Theory of Operation + +Versioneer works by adding a special `_version.py` file into your source +tree, where your `__init__.py` can import it. This `_version.py` knows how to +dynamically ask the VCS tool for version information at import time. + +`_version.py` also contains `$Revision$` markers, and the installation +process marks `_version.py` to have this marker rewritten with a tag name +during the `git archive` command. As a result, generated tarballs will +contain enough information to get the proper version. + +To allow `setup.py` to compute a version too, a `versioneer.py` is added to +the top level of your source tree, next to `setup.py` and the `setup.cfg` +that configures it. This overrides several distutils/setuptools commands to +compute the version when invoked, and changes `setup.py build` and `setup.py +sdist` to replace `_version.py` with a small static file that contains just +the generated version data. + +## Installation + +See [INSTALL.md](./INSTALL.md) for detailed installation instructions. + +## Version-String Flavors + +Code which uses Versioneer can learn about its version string at runtime by +importing `_version` from your main `__init__.py` file and running the +`get_versions()` function. From the "outside" (e.g. in `setup.py`), you can +import the top-level `versioneer.py` and run `get_versions()`. + +Both functions return a dictionary with different flavors of version +information: + +* `['version']`: A condensed version string, rendered using the selected + style. This is the most commonly used value for the project's version + string. The default "pep440" style yields strings like `0.11`, + `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section + below for alternative styles. + +* `['full-revisionid']`: detailed revision identifier. For Git, this is the + full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". + +* `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the + commit date in ISO 8601 format. This will be None if the date is not + available. + +* `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that + this is only accurate if run in a VCS checkout, otherwise it is likely to + be False or None + +* `['error']`: if the version string could not be computed, this will be set + to a string describing the problem, otherwise it will be None. It may be + useful to throw an exception in setup.py if this is set, to avoid e.g. + creating tarballs with a version string of "unknown". + +Some variants are more useful than others. Including `full-revisionid` in a +bug report should allow developers to reconstruct the exact code being tested +(or indicate the presence of local changes that should be shared with the +developers). `version` is suitable for display in an "about" box or a CLI +`--version` output: it can be easily compared against release notes and lists +of bugs fixed in various releases. + +The installer adds the following text to your `__init__.py` to place a basic +version in `YOURPROJECT.__version__`: + + from ._version import get_versions + __version__ = get_versions()['version'] + del get_versions + +## Styles + +The setup.cfg `style=` configuration controls how the VCS information is +rendered into a version string. + +The default style, "pep440", produces a PEP440-compliant string, equal to the +un-prefixed tag name for actual releases, and containing an additional "local +version" section with more detail for in-between builds. For Git, this is +TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags +--dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the +tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and +that this commit is two revisions ("+2") beyond the "0.11" tag. For released +software (exactly equal to a known tag), the identifier will only contain the +stripped tag, e.g. "0.11". + +Other styles are available. See [details.md](details.md) in the Versioneer +source tree for descriptions. + +## Debugging + +Versioneer tries to avoid fatal errors: if something goes wrong, it will tend +to return a version of "0+unknown". To investigate the problem, run `setup.py +version`, which will run the version-lookup code in a verbose mode, and will +display the full contents of `get_versions()` (including the `error` string, +which may help identify what went wrong). + +## Known Limitations + +Some situations are known to cause problems for Versioneer. This details the +most significant ones. More can be found on Github +[issues page](https://github.com/warner/python-versioneer/issues). + +### Subprojects + +Versioneer has limited support for source trees in which `setup.py` is not in +the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are +two common reasons why `setup.py` might not be in the root: + +* Source trees which contain multiple subprojects, such as + [Buildbot](https://github.com/buildbot/buildbot), which contains both + "master" and "slave" subprojects, each with their own `setup.py`, + `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI + distributions (and upload multiple independently-installable tarballs). +* Source trees whose main purpose is to contain a C library, but which also + provide bindings to Python (and perhaps other langauges) in subdirectories. + +Versioneer will look for `.git` in parent directories, and most operations +should get the right version string. However `pip` and `setuptools` have bugs +and implementation details which frequently cause `pip install .` from a +subproject directory to fail to find a correct version string (so it usually +defaults to `0+unknown`). + +`pip install --editable .` should work correctly. `setup.py install` might +work too. + +Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in +some later version. + +[Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking +this issue. The discussion in +[PR #61](https://github.com/warner/python-versioneer/pull/61) describes the +issue from the Versioneer side in more detail. +[pip PR#3176](https://github.com/pypa/pip/pull/3176) and +[pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve +pip to let Versioneer work correctly. + +Versioneer-0.16 and earlier only looked for a `.git` directory next to the +`setup.cfg`, so subprojects were completely unsupported with those releases. + +### Editable installs with setuptools <= 18.5 + +`setup.py develop` and `pip install --editable .` allow you to install a +project into a virtualenv once, then continue editing the source code (and +test) without re-installing after every change. + +"Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a +convenient way to specify executable scripts that should be installed along +with the python package. + +These both work as expected when using modern setuptools. When using +setuptools-18.5 or earlier, however, certain operations will cause +`pkg_resources.DistributionNotFound` errors when running the entrypoint +script, which must be resolved by re-installing the package. This happens +when the install happens with one version, then the egg_info data is +regenerated while a different version is checked out. Many setup.py commands +cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into +a different virtualenv), so this can be surprising. + +[Bug #83](https://github.com/warner/python-versioneer/issues/83) describes +this one, but upgrading to a newer version of setuptools should probably +resolve it. + +### Unicode version strings + +While Versioneer works (and is continually tested) with both Python 2 and +Python 3, it is not entirely consistent with bytes-vs-unicode distinctions. +Newer releases probably generate unicode version strings on py2. It's not +clear that this is wrong, but it may be surprising for applications when then +write these strings to a network connection or include them in bytes-oriented +APIs like cryptographic checksums. + +[Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates +this question. + + +## Updating Versioneer + +To upgrade your project to a new release of Versioneer, do the following: + +* install the new Versioneer (`pip install -U versioneer` or equivalent) +* edit `setup.cfg`, if necessary, to include any new configuration settings + indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. +* re-run `versioneer install` in your source tree, to replace + `SRC/_version.py` +* commit any changed files + +## Future Directions + +This tool is designed to make it easily extended to other version-control +systems: all VCS-specific components are in separate directories like +src/git/ . The top-level `versioneer.py` script is assembled from these +components by running make-versioneer.py . In the future, make-versioneer.py +will take a VCS name as an argument, and will construct a version of +`versioneer.py` that is specific to the given VCS. It might also take the +configuration arguments that are currently provided manually during +installation by editing setup.py . Alternatively, it might go the other +direction and include code from all supported VCS systems, reducing the +number of intermediate scripts. + + +## License + +To make Versioneer easier to embed, all its code is dedicated to the public +domain. The `_version.py` that it creates is also in the public domain. +Specifically, both are released under the Creative Commons "Public Domain +Dedication" license (CC0-1.0), as described in +https://creativecommons.org/publicdomain/zero/1.0/ . + +""" + +from __future__ import print_function +try: + import configparser +except ImportError: + import ConfigParser as configparser +import errno +import json +import os +import re +import subprocess +import sys + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_root(): + """Get the project root directory. + + We require that all commands are run from the project root, i.e. the + directory that contains setup.py, setup.cfg, and versioneer.py . + """ + root = os.path.realpath(os.path.abspath(os.getcwd())) + 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)): + # allow 'python path/to/setup.py COMMAND' + root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) + 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').") + raise VersioneerBadRootError(err) + try: + # Certain runtime workflows (setup.py install/develop in a setuptools + # tree) execute all dependencies in a single python process, so + # "versioneer" may be imported multiple times, and python's shared + # module-import table will cache the first one. So we can't use + # os.path.dirname(__file__), as that will find whichever + # versioneer.py was first imported, even in later projects. + me = os.path.realpath(os.path.abspath(__file__)) + 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)) + except NameError: + pass + return root + + +def get_config_from_root(root): + """Read the project setup.cfg file to determine Versioneer config.""" + # This might raise EnvironmentError (if setup.cfg is missing), or + # configparser.NoSectionError (if it lacks a [versioneer] section), or + # configparser.NoOptionError (if it lacks "VCS="). See the docstring at + # the top of versioneer.py for instructions on writing your setup.cfg . + setup_cfg = os.path.join(root, "setup.cfg") + parser = configparser.SafeConfigParser() + with open(setup_cfg, "r") as f: + parser.readfp(f) + VCS = parser.get("versioneer", "VCS") # mandatory + + 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 "" + cfg.versionfile_source = get(parser, "versionfile_source") + cfg.versionfile_build = get(parser, "versionfile_build") + cfg.tag_prefix = get(parser, "tag_prefix") + if cfg.tag_prefix in ("''", '""'): + cfg.tag_prefix = "" + cfg.parentdir_prefix = get(parser, "parentdir_prefix") + cfg.verbose = get(parser, "verbose") + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +# these dictionaries contain VCS-specific tools +LONG_VERSION_PY = {} +HANDLERS = {} + + +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): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + 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)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %s" % dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %s" % (commands,)) + return None, None + stdout = p.communicate()[0].strip() + if sys.version_info[0] >= 3: + stdout = stdout.decode() + if p.returncode != 0: + if verbose: + print("unable to run %s (error)" % dispcmd) + print("stdout was %s" % stdout) + return None, p.returncode + return stdout, p.returncode + + +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 +# directories (produced by setup.py build) will contain a much shorter file +# that just contains the computed version number. + +# This file is released into the public domain. Generated by +# versioneer-0.18 (https://github.com/warner/python-versioneer) + +"""Git implementation of _version.py.""" + +import errno +import os +import re +import subprocess +import sys + + +def get_keywords(): + """Get the keywords needed to look up the version information.""" + # these strings will be replaced by git during git-archive. + # setup.py/versioneer.py will grep for the variable names, so they must + # each be defined on a line of their own. _version.py will just call + # get_keywords(). + git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" + git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" + git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" + keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} + return keywords + + +class VersioneerConfig: + """Container for Versioneer configuration parameters.""" + + +def get_config(): + """Create, populate and return the VersioneerConfig() object.""" + # these strings are filled in when 'setup.py versioneer' creates + # _version.py + cfg = VersioneerConfig() + cfg.VCS = "git" + cfg.style = "%(STYLE)s" + cfg.tag_prefix = "%(TAG_PREFIX)s" + cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" + cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" + cfg.verbose = False + return cfg + + +class NotThisMethod(Exception): + """Exception raised if a method is not valid for the current scenario.""" + + +LONG_VERSION_PY = {} +HANDLERS = {} + + +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): + """Call the given command(s).""" + assert isinstance(commands, list) + p = None + for c in commands: + 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)) + break + except EnvironmentError: + e = sys.exc_info()[1] + if e.errno == errno.ENOENT: + continue + if verbose: + print("unable to run %%s" %% dispcmd) + print(e) + return None, None + else: + if verbose: + print("unable to find command, tried %%s" %% (commands,)) + return None, None + stdout = p.communicate()[0].strip() + if sys.version_info[0] >= 3: + stdout = stdout.decode() + if p.returncode != 0: + if verbose: + print("unable to run %%s (error)" %% dispcmd) + print("stdout was %%s" %% stdout) + return None, p.returncode + return stdout, p.returncode + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + 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} + 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)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # 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)]) + 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 + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # 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)]) + if verbose: + print("discarding '%%s', no digits" %% ",".join(refs - tags)) + if verbose: + print("likely tags: %%s" %% ",".join(sorted(tags))) + 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):] + if verbose: + print("picking %%s" %% r) + 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} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + 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) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # 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) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if 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) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%%s'" + %% describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + 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)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + 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() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post.dev%%d" %% pieces["distance"] + else: + # exception #1 + rendered = "0.post.dev%%d" %% pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%%s" %% pieces["short"] + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%%s" %% pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%%d" %% pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +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} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%%s'" %% style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +def get_versions(): + """Get version information or return default if unable to do so.""" + # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have + # __file__, we can work backwards from there to the root. Some + # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which + # case we can only use expanded keywords. + + cfg = get_config() + verbose = cfg.verbose + + try: + return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, + verbose) + except NotThisMethod: + pass + + try: + root = os.path.realpath(__file__) + # versionfile_source is the relative path from the top of the source + # tree (where the .git directory might live) to this file. Invert + # this to find the root from __file__. + for i in cfg.versionfile_source.split('/'): + root = os.path.dirname(root) + except NameError: + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None} + + try: + pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) + return render(pieces, cfg.style) + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + except NotThisMethod: + pass + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", "date": None} +''' + + +@register_vcs_handler("git", "get_keywords") +def git_get_keywords(versionfile_abs): + """Extract version information from the given file.""" + # the code embedded in _version.py can just fetch the value of these + # keywords. When used from setup.py, we don't want to import _version.py, + # so we do it with a regexp instead. This function is not used from + # _version.py. + keywords = {} + try: + f = open(versionfile_abs, "r") + for line in f.readlines(): + if line.strip().startswith("git_refnames ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["refnames"] = mo.group(1) + if line.strip().startswith("git_full ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["full"] = mo.group(1) + if line.strip().startswith("git_date ="): + mo = re.search(r'=\s*"(.*)"', line) + if mo: + keywords["date"] = mo.group(1) + f.close() + except EnvironmentError: + pass + return keywords + + +@register_vcs_handler("git", "keywords") +def git_versions_from_keywords(keywords, tag_prefix, verbose): + """Get version information from git keywords.""" + if not keywords: + raise NotThisMethod("no keywords at all, weird") + date = keywords.get("date") + if date is not None: + # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant + # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 + # -like" string, which we must then edit to make compliant), because + # it's been around since git-1.5.3, and it's too difficult to + # discover which version we're using, or to work around using an + # older one. + date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + refnames = keywords["refnames"].strip() + if refnames.startswith("$Format"): + if verbose: + print("keywords are unexpanded, not using") + raise NotThisMethod("unexpanded keywords, not a git-archive tarball") + refs = set([r.strip() for r in refnames.strip("()").split(",")]) + # 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)]) + 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 + # expansion behaves like git log --decorate=short and strips out the + # refs/heads/ and refs/tags/ prefixes that would let us distinguish + # 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)]) + if verbose: + print("discarding '%s', no digits" % ",".join(refs - tags)) + if verbose: + print("likely tags: %s" % ",".join(sorted(tags))) + 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):] + if verbose: + print("picking %s" % r) + 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} + + +@register_vcs_handler("git", "pieces_from_vcs") +def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): + """Get version from 'git describe' in the root of the source tree. + + This only gets called if the git-archive 'subst' keywords were *not* + expanded, and _version.py hasn't already been rewritten with a short + version string, meaning we're inside a checked out source tree. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + + 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) + raise NotThisMethod("'git rev-parse --git-dir' returned error") + + # 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) + # --long was added in git-1.5.5 + if describe_out is None: + raise NotThisMethod("'git describe' failed") + describe_out = describe_out.strip() + full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) + if full_out is None: + raise NotThisMethod("'git rev-parse' failed") + full_out = full_out.strip() + + pieces = {} + pieces["long"] = full_out + pieces["short"] = full_out[:7] # maybe improved later + pieces["error"] = None + + # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] + # TAG might have hyphens. + git_describe = describe_out + + # look for -dirty suffix + dirty = git_describe.endswith("-dirty") + pieces["dirty"] = dirty + if 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) + if not mo: + # unparseable. Maybe git-describe is misbehaving? + pieces["error"] = ("unable to parse git-describe output: '%s'" + % describe_out) + return pieces + + # tag + full_tag = mo.group(1) + if not full_tag.startswith(tag_prefix): + 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)) + return pieces + pieces["closest-tag"] = full_tag[len(tag_prefix):] + + # distance: number of commits since tag + pieces["distance"] = int(mo.group(2)) + + # commit: short hex revision ID + pieces["short"] = mo.group(3) + + else: + # HEX: no tags + pieces["closest-tag"] = None + 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() + pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) + + return pieces + + +def do_vcs_install(manifest_in, versionfile_source, ipy): + """Git-specific installation logic for Versioneer. + + For Git, this means creating/changing .gitattributes to mark _version.py + for export-subst keyword substitution. + """ + GITS = ["git"] + if sys.platform == "win32": + GITS = ["git.cmd", "git.exe"] + files = [manifest_in, versionfile_source] + if ipy: + files.append(ipy) + try: + me = __file__ + if me.endswith(".pyc") or me.endswith(".pyo"): + me = os.path.splitext(me)[0] + ".py" + versioneer_file = os.path.relpath(me) + except NameError: + versioneer_file = "versioneer.py" + files.append(versioneer_file) + present = False + try: + f = open(".gitattributes", "r") + for line in f.readlines(): + if line.strip().startswith(versionfile_source): + if "export-subst" in line.strip().split()[1:]: + present = True + f.close() + except EnvironmentError: + pass + if not present: + f = open(".gitattributes", "a+") + f.write("%s export-subst\n" % versionfile_source) + f.close() + files.append(".gitattributes") + run_command(GITS, ["add", "--"] + files) + + +def versions_from_parentdir(parentdir_prefix, root, verbose): + """Try to determine the version from the parent directory name. + + Source tarballs conventionally unpack into a directory that includes both + the project name and a version string. We will also support searching up + two directory levels for an appropriately named parent directory + """ + rootdirs = [] + + 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} + 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)) + raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + + +SHORT_VERSION_PY = """ +# This file was generated by 'versioneer.py' (0.18) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json + +version_json = ''' +%s +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) +""" + + +def versions_from_file(filename): + """Try to determine the version from _version.py if present.""" + try: + with open(filename) as f: + 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) + if not mo: + 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)) + + +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=(",", ": ")) + with open(filename, "w") as f: + f.write(SHORT_VERSION_PY % contents) + + print("set %s to '%s'" % (filename, versions["version"])) + + +def plus_or_dot(pieces): + """Return a + if we don't already have one, else return a .""" + if "+" in pieces.get("closest-tag", ""): + return "." + return "+" + + +def render_pep440(pieces): + """Build up version string, with post-release "local version identifier". + + Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you + get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty + + Exceptions: + 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += plus_or_dot(pieces) + rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + else: + # exception #1 + rendered = "0+untagged.%d.g%s" % (pieces["distance"], + pieces["short"]) + if pieces["dirty"]: + rendered += ".dirty" + return rendered + + +def render_pep440_pre(pieces): + """TAG[.post.devDISTANCE] -- No -dirty. + + Exceptions: + 1: no tags. 0.post.devDISTANCE + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += ".post.dev%d" % pieces["distance"] + else: + # exception #1 + rendered = "0.post.dev%d" % pieces["distance"] + return rendered + + +def render_pep440_post(pieces): + """TAG[.postDISTANCE[.dev0]+gHEX] . + + The ".dev0" means dirty. Note that .dev0 sorts backwards + (a dirty tree will appear "older" than the corresponding clean one), + but you shouldn't be releasing software with -dirty anyways. + + Exceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += plus_or_dot(pieces) + rendered += "g%s" % pieces["short"] + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + rendered += "+g%s" % pieces["short"] + return rendered + + +def render_pep440_old(pieces): + """TAG[.postDISTANCE[.dev0]] . + + The ".dev0" means dirty. + + Eexceptions: + 1: no tags. 0.postDISTANCE[.dev0] + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"] or pieces["dirty"]: + rendered += ".post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + else: + # exception #1 + rendered = "0.post%d" % pieces["distance"] + if pieces["dirty"]: + rendered += ".dev0" + return rendered + + +def render_git_describe(pieces): + """TAG[-DISTANCE-gHEX][-dirty]. + + Like 'git describe --tags --dirty --always'. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + if pieces["distance"]: + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +def render_git_describe_long(pieces): + """TAG-DISTANCE-gHEX[-dirty]. + + Like 'git describe --tags --dirty --always -long'. + The distance/hash is unconditional. + + Exceptions: + 1: no tags. HEX[-dirty] (note: no 'g' prefix) + """ + if pieces["closest-tag"]: + rendered = pieces["closest-tag"] + rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) + else: + # exception #1 + rendered = pieces["short"] + if pieces["dirty"]: + rendered += "-dirty" + return rendered + + +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} + + if not style or style == "default": + style = "pep440" # the default + + if style == "pep440": + rendered = render_pep440(pieces) + elif style == "pep440-pre": + rendered = render_pep440_pre(pieces) + elif style == "pep440-post": + rendered = render_pep440_post(pieces) + elif style == "pep440-old": + rendered = render_pep440_old(pieces) + elif style == "git-describe": + rendered = render_git_describe(pieces) + elif style == "git-describe-long": + rendered = render_git_describe_long(pieces) + else: + raise ValueError("unknown style '%s'" % style) + + return {"version": rendered, "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], "error": None, + "date": pieces.get("date")} + + +class VersioneerBadRootError(Exception): + """The project root directory is unknown or missing key files.""" + + +def get_versions(verbose=False): + """Get the project version from whatever source is available. + + Returns dict with two keys: 'version' and 'full'. + """ + if "versioneer" in sys.modules: + # see the discussion in cmdclass.py:get_cmdclass() + del sys.modules["versioneer"] + + root = get_root() + cfg = get_config_from_root(root) + + assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" + 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.tag_prefix is not None, "please set versioneer.tag_prefix" + + versionfile_abs = os.path.join(root, cfg.versionfile_source) + + # extract version from first of: _version.py, VCS command (e.g. 'git + # describe'), parentdir. This is meant to work for developers using a + # source checkout, for users of a tarball created by 'setup.py sdist', + # and for users of a tarball/zipball created by 'git archive' or github's + # download-from-tag feature or the equivalent in other VCSes. + + get_keywords_f = handlers.get("get_keywords") + from_keywords_f = handlers.get("keywords") + if get_keywords_f and from_keywords_f: + try: + keywords = get_keywords_f(versionfile_abs) + ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) + if verbose: + print("got version from expanded keyword %s" % ver) + return ver + except NotThisMethod: + pass + + try: + ver = versions_from_file(versionfile_abs) + if verbose: + print("got version from file %s %s" % (versionfile_abs, ver)) + return ver + except NotThisMethod: + pass + + from_vcs_f = handlers.get("pieces_from_vcs") + if from_vcs_f: + try: + pieces = from_vcs_f(cfg.tag_prefix, root, verbose) + ver = render(pieces, cfg.style) + if verbose: + print("got version from VCS %s" % ver) + return ver + except NotThisMethod: + pass + + try: + if cfg.parentdir_prefix: + ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) + if verbose: + print("got version from parentdir %s" % ver) + return ver + except NotThisMethod: + pass + + if verbose: + print("unable to compute version") + + return {"version": "0+unknown", "full-revisionid": None, + "dirty": None, "error": "unable to compute version", + "date": None} + + +def get_version(): + """Get the short version string for this project.""" + return get_versions()["version"] + + +def get_cmdclass(): + """Get the custom setuptools/distutils subclasses used by Versioneer.""" + if "versioneer" in sys.modules: + del sys.modules["versioneer"] + # this fixes the "python setup.py develop" case (also 'install' and + # 'easy_install .'), in which subdependencies of the main project are + # built (using setup.py bdist_egg) in the same python process. Assume + # a main project A and a dependency B, which use different versions + # of Versioneer. A's setup.py imports A's Versioneer, leaving it in + # sys.modules by the time B's setup.py is executed, causing B to run + # with the wrong versioneer. Setuptools wraps the sub-dep builds in a + # sandbox that restores sys.modules to it's pre-build state, so the + # parent is protected against the child's "import versioneer". By + # removing ourselves from sys.modules here, before the child build + # happens, we protect the child from the parent's versioneer too. + # Also see https://github.com/warner/python-versioneer/issues/52 + + cmds = {} + + # we add "version" to both distutils and setuptools + from distutils.core import Command + + class cmd_version(Command): + description = "report generated version string" + user_options = [] + boolean_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + vers = get_versions(verbose=True) + print("Version: %s" % vers["version"]) + print(" full-revisionid: %s" % vers.get("full-revisionid")) + print(" dirty: %s" % vers.get("dirty")) + 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 + # + # most invocation pathways end up running build_py: + # distutils/build -> build_py + # distutils/install -> distutils/build ->.. + # setuptools/bdist_wheel -> distutils/install ->.. + # setuptools/bdist_egg -> distutils/install_lib -> build_py + # setuptools/install -> bdist_egg ->.. + # setuptools/develop -> ? + # pip install: + # copies source tree to a tempdir before running egg_info/etc + # if .git isn't copied too, 'git describe' will fail + # then does setup.py bdist_wheel, or sometimes setup.py install + # setup.py egg_info -> ? + + # we override different "build_py" commands for both environments + if "setuptools" in sys.modules: + from setuptools.command.build_py import build_py as _build_py + else: + from distutils.command.build_py import build_py as _build_py + + class cmd_build_py(_build_py): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + _build_py.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) + 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=[{ + # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION + # "product_version": versioneer.get_version(), + # ... + + class cmd_build_exe(_build_exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _build_exe.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, + }) + cmds["build_exe"] = cmd_build_exe + del cmds["build_py"] + + if 'py2exe' in sys.modules: # py2exe enabled? + try: + from py2exe.distutils_buildexe import py2exe as _py2exe # py3 + except ImportError: + from py2exe.build_exe import py2exe as _py2exe # py2 + + class cmd_py2exe(_py2exe): + def run(self): + root = get_root() + cfg = get_config_from_root(root) + versions = get_versions() + target_versionfile = cfg.versionfile_source + print("UPDATING %s" % target_versionfile) + write_to_version_file(target_versionfile, versions) + + _py2exe.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, + }) + cmds["py2exe"] = cmd_py2exe + + # we override different "sdist" commands for both environments + if "setuptools" in sys.modules: + from setuptools.command.sdist import sdist as _sdist + else: + from distutils.command.sdist import sdist as _sdist + + class cmd_sdist(_sdist): + def run(self): + versions = get_versions() + self._versioneer_generated_versions = versions + # unless we update this, the command will keep using the old + # version + self.distribution.metadata.version = versions["version"] + return _sdist.run(self) + + def make_release_tree(self, base_dir, files): + root = get_root() + cfg = get_config_from_root(root) + _sdist.make_release_tree(self, base_dir, files) + # now locate _version.py in the new base_dir directory + # (remembering that it may be a hardlink) and replace it with an + # 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) + cmds["sdist"] = cmd_sdist + + return cmds + + +CONFIG_ERROR = """ +setup.cfg is missing the necessary Versioneer configuration. You need +a section like: + + [versioneer] + VCS = git + style = pep440 + versionfile_source = src/myproject/_version.py + versionfile_build = myproject/_version.py + tag_prefix = + parentdir_prefix = myproject- + +You will also need to edit your setup.py to use the results: + + import versioneer + setup(version=versioneer.get_version(), + cmdclass=versioneer.get_cmdclass(), ...) + +Please read the docstring in ./versioneer.py for configuration instructions, +edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. +""" + +SAMPLE_CONFIG = """ +# See the docstring in versioneer.py for instructions. Note that you must +# re-run 'versioneer.py setup' after changing this section, and commit the +# resulting files. + +[versioneer] +#VCS = git +#style = pep440 +#versionfile_source = +#versionfile_build = +#tag_prefix = +#parentdir_prefix = + +""" + +INIT_PY_SNIPPET = """ +from ._version import get_versions +__version__ = get_versions()['version'] +del get_versions +""" + + +def do_setup(): + """Main VCS-independent setup function for installing Versioneer.""" + root = get_root() + try: + cfg = get_config_from_root(root) + 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) + with open(os.path.join(root, "setup.cfg"), "a") as f: + f.write(SAMPLE_CONFIG) + print(CONFIG_ERROR, file=sys.stderr) + return 1 + + 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") + if os.path.exists(ipy): + try: + with open(ipy, "r") as f: + old = f.read() + except EnvironmentError: + old = "" + if INIT_PY_SNIPPET not in old: + print(" appending to %s" % ipy) + with open(ipy, "a") as f: + f.write(INIT_PY_SNIPPET) + else: + print(" %s unmodified" % ipy) + else: + print(" %s doesn't exist, ok" % ipy) + ipy = None + + # Make sure both the top-level "versioneer.py" and versionfile_source + # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so + # they'll be copied into source distributions. Pip won't be able to + # install the package without this. + manifest_in = os.path.join(root, "MANIFEST.in") + simple_includes = set() + try: + with open(manifest_in, "r") as f: + for line in f: + if line.startswith("include "): + for include in line.split()[1:]: + simple_includes.add(include) + except EnvironmentError: + pass + # That doesn't cover everything MANIFEST.in can do + # (http://docs.python.org/2/distutils/sourcedist.html#commands), so + # it might give some false negatives. Appending redundant 'include' + # lines is safe, though. + if "versioneer.py" not in simple_includes: + print(" appending 'versioneer.py' to MANIFEST.in") + with open(manifest_in, "a") as f: + f.write("include versioneer.py\n") + 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) + with open(manifest_in, "a") as f: + f.write("include %s\n" % cfg.versionfile_source) + else: + print(" versionfile_source already in MANIFEST.in") + + # Make VCS-specific changes. For git, this means creating/changing + # .gitattributes to mark _version.py for export-subst keyword + # substitution. + do_vcs_install(manifest_in, cfg.versionfile_source, ipy) + return 0 + + +def scan_setup_py(): + """Validate the contents of setup.py against Versioneer's expectations.""" + found = set() + setters = False + errors = 0 + with open("setup.py", "r") as f: + for line in f.readlines(): + if "import versioneer" in line: + found.add("import") + if "versioneer.get_cmdclass()" in line: + found.add("cmdclass") + if "versioneer.get_version()" in line: + found.add("get_version") + if "versioneer.VCS" in line: + setters = True + if "versioneer.versionfile_source" in line: + setters = True + if len(found) != 3: + print("") + print("Your setup.py appears to be missing some important items") + print("(but I might be wrong). Please make sure it has something") + print("roughly like the following:") + print("") + print(" import versioneer") + print(" setup( version=versioneer.get_version(),") + print(" cmdclass=versioneer.get_cmdclass(), ...)") + print("") + errors += 1 + if setters: + print("You should remove lines like 'versioneer.VCS = ' and") + print("'versioneer.versionfile_source = ' . This configuration") + print("now lives in setup.cfg, and should be removed from setup.py") + print("") + errors += 1 + return errors + + +if __name__ == "__main__": + cmd = sys.argv[1] + if cmd == "setup": + errors = do_setup() + errors += scan_setup_py() + if errors: + sys.exit(1) From a15ded4dcf6f3efa3ba6ec693bee873388e66aa5 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Fri, 10 May 2019 16:51:01 -0400 Subject: [PATCH 08/22] Fixing token name --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31c22b0..0b5361c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,5 +39,4 @@ jobs: - conda install anaconda-client conda-build script: - conda build conda.recipe/ - - anaconda --token $CONDA_UPLOAD_TOKEN upload --user pyviz --label dev --label main `conda build --output conda.recipe` - + - anaconda --token $ANACONDA_TOKEN upload --user pyviz --label dev --label main `conda build --output conda.recipe` From 3f9eae22660fcd3bc02cc5b589759118028baea4 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Wed, 15 May 2019 12:28:18 -0400 Subject: [PATCH 09/22] Updating included files, README --- MANIFEST.in | 1 + README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 5 +++-- 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 README.md diff --git a/MANIFEST.in b/MANIFEST.in index a072ecd..eff5dbf 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include versioneer.py include sphinx_pyviz_theme/_version.py include LICENSE.txt +include README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..97cb466 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# sphinx_pyviz_theme: Theme for building pyviz sites + +| | | +| --- | --- | +| Build Status | [![Linux/MacOS Build Status](https://travis-ci.org/pyviz-dev/sphinx_pyviz_theme.svg?branch=master)](https://travis-ci.org/pyviz-dev/sphinx_pyviz_theme) | +| Latest dev release | [![Github tag](https://img.shields.io/github/tag/pyviz-dev/sphinx_pyviz_theme.svg?label=tag&colorB=11ccbb)](https://github.com/pyviz-dev/sphinx_pyviz_theme/tags) | +| Latest release | [![Github release](https://img.shields.io/github/release/pyviz-dev/sphinx_pyviz_theme.svg?label=tag&colorB=11ccbb)](https://github.com/pyviz-dev/sphinx_pyviz_theme/releases) [![PyPI version](https://img.shields.io/pypi/v/sphinx_pyviz_theme.svg?colorB=cc77dd)](https://pypi.python.org/pypi/sphinx_pyviz_theme) [![sphinx_pyviz_theme version](https://img.shields.io/conda/v/pyviz/sphinx_pyviz_theme.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/sphinx_pyviz_theme) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/sphinx_pyviz_theme.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/sphinx_pyviz_theme) | + +## What is it? +sphinx_pyviz_theme is the theme that is used when building sites in the +[pyviz](https://pyviz.org) ecosystem. This theme is best used in conjunction +with [nbsite](https://github/pyviz/nbsite). See the [nbsite docs](https://nbsite.pyviz.org) +for examples. + +## How to use + +To use this theme: `pip/conda install sphinx_pyviz_theme` and set html_theme to sphinx_pyviz_theme. To control the look and feel, change html_theme_options in conf.py: + +```python +html_static_path += ['_static'] +html_theme = 'sphinx_pyviz_theme' +html_theme_options = { + 'custom_css': 'site.css', + 'logo': 'nbsite-logo.png', + 'favicon': 'favicon.ico', + 'primary_color': '#F16A25', + 'primary_color_dark': '#B5501C', + 'secondary_color': '#F5C33C', + 'second_nav': False, +} +``` + + - logo and favicon: provide paths relative to html_static_path (doc/_static by default) + - primary_color, primary_color_dark and secondary_color: control the colors that the + website uses for header, nav, links... These can be css named colors, or hex colors. + - second_nav: Boolean indicating whether to use a second nav bar. + - custom_css: path relative to html_static_path overriding styles. + Styles come first from the theme's main.css_t, which is populated with the + colors options, then extended/overridden by your site's own css. + +**NOTE:** Only use the custom_css to overwrite small pieces of the css not to make +general improvements. If you have general improvements, please open a PR on the this repo. + + + +## About PyViz + +sphinx_pyviz_theme is part of the PyViz initiative for making Python-based visualization tools work well together. +See [pyviz.org](http://pyviz.org) for related packages that you can use with sphinx_pyviz_theme and +[status.pyviz.org](http://status.pyviz.org) for the current status of each PyViz project. \ No newline at end of file diff --git a/setup.py b/setup.py index 151f528..5cdc25c 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,8 @@ name='sphinx_pyviz_theme', version=versioneer.get_version(), url="https://github.com/pyviz-dev/sphinx_pyviz_theme", - description="For nbsite", + description="Theme for building pyviz sites; best when used with nbsite.", + long_description=open("README.md").read(), license="BSD-3", zip_safe=False, packages=['sphinx_pyviz_theme'], @@ -14,7 +15,7 @@ 'theme.conf', '*.html', 'includes/*.html', - 'static/css/*.css', + 'static/css/*.css_t', 'static/js/*.js', 'static/images/*.*' ]}, From ce94599e4a56000957869f7f4400a4ab32538039 Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Wed, 15 May 2019 13:21:46 -0400 Subject: [PATCH 10/22] Fixing up setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 5cdc25c..1ace43d 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ url="https://github.com/pyviz-dev/sphinx_pyviz_theme", description="Theme for building pyviz sites; best when used with nbsite.", long_description=open("README.md").read(), + long_description_content_type="text/markdown", license="BSD-3", zip_safe=False, packages=['sphinx_pyviz_theme'], From 210533ff90fd5b227f8ac10e629dee1f3842ae7d Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Wed, 29 May 2019 16:33:39 -0400 Subject: [PATCH 11/22] Allow users to turn off first nav and footer (#9) --- sphinx_pyviz_theme/layout.html | 44 ++++++++++++++++++---------------- sphinx_pyviz_theme/theme.conf | 2 ++ 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/sphinx_pyviz_theme/layout.html b/sphinx_pyviz_theme/layout.html index e7a7a0d..49e1a05 100644 --- a/sphinx_pyviz_theme/layout.html +++ b/sphinx_pyviz_theme/layout.html @@ -61,7 +61,7 @@ - + {% if theme_first_nav|tobool %}
@@ -144,7 +146,17 @@ -{% include 'includes/ga.html' %} +{% if GOOGLE_ANALYTICS_UA %} + +{% endif %} - + {% endif %} {% endblock %} From bb6fa9187541dc67178ab8444946a299a2061a66 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Thu, 19 Dec 2019 14:05:39 +0100 Subject: [PATCH 16/22] Use new Google Analytics script (#14) --- sphinx_holoviz_theme/layout.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sphinx_holoviz_theme/layout.html b/sphinx_holoviz_theme/layout.html index 860425e..da28589 100644 --- a/sphinx_holoviz_theme/layout.html +++ b/sphinx_holoviz_theme/layout.html @@ -147,14 +147,14 @@ {% if GOOGLE_ANALYTICS_UA %} + + {% endif %} From 93549e48fcbc793881ba1efa4e81b891edf5819e Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 8 Jan 2020 18:15:23 +0100 Subject: [PATCH 17/22] Fixes for Google Custom Search ID --- sphinx_holoviz_theme/search.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx_holoviz_theme/search.html b/sphinx_holoviz_theme/search.html index bcbddea..58ae6ff 100644 --- a/sphinx_holoviz_theme/search.html +++ b/sphinx_holoviz_theme/search.html @@ -24,7 +24,7 @@

{{ _('Search') }}

{% endif %} From bd1bac7eeaf70eb7cecf2700c312d730e07a0c0a Mon Sep 17 00:00:00 2001 From: jlstevens Date: Wed, 11 Nov 2020 12:31:33 +0100 Subject: [PATCH 19/22] Added CSS for interactivity warning option --- sphinx_holoviz_theme/static/css/main.css_t | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sphinx_holoviz_theme/static/css/main.css_t b/sphinx_holoviz_theme/static/css/main.css_t index 1893535..013baea 100644 --- a/sphinx_holoviz_theme/static/css/main.css_t +++ b/sphinx_holoviz_theme/static/css/main.css_t @@ -1,3 +1,12 @@ +#scroller-right{ + position: fixed; + top: 20px; + right: 0; + max-width: 18%; + background:#CCC; +} + + button, input[type="button"], input[type="reset"], From 340377ce9b88fa0907d5de59c8eb2996d70a3ca7 Mon Sep 17 00:00:00 2001 From: jlstevens Date: Wed, 11 Nov 2020 12:32:22 +0100 Subject: [PATCH 20/22] Reverted accidental push to master --- sphinx_holoviz_theme/static/css/main.css_t | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sphinx_holoviz_theme/static/css/main.css_t b/sphinx_holoviz_theme/static/css/main.css_t index 013baea..1893535 100644 --- a/sphinx_holoviz_theme/static/css/main.css_t +++ b/sphinx_holoviz_theme/static/css/main.css_t @@ -1,12 +1,3 @@ -#scroller-right{ - position: fixed; - top: 20px; - right: 0; - max-width: 18%; - background:#CCC; -} - - button, input[type="button"], input[type="reset"], From 52b7a567922628ca7be7336203dd258283a67a83 Mon Sep 17 00:00:00 2001 From: Jean-Luc Stevens Date: Thu, 10 Dec 2020 01:56:05 -0800 Subject: [PATCH 21/22] Added CSS for interactivity warning option (#16) --- sphinx_holoviz_theme/static/css/main.css_t | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sphinx_holoviz_theme/static/css/main.css_t b/sphinx_holoviz_theme/static/css/main.css_t index 1893535..7a18ad2 100644 --- a/sphinx_holoviz_theme/static/css/main.css_t +++ b/sphinx_holoviz_theme/static/css/main.css_t @@ -1,3 +1,15 @@ +#scroller-right { + position: fixed; + top: 50%; + right: 1%; + max-width: 10%; + background: #f2f2f2; + padding: 0.8%; + transform: translate(0%, -50%); + border: 1px solid black; + font-size: smaller; +} + button, input[type="button"], input[type="reset"], From 66a645ee892395e18d0622da467b7a58a2200167 Mon Sep 17 00:00:00 2001 From: maximlt Date: Thu, 29 Dec 2022 15:08:03 +0100 Subject: [PATCH 22/22] master to main --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a54d575..ff6045b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ | | | | --- | --- | -| Build Status | [![Linux/MacOS Build Status](https://travis-ci.org/pyviz-dev/sphinx_holoviz_theme.svg?branch=master)](https://travis-ci.org/pyviz-dev/sphinx_holoviz_theme) | +| Build Status | [![Linux/MacOS Build Status](https://travis-ci.org/pyviz-dev/sphinx_holoviz_theme.svg?branch=main)](https://travis-ci.org/pyviz-dev/sphinx_holoviz_theme) | | Latest dev release | [![Github tag](https://img.shields.io/github/tag/pyviz-dev/sphinx_holoviz_theme.svg?label=tag&colorB=11ccbb)](https://github.com/pyviz-dev/sphinx_holoviz_theme/tags) | | Latest release | [![Github release](https://img.shields.io/github/release/pyviz-dev/sphinx_holoviz_theme.svg?label=tag&colorB=11ccbb)](https://github.com/pyviz-dev/sphinx_holoviz_theme/releases) [![PyPI version](https://img.shields.io/pypi/v/sphinx_holoviz_theme.svg?colorB=cc77dd)](https://pypi.python.org/pypi/sphinx_holoviz_theme) [![sphinx_holoviz_theme version](https://img.shields.io/conda/v/pyviz/sphinx_holoviz_theme.svg?colorB=4488ff&style=flat)](https://anaconda.org/pyviz/sphinx_holoviz_theme) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/sphinx_holoviz_theme.svg?label=conda%7Cconda-forge&colorB=4488ff)](https://anaconda.org/conda-forge/sphinx_holoviz_theme) |