"""update version string during build"""
#=============================================================================
# imports
#=============================================================================
from __future__ import absolute_import, division, print_function
# core
from distutils.dist import Distribution
import os
import re
import subprocess
import time
# pkg
# local
__all__ = [
"stamp_source",
"stamp_distutils_output",
"append_hg_revision",
"as_bool",
]
#=============================================================================
# helpers
#=============================================================================
def get_command_class(opts, name):
return opts['cmdclass'].get(name) or Distribution().get_command_class(name)
def get_command_options(opts, command):
return opts.setdefault("options", {}).setdefault(command, {})
def set_command_options(opts, command, **kwds):
get_command_options(opts, command).update(kwds)
def _get_file(path):
with open(path, "r") as fh:
return fh.read()
def _replace_file(path, content, dry_run=False):
if dry_run:
return
if os.path.exists(path):
# sdist likes to use hardlinks, have to remove them first,
# or we modify *source* file
os.unlink(path)
with open(path, "w") as fh:
fh.write(content)
def stamp_source(base_dir, version, dry_run=False):
"""
update version info in passlib source
"""
#
# update version string in toplevel package source
#
path = os.path.join(base_dir, "passlib", "__init__.py")
content = _get_file(path)
content, count = re.subn('(?m)^__version__\s*=.*$',
'__version__ = ' + repr(version),
content)
assert count == 1, "failed to replace version string"
_replace_file(path, content, dry_run=dry_run)
#
# update flag in setup.py
# (not present when called from bdist_wheel, etc)
#
path = os.path.join(base_dir, "setup.py")
if os.path.exists(path):
content = _get_file(path)
content, count = re.subn('(?m)^stamp_build\s*=.*$',
'stamp_build = False', content)
assert count == 1, "failed to update 'stamp_build' flag"
_replace_file(path, content, dry_run=dry_run)
def stamp_distutils_output(opts, version):
# subclass buildpy to update version string in source
_build_py = get_command_class(opts, "build_py")
class build_py(_build_py):
def build_packages(self):
_build_py.build_packages(self)
stamp_source(self.build_lib, version, self.dry_run)
opts['cmdclass']['build_py'] = build_py
# subclass sdist to do same thing
_sdist = get_command_class(opts, "sdist")
class sdist(_sdist):
def make_release_tree(self, base_dir, files):
_sdist.make_release_tree(self, base_dir, files)
stamp_source(base_dir, version, self.dry_run)
opts['cmdclass']['sdist'] = sdist
def as_bool(value):
return (value or "").lower() in "yes y true t 1".split()
def append_hg_revision(version):
# call HG via subprocess
# NOTE: for py26 compat, using Popen() instead of check_output()
try:
proc = subprocess.Popen(["hg", "tip", "--template", "{date(date, '%Y%m%d%H%M%S')}+hg.{node|short}"],
stdout=subprocess.PIPE)
stamp, _ = proc.communicate()
if proc.returncode:
raise subprocess.CalledProcessError(1, [])
stamp = stamp.decode("ascii")
except (OSError, subprocess.CalledProcessError):
# fallback - just use build date
stamp = time.strftime("%Y%m%d%H%M%S")
# modify version
if version.endswith((".dev0", ".post0")):
version = version[:-1] + stamp
else:
version += ".post" + stamp
return version
def install_build_py_exclude(opts):
_build_py = get_command_class(opts, "build_py")
class build_py(_build_py):
user_options = _build_py.user_options + [
("exclude-packages=", None,
"exclude packages from builds"),
]
exclude_packages = None
def finalize_options(self):
_build_py.finalize_options(self)
target = self.packages
for package in self.exclude_packages or []:
if package in target:
target.remove(package)
opts['cmdclass']['build_py'] = build_py
#=============================================================================
# eof
#=============================================================================