#!/usr/bin/env python3 # This file is part of 'miniver': https://github.com/jbweston/miniver import sys import os import os.path import argparse import tempfile import shutil import textwrap from zipfile import ZipFile from importlib.util import find_spec from urllib.request import urlretrieve if sys.version_info < (3, 5): print("Miniver needs at least Python 3.5") sys.exit(1) try: import miniver _miniver_version = miniver.__version__ del miniver _miniver_is_installed = True except ImportError: _miniver_version = "unknown" _miniver_is_installed = False # When we fetch miniver from local files _miniver_modules = ("_version",) # When we fetch miniver from GitHub _miniver_zip_url = "https://github.com/jbweston/miniver/archive/master.zip" _zipfile_root = "miniver-master" # tied to the fact that we fetch master.zip # File templates _setup_template = textwrap.dedent( ''' def get_version_and_cmdclass(package_path): """Load version.py module without importing the whole package. Template code from miniver """ import os from importlib.util import module_from_spec, spec_from_file_location spec = spec_from_file_location('version', os.path.join(package_path, '_version.py')) module = module_from_spec(spec) spec.loader.exec_module(module) return module.__version__, module.cmdclass version, cmdclass = get_version_and_cmdclass('{package_dir}') setup( ..., version=version, cmdclass=cmdclass, ) ''' ) _static_version_template = textwrap.dedent( """\ # -*- coding: utf-8 -*- # This file is part of 'miniver': https://github.com/jbweston/miniver # # This file will be overwritten by setup.py when a source or binary # distribution is made. The magic value "__use_git__" is interpreted by # version.py. version = "__use_git__" # These values are only set if the distribution was created with 'git archive' refnames = "$Format:%D$" git_hash = "$Format:%h$" """ ) _init_template = "from ._version import __version__" _gitattribute_template = "{package_dir}/_static_version.py export-subst" def _line_in_file(to_find, filename): """Return True if the specified line exists in the named file.""" assert "\n" not in to_find try: with open(filename) as f: for line in f: if to_find in line: return True return False except FileNotFoundError: return False def _write_line(content, filename): assert "\n" not in content if not _line_in_file(content, filename): with open(filename, "a") as f: f.write(content) def _write_content(content, filename): with open(filename, "w") as f: f.write(content) def _fail(msg): print(msg, file=sys.stderr) print("Miniver was not installed", file=sys.stderr) sys.exit(1) def extract_miniver_from_github(): filename, _ = urlretrieve(_miniver_zip_url) z = ZipFile(filename) tmpdir = tempfile.mkdtemp() input_paths = [ "/".join((_zipfile_root, "miniver", module + ".py")) for module in _miniver_modules ] for p in input_paths: z.extract(p, path=tmpdir) return [os.path.join(tmpdir, *p.split()) for p in input_paths] def extract_miniver_from_local(): return [ find_spec("." + module, package="miniver").origin for module in _miniver_modules ] def get_args(): parser = argparse.ArgumentParser( description="Install 'miniver' into the current Python package" ) parser.add_argument("-v", "--version", action="version", version=_miniver_version) parser.add_argument( "package_directory", help="Directory to install 'miniver' into." ) return parser.parse_args().package_directory def main(): package_dir = get_args() if not os.path.isdir(package_dir): _fail("Directory '{}' does not exist".format(package_dir)) if package_dir != os.path.relpath(package_dir): _fail("'{}' is not a relative directory".format(package_dir)) # Get miniver files if _miniver_is_installed: miniver_paths = extract_miniver_from_local() else: miniver_paths = extract_miniver_from_github() output_paths = [ os.path.join(package_dir, os.path.basename(path)) for path in miniver_paths ] for path in output_paths: if os.path.exists(path): _fail("'{}' already exists".format(path)) # Write content to local package directory for path, output_path in zip(miniver_paths, output_paths): shutil.copy(path, output_path) _write_content( _static_version_template, os.path.join(package_dir, "_static_version.py") ) _write_line( _gitattribute_template.format(package_dir=package_dir), ".gitattributes" ) _write_line( _init_template.format(package_dir=package_dir), os.path.join(package_dir, "__init__.py"), ) msg = "\n".join( textwrap.wrap( "Miniver is installed into '{package_dir}/'. " "You still have to copy the following snippet into your 'setup.py':" ) ) print("\n".join((msg, _setup_template)).format(package_dir=package_dir)) if __name__ == "__main__": main()