aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/application/runner
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/application/runner
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/application/runner')
-rw-r--r--contrib/python/Twisted/py2/twisted/application/runner/__init__.py7
-rw-r--r--contrib/python/Twisted/py2/twisted/application/runner/_exit.py138
-rw-r--r--contrib/python/Twisted/py2/twisted/application/runner/_pidfile.py303
-rw-r--r--contrib/python/Twisted/py2/twisted/application/runner/_runner.py185
4 files changed, 633 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/application/runner/__init__.py b/contrib/python/Twisted/py2/twisted/application/runner/__init__.py
new file mode 100644
index 0000000000..6da0ac04e7
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/application/runner/__init__.py
@@ -0,0 +1,7 @@
+# -*- test-case-name: twisted.application.runner.test -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Facilities for running a Twisted application.
+"""
diff --git a/contrib/python/Twisted/py2/twisted/application/runner/_exit.py b/contrib/python/Twisted/py2/twisted/application/runner/_exit.py
new file mode 100644
index 0000000000..ffccc417bd
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/application/runner/_exit.py
@@ -0,0 +1,138 @@
+# -*- test-case-name: twisted.application.runner.test.test_exit -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+System exit support.
+"""
+
+from sys import stdout, stderr, exit as sysexit
+
+from constantly import Values, ValueConstant
+
+
+
+def exit(status, message=None):
+ """
+ Exit the python interpreter with the given status and an optional message.
+
+ @param status: An exit status.
+ @type status: L{int} or L{ValueConstant} from L{ExitStatus}.
+
+ @param message: An options message to print.
+ @type status: L{str}
+ """
+ if isinstance(status, ValueConstant):
+ code = status.value
+ else:
+ code = int(status)
+
+ if message:
+ if code == 0:
+ out = stdout
+ else:
+ out = stderr
+ out.write(message)
+ out.write("\n")
+
+ sysexit(code)
+
+
+
+try:
+ import posix as Status
+except ImportError:
+ class Status(object):
+ """
+ Object to hang C{EX_*} values off of as a substitute for L{posix}.
+ """
+ EX__BASE = 64
+
+ EX_OK = 0
+ EX_USAGE = EX__BASE
+ EX_DATAERR = EX__BASE + 1
+ EX_NOINPUT = EX__BASE + 2
+ EX_NOUSER = EX__BASE + 3
+ EX_NOHOST = EX__BASE + 4
+ EX_UNAVAILABLE = EX__BASE + 5
+ EX_SOFTWARE = EX__BASE + 6
+ EX_OSERR = EX__BASE + 7
+ EX_OSFILE = EX__BASE + 8
+ EX_CANTCREAT = EX__BASE + 9
+ EX_IOERR = EX__BASE + 10
+ EX_TEMPFAIL = EX__BASE + 11
+ EX_PROTOCOL = EX__BASE + 12
+ EX_NOPERM = EX__BASE + 13
+ EX_CONFIG = EX__BASE + 14
+
+
+
+class ExitStatus(Values):
+ """
+ Standard exit status codes for system programs.
+
+ @cvar EX_OK: Successful termination.
+ @type EX_OK: L{ValueConstant}
+
+ @cvar EX_USAGE: Command line usage error.
+ @type EX_USAGE: L{ValueConstant}
+
+ @cvar EX_DATAERR: Data format error.
+ @type EX_DATAERR: L{ValueConstant}
+
+ @cvar EX_NOINPUT: Cannot open input.
+ @type EX_NOINPUT: L{ValueConstant}
+
+ @cvar EX_NOUSER: Addressee unknown.
+ @type EX_NOUSER: L{ValueConstant}
+
+ @cvar EX_NOHOST: Host name unknown.
+ @type EX_NOHOST: L{ValueConstant}
+
+ @cvar EX_UNAVAILABLE: Service unavailable.
+ @type EX_UNAVAILABLE: L{ValueConstant}
+
+ @cvar EX_SOFTWARE: Internal software error.
+ @type EX_SOFTWARE: L{ValueConstant}
+
+ @cvar EX_OSERR: System error (e.g., can't fork).
+ @type EX_OSERR: L{ValueConstant}
+
+ @cvar EX_OSFILE: Critical OS file missing.
+ @type EX_OSFILE: L{ValueConstant}
+
+ @cvar EX_CANTCREAT: Can't create (user) output file.
+ @type EX_CANTCREAT: L{ValueConstant}
+
+ @cvar EX_IOERR: Input/output error.
+ @type EX_IOERR: L{ValueConstant}
+
+ @cvar EX_TEMPFAIL: Temporary failure; the user is invited to retry.
+ @type EX_TEMPFAIL: L{ValueConstant}
+
+ @cvar EX_PROTOCOL: Remote error in protocol.
+ @type EX_PROTOCOL: L{ValueConstant}
+
+ @cvar EX_NOPERM: Permission denied.
+ @type EX_NOPERM: L{ValueConstant}
+
+ @cvar EX_CONFIG: Configuration error.
+ @type EX_CONFIG: L{ValueConstant}
+ """
+
+ EX_OK = ValueConstant(Status.EX_OK)
+ EX_USAGE = ValueConstant(Status.EX_USAGE)
+ EX_DATAERR = ValueConstant(Status.EX_DATAERR)
+ EX_NOINPUT = ValueConstant(Status.EX_NOINPUT)
+ EX_NOUSER = ValueConstant(Status.EX_NOUSER)
+ EX_NOHOST = ValueConstant(Status.EX_NOHOST)
+ EX_UNAVAILABLE = ValueConstant(Status.EX_UNAVAILABLE)
+ EX_SOFTWARE = ValueConstant(Status.EX_SOFTWARE)
+ EX_OSERR = ValueConstant(Status.EX_OSERR)
+ EX_OSFILE = ValueConstant(Status.EX_OSFILE)
+ EX_CANTCREAT = ValueConstant(Status.EX_CANTCREAT)
+ EX_IOERR = ValueConstant(Status.EX_IOERR)
+ EX_TEMPFAIL = ValueConstant(Status.EX_TEMPFAIL)
+ EX_PROTOCOL = ValueConstant(Status.EX_PROTOCOL)
+ EX_NOPERM = ValueConstant(Status.EX_NOPERM)
+ EX_CONFIG = ValueConstant(Status.EX_CONFIG)
diff --git a/contrib/python/Twisted/py2/twisted/application/runner/_pidfile.py b/contrib/python/Twisted/py2/twisted/application/runner/_pidfile.py
new file mode 100644
index 0000000000..50b8aed68d
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/application/runner/_pidfile.py
@@ -0,0 +1,303 @@
+# -*- test-case-name: twisted.application.runner.test.test_pidfile -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+PID file.
+"""
+
+import errno
+from os import getpid, kill, name as SYSTEM_NAME
+
+from zope.interface import Interface, implementer
+
+from twisted.logger import Logger
+
+
+
+class IPIDFile(Interface):
+ """
+ Manages a file that remembers a process ID.
+ """
+
+ def read():
+ """
+ Read the process ID stored in this PID file.
+
+ @return: The contained process ID.
+ @rtype: L{int}
+
+ @raise NoPIDFound: If this PID file does not exist.
+ @raise EnvironmentError: If this PID file cannot be read.
+ @raise ValueError: If this PID file's content is invalid.
+ """
+
+
+ def writeRunningPID():
+ """
+ Store the PID of the current process in this PID file.
+
+ @raise EnvironmentError: If this PID file cannot be written.
+ """
+
+
+ def remove():
+ """
+ Remove this PID file.
+
+ @raise EnvironmentError: If this PID file cannot be removed.
+ """
+
+
+ def isRunning():
+ """
+ Determine whether there is a running process corresponding to the PID
+ in this PID file.
+
+ @return: True if this PID file contains a PID and a process with that
+ PID is currently running; false otherwise.
+ @rtype: L{bool}
+
+ @raise EnvironmentError: If this PID file cannot be read.
+ @raise InvalidPIDFileError: If this PID file's content is invalid.
+ @raise StalePIDFileError: If this PID file's content refers to a PID
+ for which there is no corresponding running process.
+ """
+
+
+ def __enter__():
+ """
+ Enter a context using this PIDFile.
+
+ Writes the PID file with the PID of the running process.
+
+ @raise AlreadyRunningError: A process corresponding to the PID in this
+ PID file is already running.
+ """
+
+
+ def __exit__(excType, excValue, traceback):
+ """
+ Exit a context using this PIDFile.
+
+ Removes the PID file.
+ """
+
+
+
+@implementer(IPIDFile)
+class PIDFile(object):
+ """
+ Concrete implementation of L{IPIDFile} based on C{IFilePath}.
+
+ This implementation is presently not supported on non-POSIX platforms.
+ Specifically, calling L{PIDFile.isRunning} will raise
+ L{NotImplementedError}.
+ """
+
+ _log = Logger()
+
+
+ @staticmethod
+ def _format(pid):
+ """
+ Format a PID file's content.
+
+ @param pid: A process ID.
+ @type pid: int
+
+ @return: Formatted PID file contents.
+ @rtype: L{bytes}
+ """
+ return u"{}\n".format(int(pid)).encode("utf-8")
+
+
+ def __init__(self, filePath):
+ """
+ @param filePath: The path to the PID file on disk.
+ @type filePath: L{IFilePath}
+ """
+ self.filePath = filePath
+
+
+ def read(self):
+ pidString = b""
+ try:
+ with self.filePath.open() as fh:
+ for pidString in fh:
+ break
+ except OSError as e:
+ if e.errno == errno.ENOENT: # No such file
+ raise NoPIDFound("PID file does not exist")
+ raise
+
+ try:
+ return int(pidString)
+ except ValueError:
+ raise InvalidPIDFileError(
+ "non-integer PID value in PID file: {!r}".format(pidString)
+ )
+
+
+ def _write(self, pid):
+ """
+ Store a PID in this PID file.
+
+ @param pid: A PID to store.
+ @type pid: L{int}
+
+ @raise EnvironmentError: If this PID file cannot be written.
+ """
+ self.filePath.setContent(self._format(pid=pid))
+
+
+ def writeRunningPID(self):
+ self._write(getpid())
+
+
+ def remove(self):
+ self.filePath.remove()
+
+
+ def isRunning(self):
+ try:
+ pid = self.read()
+ except NoPIDFound:
+ return False
+
+ if SYSTEM_NAME == "posix":
+ return self._pidIsRunningPOSIX(pid)
+ else:
+ raise NotImplementedError(
+ "isRunning is not implemented on {}".format(SYSTEM_NAME)
+ )
+
+
+ @staticmethod
+ def _pidIsRunningPOSIX(pid):
+ """
+ POSIX implementation for running process check.
+
+ Determine whether there is a running process corresponding to the given
+ PID.
+
+ @return: True if the given PID is currently running; false otherwise.
+ @rtype: L{bool}
+
+ @raise EnvironmentError: If this PID file cannot be read.
+ @raise InvalidPIDFileError: If this PID file's content is invalid.
+ @raise StalePIDFileError: If this PID file's content refers to a PID
+ for which there is no corresponding running process.
+ """
+ try:
+ kill(pid, 0)
+ except OSError as e:
+ if e.errno == errno.ESRCH: # No such process
+ raise StalePIDFileError(
+ "PID file refers to non-existing process"
+ )
+ elif e.errno == errno.EPERM: # Not permitted to kill
+ return True
+ else:
+ raise
+ else:
+ return True
+
+
+ def __enter__(self):
+ try:
+ if self.isRunning():
+ raise AlreadyRunningError()
+ except StalePIDFileError:
+ self._log.info("Replacing stale PID file: {log_source}")
+ self.writeRunningPID()
+ return self
+
+
+ def __exit__(self, excType, excValue, traceback):
+ self.remove()
+
+
+
+@implementer(IPIDFile)
+class NonePIDFile(object):
+ """
+ PID file implementation that does nothing.
+
+ This is meant to be used as a "active None" object in place of a PID file
+ when no PID file is desired.
+ """
+
+ def __init__(self):
+ pass
+
+
+ def read(self):
+ raise NoPIDFound("PID file does not exist")
+
+
+ def _write(self, pid):
+ """
+ Store a PID in this PID file.
+
+ @param pid: A PID to store.
+ @type pid: L{int}
+
+ @raise EnvironmentError: If this PID file cannot be written.
+
+ @note: This implementation always raises an L{EnvironmentError}.
+ """
+ raise OSError(errno.EPERM, "Operation not permitted")
+
+
+ def writeRunningPID(self):
+ self._write(0)
+
+
+ def remove(self):
+ raise OSError(errno.ENOENT, "No such file or directory")
+
+
+ def isRunning(self):
+ return False
+
+
+ def __enter__(self):
+ return self
+
+
+ def __exit__(self, excType, excValue, traceback):
+ pass
+
+
+
+nonePIDFile = NonePIDFile()
+
+
+
+class AlreadyRunningError(Exception):
+ """
+ Process is already running.
+ """
+
+
+
+class InvalidPIDFileError(Exception):
+ """
+ PID file contents are invalid.
+ """
+
+
+
+class StalePIDFileError(Exception):
+ """
+ PID file contents are valid, but there is no process with the referenced
+ PID.
+ """
+
+
+
+class NoPIDFound(Exception):
+ """
+ No PID found in PID file.
+ """
diff --git a/contrib/python/Twisted/py2/twisted/application/runner/_runner.py b/contrib/python/Twisted/py2/twisted/application/runner/_runner.py
new file mode 100644
index 0000000000..7542a5b9a9
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/application/runner/_runner.py
@@ -0,0 +1,185 @@
+# -*- test-case-name: twisted.application.runner.test.test_runner -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Twisted application runner.
+"""
+
+from sys import stderr
+from signal import SIGTERM
+from os import kill
+
+from attr import attrib, attrs, Factory
+
+from twisted.logger import (
+ globalLogBeginner, textFileLogObserver,
+ FilteringLogObserver, LogLevelFilterPredicate,
+ LogLevel, Logger,
+)
+
+from ._exit import exit, ExitStatus
+from ._pidfile import nonePIDFile, AlreadyRunningError, InvalidPIDFileError
+
+
+
+@attrs(frozen=True)
+class Runner(object):
+ """
+ Twisted application runner.
+
+ @cvar _log: The logger attached to this class.
+ @type _log: L{Logger}
+
+ @ivar _reactor: The reactor to start and run the application in.
+ @type _reactor: L{IReactorCore}
+
+ @ivar _pidFile: The file to store the running process ID in.
+ @type _pidFile: L{IPIDFile}
+
+ @ivar _kill: Whether this runner should kill an existing running
+ instance of the application.
+ @type _kill: L{bool}
+
+ @ivar _defaultLogLevel: The default log level to start the logging
+ system with.
+ @type _defaultLogLevel: L{constantly.NamedConstant} from L{LogLevel}
+
+ @ivar _logFile: A file stream to write logging output to.
+ @type _logFile: writable file-like object
+
+ @ivar _fileLogObserverFactory: A factory for the file log observer to
+ use when starting the logging system.
+ @type _pidFile: callable that takes a single writable file-like object
+ argument and returns a L{twisted.logger.FileLogObserver}
+
+ @ivar _whenRunning: Hook to call after the reactor is running;
+ this is where the application code that relies on the reactor gets
+ called.
+ @type _whenRunning: callable that takes the keyword arguments specified
+ by C{whenRunningArguments}
+
+ @ivar _whenRunningArguments: Keyword arguments to pass to
+ C{whenRunning} when it is called.
+ @type _whenRunningArguments: L{dict}
+
+ @ivar _reactorExited: Hook to call after the reactor exits.
+ @type _reactorExited: callable that takes the keyword arguments
+ specified by C{reactorExitedArguments}
+
+ @ivar _reactorExitedArguments: Keyword arguments to pass to
+ C{reactorExited} when it is called.
+ @type _reactorExitedArguments: L{dict}
+ """
+
+ _log = Logger()
+
+ _reactor = attrib()
+ _pidFile = attrib(default=nonePIDFile)
+ _kill = attrib(default=False)
+ _defaultLogLevel = attrib(default=LogLevel.info)
+ _logFile = attrib(default=stderr)
+ _fileLogObserverFactory = attrib(default=textFileLogObserver)
+ _whenRunning = attrib(default=lambda **_: None)
+ _whenRunningArguments = attrib(default=Factory(dict))
+ _reactorExited = attrib(default=lambda **_: None)
+ _reactorExitedArguments = attrib(default=Factory(dict))
+
+
+ def run(self):
+ """
+ Run this command.
+ """
+ pidFile = self._pidFile
+
+ self.killIfRequested()
+
+ try:
+ with pidFile:
+ self.startLogging()
+ self.startReactor()
+ self.reactorExited()
+
+ except AlreadyRunningError:
+ exit(ExitStatus.EX_CONFIG, "Already running.")
+ return # When testing, patched exit doesn't exit
+
+
+ def killIfRequested(self):
+ """
+ If C{self._kill} is true, attempt to kill a running instance of the
+ application.
+ """
+ pidFile = self._pidFile
+
+ if self._kill:
+ if pidFile is nonePIDFile:
+ exit(ExitStatus.EX_USAGE, "No PID file specified.")
+ return # When testing, patched exit doesn't exit
+
+ try:
+ pid = pidFile.read()
+ except EnvironmentError:
+ exit(ExitStatus.EX_IOERR, "Unable to read PID file.")
+ return # When testing, patched exit doesn't exit
+ except InvalidPIDFileError:
+ exit(ExitStatus.EX_DATAERR, "Invalid PID file.")
+ return # When testing, patched exit doesn't exit
+
+ self.startLogging()
+ self._log.info("Terminating process: {pid}", pid=pid)
+
+ kill(pid, SIGTERM)
+
+ exit(ExitStatus.EX_OK)
+ return # When testing, patched exit doesn't exit
+
+
+ def startLogging(self):
+ """
+ Start the L{twisted.logger} logging system.
+ """
+ logFile = self._logFile
+
+ fileLogObserverFactory = self._fileLogObserverFactory
+
+ fileLogObserver = fileLogObserverFactory(logFile)
+
+ logLevelPredicate = LogLevelFilterPredicate(
+ defaultLogLevel=self._defaultLogLevel
+ )
+
+ filteringObserver = FilteringLogObserver(
+ fileLogObserver, [logLevelPredicate]
+ )
+
+ globalLogBeginner.beginLoggingTo([filteringObserver])
+
+
+ def startReactor(self):
+ """
+ Register C{self._whenRunning} with the reactor so that it is called
+ once the reactor is running, then start the reactor.
+ """
+ self._reactor.callWhenRunning(self.whenRunning)
+
+ self._log.info("Starting reactor...")
+ self._reactor.run()
+
+
+ def whenRunning(self):
+ """
+ Call C{self._whenRunning} with C{self._whenRunningArguments}.
+
+ @note: This method is called after the reactor starts running.
+ """
+ self._whenRunning(**self._whenRunningArguments)
+
+
+ def reactorExited(self):
+ """
+ Call C{self._reactorExited} with C{self._reactorExitedArguments}.
+
+ @note: This method is called after the reactor exits.
+ """
+ self._reactorExited(**self._reactorExitedArguments)