re-format with black
This commit is contained in:
+2
-2
@@ -37,11 +37,11 @@
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, unicode_literals)
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from .daemon import DaemonContext
|
||||
|
||||
|
||||
|
||||
# Local variables:
|
||||
# coding: utf-8
|
||||
# mode: python
|
||||
|
||||
+22
-19
@@ -12,7 +12,7 @@
|
||||
|
||||
""" Package metadata for the ‘python-daemon’ distribution. """
|
||||
|
||||
from __future__ import (absolute_import, unicode_literals)
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
import re
|
||||
@@ -21,10 +21,11 @@ import datetime
|
||||
|
||||
import pkg_resources
|
||||
|
||||
|
||||
|
||||
distribution_name = "python-daemon"
|
||||
version_info_filename = "version_info.json"
|
||||
|
||||
|
||||
def get_distribution_version_info(filename=version_info_filename):
|
||||
""" Get the version info from the installed distribution.
|
||||
|
||||
@@ -37,10 +38,10 @@ def get_distribution_version_info(filename=version_info_filename):
|
||||
|
||||
"""
|
||||
version_info = {
|
||||
'release_date': "UNKNOWN",
|
||||
'version': "UNKNOWN",
|
||||
'maintainer': "UNKNOWN",
|
||||
}
|
||||
"release_date": "UNKNOWN",
|
||||
"version": "UNKNOWN",
|
||||
"maintainer": "UNKNOWN",
|
||||
}
|
||||
|
||||
try:
|
||||
distribution = pkg_resources.get_distribution(distribution_name)
|
||||
@@ -54,15 +55,16 @@ def get_distribution_version_info(filename=version_info_filename):
|
||||
|
||||
return version_info
|
||||
|
||||
|
||||
version_info = get_distribution_version_info()
|
||||
|
||||
version_installed = version_info['version']
|
||||
version_installed = version_info["version"]
|
||||
|
||||
|
||||
rfc822_person_regex = re.compile(
|
||||
"^(?P<name>[^<]+) <(?P<email>[^>]+)>$")
|
||||
|
||||
ParsedPerson = collections.namedtuple('ParsedPerson', ['name', 'email'])
|
||||
rfc822_person_regex = re.compile("^(?P<name>[^<]+) <(?P<email>[^>]+)>$")
|
||||
|
||||
ParsedPerson = collections.namedtuple("ParsedPerson", ["name", "email"])
|
||||
|
||||
|
||||
def parse_person_field(value):
|
||||
""" Parse a person field into name and email address.
|
||||
@@ -79,19 +81,18 @@ def parse_person_field(value):
|
||||
match = rfc822_person_regex.match(value)
|
||||
if len(value):
|
||||
if match is not None:
|
||||
result = ParsedPerson(
|
||||
name=match.group('name'),
|
||||
email=match.group('email'))
|
||||
result = ParsedPerson(name=match.group("name"), email=match.group("email"))
|
||||
else:
|
||||
result = ParsedPerson(name=value, email=None)
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
|
||||
author_name = "Ben Finney"
|
||||
author_email = "ben+python@benfinney.id.au"
|
||||
author = "{name} <{email}>".format(name=author_name, email=author_email)
|
||||
|
||||
|
||||
|
||||
class YearRange:
|
||||
""" A range of years spanning a period. """
|
||||
|
||||
@@ -135,16 +136,18 @@ def make_year_range(begin_year, end_date=None):
|
||||
|
||||
return year_range
|
||||
|
||||
|
||||
copyright_year_begin = "2001"
|
||||
build_date = version_info['release_date']
|
||||
build_date = version_info["release_date"]
|
||||
copyright_year_range = make_year_range(copyright_year_begin, build_date)
|
||||
|
||||
copyright = "Copyright © {year_range} {author} and others".format(
|
||||
year_range=copyright_year_range, author=author)
|
||||
year_range=copyright_year_range, author=author
|
||||
)
|
||||
license = "Apache-2"
|
||||
url = "https://alioth.debian.org/projects/python-daemon/"
|
||||
|
||||
|
||||
|
||||
# Local variables:
|
||||
# coding: utf-8
|
||||
# mode: python
|
||||
|
||||
+68
-54
@@ -18,7 +18,7 @@
|
||||
""" Daemon process behaviour.
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, unicode_literals)
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -27,6 +27,7 @@ import errno
|
||||
import signal
|
||||
import socket
|
||||
import atexit
|
||||
|
||||
try:
|
||||
# Python 2 has both ‘str’ (bytes) and ‘unicode’ (text).
|
||||
basestring = basestring
|
||||
@@ -36,7 +37,7 @@ except NameError:
|
||||
basestring = str
|
||||
unicode = str
|
||||
|
||||
|
||||
|
||||
class DaemonError(Exception):
|
||||
""" Base exception class for errors from this module. """
|
||||
|
||||
@@ -56,7 +57,7 @@ class DaemonOSEnvironmentError(DaemonError, OSError):
|
||||
class DaemonProcessDetachError(DaemonError, OSError):
|
||||
""" Exception raised when process detach fails. """
|
||||
|
||||
|
||||
|
||||
class DaemonContext:
|
||||
""" Context for turning the current program into a daemon process.
|
||||
|
||||
@@ -231,21 +232,21 @@ class DaemonContext:
|
||||
__metaclass__ = type
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
chroot_directory=None,
|
||||
working_directory="/",
|
||||
umask=0,
|
||||
uid=None,
|
||||
gid=None,
|
||||
prevent_core=True,
|
||||
detach_process=None,
|
||||
files_preserve=None,
|
||||
pidfile=None,
|
||||
stdin=None,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
signal_map=None,
|
||||
):
|
||||
self,
|
||||
chroot_directory=None,
|
||||
working_directory="/",
|
||||
umask=0,
|
||||
uid=None,
|
||||
gid=None,
|
||||
prevent_core=True,
|
||||
detach_process=None,
|
||||
files_preserve=None,
|
||||
pidfile=None,
|
||||
stdin=None,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
signal_map=None,
|
||||
):
|
||||
""" Set up a new instance. """
|
||||
self.chroot_directory = chroot_directory
|
||||
self.working_directory = working_directory
|
||||
@@ -427,8 +428,10 @@ class DaemonContext:
|
||||
|
||||
"""
|
||||
exception = SystemExit(
|
||||
"Terminating on signal {signal_number!r}".format(
|
||||
signal_number=signal_number))
|
||||
"Terminating on signal {signal_number!r}".format(
|
||||
signal_number=signal_number
|
||||
)
|
||||
)
|
||||
raise exception
|
||||
|
||||
def _get_exclude_file_descriptors(self):
|
||||
@@ -454,8 +457,10 @@ class DaemonContext:
|
||||
if files_preserve is None:
|
||||
files_preserve = []
|
||||
files_preserve.extend(
|
||||
item for item in [self.stdin, self.stdout, self.stderr]
|
||||
if hasattr(item, 'fileno'))
|
||||
item
|
||||
for item in [self.stdin, self.stdout, self.stderr]
|
||||
if hasattr(item, "fileno")
|
||||
)
|
||||
|
||||
exclude_descriptors = set()
|
||||
for item in files_preserve:
|
||||
@@ -502,8 +507,9 @@ class DaemonContext:
|
||||
|
||||
"""
|
||||
signal_handler_map = dict(
|
||||
(signal_number, self._make_signal_handler(target))
|
||||
for (signal_number, target) in self.signal_map.items())
|
||||
(signal_number, self._make_signal_handler(target))
|
||||
for (signal_number, target) in self.signal_map.items()
|
||||
)
|
||||
return signal_handler_map
|
||||
|
||||
|
||||
@@ -520,7 +526,7 @@ def _get_file_descriptor(obj):
|
||||
|
||||
"""
|
||||
file_descriptor = None
|
||||
if hasattr(obj, 'fileno'):
|
||||
if hasattr(obj, "fileno"):
|
||||
try:
|
||||
file_descriptor = obj.fileno()
|
||||
except ValueError:
|
||||
@@ -529,7 +535,7 @@ def _get_file_descriptor(obj):
|
||||
|
||||
return file_descriptor
|
||||
|
||||
|
||||
|
||||
def change_working_directory(directory):
|
||||
""" Change the working directory of this process.
|
||||
|
||||
@@ -541,7 +547,8 @@ def change_working_directory(directory):
|
||||
os.chdir(directory)
|
||||
except Exception as exc:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"Unable to change working directory ({exc})".format(exc=exc))
|
||||
"Unable to change working directory ({exc})".format(exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
|
||||
@@ -561,7 +568,8 @@ def change_root_directory(directory):
|
||||
os.chroot(directory)
|
||||
except Exception as exc:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"Unable to change root directory ({exc})".format(exc=exc))
|
||||
"Unable to change root directory ({exc})".format(exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
|
||||
@@ -576,7 +584,8 @@ def change_file_creation_mask(mask):
|
||||
os.umask(mask)
|
||||
except Exception as exc:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"Unable to change file creation mask ({exc})".format(exc=exc))
|
||||
"Unable to change file creation mask ({exc})".format(exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
|
||||
@@ -597,10 +606,11 @@ def change_process_owner(uid, gid):
|
||||
os.setuid(uid)
|
||||
except Exception as exc:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"Unable to change process owner ({exc})".format(exc=exc))
|
||||
"Unable to change process owner ({exc})".format(exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
|
||||
|
||||
def prevent_core_dump():
|
||||
""" Prevent this process from generating a core dump.
|
||||
|
||||
@@ -618,15 +628,16 @@ def prevent_core_dump():
|
||||
core_limit_prev = resource.getrlimit(core_resource)
|
||||
except ValueError as exc:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"System does not support RLIMIT_CORE resource limit"
|
||||
" ({exc})".format(exc=exc))
|
||||
"System does not support RLIMIT_CORE resource limit"
|
||||
" ({exc})".format(exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
# Set hard and soft limits to zero, i.e. no core dump at all.
|
||||
core_limit = (0, 0)
|
||||
resource.setrlimit(core_resource, core_limit)
|
||||
|
||||
|
||||
|
||||
def detach_process_context():
|
||||
""" Detach the process context from parent and session.
|
||||
|
||||
@@ -656,15 +667,17 @@ def detach_process_context():
|
||||
os._exit(0)
|
||||
except OSError as exc:
|
||||
error = DaemonProcessDetachError(
|
||||
"{message}: [{exc.errno:d}] {exc.strerror}".format(
|
||||
message=error_message, exc=exc))
|
||||
"{message}: [{exc.errno:d}] {exc.strerror}".format(
|
||||
message=error_message, exc=exc
|
||||
)
|
||||
)
|
||||
raise error
|
||||
|
||||
fork_then_exit_parent(error_message="Failed first fork")
|
||||
os.setsid()
|
||||
fork_then_exit_parent(error_message="Failed second fork")
|
||||
|
||||
|
||||
|
||||
def is_process_started_by_init():
|
||||
""" Determine whether the current process is started by `init`.
|
||||
|
||||
@@ -699,8 +712,7 @@ def is_socket(fd):
|
||||
file_socket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW)
|
||||
|
||||
try:
|
||||
socket_type = file_socket.getsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_TYPE)
|
||||
socket_type = file_socket.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)
|
||||
except socket.error as exc:
|
||||
exc_errno = exc.args[0]
|
||||
if exc_errno == errno.ENOTSOCK:
|
||||
@@ -759,7 +771,7 @@ def is_detach_process_context_required():
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
def close_file_descriptor_if_open(fd):
|
||||
""" Close a file descriptor if already open.
|
||||
|
||||
@@ -778,13 +790,14 @@ def close_file_descriptor_if_open(fd):
|
||||
pass
|
||||
else:
|
||||
error = DaemonOSEnvironmentError(
|
||||
"Failed to close file descriptor {fd:d} ({exc})".format(
|
||||
fd=fd, exc=exc))
|
||||
"Failed to close file descriptor {fd:d} ({exc})".format(fd=fd, exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
|
||||
MAXFD = 2048
|
||||
|
||||
|
||||
def get_maximum_file_descriptors():
|
||||
""" Get the maximum number of open file descriptors for this process.
|
||||
|
||||
@@ -820,7 +833,7 @@ def close_all_open_files(exclude=set()):
|
||||
if fd not in exclude:
|
||||
close_file_descriptor_if_open(fd)
|
||||
|
||||
|
||||
|
||||
def redirect_stream(system_stream, target_stream):
|
||||
""" Redirect a system stream to a specified file.
|
||||
|
||||
@@ -844,7 +857,7 @@ def redirect_stream(system_stream, target_stream):
|
||||
target_fd = target_stream.fileno()
|
||||
os.dup2(target_fd, system_stream.fileno())
|
||||
|
||||
|
||||
|
||||
def make_default_signal_map():
|
||||
""" Make the default signal map for this system.
|
||||
|
||||
@@ -855,15 +868,16 @@ def make_default_signal_map():
|
||||
|
||||
"""
|
||||
name_map = {
|
||||
'SIGTSTP': None,
|
||||
'SIGTTIN': None,
|
||||
'SIGTTOU': None,
|
||||
'SIGTERM': 'terminate',
|
||||
}
|
||||
"SIGTSTP": None,
|
||||
"SIGTTIN": None,
|
||||
"SIGTTOU": None,
|
||||
"SIGTERM": "terminate",
|
||||
}
|
||||
signal_map = dict(
|
||||
(getattr(signal, name), target)
|
||||
for (name, target) in name_map.items()
|
||||
if hasattr(signal, name))
|
||||
(getattr(signal, name), target)
|
||||
for (name, target) in name_map.items()
|
||||
if hasattr(signal, name)
|
||||
)
|
||||
|
||||
return signal_map
|
||||
|
||||
@@ -895,7 +909,7 @@ def register_atexit_function(func):
|
||||
"""
|
||||
atexit.register(func)
|
||||
|
||||
|
||||
|
||||
def _chain_exception_from_existing_exception_context(exc, as_cause=False):
|
||||
""" Decorate the specified exception with the existing exception context.
|
||||
|
||||
@@ -918,7 +932,7 @@ def _chain_exception_from_existing_exception_context(exc, as_cause=False):
|
||||
exc.__context__ = existing_exc
|
||||
exc.__traceback__ = existing_traceback
|
||||
|
||||
|
||||
|
||||
# Local variables:
|
||||
# coding: utf-8
|
||||
# mode: python
|
||||
|
||||
+3
-3
@@ -13,11 +13,11 @@
|
||||
""" Lockfile behaviour implemented via Unix PID files.
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, unicode_literals)
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from lockfile.pidlockfile import PIDLockFile
|
||||
|
||||
|
||||
|
||||
class TimeoutPIDLockFile(PIDLockFile, object):
|
||||
""" Lockfile with default timeout, implemented as a Unix PID file.
|
||||
|
||||
@@ -59,7 +59,7 @@ class TimeoutPIDLockFile(PIDLockFile, object):
|
||||
timeout = self.acquire_timeout
|
||||
super(TimeoutPIDLockFile, self).acquire(timeout, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
# Local variables:
|
||||
# coding: utf-8
|
||||
# mode: python
|
||||
|
||||
+26
-28
@@ -17,12 +17,13 @@
|
||||
""" Daemon runner library.
|
||||
"""
|
||||
|
||||
from __future__ import (absolute_import, unicode_literals)
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import sys
|
||||
import os
|
||||
import signal
|
||||
import errno
|
||||
|
||||
try:
|
||||
# Python 3 standard library.
|
||||
ProcessLookupError
|
||||
@@ -33,11 +34,11 @@ except NameError:
|
||||
import lockfile
|
||||
|
||||
from . import pidfile
|
||||
from .daemon import (basestring, unicode)
|
||||
from .daemon import basestring, unicode
|
||||
from .daemon import DaemonContext
|
||||
from .daemon import _chain_exception_from_existing_exception_context
|
||||
|
||||
|
||||
|
||||
class DaemonRunnerError(Exception):
|
||||
""" Abstract base class for errors from DaemonRunner. """
|
||||
|
||||
@@ -65,7 +66,7 @@ class DaemonRunnerStartFailureError(DaemonRunnerError, RuntimeError):
|
||||
class DaemonRunnerStopFailureError(DaemonRunnerError, RuntimeError):
|
||||
""" Raised when failure stopping DaemonRunner. """
|
||||
|
||||
|
||||
|
||||
class DaemonRunner:
|
||||
""" Controller for a callable running in a separate background process.
|
||||
|
||||
@@ -107,15 +108,13 @@ class DaemonRunner:
|
||||
self.parse_args()
|
||||
self.app = app
|
||||
self.daemon_context = DaemonContext()
|
||||
self.daemon_context.stdin = open(app.stdin_path, 'rt')
|
||||
self.daemon_context.stdout = open(app.stdout_path, 'w+t')
|
||||
self.daemon_context.stderr = open(
|
||||
app.stderr_path, 'w+t', buffering=0)
|
||||
self.daemon_context.stdin = open(app.stdin_path, "rt")
|
||||
self.daemon_context.stdout = open(app.stdout_path, "w+t")
|
||||
self.daemon_context.stderr = open(app.stderr_path, "w+t", buffering=0)
|
||||
|
||||
self.pidfile = None
|
||||
if app.pidfile_path is not None:
|
||||
self.pidfile = make_pidlockfile(
|
||||
app.pidfile_path, app.pidfile_timeout)
|
||||
self.pidfile = make_pidlockfile(app.pidfile_path, app.pidfile_timeout)
|
||||
self.daemon_context.pidfile = self.pidfile
|
||||
|
||||
def _usage_exit(self, argv):
|
||||
@@ -130,7 +129,8 @@ class DaemonRunner:
|
||||
usage_exit_code = 2
|
||||
action_usage = "|".join(self.action_funcs.keys())
|
||||
message = "usage: {progname} {usage}".format(
|
||||
progname=progname, usage=action_usage)
|
||||
progname=progname, usage=action_usage
|
||||
)
|
||||
emit_message(message)
|
||||
sys.exit(usage_exit_code)
|
||||
|
||||
@@ -175,8 +175,8 @@ class DaemonRunner:
|
||||
self.daemon_context.open()
|
||||
except lockfile.AlreadyLocked:
|
||||
error = DaemonRunnerStartFailureError(
|
||||
"PID file {pidfile.path!r} already locked".format(
|
||||
pidfile=self.pidfile))
|
||||
"PID file {pidfile.path!r} already locked".format(pidfile=self.pidfile)
|
||||
)
|
||||
raise error
|
||||
|
||||
pid = os.getpid()
|
||||
@@ -198,8 +198,8 @@ class DaemonRunner:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except OSError as exc:
|
||||
error = DaemonRunnerStopFailureError(
|
||||
"Failed to terminate {pid:d}: {exc}".format(
|
||||
pid=pid, exc=exc))
|
||||
"Failed to terminate {pid:d}: {exc}".format(pid=pid, exc=exc)
|
||||
)
|
||||
raise error
|
||||
|
||||
def _stop(self):
|
||||
@@ -212,8 +212,8 @@ class DaemonRunner:
|
||||
"""
|
||||
if not self.pidfile.is_locked():
|
||||
error = DaemonRunnerStopFailureError(
|
||||
"PID file {pidfile.path!r} not locked".format(
|
||||
pidfile=self.pidfile))
|
||||
"PID file {pidfile.path!r} not locked".format(pidfile=self.pidfile)
|
||||
)
|
||||
raise error
|
||||
|
||||
if is_pidfile_stale(self.pidfile):
|
||||
@@ -228,10 +228,10 @@ class DaemonRunner:
|
||||
self._start()
|
||||
|
||||
action_funcs = {
|
||||
'start': _start,
|
||||
'stop': _stop,
|
||||
'restart': _restart,
|
||||
}
|
||||
"start": _start,
|
||||
"stop": _stop,
|
||||
"restart": _restart,
|
||||
}
|
||||
|
||||
def _get_action_func(self):
|
||||
""" Get the function for the specified action.
|
||||
@@ -249,8 +249,8 @@ class DaemonRunner:
|
||||
func = self.action_funcs[self.action]
|
||||
except KeyError:
|
||||
error = DaemonRunnerInvalidActionError(
|
||||
"Unknown action: {action!r}".format(
|
||||
action=self.action))
|
||||
"Unknown action: {action!r}".format(action=self.action)
|
||||
)
|
||||
raise error
|
||||
return func
|
||||
|
||||
@@ -278,12 +278,10 @@ def emit_message(message, stream=None):
|
||||
def make_pidlockfile(path, acquire_timeout):
|
||||
""" Make a PIDLockFile instance with the given filesystem path. """
|
||||
if not isinstance(path, basestring):
|
||||
error = ValueError("Not a filesystem path: {path!r}".format(
|
||||
path=path))
|
||||
error = ValueError("Not a filesystem path: {path!r}".format(path=path))
|
||||
raise error
|
||||
if not os.path.isabs(path):
|
||||
error = ValueError("Not an absolute path: {path!r}".format(
|
||||
path=path))
|
||||
error = ValueError("Not an absolute path: {path!r}".format(path=path))
|
||||
raise error
|
||||
lockfile = pidfile.TimeoutPIDLockFile(path, acquire_timeout)
|
||||
|
||||
@@ -316,7 +314,7 @@ def is_pidfile_stale(pidfile):
|
||||
|
||||
return result
|
||||
|
||||
|
||||
|
||||
# Local variables:
|
||||
# coding: utf-8
|
||||
# mode: python
|
||||
|
||||
Reference in New Issue
Block a user