diff options
author | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:16:14 +0300 |
---|---|---|
committer | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:43:30 +0300 |
commit | b8cf9e88f4c5c64d9406af533d8948deb050d695 (patch) | |
tree | 218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/scripts | |
parent | 523f645a83a0ec97a0332dbc3863bb354c92a328 (diff) | |
download | ydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz |
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/scripts')
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'] |