Compare commits

..

6 Commits

17 changed files with 138 additions and 111 deletions

2
.gitattributes vendored
View File

@ -1 +1 @@
src/_version.py export-subst nilmrun/_version.py export-subst

2
.gitignore vendored
View File

@ -3,8 +3,8 @@ build/
*.pyc *.pyc
dist/ dist/
nilmrun.egg-info/ nilmrun.egg-info/
.eggs/
# This gets generated as needed by setup.py # This gets generated as needed by setup.py
MANIFEST.in
MANIFEST MANIFEST

8
MANIFEST.in Normal file
View File

@ -0,0 +1,8 @@
# Root
include README.md
include setup.py
include versioneer.py
include Makefile
# Version
include nilmrun/_version.py

View File

@ -1,9 +1,6 @@
# By default, run the tests. # By default, run the tests.
all: test all: test
test2:
nilmrun/trainola.py data.js
version: version:
python3 setup.py version python3 setup.py version
@ -23,8 +20,11 @@ develop:
docs: docs:
make -C docs make -C docs
ctrl: flake
flake:
flake8 nilmrun
lint: lint:
pylint3 --rcfile=.pylintrc nilmdb pylint3 --rcfile=setup.cfg nilmrun
test: test:
ifneq ($(INSIDE_EMACS),) ifneq ($(INSIDE_EMACS),)
@ -37,12 +37,13 @@ else
endif endif
clean:: clean::
find . -name '*.pyc' -o -name '__pycache__' -print0 | xargs -0 rm -rf
rm -f .coverage rm -f .coverage
find . -name '*pyc' | xargs rm -f rm -rf nilmrun.egg-info/ build/
rm -rf nilmtools.egg-info/ build/ MANIFEST.in
make -C docs clean make -C docs clean
gitclean:: gitclean::
git clean -dXf git clean -dXf
.PHONY: all version dist sdist install docs lint test clean gitclean .PHONY: all version dist sdist install docs test
.PHONY: ctrl lint flake clean gitclean

View File

@ -1,5 +1,3 @@
import nilmrun.processmanager
from ._version import get_versions from ._version import get_versions
__version__ = get_versions()['version'] __version__ = get_versions()['version']
del get_versions del get_versions

View File

@ -41,9 +41,9 @@ def get_config():
cfg = VersioneerConfig() cfg = VersioneerConfig()
cfg.VCS = "git" cfg.VCS = "git"
cfg.style = "pep440" cfg.style = "pep440"
cfg.tag_prefix = "nilmdb-" cfg.tag_prefix = "nilmrun-"
cfg.parentdir_prefix = "nilmdb-" cfg.parentdir_prefix = "nilmrun-"
cfg.versionfile_source = "nilmdb/_version.py" cfg.versionfile_source = "nilmrun/_version.py"
cfg.verbose = False cfg.verbose = False
return cfg return cfg
@ -86,20 +86,20 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
continue continue
if verbose: if verbose:
print(("unable to run %s" % dispcmd)) print("unable to run %s" % dispcmd)
print(e) print(e)
return None, None return None, None
else: else:
if verbose: if verbose:
print(("unable to find command, tried %s" % (commands,))) print("unable to find command, tried %s" % (commands,))
return None, None return None, None
stdout = p.communicate()[0].strip() stdout = p.communicate()[0].strip()
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
stdout = stdout.decode() stdout = stdout.decode()
if p.returncode != 0: if p.returncode != 0:
if verbose: if verbose:
print(("unable to run %s (error)" % dispcmd)) print("unable to run %s (error)" % dispcmd)
print(("stdout was %s" % stdout)) print("stdout was %s" % stdout)
return None, p.returncode return None, p.returncode
return stdout, p.returncode return stdout, p.returncode
@ -124,8 +124,8 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
root = os.path.dirname(root) # up a level root = os.path.dirname(root) # up a level
if verbose: if verbose:
print(("Tried directories %s but none started with prefix %s" % print("Tried directories %s but none started with prefix %s" %
(str(rootdirs), parentdir_prefix))) (str(rootdirs), parentdir_prefix))
raise NotThisMethod("rootdir doesn't start with parentdir_prefix") raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
@ -192,15 +192,15 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
# "stabilization", as well as "HEAD" and "master". # "stabilization", as well as "HEAD" and "master".
tags = set([r for r in refs if re.search(r'\d', r)]) tags = set([r for r in refs if re.search(r'\d', r)])
if verbose: if verbose:
print(("discarding '%s', no digits" % ",".join(refs - tags))) print("discarding '%s', no digits" % ",".join(refs - tags))
if verbose: if verbose:
print(("likely tags: %s" % ",".join(sorted(tags)))) print("likely tags: %s" % ",".join(sorted(tags)))
for ref in sorted(tags): for ref in sorted(tags):
# sorting will prefer e.g. "2.0" over "2.0rc1" # sorting will prefer e.g. "2.0" over "2.0rc1"
if ref.startswith(tag_prefix): if ref.startswith(tag_prefix):
r = ref[len(tag_prefix):] r = ref[len(tag_prefix):]
if verbose: if verbose:
print(("picking %s" % r)) print("picking %s" % r)
return {"version": r, return {"version": r,
"full-revisionid": keywords["full"].strip(), "full-revisionid": keywords["full"].strip(),
"dirty": False, "error": None, "dirty": False, "error": None,
@ -229,7 +229,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
hide_stderr=True) hide_stderr=True)
if rc != 0: if rc != 0:
if verbose: if verbose:
print(("Directory %s not under git control" % root)) print("Directory %s not under git control" % root)
raise NotThisMethod("'git rev-parse --git-dir' returned error") 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 is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
@ -278,7 +278,7 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
if not full_tag.startswith(tag_prefix): if not full_tag.startswith(tag_prefix):
if verbose: if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'" fmt = "tag '%s' doesn't start with prefix '%s'"
print((fmt % (full_tag, tag_prefix))) print(fmt % (full_tag, tag_prefix))
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
% (full_tag, tag_prefix)) % (full_tag, tag_prefix))
return pieces return pieces

View File

@ -1,6 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
from nilmdb.utils.printf import *
import threading import threading
import subprocess import subprocess
@ -15,16 +13,18 @@ import tempfile
import atexit import atexit
import shutil import shutil
class ProcessError(Exception): class ProcessError(Exception):
pass pass
class LogReceiver(object): class LogReceiver(object):
"""Spawn a thread that listens to a pipe for log messages, """Spawn a thread that listens to a pipe for log messages,
and stores them locally.""" and stores them locally."""
def __init__(self, pipe): def __init__(self, pipe):
self.pipe = pipe self.pipe = pipe
self.log = io.BytesIO() self.log = io.BytesIO()
self.thread = threading.Thread(target = self.run) self.thread = threading.Thread(target=self.run)
self.thread.start() self.thread.start()
def run(self): def run(self):
@ -41,9 +41,10 @@ class LogReceiver(object):
def clear(self): def clear(self):
self.log = io.BytesIO() self.log = io.BytesIO()
class Process(object): class Process(object):
"""Spawn and manage a subprocess, and capture its output.""" """Spawn and manage a subprocess, and capture its output."""
def __init__(self, argv, tempfile = None): def __init__(self, argv, tempfile=None):
self.start_time = None self.start_time = None
# Use a pipe for communicating log data # Use a pipe for communicating log data
@ -55,9 +56,9 @@ class Process(object):
# Spawn the new process # Spawn the new process
try: try:
self._process = subprocess.Popen(args = argv, stdin = nullfd, self._process = subprocess.Popen(args=argv, stdin=nullfd,
stdout = wpipe, stderr = wpipe, stdout=wpipe, stderr=wpipe,
close_fds = True, cwd = "/tmp") close_fds=True, cwd="/tmp")
except (OSError, TypeError) as e: except (OSError, TypeError) as e:
raise ProcessError(str(e)) raise ProcessError(str(e))
finally: finally:
@ -69,7 +70,7 @@ class Process(object):
self.start_time = time.time() self.start_time = time.time()
self.pid = str(uuid.uuid1(self._process.pid or 0)) self.pid = str(uuid.uuid1(self._process.pid or 0))
def _join(self, timeout = 1.0): def _join(self, timeout=1.0):
start = time.time() start = time.time()
while True: while True:
if self._process.poll() is not None: if self._process.poll() is not None:
@ -78,7 +79,7 @@ class Process(object):
return False return False
time.sleep(0.1) time.sleep(0.1)
def terminate(self, timeout = 1.0): def terminate(self, timeout=1.0):
"""Terminate a process, and all of its children that are in the same """Terminate a process, and all of its children that are in the same
process group.""" process group."""
try: try:
@ -89,19 +90,19 @@ class Process(object):
def getpgid(pid): def getpgid(pid):
try: try:
return os.getpgid(pid) return os.getpgid(pid)
except OSError: # pragma: no cover except OSError: # pragma: no cover
return None return None
def kill(pid, sig): def kill(pid, sig):
try: try:
return os.kill(pid, sig) return os.kill(pid, sig)
except OSError: # pragma: no cover except OSError: # pragma: no cover
return return
# Find all children # Find all children
group = getpgid(self._process.pid) group = getpgid(self._process.pid)
main = psutil.Process(self._process.pid) main = psutil.Process(self._process.pid)
allproc = [ main ] + main.children(recursive = True) allproc = [main] + main.children(recursive=True)
# Kill with SIGTERM, if they're still in this process group # Kill with SIGTERM, if they're still in this process group
for proc in allproc: for proc in allproc:
@ -119,7 +120,7 @@ class Process(object):
# See if it worked # See if it worked
return self._join(timeout) return self._join(timeout)
except psutil.Error: # pragma: no cover (race condition) except psutil.Error: # pragma: no cover (race condition)
return True return True
def clear_log(self): def clear_log(self):
@ -142,22 +143,22 @@ class Process(object):
Call .get_info() about a second later.""" Call .get_info() about a second later."""
try: try:
main = psutil.Process(self._process.pid) main = psutil.Process(self._process.pid)
self._process_list = [ main ] + main.children(recursive = True) self._process_list = [main] + main.children(recursive=True)
for proc in self._process_list: for proc in self._process_list:
proc.cpu_percent(0) proc.cpu_percent(0)
except psutil.Error: # pragma: no cover (race condition) except psutil.Error: # pragma: no cover (race condition)
self._process_list = [ ] self._process_list = []
@staticmethod @staticmethod
def get_empty_info(): def get_empty_info():
return { "cpu_percent": 0, return {"cpu_percent": 0,
"cpu_user": 0, "cpu_user": 0,
"cpu_sys": 0, "cpu_sys": 0,
"mem_phys": 0, "mem_phys": 0,
"mem_virt": 0, "mem_virt": 0,
"io_read": 0, "io_read": 0,
"io_write": 0, "io_write": 0,
"procs": 0 } "procs": 0}
def get_info(self): def get_info(self):
"""Return a dictionary with info about the process CPU and memory """Return a dictionary with info about the process CPU and memory
@ -180,6 +181,7 @@ class Process(object):
pass pass
return d return d
class ProcessManager(object): class ProcessManager(object):
"""Track and manage a collection of Process objects""" """Track and manage a collection of Process objects"""
def __init__(self): def __init__(self):
@ -191,7 +193,7 @@ class ProcessManager(object):
if pid in self.tmpdirs: if pid in self.tmpdirs:
try: try:
shutil.rmtree(self.tmpdirs[pid]) shutil.rmtree(self.tmpdirs[pid])
except OSError: # pragma: no cover except OSError: # pragma: no cover
pass pass
del self.tmpdirs[pid] del self.tmpdirs[pid]
@ -203,7 +205,7 @@ class ProcessManager(object):
del self.processes[pid] del self.processes[pid]
shutil.rmtree(self.tmpdirs[pid]) shutil.rmtree(self.tmpdirs[pid])
del self.tmpdirs[pid] del self.tmpdirs[pid]
except Exception: # pragma: no cover except Exception: # pragma: no cover
pass pass
def __iter__(self): def __iter__(self):
@ -218,7 +220,7 @@ class ProcessManager(object):
accessible in the code as sys.argv[1:].""" accessible in the code as sys.argv[1:]."""
# The easiest way to do this, by far, is to just write the # The easiest way to do this, by far, is to just write the
# code to a file. Make a directory to put it in. # code to a file. Make a directory to put it in.
tmpdir = tempfile.mkdtemp(prefix = "nilmrun-usercode-") tmpdir = tempfile.mkdtemp(prefix="nilmrun-usercode-")
try: try:
# Write the code # Write the code
codepath = os.path.join(tmpdir, "usercode.py") codepath = os.path.join(tmpdir, "usercode.py")
@ -229,7 +231,7 @@ class ProcessManager(object):
f.write(repr(args)) f.write(repr(args))
# Run the code # Run the code
argv = [ sys.executable, "-B", "-s", "-u", codepath ] + args argv = [sys.executable, "-B", "-s", "-u", codepath] + args
pid = self.run_command(argv) pid = self.run_command(argv)
# Save the temp dir # Save the temp dir
@ -242,7 +244,7 @@ class ProcessManager(object):
if tmpdir is not None: if tmpdir is not None:
try: try:
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
except OSError: # pragma: no cover except OSError: # pragma: no cover
pass pass
def run_command(self, argv): def run_command(self, argv):
@ -260,15 +262,16 @@ class ProcessManager(object):
def get_info(self): def get_info(self):
"""Get info about all running PIDs""" """Get info about all running PIDs"""
info = { "total" : Process.get_empty_info(), info = {
"pids" : {}, "total": Process.get_empty_info(),
"system" : {} "pids": {},
} "system": {}
}
# Trigger CPU usage collection # Trigger CPU usage collection
for pid in self: for pid in self:
self[pid].get_info_prepare() self[pid].get_info_prepare()
psutil.cpu_percent(0, percpu = True) psutil.cpu_percent(0, percpu=True)
# Give it some time # Give it some time
time.sleep(1) time.sleep(1)

View File

@ -1,18 +1,12 @@
"""CherryPy-based server for running NILM filters via HTTP""" """CherryPy-based server for running NILM filters via HTTP"""
import cherrypy import cherrypy
import sys
import os import os
import socket import socket
import json
import traceback import traceback
import time
import nilmdb from nilmdb.utils.printf import sprintf
from nilmdb.utils.printf import *
from nilmdb.server.serverutil import ( from nilmdb.server.serverutil import (
chunked_response,
response_type,
exception_to_httperror, exception_to_httperror,
CORS_allow, CORS_allow,
json_to_request_params, json_to_request_params,
@ -23,10 +17,12 @@ from nilmdb.server.serverutil import (
) )
from nilmdb.utils import serializer_proxy from nilmdb.utils import serializer_proxy
import nilmrun import nilmrun
import nilmrun.processmanager
# Add CORS_allow tool # Add CORS_allow tool
cherrypy.tools.CORS_allow = cherrypy.Tool('on_start_resource', CORS_allow) cherrypy.tools.CORS_allow = cherrypy.Tool('on_start_resource', CORS_allow)
# CherryPy apps # CherryPy apps
class App(object): class App(object):
"""Root application for NILM runner""" """Root application for NILM runner"""
@ -53,6 +49,7 @@ class App(object):
def version(self): def version(self):
return nilmrun.__version__ return nilmrun.__version__
class AppProcess(object): class AppProcess(object):
def __init__(self, manager): def __init__(self, manager):
@ -74,7 +71,7 @@ class AppProcess(object):
# /process/status # /process/status
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
def status(self, pid, clear = False): def status(self, pid, clear=False):
"""Return status about a process. If clear = True, also clear """Return status about a process. If clear = True, also clear
the log.""" the log."""
clear = bool_param(clear) clear = bool_param(clear)
@ -104,18 +101,19 @@ class AppProcess(object):
@cherrypy.expose @cherrypy.expose
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@cherrypy.tools.CORS_allow(methods = ["POST"]) @cherrypy.tools.CORS_allow(methods=["POST"])
def remove(self, pid): def remove(self, pid):
"""Remove a process from the manager, killing it if necessary.""" """Remove a process from the manager, killing it if necessary."""
if pid not in self.manager: if pid not in self.manager:
raise cherrypy.HTTPError("404 Not Found", "No such PID") raise cherrypy.HTTPError("404 Not Found", "No such PID")
if not self.manager.terminate(pid): # pragma: no cover if not self.manager.terminate(pid): # pragma: no cover
raise cherrypy.HTTPError("503 Service Unavailable", raise cherrypy.HTTPError("503 Service Unavailable",
"Failed to stop process") "Failed to stop process")
status = self.process_status(pid) status = self.process_status(pid)
self.manager.remove(pid) self.manager.remove(pid)
return status return status
class AppRun(object): class AppRun(object):
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self.manager = manager
@ -125,7 +123,7 @@ class AppRun(object):
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@exception_to_httperror(nilmrun.processmanager.ProcessError) @exception_to_httperror(nilmrun.processmanager.ProcessError)
@cherrypy.tools.CORS_allow(methods = ["POST"]) @cherrypy.tools.CORS_allow(methods=["POST"])
def command(self, argv): def command(self, argv):
"""Execute an arbitrary program on the server. argv is a """Execute an arbitrary program on the server. argv is a
list of the program and its arguments: 'argv[0]' is the program list of the program and its arguments: 'argv[0]' is the program
@ -140,8 +138,8 @@ class AppRun(object):
@cherrypy.tools.json_in() @cherrypy.tools.json_in()
@cherrypy.tools.json_out() @cherrypy.tools.json_out()
@exception_to_httperror(nilmrun.processmanager.ProcessError) @exception_to_httperror(nilmrun.processmanager.ProcessError)
@cherrypy.tools.CORS_allow(methods = ["POST"]) @cherrypy.tools.CORS_allow(methods=["POST"])
def code(self, code, args = None): def code(self, code, args=None):
"""Execute arbitrary Python code. 'code' is a formatted string. """Execute arbitrary Python code. 'code' is a formatted string.
It will be run as if it were written into a Python file and It will be run as if it were written into a Python file and
executed. 'args' is a list of strings, and they are passed executed. 'args' is a list of strings, and they are passed
@ -154,10 +152,11 @@ class AppRun(object):
"args must be a list of strings") "args must be a list of strings")
return self.manager.run_code(code, args) return self.manager.run_code(code, args)
class Server(object): class Server(object):
def __init__(self, host = '127.0.0.1', port = 8080, def __init__(self, host='127.0.0.1', port=8080,
force_traceback = False, # include traceback in all errors force_traceback=False, # include traceback in all errors
basepath = '', # base URL path for cherrypy.tree basepath='', # base URL path for cherrypy.tree
): ):
# Build up global server configuration # Build up global server configuration
@ -176,23 +175,25 @@ class Server(object):
}) })
# Some default headers to just help identify that things are working # Some default headers to just help identify that things are working
app_config.update({ 'response.headers.X-Jim-Is-Awesome': 'yeah' }) app_config.update({'response.headers.X-Jim-Is-Awesome': 'yeah'})
# Set up Cross-Origin Resource Sharing (CORS) handler so we # Set up Cross-Origin Resource Sharing (CORS) handler so we
# can correctly respond to browsers' CORS preflight requests. # can correctly respond to browsers' CORS preflight requests.
# This also limits verbs to GET and HEAD by default. # This also limits verbs to GET and HEAD by default.
app_config.update({ 'tools.CORS_allow.on': True, app_config.update({
'tools.CORS_allow.methods': ['GET', 'HEAD'] }) 'tools.CORS_allow.on': True,
'tools.CORS_allow.methods': ['GET', 'HEAD']
})
# Configure the 'json_in' tool to also allow other content-types # Configure the 'json_in' tool to also allow other content-types
# (like x-www-form-urlencoded), and to treat JSON as a dict that # (like x-www-form-urlencoded), and to treat JSON as a dict that
# fills requests.param. # fills requests.param.
app_config.update({ 'tools.json_in.force': False, app_config.update({'tools.json_in.force': False,
'tools.json_in.processor': json_to_request_params }) 'tools.json_in.processor': json_to_request_params})
# Send tracebacks in error responses. They're hidden by the # Send tracebacks in error responses. They're hidden by the
# error_page function for client errors (code 400-499). # error_page function for client errors (code 400-499).
app_config.update({ 'request.show_tracebacks' : True }) app_config.update({'request.show_tracebacks': True})
self.force_traceback = force_traceback self.force_traceback = force_traceback
# Patch CherryPy error handler to never pad out error messages. # Patch CherryPy error handler to never pad out error messages.
@ -210,7 +211,7 @@ class Server(object):
root.process = AppProcess(manager) root.process = AppProcess(manager)
root.run = AppRun(manager) root.run = AppRun(manager)
cherrypy.tree.apps = {} cherrypy.tree.apps = {}
cherrypy.tree.mount(root, basepath, config = { "/" : app_config }) cherrypy.tree.mount(root, basepath, config={"/": app_config})
# Set up the WSGI application pointer for external programs # Set up the WSGI application pointer for external programs
self.wsgi_application = cherrypy.tree self.wsgi_application = cherrypy.tree
@ -220,17 +221,20 @@ class Server(object):
return json_error_page(status, message, traceback, version, return json_error_page(status, message, traceback, version,
self.force_traceback) self.force_traceback)
def start(self, blocking = False, event = None): def start(self, blocking=False, event=None):
cherrypy_start(blocking, event) cherrypy_start(blocking, event)
def stop(self): def stop(self):
cherrypy_stop() cherrypy_stop()
# Multiple processes and threads should be OK here, but we'll still # Multiple processes and threads should be OK here, but we'll still
# follow the NilmDB approach of having just one globally initialized # follow the NilmDB approach of having just one globally initialized
# copy of the server object. # copy of the server object.
_wsgi_server = None _wsgi_server = None
def wsgi_application(basepath): # pragma: no cover
def wsgi_application(basepath): # pragma: no cover
"""Return a WSGI application object. """Return a WSGI application object.
'basepath' is the URL path of the application base, which 'basepath' is the URL path of the application base, which
@ -243,12 +247,11 @@ def wsgi_application(basepath): # pragma: no cover
# Try to start the server # Try to start the server
try: try:
_wsgi_server = nilmrun.server.Server( _wsgi_server = nilmrun.server.Server(
basepath = basepath.rstrip('/')) basepath=basepath.rstrip('/'))
except Exception: except Exception:
# Build an error message on failure # Build an error message on failure
import pprint import pprint
err = sprintf("Initializing nilmrun failed:\n\n", err = "Initializing nilmrun failed:\n\n"
dbpath)
err += traceback.format_exc() err += traceback.format_exc()
try: try:
import pwd import pwd
@ -263,8 +266,10 @@ def wsgi_application(basepath): # pragma: no cover
err += sprintf("\nEnvironment:\n%s\n", pprint.pformat(environ)) err += sprintf("\nEnvironment:\n%s\n", pprint.pformat(environ))
if _wsgi_server is None: if _wsgi_server is None:
# Serve up the error with our own mini WSGI app. # Serve up the error with our own mini WSGI app.
headers = [ ('Content-type', 'text/plain'), headers = [
('Content-length', str(len(err))) ] ('Content-type', 'text/plain'),
('Content-length', str(len(err)))
]
start_response("500 Internal Server Error", headers) start_response("500 Internal Server Error", headers)
return [err] return [err]

View File

@ -1,2 +1,2 @@
nilmdb>=2.0.2 nilmdb>=2.0.3
psutil>=2.0.0 psutil==5.7.2

View File

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError
from nilmdb.utils.printf import * from nilmdb.utils.printf import *
@ -13,8 +13,9 @@ def main():
def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/") def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description = 'Kill/remove a process from the NilmRun server', description = 'Kill/remove a process from the NilmRun server',
formatter_class = argparse.ArgumentDefaultsHelpFormatter, formatter_class = argparse.ArgumentDefaultsHelpFormatter)
version = nilmrun.__version__) parser.add_argument("-v", "--version", action="version",
version=nilmrun.__version__)
group = parser.add_argument_group("Standard options") group = parser.add_argument_group("Standard options")
group.add_argument('-u', '--url', group.add_argument('-u', '--url',
help = 'NilmRun server URL', default = def_url) help = 'NilmRun server URL', default = def_url)

View File

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
import nilmrun.server import nilmrun.server
import argparse import argparse
@ -10,8 +10,9 @@ def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description = 'Run the NilmRun server', description = 'Run the NilmRun server',
formatter_class = argparse.ArgumentDefaultsHelpFormatter, formatter_class = argparse.ArgumentDefaultsHelpFormatter)
version = nilmrun.__version__) parser.add_argument("-v", "--version", action="version",
version=nilmrun.__version__)
group = parser.add_argument_group("Standard options") group = parser.add_argument_group("Standard options")
group.add_argument('-a', '--address', group.add_argument('-a', '--address',
@ -34,7 +35,6 @@ def main():
embedded = False embedded = False
server = nilmrun.server.Server(host = args.address, server = nilmrun.server.Server(host = args.address,
port = args.port, port = args.port,
embedded = embedded,
force_traceback = args.traceback) force_traceback = args.traceback)
# Print info # Print info

View File

@ -1,8 +1,8 @@
#!/usr/bin/python #!/usr/bin/env python3
from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError
from nilmdb.utils.printf import * from nilmdb.utils.printf import *
from nilmdb.utils import datetime_tz import datetime_tz
import nilmrun import nilmrun
import argparse import argparse
@ -13,8 +13,9 @@ def main():
def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/") def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description = 'List NilmRun processes', description = 'List NilmRun processes',
formatter_class = argparse.ArgumentDefaultsHelpFormatter, formatter_class = argparse.ArgumentDefaultsHelpFormatter)
version = nilmrun.__version__) parser.add_argument("-v", "--version", action="version",
version=nilmrun.__version__)
group = parser.add_argument_group("Standard options") group = parser.add_argument_group("Standard options")
group.add_argument('-u', '--url', group.add_argument('-u', '--url',
help = 'NilmRun server URL', default = def_url) help = 'NilmRun server URL', default = def_url)

View File

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError from nilmdb.client.httpclient import HTTPClient, ClientError, ServerError
from nilmdb.utils.printf import * from nilmdb.utils.printf import *
@ -14,8 +14,9 @@ def main():
def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/") def_url = os.environ.get("NILMRUN_URL", "http://localhost/nilmrun/")
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description = 'Run a command on the NilmRun server', description = 'Run a command on the NilmRun server',
formatter_class = argparse.ArgumentDefaultsHelpFormatter, formatter_class = argparse.ArgumentDefaultsHelpFormatter)
version = nilmrun.__version__) parser.add_argument("-v", "--version", action="version",
version=nilmrun.__version__)
group = parser.add_argument_group("Standard options") group = parser.add_argument_group("Standard options")
group.add_argument('-u', '--url', group.add_argument('-u', '--url',
help = 'NilmRun server URL', default = def_url) help = 'NilmRun server URL', default = def_url)
@ -30,7 +31,8 @@ def main():
help="Arguments for command") help="Arguments for command")
args = parser.parse_args() args = parser.parse_args()
client = HTTPClient(baseurl = args.url, verify_ssl = not args.noverify) client = HTTPClient(baseurl=args.url, verify_ssl=not args.noverify,
post_json=True)
# Run command # Run command
pid = client.post("run/command", { "argv": [ args.cmd ] + args.arg }) pid = client.post("run/command", { "argv": [ args.cmd ] + args.arg })

View File

@ -28,3 +28,11 @@ versionfile_source=nilmrun/_version.py
versionfile_build=nilmrun/_version.py versionfile_build=nilmrun/_version.py
tag_prefix=nilmrun- tag_prefix=nilmrun-
parentdir_prefix=nilmrun- parentdir_prefix=nilmrun-
[flake8]
exclude=_version.py
extend-ignore=E731
[pylint]
ignore=_version.py
disable=C0103,C0111,R0913,R0914

View File

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# To release a new version, tag it: # To release a new version, tag it:
# git tag -a nilmrun-1.1 -m "Version 1.1" # git tag -a nilmrun-1.1 -m "Version 1.1"

View File

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
import nose import nose
import os import os

View File

@ -276,11 +276,11 @@ https://creativecommons.org/publicdomain/zero/1.0/ .
""" """
from __future__ import print_function
try: try:
import configparser import configparser
except ImportError: except ImportError:
import configparser as configparser import ConfigParser as configparser
import errno import errno
import json import json
import os import os