Added setup.py and other build tools like versioneer. Split functionlaity into modules so that new filters are easier to write.tags/nilmtools-0.1^0
@@ -0,0 +1 @@ | |||||
nilmtools/_version.py export-subst |
@@ -0,0 +1,6 @@ | |||||
build/ | |||||
*.pyc | |||||
dist/ | |||||
*.egg-info/ | |||||
MANIFEST.in | |||||
MANIFEST |
@@ -0,0 +1,24 @@ | |||||
all: | |||||
@echo "Try 'make install'" | |||||
version: | |||||
python setup.py version | |||||
dist: sdist | |||||
sdist: | |||||
python setup.py sdist | |||||
install: | |||||
python setup.py install | |||||
develop: | |||||
python setup.py develop | |||||
clean:: | |||||
find . -name '*pyc' | xargs rm -f | |||||
rm -rf nilmtools.egg-info/ build/ MANIFEST.in | |||||
gitclean:: | |||||
git clean -dXf | |||||
.PHONY: all version dist sdist install clean gitclean |
@@ -0,0 +1,14 @@ | |||||
nilmtools: Tools and utilities for interacting with the NILM Database, | |||||
or writing programs that interact with the NILM database. | |||||
by Jim Paris <jim@jtan.com> | |||||
Prerequisites: | |||||
# Runtime and build environments | |||||
sudo apt-get install python2.7 python2.7-dev python-setuptools | |||||
nilmdb (1.3.1+) | |||||
Install: | |||||
python setup.py install |
@@ -1,155 +0,0 @@ | |||||
#!/usr/bin/python | |||||
import nilmdb.client | |||||
from nilmdb.utils.printf import * | |||||
from nilmdb.utils.time import parse_time, format_time | |||||
import time | |||||
import sys | |||||
import re | |||||
import argparse | |||||
import subprocess | |||||
class ParseError(Exception): | |||||
def __init__(self, filename, error): | |||||
msg = filename + ": " + error | |||||
super(ParseError, self).__init__(msg) | |||||
parser = argparse.ArgumentParser(description = """\ | |||||
Insert data from ethstream, either live (using the system time as a | |||||
reference) or prerecorded (using comments in the file as a reference). | |||||
The data is assumed to have been recorded at the specified rate. | |||||
Small discrepencies between the accumulated timestamps and the | |||||
reference time are ignored; larger discrepencies cause gaps to be | |||||
created in the stream. Overlapping data returns an error. | |||||
""", formatter_class = argparse.RawDescriptionHelpFormatter) | |||||
parser.add_argument("-u", "--url", action="store", | |||||
default="http://localhost:12380/", | |||||
help="NilmDB server URL (default: %(default)s)") | |||||
parser.add_argument("-r", "--rate", action="store", default=8000, type=float, | |||||
help="Data rate in Hz (default: %(default)s)") | |||||
parser.add_argument("-l", "--live", action="store_true", | |||||
help="Live capture; use system time to verify rate") | |||||
parser.add_argument("path", action="store", | |||||
help="Path of stream, e.g. /foo/bar") | |||||
parser.add_argument("infile", type=argparse.FileType('r'), nargs='*', | |||||
default=[sys.stdin], help="Input files (default: stdin)") | |||||
args = parser.parse_args() | |||||
printf("Stream path: %s\n", args.path) | |||||
printf(" Data rate: %s Hz\n", repr(args.rate)) | |||||
client = nilmdb.client.Client(args.url) | |||||
# Local copies to save dictionary lookups | |||||
live = args.live | |||||
# data_ts is the timestamp that we'll use for the current line | |||||
data_ts_base = 0 | |||||
data_ts_inc = 0 | |||||
data_ts_step = 1.0 / args.rate | |||||
# clock_ts is the imprecise "real" timestamp (from the filename, | |||||
# comments, or or system clock) | |||||
clock_ts = None | |||||
def print_clock_updated(): | |||||
printf("Clock time updated to %s\n", format_time(clock_ts)) | |||||
if data_ts_base != 0: | |||||
diff = data_ts - clock_ts | |||||
if diff >= 0: | |||||
printf(" (data timestamp ahead by %.6f sec)\n", diff) | |||||
else: | |||||
printf(" (data timestamp behind by %.6f sec)\n", -diff) | |||||
with client.stream_insert_context(args.path) as stream: | |||||
for f in args.infile: | |||||
filename = f.name | |||||
printf("Processing %s\n", filename) | |||||
# If the filename ends in .gz, open it with gzcat instead. | |||||
if filename.endswith(".gz"): | |||||
p = subprocess.Popen(["gzip", "-dc"], | |||||
stdin = f, stdout = subprocess.PIPE) | |||||
f = p.stdout | |||||
# Try to get a real timestamp from the filename | |||||
try: | |||||
# Subtract 1 hour because files are created at the end of the hour. | |||||
# Hopefully, we'll be able to use internal comments and this value | |||||
# won't matter anyway. | |||||
clock_ts = parse_time(filename).totimestamp() - 3600 | |||||
print_clock_updated() | |||||
except ValueError: | |||||
pass | |||||
truncated_lines = 0 | |||||
# Read each line | |||||
for line in f: | |||||
data_ts = data_ts_base + data_ts_inc * data_ts_step | |||||
# If no content other than the newline, skip it | |||||
if len(line) <= 1: | |||||
continue | |||||
# If line starts with a comment, look for a timestamp | |||||
if line[0] == '#': | |||||
try: | |||||
clock_ts = parse_time(line[1:]).totimestamp() | |||||
print_clock_updated() | |||||
except ValueError: | |||||
pass | |||||
continue | |||||
# If inserting live, use clock timestamp | |||||
if live: | |||||
clock_ts = time.time() | |||||
# If we have a real timestamp, compare it to the data | |||||
# timestamp, and make sure things match up. | |||||
if clock_ts is not None: | |||||
if (data_ts - 10) > clock_ts: | |||||
# Accumulated line timestamps are in the future. | |||||
# If we were to set data_ts=clock_ts, we'd create | |||||
# an overlap, so we have to just bail out here. | |||||
err = sprintf("Data is coming in too fast: data time is %s " | |||||
"but clock time is only %s", | |||||
format_time(data_ts), format_time(clock_ts)) | |||||
raise ParseError(filename, err) | |||||
if (data_ts + 10) < clock_ts: | |||||
# Accumulated line timetamps are in the past. We | |||||
# can just skip some time and leave a gap in the | |||||
# data. | |||||
if data_ts_base != 0: | |||||
printf("Skipping data timestamp forward from %s to %s " | |||||
"to match clock time\n", | |||||
format_time(data_ts), format_time(clock_ts)) | |||||
stream.finalize() | |||||
data_ts_base = data_ts = clock_ts | |||||
data_ts_inc = 0 | |||||
# Don't use this clock time anymore until we update it | |||||
clock_ts = None | |||||
if data_ts_base == 0: | |||||
raise ParseError(filename, "No idea what timestamp to use") | |||||
# This line is legit, so increment timestamp | |||||
data_ts_inc += 1 | |||||
# Once in a while a line might be truncated, if we're at | |||||
# the end of a file. Ignore it, but if we ignore too many, | |||||
# bail out. | |||||
if line[-1] != '\n': | |||||
truncated_lines += 1 | |||||
if truncated_lines > 3: | |||||
raise ParseError(filename, "too many short lines") | |||||
printf("Ignoring short line in %s\n", filename) | |||||
continue | |||||
# Insert it | |||||
stream.insert("%.6f %s" % (data_ts, line)) | |||||
print "Done" |
@@ -0,0 +1,4 @@ | |||||
from ._version import get_versions | |||||
__version__ = get_versions()['version'] | |||||
del get_versions |
@@ -0,0 +1,197 @@ | |||||
IN_LONG_VERSION_PY = True | |||||
# 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 (build 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.7+ (https://github.com/warner/python-versioneer) | |||||
# these strings will be replaced by git during git-archive | |||||
git_refnames = "$Format:%d$" | |||||
git_full = "$Format:%H$" | |||||
import subprocess | |||||
import sys | |||||
def run_command(args, cwd=None, verbose=False): | |||||
try: | |||||
# remember shell=False, so use git.cmd on windows, not just git | |||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd) | |||||
except EnvironmentError: | |||||
e = sys.exc_info()[1] | |||||
if verbose: | |||||
print("unable to run %s" % args[0]) | |||||
print(e) | |||||
return None | |||||
stdout = p.communicate()[0].strip() | |||||
if sys.version >= '3': | |||||
stdout = stdout.decode() | |||||
if p.returncode != 0: | |||||
if verbose: | |||||
print("unable to run %s (error)" % args[0]) | |||||
return None | |||||
return stdout | |||||
import sys | |||||
import re | |||||
import os.path | |||||
def get_expanded_variables(versionfile_source): | |||||
# the code embedded in _version.py can just fetch the value of these | |||||
# variables. 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. | |||||
variables = {} | |||||
try: | |||||
for line in open(versionfile_source,"r").readlines(): | |||||
if line.strip().startswith("git_refnames ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["refnames"] = mo.group(1) | |||||
if line.strip().startswith("git_full ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["full"] = mo.group(1) | |||||
except EnvironmentError: | |||||
pass | |||||
return variables | |||||
def versions_from_expanded_variables(variables, tag_prefix, verbose=False): | |||||
refnames = variables["refnames"].strip() | |||||
if refnames.startswith("$Format"): | |||||
if verbose: | |||||
print("variables are unexpanded, not using") | |||||
return {} # unexpanded, so not in an unpacked git-archive tarball | |||||
refs = set([r.strip() for r in refnames.strip("()").split(",")]) | |||||
for ref in list(refs): | |||||
if not re.search(r'\d', ref): | |||||
if verbose: | |||||
print("discarding '%s', no digits" % ref) | |||||
refs.discard(ref) | |||||
# Assume all version tags have a digit. git's %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". | |||||
if verbose: | |||||
print("remaining refs: %s" % ",".join(sorted(refs))) | |||||
for ref in sorted(refs): | |||||
# 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": variables["full"].strip() } | |||||
# no suitable tags, so we use the full revision id | |||||
if verbose: | |||||
print("no suitable tags, using full revision id") | |||||
return { "version": variables["full"].strip(), | |||||
"full": variables["full"].strip() } | |||||
def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): | |||||
# this runs 'git' from the root of the source tree. That either means | |||||
# someone ran a setup.py command (and this code is in versioneer.py, so | |||||
# IN_LONG_VERSION_PY=False, thus the containing directory is the root of | |||||
# the source tree), or someone ran a project-specific entry point (and | |||||
# this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the | |||||
# containing directory is somewhere deeper in the source tree). This only | |||||
# gets called if the git-archive 'subst' variables 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. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# some py2exe/bbfreeze/non-CPython implementations don't do __file__ | |||||
return {} # not always correct | |||||
# 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__. | |||||
root = here | |||||
if IN_LONG_VERSION_PY: | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
root = os.path.dirname(here) | |||||
if not os.path.exists(os.path.join(root, ".git")): | |||||
if verbose: | |||||
print("no .git in %s" % root) | |||||
return {} | |||||
GIT = "git" | |||||
if sys.platform == "win32": | |||||
GIT = "git.cmd" | |||||
stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"], | |||||
cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
if not stdout.startswith(tag_prefix): | |||||
if verbose: | |||||
print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) | |||||
return {} | |||||
tag = stdout[len(tag_prefix):] | |||||
stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
full = stdout.strip() | |||||
if tag.endswith("-dirty"): | |||||
full += "-dirty" | |||||
return {"version": tag, "full": full} | |||||
def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): | |||||
if IN_LONG_VERSION_PY: | |||||
# We're running from _version.py. If it's from a source tree | |||||
# (execute-in-place), we can work upwards to find the root of the | |||||
# tree, and then check the parent directory for a version string. If | |||||
# it's in an installed application, there's no hope. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# py2exe/bbfreeze/non-CPython don't have __file__ | |||||
return {} # without __file__, we have no hope | |||||
# versionfile_source is the relative path from the top of the source | |||||
# tree to _version.py. Invert this to find the root from __file__. | |||||
root = here | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
# we're running from versioneer.py, which means we're running from | |||||
# the setup.py in a source tree. sys.argv[0] is setup.py in the root. | |||||
here = os.path.abspath(sys.argv[0]) | |||||
root = os.path.dirname(here) | |||||
# Source tarballs conventionally unpack into a directory that includes | |||||
# both the project name and a version string. | |||||
dirname = os.path.basename(root) | |||||
if not dirname.startswith(parentdir_prefix): | |||||
if verbose: | |||||
print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % | |||||
(root, dirname, parentdir_prefix)) | |||||
return None | |||||
return {"version": dirname[len(parentdir_prefix):], "full": ""} | |||||
tag_prefix = "nilmtools-" | |||||
parentdir_prefix = "nilmtools-" | |||||
versionfile_source = "nilmtools/_version.py" | |||||
def get_versions(default={"version": "unknown", "full": ""}, verbose=False): | |||||
variables = { "refnames": git_refnames, "full": git_full } | |||||
ver = versions_from_expanded_variables(variables, tag_prefix, verbose) | |||||
if not ver: | |||||
ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) | |||||
if not ver: | |||||
ver = versions_from_parentdir(parentdir_prefix, versionfile_source, | |||||
verbose) | |||||
if not ver: | |||||
ver = default | |||||
return ver | |||||
@@ -0,0 +1,66 @@ | |||||
#!/usr/bin/python | |||||
import nilmdb.client | |||||
from nilmdb.utils.printf import * | |||||
from nilmdb.utils.time import parse_time, format_time | |||||
import time | |||||
import sys | |||||
import re | |||||
import argparse | |||||
class Filter(object): | |||||
def __init__(self, description = "Filter data"): | |||||
self.args = None | |||||
self._client = None | |||||
self.parse_args(description) | |||||
def parse_args(self, description): | |||||
parser = argparse.ArgumentParser( | |||||
description = description, | |||||
formatter_class = argparse.RawDescriptionHelpFormatter) | |||||
parser.add_argument("-u", "--url", action="store", | |||||
default="http://localhost:12380/", | |||||
help="Server URL (default: %(default)s)") | |||||
parser.add_argument("srcpath", action="store", | |||||
help="Path of source stream, e.g. /foo/bar") | |||||
parser.add_argument("destpath", action="store", | |||||
help="Path of destination stream, e.g. /foo/bar") | |||||
self.args = parser.parse_args() | |||||
self._client = nilmdb.client.Client(args.url) | |||||
if args.srcpath == args.destpath: | |||||
raise Exception("source and destination path must be different") | |||||
# Open and print info about the streams | |||||
def stream_info_string(info): | |||||
return sprintf("%s (%s), %.2fM rows, %.2f hours\n", | |||||
info[0], info[1], info[4] / 1e6, info[5] / 3600) | |||||
src = self._client.stream_list(args.srcpath, extended = True) | |||||
if len(src) != 1: | |||||
raise Exception("source path " + args.srcpath + " not found") | |||||
print "Source:", stream_info_string(src[0]) | |||||
dest = self._client.stream_list(args.destpath, extended = True) | |||||
if len(dest) != 1: | |||||
raise Exception("destination path " + args.destpath + " not found") | |||||
print " Dest:", stream_info_string(dest[0]) | |||||
def intervals(self): | |||||
"""Generate all the intervals that this filter should process""" | |||||
for i in self._client.stream_intervals( | |||||
args.srcpath, diffpath = args.destpath): | |||||
yield i | |||||
def main(): | |||||
# This is just a dummy function; actual filters can use the other | |||||
# functions to prepare stuff, and then do something with the data. | |||||
f = Filter() | |||||
for interval in f.intervals(): | |||||
print "Generic filter: need to handle interval:", interval | |||||
if __name__ == "__main__": | |||||
main() |
@@ -0,0 +1,167 @@ | |||||
#!/usr/bin/python | |||||
import nilmdb.client | |||||
from nilmdb.utils.printf import * | |||||
from nilmdb.utils.time import parse_time, format_time | |||||
import time | |||||
import sys | |||||
import re | |||||
import argparse | |||||
import subprocess | |||||
class ParseError(Exception): | |||||
def __init__(self, filename, error): | |||||
msg = filename + ": " + error | |||||
super(ParseError, self).__init__(msg) | |||||
def parse_args(): | |||||
parser = argparse.ArgumentParser(description = """\ | |||||
Insert data from ethstream, either live (using the system time as a | |||||
reference) or prerecorded (using comments in the file as a reference). | |||||
The data is assumed to have been recorded at the specified rate. | |||||
Small discrepencies between the accumulated timestamps and the | |||||
reference time are ignored; larger discrepencies cause gaps to be | |||||
created in the stream. Overlapping data returns an error. | |||||
""", formatter_class = argparse.RawDescriptionHelpFormatter) | |||||
parser.add_argument("-u", "--url", action="store", | |||||
default="http://localhost:12380/", | |||||
help="NilmDB server URL (default: %(default)s)") | |||||
parser.add_argument("-r", "--rate", action="store", default=8000, type=float, | |||||
help="Data rate in Hz (default: %(default)s)") | |||||
parser.add_argument("-l", "--live", action="store_true", | |||||
help="Live capture; use system time to verify rate") | |||||
parser.add_argument("path", action="store", | |||||
help="Path of stream, e.g. /foo/bar") | |||||
parser.add_argument("infile", type=argparse.FileType('r'), nargs='*', | |||||
default=[sys.stdin], help="Input files (default: stdin)") | |||||
args = parser.parse_args() | |||||
printf("Stream path: %s\n", args.path) | |||||
printf(" Data rate: %s Hz\n", repr(args.rate)) | |||||
return args | |||||
def main(args = None): | |||||
if args is None: | |||||
args = parse_args() | |||||
client = nilmdb.client.Client(args.url) | |||||
# Local copies to save dictionary lookups | |||||
live = args.live | |||||
# data_ts is the timestamp that we'll use for the current line | |||||
data_ts_base = 0 | |||||
data_ts_inc = 0 | |||||
data_ts_step = 1.0 / args.rate | |||||
# clock_ts is the imprecise "real" timestamp (from the filename, | |||||
# comments, or or system clock) | |||||
clock_ts = None | |||||
def print_clock_updated(): | |||||
printf("Clock time updated to %s\n", format_time(clock_ts)) | |||||
if data_ts_base != 0: | |||||
diff = data_ts - clock_ts | |||||
if diff >= 0: | |||||
printf(" (data timestamp ahead by %.6f sec)\n", diff) | |||||
else: | |||||
printf(" (data timestamp behind by %.6f sec)\n", -diff) | |||||
with client.stream_insert_context(args.path) as stream: | |||||
for f in args.infile: | |||||
filename = f.name | |||||
printf("Processing %s\n", filename) | |||||
# If the filename ends in .gz, open it with gzcat instead. | |||||
if filename.endswith(".gz"): | |||||
p = subprocess.Popen(["gzip", "-dc"], | |||||
stdin = f, stdout = subprocess.PIPE) | |||||
f = p.stdout | |||||
# Try to get a real timestamp from the filename | |||||
try: | |||||
# Subtract 1 hour because files are created at the end | |||||
# of the hour. Hopefully, we'll be able to use | |||||
# internal comments and this value won't matter anyway. | |||||
clock_ts = parse_time(filename).totimestamp() - 3600 | |||||
print_clock_updated() | |||||
except ValueError: | |||||
pass | |||||
truncated_lines = 0 | |||||
# Read each line | |||||
for line in f: | |||||
data_ts = data_ts_base + data_ts_inc * data_ts_step | |||||
# If no content other than the newline, skip it | |||||
if len(line) <= 1: | |||||
continue | |||||
# If line starts with a comment, look for a timestamp | |||||
if line[0] == '#': | |||||
try: | |||||
clock_ts = parse_time(line[1:]).totimestamp() | |||||
print_clock_updated() | |||||
except ValueError: | |||||
pass | |||||
continue | |||||
# If inserting live, use clock timestamp | |||||
if live: | |||||
clock_ts = time.time() | |||||
# If we have a real timestamp, compare it to the data | |||||
# timestamp, and make sure things match up. | |||||
if clock_ts is not None: | |||||
if (data_ts - 10) > clock_ts: | |||||
# Accumulated line timestamps are in the future. | |||||
# If we were to set data_ts=clock_ts, we'd create | |||||
# an overlap, so we have to just bail out here. | |||||
err = sprintf("Data is coming in too fast: data time " | |||||
"is %s but clock time is only %s", | |||||
format_time(data_ts), | |||||
format_time(clock_ts)) | |||||
raise ParseError(filename, err) | |||||
if (data_ts + 10) < clock_ts: | |||||
# Accumulated line timetamps are in the past. We | |||||
# can just skip some time and leave a gap in the | |||||
# data. | |||||
if data_ts_base != 0: | |||||
printf("Skipping data timestamp forward from " | |||||
"%s to %s to match clock time\n", | |||||
format_time(data_ts), | |||||
format_time(clock_ts)) | |||||
stream.finalize() | |||||
data_ts_base = data_ts = clock_ts | |||||
data_ts_inc = 0 | |||||
# Don't use this clock time anymore until we update it | |||||
clock_ts = None | |||||
if data_ts_base == 0: | |||||
raise ParseError(filename, "No idea what timestamp to use") | |||||
# This line is legit, so increment timestamp | |||||
data_ts_inc += 1 | |||||
# Once in a while a line might be truncated, if we're at | |||||
# the end of a file. Ignore it, but if we ignore too many, | |||||
# bail out. | |||||
if line[-1] != '\n': | |||||
truncated_lines += 1 | |||||
if truncated_lines > 3: | |||||
raise ParseError(filename, "too many short lines") | |||||
printf("Ignoring short line in %s\n", filename) | |||||
continue | |||||
# Insert it | |||||
stream.insert("%.6f %s" % (data_ts, line)) | |||||
print "Done" | |||||
if __name__ == "__main__": | |||||
main() |
@@ -0,0 +1,75 @@ | |||||
#!/usr/bin/python | |||||
# To release a new version, tag it: | |||||
# git tag -a nilmtools-1.1 -m "Version 1.1" | |||||
# git push --tags | |||||
# Then just package it up: | |||||
# python setup.py sdist | |||||
# This is supposed to be using Distribute: | |||||
# | |||||
# distutils provides a "setup" method. | |||||
# setuptools is a set of monkeypatches on top of that. | |||||
# distribute is a particular version/implementation of setuptools. | |||||
# | |||||
# So we don't really know if this is using the old setuptools or the | |||||
# Distribute-provided version of setuptools. | |||||
import traceback | |||||
import sys | |||||
import os | |||||
try: | |||||
from setuptools import setup, find_packages | |||||
import distutils.version | |||||
except ImportError: | |||||
traceback.print_exc() | |||||
print "Please install the prerequisites listed in README.txt" | |||||
sys.exit(1) | |||||
# Versioneer manages version numbers from git tags. | |||||
# https://github.com/warner/python-versioneer | |||||
import versioneer | |||||
versioneer.versionfile_source = 'nilmtools/_version.py' | |||||
versioneer.versionfile_build = 'nilmtools/_version.py' | |||||
versioneer.tag_prefix = 'nilmtools-' | |||||
versioneer.parentdir_prefix = 'nilmtools-' | |||||
# Hack to workaround logging/multiprocessing issue: | |||||
# https://groups.google.com/d/msg/nose-users/fnJ-kAUbYHQ/_UsLN786ygcJ | |||||
try: import multiprocessing | |||||
except: pass | |||||
# We need a MANIFEST.in. Generate it here rather than polluting the | |||||
# repository with yet another setup-related file. | |||||
with open("MANIFEST.in", "w") as m: | |||||
m.write(""" | |||||
# Root | |||||
include README.txt | |||||
include setup.py | |||||
include versioneer.py | |||||
include Makefile | |||||
""") | |||||
# Run setup | |||||
setup(name='nilmtools', | |||||
version = versioneer.get_version(), | |||||
cmdclass = versioneer.get_cmdclass(), | |||||
url = 'https://git.jim.sh/jim/lees/nilmtools.git', | |||||
author = 'Jim Paris', | |||||
description = "NILM Database Tools", | |||||
long_description = "NILM Database Tools", | |||||
license = "Proprietary", | |||||
author_email = 'jim@jtan.com', | |||||
install_requires = [ 'nilmdb >= 1.3.0', | |||||
], | |||||
packages = [ 'nilmtools', | |||||
], | |||||
entry_points = { | |||||
'console_scripts': [ | |||||
'nilm-decimate = nilmtools.decimate:main', | |||||
'nilm-insert = nilmtools.insert:main', | |||||
], | |||||
}, | |||||
zip_safe = False, | |||||
) |
@@ -0,0 +1,655 @@ | |||||
#! /usr/bin/python | |||||
"""versioneer.py | |||||
(like a rocketeer, but for versions) | |||||
* https://github.com/warner/python-versioneer | |||||
* Brian Warner | |||||
* License: Public Domain | |||||
* Version: 0.7+ | |||||
This file helps distutils-based projects manage their version number by just | |||||
creating version-control tags. | |||||
For developers who work from a VCS-generated tree (e.g. 'git clone' etc), | |||||
each 'setup.py version', 'setup.py build', 'setup.py sdist' will compute a | |||||
version number by asking your version-control tool about the current | |||||
checkout. The version number will be written into a generated _version.py | |||||
file of your choosing, where it can be included by your __init__.py | |||||
For users who work from a VCS-generated tarball (e.g. 'git archive'), it will | |||||
compute a version number by looking at the name of the directory created when | |||||
te tarball is unpacked. This conventionally includes both the name of the | |||||
project and a version number. | |||||
For users who work from a tarball built by 'setup.py sdist', it will get a | |||||
version number from a previously-generated _version.py file. | |||||
As a result, loading code directly from the source tree will not result in a | |||||
real version. If you want real versions from VCS trees (where you frequently | |||||
update from the upstream repository, or do new development), you will need to | |||||
do a 'setup.py version' after each update, and load code from the build/ | |||||
directory. | |||||
You need to provide this code with a few configuration values: | |||||
versionfile_source: | |||||
A project-relative pathname into which the generated version strings | |||||
should be written. This is usually a _version.py next to your project's | |||||
main __init__.py file. If your project uses src/myproject/__init__.py, | |||||
this should be 'src/myproject/_version.py'. This file should be checked | |||||
in to your VCS as usual: the copy created below by 'setup.py | |||||
update_files' will include code that parses expanded VCS keywords in | |||||
generated tarballs. The 'build' and 'sdist' commands will replace it with | |||||
a copy that has just the calculated version string. | |||||
versionfile_build: | |||||
Like versionfile_source, but relative to the build directory instead of | |||||
the source directory. These will differ when your setup.py uses | |||||
'package_dir='. If you have package_dir={'myproject': 'src/myproject'}, | |||||
then you will probably have versionfile_build='myproject/_version.py' and | |||||
versionfile_source='src/myproject/_version.py'. | |||||
tag_prefix: a string, like 'PROJECTNAME-', which appears at the start of all | |||||
VCS tags. If your tags look like 'myproject-1.2.0', then you | |||||
should use tag_prefix='myproject-'. If you use unprefixed tags | |||||
like '1.2.0', this should be an empty string. | |||||
parentdir_prefix: a string, frequently the same as tag_prefix, which | |||||
appears at the start of all unpacked tarball filenames. If | |||||
your tarball unpacks into 'myproject-1.2.0', this should | |||||
be 'myproject-'. | |||||
To use it: | |||||
1: include this file in the top level of your project | |||||
2: make the following changes to the top of your setup.py: | |||||
import versioneer | |||||
versioneer.versionfile_source = 'src/myproject/_version.py' | |||||
versioneer.versionfile_build = 'myproject/_version.py' | |||||
versioneer.tag_prefix = '' # tags are like 1.2.0 | |||||
versioneer.parentdir_prefix = 'myproject-' # dirname like 'myproject-1.2.0' | |||||
3: add the following arguments to the setup() call in your setup.py: | |||||
version=versioneer.get_version(), | |||||
cmdclass=versioneer.get_cmdclass(), | |||||
4: run 'setup.py update_files', which will create _version.py, and will | |||||
append the following to your __init__.py: | |||||
from _version import __version__ | |||||
5: modify your MANIFEST.in to include versioneer.py | |||||
6: add both versioneer.py and the generated _version.py to your VCS | |||||
""" | |||||
import os, sys, re | |||||
from distutils.core import Command | |||||
from distutils.command.sdist import sdist as _sdist | |||||
from distutils.command.build_py import build_py as _build_py | |||||
versionfile_source = None | |||||
versionfile_build = None | |||||
tag_prefix = None | |||||
parentdir_prefix = None | |||||
VCS = "git" | |||||
IN_LONG_VERSION_PY = False | |||||
LONG_VERSION_PY = ''' | |||||
IN_LONG_VERSION_PY = True | |||||
# 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 (build 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.7+ (https://github.com/warner/python-versioneer) | |||||
# these strings will be replaced by git during git-archive | |||||
git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" | |||||
git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" | |||||
import subprocess | |||||
import sys | |||||
def run_command(args, cwd=None, verbose=False): | |||||
try: | |||||
# remember shell=False, so use git.cmd on windows, not just git | |||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd) | |||||
except EnvironmentError: | |||||
e = sys.exc_info()[1] | |||||
if verbose: | |||||
print("unable to run %%s" %% args[0]) | |||||
print(e) | |||||
return None | |||||
stdout = p.communicate()[0].strip() | |||||
if sys.version >= '3': | |||||
stdout = stdout.decode() | |||||
if p.returncode != 0: | |||||
if verbose: | |||||
print("unable to run %%s (error)" %% args[0]) | |||||
return None | |||||
return stdout | |||||
import sys | |||||
import re | |||||
import os.path | |||||
def get_expanded_variables(versionfile_source): | |||||
# the code embedded in _version.py can just fetch the value of these | |||||
# variables. 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. | |||||
variables = {} | |||||
try: | |||||
for line in open(versionfile_source,"r").readlines(): | |||||
if line.strip().startswith("git_refnames ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["refnames"] = mo.group(1) | |||||
if line.strip().startswith("git_full ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["full"] = mo.group(1) | |||||
except EnvironmentError: | |||||
pass | |||||
return variables | |||||
def versions_from_expanded_variables(variables, tag_prefix, verbose=False): | |||||
refnames = variables["refnames"].strip() | |||||
if refnames.startswith("$Format"): | |||||
if verbose: | |||||
print("variables are unexpanded, not using") | |||||
return {} # unexpanded, so not in an unpacked git-archive tarball | |||||
refs = set([r.strip() for r in refnames.strip("()").split(",")]) | |||||
for ref in list(refs): | |||||
if not re.search(r'\d', ref): | |||||
if verbose: | |||||
print("discarding '%%s', no digits" %% ref) | |||||
refs.discard(ref) | |||||
# Assume all version tags have a digit. git's %%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". | |||||
if verbose: | |||||
print("remaining refs: %%s" %% ",".join(sorted(refs))) | |||||
for ref in sorted(refs): | |||||
# 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": variables["full"].strip() } | |||||
# no suitable tags, so we use the full revision id | |||||
if verbose: | |||||
print("no suitable tags, using full revision id") | |||||
return { "version": variables["full"].strip(), | |||||
"full": variables["full"].strip() } | |||||
def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): | |||||
# this runs 'git' from the root of the source tree. That either means | |||||
# someone ran a setup.py command (and this code is in versioneer.py, so | |||||
# IN_LONG_VERSION_PY=False, thus the containing directory is the root of | |||||
# the source tree), or someone ran a project-specific entry point (and | |||||
# this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the | |||||
# containing directory is somewhere deeper in the source tree). This only | |||||
# gets called if the git-archive 'subst' variables 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. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# some py2exe/bbfreeze/non-CPython implementations don't do __file__ | |||||
return {} # not always correct | |||||
# 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__. | |||||
root = here | |||||
if IN_LONG_VERSION_PY: | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
root = os.path.dirname(here) | |||||
if not os.path.exists(os.path.join(root, ".git")): | |||||
if verbose: | |||||
print("no .git in %%s" %% root) | |||||
return {} | |||||
GIT = "git" | |||||
if sys.platform == "win32": | |||||
GIT = "git.cmd" | |||||
stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"], | |||||
cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
if not stdout.startswith(tag_prefix): | |||||
if verbose: | |||||
print("tag '%%s' doesn't start with prefix '%%s'" %% (stdout, tag_prefix)) | |||||
return {} | |||||
tag = stdout[len(tag_prefix):] | |||||
stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
full = stdout.strip() | |||||
if tag.endswith("-dirty"): | |||||
full += "-dirty" | |||||
return {"version": tag, "full": full} | |||||
def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): | |||||
if IN_LONG_VERSION_PY: | |||||
# We're running from _version.py. If it's from a source tree | |||||
# (execute-in-place), we can work upwards to find the root of the | |||||
# tree, and then check the parent directory for a version string. If | |||||
# it's in an installed application, there's no hope. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# py2exe/bbfreeze/non-CPython don't have __file__ | |||||
return {} # without __file__, we have no hope | |||||
# versionfile_source is the relative path from the top of the source | |||||
# tree to _version.py. Invert this to find the root from __file__. | |||||
root = here | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
# we're running from versioneer.py, which means we're running from | |||||
# the setup.py in a source tree. sys.argv[0] is setup.py in the root. | |||||
here = os.path.abspath(sys.argv[0]) | |||||
root = os.path.dirname(here) | |||||
# Source tarballs conventionally unpack into a directory that includes | |||||
# both the project name and a version string. | |||||
dirname = os.path.basename(root) | |||||
if not dirname.startswith(parentdir_prefix): | |||||
if verbose: | |||||
print("guessing rootdir is '%%s', but '%%s' doesn't start with prefix '%%s'" %% | |||||
(root, dirname, parentdir_prefix)) | |||||
return None | |||||
return {"version": dirname[len(parentdir_prefix):], "full": ""} | |||||
tag_prefix = "%(TAG_PREFIX)s" | |||||
parentdir_prefix = "%(PARENTDIR_PREFIX)s" | |||||
versionfile_source = "%(VERSIONFILE_SOURCE)s" | |||||
def get_versions(default={"version": "unknown", "full": ""}, verbose=False): | |||||
variables = { "refnames": git_refnames, "full": git_full } | |||||
ver = versions_from_expanded_variables(variables, tag_prefix, verbose) | |||||
if not ver: | |||||
ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) | |||||
if not ver: | |||||
ver = versions_from_parentdir(parentdir_prefix, versionfile_source, | |||||
verbose) | |||||
if not ver: | |||||
ver = default | |||||
return ver | |||||
''' | |||||
import subprocess | |||||
import sys | |||||
def run_command(args, cwd=None, verbose=False): | |||||
try: | |||||
# remember shell=False, so use git.cmd on windows, not just git | |||||
p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd) | |||||
except EnvironmentError: | |||||
e = sys.exc_info()[1] | |||||
if verbose: | |||||
print("unable to run %s" % args[0]) | |||||
print(e) | |||||
return None | |||||
stdout = p.communicate()[0].strip() | |||||
if sys.version >= '3': | |||||
stdout = stdout.decode() | |||||
if p.returncode != 0: | |||||
if verbose: | |||||
print("unable to run %s (error)" % args[0]) | |||||
return None | |||||
return stdout | |||||
import sys | |||||
import re | |||||
import os.path | |||||
def get_expanded_variables(versionfile_source): | |||||
# the code embedded in _version.py can just fetch the value of these | |||||
# variables. 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. | |||||
variables = {} | |||||
try: | |||||
for line in open(versionfile_source,"r").readlines(): | |||||
if line.strip().startswith("git_refnames ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["refnames"] = mo.group(1) | |||||
if line.strip().startswith("git_full ="): | |||||
mo = re.search(r'=\s*"(.*)"', line) | |||||
if mo: | |||||
variables["full"] = mo.group(1) | |||||
except EnvironmentError: | |||||
pass | |||||
return variables | |||||
def versions_from_expanded_variables(variables, tag_prefix, verbose=False): | |||||
refnames = variables["refnames"].strip() | |||||
if refnames.startswith("$Format"): | |||||
if verbose: | |||||
print("variables are unexpanded, not using") | |||||
return {} # unexpanded, so not in an unpacked git-archive tarball | |||||
refs = set([r.strip() for r in refnames.strip("()").split(",")]) | |||||
for ref in list(refs): | |||||
if not re.search(r'\d', ref): | |||||
if verbose: | |||||
print("discarding '%s', no digits" % ref) | |||||
refs.discard(ref) | |||||
# Assume all version tags have a digit. git's %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". | |||||
if verbose: | |||||
print("remaining refs: %s" % ",".join(sorted(refs))) | |||||
for ref in sorted(refs): | |||||
# 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": variables["full"].strip() } | |||||
# no suitable tags, so we use the full revision id | |||||
if verbose: | |||||
print("no suitable tags, using full revision id") | |||||
return { "version": variables["full"].strip(), | |||||
"full": variables["full"].strip() } | |||||
def versions_from_vcs(tag_prefix, versionfile_source, verbose=False): | |||||
# this runs 'git' from the root of the source tree. That either means | |||||
# someone ran a setup.py command (and this code is in versioneer.py, so | |||||
# IN_LONG_VERSION_PY=False, thus the containing directory is the root of | |||||
# the source tree), or someone ran a project-specific entry point (and | |||||
# this code is in _version.py, so IN_LONG_VERSION_PY=True, thus the | |||||
# containing directory is somewhere deeper in the source tree). This only | |||||
# gets called if the git-archive 'subst' variables 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. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# some py2exe/bbfreeze/non-CPython implementations don't do __file__ | |||||
return {} # not always correct | |||||
# 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__. | |||||
root = here | |||||
if IN_LONG_VERSION_PY: | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
root = os.path.dirname(here) | |||||
if not os.path.exists(os.path.join(root, ".git")): | |||||
if verbose: | |||||
print("no .git in %s" % root) | |||||
return {} | |||||
GIT = "git" | |||||
if sys.platform == "win32": | |||||
GIT = "git.cmd" | |||||
stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"], | |||||
cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
if not stdout.startswith(tag_prefix): | |||||
if verbose: | |||||
print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) | |||||
return {} | |||||
tag = stdout[len(tag_prefix):] | |||||
stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) | |||||
if stdout is None: | |||||
return {} | |||||
full = stdout.strip() | |||||
if tag.endswith("-dirty"): | |||||
full += "-dirty" | |||||
return {"version": tag, "full": full} | |||||
def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): | |||||
if IN_LONG_VERSION_PY: | |||||
# We're running from _version.py. If it's from a source tree | |||||
# (execute-in-place), we can work upwards to find the root of the | |||||
# tree, and then check the parent directory for a version string. If | |||||
# it's in an installed application, there's no hope. | |||||
try: | |||||
here = os.path.abspath(__file__) | |||||
except NameError: | |||||
# py2exe/bbfreeze/non-CPython don't have __file__ | |||||
return {} # without __file__, we have no hope | |||||
# versionfile_source is the relative path from the top of the source | |||||
# tree to _version.py. Invert this to find the root from __file__. | |||||
root = here | |||||
for i in range(len(versionfile_source.split("/"))): | |||||
root = os.path.dirname(root) | |||||
else: | |||||
# we're running from versioneer.py, which means we're running from | |||||
# the setup.py in a source tree. sys.argv[0] is setup.py in the root. | |||||
here = os.path.abspath(sys.argv[0]) | |||||
root = os.path.dirname(here) | |||||
# Source tarballs conventionally unpack into a directory that includes | |||||
# both the project name and a version string. | |||||
dirname = os.path.basename(root) | |||||
if not dirname.startswith(parentdir_prefix): | |||||
if verbose: | |||||
print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % | |||||
(root, dirname, parentdir_prefix)) | |||||
return None | |||||
return {"version": dirname[len(parentdir_prefix):], "full": ""} | |||||
import sys | |||||
def do_vcs_install(versionfile_source, ipy): | |||||
GIT = "git" | |||||
if sys.platform == "win32": | |||||
GIT = "git.cmd" | |||||
run_command([GIT, "add", "versioneer.py"]) | |||||
run_command([GIT, "add", versionfile_source]) | |||||
run_command([GIT, "add", ipy]) | |||||
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() | |||||
run_command([GIT, "add", ".gitattributes"]) | |||||
SHORT_VERSION_PY = """ | |||||
# This file was generated by 'versioneer.py' (0.7+) 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. | |||||
version_version = '%(version)s' | |||||
version_full = '%(full)s' | |||||
def get_versions(default={}, verbose=False): | |||||
return {'version': version_version, 'full': version_full} | |||||
""" | |||||
DEFAULT = {"version": "unknown", "full": "unknown"} | |||||
def versions_from_file(filename): | |||||
versions = {} | |||||
try: | |||||
f = open(filename) | |||||
except EnvironmentError: | |||||
return versions | |||||
for line in f.readlines(): | |||||
mo = re.match("version_version = '([^']+)'", line) | |||||
if mo: | |||||
versions["version"] = mo.group(1) | |||||
mo = re.match("version_full = '([^']+)'", line) | |||||
if mo: | |||||
versions["full"] = mo.group(1) | |||||
return versions | |||||
def write_to_version_file(filename, versions): | |||||
f = open(filename, "w") | |||||
f.write(SHORT_VERSION_PY % versions) | |||||
f.close() | |||||
print("set %s to '%s'" % (filename, versions["version"])) | |||||
def get_best_versions(versionfile, tag_prefix, parentdir_prefix, | |||||
default=DEFAULT, verbose=False): | |||||
# returns dict with two keys: 'version' and 'full' | |||||
# | |||||
# extract version from first of _version.py, '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. | |||||
variables = get_expanded_variables(versionfile_source) | |||||
if variables: | |||||
ver = versions_from_expanded_variables(variables, tag_prefix) | |||||
if ver: | |||||
if verbose: print("got version from expanded variable %s" % ver) | |||||
return ver | |||||
ver = versions_from_file(versionfile) | |||||
if ver: | |||||
if verbose: print("got version from file %s %s" % (versionfile, ver)) | |||||
return ver | |||||
ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) | |||||
if ver: | |||||
if verbose: print("got version from git %s" % ver) | |||||
return ver | |||||
ver = versions_from_parentdir(parentdir_prefix, versionfile_source, verbose) | |||||
if ver: | |||||
if verbose: print("got version from parentdir %s" % ver) | |||||
return ver | |||||
if verbose: print("got version from default %s" % ver) | |||||
return default | |||||
def get_versions(default=DEFAULT, verbose=False): | |||||
assert versionfile_source is not None, "please set versioneer.versionfile_source" | |||||
assert tag_prefix is not None, "please set versioneer.tag_prefix" | |||||
assert parentdir_prefix is not None, "please set versioneer.parentdir_prefix" | |||||
return get_best_versions(versionfile_source, tag_prefix, parentdir_prefix, | |||||
default=default, verbose=verbose) | |||||
def get_version(verbose=False): | |||||
return get_versions(verbose=verbose)["version"] | |||||
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): | |||||
ver = get_version(verbose=True) | |||||
print("Version is currently: %s" % ver) | |||||
class cmd_build_py(_build_py): | |||||
def run(self): | |||||
versions = get_versions(verbose=True) | |||||
_build_py.run(self) | |||||
# now locate _version.py in the new build/ directory and replace it | |||||
# with an updated value | |||||
target_versionfile = os.path.join(self.build_lib, versionfile_build) | |||||
print("UPDATING %s" % target_versionfile) | |||||
os.unlink(target_versionfile) | |||||
f = open(target_versionfile, "w") | |||||
f.write(SHORT_VERSION_PY % versions) | |||||
f.close() | |||||
class cmd_sdist(_sdist): | |||||
def run(self): | |||||
versions = get_versions(verbose=True) | |||||
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): | |||||
_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, versionfile_source) | |||||
print("UPDATING %s" % target_versionfile) | |||||
os.unlink(target_versionfile) | |||||
f = open(target_versionfile, "w") | |||||
f.write(SHORT_VERSION_PY % self._versioneer_generated_versions) | |||||
f.close() | |||||
INIT_PY_SNIPPET = """ | |||||
from ._version import get_versions | |||||
__version__ = get_versions()['version'] | |||||
del get_versions | |||||
""" | |||||
class cmd_update_files(Command): | |||||
description = "modify __init__.py and create _version.py" | |||||
user_options = [] | |||||
boolean_options = [] | |||||
def initialize_options(self): | |||||
pass | |||||
def finalize_options(self): | |||||
pass | |||||
def run(self): | |||||
ipy = os.path.join(os.path.dirname(versionfile_source), "__init__.py") | |||||
print(" creating %s" % versionfile_source) | |||||
f = open(versionfile_source, "w") | |||||
f.write(LONG_VERSION_PY % {"DOLLAR": "$", | |||||
"TAG_PREFIX": tag_prefix, | |||||
"PARENTDIR_PREFIX": parentdir_prefix, | |||||
"VERSIONFILE_SOURCE": versionfile_source, | |||||
}) | |||||
f.close() | |||||
try: | |||||
old = open(ipy, "r").read() | |||||
except EnvironmentError: | |||||
old = "" | |||||
if INIT_PY_SNIPPET not in old: | |||||
print(" appending to %s" % ipy) | |||||
f = open(ipy, "a") | |||||
f.write(INIT_PY_SNIPPET) | |||||
f.close() | |||||
else: | |||||
print(" %s unmodified" % ipy) | |||||
do_vcs_install(versionfile_source, ipy) | |||||
def get_cmdclass(): | |||||
return {'version': cmd_version, | |||||
'update_files': cmd_update_files, | |||||
'build_py': cmd_build_py, | |||||
'sdist': cmd_sdist, | |||||
} |