forked from conda/conda
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
289 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# -*- coding: utf-8 -*- | ||
"""auxiliary library to the python standard library""" | ||
from __future__ import absolute_import, division, print_function | ||
from logging import getLogger, NullHandler | ||
|
||
# don't mess up logging for users | ||
getLogger('auxlib').addHandler(NullHandler()) | ||
|
||
from .packaging import BuildPyCommand, SDistCommand, Tox, get_version # NOQA | ||
|
||
__all__ = [ | ||
"__title__", "__version__", "__author__", | ||
"__email__", "__license__", "__copyright__", | ||
"__summary__", "__homepage__", | ||
"BuildPyCommand", "SDistCommand", "Tox", "get_version", | ||
] | ||
|
||
__version__ = get_version(__file__, __package__) | ||
|
||
__title__ = "auxlib" | ||
__author__ = 'Kale Franz' | ||
__email__ = '[email protected]' | ||
__homepage__ = 'https://github.com/kalefranz/auxlib' | ||
__license__ = "ISC" | ||
__copyright__ = "(c) 2015 Kale Franz. All rights reserved." | ||
__summary__ = __doc__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import print_function, division, absolute_import | ||
from logging import getLogger | ||
from os import remove | ||
from os.path import isdir, isfile, join | ||
from re import match | ||
try: | ||
from setuptools.command.build_py import build_py | ||
from setuptools.command.sdist import sdist | ||
from setuptools.command.test import test as TestCommand | ||
except ImportError: | ||
from distutils.command.build_py import build_py | ||
from distutils.command.sdist import sdist | ||
TestCommand = object | ||
|
||
from subprocess import CalledProcessError, check_call, check_output, call | ||
import sys | ||
|
||
from .path import absdirname, PackageFile | ||
|
||
log = getLogger(__name__) | ||
|
||
|
||
def _get_version_from_pkg_info(package_name): | ||
with PackageFile('.version', package_name) as fh: | ||
return fh.read() | ||
|
||
|
||
def _is_git_dirty(path): | ||
try: | ||
check_call(('git', 'diff', '--quiet'), cwd=path) | ||
check_call(('git', 'diff', '--cached', '--quiet'), cwd=path) | ||
return False | ||
except CalledProcessError: | ||
return True | ||
|
||
|
||
def _get_most_recent_git_tag(path): | ||
try: | ||
return check_output(("git", "describe", "--tags"), cwd=path).strip() | ||
except CalledProcessError as e: | ||
if e.returncode == 128: | ||
return "0.0.0.0" | ||
else: | ||
raise # pragma: no cover | ||
|
||
|
||
def _get_git_hash(path): | ||
try: | ||
return check_output(("git", "rev-parse", "HEAD"), cwd=path).strip()[:7] | ||
except CalledProcessError: | ||
return 0 | ||
|
||
|
||
def _get_version_from_git_tag(path): | ||
"""Return a PEP440-compliant version derived from the git status. | ||
If that fails for any reason, return the first 7 chars of the changeset hash. | ||
""" | ||
tag = _get_most_recent_git_tag(path) | ||
m = match(b"(?P<xyz>\d+\.\d+\.\d+)(?:-(?P<dev>\d+)-(?P<hash>.+))?", tag) | ||
version = m.group('xyz').decode('utf-8') | ||
if m.group('dev') or _is_git_dirty(path): | ||
dev = (m.group('dev') or b'0').decode('utf-8') | ||
hash_ = (m.group('hash') or _get_git_hash(path)).decode('utf-8') | ||
version += ".dev{dev}+{hash_}".format(dev=dev, hash_=hash_) | ||
return version | ||
|
||
|
||
def is_git_repo(path): | ||
return call(('git', 'rev-parse'), cwd=path) == 0 | ||
|
||
|
||
def get_version(file, package): | ||
"""Returns a version string for the current package, derived | ||
either from git or from a .version file. | ||
This function is expected to run in two contexts. In a development | ||
context, where .git/ exists, the version is pulled from git tags. | ||
Using the BuildPyCommand and SDistCommand classes for cmdclass in | ||
setup.py will write a .version file into any dist. | ||
In an installed context, the .version file written at dist build | ||
time is the source of version information. | ||
""" | ||
# check for .version file | ||
try: | ||
version_from_pkg = _get_version_from_pkg_info(package) | ||
return version_from_pkg.decode('UTF-8') if hasattr(version_from_pkg, 'decode') else version_from_pkg # NOQA | ||
except IOError: | ||
# no .version file found; fall back to git repo | ||
here = absdirname(file) | ||
if is_git_repo(here): | ||
return _get_version_from_git_tag(here) | ||
|
||
raise RuntimeError("Could not get package version (no .git or .version file)") | ||
|
||
|
||
def write_version_into_init(target_dir, version): | ||
target_init_file = join(target_dir, "__init__.py") | ||
assert isfile(target_init_file), "File not found: {0}".format(target_init_file) | ||
with open(target_init_file, 'r') as f: | ||
init_lines = f.readlines() | ||
for q in range(len(init_lines)): | ||
if init_lines[q].startswith('__version__'): | ||
init_lines[q] = '__version__ = "{0}"\n'.format(version) | ||
elif init_lines[q].startswith(('from auxlib', 'import auxlib')): | ||
init_lines[q] = None | ||
print("UPDATING {0}".format(target_init_file)) | ||
remove(target_init_file) | ||
with open(target_init_file, 'w') as f: | ||
f.write(''.join(l for l in init_lines if l is not None)) | ||
|
||
|
||
def write_version_file(target_dir, version): | ||
assert isdir(target_dir), "Directory not found: {0}".format(target_dir) | ||
target_file = join(target_dir, ".version") | ||
with open(target_file, 'w') as f: | ||
f.write(version) | ||
|
||
|
||
class BuildPyCommand(build_py): | ||
def run(self): | ||
build_py.run(self) | ||
target_dir = join(self.build_lib, self.distribution.metadata.name) | ||
write_version_into_init(target_dir, self.distribution.metadata.version) | ||
write_version_file(target_dir, self.distribution.metadata.version) | ||
|
||
|
||
class SDistCommand(sdist): | ||
def make_release_tree(self, base_dir, files): | ||
sdist.make_release_tree(self, base_dir, files) | ||
target_dir = join(base_dir, self.distribution.metadata.name) | ||
write_version_into_init(target_dir, self.distribution.metadata.version) | ||
write_version_file(target_dir, self.distribution.metadata.version) | ||
|
||
|
||
class Tox(TestCommand): | ||
user_options = [('tox-args=', 'a', "Arguments to pass to tox")] | ||
|
||
def initialize_options(self): | ||
TestCommand.initialize_options(self) | ||
self.tox_args = None | ||
|
||
def finalize_options(self): | ||
TestCommand.finalize_options(self) | ||
self.test_args = [] | ||
self.test_suite = True | ||
|
||
def run_tests(self): | ||
# import here, cause outside the eggs aren't loaded | ||
import tox | ||
import shlex | ||
args = self.tox_args | ||
if args: | ||
args = shlex.split(self.tox_args) | ||
else: | ||
args = '' | ||
errno = tox.cmdline(args=args) | ||
sys.exit(errno) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import print_function, division, absolute_import | ||
from distutils.sysconfig import get_python_lib | ||
from logging import getLogger | ||
from os import chdir, getcwd | ||
from os.path import (abspath, dirname, exists, expanduser, expandvars, isdir, isfile, join, | ||
normpath, sep) | ||
try: | ||
import pkg_resources | ||
except ImportError: | ||
pkg_resources = None | ||
import sys | ||
|
||
log = getLogger(__name__) | ||
|
||
|
||
ROOT_PATH = abspath(sep) | ||
|
||
|
||
def site_packages_paths(): | ||
if hasattr(sys, 'real_prefix'): | ||
# in a virtualenv | ||
log.debug('searching virtualenv') | ||
return [p for p in sys.path if p.endswith('site-packages')] | ||
else: | ||
# not in a virtualenv | ||
log.debug('searching outside virtualenv') # pragma: no cover | ||
return get_python_lib() # pragma: no cover | ||
|
||
|
||
class PackageFile(object): | ||
|
||
def __init__(self, file_path, package_name): | ||
self.file_path = file_path | ||
self.package_name = package_name | ||
|
||
def __enter__(self): | ||
self.file_handle = open_package_file(self.file_path, self.package_name) | ||
return self.file_handle | ||
|
||
def __exit__(self, *args): | ||
self.file_handle.close() | ||
|
||
|
||
class ChangePath(object): | ||
|
||
def __init__(self, path): | ||
self.dirpath = dirname(path) if isfile(path) else path | ||
if not isdir(self.dirpath): | ||
raise IOError('File or directory not found: {0}'.format(path)) | ||
|
||
def __enter__(self): | ||
self.cwd = getcwd() | ||
chdir(self.dirpath) | ||
return self | ||
|
||
def __exit__(self, *args): | ||
chdir(self.cwd) | ||
|
||
|
||
def open_package_file(file_path, package_name): | ||
file_path = expand(file_path) | ||
|
||
# look for file at relative path | ||
if exists(file_path): | ||
log.info("found real file {0}".format(file_path)) | ||
return open(file_path) | ||
|
||
# look for file in package resources | ||
if (package_name and pkg_resources is not None | ||
and pkg_resources.resource_exists(package_name, file_path)): | ||
log.info("found package resource file {0} for package {1}".format(file_path, package_name)) | ||
return pkg_resources.resource_stream(package_name, file_path) | ||
|
||
# look for file in site-packages | ||
package_path = find_file_in_site_packages(file_path, package_name) | ||
if package_path: | ||
return open(package_path) # pragma: no cover | ||
|
||
msg = "file for module [{0}] cannot be found at path {1}".format(package_name, file_path) | ||
log.error(msg) | ||
raise IOError(msg) | ||
|
||
|
||
def find_file_in_site_packages(file_path, package_name): | ||
package_path = package_name.replace('.', '/') | ||
for site_packages_path in site_packages_paths(): | ||
test_path = join(site_packages_path, package_path, file_path) | ||
if exists(test_path): | ||
log.info("found site-package file {0} for package {1}".format(file_path, package_name)) | ||
return test_path | ||
else: | ||
log.error("No file found at {0}.".format(test_path)) | ||
return None | ||
|
||
|
||
def expand(path): | ||
return normpath(expanduser(expandvars(path))) | ||
|
||
|
||
def absdirname(path): | ||
return abspath(expanduser(dirname(path))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters