aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/scripts
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/scripts
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/scripts')
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/__init__.py9
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/_twistd_unix.py453
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/_twistw.py54
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/htmlizer.py74
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/trial.py627
-rw-r--r--contrib/python/Twisted/py2/twisted/scripts/twistd.py34
6 files changed, 1251 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/scripts/__init__.py b/contrib/python/Twisted/py2/twisted/scripts/__init__.py
new file mode 100644
index 0000000000..73d90a8eaf
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/__init__.py
@@ -0,0 +1,9 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Subpackage containing the modules that implement the command line tools.
+
+Note that these are imported by top-level scripts which are intended to be
+invoked directly from a shell.
+"""
diff --git a/contrib/python/Twisted/py2/twisted/scripts/_twistd_unix.py b/contrib/python/Twisted/py2/twisted/scripts/_twistd_unix.py
new file mode 100644
index 0000000000..00488bf429
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/_twistd_unix.py
@@ -0,0 +1,453 @@
+# -*- test-case-name: twisted.test.test_twistd -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+from __future__ import absolute_import, division, print_function
+
+import errno
+import os
+import pwd
+import sys
+import traceback
+
+from twisted.python import log, logfile, usage
+from twisted.python.compat import (intToBytes, _bytesRepr, _PY3)
+from twisted.python.util import (
+ switchUID, uidFromString, gidFromString, untilConcludes)
+from twisted.application import app, service
+from twisted.internet.interfaces import IReactorDaemonize
+from twisted import copyright, logger
+from twisted.python.runtime import platformType
+
+
+
+if platformType == "win32":
+ raise ImportError("_twistd_unix doesn't work on Windows.")
+
+
+def _umask(value):
+ return int(value, 8)
+
+
+class ServerOptions(app.ServerOptions):
+ synopsis = "Usage: twistd [options]"
+
+ optFlags = [['nodaemon', 'n', "don't daemonize, don't use default umask of 0077"],
+ ['originalname', None, "Don't try to change the process name"],
+ ['syslog', None, "Log to syslog, not to file"],
+ ['euid', '',
+ "Set only effective user-id rather than real user-id. "
+ "(This option has no effect unless the server is running as "
+ "root, in which case it means not to shed all privileges "
+ "after binding ports, retaining the option to regain "
+ "privileges in cases such as spawning processes. "
+ "Use with caution.)"],
+ ]
+
+ optParameters = [
+ ['prefix', None,'twisted',
+ "use the given prefix when syslogging"],
+ ['pidfile','','twistd.pid',
+ "Name of the pidfile"],
+ ['chroot', None, None,
+ 'Chroot to a supplied directory before running'],
+ ['uid', 'u', None, "The uid to run as.", uidFromString],
+ ['gid', 'g', None,
+ "The gid to run as. If not specified, the default gid "
+ "associated with the specified --uid is used.",
+ gidFromString],
+ ['umask', None, None,
+ "The (octal) file creation mask to apply.", _umask],
+ ]
+
+ compData = usage.Completions(
+ optActions={"pidfile": usage.CompleteFiles("*.pid"),
+ "chroot": usage.CompleteDirs(descr="chroot directory"),
+ "gid": usage.CompleteGroups(descr="gid to run as"),
+ "uid": usage.CompleteUsernames(descr="uid to run as"),
+ "prefix": usage.Completer(descr="syslog prefix"),
+ },
+ )
+
+
+ def opt_version(self):
+ """
+ Print version information and exit.
+ """
+ print('twistd (the Twisted daemon) {}'.format(copyright.version),
+ file=self.stdout)
+ print(copyright.copyright, file=self.stdout)
+ sys.exit()
+
+
+ def postOptions(self):
+ app.ServerOptions.postOptions(self)
+ if self['pidfile']:
+ self['pidfile'] = os.path.abspath(self['pidfile'])
+
+
+def checkPID(pidfile):
+ if not pidfile:
+ return
+ if os.path.exists(pidfile):
+ try:
+ with open(pidfile) as f:
+ pid = int(f.read())
+ except ValueError:
+ sys.exit('Pidfile {} contains non-numeric value'.format(pidfile))
+ try:
+ os.kill(pid, 0)
+ except OSError as why:
+ if why.errno == errno.ESRCH:
+ # The pid doesn't exist.
+ log.msg('Removing stale pidfile {}'.format(pidfile), isError=True)
+ os.remove(pidfile)
+ else:
+ sys.exit(
+ "Can't check status of PID {} from pidfile {}: {}".format(
+ pid, pidfile, why))
+ else:
+ sys.exit("""\
+Another twistd server is running, PID {}\n
+This could either be a previously started instance of your application or a
+different application entirely. To start a new one, either run it in some other
+directory, or use the --pidfile and --logfile parameters to avoid clashes.
+""".format(pid))
+
+
+
+class UnixAppLogger(app.AppLogger):
+ """
+ A logger able to log to syslog, to files, and to stdout.
+
+ @ivar _syslog: A flag indicating whether to use syslog instead of file
+ logging.
+ @type _syslog: C{bool}
+
+ @ivar _syslogPrefix: If C{sysLog} is C{True}, the string prefix to use for
+ syslog messages.
+ @type _syslogPrefix: C{str}
+
+ @ivar _nodaemon: A flag indicating the process will not be daemonizing.
+ @type _nodaemon: C{bool}
+ """
+
+ def __init__(self, options):
+ app.AppLogger.__init__(self, options)
+ self._syslog = options.get("syslog", False)
+ self._syslogPrefix = options.get("prefix", "")
+ self._nodaemon = options.get("nodaemon", False)
+
+
+ def _getLogObserver(self):
+ """
+ Create and return a suitable log observer for the given configuration.
+
+ The observer will go to syslog using the prefix C{_syslogPrefix} if
+ C{_syslog} is true. Otherwise, it will go to the file named
+ C{_logfilename} or, if C{_nodaemon} is true and C{_logfilename} is
+ C{"-"}, to stdout.
+
+ @return: An object suitable to be passed to C{log.addObserver}.
+ """
+ if self._syslog:
+ from twisted.python import syslog
+ return syslog.SyslogObserver(self._syslogPrefix).emit
+
+ if self._logfilename == '-':
+ if not self._nodaemon:
+ sys.exit('Daemons cannot log to stdout, exiting!')
+ logFile = sys.stdout
+ elif self._nodaemon and not self._logfilename:
+ logFile = sys.stdout
+ else:
+ if not self._logfilename:
+ self._logfilename = 'twistd.log'
+ logFile = logfile.LogFile.fromFullPath(self._logfilename)
+ try:
+ import signal
+ except ImportError:
+ pass
+ else:
+ # Override if signal is set to None or SIG_DFL (0)
+ if not signal.getsignal(signal.SIGUSR1):
+ def rotateLog(signal, frame):
+ from twisted.internet import reactor
+ reactor.callFromThread(logFile.rotate)
+ signal.signal(signal.SIGUSR1, rotateLog)
+ return logger.textFileLogObserver(logFile)
+
+
+
+def launchWithName(name):
+ if name and name != sys.argv[0]:
+ exe = os.path.realpath(sys.executable)
+ log.msg('Changing process name to ' + name)
+ os.execv(exe, [name, sys.argv[0], '--originalname'] + sys.argv[1:])
+
+
+
+class UnixApplicationRunner(app.ApplicationRunner):
+ """
+ An ApplicationRunner which does Unix-specific things, like fork,
+ shed privileges, and maintain a PID file.
+ """
+ loggerFactory = UnixAppLogger
+
+ def preApplication(self):
+ """
+ Do pre-application-creation setup.
+ """
+ checkPID(self.config['pidfile'])
+ self.config['nodaemon'] = (self.config['nodaemon']
+ or self.config['debug'])
+ self.oldstdout = sys.stdout
+ self.oldstderr = sys.stderr
+
+
+ def _formatChildException(self, exception):
+ """
+ Format the C{exception} in preparation for writing to the
+ status pipe. This does the right thing on Python 2 if the
+ exception's message is Unicode, and in all cases limits the
+ length of the message afte* encoding to 100 bytes.
+
+ This means the returned message may be truncated in the middle
+ of a unicode escape.
+
+ @type exception: L{Exception}
+ @param exception: The exception to format.
+
+ @return: The formatted message, suitable for writing to the
+ status pipe.
+ @rtype: L{bytes}
+ """
+ # On Python 2 this will encode Unicode messages with the ascii
+ # codec and the backslashreplace error handler.
+ exceptionLine = traceback.format_exception_only(exception.__class__,
+ exception)[-1]
+ # remove the trailing newline
+ formattedMessage = '1 {}'.format(exceptionLine.strip())
+ # On Python 3, encode the message the same way Python 2's
+ # format_exception_only does
+ if _PY3:
+ formattedMessage = formattedMessage.encode('ascii',
+ 'backslashreplace')
+ # By this point, the message has been encoded, if appropriate,
+ # with backslashreplace on both Python 2 and Python 3.
+ # Truncating the encoded message won't make it completely
+ # unreadable, and the reader should print out the repr of the
+ # message it receives anyway. What it will do, however, is
+ # ensure that only 100 bytes are written to the status pipe,
+ # ensuring that the child doesn't block because the pipe's
+ # full. This assumes PIPE_BUF > 100!
+ return formattedMessage[:100]
+
+
+ def postApplication(self):
+ """
+ To be called after the application is created: start the application
+ and run the reactor. After the reactor stops, clean up PID files and
+ such.
+ """
+ try:
+ self.startApplication(self.application)
+ except Exception as ex:
+ statusPipe = self.config.get("statusPipe", None)
+ if statusPipe is not None:
+ message = self._formatChildException(ex)
+ untilConcludes(os.write, statusPipe, message)
+ untilConcludes(os.close, statusPipe)
+ self.removePID(self.config['pidfile'])
+ raise
+ else:
+ statusPipe = self.config.get("statusPipe", None)
+ if statusPipe is not None:
+ untilConcludes(os.write, statusPipe, b"0")
+ untilConcludes(os.close, statusPipe)
+ self.startReactor(None, self.oldstdout, self.oldstderr)
+ self.removePID(self.config['pidfile'])
+
+
+ def removePID(self, pidfile):
+ """
+ Remove the specified PID file, if possible. Errors are logged, not
+ raised.
+
+ @type pidfile: C{str}
+ @param pidfile: The path to the PID tracking file.
+ """
+ if not pidfile:
+ return
+ try:
+ os.unlink(pidfile)
+ except OSError as e:
+ if e.errno == errno.EACCES or e.errno == errno.EPERM:
+ log.msg("Warning: No permission to delete pid file")
+ else:
+ log.err(e, "Failed to unlink PID file:")
+ except:
+ log.err(None, "Failed to unlink PID file:")
+
+
+ def setupEnvironment(self, chroot, rundir, nodaemon, umask, pidfile):
+ """
+ Set the filesystem root, the working directory, and daemonize.
+
+ @type chroot: C{str} or L{None}
+ @param chroot: If not None, a path to use as the filesystem root (using
+ L{os.chroot}).
+
+ @type rundir: C{str}
+ @param rundir: The path to set as the working directory.
+
+ @type nodaemon: C{bool}
+ @param nodaemon: A flag which, if set, indicates that daemonization
+ should not be done.
+
+ @type umask: C{int} or L{None}
+ @param umask: The value to which to change the process umask.
+
+ @type pidfile: C{str} or L{None}
+ @param pidfile: If not L{None}, the path to a file into which to put
+ the PID of this process.
+ """
+ daemon = not nodaemon
+
+ if chroot is not None:
+ os.chroot(chroot)
+ if rundir == '.':
+ rundir = '/'
+ os.chdir(rundir)
+ if daemon and umask is None:
+ umask = 0o077
+ if umask is not None:
+ os.umask(umask)
+ if daemon:
+ from twisted.internet import reactor
+ self.config["statusPipe"] = self.daemonize(reactor)
+ if pidfile:
+ with open(pidfile, 'wb') as f:
+ f.write(intToBytes(os.getpid()))
+
+
+ def daemonize(self, reactor):
+ """
+ Daemonizes the application on Unix. This is done by the usual double
+ forking approach.
+
+ @see: U{http://code.activestate.com/recipes/278731/}
+ @see: W. Richard Stevens,
+ "Advanced Programming in the Unix Environment",
+ 1992, Addison-Wesley, ISBN 0-201-56317-7
+
+ @param reactor: The reactor in use. If it provides
+ L{IReactorDaemonize}, its daemonization-related callbacks will be
+ invoked.
+
+ @return: A writable pipe to be used to report errors.
+ @rtype: C{int}
+ """
+ # If the reactor requires hooks to be called for daemonization, call
+ # them. Currently the only reactor which provides/needs that is
+ # KQueueReactor.
+ if IReactorDaemonize.providedBy(reactor):
+ reactor.beforeDaemonize()
+ r, w = os.pipe()
+ if os.fork(): # launch child and...
+ code = self._waitForStart(r)
+ os.close(r)
+ os._exit(code) # kill off parent
+ os.setsid()
+ if os.fork(): # launch child and...
+ os._exit(0) # kill off parent again.
+ null = os.open('/dev/null', os.O_RDWR)
+ for i in range(3):
+ try:
+ os.dup2(null, i)
+ except OSError as e:
+ if e.errno != errno.EBADF:
+ raise
+ os.close(null)
+
+ if IReactorDaemonize.providedBy(reactor):
+ reactor.afterDaemonize()
+
+ return w
+
+
+ def _waitForStart(self, readPipe):
+ """
+ Wait for the daemonization success.
+
+ @param readPipe: file descriptor to read start information from.
+ @type readPipe: C{int}
+
+ @return: code to be passed to C{os._exit}: 0 for success, 1 for error.
+ @rtype: C{int}
+ """
+ data = untilConcludes(os.read, readPipe, 100)
+ dataRepr = _bytesRepr(data[2:])
+ if data != b"0":
+ msg = ("An error has occurred: {}\nPlease look at log "
+ "file for more information.\n".format(dataRepr))
+ untilConcludes(sys.__stderr__.write, msg)
+ return 1
+ return 0
+
+
+ def shedPrivileges(self, euid, uid, gid):
+ """
+ Change the UID and GID or the EUID and EGID of this process.
+
+ @type euid: C{bool}
+ @param euid: A flag which, if set, indicates that only the I{effective}
+ UID and GID should be set.
+
+ @type uid: C{int} or L{None}
+ @param uid: If not L{None}, the UID to which to switch.
+
+ @type gid: C{int} or L{None}
+ @param gid: If not L{None}, the GID to which to switch.
+ """
+ if uid is not None or gid is not None:
+ extra = euid and 'e' or ''
+ desc = '{}uid/{}gid {}/{}'.format(extra, extra, uid, gid)
+ try:
+ switchUID(uid, gid, euid)
+ except OSError as e:
+ log.msg('failed to set {}: {} (are you root?) -- '
+ 'exiting.'.format(desc, e))
+ sys.exit(1)
+ else:
+ log.msg('set {}'.format(desc))
+
+
+ def startApplication(self, application):
+ """
+ Configure global process state based on the given application and run
+ the application.
+
+ @param application: An object which can be adapted to
+ L{service.IProcess} and L{service.IService}.
+ """
+ process = service.IProcess(application)
+ if not self.config['originalname']:
+ launchWithName(process.processName)
+ self.setupEnvironment(
+ self.config['chroot'], self.config['rundir'],
+ self.config['nodaemon'], self.config['umask'],
+ self.config['pidfile'])
+
+ service.IService(application).privilegedStartService()
+
+ uid, gid = self.config['uid'], self.config['gid']
+ if uid is None:
+ uid = process.uid
+ if gid is None:
+ gid = process.gid
+ if uid is not None and gid is None:
+ gid = pwd.getpwuid(uid).pw_gid
+
+ self.shedPrivileges(self.config['euid'], uid, gid)
+ app.startApplication(application, not self.config['no_save'])
diff --git a/contrib/python/Twisted/py2/twisted/scripts/_twistw.py b/contrib/python/Twisted/py2/twisted/scripts/_twistw.py
new file mode 100644
index 0000000000..24db4e8a75
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/_twistw.py
@@ -0,0 +1,54 @@
+# -*- test-case-name: twisted.test.test_twistd -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+from __future__ import print_function
+
+from twisted.python import log
+from twisted.application import app, service, internet
+from twisted import copyright
+import sys, os
+
+
+
+class ServerOptions(app.ServerOptions):
+ synopsis = "Usage: twistd [options]"
+
+ optFlags = [['nodaemon','n', "(for backwards compatibility)."],
+ ]
+
+ def opt_version(self):
+ """
+ Print version information and exit.
+ """
+ print('twistd (the Twisted Windows runner) {}'.format(copyright.version),
+ file=self.stdout)
+ print(copyright.copyright, file=self.stdout)
+ sys.exit()
+
+
+
+class WindowsApplicationRunner(app.ApplicationRunner):
+ """
+ An ApplicationRunner which avoids unix-specific things. No
+ forking, no PID files, no privileges.
+ """
+
+ def preApplication(self):
+ """
+ Do pre-application-creation setup.
+ """
+ self.oldstdout = sys.stdout
+ self.oldstderr = sys.stderr
+ os.chdir(self.config['rundir'])
+
+
+ def postApplication(self):
+ """
+ Start the application and run the reactor.
+ """
+ service.IService(self.application).privilegedStartService()
+ app.startApplication(self.application, not self.config['no_save'])
+ app.startApplication(internet.TimerService(0.1, lambda:None), 0)
+ self.startReactor(None, self.oldstdout, self.oldstderr)
+ log.msg("Server Shut Down.")
diff --git a/contrib/python/Twisted/py2/twisted/scripts/htmlizer.py b/contrib/python/Twisted/py2/twisted/scripts/htmlizer.py
new file mode 100644
index 0000000000..ccc1419495
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/htmlizer.py
@@ -0,0 +1,74 @@
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+#
+
+"""
+HTML pretty-printing for Python source code.
+"""
+
+from __future__ import print_function
+
+__version__ = '$Revision: 1.8 $'[11:-2]
+
+from twisted.python import htmlizer, usage
+from twisted import copyright
+
+import os, sys
+
+header = '''<html><head>
+<title>%(title)s</title>
+<meta name=\"Generator\" content="%(generator)s" />
+%(alternate)s
+%(stylesheet)s
+</head>
+<body>
+'''
+footer = """</body>"""
+
+styleLink = '<link rel="stylesheet" href="%s" type="text/css" />'
+alternateLink = '<link rel="alternate" href="%(source)s" type="text/x-python" />'
+
+class Options(usage.Options):
+ synopsis = """%s [options] source.py
+ """ % (
+ os.path.basename(sys.argv[0]),)
+
+ optParameters = [
+ ('stylesheet', 's', None, "URL of stylesheet to link to."),
+ ]
+
+ compData = usage.Completions(
+ extraActions=[usage.CompleteFiles('*.py', descr='source python file')]
+ )
+
+
+ def parseArgs(self, filename):
+ self['filename'] = filename
+
+
+
+def run():
+ options = Options()
+ try:
+ options.parseOptions()
+ except usage.UsageError as e:
+ print(str(e))
+ sys.exit(1)
+ filename = options['filename']
+ if options.get('stylesheet') is not None:
+ stylesheet = styleLink % (options['stylesheet'],)
+ else:
+ stylesheet = ''
+
+ with open(filename + '.html', 'wb') as output:
+ outHeader = (header % {
+ 'title': filename,
+ 'generator': 'htmlizer/%s' % (copyright.longversion,),
+ 'alternate': alternateLink % {'source': filename},
+ 'stylesheet': stylesheet
+ })
+ output.write(outHeader.encode("utf-8"))
+ with open(filename, 'rb') as f:
+ htmlizer.filter(f, output, htmlizer.SmallerHTMLWriter)
+ output.write(footer.encode("utf-8"))
diff --git a/contrib/python/Twisted/py2/twisted/scripts/trial.py b/contrib/python/Twisted/py2/twisted/scripts/trial.py
new file mode 100644
index 0000000000..6c6e80aa16
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/trial.py
@@ -0,0 +1,627 @@
+# -*- test-case-name: twisted.trial.test.test_script -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+from __future__ import absolute_import, division, print_function
+
+import gc
+import inspect
+import os
+import pdb
+import random
+import sys
+import time
+import warnings
+
+from twisted.internet import defer
+from twisted.application import app
+from twisted.python import usage, reflect, failure
+from twisted.python.filepath import FilePath
+from twisted.python.reflect import namedModule
+from twisted.python.compat import long
+from twisted import plugin
+from twisted.trial import runner, itrial, reporter
+
+
+# Yea, this is stupid. Leave it for command-line compatibility for a
+# while, though.
+TBFORMAT_MAP = {
+ 'plain': 'default',
+ 'default': 'default',
+ 'emacs': 'brief',
+ 'brief': 'brief',
+ 'cgitb': 'verbose',
+ 'verbose': 'verbose'
+ }
+
+
+def _parseLocalVariables(line):
+ """
+ Accepts a single line in Emacs local variable declaration format and
+ returns a dict of all the variables {name: value}.
+ Raises ValueError if 'line' is in the wrong format.
+
+ See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html
+ """
+ paren = '-*-'
+ start = line.find(paren) + len(paren)
+ end = line.rfind(paren)
+ if start == -1 or end == -1:
+ raise ValueError("%r not a valid local variable declaration" % (line,))
+ items = line[start:end].split(';')
+ localVars = {}
+ for item in items:
+ if len(item.strip()) == 0:
+ continue
+ split = item.split(':')
+ if len(split) != 2:
+ raise ValueError("%r contains invalid declaration %r"
+ % (line, item))
+ localVars[split[0].strip()] = split[1].strip()
+ return localVars
+
+
+def loadLocalVariables(filename):
+ """
+ Accepts a filename and attempts to load the Emacs variable declarations
+ from that file, simulating what Emacs does.
+
+ See http://www.gnu.org/software/emacs/manual/html_node/File-Variables.html
+ """
+ with open(filename, "r") as f:
+ lines = [f.readline(), f.readline()]
+ for line in lines:
+ try:
+ return _parseLocalVariables(line)
+ except ValueError:
+ pass
+ return {}
+
+
+def getTestModules(filename):
+ testCaseVar = loadLocalVariables(filename).get('test-case-name', None)
+ if testCaseVar is None:
+ return []
+ return testCaseVar.split(',')
+
+
+def isTestFile(filename):
+ """
+ Returns true if 'filename' looks like a file containing unit tests.
+ False otherwise. Doesn't care whether filename exists.
+ """
+ basename = os.path.basename(filename)
+ return (basename.startswith('test_')
+ and os.path.splitext(basename)[1] == ('.py'))
+
+
+def _reporterAction():
+ return usage.CompleteList([p.longOpt for p in
+ plugin.getPlugins(itrial.IReporter)])
+
+
+def _maybeFindSourceLine(testThing):
+ """
+ Try to find the source line of the given test thing.
+
+ @param testThing: the test item to attempt to inspect
+ @type testThing: an L{TestCase}, test method, or module, though only the
+ former two have a chance to succeed
+ @rtype: int
+ @return: the starting source line, or -1 if one couldn't be found
+ """
+
+ # an instance of L{TestCase} -- locate the test it will run
+ method = getattr(testThing, "_testMethodName", None)
+ if method is not None:
+ testThing = getattr(testThing, method)
+
+ # If it's a function, we can get the line number even if the source file no
+ # longer exists
+ code = getattr(testThing, "__code__", None)
+ if code is not None:
+ return code.co_firstlineno
+
+ try:
+ return inspect.getsourcelines(testThing)[1]
+ except (IOError, TypeError):
+ # either testThing is a module, which raised a TypeError, or the file
+ # couldn't be read
+ return -1
+
+
+# orders which can be passed to trial --order
+_runOrders = {
+ "alphabetical" : (
+ "alphabetical order for test methods, arbitrary order for test cases",
+ runner.name),
+ "toptobottom" : (
+ "attempt to run test cases and methods in the order they were defined",
+ _maybeFindSourceLine),
+}
+
+
+def _checkKnownRunOrder(order):
+ """
+ Check that the given order is a known test running order.
+
+ Does nothing else, since looking up the appropriate callable to sort the
+ tests should be done when it actually will be used, as the default argument
+ will not be coerced by this function.
+
+ @param order: one of the known orders in C{_runOrders}
+ @return: the order unmodified
+ """
+ if order not in _runOrders:
+ raise usage.UsageError(
+ "--order must be one of: %s. See --help-orders for details" %
+ (", ".join(repr(order) for order in _runOrders),))
+ return order
+
+
+
+class _BasicOptions(object):
+ """
+ Basic options shared between trial and its local workers.
+ """
+ longdesc = ("trial loads and executes a suite of unit tests, obtained "
+ "from modules, packages and files listed on the command line.")
+
+ optFlags = [["help", "h"],
+ ["no-recurse", "N", "Don't recurse into packages"],
+ ['help-orders', None, "Help on available test running orders"],
+ ['help-reporters', None,
+ "Help on available output plugins (reporters)"],
+ ["rterrors", "e", "realtime errors, print out tracebacks as "
+ "soon as they occur"],
+ ["unclean-warnings", None,
+ "Turn dirty reactor errors into warnings"],
+ ["force-gc", None, "Have Trial run gc.collect() before and "
+ "after each test case."],
+ ["exitfirst", "x",
+ "Exit after the first non-successful result (cannot be "
+ "specified along with --jobs)."],
+ ]
+
+ optParameters = [
+ ["order", "o", None,
+ "Specify what order to run test cases and methods. "
+ "See --help-orders for more info.", _checkKnownRunOrder],
+ ["random", "z", None,
+ "Run tests in random order using the specified seed"],
+ ['temp-directory', None, '_trial_temp',
+ 'Path to use as working directory for tests.'],
+ ['reporter', None, 'verbose',
+ 'The reporter to use for this test run. See --help-reporters for '
+ 'more info.']]
+
+ compData = usage.Completions(
+ optActions={"order": usage.CompleteList(_runOrders),
+ "reporter": _reporterAction,
+ "logfile": usage.CompleteFiles(descr="log file name"),
+ "random": usage.Completer(descr="random seed")},
+ extraActions=[usage.CompleteFiles(
+ "*.py", descr="file | module | package | TestCase | testMethod",
+ repeat=True)],
+ )
+
+ fallbackReporter = reporter.TreeReporter
+ tracer = None
+
+ def __init__(self):
+ self['tests'] = []
+ usage.Options.__init__(self)
+
+ def getSynopsis(self):
+ executableName = reflect.filenameToModuleName(sys.argv[0])
+
+ if executableName.endswith('.__main__'):
+ executableName = '{} -m {}'.format(os.path.basename(sys.executable),
+ executableName.replace('.__main__', ''))
+
+ return """%s [options] [[file|package|module|TestCase|testmethod]...]
+ """ % (executableName,)
+
+ def coverdir(self):
+ """
+ Return a L{FilePath} representing the directory into which coverage
+ results should be written.
+ """
+ coverdir = 'coverage'
+ result = FilePath(self['temp-directory']).child(coverdir)
+ print("Setting coverage directory to %s." % (result.path,))
+ return result
+
+
+ # TODO: Some of the opt_* methods on this class have docstrings and some do
+ # not. This is mostly because usage.Options's currently will replace
+ # any intended output in optFlags and optParameters with the
+ # docstring. See #6427. When that is fixed, all methods should be
+ # given docstrings (and it should be verified that those with
+ # docstrings already have content suitable for printing as usage
+ # information).
+
+ def opt_coverage(self):
+ """
+ Generate coverage information in the coverage file in the
+ directory specified by the temp-directory option.
+ """
+ import trace
+ self.tracer = trace.Trace(count=1, trace=0)
+ sys.settrace(self.tracer.globaltrace)
+ self['coverage'] = True
+
+
+ def opt_testmodule(self, filename):
+ """
+ Filename to grep for test cases (-*- test-case-name).
+ """
+ # If the filename passed to this parameter looks like a test module
+ # we just add that to the test suite.
+ #
+ # If not, we inspect it for an Emacs buffer local variable called
+ # 'test-case-name'. If that variable is declared, we try to add its
+ # value to the test suite as a module.
+ #
+ # This parameter allows automated processes (like Buildbot) to pass
+ # a list of files to Trial with the general expectation of "these files,
+ # whatever they are, will get tested"
+ if not os.path.isfile(filename):
+ sys.stderr.write("File %r doesn't exist\n" % (filename,))
+ return
+ filename = os.path.abspath(filename)
+ if isTestFile(filename):
+ self['tests'].append(filename)
+ else:
+ self['tests'].extend(getTestModules(filename))
+
+
+ def opt_spew(self):
+ """
+ Print an insanely verbose log of everything that happens. Useful
+ when debugging freezes or locks in complex code.
+ """
+ from twisted.python.util import spewer
+ sys.settrace(spewer)
+
+
+ def opt_help_orders(self):
+ synopsis = ("Trial can attempt to run test cases and their methods in "
+ "a few different orders. You can select any of the "
+ "following options using --order=<foo>.\n")
+
+ print(synopsis)
+ for name, (description, _) in sorted(_runOrders.items()):
+ print(' ', name, '\t', description)
+ sys.exit(0)
+
+
+ def opt_help_reporters(self):
+ synopsis = ("Trial's output can be customized using plugins called "
+ "Reporters. You can\nselect any of the following "
+ "reporters using --reporter=<foo>\n")
+ print(synopsis)
+ for p in plugin.getPlugins(itrial.IReporter):
+ print(' ', p.longOpt, '\t', p.description)
+ sys.exit(0)
+
+
+ def opt_disablegc(self):
+ """
+ Disable the garbage collector
+ """
+ self["disablegc"] = True
+ gc.disable()
+
+
+ def opt_tbformat(self, opt):
+ """
+ Specify the format to display tracebacks with. Valid formats are
+ 'plain', 'emacs', and 'cgitb' which uses the nicely verbose stdlib
+ cgitb.text function
+ """
+ try:
+ self['tbformat'] = TBFORMAT_MAP[opt]
+ except KeyError:
+ raise usage.UsageError(
+ "tbformat must be 'plain', 'emacs', or 'cgitb'.")
+
+
+ def opt_recursionlimit(self, arg):
+ """
+ see sys.setrecursionlimit()
+ """
+ try:
+ sys.setrecursionlimit(int(arg))
+ except (TypeError, ValueError):
+ raise usage.UsageError(
+ "argument to recursionlimit must be an integer")
+ else:
+ self["recursionlimit"] = int(arg)
+
+
+ def opt_random(self, option):
+ try:
+ self['random'] = long(option)
+ except ValueError:
+ raise usage.UsageError(
+ "Argument to --random must be a positive integer")
+ else:
+ if self['random'] < 0:
+ raise usage.UsageError(
+ "Argument to --random must be a positive integer")
+ elif self['random'] == 0:
+ self['random'] = long(time.time() * 100)
+
+
+ def opt_without_module(self, option):
+ """
+ Fake the lack of the specified modules, separated with commas.
+ """
+ self["without-module"] = option
+ for module in option.split(","):
+ if module in sys.modules:
+ warnings.warn("Module '%s' already imported, "
+ "disabling anyway." % (module,),
+ category=RuntimeWarning)
+ sys.modules[module] = None
+
+
+ def parseArgs(self, *args):
+ self['tests'].extend(args)
+
+
+ def _loadReporterByName(self, name):
+ for p in plugin.getPlugins(itrial.IReporter):
+ qual = "%s.%s" % (p.module, p.klass)
+ if p.longOpt == name:
+ return reflect.namedAny(qual)
+ raise usage.UsageError("Only pass names of Reporter plugins to "
+ "--reporter. See --help-reporters for "
+ "more info.")
+
+
+ def postOptions(self):
+ # Only load reporters now, as opposed to any earlier, to avoid letting
+ # application-defined plugins muck up reactor selecting by importing
+ # t.i.reactor and causing the default to be installed.
+ self['reporter'] = self._loadReporterByName(self['reporter'])
+ if 'tbformat' not in self:
+ self['tbformat'] = 'default'
+ if self['order'] is not None and self['random'] is not None:
+ raise usage.UsageError(
+ "You can't specify --random when using --order")
+
+
+
+class Options(_BasicOptions, usage.Options, app.ReactorSelectionMixin):
+ """
+ Options to the trial command line tool.
+
+ @ivar _workerFlags: List of flags which are accepted by trial distributed
+ workers. This is used by C{_getWorkerArguments} to build the command
+ line arguments.
+ @type _workerFlags: C{list}
+
+ @ivar _workerParameters: List of parameter which are accepted by trial
+ distributed workers. This is used by C{_getWorkerArguments} to build
+ the command line arguments.
+ @type _workerParameters: C{list}
+ """
+
+ optFlags = [
+ ["debug", "b", "Run tests in a debugger. If that debugger is "
+ "pdb, will load '.pdbrc' from current directory if it exists."
+ ],
+ ["debug-stacktraces", "B", "Report Deferred creation and "
+ "callback stack traces"],
+ ["nopm", None, "don't automatically jump into debugger for "
+ "postmorteming of exceptions"],
+ ["dry-run", 'n', "do everything but run the tests"],
+ ["profile", None, "Run tests under the Python profiler"],
+ ["until-failure", "u", "Repeat test until it fails"],
+ ]
+
+ optParameters = [
+ ["debugger", None, "pdb", "the fully qualified name of a debugger to "
+ "use if --debug is passed"],
+ ["logfile", "l", "test.log", "log file name"],
+ ["jobs", "j", None, "Number of local workers to run"]
+ ]
+
+ compData = usage.Completions(
+ optActions = {
+ "tbformat": usage.CompleteList(["plain", "emacs", "cgitb"]),
+ "reporter": _reporterAction,
+ },
+ )
+
+ _workerFlags = ["disablegc", "force-gc", "coverage"]
+ _workerParameters = ["recursionlimit", "reactor", "without-module"]
+
+ fallbackReporter = reporter.TreeReporter
+ extra = None
+ tracer = None
+
+
+ def opt_jobs(self, number):
+ """
+ Number of local workers to run, a strictly positive integer.
+ """
+ try:
+ number = int(number)
+ except ValueError:
+ raise usage.UsageError(
+ "Expecting integer argument to jobs, got '%s'" % number)
+ if number <= 0:
+ raise usage.UsageError(
+ "Argument to jobs must be a strictly positive integer")
+ self["jobs"] = number
+
+
+ def _getWorkerArguments(self):
+ """
+ Return a list of options to pass to distributed workers.
+ """
+ args = []
+ for option in self._workerFlags:
+ if self.get(option) is not None:
+ if self[option]:
+ args.append("--%s" % (option,))
+ for option in self._workerParameters:
+ if self.get(option) is not None:
+ args.extend(["--%s" % (option,), str(self[option])])
+ return args
+
+
+ def postOptions(self):
+ _BasicOptions.postOptions(self)
+ if self['jobs']:
+ conflicts = ['debug', 'profile', 'debug-stacktraces', 'exitfirst']
+ for option in conflicts:
+ if self[option]:
+ raise usage.UsageError(
+ "You can't specify --%s when using --jobs" % option)
+ if self['nopm']:
+ if not self['debug']:
+ raise usage.UsageError("You must specify --debug when using "
+ "--nopm ")
+ failure.DO_POST_MORTEM = False
+
+
+
+def _initialDebugSetup(config):
+ # do this part of debug setup first for easy debugging of import failures
+ if config['debug']:
+ failure.startDebugMode()
+ if config['debug'] or config['debug-stacktraces']:
+ defer.setDebugging(True)
+
+
+
+def _getSuite(config):
+ loader = _getLoader(config)
+ recurse = not config['no-recurse']
+ return loader.loadByNames(config['tests'], recurse=recurse)
+
+
+
+def _getLoader(config):
+ loader = runner.TestLoader()
+ if config['random']:
+ randomer = random.Random()
+ randomer.seed(config['random'])
+ loader.sorter = lambda x : randomer.random()
+ print('Running tests shuffled with seed %d\n' % config['random'])
+ elif config['order']:
+ _, sorter = _runOrders[config['order']]
+ loader.sorter = sorter
+ if not config['until-failure']:
+ loader.suiteFactory = runner.DestructiveTestSuite
+ return loader
+
+
+def _wrappedPdb():
+ """
+ Wrap an instance of C{pdb.Pdb} with readline support and load any .rcs.
+
+ """
+
+ dbg = pdb.Pdb()
+ try:
+ namedModule('readline')
+ except ImportError:
+ print("readline module not available")
+ for path in ('.pdbrc', 'pdbrc'):
+ if os.path.exists(path):
+ try:
+ rcFile = open(path, 'r')
+ except IOError:
+ pass
+ else:
+ with rcFile:
+ dbg.rcLines.extend(rcFile.readlines())
+ return dbg
+
+
+class _DebuggerNotFound(Exception):
+ """
+ A debugger import failed.
+
+ Used to allow translating these errors into usage error messages.
+
+ """
+
+
+
+def _makeRunner(config):
+ """
+ Return a trial runner class set up with the parameters extracted from
+ C{config}.
+
+ @return: A trial runner instance.
+ @rtype: L{runner.TrialRunner} or C{DistTrialRunner} depending on the
+ configuration.
+ """
+ cls = runner.TrialRunner
+ args = {'reporterFactory': config['reporter'],
+ 'tracebackFormat': config['tbformat'],
+ 'realTimeErrors': config['rterrors'],
+ 'uncleanWarnings': config['unclean-warnings'],
+ 'logfile': config['logfile'],
+ 'workingDirectory': config['temp-directory']}
+ if config['dry-run']:
+ args['mode'] = runner.TrialRunner.DRY_RUN
+ elif config['jobs']:
+ from twisted.trial._dist.disttrial import DistTrialRunner
+ cls = DistTrialRunner
+ args['workerNumber'] = config['jobs']
+ args['workerArguments'] = config._getWorkerArguments()
+ else:
+ if config['debug']:
+ args['mode'] = runner.TrialRunner.DEBUG
+ debugger = config['debugger']
+
+ if debugger != 'pdb':
+ try:
+ args['debugger'] = reflect.namedAny(debugger)
+ except reflect.ModuleNotFound:
+ raise _DebuggerNotFound(
+ '%r debugger could not be found.' % (debugger,))
+ else:
+ args['debugger'] = _wrappedPdb()
+
+ args['exitFirst'] = config['exitfirst']
+ args['profile'] = config['profile']
+ args['forceGarbageCollection'] = config['force-gc']
+
+ return cls(**args)
+
+
+
+def run():
+ if len(sys.argv) == 1:
+ sys.argv.append("--help")
+ config = Options()
+ try:
+ config.parseOptions()
+ except usage.error as ue:
+ raise SystemExit("%s: %s" % (sys.argv[0], ue))
+ _initialDebugSetup(config)
+
+ try:
+ trialRunner = _makeRunner(config)
+ except _DebuggerNotFound as e:
+ raise SystemExit('%s: %s' % (sys.argv[0], str(e)))
+
+ suite = _getSuite(config)
+ if config['until-failure']:
+ test_result = trialRunner.runUntilFailure(suite)
+ else:
+ test_result = trialRunner.run(suite)
+ if config.tracer:
+ sys.settrace(None)
+ results = config.tracer.results()
+ results.write_results(show_missing=1, summary=False,
+ coverdir=config.coverdir().path)
+ sys.exit(not test_result.wasSuccessful())
diff --git a/contrib/python/Twisted/py2/twisted/scripts/twistd.py b/contrib/python/Twisted/py2/twisted/scripts/twistd.py
new file mode 100644
index 0000000000..2c6439353c
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/scripts/twistd.py
@@ -0,0 +1,34 @@
+# -*- test-case-name: twisted.test.test_twistd -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+The Twisted Daemon: platform-independent interface.
+
+@author: Christopher Armstrong
+"""
+
+from __future__ import absolute_import, division
+
+from twisted.application import app
+
+from twisted.python.runtime import platformType
+if platformType == "win32":
+ from twisted.scripts._twistw import ServerOptions, \
+ WindowsApplicationRunner as _SomeApplicationRunner
+else:
+ from twisted.scripts._twistd_unix import ServerOptions, \
+ UnixApplicationRunner as _SomeApplicationRunner
+
+def runApp(config):
+ runner = _SomeApplicationRunner(config)
+ runner.run()
+ if runner._exitSignal is not None:
+ app._exitWithSignal(runner._exitSignal)
+
+
+def run():
+ app.run(runApp, ServerOptions)
+
+
+__all__ = ['run', 'runApp']