././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1644239601.593946 Twisted-22.1.0/0000755000175100001710000000000000000000000012557 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/.coveragerc0000644000175100001710000000045600000000000014705 0ustar00runnerdocker[run] branch = True parallel = True source = twisted omit = .tox/*/tmp/_trial_temp/* [paths] source= src/twisted */site-packages/twisted *\site-packages\twisted [report] precision = 2 ignore_errors = True exclude_lines = if TYPE_CHECKING \s*\.\.\.$ raise NotImplementedError ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/CONTRIBUTING.md0000644000175100001710000000163300000000000015013 0ustar00runnerdockerContributing to Twisted ======================= As an open source project, Twisted welcomes contributions of many forms. This document summarizes the process. Twisted has a [Code of Conduct](./code_of_conduct.md). Examples of contributions include: * Code patches * Documentation improvements * Bug reports * Pull request reviews Contributions are managed using GitHub's Pull Requests. For a PR to be accepted: * It must have an associated Trac ticket ([file one here](https://twistedmatrix.com/trac/newticket)) * All automated checks must pass * The changeset must have 100% patch test coverage Twisted uses Trac to keep track of bugs, feature requests, and associated patches because GitHub doesn't provide adequate tooling for its community. You can log in to Trac with your GitHub account. Extensive contribution guidelines are [available online](https://twistedmatrix.com/trac/wiki/ContributingToTwistedLabs). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/INSTALL.rst0000644000175100001710000000211600000000000014417 0ustar00runnerdockerInstalling Twisted ================== Installation Requirements ------------------------- To install Twisted, you need: - Python 3.6/3.7/3.8/3.9 - `setuptools `_ (installed automatically if you use pip). - `Zope Interface `_ 4.4.2 or newer. Installing via pip will automatically download a suitable Zope Interface. - On Windows `pywin32 `_ is required. Build 223 or later is highly recommended for reliable operation. We also have `setuptools extras `_ for automatically installing optional packages used by Twisted. Installing Twisted ------------------ To install the latest version of Twisted using pip:: $ pip install twisted You can install optional dependencies for specific functionality in Twisted (such as TLS or serial support) by using our setuptools extras (see above). As an example, to install Twisted with the TLS dependencies, use:: $ pip install twisted[tls] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/LICENSE0000644000175100001710000000362600000000000013573 0ustar00runnerdockerCopyright (c) 2001-2022 Allen Short Amber Hawkie Brown Andrew Bennetts Andy Gayton Antoine Pitrou Apple Computer, Inc. Ashwini Oruganti Benjamin Bruheim Bob Ippolito Canonical Limited Christopher Armstrong Ciena Corporation David Reid Divmod Inc. Donovan Preston Eric Mangold Eyal Lotem Google Inc. Hybrid Logic Ltd. Hynek Schlawack Itamar Turner-Trauring James Knight Jason A. Mobarak Jean-Paul Calderone Jessica McKellar Jonathan D. Simms Jonathan Jacobs Jonathan Lange Julian Berman Jürgen Hermann Kevin Horn Kevin Turner Laurens Van Houtven Mary Gardiner Massachusetts Institute of Technology Matthew Lefkowitz Moshe Zadka Paul Swartz Pavel Pergamenshchik Rackspace, US Inc. Ralph Meijer Richard Wall Sean Riley Software Freedom Conservancy Tavendo GmbH Thijs Triemstra Thomas Grainger Thomas Herve Timothy Allen Tom Most Tom Prince Travis B. Hartwell and others that have contributed code to the public domain. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/MANIFEST.in0000644000175100001710000000564000000000000014322 0ustar00runnerdocker# This file dictates what goes into a sdist tarball. # As a release tarball, we want to include: # - All of the library code. # - All the useful documentation (but not the super historic stuff) # - All of the test files, so that the tests can be run on an installed copy. # We do not want to include: # - Release management files (e.g. topfiles) # - Things only useful when running from a source checkout # Do not include the old topfiles, or news fragments recursive-exclude src/twisted *.misc *.bugfix *.doc *.feature *.removal recursive-exclude src/twisted NEWS README newsfragments exclude src/twisted/topfiles/CREDITS src/twisted/topfiles/ChangeLog.Old # Include NEWS, READMEs, etc recursive-include docs README include NEWS.rst README.rst INSTALL.rst CONTRIBUTING.md LICENSE code_of_conduct.md # Exclude admin scripts and things only useful when running from a source checkout exclude codecov.yml exclude .coveralls.yml exclude .git-blame-ignore-revs exclude .readthedocs.yml exclude .pre-commit-config.yaml recursive-exclude .circleci * exclude azure-pipelines recursive-exclude azure-pipelines * prune bin prune admin prune .github prune docs/_build # Include marker indicating that we ship type annotations (PEP 561) include src/twisted/py.typed # Include test-running utilities for downstream packagers include mypy.ini tox.ini .coveragerc # Include our docs templates recursive-include src/twisted/python/_pydoctortemplates *.html # Include all modules, even on a Python we're not installing for recursive-include src/twisted *.py # Some tests stuff recursive-include src/twisted *.pem recursive-include src/twisted/test *.pem.* include src/twisted/internet/test/fake_CAs/* include src/twisted/mail/test/rfc822.message # Some extras recursive-include src/twisted *.glade *.pxi *.h *.c *.bat *.g *.pyx *.zsh *.txt # Docs include docs/fun/lightbulb recursive-include docs/historic/Quotes * recursive-include docs *.1 recursive-include docs *.Quotes recursive-include docs *.bat recursive-include docs *.bmp recursive-include docs *.com recursive-include docs *.conf recursive-include docs *.css recursive-include docs *.dia recursive-include docs *.gif recursive-include docs *.glade recursive-include docs *.html recursive-include docs *.ico recursive-include docs *.nib recursive-include docs *.pem recursive-include docs *.placeholder recursive-include docs *.png recursive-include docs *.py recursive-include docs *.rpy recursive-include docs *.rst recursive-include docs *.rtl recursive-include docs *.service recursive-include docs *.socket recursive-include docs *.svg recursive-include docs *.tac recursive-include docs *.tidyrc recursive-include docs *.tpl recursive-include docs *.txt recursive-include docs *.users recursive-include docs *.xml recursive-include docs Makefile recursive-include docs/_static/js *.js prune docs/_build # Don't have the real old historic docs exclude docs/historic/2003 recursive-exclude docs/historic/2003 * ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/NEWS.rst0000644000175100001710000040666600000000000014107 0ustar00runnerdockerTicket numbers in this file can be looked up by visiting http://twistedmatrix.com/trac/ticket/ .. towncrier release notes start Twisted 22.1.0 (2022-02-03) =========================== Features -------- - Python 3.10 is now a supported platform (#10224) - Type annotations have been added to the twisted.python.fakepwd module. (#10287) Bugfixes -------- - twisted.internet.defer.inlineCallbacks has an improved type annotation, to avoid typing errors when it is used on a function which returns a non-None result. (#10231) - ``twisted.internet.base.DelayedCall.__repr__`` and ``twisted.internet.task.LoopingCall.__repr__`` had the changes from #10155 reverted to accept non-function callables. (#10235) - Revert the removal of .whl building that was done as part of #10177. (#10236) - The type annotation of the host parameter to twisted.internet.interfaces.IReactorTCP.connectTCP has been corrected from bytes to str. (#10251) - Deprecated ``twisted.python.threading.ThreadPool.currentThread()`` in favor of ``threading.current_thread()``. Switched ``twisted.python.threading.ThreadPool.currentThread()`` and ``twisted.python.threadable.getThreadID()`` to use `threading.current_thread()`` to avoid the deprecation warnings introduced for ``threading.currentThread()`` in Python 3.10. (#10273) Improved Documentation ---------------------- - twisted.internet.utils.runWithWarningsSupressed behavior of waiting on deferreds has been documented. (#10238) - Sync API docs templates with pydoctor 21.9.0 release, using new theming capabilities. (#10267) Misc ---- - #1681, #9944, #10198, #10218, #10219, #10228, #10229, #10234, #10239, #10240, #10245, #10246, #10248, #10250, #10255, #10277, #10288, #10292 Conch ----- Bugfixes -------- - SSHTransportBase.ssh_KEXINIT now uses the remote peer preferred MAC list for negotiation. In previous versions it was only using the local preferred MAC list. (#10241) Features ~~~~~~~~ - twisted.conch.ssh now supports SSH extension negotiation (RFC 8308). (#10266) Bugfixes ~~~~~~~~ - twisted.conch now uses constant-time comparisons for MACs. (#8199) - twisted.conch.ssh.filetransfer.FileTransferServer will now return an ENOENT error status if an SFTP client tries to close an unrecognized file handle. (#10293) Web --- Bugfixes ~~~~~~~~ - twisted.web.client.RedirectAgent and twisted.web.client.BrowserLikeRedirectAgent now properly remove sensitive headers when redirecting to a different origin. (#10294) Improved Documentation ---------------------- - Add type annotations for twisted.web.client.readBody. (#10269) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.web.client.getPage, twisted.web.client.downladPage, and the associated implementation classes (HTTPPageGetter, HTTPPageDownloader, HTTPClientFactory, HTTPDownloader) have been removed because they do not segregate cookies by domain. They were deprecated in Twisted 16.7.0 in favor of twisted.web.client.Agent. GHSA-92x2-jw7w-xvvx. (#10295) Mail ---- No significant changes. Words ----- No significant changes. Names ----- No significant changes. Trial ----- Bugfixes ~~~~~~~~ - trial.runner.filenameToModule now sets the correct module.__name__ and sys.modules key (#10230) Twisted 21.7.0 (2021-07-26) =========================== Features -------- - Python 3.10b3 is now supported (#10224) - Type hinting was added to twisted.internet.defer, making this is the first release of Twisted where you might reasonably be able to use mypy without your own custom stub files (#10017) Bugfixes -------- - The changes to ``DelayedCall.__repr__`` and ``LoopingCall.__repr__`` from 21.7.0.rc1 were reverted as the wrong assumption that ``__qualname__`` is available on all the supported Python versions. (#10235) - The automated release process was updated to generate and release wheel files to PyPI (#10236) - twisted.internet.defer.inlineCallbacks has an improved type annotation, to avoid typing errors when it is used on a function which returns a non-None result. (#10231) - trial.runner.filenameToModule now sets the correct ``module.__name__`` and ``sys.modules`` key (#10230) - twisted.internet.process can now pause and resume producing in python 3 (#9933) - When installing Twisted it now requires a minimum Python 3.6.7 version to match the version used with automated testing. This is the minimum Python version that we know that Twisted works with. (#10098) - twisted.internet.asyncioreactor.AsyncioSelectorReactor will no longer raise a TypeError like "SelectorEventLoop required, instead got: " (broken since 21.2.0). (#10106) - twisted.web.template.flatten and flattenString will no longer raise RecursionError if a large number of synchronous Deferreds are included in a document. (#10125) - Fix type hint for http.Request.uri (from str to bytes). (#10139) - twisted.web.http_headers.getRawHeaders and twisted.web.http_headers.getAllRawHeaders are now typed to return immutable sequences of header values instead of lists. twisted.web.http_headers.getRawHeaders is now typed to return a non-optional value if a non-None default value is given. (#10142) - Fixed type hint for addr argument to twisted.internet.interfaces.buildProtocol. (#10147) - twisted.trial._dist.worker.LocalWorker.connectionMade now always writes the log file using UTF-8 encoding. In previous versions it was using the system default encoding. This was causing encoding errors as the distributed trial workers are sending Unicode data and the system default encoding might not always be Unicode compatible. For example, it can be CP1252 on Windows. (#10157) - twisted.words.protocols.irc.ctcpExtract was updated to work with PYPY 3.7.4. (#10189) - twisted.conch.ssh.transport.SSHServerTransport and twisted.conch.ssh.transport.SSHClientTransport no longer use the hardcoded SHA1 digest for non-group key exchanges. (#10203) - haproxy transport wrapper now returns hosts of type str for getPeer() and getHost(), as specified by IPv4Address and IPv6Address documentation. Previously it was returning bytes for the host. (#10211) Improved Documentation ---------------------- - Remove dead link in twisted.internet._dumbwin32proc module docstring (#9520) - Sync API docs templates with pydoctor 21.2.2 release. (#10105) - Twisted IRC channels are now hosted by Libera.Chat. (#10213) Deprecations and Removals ------------------------- - Python 3.5 is no longer supported. (#9958) Misc ---- - #9816, #9915, #10068, #10085, #10094, #10102, #10107, #10108, #10109, #10110, #10112, #10119, #10120, #10121, #10122, #10123, #10140, #10143, #10145, #10150, #10151, #10155, #10159, #10168, #10169, #10171, #10172, #10173, #10174, #10179, #10194, #10201, #10212, #10215, #10217, #11017 Conch ----- Misc ~~~~ - #10097 Web --- Features ~~~~~~~~ - twisted.web.template.renderElement() now accepts any IRequest implementer instead of only twisted.web.server.Request. Add type hints to twisted.web.template. (#10184) Bugfixes ~~~~~~~~ - The server-side HTTP/1.1 chunking implementation no longer performs quadratic work when input arrives in small chunks, preventing CPU exhaustion. (#3795) - twisted.web.http's chunked encoding support now rejects chunk sizes that are invalid because they look like negative hexadecimal integers. (#10130) - The type hint of twisted.web.server.Request.postpath is now correctly listed as Optional[List[bytes]]. This was incorrect in Twisted v21.2.0. (#10136) - The server-side HTTP/1.1 chunking implementation now rejects invalid chunk boundaries, preventing unbounded buffering. (#10137) - The server-side HTTP/1.1 chunking implementation now limits the length of the chunk size line (which includes chunk extensions) to twisted.web.http.maxChunkSizeLineLength — 1 KiB — so that it may not consume an unbounded amount of memory. (#10144) - Calling twisted.web.server.Site now registers its expiration timeout using the reactor associated with its twisted.web.server.Site. Site now a reactor attribute via its superclass, twisted.web.http.HTTPFactory. (#10177) Misc ~~~~ - #9659, #10100, #10154, #10186 Mail ---- No significant changes. Words ----- No significant changes. Names ----- No significant changes. Twisted 21.2.0 (2021-02-28) =========================== Features -------- - The enableSessions argument to twisted.internet.ssl.CertificateOptions now actually enables/disables OpenSSL's session cache. Also, due to session-related bugs, it defaults to False. (#9583) - twisted.internet.defer.inlineCallbacks and ensureDeferred will now associate a contextvars.Context with the coroutines they run, meaning that ContextVar objects will maintain their value within the same coroutine, similarly to asyncio Tasks. This functionality requires Python 3.7+, or the contextvars PyPI backport to be installed for Python 3.5-3.6. (#9719, #9826) - twisted.internet.defer.Deferred.fromCoroutine has been added. This is similar to the existing ensureDeferred function, but is named more consistently inside Twisted and does not pass through Deferreds. (#9825) - trial now allows the @unittest.skipIf decorator to specify that an entire test class should be skipped. (#9829) - The twisted.python.deprecate.deprecatedKeywordParameter decorator can be used to mark a keyword paramater of a function or method as deprecated. (#9844) - Projects using Twisted can now perform type checking against a Twisted installation, for example using mypy. (#9908) - twisted.python.util.InsensitiveDict now fully implements MutableMapping. (#9919) - Python 3.8 is now tested and supported. (#9955) - Support a coroutine function in twisted.internet.task.react (#9974) - PyPy 3.7 is now tested and supported. (#10093) Bugfixes -------- - twisted.web.twcgi.CGIProcessProtocol.processEnded(...) now handles an already-finished request, for example when request.connectionLost(...) was called previously. (#9468) - Twisted's dependency on PyHamcrest has been moved from the base package to the new "test" extra. Consequently the test extra must be installed for Twisted's test suite to pass. (#9509) - Fixed serialization of timedelta, date, and time objects in twisted.spread. (#9716) - twisted.internet.asyncioreactor.AsyncioSelectorReactor now raises an exception if instantiated with an event loop which is not compatible with asyncio.SelectorEventLoop. This fixes the AsyncioSelectorReactor in Python 3.8+ on Windows, where in bp-34687 the default Windows asyncio event loop was changed to ProactorEventLoop. Applications that use AsyncioSelectorReactor on Windows with Python 3.8+ must call asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) before instantiating and running AsyncioSelectorReactor. (#9766) - twisted.internet.process.registerReapProcessHandler and ._BaseProcess.reapProcess will no longer raise a TypeError when processing a None PID (#9775) - INotify will close its file descriptor if a directory is automatically removed by twisted from the watchlist because it's deleted, avoiding orphaned filedescriptors. (#9777) - DelayedCall.reset() is now working properly with asyncioreactor (#9780) - AsyncioSelectorReactor.seconds() now correctly returns an epoch time. (#9787) - The _connDone parameter has been removed from twisted.internet.abstract.FileDescriptor.loseConnection()'s signature in order to match the signature in the base class twisted.internet._newtls.ConnectionMixin loseConnection(). (#9849) - The Gtk3 reactor now runs on Wayland-only sessions (#9904) - Descriptive error messages from twisted.internet.error are now present when running with 'python -OO'. (#9918) - Comparator methods such as __eq__() now always return NotImplemented for uncomparable types. (#9919) - When installing Twisted it now requires a minimum Python 3.5.4 version to match the version used with automated testing. This is the minimum Python version that we know that Twisted works with. (#10098) Improved Documentation ---------------------- - The narrative docs now contains the associated Twisted version and the date when they were generated. (#3945) - The "Writing a twistd plugin" howto now explains how to deploy twistd plugins using Python packaging and pip (#9243) - (#9868, #9873, #9874) - Fix a typo in "Introduction to Deferreds" document. (#9948) - The Twisted Coding Standard has been changed to refer to The Black code style for guidelines regarding whitespace and line lengths. (#9957) - Exempt ``__repr__``, ``__slots__`` and other ``@attrs.define`` related changes from compatibility policy. (#9982) - Fix many docstring mistakes flagged by new sanity checks in pydoctor. (#10021) - Fix a few dozen broken links to API documentation pages. (#10057) Deprecations and Removals ------------------------- - twisted.cred.credentials.UsernameHashedPassword is now deprecated because it doesn't hash the password, causing it to return the wrong result. (#8368) - twisted.news is now removed from the codebase. (This module was never installed on Python 3.) (#9782) - Support for Python 2.7 has been removed. Twisted now supports only Python versions 3.5/3.6/3.7. (#9790) - twisted.pair.ethernet.IEthernetProtocol.addProto()'s interface was changed to match the existing implementations in the Twisted source code. (#9877) - twisted.python.filepath.FilePath.statinfo was deprecated in Twisted 15.0.0 and has now been removed. (#9881) - The parameters to twisted.internet.base.ReactorBase.addSystemEventTrigger(), twisted.internet.base.ReactorBase.callWhenRunning(), twisted.internet.base.ReactorBase.callLater(), twisted.internet.task.Clock.callLater() have been renamed to match the parameters defined in the following interfaces: twisted.internet.interfaces.IReactorCore, twisted.internet.interfaces.IReactorTime. (#9897) - Functions and types in twisted.python.compat that existed to support the transition from Python 2 to 3 have been deprecated. (#9922) - twisted.logger.LoggingFile.softspace has been deprecated. (#10042) - twisted.python.win32.WindowsError and FakeWindowsError have been deprecated. (#10053) - twisted.mail.pop3client has been renamed to twisted.mail._pop3client, since it has always been a private implementation module. (#10054) Misc ---- - #5356, #6460, #6903, #6986, #7945, #9306, #9512, #9531, #9622, #9652, #9718, #9744, #9768, #9773, #9776, #9778, #9781, #9784, #9785, #9788, #9789, #9791, #9793, #9795, #9796, #9797, #9798, #9800, #9802, #9803, #9808, #9809, #9810, #9811, #9812, #9820, #9823, #9827, #9833, #9837, #9840, #9842, #9846, #9847, #9848, #9850, #9851, #9852, #9854, #9855, #9856, #9857, #9858, #9861, #9862, #9863, #9864, #9865, #9866, #9867, #9869, #9870, #9871, #9872, #9876, #9878, #9879, #9880, #9882, #9883, #9884, #9886, #9889, #9890, #9891, #9892, #9895, #9896, #9898, #9899, #9902, #9903, #9916, #9917, #9921, #9924, #9927, #9928, #9936, #9953, #9954, #9956, #9959, #9960, #9969, #9970, #9971, #9975, #9976, #9977, #9978, #9979, #9980, #9981, #9983, #9985, #9986, #9987, #9988, #9989, #9991, #9992, #9995, #9999, #10000, #10002, #10009, #10010, #10011, #10014, #10015, #10018, #10025, #10027, #10029, #10032, #10033, #10034, #10036, #10038, #10043, #10044, #10046, #10054, #10059, #10060, #10061, #10063, #10064, #10065, #10069, #10080, #10090 Conch ----- Features ~~~~~~~~ - twisted.conch.ssh now supports Ed25519 keys (requires OpenSSL >= 1.1.1b). (#8966) - twisted.conch.ssh.session.SSHSession can now accept environment variables sent by the client, if the SSH avatar implements the new ISessionSetEnv interface. (#9315) - twisted.conch.ssh.keys.Key.fromString and twisted.conch.ssh.keys.Key.toString now normalize Unicode passphrases as required by NIST 800-63B. (#9736) - twisted.conch.telnet now implements EOR (End of Record) command (RFC 885) (#9875) Bugfixes ~~~~~~~~ - t.c.ssh.filetransfer.FileTransferClient now errbacks any outstanding requests if the connection is lost before a reply is received. (#9571) - t.c.ssh.filetransfer.FileTransferClient immediately errbacks any attempt to send a request on a closed channel. (#9572) - twisted.conch.ssh.session.SSHSession now accepts environment variables also for multiplexed SSH session. (#10016) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - construct and assign portal and checkers consistently in ssh server example (#9578) Misc ~~~~ - #6446, #9571, #9831, #9913 Web --- Bugfixes ~~~~~~~~ - twisted.web.http.Request.getRequestHostname now supports IPv6 literal hostnames in HTTP host headers. (#6014) - Fixed unexpected exception by handling subclass of TaskFinished when FileBodyProducer's task stopped twice. (#6528) - Importing twisted.web.client no longer has the side effect of initializing the reactor. (#9774) - Ensure that all calls to connectionLost use a Failure instance in the HTTP 2 code. (#9817) - twisted.web.util.ParentRedirect has been fixed and documented. It was broken by a security fix in Twisted 19.2.0. (#9835) - xmlrpc's Proxy class now verifies HTTPS certificates against the system bundle. (#9836) - twisted.web.twcgi can now handle url parameters in python 3 (#9887) - defer reactor import in twisted.web.xmlrpc (#9931) - twisted.web.RedirectAgent now supports 308 redirects (#9940) - Fixed an error where twisted.web.http.requestReceived() tries to encode a NoneType returned by cgi.parse_multipart when a multipart body does not contain a "content-disposition" definition. (#10084) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - xmlrpc's QueryFactory class is now public, more explanation for xmlrpc's queryFactory, and new xmlrpc-debug.py example script for debugging raw XML-RPC traffic. (#9350) - twisted.web.client.ContentDecoderAgent's documentation has been corrected and improved. (#9742) Misc ~~~~ - #6446, #9758, #9801, #9831, #9834, #9841 Mail ---- Bugfixes ~~~~~~~~ - twisted.mail.smtp.ESMTPSender no longer forces TLSv1.0 when used without explicit context factory. (#9740) Misc ~~~~ - #6446, #9831, #9832, #9900, #9910 Words ----- Misc ~~~~ - #9901 Names ----- Features ~~~~~~~~ - twisted.names.hosts.Resolver and twisted.names.hosts.searchFileForAll() now ignore malformed lines in hosts files like /etc/hosts (#9752) - New interface IEncodableRecord combines IEncodable and IRecord, which is useful when using type annotations. (#9920) Bugfixes ~~~~~~~~ - twistd -n dns --pyzone example-domain.com will no longer throw an exception on startup with Python 3. (#9783) - twist dns --pyzone example-domain.com now works on Python 3. (#9786) Misc ~~~~ - #9749 Twisted 20.3.0 (2020-03-13) =========================== Bugfixes -------- - twisted.protocols.amp.BoxDispatcher.callRemote and callRemoteString will no longer return failing Deferreds for requiresAnswer=False commands when the transport they're operating on has been disconnected. (#9756) Improved Documentation ---------------------- - Added a missing hyphen to a reference to the ``--debug`` option of ``pdb`` in the Trial how-to. (#9690) - The documentation of the twisted.cred.checkers module has been extended and corrected. (#9724) Deprecations and Removals ------------------------- - twisted.news is deprecated. (#9405) Misc ---- - #9634, #9701, #9707, #9710, #9715, #9726, #9727, #9728, #9729, #9735, #9737, #9757 Conch ----- Features ~~~~~~~~ - twisted.conch.ssh now supports the curve25519-sha256 key exchange algorithm (requires OpenSSL >= 1.1.0). (#6814) - twisted.conch.ssh.keys can now write private keys in the new "openssh-key-v1" format, introduced in OpenSSH 6.5 and made the default in OpenSSH 7.8. ckeygen has a corresponding new --private-key-subtype=v1 option. (#9683) Bugfixes ~~~~~~~~ - twisted.conch.keys.Key.privateBlob now returns the correct blob format for ECDSA (i.e. the same as that implemented by OpenSSH). (#9682) Misc ~~~~ - #9760 Web --- Bugfixes ~~~~~~~~ - Fixed return type of twisted.web.http.Request.getUser and twisted.web.http.Request.getPassword to binary if no authorization header was found or an exception was thrown (#9596) - twisted.web.http.HTTPChannel now rejects requests (with status code 400 and a drop) that have malformed headers of the form "Foo : value" or ": value". (#9646) - twisted.web.http.Request now correctly parses multipart-encoded form data submitted as a chunked request on Python 3.7+. (#9678) - twisted.web.client.BrowserLikePolicyForHTTPS is now listed in __all__, since it's a user-facing class that anyone could import and extend. (#9769) - twisted.web.http was subject to several request smuggling attacks. Requests with multiple Content-Length headers were allowed (CVE-2020-10108, thanks to Jake Miller from Bishop Fox and ZeddYu Lu for reporting this) and now fail with a 400; requests with a Content-Length header and a Transfer-Encoding header honored the first header (CVE-2020-10109, thanks to Jake Miller from Bishop Fox for reporting this) and now fail with a 400; requests whose Transfer-Encoding header had a value other than "chunked" and "identity" (thanks to ZeddYu Lu) were allowed and now fail with a 400. (#9770) Mail ---- Misc ~~~~ - #9733 Words ----- Bugfixes ~~~~~~~~ - Fixed parsing of streams with Python 3.8 when there are spaces in namespaces or namespaced attributes in twisted.words.xish.domish.ExpatElementStream (#9730) Names ----- Bugfixes ~~~~~~~~ - twisted.names.secondary.SecondaryAuthority now accepts str for its domain parameter, so twist dns --secondary now functions on Python 3. (#9496) Twisted 19.10.0 (2019-11-03) ============================ Features -------- - twisted.trial.successResultOf, twisted.trial.failureResultOf, and twisted.trial.assertNoResult accept coroutines as well as Deferreds. (#9006) Bugfixes -------- - Fixed circular import in twisted.trial.reporter, introduced in Twisted 16.0.0. (#8267) - The POP3 server implemented by twisted.mail.pop3 now accepts passwords that contain spaces. (#9100) - Incoming HTTP/2 connections will now not time out if they persist for longer than one minute. (#9653) - The serial extra now requires pywin32 on Windows enabling use of twisted.internet.serialport without specifying the windows_platform extra. (#9700) Misc ---- - #8506, #9677, #9684, #9687, #9688 Conch ----- Bugfixes ~~~~~~~~ - twisted.conch.ssh.keys now correctly writes the "iqmp" parameter in serialized RSA private keys as q^-1 mod p rather than p^-1 mod q. (#9681) Misc ~~~~ - #9689 Web --- Features ~~~~~~~~ - twisted.web.server.Request will now use twisted.web.server.Site.getContentFile, if it exists, to get a file into which to write request content. If getContentFile is not provided by the site, it will fall back to the previous behavior of using io.BytesIO for small requests and tempfile.TemporaryFile for large ones. (#9655) Bugfixes ~~~~~~~~ - twisted.web.client.FileBodyProducer will now stop producing when the Deferred returned by FileBodyProducer.startProducing is cancelled. (#9547) - The HTTP/2 server implementation now enforces TCP flow control on control frame messages and times out clients that send invalid data without reading responses. This closes CVE-2019-9512 (Ping Flood), CVE-2019-9514 (Reset Flood), and CVE-2019-9515 (Settings Flood). Thanks to Jonathan Looney and Piotr Sikora. (#9694) Mail ---- No significant changes. Words ----- No significant changes. Names ----- No significant changes. Twisted 19.7.0 (2019-07-28) =========================== Features -------- - The callable argument to twisted.internet.task.deferLater() is no longer required. (#9577) - Twisted's minimum Cryptography requirement is now 2.5. (#9592) - twisted.internet.utils.getProcessOutputAndValue now accepts `stdinBytes` to write to the child process's standard input. (#9607) - Add new twisted.logger.capturedLogs context manager for capturing observed log events in tests. (#9617) - twisted.internet.base.PluggableResolverMixin, which implements the pluggable resolver interfaces for easier re-use in other reactors, has been factored out of ReactorBase. (#9632) - The PyPI page for Twisted has been enhanced to include more information and useful links. (#9648) Bugfixes -------- - twisted.internet.endpoints is now importable on Windows when pywin32 is not installed. (#6032) - twisted.conch.ssh now generates correct keys when using hmac-sha2-512 with SHA1 based KEX algorithms. (#8258) - twisted.internet.iocpreactor.abstract.FileHandle no longer duplicates/looses outgoing data when .write() is called in rapid succession with large payloads (#9446) - twisted.application.backoffPolicy will not fail on connection attempts > 1750 with default settings. (#9476) - Trial on Python 3 will now properly re-raise ImportErrors that occur during the import of a module, rather than saying the module doesn't exist. (#9628) - twisted.internet.process does not fail on import when the process has more than 1024 file descriptors opened. (#9636) - Add the stackLevel keyword argument to twisted.logger.STDLibLogObserver._findCaller to fix an incompatibility with Python 3.8. (#9668) Improved Documentation ---------------------- - Fix the incorrect docstring for twisted.python.components.Componentized.addComponent which stated that the function returned a list of interfaces, even though the function doesn't actually do so. (#9637) Deprecations and Removals ------------------------- - twisted.test.proto_helpers has moved to twisted.internet.testing. twisted.test.proto_helpers has been deprecated. (#6435) - twisted.protocols.mice, deprecated since Twisted 16.0, has been removed. (#9602) - twisted.conch.insults.client and twisted.conch.insults.colors, deprecated since Twisted 10.1, have been removed. (#9603) - The __version__ attribute of Twisted submodules that were previously packaged separately, deprecated since Twisted 16.0, has been removed. (#9604) - Python 3.4 is no longer supported. (#9613) - twisted.python.compat.OrderedDict, an alias for collections.OrderedDict and deprecated since Twisted 15.5, has been removed. (#9639) Misc ---- - #9217, #9445, #9454, #9605, #9614, #9615, #9619, #9625, #9633, #9640, #9674 Conch ----- Bugfixes ~~~~~~~~ - t.c.ssh.connection.SSHConnection now fails channels that are in the process of opening when the connection is lost. (#2782) Misc ~~~~ - #9610 Web --- Features ~~~~~~~~ - twisted.web.tap, the module that is run by `twist web`, now accepts --display-tracebacks to render tracebacks on uncaught exceptions. (#9656) Bugfixes ~~~~~~~~ - twisted.web.http.Request.write after the channel is disconnected will no longer raise AttributeError. (#9410) - twisted.web.client.Agent.request() and twisted.web.client.ProxyAgent.request() now produce TypeError when the method argument is not bytes, rather than failing to generate the request. (#9643) - twisted.web.http.HTTPChannel no longer raises TypeError internally when receiving a line-folded HTTP header on Python 3. (#9644) - All HTTP clients in twisted.web.client now raise a ValueError when called with a method and/or URL that contain invalid characters. This mitigates CVE-2019-12387. Thanks to Alex Brasetvik for reporting this vulnerability. (#9647) - twisted.web.server.Site's instance variable displayTracebacks is now set to False by default. (#9656) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - twisted.web.iweb.IRequest's "prepath" and "postpath" attributes, which have existed for a long time, are now documented. (#5533) - The documented type of t.w.iweb.IRequest's "method" and "uri" attributes on Python 3 has been corrected to match the implementation. (#9091) - t.w.iweb.IRequest's "args" attribute is now correctly documented to be bytes. (#9458) - The API documentation of twisted.web.iweb.IRequest and twisted.web.http.Request has been updated and extended to match the implementation. (#9593) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - Passing a path argument to twisted.web.resource.Resource.putChild which is not of type bytes is now deprecated. In the future, passing a non-bytes argument to putChild will return an error. (#9135) - Passing --notracebacks/-n to twisted.web.tap, the module that is run by `twist web`, is now deprecated due to traceback rendering being disabled by default. (#9656) Misc ~~~~ - #9597 Mail ---- No significant changes. Words ----- Features ~~~~~~~~ - twisted.words.protocols.jabber.xmlstream.TLSInitiatingInitializer and twisted.words.protocols.jabber.client.XMPPClientFactory now take an optional configurationForTLS for customizing certificate options for StartTLS. (#9561) Bugfixes ~~~~~~~~ - twisted.words.protocols.jabber.xmlstream.TLSInitiatingInitializer now properly verifies the server's certificate against platform CAs and the stream's domain, mitigating CVE-2019-12855. (#9561) Names ----- Bugfixes ~~~~~~~~ - twisted.names.client.Resolver will no longer infinite loop if it cannot bind a UDP port to use for resolving. (#9620) Twisted 19.2.0 (2019-04-07) =========================== This is the final release that will support Python 3.4. Features -------- - twisted.internet.ssl.CertificateOptions now uses 32 random bytes instead of an MD5 hash for the ssl session identifier context. (#9463) - DeferredLock and DeferredSemaphore can be used as asynchronous context managers on Python 3.5+. (#9546) - t.i.b.BaseConnector has custom __repr__ (#9548) - twisted.internet.ssl.optionsForClientTLS now supports validating IP addresses from the certificate subjectAltName (#9585) - Twisted's minimum Cryptography requirement is now 2.5. (#9592) Bugfixes -------- - twisted.web.proxy.ReverseProxyResource fixed documentation and example snippet (#9192) - twisted.python.failure.Failure.getTracebackObject now returns traceback objects whose frames can be passed into traceback.print_stack for better debugging of where the exception came from. (#9305) - twisted.internet.ssl.KeyPair.generate: No longer generate 1024-bit RSA keys by default. Anyone who generated a key with this method using the default value should move to replace it immediately. (#9453) - The message of twisted.internet.error.ConnectionAborted is no longer truncated. (#9522) - twisted.enterprise.adbapi.ConnectionPool.connect now logs only the dbapiName and not the connection arguments, which may contain credentials (#9544) - twisted.python.runtime.Platform.supportsINotify no longer considers the result of isDocker for its own result. (#9579) Improved Documentation ---------------------- - The documentation for the the twisted.internet.interfaces.IConsumer, IProducer, and IPullProducer interfaces is more detailed. (#2546) - The errback example in the docstring of twisted.logger.Logger.failure has been corrected. (#9334) - The sample code in the "Twisted Web In 60 Seconds" tutorial runs on Python 3. (#9559) Misc ---- - #8921, #9071, #9125, #9428, #9536, #9540, #9580 Conch ----- Features ~~~~~~~~ - twisted.conch.ssh.keys can now read private keys in the new "openssh-key-v1" format, introduced in OpenSSH 6.5 and made the default in OpenSSH 7.8. (#9515) Bugfixes ~~~~~~~~ - Conch now uses pyca/cryptography for Diffie-Hellman key generation and agreement. (#8831) Misc ~~~~ - #9584 Web --- Features ~~~~~~~~ - twisted.web.client.HostnameCachingHTTPSPolicy was added as a new contextFactory option. The policy caches a specified number of twisted.internet.interfaces.IOpenSSLClientConnectionCreator instances to to avoid the cost of instantiating a connection creator for multiple requests to the same host. (#9138) Bugfixes ~~~~~~~~ - twisted.web.http.Request.cookies, twisted.web.http.HTTPChannel.writeHeaders, and twisted.web.http_headers.Headers were all vulnerable to header injection attacks. They now replace linear whitespace ('\r', '\n', and '\r\n') with a single space. twisted.web.http.Reqeuest.cookies also replaces semicolons (';') with a single space. (#9420) - twisted.web.client.Request and twisted.web.client.HTTPClient were both vulnerable to header injection attacks. They now replace linear whitespace ('\r', '\n', and '\r\n') with a single space. (#9421) Mail ---- No significant changes. Words ----- No significant changes. Names ----- Features ~~~~~~~~ - twisted.names.dns now has IRecord implementations for the SSHFP and TSIG record types. (#9373) Twisted 18.9.0 (2018-10-10) =========================== Features -------- - twisted.internet._sslverify.ClientTLSOptions no longer raises IDNAError when given an IPv6 address as a hostname in a HTTPS URL. (#9433) - The repr() of a twisted.internet.base.DelayedCall now encodes the same information as its str(), exposing details of its scheduling and target callable. (#9481) - Python 3.7 is now supported. (#9502) Bugfixes -------- - twisted.logger.LogBeginner's default critical observer now prints tracebacks for new and legacy log system events through the use of the new eventAsText API. This API also does not raise an error for non-ascii encoded data in Python2, it attempts as well as possible to format the traceback. (#7927) - Syntax error under Python 3.7 fixed for twisted.conch.manhole and twisted.main.imap4. (#9384) - `trial -j` reports tracebacks on test failures under Python 3. (#9436) - Properly format multi-byte and non-ascii encoded data in a traceback. (#9456) - twisted.python.rebuild now functions on Python 3.7. (#9492) - HTTP/2 server connections will no longer time out active downloads that take too long. (#9529) Improved Documentation ---------------------- - Several minor formatting problems in the API documentation have been corrected. (#9461) - The documentation of twisted.internet.defer.Deferred.fromFuture() has been updated to reflect upstream changes. (#9539) Deprecations and Removals ------------------------- - async keyword argument is deprecated in twisted.conch.manhole (ManholeInterpreter.write and Manhole.add) and in twisted.main.imap4.IMAP4Server.sendUntaggedResponse, isAsync keyword argument is introduced instead. (#9384) Misc ---- - #9379, #9485, #9489, #9499, #9501, #9511, #9514, #9523, #9524, #9525, #9538 Conch ----- Bugfixes ~~~~~~~~ - twisted.conch.keys.Key.public returns the same twisted.conch.keys.Key instance when it is already a public key instead of failing with an exception. (#9441) - RSA private keys are no longer corrupted during loading, allowing OpenSSL's fast-path to operate for RSA signing. (#9518) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - The documentation for IConchUser.gotGlobalRequest() is more accurate. (#9413) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.conch.ssh.filetransfer.ClientDirectory's use as an iterator has been deprecated. (#9527) Web --- Bugfixes ~~~~~~~~ - twisted.web.server.Request.getSession now returns a new session if the previous session has expired. (#9288) Misc ~~~~ - #9479, #9480, #9482, #9491 Mail ---- No significant changes. Words ----- No significant changes. Names ----- No significant changes. Twisted 18.7.0 (2018-07-10) =========================== Features -------- - Cancelling a Deferred returned by twisted.internet.defer.inlineCallbacks now cancels the Deferred it is waiting on. (#4632) - twisted.application.internet.ClientService now accepts a function to initialize or validate a connection before it is returned by the whenConnected method as the prepareConnection argument. (#8375) - Traceback generated for twisted.internet.defer.inlineCallbacks now includes the full stack of inlineCallbacks generators between catcher and raiser (before it only contained raiser's stack). (#9176) - Add optional cwd argument to twisted.runner.procmon.ProcMon.addProcess (#9287) - twisted.python.failure.Failure tracebacks generated by coroutines scheduled with twisted.internet.defer.ensureDeferred - i.e. any Deferred-awaiting coroutine - now contain fewer extraneous frames from the trampoline implementation, and correctly indicate the source of exceptions raised in other call stacks - i.e. the function that raised the exception. In other words: if you 'await' a function that raises an exception, you'll be able to see where the error came from. (#9459) Bugfixes -------- - On UNIX-like platforms, Twisted attempts to recover from EMFILE when accepting connections on TCP and UNIX ports by shedding incoming clients. (#5368) - The documentation of IReactorTime.getDelayedCalls() has been corrected to indicate that the method returns a list, not a tuple. (#9418) - "python -m twisted web --help" now refers to "--listen" instead of the non-existing "--http" (#9434) - twisted.python.htmlizer.TokenPrinter now explicitly works on bytestrings. (#9442) - twisted.enterprise.adbapi.ConnectionPool.runWithConnection and runInteraction now use the reactor that is passed to ConnectionPool's constructor. (#9467) Improved Documentation ---------------------- - The Twisted Coding Standard now contains examples of how to mark up a feature as added in the next Twisted release. (#9460) Deprecations and Removals ------------------------- - Deprecate direct introspection of ProcMon's processes: processes should not be directly accessed or pickled. (#9287) - twisted.internet.address.IPv4Address._bwHack and twisted.internet.address.UNIXAddress._bwHack, as well as the parameters to the constructors, deprecated since Twisted 11.0, have been removed. (#9450) Misc ---- - #7495, #9399, #9406, #9411, #9425, #9439, #9449, #9450, #9452 Conch ----- Features ~~~~~~~~ - twisted.conch.ssh.transport.SSHTransportBase now includes Twisted's version in the software version string it sends to the server, allowing servers to apply compatibility workarounds for bugs in particular client versions. (#9424) Bugfixes ~~~~~~~~ - If the command run by twisted.conch.endpoints.SSHCommandClientEndpoint exits because of a delivered signal, the client protocol's connectionLost is now called with a ProcessTerminated exception instead of a ConnectionDone exception. (#9412) - twisted.conch.ssh.transport.SSHTransportBase now correctly handles MSG_DEBUG with a false alwaysDisplay field on Python 2 (broken since 8.0.0). (#9422) - twisted.conch.manhole.lastColorizedLine now does not throw a UnicodeDecodeError on non-ASCII input. (#9442) Web --- Features ~~~~~~~~ - Added support for SameSite cookies in ``http.Request.addCookie``. (#9387) Bugfixes ~~~~~~~~ - twisted.web.server.GzipEncoderFactory would sometimes fail to gzip requests if the Accept-Encoding header contained whitespace between the comma-separated acceptable encodings. It now trims whitespace before checking if gzip is an acceptable encoding. (#9086) - twisted.web.static.File renders directory listings on Python 2, including those with text paths. (#9438) - twisted.python.http.Request now correcly parses multipart bodies on Python 3.7. (#9448) - twisted.web.http.combinedLogFormatter (used by t.w.http.Server and t.w.server.Site) no longer produces DeprecationWarning about Request.getClientIP. (#9470) Misc ~~~~ - #9432, #9466, #9479, #9480 Mail ---- No significant changes. Words ----- No significant changes. Names ----- Misc ~~~~ - #9398 Twisted 18.4.0 (2018-04-13) =========================== Features -------- - The --port/--https arguments to web plugin are now deprecated, in favor of --listen. The --listen argument can be given multiple times to listen on multiple ports. (#6670) - Twisted now requires zope.interface 4.4.2 or higher across all platforms and Python versions. (#8149) - The osx_platform setuptools extra has been renamed to macos_platform, with the former name being a compatibility alias. (#8848) - Zsh completions are now provided for the twist command. (#9338) - twisted.internet.endpoints.HostnameEndpoint now has a __repr__ method which includes the host and port to which the endpoint connects. (#9341) Bugfixes -------- - twistd now uses the UID's default GID to initialize groups when --uid is given but --gid is not. This prevents an unhandled TypeError from being raised when os.initgroups() is called. (#4442) - twisted.protocols.basic.LineReceiver checks received lines' lengths against its MAX_LENGTH only after receiving a complete delimiter. A line ending in a multi-byte delimiter like '\r\n' might be split by the network, with the first part arriving before the rest; previously, LineReceiver erroneously disconnected if the first part, e.g. 'zzzz....\r' exceeded MAX_LENGTH. LineReceiver now checks received data against MAX_LENGTH plus the delimiter's length, allowing short reads to complete a line. (#6556) - twisted.protocols.basic.LineOnlyReceiver disconnects the transport after receiving a line that exceeds MAX_LENGTH, like LineReceiver. (#6557) - twisted.web.http.Request.getClientIP now returns the host part of the client's address when connected over IPv6. (#7704) - twisted.application.service.IService is now documented as requiring the 'running', 'name' and 'parent' attributes (the documentation previously implied they were required, but was unclear). (#7922) - twisted.web.wsgi.WSGIResource no longer raises an exception when a client connects over IPv6. (#8241) - When using TLS enable automatic ECDH curve selection on OpenSSL 1.0.2+ instead of only supporting P-256 (#9210) - twisted.trial._dist.worker and twisted.trial._dist.workertrial consistently pass bytes, not unicode to AMP. This fixes "trial -j" on Python 3. (#9264) - twisted.trial.runner now uses the 'importlib' module instead of the 'imp' module on Python 3+. This eliminates DeprecationWarnings caused by importing 'imp' on Python 3. (#9275) - twisted.web.client.HTTP11ClientProtocol now closes the connection when the server is sending a header line which is longer than he line limit of twisted.protocols.basic.LineReceiver.MAX_LENGTH. (#9295) - twisted.python.failure now handles long stacktraces better; in particular it will log tracebacks for stack overflow errors. (#9301) - The "--_shell-completion" argument to twistd now works on Python 3. (#9303) - twisted.python.failure.Failure now raises the wrapped exception in Python3, and self (Failure) n Python2 when trap() is called without a matching exception (#9307) - Writing large amounts of data no longer implies repeated, expensive copying under Python 3. Python 3's write speeds are now as fast as Python 2's. (#9324) - twisted.protocols.postfix now properly encodes errors which are unicode strings to bytes. (#9335) - twisted.protocols.policies.ProtocolWrapper and twisted.protocols.tls.TLSMemoryBIOProtocol no longer create circular references that keep protocol instances in memory after connection is closed. (#9374) - twisted.conch.ssh.transport.SSHTransportBase no longer strips trailing spaces from the SSH version string of the connected peer. (#9377) - `trial -j` no longer crashes on Python 2 on test failure messages containing non-ASCII bytes. (#9378) - RSA keys replaced with 2048bit ones in twisted.conch.test.keydata in order to be compatible with OpenSSH 7.6. (#9388) - AsyncioSelectorReactor uses the global policy's event loop. asyncio libraries that retrieve the running event loop with get_event_loop() will now receive the one used by AsyncioSelectorReactor. (#9390) Improved Documentation ---------------------- - public attributes of `twisted.logger.Logger` are now documented as attributes. (#8157) - List indentation formatting errors have been corrected throughout the documentation. (#9256) Deprecations and Removals ------------------------- - twisted.protocols.basic.LineOnlyReceiver.lineLengthExceeded no longer returns twisted.internet.error.ConnectionLost. It instead directly disconnects the transport and returns None. (#6557) - twisted.python.win32.getProgramsMenuPath and twisted.python.win32.getProgramFilesPath were deprecated in Twisted 15.3.0 and have now been removed. (#9312) - Python 3.3 is no longer supported. (#9352) Misc ---- - #7033, #8887, #9204, #9289, #9291, #9292, #9293, #9302, #9336, #9355, #9356, #9364, #9375, #9381, #9382, #9389, #9391, #9393, #9394, #9396 Conch ----- Bugfixes ~~~~~~~~ - twisted.plugins.cred_unix now properly converts a username and password from bytes to str on Python 3. In addition, passwords which are encrypted with SHA512 and SH256 are properly verified. This fixes running a conch server with: "twistd -n conch -d /etc/ssh/ --auth=unix". (#9130) - In twisted.conch.scripts.conch, on Python 3 do not write bytes directly to sys.stderr. On Python 3, this fixes remote SSH execution of a command which fails. (#9344) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.conch.ssh.filetransfer.FileTransferClient.wasAFile attribute has been removed as it serves no purpose. (#9362) - Removed deprecated support for PyCrypto key objects in conch (#9368) Web --- Features ~~~~~~~~ - The new twisted.iweb.IRequest.getClientAddress returns the IAddress provider representing the client's address. Callers should check the type of the returned value before using it. (#7707) - Eliminate use of twisted.python.log in twisted.web modules. (#9280) Bugfixes ~~~~~~~~ - Scripts ending with .rpy, .epy, and .cgi now execute properly in Twisted Web on Python 3. (#9271) - twisted.web.http.Request and twisted.web.server.Request are once again hashable on Python 2, fixing a regression introduced in Twisted 17.5.0. (#9314) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - Correct reactor docstrings for twisted.web.client.Agent and twisted.web.client._StandardEndpointFactory to communicate interface requirements since 17.1. (#9274) - The examples for the "Twisted Web in 60 Seconds" tutorial have been fixed to work on Python 3. (#9285) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.iweb.IRequest.getClientIP is deprecated. Use twisted.iweb.IRequest.getClientAddress instead (see #7707). (#7705) - twisted.web.iweb.IRequest.getClient and its implementations (deprecated in #2552) have been removed. (#9395) Mail ---- Bugfixes ~~~~~~~~ - twistd.mail.scripts.mailmail has been ported to Python 3. (#8487) - twisted.mail.bounce now works on Python 3. (#9260) - twisted.mail.pop3 and twisted.mail.pop3client now work on Python 3. (#9269) - SMTP authentication in twisted.mail.smtp now works better on Python 3, due to improved improved bytes vs unicode handling. (#9299) Misc ~~~~ - #9310 Words ----- No significant changes. Names ----- No significant changes. Twisted 17.9.0 (2017-09-23) =========================== This is the last Twisted release where Python 3.3 is supported, on any platform. Features -------- - twisted.python.failure.Failure is now a new-style class which subclasses BaseException. (#5519) - twisted.internet.posixbase.PosixReactorBase.adoptStreamPort and twisted.internet.posixbase.PosixReactorBase.adoptStreamConnection now support AF_UNIX SOCK_STREAM sockets. (#5573) - (#8940) - t.protocol.policies.TimeoutMixin.setTimeout and t.protocol.policies.TimeoutProtocol.cancelTimeout (used in t.protocol.policies.TimeoutFactory) no longer raise a t.internet.error.AlreadyCancelled exception when calling them for an already cancelled timeout. (#9131) - twisted.web.template.flatten now supports coroutines that yield Deferreds. (#9199) - twisted.web.client.HTTPConnectionPool passes the repr() of the endpoint to the client protocol factory, and the protocol factory adds that to its own repr(). This makes logs more useful. (#9235) - Python 3.6 is now supported (#9240) Bugfixes -------- - twisted.python.logfile.BaseLogFile and subclasses now always open the file in binary mode, and will process text as UTF-8. (#6938) - The `ssl:` endpoint now accepts `certKey` PEM files without trailing newlines. (#7530) - Logger.__init__ sets the namespace to "" instead of raising KeyError when unable to determine the namespace from the calling context. (#7930) - twisted.internet._win32serialport updated to support pySerial 3.x and dropped pySerial 2.x support. (#8159) - twisted.python.rebuild now works on Python 3. (#8213) - twisted.web.server.Request.notifyFinish will now once again promptly notify applications of client disconnection (assuming that the client doesn't send a large amount of pipelined request data) rather than waiting for the timeout; this fixes a bug introduced in Twisted 16.3.0. (#8692) - twisted.web.guard.HTTPAuthSessionWrapper configured with DigestCredentialFactory now works on both Python 2 and 3. (#9127) - Detect when we’re being run using “-m twisted” or “-m twisted.trial” and use it to build an accurate usage message. (#9133) - twisted.protocols.tls.TLSMemoryBIOProtocol now allows unregisterProducer to be called when no producer is registered, bringing it in line with other transports. (#9156) - twisted.web web servers no longer print tracebacks when they timeout clients that do not respond to TLS CLOSE_NOTIFY messages. (#9157) - twisted.mail.imap4 now works on Python 3. (#9161) - twisted.python.shortcut now works on Python 3 in Windows. (#9170) - Fix traceback forwarding with inlineCallbacks on python 3. (#9175) - twisted.mail.imap4.MessageSet now treats * as larger than every message ID, leading to more consistent and robust behavior. (#9177) - The following plugins can now be used on Python 3 with twistd: dns, inetd, portforward, procmon, socks, and words. (#9184) - twisted.internet._win32serialport now uses serial.serialutil.to_bytes() to provide bytes in Python 3. (#9186) - twisted.internet.reactor.spawnProcess() now does not fail on Python 3 in Windows if passed a bytes-encoded path argument. (#9200) - twisted.protocols.ident now works on Python 3. (#9221) - Ignore PyPy's implementation differences in base object class. (#9225) - twisted.python.test.test_setup now passes with setuptools 36.2.1 (#9231) - twisted.internet._win32serialport SerialPort._clearCommError() no longer raises AttributeError (#9252) - twisted.trial.unittest.SynchronousTestCase and twisted.trial.unittest.TestCase now always run their tearDown methods, even when a test method fails with an exception. They also flush all errors logged by a test method before running another, ensuring the logged errors are associated with their originating test method. (#9267) Improved Documentation ---------------------- - Trial's documentation now directly mentions the preferred way of running Trial, via "python -m twisted.trial". (#9052) - twisted.internet.endpoints.HostnameEndpoint and twisted.internet.endpoints.TCP4Client endpoint documentation updated to correctly reflect that the timeout argument takes a float as well as an int. (#9151) - Badges at top of README now correctly render as links to respective result pages on GitHub. (#9216) - The example code for the trial tutorial is now compatible with Python3 and the current version of Twisted. (#9223) Deprecations and Removals ------------------------- - twisted.protocols.dict is deprecated. (#9141) - gpsfix.py has been removed from the examples. It uses twisted.protocols.gps which was removed in Twisted 16.5.0. (#9253) - oscardemo.py, which illustrates the use of twisted.words.protocols.oscar, as been removed. twisted.words.protocols.oscar was removed in Twisted 17.5.0. (#9255) Misc ---- - #5949, #8566, #8650, #8944, #9159, #9160, #9162, #9196, #9219, #9228, #9229, #9230, #9247, #9248, #9249, #9251, #9254, #9262, #9276, #9308 Conch ----- Bugfixes ~~~~~~~~ - twisted.conch.ssh.userauth.SSHUserAuthServer now gracefully handles unsupported authentication key types. (#9139) - twisted.conch.client.default verifyHostKey now opens /dev/tty with no buffer to be compatible with Python 3. This lets the conch cli work with Python 3. (#9265) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.conch.ssh._cryptography_backports has been removed in favor of using int_to_bytes() and int_from_bytes() from cryptography.utils. (#9263) Misc ~~~~ - #9158, #9272 Web --- Features ~~~~~~~~ - twisted.web.static.File.contentTypes is now documented. (#5739) - twisted.web.server.Request and any Twisted web server using it now support automatic fast responses to HTTP/1.1 and HTTP/2 OPTIONS * requests, and reject any other verb using the * URL form. (#9190) - --add-header "HeaderName: Value" can be passed to twist web in order to set extra headers on all responses (#9241) Bugfixes ~~~~~~~~ - twisted.web.client.HTTPClientFactory(...).gotHeaders(...) now handles a wrong Set-Cookie header without a traceback. (#9136) - twisted.python.web.http.HTTPFactory now always opens logFile in binary mode and writes access logs in UTF-8, to avoid encoding issues and newline differences on Windows. (#9143) - The code examples in "Using the Twisted Web Client" now work on Python 3. (#9172) - twisted.web.server.Request and all web servers that use it now no longer send a default Content-Type header on responses that do not have a body (i.e. that set Content-Length: 0 or that send a 204 status code). (#9191) - twisted.web.http.Request and all subclasses now correctly fire Deferreds returned from notifyFinish with errbacks when errors are encountered in HTTP/2 streams. (#9208) - twisted.web.microdom, twisted.web.domhelpers, and twisted.web.sux now work on Python 3. (#9222) Mail ---- Bugfixes ~~~~~~~~ - Sending a list of recipients with twisted.smtp.SenderFactory has been fixed. This fixes a problem found when running buildbot. (#9180) - twisted.mail.imap4.IMAP4Server parses empty string literals even when they are the last argument to a command, such as LOGIN. (#9207) Words ----- Bugfixes ~~~~~~~~ - twisted.words.tap has been ported to Python 3 (#9169) Misc ~~~~ - #9246 Names ----- Bugfixes ~~~~~~~~ - Queries for unknown record types no longer incorrectly result in a server error. (#9095) - Failed TCP connections for AFXR queries no longer raise an AttributeError. (#9174) Twisted 17.5.0 (2017-06-04) =========================== Bugfixes -------- - spawnProcess no longer opens an unwanted console on Windows (#5726) - The transition to the hyperlink package adds IPv6 support to twisted.python.url.URL. This is now deprecated and new code should use hyperlink directly (see #9126). (#8069) - twisted.logger now buffers only 200 events by default (reduced from 65536) while waiting for observers to be configured. (#8164) - The transition of twisted.python.url to using the hyperlink package enables a URL.click() with no arguments (or 0-length string argument) to resolve dot segments in the path. (#8184) - twisted.protocols.finger now works on Python 3. (#8230) - TLS-related tests now pass when run with OpenSSL 1.1.0. This makes tests pass again on macOS and Windows, as cryptography 1.8 and later include OpenSSL 1.1.0. (#8898) - UNIX socket endpoints now process all messages from recvmsg's ancillary data via twisted.internet.unix.Server.doRead/twisted.internet.unix.Client.doRead, while discarding and logging ones that don't contain file descriptors. (#8912) - twisted.internet.endpoints.HostnameEndpoint and twisted.web.client.Agent work again with reactors that do not provide IReactorPluggableNameResolver. This undoes the changes that broke downstream users such as treq.testing. Note that passing reactors that do not provide IReactorPluggableNameResolver to either is deprecated. (#9032) - A Python 3 Perspective Broker server which receives a remote call with keyword arguments from a Python 2 client will now decode any keys which are binary to strings instead of crashing. This fixes interoperability between Python 2 Buildbot clients and Python 3 Buildbot servers. (#9047) - twisted.internet._threadedselect now works on both Python 2 and 3. (#9053) - twisted.internet.interfaces.IResolverSimple implementers will now always be passed bytes, properly IDNA encoded if required, on Python 2. On Python 3, they will now be passed correctly IDNA-encoded Unicode forms of the domain, taking advantage of the idna library from PyPI if possible. This is to avoid Python's standard library (which has an out of date idna module) from mis- encoding domain names when non-ASCII Unicode is passed to it. (#9137) Improved Documentation ---------------------- - The examples in Twisted howto "Using the Twisted Application Framework", section "Customizing twistd logging" have been updated to use latest logging modules and syntax (#9084) Features -------- - twisted.internet.defer.Deferred.asFuture and twisted.internet.defer.Deferred.fromFuture were added, allowing for easy transitions between asyncio coroutines (which await Futures) and twisted coroutines (which await Deferreds). (#8748) - twisted.application.internet.ClientService.whenConnected now accepts an argument "failAfterFailures". If you set this to 1, the Deferred returned by whenConnected will errback when the connection attempt fails, rather than retrying forever. This lets you react (probably by stopping the ClientService) to connection errors that are likely to be persistent, such as using the wrong hostname, or not being connected to the internet at all. (#9116) - twisted.protocols.tls.TLSMemoryBIOProtocol and anything that uses it indirectly including the TLS client and server endpoints now enables TLS 1.3 cipher suites. (#9128) Misc ---- - #8133, #8995, #8997, #9003, #9015, #9021, #9026, #9027, #9049, #9057, #9062, #9065, #9069, #9070, #9072, #9074, #9075, #9111, #9117, #9140, #9144, #9145 Deprecations and Removals ------------------------- - twisted.runner.inetdconf.InvalidRPCServicesConfError, twisted.runner.inetdconf.RPCServicesConf, twisted.runner.inetdtap.RPCServer, and twisted.runner.portmap, deprecated since 16.2.0, have been removed. (#8464) - twisted.python.url and twisted.python._url were modified to rely on hyperlink, a new package based on the Twisted URL implementation. Hyperlink adds support for IPv6 (fixing #8069), correct username/password encoding, better scheme/netloc inference, improved URL.click() behavior (fixing #8184), and more. For full docs see hyperlink.readthedocs.io and the CHANGELOG in the hyperlink GitHub repo. (#9126) Conch ----- Bugfixes ~~~~~~~~ - History-aware terminal protocols like twisted.conch.manhole.Manhole no longer raise a TypeError when a user visits a partial line they added to the command line history by pressing up arrow before return. (#9031) - The telnet_echo.tac example had conflicting port callouts between runtime and documentation. File was altered to run on documented port, 6023. (#9055) Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - Remove diffie-hellman-group1-sha1 from twisted.conch. See https://weakdh.org/ (#9019) - Removed small and obscure elliptic curves from conch. The only curves conch supports now are the ones also supported by OpenSSH. (#9088) Mail ---- Bugfixes ~~~~~~~~ - twisted.mail.smtp has been ported to Python 3. (#8770) Names ----- Bugfixes ~~~~~~~~ - RRHeader now converts its ttl argument to an integer, raising a TypeError if it cannot. (#8340) Web --- Bugfixes ~~~~~~~~ - twisted.web.cgi now works on Python 3 (#8009) - twisted.web.distrib now works on Python 3 (#8010) - twisted.web.http.HTTPFactory now propagates its reactor's callLater method to the HTTPChannel object, rather than having callLater grab the global reactor. This prevents the possibility of HTTPFactory logging using one reactor, but HTTPChannel running timeouts on another. (#8904) Improved Documentation ~~~~~~~~~~~~~~~~~~~~~~ - twisted.web.template.flattenString docstring now correctly references io.BytesIO (rather than NativeStringIO). (#9028) Features ~~~~~~~~ - twisted.web.client now exposes the RequestGenerationFailed exception type. (#5310) - twisted.web.client.Agent will now parse responses that begin with a status line that is missing a phrase. (#7673) - twisted.web.http.HTTPChannel and twisted.web._http2.H2Connection have been enhanced so that after they time out they wait a small amount of time to allow the connection to close gracefully and, if it does not, they forcibly close it to avoid allowing malicious clients to forcibly keep the connection open. (#8902) Misc ~~~~ - #8981, #9018, #9067, #9090, #9092, #9093, #9096 Words ----- Deprecations and Removals ~~~~~~~~~~~~~~~~~~~~~~~~~ - twisted.words.protocols.oscar, which is client code for Oscar/ICQ, was deprecated in 16.2.0 and has now been removed. (#9024) Twisted Core 17.1.0 (2017-02-04) ================================ Features -------- - Added a new interface, twisted.internet.interfaces.IHostnameResolver, which is an improvement to twisted.internet.interfaces.IResolverSimple that supports resolving multiple addresses as well as resolving IPv6 addresses. This is a native, asynchronous, Twisted analogue to getaddrinfo. (#4362) - twisted.web.client.Agent now uses HostnameEndpoint internally; as a consequence, it now supports IPv6, as well as making connections faster and more reliably to hosts that have more than one DNS name. (#6712) - twisted.internet.ssl.CertificateOptions now has the new constructor argument 'raiseMinimumTo', allowing you to increase the minimum TLS version to this version or Twisted's default, whichever is higher. The additional new constructor arguments 'lowerMaximumSecurityTo' and 'insecurelyLowerMinimumTo' allow finer grained control over negotiated versions that don't honour Twisted's defaults, for working around broken peers, at the cost of reducing the security of the TLS it will negotiate. (#6800) - twisted.internet.ssl.CertificateOptions now sets the OpenSSL context's mode to MODE_RELEASE_BUFFERS, which will free the read/write buffers on idle TLS connections to save memory. (#8247) - trial --help-reactors will only list reactors which can be imported. (#8745) - twisted.internet.endpoints.HostnameEndpoint now uses the passed reactor's implementation of twisted.internet.interfaces.IReactorPluggableResolver to resolve hostnames rather than its own deferToThread/getaddrinfo wrapper; this makes its hostname resolution pluggable via a public API. (#8922) - twisted.internet.reactor.spawnProcess now does not emit a deprecation warning on Unicode arguments. It will encode Unicode arguments down to bytes using the filesystem encoding on UNIX and Python 2 on Windows, and pass Unicode through unchanged on Python 3 on Windows. (#8941) - twisted.trial._dist.test.test_distreporter now works on Python 3. (#8943) Bugfixes -------- - trial --help-reactors will now display iocp and win32er reactors with Python 3. (#8745) - twisted.logger._flatten.flattenEvent now handles log_format being None instead of assuming the value is always a string. (#8860) - twisted.protocol.ftp is now Python 3 compatible (#8865) - twisted.names.client.Resolver can now resolve names with IPv6 DNS servers. (#8877) - twisted.application.internet.ClientService now waits for existing connections to disconnect before trying to connect again when restarting. (#8899) - twisted.internet.unix.Server.doRead and twisted.internet.unix.Client.doRead no longer fail if recvmsg's ancillary data contains more than one file descriptor. (#8911) - twist on Python 3 now correctly prints the help text when given no plugin to run. (#8918) - twisted.python.sendmsg.sendmsg no longer segfaults on Linux + Python 2. (#8969) - IHandshakeListener providers connected via SSL4ClientEndpoint will now have their handshakeCompleted methods called. (#8973) - The twist script now respects the --reactor option. (#8983) - Fix crash when using SynchronousTestCase with Warning object which does not store a string as its first argument (like libmysqlclient). (#9005) - twisted.python.compat.execfile() does not open files with the deprecated 'U' flag on Python 3. (#9012) Deprecations and Removals ------------------------- - twisted.internet.ssl.CertificateOption's 'method' constructor argument is now deprecated, in favour of the new 'raiseMinimumTo', 'lowerMaximumSecurityTo', and 'insecurelyLowerMinimumTo' arguments. (#6800) - twisted.protocols.telnet (not to be confused with the supported twisted.conch.telnet), deprecated since Twisted 2.5, has been removed. (#8925) - twisted.application.strports.parse, as well as the deprecated default arguments in strports.service/listen, deprecated since Twisted 10.2, has been removed. (#8926) - twisted.web.client.getPage and twisted.web.client.downloadPage have been deprecated in favour of https://pypi.org/project/treq and twisted.web.client.Agent. (#8960) - twisted.internet.defer.timeout is deprecated in favor of twisted.internet.defer.Deferred.addTimeout (#8971) Other ----- - #7879, #8583, #8764, #8809, #8859, #8906, #8910, #8913, #8916, #8934, #8945, #8949, #8950, #8952, #8953, #8959, #8962, #8963, #8967, #8975, #8976, #8993, #9013 Twisted Conch 17.1.0 (2017-02-04) ================================= Features -------- - twisted.conch.manhole now works on Python 3. (#8327) - Twisted Conch now supports ECDH key exchanges. (#8730) - Add support in twisted.conch.ssh for hmac-sha2-384 (#8784) - conch and cftp scripts now work on Python 3. (#8791) - twisted.conch.ssh supports ECDH key exchange. (#8811) Bugfixes -------- - twisted.conch.ssh.keys.Key.fromString now supports OpenSSL private keys with Windows line endings (\r\n) again (broken since 16.6.0). (#8928) Improved Documentation ---------------------- - The documentation for twisted.conch.endpoints.SSHCommandClientEndpoint.existingConnection now describes where the value for the connection parameter might come from. (#8892) Other ----- - #8890, #8894, #8957, #8958, #8968 Twisted Mail 17.1.0 (2017-02-04) ================================ Deprecations and Removals ------------------------- - twisted.mail.tap (the twist plugin for mail) no longer accepts the --pop3s option or implicit port numbers to --pop3 and --smtp. This functionality has been deprecated since 11.0. (#8920) Twisted Names 17.1.0 (2017-02-04) ================================= Bugfixes -------- - twisted.names.authority.BindAuthority has been ported to Python 3. (#8880) Twisted News 17.1.0 (2017-02-04) ================================ No significant changes have been made for this release. Twisted Pair 17.1.0 (2017-02-04) ================================ No significant changes have been made for this release. Twisted Runner 17.1.0 (2017-02-04) ================================== Bugfixes -------- - On Python 3, procmon now handles process output without exceptions (#8919) Twisted Web 17.1.0 (2017-02-04) =============================== Features -------- - twisted.web.client.Agent now sets ``Content-Length: 0`` for PUT and POST requests made without a body producer. (#8984) Bugfixes -------- - twisted.web.http.HTTPFactory now times connections out after one minute of no data from the client being received, before the request is complete, rather than twelve hours. (#3746) - twisted.web.http.HTTPChannel, the server class for Twisted's HTTP/1.1 server, now exerts backpressure against clients that do not read responses. This means that if a client stops reading from a socket for long enough, Twisted will stop reading further requests from that client until it consumes some responses. (#8868) - twisted.web.http_headers.Headers.getRawHeaders no longer attempts to decode the default value when called with a unicode header name. (#8974) - twisted.web.http.HTTPChannel is less likely to leak file descriptors when timing out clients using HTTPS connections. In some cases it is still possible to leak a file descriptor when timing out HTTP clients: further patches will address this issue. (#8992) Other ----- - #7744, #8909, #8935 Twisted Words 17.1.0 (2017-02-04) ================================= No significant changes have been made for this release. Twisted Core 16.6.0 (2016-11-17) ================================ Features -------- - The twist script can now be run by invoking python -m twisted. (#8657) - twisted.protocols.sip has been ported to Python 3. (#8669) - twisted.persisted.dirdbm has been ported to Python 3. (#8888) Bugfixes -------- - twisted.internet.defer.Deferred now implements send, not __send__, which means that it is now a conforming generator. (#8861) - The IOCP reactor no longer transmits the contents of uninitialized memory when writing large amounts of data. (#8870) - Deferreds awaited/yielded from in a twisted.internet.defer.ensureDeferred wrapped coroutine will now properly raise exceptions. Additionally, it more closely models asyncio.ensure_future and will pass through Deferreds. (#8878) - Deferreds that are paused or chained on other Deferreds will now return a result when yielded/awaited in a twisted.internet.defer .ensureDeferred-wrapped coroutine, instead of returning the Deferred it was chained to. (#8890) Improved Documentation ---------------------- - twisted.test.proto_helpers is now explicitly covered by the compatibility policy. (#8857) Other ----- - #8281, #8823, #8862 Twisted Conch 16.6.0 (2016-11-17) ================================= Features -------- - twisted.conch.ssh.keys supports ECDSA keys (#8798) - scripts/ckeygen can now generate ecdsa keys. (#8828) - ckeygen has been ported to Python 3 (#8855) Deprecations and Removals ------------------------- - twisted.conch.ssh no longer uses gmpy, if available. gmpy is unmaintained, does not have binary wheels for any platforms, and an alternative for higher performance is available in the form of PyPy. (#8079) Twisted Mail 16.6.0 (2016-11-17) ================================ No significant changes have been made for this release. Twisted Names 16.6.0 (2016-11-17) ================================= No significant changes have been made for this release. Twisted News 16.6.0 (2016-11-17) ================================ No significant changes have been made for this release. Twisted Pair 16.6.0 (2016-11-17) ================================ No significant changes have been made for this release. Twisted Runner 16.6.0 (2016-11-17) ================================== No significant changes have been made for this release. Twisted Web 16.6.0 (2016-11-17) =============================== Features -------- - twisted.web.server.Site's HTTP/2 server support now emits vastly fewer WINDOW_UPDATE frames than previously. (#8681) Bugfixes -------- - twisted.web.Agent now tolerates receiving unexpected status codes in the 100 range by discarding them, which is what RFC 7231 recommends doing. (#8885) - twisted.web._http.H2Stream's getHost and getPeer implementations now actually return the host and peer instead of None. (#8893) Twisted Words 16.6.0 (2016-11-17) ================================= Features -------- - twisted.words.protocols.irc has been ported to Python 3 (#6320) Twisted Core 16.5.0 (2016-10-28) ================================ Features -------- - Added twisted.internet.defer.Deferred.addTimeout method to enable timeouts of deferreds. (#5786) - Perspective Broker (the twisted.spread package) has been ported to Python 3 (#7598) - 'yield from' can now be used on Deferreds inside generators, when the generator is wrapped with twisted.internet.defer.ensureDeferred. (#8087) - twisted.internet.asyncioreactor has been added, which is a Twisted reactor on top of Python 3.4+'s native asyncio reactor. It can be selected by passing "--reactor=asyncio" to Twisted tools (twistd, Trial, etc) on platforms that support it (Python 3.4+). (#8367) - twisted.python.zippath now works on Windows with Python 3. (#8747) - twisted.internet.cfreactor is ported to Python 3 and supported on 2.7 and 3.5+. (#8838) Bugfixes -------- - twisted.internet.test.test_iocp and twisted.internet.test.test_tcp have been fixed to work under Python 3 with the Windows IOCP reactor (#8631) - Arguments to processes on Windows are now passed mbcs-encoded arguments. This prevents process-related tests from hanging on Windows with Python 3. (#8735) - Client and server TLS connections made via the client TLS endpoint and the server SSL endpoint, as well as any other code that uses twisted.internet.ssl.CertificateOptions, no longer accept 3DES- based cipher suites by default, to defend against SWEET32. (#8781) - twisted.logger.jsonFileLogObserver no longer emits non-JSON tracebacks into its file; additionally, twisted.logger.formatEventAsClassicLogText now includes traceback information for the log event it formats. (#8858) - twisted.python.version now exports a version of Incremental that is 16.10.1 or higher, making t.p.v.Version package name comparisons case-insensitive. (#8863) - twisted.python.reflect.safe_str encodes unicode as ascii with backslashreplace error handling on Python 2. (#8864) Improved Documentation ---------------------- - The twisted.internet.interfaces.IProtocol.dataReceived() method takes one parameter of type bytes. This has been clarified in the doc string. (#8763) Deprecations and Removals ------------------------- - twisted.python.constants is deprecated in preference to constantly on PyPI, which is the same code rolled into its own package. (#7351) - twisted.python.dist3 has been made private API. (#8761) - When the source code is checked out, bin/trial is no longer in the tree. Developers working on the Twisted source code itself should either (1) run all tests under tox, or (2) run 'python setup.py develop' to install trial before running any tests. (#8765) - twisted.protocols.gps, deprecated since Twisted 15.2, has been removed. (#8787) Other ----- - #4926, #7868, #8209, #8214, #8271, #8308, #8324, #8348, #8367, #8377, #8378, #8379, #8380, #8381, #8383, #8385, #8387, #8388, #8389, #8391, #8392, #8393, #8394, #8397, #8406, #8410, #8412, #8413, #8414, #8421, #8425, #8426, #8430, #8432, #8434, #8435, #8437, #8438, #8439, #8444, #8451, #8452, #8453, #8454, #8456, #8457, #8459, #8462, #8463, #8465, #8468, #8469, #8479, #8482, #8483, #8486, #8490, #8493, #8494, #8496, #8497, #8498, #8499, #8501, #8503, #8504, #8507, #8508, #8510, #8513, #8514, #8515, #8516, #8517, #8520, #8521, #8522, #8523, #8524, #8527, #8528, #8529, #8531, #8532, #8534, #8536, #8537, #8538, #8543, #8544, #8548, #8552, #8553, #8554, #8555, #8557, #8560, #8563, #8565, #8568, #8569, #8572, #8573, #8574, #8580, #8581, #8582, #8586, #8589, #8590, #8592, #8593, #8598, #8603, #8604, #8606, #8609, #8615, #8616, #8617, #8618, #8619, #8621, #8622, #8624, #8627, #8628, #8630, #8632, #8634, #8640, #8644, #8645, #8646, #8647, #8662, #8664, #8666, #8668, #8671, #8672, #8677, #8678, #8684, #8691, #8702, #8705, #8706, #8716, #8719, #8724, #8725, #8727, #8734, #8741, #8749, #8752, #8754, #8755, #8756, #8757, #8758, #8767, #8773, #8776, #8779, #8780, #8785, #8788, #8789, #8790, #8792, #8793, #8799, #8808, #8817, #8839, #8845, #8852 Twisted Conch 16.5.0 (2016-10-28) ================================= Features -------- - SSH key fingerprints can be generated using base64 encoded SHA256 hashes. (#8701) Bugfixes -------- - SSHUserAuthServer does not crash on keyboard interactive authentication when running on Python 3 (#8771) - twisted.conch.insults.insults.ServerProtocol no longer corrupts a client's display when attempting to set the cursor position, and its ECMA-48 terminal manipulation works on Python 3. (#8803) Other ----- - #8495, #8511, #8715, #8851 Twisted Mail 16.5.0 (2016-10-28) ================================ Deprecations and Removals ------------------------- - twisted.mail.protocols.DomainSMTP and DomainESMTP, deprecated since 2003, have been removed. (#8772) Other ----- - #6289, #8525, #8786, #8830 Twisted Names 16.5.0 (2016-10-28) ================================= No significant changes have been made for this release. Other ----- - #8625, #8663 Twisted News 16.5.0 (2016-10-28) ================================ No significant changes have been made for this release. Twisted Pair 16.5.0 (2016-10-28) ================================ Features -------- - twisted.pair has been ported to Python 3 (#8744) Twisted Runner 16.5.0 (2016-10-28) ================================== No significant changes have been made for this release. Twisted Web 16.5.0 (2016-10-28) =============================== Bugfixes -------- - twisted.web.client.HTTPConnectionPool and anything that uses it, like twisted.web.client.Agent, have had their logic for resuming transports changed so that transports are resumed after state machine transitions are complete, rather than before. This change allows the HTTP client infrastructure to work with alternative HTTP implementations such as HTTP/2 which may be able to deliver a complete response synchronously when producing is resumed. (#8720) Other ----- - #8519, #8530, #8629, #8707, #8777, #8778, #8844 Twisted Words 16.5.0 (2016-10-28) ================================= No significant changes have been made for this release. Other ----- - #8360, #8460 Twisted Core 16.4.1 (2016-09-07) ================================ Features -------- - Client and server TLS connections made via the client TLS endpoint and the server SSL endpoint, as well as any other code that uses twisted.internet.ssl.CertificateOptions, now support ChaCha20 ciphers when available from the OpenSSL on the system. (#8760) Bugfixes -------- - Client and server TLS connections made via the client TLS endpoint and the server SSL endpoint, as well as any other code that uses twisted.internet.ssl.CertificateOptions, no longer accept 3DES- based cipher suites by default, to defend against SWEET32. (#8781) Twisted Conch 16.4.1 (2016-09-07) ================================= No significant changes have been made for this release. Twisted Mail 16.4.1 (2016-09-07) ================================ No significant changes have been made for this release. Twisted Names 16.4.1 (2016-09-07) ================================= No significant changes have been made for this release. Twisted News 16.4.1 (2016-09-07) ================================ No significant changes have been made for this release. Twisted Pair 16.4.1 (2016-09-07) ================================ No significant changes have been made for this release. Twisted Runner 16.4.1 (2016-09-07) ================================== No significant changes have been made for this release. Twisted Web 16.4.1 (2016-09-07) =============================== No significant changes have been made for this release. Twisted Words 16.4.1 (2016-09-07) ================================= No significant changes have been made for this release. Twisted Core 16.4.0 (2016-08-25) ================================ Features -------- - Add twisted.application.twist, meant to eventually replace twistd with a simpler interface. Add twisted.application.runner API, currently private, which twist is built on. (#5705) - The new interface IHandshakeListener that can be implemented by any Protocol provides a callback that is called when the TLS handshake has been completed, allowing Protocols to make decisions about the TLS configuration before application data is sent. (#6024) - twisted.python.syslog has been ported to Python 3. (#7957) - twisted.internet.defer.ensureDeferred has been added, similar to asyncio's ensure_future. Wrapping a coroutine (the result of a function defined using async def, available only on Python 3.5+) with it allows you to use the "await" keyword with Deferreds inside the coroutine, similar to "yield" when using inlineCallbacks. (#8088) - twisted.internet.inotify have been ported to Python 3 (#8211) - twisted.enterprise has been ported to Python 3. The third-party pysqlite2 package has not been ported to Python 3, so any database connector based on pysqlite2 cannot be used. Instead the sqlite3 module included with Python 3 should be used. (#8303) - Scripts such as cftp, ckeygen, conch, mailmail, pyhtmlizer, tkconch, twistd and trial have been updated to be setuptools console scripts. (#8491) - twisted.pair.raw and twisted.pair.rawudp have been ported to Python 3 (#8545) - twisted.internet.baseprocess has been ported to Python 3. (#8546) - twisted.python.dist has been ported to Python 3 (#8556) - twisted.internet.interfaces.IOpenSSLContextFactory has been added, which defines the interface provided both by the old-style twisted.internet.ssl.ContextFactory class and the newer twisted.interface.ssl.CertificateOptions class. This is a precursor to formally deprecating the former class in favour of the latter. (#8597) - twisted.python.zipstream has been ported to Python 3 (#8607) - Zip file entries returned by ChunkingZipFile.readfile() are now context managers. (#8641) - twisted.protocols.socks has been ported to Python 3 (#8665) - twisted.spread.banana has been ported to Python 3 (#8667) - Trial can now be invoked via "python -m twisted.trial". (#8712) - twisted.protocols.postfix has been ported to Python 3 (#8713) - twisted.protocols.wire and twisted.protocols.portforwarding have been ported to Python 3 (#8717) - twisted.protocols.stateful has been ported to Python 3 (#8718) - twisted.protocols.memcache is now compatible with Python 3. (#8726) - twisted.protocols.dict has been ported to Python 3 (#8732) Bugfixes -------- - pip install -e ".[dev]" now works on Python 3, but it will not install twistedchecker or pydoctor, which have not yet been ported. (#7807) - twistd can now properly daemonize on Linux/Unix when run under Python3 and will not hang indefinitely. (#8155) - tox can now be used to run Twisted's tests on Windows (#8578) - twisted.python.filepath.setContent() and twisted.python.filepath.moveTo() now work on Windows with Python 3 (#8610) - twisted.internet.win32eventreactor works on Python 3 in Windows (#8626) - The TLS payload buffer size was reduced in twisted.protocols.tls.TLSMemoryBIOProtocol. This fixes writing of very long strings using the TLSv1_1 method from the OpenSSL library. (#8693) - twisted.logger._flatten.flattenEvent() now does not crash if passed a unicode string. (#8699) - twisted.application.strports.service (and thus twistd) no longer swallow asynchronous exceptions from IStreamServerEndpoint.listen. (#8710) - _twistd_unix now reports the name and encoded message of an exception raised during daemonization on Python 2 and 3. (#8731) - twisted.protocols.amp now handles floats on Python 3. Previously, sending a float would raise a ValueError. (#8746) Improved Documentation ---------------------- - Some broken links to xprogramming in the unit test documentation have been fixed. (#8579) - The Twisted Tutorial "The Evolution of Finger" has been updated to use endpoints throughout. (#8588) - Updated the mail examples to use endpoints and better TLS. (#8595) - Changed the Twisted Web howto to use endpoints and modern TLS. (#8596) - Updated bug report URL in man pages. (#8600) - In twisted.internet.udp.Port, write() takes a parameter of type bytes. This is clarified in the docstring. (#8635) - twisted.internet.interfaces.ITransport.write() and twisted.internet.interfaces.ITransport.writeSequence() take bytes parameters. (#8636) - twisted.python.filepath.AbstractFilePath.getContent() returns bytes. The docstring was updated to clarify this. (#8637) - Updated release notes to reflect that 15.4 is the last version that supported Python 2.6, not 15.5. (#8651) - A missing space in defer.rst resulted in badly rendered output. The space was added. (#8723) Deprecations and Removals ------------------------- - Dropped support for pyOpenSSL versions less than 16.0.0. (#8441) Other ----- - #4926, #7868, #8209, #8271, #8276, #8308, #8324, #8348, #8367, #8377, #8378, #8379, #8380, #8381, #8383, #8385, #8386, #8387, #8388, #8389, #8391, #8392, #8393, #8394, #8397, #8406, #8410, #8412, #8413, #8414, #8421, #8425, #8426, #8428, #8429, #8430, #8432, #8434, #8435, #8437, #8438, #8439, #8444, #8451, #8452, #8453, #8454, #8456, #8457, #8459, #8462, #8463, #8465, #8468, #8469, #8479, #8482, #8483, #8486, #8490, #8493, #8494, #8496, #8497, #8498, #8499, #8501, #8503, #8504, #8507, #8508, #8510, #8513, #8514, #8515, #8516, #8517, #8520, #8521, #8522, #8523, #8524, #8527, #8528, #8529, #8531, #8532, #8534, #8536, #8537, #8538, #8540, #8541, #8543, #8548, #8552, #8553, #8554, #8555, #8557, #8560, #8563, #8565, #8568, #8569, #8572, #8573, #8574, #8577, #8580, #8581, #8582, #8584, #8586, #8589, #8590, #8592, #8593, #8598, #8603, #8604, #8606, #8609, #8615, #8616, #8617, #8618, #8619, #8621, #8624, #8627, #8628, #8630, #8632, #8634, #8640, #8644, #8645, #8646, #8647, #8648, #8662, #8664, #8666, #8668, #8671, #8672, #8684, #8691, #8702, #8703, #8705, #8706, #8716, #8719, #8724, #8725, #8727, #8733, #8734, #8741 Twisted Conch 16.4.0 (2016-08-25) ================================= Features -------- - twisted.conch.ssh.address is now ported to Python 3. (#8495) - twisted.conch.ssh.transport is now ported to Python 3. (#8638) - twisted.conch.ssh.channel is now ported to Python 3. (#8649) - twisted.conch.ssh.userauth is now ported to Python 3. (#8654) - twisted.conch.ssh.connection is now ported to Python 3. (#8660) - twisted.conch.ssh.session is now ported to Python 3. (#8661) - twisted.conch.ssh.filetransfer is now ported to Python 3. (#8675) - twisted.conch.ssh.agent is now ported to Python 3. (#8686) - twisted.conch.ssh is now ported to Python 3. (#8690) - twisted.conch.openssh_compat.* is now ported to Python 3. (#8694) - twisted.conch.client.knownhosts is now ported to Python 3. (#8697) - twisted.conch.insults.insults has been ported to Python 3 (#8698) - twisted.conch.client.default is now ported to Python 3. (#8700) - twisted.conch.recvline has been ported to Python 3 (#8709) - twisted.conch.endpoints is now ported to Python 3. (#8722) Bugfixes -------- - The SSHService is now a bytestring (#8653) - The name field in SShChannel is now a bytestring (#8683) Improved Documentation ---------------------- - Fixed syntax errors in cftp man page. (#8601) Other ----- - #8495, #8511, #8715 Twisted Mail 16.4.0 (2016-08-25) ================================ Deprecations and Removals ------------------------- - twisted.mail.mail.DomainWithDefaultDict.has_key is now deprecated in favor of the `in` keyword. (#8361) - twisted.mail.protocols.SSLContextFactory, deprecated since Twisted 12.0, has been removed. (#8591) Other ----- - #8525 Twisted Names 16.4.0 (2016-08-25) ================================= Features -------- - twisted.names.srvconnect is now ported to Python 3. (#8262) - twisted.names.resolve and twisted.names.tap have been ported to Python 3 (#8550) Other ----- - #8625, #8663 Twisted News 16.4.0 (2016-08-25) ================================ No significant changes have been made for this release. Twisted Pair 16.4.0 (2016-08-25) ================================ No significant changes have been made for this release. Twisted Runner 16.4.0 (2016-08-25) ================================== Features -------- - twisted.runner has been ported to Python 3. (#8739) Twisted Web 16.4.0 (2016-08-25) =============================== Features -------- - Twisted web HTTP/2 servers now time out HTTP/2 connections in the same manner as HTTP/1.1 connections. (#8480) Bugfixes -------- - A bug in twisted.web.server.Site.makeSession which may lead to predictable session IDs was fixed. Session IDs are now generated securely using `os.urandom`. (#3460) - twisted.web.server.Request.getSession will now, for a request sent over HTTPS, set a "Secure" cookie, preventing the secure session from being sent over plain-text HTTP. (#3461) - If called multiple times, twisted.web.http.Request.setLastModified now correctly observes the greatest supplied value. (#3807) - The HTTP server now correctly times connections out. (broken in 16.2) (#8481) - Twisted's HTTP/2 support no longer throws priority exceptions when WINDOW_UDPATE frames are received after a response has been completed. (#8558) - twisted.web.twcgi.CGIScript will now not pass the "Proxy" header to CGI scripts, as a mitigation to CVE-2016-1000111. (#8623) - Twisted Web's HTTP/2 server can now tolerate streams being reset by the client midway through a data upload without throwing exceptions. (#8682) - twisted.web.http.Request now swallows header writes on reset HTTP/2 streams, rather than erroring out. (#8685) - twisted.web's HTTP/2 server now tolerates receiving WINDOW_UPDATE frames for streams for which it has no outstanding data to send. (#8695) - twisted.web.http.HTTPChannel now resumes producing on finished, non-persistent connections. This prevents HTTP/1 servers using TLS from leaking a CLOSE_WAIT socket per request. (#8766) Other ----- - #8519, #8530, #8629, #8707 Twisted Words 16.4.0 (2016-08-25) ================================= Features -------- - twisted.words.xish is now ported to Python 3 (#8337) - twisted.words.protocols.jabber is now ported to Python 3 (#8423) - twisted.words.protocols.irc.ERR_TOOMANYMATCHES was introduced according to the RFC 2812 errata. (#8585) Bugfixes -------- - twisted.words.protocols.irc.RPL_ADMINLOC was removed and replaced with twisted.words.protocols.irc.RPL_ADMINLOC1 and twisted.words.protocols.irc.RPL_ADMINLOC2 to match the admin commands defined in RFC 2812. (#8585) - twisted.words.protocols.jabber.sasl_mechanisms has been fixed for Python 3.3 (#8738) Improved Documentation ---------------------- - The XMPP client example now works on Python 3. (#8509) Other ----- - #8360, #8460 Twisted Core 16.3.0 (2016-07-05) ================================ Features -------- - Defined a new interface, IProtocolNegotiationFactory, that can be implemented by IOpenSSLClientConnectionCreator or IOpenSSLServerConnectionCreator factories to allow them to offer protocols for negotiation using ALPN or NPN during the TLS handshake. (#8188) - twisted.trial.unittest.SynchronousTestCase.assertRegex is now available to provide Python 2.7 and Python 3 compatibility. (#8372) Improved Documentation ---------------------- - Development documentation has been updated to refer to Git instead of SVN. (#8335) Deprecations and Removals ------------------------- - twisted.python.reflect's deprecated functions have been removed. This includes funcinfo (deprecated since Twisted 2.5), allYourBase and accumulateBases (deprecated since Twisted 11.0), getcurrent and isinst (deprecated since Twisted 14.0). (#8293) - twisted.scripts.tap2deb and twisted.scripts.tap2rpm (along with the associated executables), deprecated since Twisted 15.2, have now been removed. (#8326) - twisted.spread.ui has been removed. (#8329) - twisted.manhole -- not to be confused with manhole in Conch -- has been removed. This includes the semi-functional Glade reactor, the manhole application, and the manhole-old twistd plugin. (#8330) - twisted.protocols.sip.DigestAuthorizer, BasicAuthorizer, and related functions have been removed. (#8445) Other ----- - #7229, #7826, #8290, #8323, #8331, #8336, #8341, #8344, #8345, #8347, #8351, #8363, #8365, #8366, #8374, #8382, #8384, #8390, #8395, #8396, #8398, #8399, #8400, #8401, #8403, #8404, #8405, #8407, #8408, #8409, #8415, #8416, #8417, #8418, #8419, #8420, #8427, #8433, #8436, #8461 Twisted Conch 16.3.0 (2016-07-05) ================================= No significant changes have been made for this release. Twisted Mail 16.3.0 (2016-07-05) ================================ No significant changes have been made for this release. Twisted Names 16.3.0 (2016-07-05) ================================= Bugfixes -------- - twisted.names.client.Resolver as well as all resolvers inheriting from twisted.names.common.ResolverBase can now understand DNS answers that come back in a different case than the query. Example: querying for www.google.com and the answer comes back with an A record for www.google.COM will now work. (#8343) Twisted News 16.3.0 (2016-07-05) ================================ No significant changes have been made for this release. Twisted Pair 16.3.0 (2016-07-05) ================================ No significant changes have been made for this release. Twisted Runner 16.3.0 (2016-07-05) ================================== No significant changes have been made for this release. Twisted Web 16.3.0 (2016-07-05) =============================== Features -------- - twisted.web.http.HTTPChannel now implements ITransport. Along with this change, twisted.web.http.Request now directs all its writes to the HTTPChannel, rather than to the backing transport. This change is required for future HTTP/2 work. (#8191) - twisted.web.http.HTTPChannel now has a HTTP/2 implementation which will be used if the transport has negotiated using it through ALPN/NPN (see #8188). (#8194) Bugfixes -------- - twisted.web.client.Agent and twisted.web.client.ProxyAgent now add brackets to IPv6 literal addresses in the host header they send. (#8369) - The HTTP server now correctly times connections out. (broken in 16.2) (#8481) Deprecations and Removals ------------------------- - twisted.web would previously dispatch pipelined requests simultaneously and queue the responses. This behaviour did not enforce any of the guarantees required by RFC 7230 or make it possible for users to enforce those requirements. For this reason, the parallel dispatch of requests has been removed. Pipelined requests are now processed serially. (#8320) Twisted Words 16.3.0 (2016-07-05) ================================= No significant changes have been made for this release. Twisted Core 16.2.0 (2016-05-18) ================================ Features -------- - twisted.protocols.haproxy.proxyEndpoint provides an endpoint that wraps any other stream server endpoint with the PROXY protocol that retains information about the original client connection handled by the proxy; this wrapper is also exposed via the string description prefix 'haproxy'; for example 'twistd web --port haproxy:tcp:8765'. (#8203) - twisted.application.app.AppLogger (used by twistd) now uses the new logging system. (#8235) Bugfixes -------- - twisted.application-using applications (trial, twistd, etc) now work with the --reactor option on Python 3. (#8299) - Failures are now logged by STDLibLogObserver. (#8316) Improved Documentation ---------------------- - Deprecation documentation was extended to include a quick check list for developers. (#5645) - The Twisted Deprecation Policy is now documented in the Twisted Development Policy. (#8082) - The documentation examples for UDP now work on Python 3. (#8280) Deprecations and Removals ------------------------- - Passing a factory that produces log observers that do not implement twisted.logger.ILogObserver or twisted.python.log.ILogObserver to twisted.application.app.AppLogger has been deprecated. This is primarily used by twistd's --logger option. Please use factories that produce log observers implementing twisted.logger.ILogObserver or the legacy twisted.python.log.ILogObserver. (#8235) - twisted.internet.qtreactor, a stub that imported the external qtreactor, has been removed. (#8288) Other ----- - #6266, #8231, #8244, #8256, #8266, #8269, #8275, #8277, #8286, #8291, #8292, #8304, #8315 Twisted Conch 16.2.0 (2016-05-18) ================================= No significant changes have been made for this release. Other ----- - #8279 Twisted Mail 16.2.0 (2016-05-18) ================================ No significant changes have been made for this release. Twisted Names 16.2.0 (2016-05-18) ================================= Features -------- - twisted.names.server is now ported to Python 3 (#8195) - twisted.names.authority and twisted.names.secondary have been ported to Python 3 (#8259) Twisted News 16.2.0 (2016-05-18) ================================ No significant changes have been made for this release. Twisted Pair 16.2.0 (2016-05-18) ================================ No significant changes have been made for this release. Twisted Runner 16.2.0 (2016-05-18) ================================== Deprecations and Removals ------------------------- - twisted.runner.inetdtap and twisted.runner.inetdconf RPC support was deprecated as it was broken for a long time. (#8123) Twisted Web 16.2.0 (2016-05-18) =============================== Features -------- - twisted.web.http.HTTPFactory's constructor now accepts a reactor argument, for explicit reactor selection. (#8246) Bugfixes -------- - twisted.web.http.HTTPChannel.headerReceived now respond with 400 and disconnect when a malformed header is received. (#8101) - twisted.web.http.Request once again has a reference to the HTTPFactory which created it, the absence of which was preventing log messages from being created. (#8272) - twisted.web.http.HTTPChannel no longer processes requests that have invalid headers as the final header in their header block. (#8317) - twisted.web.client.HTTPClientFactory (and the getPage and downloadPage APIs) now timeouts correctly on TLS connections where the remote party is not responding on the connection. (#8318) Other ----- - #8300 Twisted Words 16.2.0 (2016-05-18) ================================= Deprecations and Removals ------------------------- - twisted.words.protocols.msn, deprecated since Twisted 15.1, has been removed. (#8253) - twisted.words.protocols.oscar is deprecated. (#8260) Twisted Core 16.1.1 (2016-04-08) ================================ No significant changes have been made for this release. Twisted Conch 16.1.1 (2016-04-08) ================================= No significant changes have been made for this release. Twisted Mail 16.1.1 (2016-04-08) ================================ No significant changes have been made for this release. Twisted Names 16.1.1 (2016-04-08) ================================= No significant changes have been made for this release. Twisted News 16.1.1 (2016-04-08) ================================ No significant changes have been made for this release. Twisted Pair 16.1.1 (2016-04-08) ================================ No significant changes have been made for this release. Twisted Runner 16.1.1 (2016-04-08) ================================== No significant changes have been made for this release. Twisted Web 16.1.1 (2016-04-08) =============================== Bugfixes -------- - twisted.web.http.Request once again has a reference to the HTTPFactory which created it, the absence of which was preventing log messages from being created. (#8272) Twisted Words 16.1.1 (2016-04-08) ================================= No significant changes have been made for this release. Twisted Core 16.1.0 (2016-04-04) ================================ Features -------- - twisted.application.internet.ClientService, a service that maintains a persistent outgoing endpoint-based connection; a replacement for ReconnectingClientFactory that uses modern APIs. (#4735) - Twisted now uses setuptools' sdist to build tarballs. (#7985) Bugfixes -------- - Twisted is now compatible with OpenSSL 1.0.2f. (#8189) Other ----- - #4543, #8124, #8193, #8210, #8220, #8223, #8226, #8242 Twisted Conch 16.1.0 (2016-04-04) ================================= Features -------- - twisted.conch.checkers is now ported to Python 3. (#8225) - twisted.conch.telnet is now ported to Python 3. (#8228) - twisted.conch.manhole_ssh.ConchFactory (used by `twistd manhole`) no longer uses a hardcoded SSH server key, and will generate a persistent one, saving it in your user appdir. If you use ConchFactory, you will now need to provide your own SSH server key. (#8229) Other ----- - #8237, #8240 Twisted Mail 16.1.0 (2016-04-04) ================================ No significant changes have been made for this release. Twisted Names 16.1.0 (2016-04-04) ================================= No significant changes have been made for this release. Twisted News 16.1.0 (2016-04-04) ================================ No significant changes have been made for this release. Twisted Pair 16.1.0 (2016-04-04) ================================ No significant changes have been made for this release. Twisted Runner 16.1.0 (2016-04-04) ================================== No significant changes have been made for this release. Twisted Web 16.1.0 (2016-04-04) =============================== Features -------- - twisted.web.http.Request.addCookie now supports both unicode and bytes arguments, with unicode arguments being encoded to UTF-8. (#8067) Bugfixes -------- - twisted.web.util.DeferredResource no longer causes spurious "Unhandled error in Deferred" log messages. (#8192) - twisted.web.server.site.makeSession now generates an uid of type bytes on both Python 2 and 3. (#8215) Other ----- - #8238 Twisted Words 16.1.0 (2016-04-04) ================================= No significant changes have been made for this release. Twisted Core 16.0.0 (2016-03-10) ================================ Features -------- - todo parameter for IReporter.addExpectedSuccess and IReporter.addUnexpectedSuccess is no longer required. If not provided, a sensible default will be used instead. (#4811) - A new string endpoint type, "tls:", allows for properly-verified TLS (unlike "ssl:", always matching hostname resolution with certificate hostname verification) with faster IPv4/IPv6 connections. This comes with an accompanying function, twisted.internet.endpoints.wrapClientTLS, which can wrap an arbitrary client endpoint with client TLS. (#5642) - twisted.python.filepath.makedirs accepts an ignoreExistingDirectory flag which ignore the OSError raised by os.makedirs if requested directory already exists. (#5704) - twisted.protocols.amp has been ported to Python 3. (#6833) - twisted.internet.ssl.trustRootFromCertificates returns an object suitable for use as trustRoot= to twisted.internet.ssl.optionsForClientTLS that trusts multiple certificates. (#7671) - twisted.python.roots is now ported to Python 3. (#8131) - twisted.cred.strports has been ported to Python 3. (#8216) Bugfixes -------- - Expected failures from standard library unittest no longer fail with Trial reporters. (#4811) - twisted.internet.endpoints.HostnameEndpoint.connect no longer fails with an AlreadyCalledError when the Deferred it returns is cancelled after all outgoing connection attempts have been made but none have yet succeeded or failed. (#8014) - twisted.internet.task.LoopingCall.withCount when run with internal of 0, now calls the countCallable with 1, regardless of the time passed between calls. (#8125) - twisted.internet.endpoints.serverFromString, when parsing a SSL strports definition, now gives the correct error message when an empty chain file is given. (#8222) Improved Documentation ---------------------- - The Twisted Project has adopted the Contributor Covenant as its Code of Conduct. (#8173) Deprecations and Removals ------------------------- - twisted.internet.task.LoopingCall.deferred is now deprecated. Use the deferred returned by twisted.internet.task.LoopingCall.start() (#8116) - twisted.internet.gtkreactor, the GTK+ 1 reactor deprecated since Twisted 10.1, has been removed. This does not affect the GTK2, GLib, GTK3, or GObject-Introspection reactors. (#8145) - twisted.protocols.mice, containing a Logitech MouseMan serial driver, has been deprecated. (#8148) - The __version__ attribute of former subprojects (conch, mail, names, news, pair, runner, web, and words) is deprecated in preference to the central twisted.__version__. (#8219) Other ----- - #6842, #6978, #7668, #7791, #7881, #7943, #7944, #8050, #8104, #8115, #8119, #8122, #8139, #8144, #8154, #8162, #8180, #8187, #8220 Twisted Conch 16.0.0 (2016-03-10) ================================= Features -------- - twisted.conch now uses cryptography instead of PyCrypto for its underlying crypto operations. (#7413) - twisted.conch.ssh.keys is now ported to Python 3. (#7998) Bugfixes -------- - twisted.conch.ssh.channel.SSHChannel's getPeer and getHost methods now return an object which provides IAddress instead of an old- style tuple address. (#5999) - twisted.conch.endpoint.SSHCommandClientEndpoint, when authentication is delegated to an SSH agent, no longer leaves the agent connection opened when connection to the server is lost. (#8138) Other ----- - #7037, #7715, #8200, #8208 Twisted Mail 16.0.0 (2016-03-10) ================================ No significant changes have been made for this release. Twisted Names 16.0.0 (2016-03-10) ================================= No significant changes have been made for this release. Twisted News 16.0.0 (2016-03-10) ================================ No significant changes have been made for this release. Twisted Pair 16.0.0 (2016-03-10) ================================ No significant changes have been made for this release. Twisted Runner 16.0.0 (2016-03-10) ================================== No significant changes have been made for this release. Twisted Web 16.0.0 (2016-03-10) =============================== Features -------- - twisted.web.http_headers._DictHeaders now correctly handles updating via keyword arguments in Python 3 (therefore twisted.web.http_headers is now fully ported to Python 3). (#6082) - twisted.web.wsgi has been ported to Python 3. (#7993) - twisted.web.http_headers.Headers now accepts both Unicode and bytestring keys and values, encoding to iso-8859-1 and utf8 respectively. (#8129) - twisted.web.vhost ported to Python 3. (#8132) Bugfixes -------- - twisted.web.http.HTTPChannel now correctly handles non-ascii method name by returning 400. Previously non-ascii method name was causing unhandled exceptions. (#8102) - twisted.web.static.File on Python 3 now redirects paths to directories without a trailing slash, to a path with a trailing slash, as on Python 2. (#8169) Deprecations and Removals ------------------------- - twisted.web.http.Request's headers and received_headers attributes, deprecated since Twisted 13.2, have been removed. (#8136) - twisted.web.static.addSlash is deprecated. (#8169) Other ----- - #8140, #8182 Twisted Words 16.0.0 (2016-03-10) ================================= No significant changes have been made for this release. Twisted Core 15.5.0 (2015-11-28) ================================ Python 3.5 (on POSIX) support has been added. This release introduces changes that are required for Conch's SSH implementation to work with OpenSSH 6.9+ servers. Features -------- - twisted.python.url is a new abstraction for URLs, supporting RFC 3987 IRIs. (#5388) - twisted.python.logfile is now ported to Python 3. (#6749) - twisted.python.zippath has been ported to Python 3. (#6917) - twisted.internet.ssl.CertificateOptions and twisted.internet.ssl.optionsForClientTLS now take a acceptableProtocols parameter that enables negotiation of the next protocol to speak after the TLS handshake has completed. This field advertises protocols over both NPN and ALPN. Also added new INegotiated interface for TLS interfaces that support protocol negotiation. This interface adds a negotiatedProtocol property that reports what protocol, if any, was negotiated in the TLS handshake. (#7860) - twisted.python.urlpath.URLPath now operates correctly on Python 3, using bytes instead of strings, and introduces the fromBytes constructor to assist with creating them cross-version. (#7994) - twisted.application.strports is now ported to Python 3. (#8011) - twistd (the Twisted Daemon) is now ported to Python 3. (#8012) - Python 3.5 is now supported on POSIX platforms. (#8042) - twisted.internet.serialport is now ported on Python 3. (#8099) Bugfixes -------- - twisted.logger.formatEvent now can format an event if it was flattened (twisted.logger.eventAsJSON does this) and has text after the last replacement field. (#8003) - twisted.cred.checkers.FilePasswordDB now logs an error if the credentials db file does not exist, no longer raises an unhandled error. (#8028) - twisted.python.threadpool.ThreadPool now properly starts enough threads to do any work scheduled before ThreadPool.start() is called, such as when work is scheduled in the reactor via reactor.callInThread() before reactor.run(). (#8090) Improved Documentation ---------------------- - Twisted Development test standard documentation now contain information about avoiding test data files. (#6535) - The documentation for twisted.internet.defer.DeferredSemaphore now describes the actual usage for limit and tokens instance attributes. (#8024) Deprecations and Removals ------------------------- - twisted.python._initgroups, a C extension, has been removed and stdlib support is now always used instead. (#5861) - Python 2.6 is no longer supported. (#8017) - twisted.python.util.OrderedDict is now deprecated, and uses of it in Twisted are replaced with collections.OrderedDict. (#8051) - twisted.persisted.sob.load, twisted.persisted.sob.loadValueFromFile and twisted.persisted.sob.Persistent.save() are now deprecated when used with a passphrase. The encyption used by these methods are weak. (#8081) - twisted.internet.interfaces.IStreamClientEndpointStringParser has been removed and Twisted will no longer use parsers implementing this interface. (#8094) Other ----- - #5976, #6628, #6894, #6980, #7228, #7693, #7731, #7997, #8046, #8054, #8056, #8060, #8063, #8064, #8068, #8072, #8091, #8095, #8096, #8098, #8106 Twisted Conch 15.5.0 (2015-11-18) ================================= Features -------- - twisted.conch.ssh now supports the diffie-hellman-group-exchange- sha256 key exchange algorithm. (#7672) - twisted.conch.ssh now supports the diffie-hellman-group14-sha1 key exchange algorithm. (#7717) - twisted.conch.ssh.transport.SSHClientTransport now supports Diffie- Hellman key exchange using MSG_KEX_DH_GEX_REQUEST as described in RFC 4419. (#8100) - twisted.conch.ssh now supports the hmac-sha2-256 and hmac-sha2-512 MAC algorithms. (#8108) Deprecations and Removals ------------------------- - twisted.conch.ssh.keys.objectType is now deprecated. Use twisted.conch.ssh.keys.Key.sshType. (#8080) - twisted.conch.ssh.transport.SSHClientTransport no longer supports Diffie-Hellman key exchange using MSG_KEX_DH_GEX_REQUEST_OLD for pre RFC 4419 servers. (#8100) Twisted Mail 15.5.0 (2015-11-18) ================================ No significant changes have been made for this release. Twisted Names 15.5.0 (2015-11-18) ================================= No significant changes have been made for this release. Twisted News 15.5.0 (2015-11-18) ================================ No significant changes have been made for this release. Twisted Pair 15.5.0 (2015-11-18) ================================ No significant changes have been made for this release. Twisted Runner 15.5.0 (2015-11-18) ================================== No significant changes have been made for this release. Twisted Web 15.5.0 (2015-11-18) ================================ Features -------- - twisted.web.http.Request.addCookie now supports the httpOnly attribute which when set on cookies prevents the browser exposing it through channels other than HTTP and HTTPS requests (i.e. they will not be accessible through JavaScript). (#5911) - twisted.web.client.downloadPage is now ported to Python 3. (#6197) - twisted.web.client.Agent is now ported to Python 3. (#7407) - twisted.web.tap (ran when calling `twistd web`) has now been ported to Python 3. Not all features are enabled -- CGI, WSGI, and distributed web serving will be enabled in their respective tickets as they are ported. (#8008) Bugfixes -------- - twisted.web.client.URI now supports IPv6 addresses. Previously this would mistake the colons used as IPv6 address group separators as the start of a port specification. (#7650) - twisted.web.util's failure template has been moved inline to work around Python 3 distribution issues. (#8047) - twisted.web.http.Request on Python 3 now handles multipart/form- data requests correctly. (#8052) Other ----- - #8016, #8070 Twisted Words 15.5.0 (2015-11-18) ================================= Features -------- - twisted.words.protocol.irc.IRC now has a sendCommand() method which can send messages with tags. (#6667) Other ----- - #8015, #8097 Twisted Core 15.4.0 (2015-09-04) ================================ This is the last Twisted release where Python 2.6 is supported, on any platform. Features -------- - Trial has been ported to Python 3. (#5965) - Twisted now requires setuptools for installation. (#7177) - twisted.internet.endpoints.clientFromString is now ported to Python 3. (#7973) - twisted.internet._sslverify now uses SHA256 instead of MD5 for certificate request signing by default. (#7979) - twisted.internet.endpoints.serverFromString is now ported to Python 3. (#7982) - twisted.positioning is now ported to Python 3. (#7987) - twisted.python.failure.Failure's __repr__ now includes the exception message. (#8004) Bugfixes -------- - fixed a bug which could lead to a hang at shutdown in twisted.python.threadpool. (#2673) - twisted.internet.kqreactor on Python 3 now supports EINTR (Control-C) gracefully. (#7887) - Fix a bug introduced in 15.3.0; pickling a lambda function after importing twisted.persisted.styles raises PicklingError rather than AttributeError. (#7989) Other ----- - #7902, #7980, #7990, #7992 Twisted Conch 15.4.0 (2015-09-04) ================================= No significant changes have been made for this release. Other ----- - #7977 Twisted Mail 15.4.0 (2015-09-04) ================================ No significant changes have been made for this release. Twisted Names 15.4.0 (2015-09-04) ================================= No significant changes have been made for this release. Twisted News 15.4.0 (2015-09-04) ================================ No significant changes have been made for this release. Twisted Pair 15.4.0 (2015-09-04) ================================ No significant changes have been made for this release. Twisted Runner 15.4.0 (2015-09-04) ================================== No significant changes have been made for this release. Twisted Web 15.4.0 (2015-09-04) =============================== Features -------- - twisted.web.proxy is now ported to Python 3. (#7939) - twisted.web.guard is now ported to Python 3. (#7974) Bugfixes -------- - twisted.web.http.Request.setResponseCode now only allows bytes messages. (#7981) - twisted.web.server.Request.processingFailed will now correctly write out the traceback on Python 3. (#7996) Twisted Words 15.4.0 (2015-09-04) ================================= No significant changes have been made for this release. Twisted Core 15.3.0 (2015-08-04) ================================ Features -------- - twisted.application.app is now ported to Python 3 (#6914) - twisted.plugin now supports Python 3 (#7182) - twisted.cred.checkers is now ported to Python 3. (#7834) - twisted.internet.unix is now ported to Python 3. (#7874) - twisted.python.sendmsg has now been ported to Python 3, using the stdlib sendmsg/recvmsg functionality when available. (#7884) - twisted.internet.protocol.Factory now uses the new logging system (twisted.logger) for all its logging statements. (#7897) - twisted.internet.stdio is now ported to Python 3. (#7899) - The isDocker method has been introduced on twisted.python.runtime.Platform to detect if the running Python is inside a Docker container. Additionally, Platform.supportsINotify() now returns False if isDocker() is True, because of many Docker storage layers having broken INotify. (#7968) Bugfixes -------- - twisted.logger.LogBeginner.beginLoggingTo now outputs the correct warning when it is called more than once. (#7916) Deprecations and Removals ------------------------- - twisted.cred.pamauth (providing PAM support) has been removed due to it being unusable in current supported Python versions. (#3728) - twisted.application.app.HotshotRunner (twistd's hotshot profiler module) is removed and twistd now uses cProfile by default. (#5137) - twisted.python.win32.getProgramsMenuPath and twisted.python.win32.getProgramFilesPath are now deprecated. (#7883) - twisted.lore has now been removed, in preference to Sphinx. (#7892) - Deprecated zsh tab-complete files are now removed in preference to twisted.python.usage's tab-complete functionality. (#7898) - twisted.python.hashlib, deprecated since 13.1, has now been removed. (#7905) - twisted.trial.runner.DryRunVisitor, deprecated in Twisted 13.0, has now been removed. (#7919) - twisted.trial.util.getPythonContainers, deprecated since Twisted 12.3, is now removed. (#7920) - Twisted no longer supports being packaged as subprojects. (#7964) Other ----- - #6136, #7035, #7803, #7817, #7827, #7844, #7876, #7906, #7908, #7915, #7931, #7940, #7967, #7983 Twisted Conch 15.3.0 (2015-08-04) ================================= Bugfixes -------- - The Conch Unix server now sets the HOME environment variable when executing commands. (#7936) Other ----- - #7937 Twisted Mail 15.3.0 (2015-08-04) ================================ No significant changes have been made for this release. Twisted Names 15.3.0 (2015-08-04) ================================= No significant changes have been made for this release. Twisted News 15.3.0 (2015-08-04) ================================ No significant changes have been made for this release. Twisted Pair 15.3.0 (2015-08-04) ================================ No significant changes have been made for this release. Twisted Runner 15.3.0 (2015-08-04) ================================== No significant changes have been made for this release. Twisted Web 15.3.0 (2015-08-04) =============================== Features -------- - twisted.web.xmlrpc is now ported to Python 3. (#7795) - twisted.web.template and twisted.web.util are now ported to Python 3. (#7811) - twisted.web.error is now ported to Python 3. (#7845) Deprecations and Removals ------------------------- - twisted.web.html is now deprecated in favor of twisted.web.template. (#4948) Other ----- - #7895, #7942, #7949, #7952, #7975 Twisted Words 15.3.0 (2015-08-04) ================================= No significant changes have been made for this release. Twisted Core 15.2.1 (2015-05-23) ================================ Bugfixes -------- - twisted.logger now marks the `isError` key correctly on legacy events generated by writes to stderr. (#7903) Improved Documentation ---------------------- - twisted.logger's documentation is now correctly listed in the table of contents. (#7904) Twisted Conch 15.2.1 (2015-05-23) ================================= No significant changes have been made for this release. Twisted Lore 15.2.1 (2015-05-23) ================================ No significant changes have been made for this release. Twisted Mail 15.2.1 (2015-05-23) ================================ No significant changes have been made for this release. Twisted Names 15.2.1 (2015-05-23) ================================= No significant changes have been made for this release. Twisted News 15.2.1 (2015-05-23) ================================ No significant changes have been made for this release. Twisted Pair 15.2.1 (2015-05-23) ================================ No significant changes have been made for this release. Twisted Runner 15.2.1 (2015-05-23) ================================== No significant changes have been made for this release. Twisted Web 15.2.1 (2015-05-23) =============================== No significant changes have been made for this release. Twisted Words 15.2.1 (2015-05-23) ================================= No significant changes have been made for this release. Twisted Core 15.2.0 (2015-05-18) ================================ Features -------- - twisted.internet.process has now been ported to Python 3. (#5987) - twisted.cred.credentials is now ported to Python 3. (#6176) - twisted.trial.unittest.TestCase's assertEqual, assertTrue, and assertFalse methods now pass through the standard library's more informative failure messages. (#6306) - The new package twisted.logger provides a new, fully tested, and feature-rich logging framework. The old module twisted.python.log is now implemented using the new framework. The new logger HOWTO documents the new framework. (#6750) - twisted.python.modules is now ported to Python 3. (#7804) - twisted.python.filepath.FilePath now supports Unicode (text) paths. Like the os module, instantiating it with a Unicode path will return a Unicode-mode FilePath, instantiating with a bytes path will return a bytes-mode FilePath. (#7805) - twisted.internet.kqreactor is now ported to Python 3 (#7823) - twisted.internet.endpoints.ProcessEndpoint is now ported to Python 3. (#7824) - twisted.python.filepath.FilePath now has asBytesMode and asTextMode methods which return a FilePath in the requested mode. (#7830) - twisted.python.components.proxyForInterface now creates method proxies that can be used with functools.wraps. (#7832) - The tls optional dependency will now also install the idna package to validate idna2008 names. (#7853) Bugfixes -------- - Don't raise an exception if `DefaultLogObserver.emit()` gets an event with a message that raises when `repr()` is called on it. Specifically: use `textFromEventDict()` instead of a separate (and inferior) message rendering implementation. (#6569) - twisted.cred.credentials.DigestedCredentials incorrectly handled md5-sess hashing according to the RFC, which has now been fixed. (#7835) - Fixed an issue with twisted.internet.task.LoopingCall.withCount where sometimes the passed callable would be invoked with "0" when we got close to tricky floating point boundary conditions. (#7836) - twisted.internet.defer now properly works with the new logging system. (#7851) - Change `messages` key to `log_io` for events generated by `LoggingFile`. (#7852) - twisted.logger had literal characters in docstrings that are now quoted. (#7854) - twisted.logger now correctly formats a log event with a key named `message` when passed to a legacy log observer. (#7855) - twisted.internet.endpoints.HostnameEndpoint now uses getaddrinfo properly on Python 3.4 and above. (#7886) Improved Documentation ---------------------- - Fix a typo in narrative documentation for logger (#7875) Deprecations and Removals ------------------------- - tkunzip and tapconvert in twisted.scripts were deprecated in 11.0 and 12.1 respectively, and are now removed. (#6747) - twisted.protocols.gps is deprecated in preference to twisted.positioning. (#6810) - twisted.scripts.tap2deb and twisted.scripts.tap2rpm are now deprecated. (#7682) - twisted.trial.reporter.TestResult and twisted.trial.reporter.Reporter contained deprecated methods (since 8.0) which have now been removed. (#7815) Other ----- - #6027, #7287, #7701, #7727, #7758, #7776, #7786, #7812, #7819, #7831, #7838, #7865, #7866, #7869, #7872, #7877, #7878, #7885 Twisted Conch 15.2.0 (2015-05-18) ================================= Features -------- - twisted.conch.ssh.forwarding now supports local->remote forwarding of IPv6 (#7751) Twisted Lore 15.2.0 (2015-05-18) ================================ No significant changes have been made for this release. Twisted Mail 15.2.0 (2015-05-18) ================================ Features -------- - twisted.mail.smtp.sendmail now uses ESMTP. It will opportunistically enable encryption and allow the use of authentication. (#7257) Twisted Names 15.2.0 (2015-05-18) ================================= No significant changes have been made for this release. Twisted News 15.2.0 (2015-05-18) ================================ No significant changes have been made for this release. Twisted Pair 15.2.0 (2015-05-18) ================================ No significant changes have been made for this release. Twisted Runner 15.2.0 (2015-05-18) ================================== No significant changes have been made for this release. Twisted Web 15.2.0 (2015-05-18) =============================== Features -------- - twisted.web.static is now ported to Python 3. (#6177) - twisted.web.server.Site accepts requestFactory as constructor argument. (#7016) Deprecations and Removals ------------------------- - twisted.web.util had some HTML generation functions deprecated since 12.1 that have now been removed. (#7828) Other ----- - #6927, #7797, #7802, #7846 Twisted Words 15.2.0 (2015-05-18) ================================= Bugfixes -------- - The resumeOffset argument to twisted.words.protocol.irc.DccFileReceive now works as it is documented. (#7775) Twisted Core 15.1.0 (2015-04-02) ================================ Features -------- - Optional dependencies can be installed using the extra_requires facility provided by setuptools. (#3696) Improved Documentation ---------------------- - Twisted Trial's basics documentation now has a link to the documentation about how Trial finds tests. (#4526) Deprecations and Removals ------------------------- - twisted.application.internet.UDPClient, deprecated since Twisted 13.1.0, has been removed. (#7702) Other ----- - #6988, #7005, #7006, #7007, #7008, #7044, #7335, #7666, #7723, #7724, #7725, #7748, #7763, #7765, #7766, #7768 Twisted Conch 15.1.0 (2015-04-02) ================================= No significant changes have been made for this release. Twisted Lore 15.1.0 (2015-04-02) ================================ No significant changes have been made for this release. Twisted Mail 15.1.0 (2015-04-02) ================================ Bugfixes -------- - twisted.mail.smtp.ESMTPClient now does not fall back to plain SMTP if authentication or TLS is required and not able to occur. (#7258) Other ----- - #6705 Twisted Names 15.1.0 (2015-04-02) ================================= No significant changes have been made for this release. Other ----- - #7728 Twisted News 15.1.0 (2015-04-02) ================================ No significant changes have been made for this release. Twisted Pair 15.1.0 (2015-04-02) ================================ No significant changes have been made for this release. Twisted Runner 15.1.0 (2015-04-02) ================================== No significant changes have been made for this release. Other ----- - #7726 Twisted Web 15.1.0 (2015-04-02) =============================== Features -------- - twisted.web.static.File allows defining a custom resource for rendering forbidden pages. (#6951) Other ----- - #7000, #7485, #7750, #7762 Twisted Words 15.1.0 (2015-04-02) ================================= Deprecations and Removals ------------------------- - twisted.words.protocols.msn is now deprecated (#6395) Other ----- - #6494 Twisted Core 15.0.0 (2015-01-24) ================================ Features -------- - twisted.internet.protocol.ClientFactory (and subclasses) may now return None from buildProtocol to immediately close the connection. (#710) - twisted.trial.unittest.SynchronousTestCase.assertRaises can now return a context manager. (#5339) - Implementations of twisted.internet.interfaces.IStreamClientEndpoint included in Twisted itself will now handle None being returned from the client factory's buildProtocol method by immediately closing the connection and firing the waiting Deferred with a Failure. (#6976) - inlineCallbacks now supports using the return statement with a value on Python 3 (#7624) - twisted.spread.banana.Banana.sendEncoded() now raises a more informative error message if the user tries to encode objects of unsupported type. (#7663) Bugfixes -------- - twisted.internet.interfaces.IReactorMulticast.listenMultiple works again RHEL 6's python 2.6. (#7159) - Allow much more of the code within Twisted to use ProcessEndpoint by adding IPushProducer and IConsumer interfaces to its resulting transport. (#7436) - twisted.internet.ssl.Certificate(...).getPublicKey().keyHash() now produces a stable value regardless of OpenSSL version. Unfortunately this means that it is different than the value produced by older Twisted versions. (#7651) - twisted.python.reflect.safe_str on Python 3 converts utf-8 encoded bytes to clean str instead of "b'a'" (#7660) - twisted.spread.banana.Banana now raises NotImplementedError when receiving pb messages without pb being the selected dialect (#7662) - The SSL server string endpoint parser (twisted.internet.endpoints.serverFromString) now constructs endpoints which, by default, disable the insecure SSLv3 protocol. (#7684) - The SSL client string endpoint parser (twisted.internet.endpoints.clientFromString) now constructs endpoints which, by default, disable the insecure SSLv3 protocol. (#7686) Improved Documentation ---------------------- - inlineCallbacks now has introductory documentation. (#1009) - The echoclient example now uses twisted.internet.task.react. (#7083) - Twisted Trial's how-to documentation now has a link to Twisted's contribution guidelines and has been reformatted. (#7475) - Fixed a path error in the make.bat file for building Sphinx documentation, so that it is now possible to build the documentation using make.bat on Windows. (#7542) Deprecations and Removals ------------------------- - twisted.python.filepath.FilePath.statinfo was deprecated. (#4450) - twisted.internet.defer.deferredGenerator is now deprecated. twisted.internet.defer.inlineCallbacks should be used instead. (#6044) - Pickling twisted.internet.ssl.OptionSSLCertificationOptions and twisted.internet.ssl.Keypair is no longer supported. __getstate__ and __setstate__ methods of these classes have been deprecated. (#6166) - twisted.spread.jelly's support for unjellying "instance" atoms is now deprecated. (#7653) Other ----- - #3404, #4711, #5730, #6042, #6626, #6947, #6953, #6989, #7032, #7038, #7039, #7097, #7098, #7142, #7143, #7154, #7155, #7156, #7157, #7158, #7160, #7161, #7162, #7164, #7165, #7176, #7234, #7252, #7329, #7333, #7355, #7369, #7370, #7419, #7529, #7531, #7534, #7537, #7538, #7620, #7621, #7633, #7636, #7637, #7638, #7640, #7641, #7642, #7643, #7665, #7667, #7713, #7719 Twisted Conch 15.0.0 (2015-01-24) ================================= Features -------- - The new APIs: twisted.conch.checkers.IAuthorizedKeysDB, twisted.conch.checkers.InMemorySSHKeyDB, twisted.conch.checkers.UNIXAuthorizedKeyFiles, and twisted.conch.checkers.SSHPublicKeyChecker have been added to provide functionality to check the validity of SSH public keys and specify where authorized keys are to be found. (#7144) Deprecations and Removals ------------------------- - twisted.conch.checkers.SSHPublicKeyDatabase is now deprecated in favor of a twisted.conch.checkers.SSHPublicKeyChecker instantiated with a twisted.conch.checkers.UNIXAuthorizedKeyFiles. (#7144) Other ----- - #6626, #7002, #7526, #7532, #7698 Twisted Lore 15.0.0 (2015-01-24) ================================ No significant changes have been made for this release. Twisted Mail 15.0.0 (2015-01-24) ================================ No significant changes have been made for this release. Other ----- - #6999, #7708 Twisted Names 15.0.0 (2015-01-24) ================================= Bugfixes -------- - twisted.names.secondary.SecondaryAuthority can now answer queries again (broken since 13.2.0). (#7408) Other ----- - #7352 Twisted News 15.0.0 (2015-01-24) ================================ No significant changes have been made for this release. Other ----- - #7703 Twisted Pair 15.0.0 (2015-01-24) ================================ No significant changes have been made for this release. Other ----- - #7722 Twisted Runner 15.0.0 (2015-01-24) ================================== No significant changes have been made for this release. Twisted Web 15.0.0 (2015-01-24) =============================== Features -------- - twisted.web.client.Agent.usingEndpointFactory allows creating an Agent that connects in non-standard ways, e.g. via a proxy or a UNIX socket. (#6634) - The Deferred returned by twisted.web.client.readBody can now be cancelled. (#6686) Deprecations and Removals ------------------------- - twisted.web.iweb.IRequest.getClient is now deprecated. Its implementation in Twisted, twisted.web.http.Request.getClient, is also deprecated and will no longer attempt to resolve the client IP address to a hostname. (#2252) Other ----- - #7247, #7302, #7680, #7689 Twisted Words 15.0.0 (2015-01-24) ================================= No significant changes have been made for this release. Other ----- - #6994, #7163, #7622 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1644239601.593946 Twisted-22.1.0/PKG-INFO0000644000175100001710000001355500000000000013665 0ustar00runnerdockerMetadata-Version: 2.1 Name: Twisted Version: 22.1.0 Summary: An asynchronous networking framework written in Python Home-page: https://twistedmatrix.com/ Author: Twisted Matrix Laboratories Author-email: twisted-python@twistedmatrix.com Maintainer: Glyph Lefkowitz Maintainer-email: glyph@twistedmatrix.com License: MIT Project-URL: Documentation, https://docs.twistedmatrix.com/ Project-URL: Source, https://github.com/twisted/twisted Project-URL: Issues, https://twistedmatrix.com/trac/report Project-URL: Twitter, https://twitter.com/twistedmatrix Project-URL: Changelog, https://github.com/twisted/twisted/blob/HEAD/NEWS.rst Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Requires-Python: >=3.6.7 Description-Content-Type: text/x-rst Provides-Extra: test Provides-Extra: dev_release Provides-Extra: dev Provides-Extra: tls Provides-Extra: conch Provides-Extra: serial Provides-Extra: http2 Provides-Extra: contextvars Provides-Extra: all_non_platform Provides-Extra: macos_platform Provides-Extra: windows_platform Provides-Extra: osx_platform Provides-Extra: mypy License-File: LICENSE Twisted ======= |gitter|_ |pypi|_ |mypy|_ For information on changes in this release, see the `NEWS `_ file. What is this? ------------- Twisted is an event-based framework for internet applications, supporting Python 3.6+. It includes modules for many different purposes, including the following: - ``twisted.web``: HTTP clients and servers, HTML templating, and a WSGI server - ``twisted.conch``: SSHv2 and Telnet clients and servers and terminal emulators - ``twisted.words``: Clients and servers for IRC, XMPP, and other IM protocols - ``twisted.mail``: IMAPv4, POP3, SMTP clients and servers - ``twisted.positioning``: Tools for communicating with NMEA-compatible GPS receivers - ``twisted.names``: DNS client and tools for making your own DNS servers - ``twisted.trial``: A unit testing framework that integrates well with Twisted-based code. Twisted supports all major system event loops -- ``select`` (all platforms), ``poll`` (most POSIX platforms), ``epoll`` (Linux), ``kqueue`` (FreeBSD, macOS), IOCP (Windows), and various GUI event loops (GTK+2/3, Qt, wxWidgets). Third-party reactors can plug into Twisted, and provide support for additional event loops. Installing ---------- To install the latest version of Twisted using pip:: $ pip install twisted Additional instructions for installing this software are in `the installation instructions `_. Documentation and Support ------------------------- Twisted's documentation is available from the `Twisted Matrix website `_. This documentation contains how-tos, code examples, and an API reference. Help is also available on the `Twisted mailing list `_. There is also a pair of very lively IRC channels, ``#twisted`` (for general Twisted questions) and ``#twisted.web`` (for Twisted Web), on `irc.libera.chat _`. Unit Tests ---------- Twisted has a comprehensive test suite, which can be run by ``tox``:: $ tox -l # to view all test environments $ tox -e nocov # to run all the tests without coverage $ tox -e withcov # to run all the tests with coverage $ tox -e alldeps-withcov-posix # install all dependencies, run tests with coverage on POSIX platform You can test running the test suite under the different reactors with the ``TWISTED_REACTOR`` environment variable:: $ env TWISTED_REACTOR=epoll tox -e alldeps-withcov-posix Some of these tests may fail if you: * don't have the dependencies required for a particular subsystem installed, * have a firewall blocking some ports (or things like Multicast, which Linux NAT has shown itself to do), or * run them as root. Static Code Checkers -------------------- You can ensure that code complies to Twisted `coding standards `_:: $ tox -e lint # run pre-commit to check coding stanards $ tox -e mypy # run MyPy static type checker to check for type errors Or, for speed, use pre-commit directly:: $ pipx run pre-commit run Copyright --------- All of the code in this distribution is Copyright (c) 2001-2022 Twisted Matrix Laboratories. Twisted is made available under the MIT license. The included `LICENSE `_ file describes this in detail. Warranty -------- THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE USE OF THIS SOFTWARE IS WITH YOU. IN NO EVENT WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY, BE LIABLE TO YOU FOR ANY DAMAGES, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Again, see the included `LICENSE `_ file for specific legal details. .. |pypi| image:: https://img.shields.io/pypi/v/twisted.svg .. _pypi: https://pypi.python.org/pypi/twisted .. |gitter| image:: https://img.shields.io/gitter/room/twisted/twisted.svg .. _gitter: https://gitter.im/twisted/twisted .. |mypy| image:: https://github.com/twisted/twisted/workflows/mypy/badge.svg .. _mypy: https://github.com/twisted/twisted ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/README.rst0000644000175100001710000001045100000000000014247 0ustar00runnerdockerTwisted ======= |gitter|_ |pypi|_ |mypy|_ For information on changes in this release, see the `NEWS `_ file. What is this? ------------- Twisted is an event-based framework for internet applications, supporting Python 3.6+. It includes modules for many different purposes, including the following: - ``twisted.web``: HTTP clients and servers, HTML templating, and a WSGI server - ``twisted.conch``: SSHv2 and Telnet clients and servers and terminal emulators - ``twisted.words``: Clients and servers for IRC, XMPP, and other IM protocols - ``twisted.mail``: IMAPv4, POP3, SMTP clients and servers - ``twisted.positioning``: Tools for communicating with NMEA-compatible GPS receivers - ``twisted.names``: DNS client and tools for making your own DNS servers - ``twisted.trial``: A unit testing framework that integrates well with Twisted-based code. Twisted supports all major system event loops -- ``select`` (all platforms), ``poll`` (most POSIX platforms), ``epoll`` (Linux), ``kqueue`` (FreeBSD, macOS), IOCP (Windows), and various GUI event loops (GTK+2/3, Qt, wxWidgets). Third-party reactors can plug into Twisted, and provide support for additional event loops. Installing ---------- To install the latest version of Twisted using pip:: $ pip install twisted Additional instructions for installing this software are in `the installation instructions `_. Documentation and Support ------------------------- Twisted's documentation is available from the `Twisted Matrix website `_. This documentation contains how-tos, code examples, and an API reference. Help is also available on the `Twisted mailing list `_. There is also a pair of very lively IRC channels, ``#twisted`` (for general Twisted questions) and ``#twisted.web`` (for Twisted Web), on `irc.libera.chat _`. Unit Tests ---------- Twisted has a comprehensive test suite, which can be run by ``tox``:: $ tox -l # to view all test environments $ tox -e nocov # to run all the tests without coverage $ tox -e withcov # to run all the tests with coverage $ tox -e alldeps-withcov-posix # install all dependencies, run tests with coverage on POSIX platform You can test running the test suite under the different reactors with the ``TWISTED_REACTOR`` environment variable:: $ env TWISTED_REACTOR=epoll tox -e alldeps-withcov-posix Some of these tests may fail if you: * don't have the dependencies required for a particular subsystem installed, * have a firewall blocking some ports (or things like Multicast, which Linux NAT has shown itself to do), or * run them as root. Static Code Checkers -------------------- You can ensure that code complies to Twisted `coding standards `_:: $ tox -e lint # run pre-commit to check coding stanards $ tox -e mypy # run MyPy static type checker to check for type errors Or, for speed, use pre-commit directly:: $ pipx run pre-commit run Copyright --------- All of the code in this distribution is Copyright (c) 2001-2022 Twisted Matrix Laboratories. Twisted is made available under the MIT license. The included `LICENSE `_ file describes this in detail. Warranty -------- THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE USE OF THIS SOFTWARE IS WITH YOU. IN NO EVENT WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY, BE LIABLE TO YOU FOR ANY DAMAGES, EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. Again, see the included `LICENSE `_ file for specific legal details. .. |pypi| image:: https://img.shields.io/pypi/v/twisted.svg .. _pypi: https://pypi.python.org/pypi/twisted .. |gitter| image:: https://img.shields.io/gitter/room/twisted/twisted.svg .. _gitter: https://gitter.im/twisted/twisted .. |mypy| image:: https://github.com/twisted/twisted/workflows/mypy/badge.svg .. _mypy: https://github.com/twisted/twisted ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/code_of_conduct.md0000644000175100001710000000516300000000000016223 0ustar00runnerdocker# Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer (see below). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. You may send reports to [our Conduct email](mailto:conduct@twistedmatrix.com). If you wish to contact specific maintainers directly, the following have made themselves available for conduct issues: - Amber Brown (HawkOwl) (hawkowl at twistedmatrix.com) - Moshe Zadka (zadka.moshe at gmail.com) This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [http://contributor-covenant.org/version/1/3/0/][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/3/0/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/0000755000175100001710000000000000000000000013507 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/Makefile0000644000175100001710000001515700000000000015160 0ustar00runnerdocker# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Twisted.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Twisted.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Twisted" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Twisted" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_extensions/0000755000175100001710000000000000000000000016045 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_extensions/traclinks.py0000644000175100001710000000157000000000000020414 0ustar00runnerdocker""" Sphinx/docutils extension to create links to a Trac site using a RestructuredText interpreted text role that looks like this:: :trac:`trac_link_text` for example:: :trac:`#2015` creates a link to ticket number 2015. adapted from recipe U{here } """ import urllib from docutils import nodes, utils def make_trac_link(name, rawtext, text, lineno, inliner, options={}, content=[]): env = inliner.document.settings.env trac_url = env.config.traclinks_base_url ref = trac_url + "/intertrac/" + urllib.quote(text, safe="") node = nodes.reference(rawtext, utils.unescape(text), refuri=ref, **options) return [node], [] # setup function to register the extension def setup(app): app.add_config_value("traclinks_base_url", "https://twistedmatrix.com/trac", "env") app.add_role("trac", make_trac_link) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_static/0000755000175100001710000000000000000000000015135 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_static/.placeholder0000644000175100001710000000016000000000000017415 0ustar00runnerdockerThis is a placeholder to get Mercurial to keep an empty directory in the repository. You can ignore this file.././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_static/js/0000755000175100001710000000000000000000000015551 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_static/js/custom.js0000644000175100001710000000022600000000000017421 0ustar00runnerdocker// Configure the Gitter sidecar chat button. // https://sidecar.gitter.im/ window.gitter = {'chat': {'options': { 'room': 'twisted/twisted' }}} ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_templates/0000755000175100001710000000000000000000000015644 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_templates/.placeholder0000644000175100001710000000016000000000000020124 0ustar00runnerdockerThis is a placeholder to get Mercurial to keep an empty directory in the repository. You can ignore this file.././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4579442 Twisted-22.1.0/docs/_themes/0000755000175100001710000000000000000000000015133 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_themes/twisteddefault/0000755000175100001710000000000000000000000020163 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twisteddefault/theme.conf0000644000175100001710000000134200000000000022134 0ustar00runnerdocker[theme] inherit = default stylesheet = default.css pygments_style = sphinx [options] rightsidebar = true stickysidebar = false footerbgcolor = #565656 footertextcolor = #ffffff sidebarbgcolor = #565656 sidebartextcolor = #ffffff sidebarlinkcolor = #ffffff relbarbgcolor = #565656 relbartextcolor = #ffffff relbarlinkcolor = #ffffff bgcolor = #ffffff textcolor = #000000 headbgcolor = #ffffff headtextcolor = #83a501 headlinkcolor = #c60f0f linkcolor = #517489 codebgcolor = #eeffcc codetextcolor = #333333 bodyfont = 'Trebuchet MS', arial, verdana, 'Bitstream Vera Sans', helvetica, sans-serif headfont = 'Trebuchet MS', arial, verdana, 'Bitstream Vera Sans', helvetica, sans-serif ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4659443 Twisted-22.1.0/docs/_themes/twistedtrac/0000755000175100001710000000000000000000000017470 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/layout.html0000644000175100001710000002434100000000000021677 0ustar00runnerdocker{%- block doctype -%} {%- endblock %} {%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} {%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} {%- macro relbar() %} {%- endmacro %} {%- macro sidebar() %} {%- if not embedded %}{% if not theme_nosidebar|tobool %}
{%- block sidebarlogo %} {%- if logo %} {%- endif %} {%- endblock %} {%- block sidebartoc %} {%- if display_toc %}

{{ _('Table Of Contents') }}

{{ toc }} {%- endif %} {%- endblock %} {%- block sidebarrel %} {%- if prev %}

{{ _('Previous topic') }}

{{ prev.title }}

{%- endif %} {%- if next %}

{{ _('Next topic') }}

{{ next.title }}

{%- endif %} {%- endblock %} {%- block sidebarsourcelink %} {%- if show_source and has_source and sourcename %}

{{ _('This Page') }}

{%- endif %} {%- endblock %} {%- if customsidebar %} {% include customsidebar %} {%- endif %} {%- block sidebarsearch %} {%- if pagename != "search" %} {%- endif %} {%- endblock %}
{%- endif %}{% endif %} {%- endmacro %} {{ metatags }} {%- if not embedded and docstitle %} {%- set titlesuffix = " — "|safe + docstitle|e %} {%- else %} {%- set titlesuffix = "" %} {%- endif %} {{ title|striptags }}{{ titlesuffix }} {%- if not embedded %} {%- for scriptfile in script_files %} {%- endfor %} {%- if use_opensearch %} {%- endif %} {%- if favicon %} {%- endif %} {%- endif %} {%- block linktags %} {%- if hasdoc('about') %} {%- endif %} {%- if hasdoc('genindex') %} {%- endif %} {%- if hasdoc('search') %} {%- endif %} {%- if hasdoc('copyright') %} {%- endif %} {%- if parents %} {%- endif %} {%- if next %} {%- endif %} {%- if prev %} {%- endif %} {%- endblock %} {%- block extrahead %} {% endblock %} {%- block relbar1 %}{{ relbar() }}{% endblock %}
{%- block sidebar1 %} {# possible location for sidebar #} {% endblock %}
{%- block document %}
{%- if not embedded %}{% if not theme_nosidebar|tobool %}
{%- endif %}{% endif %}
{% block body %} {% endblock %}
{%- if not embedded %}{% if not theme_nosidebar|tobool %}
{%- endif %}{% endif %}
{%- endblock %} {%- block sidebar2 %}{{ sidebar() }}{% endblock %}
././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/_themes/twistedtrac/static/0000755000175100001710000000000000000000000020757 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/_themes/twistedtrac/static/css/0000755000175100001710000000000000000000000021547 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/css/trac.css0000644000175100001710000003734700000000000023230 0ustar00runnerdocker/* Trac CSS */ body { background: #fff; color: #000; margin: 0px; /* Modified */ padding: 0; } body, th, td { font: normal 13px trebuchet ms,verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif; /* Modified */ } h1, h2, h3, h4 { font-family: trebuchet ms,arial,verdana,'Bitstream Vera Sans',helvetica,sans-serif; /* Modified */ font-weight: normal; /* Modified */ letter-spacing: -0.018em; color: #83A501; /* Modified */ border-width: 0 0 1px 0; /* Modified */ border-style: dotted; /* Modified */ border-color: #dddddd; /* Modified */ } h1 { font-size: 19px; margin: .15em 1em 0 0 } h2 { font-size: 16px } h3 { font-size: 14px } hr { border: none; border-top: 1px solid #ccb; margin: 2em 0 } address { font-style: normal } img { border: none; } .underline { text-decoration: underline } ol.loweralpha { list-style-type: lower-alpha } ol.upperalpha { list-style-type: upper-alpha } ol.lowerroman { list-style-type: lower-roman } ol.upperroman { list-style-type: upper-roman } ol.arabic { list-style-type: decimal } /* Link styles */ :link, :visited { text-decoration: none; color: #517489; /* Modified */ border-bottom: 0px dotted #bbb; } :link:hover, :visited:hover { background-color: transparent; color: #555; } h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited, h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited { color: inherit; } /* Heading anchors */ .anchor:link, .anchor:visited { border: none; color: #d7d7d7; font-size: .8em; vertical-align: text-top; } * > .anchor:link, * > .anchor:visited { visibility: hidden; } h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, h4:hover .anchor, h5:hover .anchor, h6:hover .anchor { visibility: visible; } @media screen { a.ext-link .icon { background: url(../extlink.gif) left center no-repeat; padding-left: 16px; } * html a.ext-link .icon { display: inline-block; } } /* Forms */ input, textarea, select { margin: 0px } input, select { vertical-align: middle } input[type=button], input[type=submit], input[type=reset] { background-color: #517489; /* Modified */ color: #ffffff; /* Modified */ border: 1px solid #334a57; /* Modified */ padding: .1em .5em; } input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover { background: #303030; /* Modified */ border-color: #292929; /* Modified */ color: #ffffff; /* Modified */ } input[type=button][disabled], input[type=submit][disabled], input[type=reset][disabled] { background: #f6f6f6; border-style: solid; color: #999; } input[type=text], input.textwidget, textarea { border: 1px solid #8caabb; background-color: #d5e1e7; color: #000000; } /* Modified */ input[type=text], input.textwidget { padding: .2em .5em } input[type=text]:focus, input.textwidget:focus, textarea:focus { border-color: #517489; /* Modified */ } option { border-bottom: 1px dotted #d7d7d7 } fieldset { border: 1px solid #d7d7d7; padding: .5em; margin: 0 } fieldset.iefix { background: transparent; border: none; padding: 0; margin: 0 } * html fieldset.iefix { width: 98% } fieldset.iefix p { margin: 0 } legend { color: #999; padding: 0 .25em; font-size: 90%; font-weight: bold } label.disabled { color: #d7d7d7 } .buttons { margin: .5em .5em .5em 0 } .buttons form, .buttons form div { display: inline } .buttons input { margin: 1em .5em .1em 0 } .inlinebuttons input { font-size: 70%; border-width: 1px; border-style: dotted; margin: 0; padding: 0.1em; background: none; } input[type=submit] { background-color: #517489; } /* Header */ #header hr { display: none } #header h1 { margin: 1.5em 0 -1.5em; } #header img { border: none; margin: 0 0 -3em } #header :link, #header :visited, #header :link:hover, #header :visited:hover { background: transparent; color: #555; margin-bottom: 2px; border: none; } #header h1 :link:hover, #header h1 :visited:hover { color: #000 } /* Quick search */ #search, #topsearch { clear: both; font-size: 10px; height: 2.2em; margin: 0 71px 1em 0; /* Modified */ text-align: right; } #topsearch input, #topsearch input[type=button], #topsearch input[type=submit], #topsearch input[type=reset], #topsearch input[type=text], #topsearch input.textwidget { font-size: 10px; background-color: #333; /* Modified */ color: #ffffff; /* Modified */ border: 1px solid #222; /* Modified */ } #topsearch input[type=text] { background-color: #222; /* Modified */ } #search input, #search input[type=button], #search input[type=submit], #search input[type=reset], #search input[type=text], #search input.textwidget { font-size: 10px; background-color: #383838; /* Modified */ color: #ffffff; /* Modified */ border: 1px solid #333333; /* Modified */ } #search, #topsearch label { display: none } /* Navigation */ .nav h2, .nav hr { display: none } .nav ul { font-size: 10px; list-style: none; margin: 0; text-align: right } .nav li { border-right: 0px solid #d7d7d7; /* Modified */ display: inline; padding: 0 .75em; white-space: nowrap; } .nav li.last { border-right: none } /* Main navigation bar */ #mainnav { background: #f7f7f7 url(../menu_grad.png) 0 0; /* Modified */ border: 0px solid #000; /* Modified */ font: normal 10px trebuchet ms,verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif; /* Modified */ margin: 0; /* Modified */ padding: .3em 50px .3em 0; /* Modified */ } #mainnav li { border-right: none; padding: .25em 0 } #mainnav :link, #mainnav :visited { background: url(../dots.gif) 0 0 no-repeat; border-right: 0px solid #fff; /* Modified */ border-bottom: none; border-left: 0px solid #555; /* Modified */ color: #ffffff; /* Modified */ padding: .3em 20px; /* Modified */ } * html #mainnav :link, * html #mainnav :visited { background-position: 1px 0 } #mainnav :link:hover, #mainnav :visited:hover { background-color: #444444; /* Modified */ border-right: 0px solid #ddd; /* Modified */ } #mainnav .active :link, #mainnav .active :visited { background: #444444 0 0 repeat-x; /* Modified */ border-top: none; border-right: 0px solid #000; /* Modified */ color: #eee; font-weight: bold; } #mainnav .active :link:hover, #mainnav .active :visited:hover { /* border-right: 1px solid #000; */ } /* Context-dependent navigation links */ #ctxtnav { background-image: url(../ctxt_grad.png); height: 30px; padding: 5px 61px 0 0; } /* Modified */ #ctxtnav li ul { background: #f7f7f7; color: #ccc; border: 1px solid; padding: 0; display: inline; margin: 0; } #ctxtnav li li { padding: 0; } #ctxtnav li li :link, #ctxtnav li li :visited { padding: 0 1em } #ctxtnav li li :link:hover, #ctxtnav li li :visited:hover { background: #bba; color: #fff; } #ctxtnav :link, #ctxtnav :visited { color: #555555; } #ctxtnav :link:hover, #ctxtnav :visited:hover { color: #999999; } /* Alternate links */ #altlinks { clear: both; text-align: center } #altlinks h3 { font-size: 12px; letter-spacing: normal; margin: 0 } #altlinks ul { list-style: none; margin: 0; padding: 0 0 1em } #altlinks li { border-right: 1px solid #d7d7d7; display: inline; font-size: 11px; line-height: 16px; padding: 0 1em; white-space: nowrap; } #altlinks li.last { border-right: none } #altlinks li :link, #altlinks li :visited { background-position: 0 -1px; background-repeat: no-repeat; border: none; } #altlinks li a.ics { background-image: url(../ics.png); padding-left: 22px } #altlinks li a.rss { background-image: url(../xml.png); padding-left: 42px } /* Footer */ #footer { clear: both; color: #bbb; font-size: 10px; border-top: 0px solid; /* Modified */ height: 150px; /* Modified */ background-image: url(../footer_grad.png); /* Modified */ padding: 50px 50px 0 50px; /* Modified */ } #footer :link, #footer :visited { color: #bbb; } #footer hr { display: none } #footer #tracpowered { border: 0; float: left } #footer #tracpowered:hover { background: transparent } #footer p { margin: 0 } #footer p.left, p.left2 { float: left; margin-left: 1em; padding: 0 1em; } #footer p.left { border-left: 1px solid #d7d7d7; border-right: 1px solid #d7d7d7; } #footer p.left2 { color: #666; } #footer p.right { float: right; text-align: right; } #content { padding:0 50px 0 50px; position: relative } #help { clear: both; color: #999; font-size: 90%; margin: 1em; text-align: right; } #help :link, #help :visited { cursor: help } #help hr { display: none } /* Page preferences form */ #prefs { background: #edf9c0; border: 1px solid #bce715; float: right; font-size: 9px; padding: .8em; position: relative; margin: 0 1em 1em; } * html #prefs { width: 26em } /* Set width only for IE */ #prefs input, #prefs select { font-size: 9px; vertical-align: middle } #prefs fieldset { background: transparent; border: none; margin: .5em; padding: 0; } #prefs fieldset legend { background: transparent; color: #000; font-size: 9px; font-weight: normal; margin: 0 0 0 -1.5em; padding: 0; } #prefs .buttons { text-align: right } /* Version information (browser, wiki, attachments) */ #info { margin: 1em 0 0 0; background: #f7f7f0; border: 1px solid #d7d7d7; border-collapse: collapse; border-spacing: 0; clear: both; width: 100%; } #info th, #info td { font-size: 85%; padding: 2px .5em; vertical-align: top } #info th { font-weight: bold; text-align: left; white-space: nowrap } #info td.message { width: 100% } #info .message ul { padding: 0; margin: 0 2em } #info .message p { margin: 0; padding: 0 } /* Wiki */ .wikipage { padding-left: 18px; margin: 0; } /* Modified */ .wikipage h1, .wikipage h2, .wikipage h3 { margin-left: -18px } a.missing:link, a.missing:visited, span.missing { color: #998 } a.missing:link, a.missing:visited { background: #fafaf0 } a.missing:hover { color: #000 } a.closed:link, a.closed:visited { text-decoration: line-through } dl.wiki dt { font-weight: bold } dl.compact dt { float: left; padding-right: .5em } dl.compact dd { margin: 0; padding: 0 } div.code, pre.wiki, pre.literal-block { background: #edf9c0; border-width: 1px 0 1px 0; border-style: solid; border-color: #bce715; margin: 30px; padding: 10px; overflow: auto; } blockquote.citation { margin: -0.6em 0; border-style: solid; border-width: 0 0 0 2px; padding-left: .5em; border-color: #b44; } .citation blockquote.citation { border-color: #4b4; } .citation .citation blockquote.citation { border-color: #44b; } .citation .citation .citation blockquote.citation { border-color: #c55; } table.wiki { border: 2px solid #ccc; border-collapse: collapse; border-spacing: 0; } table.wiki td { border: 1px solid #ccc; padding: .1em .25em; } .wikitoolbar { border: solid #d7d7d7; border-width: 1px 1px 1px 0; height: 18px; width: 208px; } .wikitoolbar :link, .wikitoolbar :visited { background: transparent url(../edit_toolbar.png) no-repeat; border: 1px solid #fff; border-left-color: #d7d7d7; cursor: default; display: block; float: left; width: 24px; height: 16px; } .wikitoolbar :link:hover, .wikitoolbar :visited:hover { background-color: transparent; border: 1px solid #fb2; } .wikitoolbar a#em { background-position: 0 0 } .wikitoolbar a#strong { background-position: 0 -16px } .wikitoolbar a#heading { background-position: 0 -32px } .wikitoolbar a#link { background-position: 0 -48px } .wikitoolbar a#code { background-position: 0 -64px } .wikitoolbar a#hr { background-position: 0 -80px } .wikitoolbar a#np { background-position: 0 -96px } .wikitoolbar a#br { background-position: 0 -112px } /* Styles for the form for adding attachments. */ #attachment .field { margin-top: 1.3em } #attachment label { padding-left: .2em } #attachment fieldset { margin-top: 2em } #attachment fieldset .field { float: left; margin: 0 1em .5em 0 } #attachment .options { float: left; padding: 0 0 1em 1em } #attachment br { clear: left } .attachment #preview { margin-top: 1em } /* Styles for the list of attachments. */ #attachments { border: 1px outset #996; padding: 1em } #attachments .attachments { margin-left: 2em; padding: 0 } #attachments dt { display: list-item; list-style: square; } #attachments dd { font-style: italic; margin-left: 0; padding-left: 0; } /* Styles for tabular listings such as those used for displaying directory contents and report results. */ table.listing { clear: both; border-bottom: 1px solid #d7d7d7; border-collapse: collapse; border-spacing: 0; margin-top: 1em; width: 100%; } table.listing th { text-align: left; padding: 0 1em .1em 0; font-size: 12px } table.listing thead { background: #f7f7f0 } table.listing thead th { border: 1px solid #d7d7d7; border-bottom-color: #999; font-size: 11px; font-weight: bold; padding: 2px .5em; vertical-align: bottom; } table.listing thead th :link:hover, table.listing thead th :visited:hover { background-color: transparent; } table.listing thead th a { border: none; padding-right: 12px } table.listing th.asc a, table.listing th.desc a { font-weight: bold } table.listing th.asc a, table.listing th.desc a { background-position: 100% 50%; background-repeat: no-repeat; } table.listing th.asc a { background-image: url(../asc.png) } table.listing th.desc a { background-image: url(../desc.png) } table.listing tbody td, table.listing tbody th { border: 1px dotted #ddd; padding: .33em .5em; vertical-align: top; } table.listing tbody td a:hover, table.listing tbody th a:hover { background-color: transparent; } table.listing tbody tr { border-top: 1px solid #ddd } table.listing tbody tr.even { background-color: #fcfcfc } table.listing tbody tr.odd { background-color: #f7f7f7 } table.listing tbody tr:hover { background: #eed !important } /* Styles for the error page (and rst errors) */ #content.error .message, div.system-message { background: #fdc; border: 2px solid #d00; color: #500; padding: .5em; margin: 1em 0; } #content.error pre, div.system-message pre { margin-left: 1em; overflow: auto } div.system-message p { margin: 0; } div.system-message p.system-message-title { font-weight: bold; } /* Styles for search word highlighting */ @media screen { .searchword0 { background: #ff9 } .searchword1 { background: #cfc } .searchword2 { background: #cff } .searchword3 { background: #ccf } .searchword4 { background: #fcf } } @media print { #header, #altlinks, #footer, #help { display: none } .nav, form, .buttons form, form .buttons { display: none } form.printableform { display: block } } /* Custom */ #top_grad { background-image: url(../top_grad.png); height: 26px; } #tab { height: 32px; width: 282px; color: #ffffff; text-align: center; margin: -2px 50px 0 0; background-image: url(../tab.png); background-repeat: no-repeat; float: right; } #tab :link, #tab :visited { background: transparent; color: #ffffff; font-size: 12px; border: none; padding: 4px; } #tab :link:hover, #tab :visited:hover { border-width: 0 0 1px 0; border-style: solid; border-color: #a4cd0d; } #banner { background-image: url(../main_grad.png); height: 135px; } #header { margin: 20px 0 0 50px; } #metanav { margin-right: 61px; } #metanav :link, #metanav :visited { color: #ffffff; } #metanav :link:hover, #metanav :visited:hover { color: #bbbbbb; } #metanav ul li.first { color: #ccc; } #side_bar { float: right; // width: 350px; background-color: #ffffff; padding: 0 0 0 15px; } #download_top { height: 145px; width: 273px; background-image: url(../dld_top.png); } #download_bottom { height: 91px; width: 273px; background-image: url(../dld_bottom.png); } #download { // height: 145px; width: 243px; background-image: url(../dld_back.png); background-repeat: repeat-y; padding: 0 10px 0 20px; } #download_old { height: 284px; width: 269px; // float: right; margin: 0; padding: 0 0 0 19px; background-image: url(../download.png); background-repeat: no-repeat; } #donate { height: 284px; width: 269px; // float: right; margin: 0; padding: 0 0 0 19px; } #download :link, #download :visited, #download :link:hover, #download :visited:hover { background: transparent; color: #9ac300; font-size: 12px; border: none; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/css/wiki.css0000644000175100001710000001361500000000000023232 0ustar00runnerdocker/* BEGIN inline of code.css */ /* div.code { background: #f7f7f7; border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto } */ div.code pre { margin: 0; } table.code { border: 1px solid #ddd; border-spacing: 0; border-top: 0; empty-cells: show; font-size: 12px; line-height: 130%; padding: 0; margin: 0 auto; table-layout: fixed; width: 100%; } table.code th { border-right: 1px solid #d7d7d7; border-bottom: 1px solid #998; font-size: 11px; } table.code th.lineno { width: 4em } table.code thead th { background: #eee; border-top: 1px solid #d7d7d7; color: #999; padding: 0 .25em; text-align: center; white-space: nowrap; } table.code tbody th { background: #eed; color: #886; font-weight: normal; padding: 0 .5em; text-align: right; vertical-align: top; } table.code tbody th :link, table.code tbody th :visited { border: none; color: #886; text-decoration: none; } table.code tbody th :link:hover, table.code tbody th :visited:hover { color: #000; } table.code tbody td { background: #fff; font: normal 11px monospace; overflow: hidden; padding: 1px 2px; vertical-align: top; } .image-file { background: #eee; padding: .3em } .image-file img { background: url(../imggrid.png) } /* Default */ .code-block span { font-family: monospace; } /* Comments */ .code-comment, .css_comment, .c_comment, .c_commentdoc, .c_commentline, .c_commentlinedoc, .h_comment,.pl_commentline, .p_commentblock, .p_commentline, .hphp_comment, .hphp_commentblock, .hphp_commentline, .yaml_comment { color: #998; font-style: italic; } /* Language keyword */ .code-keyword, .pl_word { color: #789; font-weight: bold } /* Type */ .code-type, .c_word, .c_word2, .p_classname, .hphp_classname{ color: #468; font-weight: bold; } /* Function */ .code-func, .p_defname { color: #900; font-weight: bold; border-bottom: none; } /* Pre-processor */ .code-prep, .c_preprocessor, .pl_preprocessor, .yaml_identifier { color: #999; font-weight: bold; } /* Language construct */ .code-lang, .p_word { color: #000; font-weight: bold } /* String */ .code-string, .c_string, .c_stringeol, .css_doublestring, .css_singlestring, .h_singlestring, .h_doublestring, .pl_string, .pl_string_q, .pl_string_qq, .pl_string_qr, .pl_string_qw, .pl_string_qx, .pl_backticks, .pl_character, .p_string, .p_stringeol, .hphp_string, .hphp_stringeol, .hphp_triple, .hphp_tripledouble, .p_character, .p_triple, .p_tripledouble { color: #b84; font-weight: normal; } /* Variable name */ .code-var { color: #f9f } /* SilverCity-specific styles */ .css_id, .css_class, .css_pseudoclass, .css_tag { color: #900000 } .css_directive { color: #009000; font-weight: bold } .css_important { color: blue } .css_operator { color: #000090; font-weight: bold } .css_tag { font-weight: bold } .css_unknown_identifier, .css_unknown_pseudoclass { color: red } .css_value { color: navy } .c_commentdockeyword { color: navy; font-weight: bold } .c_commentdockeyworderror { color: red; font-weight: bold } .c_character, .c_regex, .c_uuid, .c_verbatim { color: olive } .c_number { color: #099 } .h_asp { color: #ff0 } .h_aspat { color: #ffdf00 } .h_attribute { color: teal } .h_attributeunknown { color: red } .h_cdata { color: #373 } .h_entity { color: purple } .h_number { color: #099 } .h_other { color: purple } .h_script, .h_tag, .h_tagend { color: navy } .h_tagunknown { color: red } .h_xmlend, .h_xmlstart { color: blue } .pl_datasection { color: olive } .pl_error { color: red; font-weight: bold } .pl_hash { color: #000 } .pl_here_delim, .pl_here_q, .pl_here_qq, .pl_here_qx, .pl_longquote { color: olive } .pl_number { color: #099 } .pl_pod { font-style: italic } .pl_regex, .pl_regsubst { color: olive } .p_number { color: #099 } .hphp_character { color: olive } .hphp_defname { color: #099; font-weight: bold } .hphp_number { color: #099 } .hphp_word { color: navy; font-weight: bold } .yaml_document { color: gray; font-style: italic } .yaml_keyword { color: #808 } .yaml_number { color: #800 } .yaml_reference { color: #088 } .v_comment { color: gray; font-style: italic } .v_commentline, .v_commentlinebang { color: red; font-style: italic } .v_number, .v_preprocessor { color: #099 } .v_string, .v_stringeol { color: olive } .v_user{ color: blue; font-weight: bold } .v_word, .v_word3 { color: navy; font-weight: bold } .v_word2 { color: green; font-weight: bold } /* END OF code.css */ /* @import url(code.css); */ /* Styles for the page editing form */ #edit #rows { float: right; font-size: 80% } #edit #rows select { font-size: 90% } #edit #text { clear: both; width: 100% } #edit .wikitoolbar { float: left; } #changeinfo { padding: .5em } #changeinfo .field { float: left; margin: 0 1em .5em 0 } #changeinfo br { clear: left } #changeinfo .options { padding: 0 0 1em 1em } #changeinfo .options, #changeinfo .buttons { clear: left } #delete { margin-left: 6em } #preview { background: #f4f4f4 url(../draft.png); margin: 1em 0 2em; overflow: auto; } /* Diff view */ #overview .multi { color: #999 } #overview .ipnr { color: #999; font-size: 80% } #overview .comment { padding: 1em 0 0 } /* Styles for the page history table (extends the styles for "table.listing") */ #wikihist td { padding: 0 .5em } #wikihist td.date, #wikihist td.diff, #wikihist td.version, #wikihist td.author { white-space: nowrap; } #wikihist td.version { text-align: center } #wikihist td.comment { width: 100% } @media print { th.diff, td.diff { display: none } } /* Styles for the TracGuideToc wikimacro */ .wiki-toc { padding: .5em 1em; margin: 0 0 2em 1em; float: right; border: 1px solid #bce715; background: #edf9c0; font-size: 85%; position: relative; } .wiki-toc h4 { font-size: 12px; margin: 0; color: #444444; } .wiki-toc ul, .wiki-toc ol { list-style: none; padding: 0; margin: 0 } .wiki-toc ul ul, .wiki-toc ol ol { padding-left: 1.2em } .wiki-toc li { margin: 0; padding: 0 } .wiki-toc .active { background: #ddf482; position: relative; padding: 2px 0 2px 5px; border-left: 2px solid #bce715; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/ctxt_grad.png0000644000175100001710000000030000000000000023435 0ustar00runnerdockerPNG  IHDR#z;9sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<=IDAT8c~2 ###9FF&&&:84-4*C J'F!.yWIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/extlink.gif0000644000175100001710000000013200000000000023120 0ustar00runnerdockerGIF89a ׿!, 8,C@"& H4hdyTYH(LdS;././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/footer_grad.png0000644000175100001710000000031400000000000023756 0ustar00runnerdockerPNG  IHDRr;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IIDAT8ݏ1 0 e,.;B[ m㧝7%UB =v9waIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/main_grad.png0000644000175100001710000000025600000000000023411 0ustar00runnerdockerPNG  IHDRKRsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<+IDAT(c  ?Aivj[9 9.' IENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/menu_grad.png0000644000175100001710000000024200000000000023424 0ustar00runnerdockerPNG  IHDRsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATc @4Db>\fIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/tab.png0000644000175100001710000000307500000000000022240 0ustar00runnerdockerPNG  IHDR %sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDATxo/xȲ%uaȆ]ѡCa;ݿa0ARY[9;'Ϯ(Z'J.[ı-R|mu}R䓏͈ь]OF!8~GeB;09h&QOe ~q4G?qP(z63X` Eqttv]۴ CE$!I8xkqϧTRR=wȕo$Q}%صƿWtSUEQ{EQG?h~O%صv-|.^ůurBinyxJeiVXi~ʶm(ip;dDq9^#bYeaƑѓ:W/븮KX$ C~mVzGjKqzwt uqȹqܓ4 aMۤ}rsR4w1MrL,ߘnP[OڬbJr])%TU=q'UU}0 Y9amOi!D٬ܜ`p]wCs۹-hEq]((,?eʣ4[@څz6?Q(!r$p94%.Hfh^ŶmvܽQGlZ[b2og6_UrPkp]"oWYe=h BٶͿ>?-# Bde~L#S H|%~,r.Ĥv|%L#]wZ4j|UYY,KIin|wQ uz.S HQ^ʯsδOI{opo (J?bp+_/5⨚ 2y-po(J%6e6Vlp.ouDap4V*@rm>ԯhxӡRR( y'`b[RV Y&T=Jck:;%^%M(S]zS4L3ݯLԄ8t]?$rfd8I0 8$Iƛo EQƛa~*üVaIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/top_grad.png0000644000175100001710000000026400000000000023266 0ustar00runnerdockerPNG  IHDRv6sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<1IDATcJ1?):]IENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/trac_banner.png0000644000175100001710000004746300000000000023761 0ustar00runnerdockerPNG  IHDRRK"{sBIT|dtEXtSoftwarewww.inkscape.org< IDATxy|Tg}'IH nKQ\ZEE^V7VikbŽuTj+TDEPv!d%۬g0LD{^d9y /4888CCW((?I&vZ"=:u*^x!uuu޽v8 2^-6l>W^y%>J;$Irm֣n77|3]wjŨQ8ꪫرcad/I'O .{M6{:;`ܸq͛կ~Ջ5:0 ^ɓx<=.wȐ!G$ϟq .;={BJJA3{lRSSذa{ASOQPPNuuAq x^^z%UUƯ7ń y~W'[6.R {=^|^(.2^333y-" C=~3A*B[n=ܳgݻwnG$~?=.@$ ꙁZD@ t(8locccknnG"z B;8n6_"\s5 >z]؇/2\.#I:[D@8ӾJ}Oϑ${Q__π~jG (\y\tE D0Fc=ƬYXn]O_gy?K"&?y%ܣiZ֧;hll7ߤUU=u߂;dee^Q;vaUC4ϟOKK @\v]!MKK㮻bС z-}Y~s'Zgdd_7|]{R vq\/ p8HKK$CWG@ [D`Ι#(~xtĈ< D4 1m4MgA?98w },w[D|r9C0<3QcͦMhoodY t8bp:As p3iҤݺu+v7oc|'[aÆ1k,x ۞0~x^d ~i^wuV{+ϟ};7o$/>KxV$N& &P^^Njj*aHMM +W䣏>:ng񔕕GZZ---455f>>(o3f N;ٳgôp ̟?zGu. Zvł kUyj*o[DQd|Ws1q <7xO>_Zynɒn^y.J̙>q 7p)r,#cTWW3w\v222k)**"??A[[wfժƯ7(r '0zhO?͛9(.pCe{9k/y曹[8ӭ璓>}:s^CUnק'{챀.x7;U\.&L` OYe.^z͵~_r%q{aĈ e&''#rȰʋO>P(dVY[/ EVV{,?O1cF8̘10`Fbq0 |׌7|h7f}tMV]bFa 6 3f k֬_u\ cx8䓭}]HJJ`ȑH_|ĉ`Μ98s8sʋRƎ}GCCC.;ab~nuByy9OC6mڄ(D"kYjӧO駟Yt)7|ٴiSN '؅{}ntc'r#].W'(֚%a%o8ݻymsθXpa9e~_ H۷S__@t]k ,b>~xd9Qt8wy97>#FGD:]o,)SЦr饗,77[n%L>=|>K>***֜U4K//#۷oncÆ L2;#n2~Kߑ̙3y饗ٸq#ӧO̚5~%Ktjv8p ^lχn3d)JH~?ׯSotSZZ SpBDQt"2%׳k׮8umm-K,t)))7zYg1flܸ`0Hjj*_|1z+RN9꫸袋۷/`Z_{5VXڵkeb;g};3ɓ'3i$կjyꩧ[6l=a+lٲR S%رZH6CR__oqQBXn]`UUUYo;']Dq'!6F|]'mܸ?5 ?K.$FMUUPUUy%&i:s)**ÁsNlcBEgƢbΜ9{dgg3|p((( SOx#Saa!gq#//UU|uV*++|)))v)vrrrTUEemƖ-[,U=]wXE ?@DUUe̙x[ou⤓N3C W^aڴiq,Q5k#F:b٣>JQQAPˁ 6E%(XbATʀӏ)=rk^ "]ssѱZ666QCQ~477w<3L~ѷo_1b{.z+\rIsTVVxbԕFo׿v Y&M/$!0cڵ_-[tiXɜ%.˚'aqF~_[x<ٳ9s&YYYܸq#z+vK/My1c Ex|wD"l6#Gd$''[ba,Z/֬YCyy9vԏ(UYYɝw<@^^̜9ϊh:Zs/N+D%'_:b… 4I'ڵk-`ƨ 7l-[+W ns뭷rUW}m;Xڵ;wRXXngŊGL8>?ٳIl^`XعEUUSNw#--Gy3ftW9N#+gfڴiq>j-+Gy5440/y9Sx뭷oIu7|3aAaOn%].,,j˩8##)Q񐗗_~l6KXQQaF>wʕ#^Nxˁ=bk;quOXFXJw 8'oͮx;xN+|'X~ͣ+D9sXn?/g~w|q8u{c(3.~?s(vYYfbܸq|r-Y1ѿ:^z)pW_}뮻Κ6m/?O袋زe dff ':t(V:ߡ:ׯhSw:=( 3gwLwܱ_+qW}dPTTOT5==0DNIII|̀{lٲnXB}v<Ox#{ꩧ2`EEHnjs Ct5ehhhﺌD"+VX`H<@ ygx8쳭xٳgs7Z|ee%\r {/O?tB ٰaFO4qqg…ar8q"`G#N Pź5.D礓N괜e˖Ye}tTVVZ"55n Q;ԩS{2];7nHEEIII-' z8餓W_}u@ۡ΅&2_~9غu+iiiVDU17te c%)SmK.{_]nN /;wn벳O5{B,f͚in;AŹB;̘1d&(j#3g;N#ªE>E/zjN;4P e\.¸Y˗/t]gɒ%#0glB8ȷ~: Y੧bʕlܸzzdffRTTć~8?)SfRq_g eee1G9/ر|͛fzRSS<|Ǭ^χ ++?N8RӢEMoO>^P(DUUUwyv EQ裏(++gÆ =ݎ=SvKmNJ+8s3geTWW HJJcZe]Ƅ شi6m–O:$/6nW_}/)) 2999g?" q/ IDATR;wr7r뭷%i>Sp曌7?O Ν;Yf iii?A@ؼyu.7|sPEFqщ'Z:t54o|8}r媫p{n^y빑#GZ:{W4m`ǎkMl+# w…\q*.sYD^/_|q>NcA$.rkQUW«.]jl碋.fqM71eʔ-[ypfhhgI4i׿կ~_"`.B֭[Gnn.YYYGxk<305kX?AhiӦxϟ`:Xϝ;GydɢBrO2}dGZI&}b|kŋyꩧ:͢Ekk+ӟK0 qVXp t'N /p8^ss3-L)ҩ}͚C=o~}D}[]]͸q|ݻ2e ֭o߾]Ҝ\$aܹ} kGIXh6l`ƌ3}tn7s̱4BUU{1^z&]-ܹsYr%p?p"LCCDHNj1e˖DHIIvc 466R]][oEcc#^͛7s72zh42/"Nff&)))Nkk+{졹9nC?LRR 8|멬d,_Ái.Ds ?V455ؘ@t>yFI~ׯ,SUUEUU-󑞞)..N𽎢 2RөR&橯W_%`ŋ|7B[ZZhݺuūa}9GqhHwlTMUuV%ll q8u`N(ADǨgv?̆5ݱKn&c@ ql(q/2)rPc㣗M$5U?bӗo6 \!mdr {{]ai߱^ M=d?Cvzd%K2e UUUdgg~r?&?f71Gɰ3>8NFDsq'i%gP 'DiIvH{(QDMdo"|Q_)uE 8Hz~34?N܂ãD:Q(=y Gn cs*fq&(=y t YhܞIސ]0(, 3pv |n?1ptE(؃K$ܕ NOJߢzvs@jv+I픜+brIIV);e3َݭIk8󨝲S79)SInY-Jn Qz Gn#)GKm”LIFkm &[!D$- MԜrvѼkqHJН44e(9:tUbS[[TOkݾ@!myW*&oX C#t7EoetT%8r^[WJ&FoE7yp\5}#C;] %'nHn`IP6pvgPz&.VQ@;NՑܧA'n߰ -u)S7bci -u=V#H&hkkcԩoOr5ׯQ}h/ÕJwm]崟[ZmM;툢AjjLØKnF0O?6l7!}aJ٩PúlWQtJK9OW[e4q7#&“=%5,ӰÎEd7떫VNg 9J䵤7A08Kf6; ur"saox5lfe33{rr: v+*p*.\ArL°dĄ8ӷ pzCoFYii4 C~!}bG-.Z%= ƩmOe) 4=B[\'r;X[ >E~+>F[[GncOS+Œ<R Γv ǹq?=-fR: 6O ~`[ Nt,l|*MJ&|#֛Fhl`R MYX0 .M*JO]Ŷ2{Z9ńæ#}FA'LZNNo?S\I+0 */WV9X"" կdʔ)<$''SPP1è ś7DžܪٙQT,Ksh6+OB}N }.lry|> |-5Zz<6;H$!3Ȅ[3 j -Qof5_K$πzI~6@v>R +D\7Dc4n-eql]C^fwSB;f~?T? vdcvvU 2i:[fWP<[,wAlt iA g/@niC']̊wN %++k $ %Fb4+A;5VmzX>ߴTyT,ͥ86/eЉ[M<$ȍ;>J9$+,aA7 hKCnk F2S249YG O 'mʫ!\fz:H Hfۚv=fS6jA:kf35 Z[ha4ThB̞T+ZrQٰa\s dgg)b j ō\vޙ yI4?+pfW l]=$%%v HJp:˄ڒ*0%L k'V[S"-- ݎK@ @JCEӟઔP%N_0KDL.\i4ʖَMnq\ER(~1k2N |A0tJ|N[Wl\~Gd0j MdFaи=4-ݥ?tg-7lCH؎( ;c}^Z ?!.VDjVA^BYd* 9!@~A (KQiV֐DP/|2hhm ;};=BΠZrZB^)!'ӯD&??|JS/'aWe&5fpپ}QTI ؽ91{@cs)Iz/$Ѵ#> `mPZ߭u^sm7e3hRvc/#l`@U^TK@x.8dpuǨaڪ>~9ԑf&ܲZ7s_(:~On~zǗvV1=\g =ag1Ҵ3-+Q#%Ѵ# -b?ϟPv.Q?݀,@ ڱ;U>G 5U`F2s|q%)ߏ]@F2 =SjRٲ2O]ԯa"La% G~a%Lp [?c(q]]>RHJJ_P9w)g ꪒXTf%mT|DžSW!ue_p≷{o띥~\>1}GjV-߼[F}U>Y׭%@@ʱ qwnZRJwb<8*͵ZEpf)YQzS*{G)%ŵln<Q@kSLflz~?YYY{3''I4n[dɅ!ڵ<Ab.O Fa1 +~[i2Si qݤc^`Jd!eq]ծ( :ClmN233e:RRRz|>BێVc癚j4Knim aIII]fM7\K Dߋx~d D"ڃ ZAzzűN χ`8HNNtqL:禦&+ƿc=QB󪭭Mh"PCf( qۉD"V(lt""ڈ`k& NRSS-s4 ].ңuO%d'3+:$1€q%QC6Kl^/~EGL4QGpD ;6Xnv QtGn0QhQ%[Z#$??vdQQ>)pyu}ụN2D?6~ ?1P(HasFD %""86Zl?.b+h(Fɮ`~(Ǐn(G#DLԟ504t=Qego>glgr#!`Cu4 p߻a)bB:}i`0D빮`:fwPڡG bwQOh?WgME1l C7ݮbuiշrc)vDɆ+ՏЀDBjv&0wU>Dm}Yv,:a)>1sI :ip@Dؐ$ Q2pxHa^KŁ,K$eis" ᠀DkpzUk;%H'`hvJ4'`%ݪ1;'Noɦ=.!BAsv ;L{aI67 HEyo]$]EOF qz"h|I~k3$YǾY5"Q$@$kxB5F ?mMvQu %`^98<tM@ ǁ $.A١* [eT_'DB];{Y[C EgRQ25Ȳ}adGA%nz;`Ǖ"0".7=@{ H8aȾzJ+9HK9GdF fCW% f2e̝nyh AJkwf@D|v3S kv%?B[d\0;UaĽD&AՍ(+%h-T@iot!O;+9 ;mNopsnQuDYɉ$J%Wr@ ]5`Dbӂ. McL EaAGe lC:˫lݎlWh茻 6 8:AfCcMJWFȧ!"6WF' cw+߇zL2vlӽ(X+w^LW*̾h_Dhk# s:Q? dl] dF0`>]HHF;?eJ6pPDt O#9E#bc hÎ5hcQB|ҏb(Ng5ICu~;v؜"i$uA5¾zQl*j(Dv7:ZD#l;YU Al*fْf)]tDYGU ] .&Od5"!vGԈ0VG5Iv6odJ]DS>Y0 MA׫^䨒At*Y Mvd+0ݽu)7 >U:;]ܢ2cCgA#: ;{E%H ĝ"J+MPbcV v|}?D+/͏~! ݡcs실D_zu"* K7Epђ`ElDB"EGmL.`0bu1j0Ova`ˌ#&:N$zCbyƒWÕoDѠ FHNٵ@ ` 1s.!k&S a|3/:qb˾yk"]' }f)Ͳ}$'A14a[Hu"ƴ'Q$7 HȬ ꨑF(d]fL i7]1thcClí`Y&nDQDu44F)@Q։&C2l|_j8]5@pHd6tUXA09k]%Q4S%{uY^G LnZW}==ݯ}ϟt\&x~~w*{gxK(̄"'{+LCkdx8RJ3tyhS)q>6/7 pwysw?(~OJ)j[יy`MCg4FZkVskq)̘1*1^˶mkw a>{pl6&6wc<Ҡv;J{ ^;ec;12'v"MNij / eIUUQut>G®2)WM&乕Kx*x]XAm6}tv;#P}N =R*t:+(W;yF $0U~hcy|eRt\F<<ϭy \On;g뚪rzVk$txKDQ] a}|C!NCdY6 *ɜx0B*ާ y4}!ܒ7w.+\k󣜞<'㑡&%ۘ "ˆ/P}SUUqEˀ5t OD*eYGkmc(l.R0B>Y0qFap8_Vsb ,l4J/I)` j e# M4M6ZeNu&^+" '>b>Zkk|щO]j{yإ4 uXBKqsRu] Gj0 n3nC&l)8J_ hbB%ڶ5J*RNL6&؈{`4_Ip |^-m1oCoـgxiR]4 u]7RW!$ɜLUصw( b%Klv\sewEm`MxՆ4޺2|Ls4v]gڵdrǕJVm[}{Cs4_:0x瘯%ڶʋhF}MJ+9#ryg_YNb+ES/`c\> 5!<#sF"af#^WaxBIz(rCC.\"з26s\y| -NO)#7µA֑diD]4eCRni >"@m<[+s#2E-S\ԕ;6\ȍ1_YEOp\掦CB4H]c -B{_g3h]gm]}N_~ Bs<e 23fnu2?_Lwso1}oU/&|[آĖMF}'1rx_D-7xR#ŌmsNBǶ|Mݭo]Vǖ٢WZl+y=R:oW~X"}X"}X\ nIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/twisted-icon.ico0000644000175100001710000000257600000000000024076 0ustar00runnerdockerh(  )))+++,,,///111444555666EEEIII\\\^^^___kkkmmm}}}~~~((&$ # 1," 0 ,  .+.*-55,0 !/#1$1%2'3(4)??././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/static/twistedtrac.css0000644000175100001710000001630300000000000024031 0ustar00runnerdocker/** * Sphinx stylesheet -- twistedtrac theme * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * (inherits from basic theme) * */ @import url("css/trac.css"); /* -- overrides to trac.css ------------------------------------------------- */ #mainnav li { color: #FFFFFF; padding: 0; } #mainnav { padding-right: 0px; } /* -- main layout ----------------------------------------------------------- */ div.clearer { clear: both; } /* -- relbar ---------------------------------------------------------------- */ div.related { width: 100%; font-size: 90%; } div.related h3 { display: none; } div.related ul { margin: 0; padding: 0 0 0 10px; list-style: none; } div.related li { display: inline; } div.related li.right { float: right; margin-right: 5px; } /* -- sidebar --------------------------------------------------------------- */ div.sphinxsidebarwrapper { padding: 10px 5px 0 10px; } div.sphinxsidebar { float: right; width: 230px; margin-left: -230px%; font-size: 90%; padding-right: 40px; /* use padding here? or corresponding margin? */ } div.sphinxsidebar ul { list-style: none; padding-left: 10px; } div.sphinxsidebar ul ul, div.sphinxsidebar ul.want-points { margin-left: 20px; list-style: square; } div.sphinxsidebar ul ul { margin-top: 0; margin-bottom: 0; } div.sphinxsidebar form { margin-top: 10px; } div.sphinxsidebar input { border: 1px solid #98dbcc; font-family: sans-serif; font-size: 1em; } img { border: 0; } /* -- search page ----------------------------------------------------------- */ ul.search { margin: 10px 0 0 20px; padding: 0; } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px; } ul.search li a { font-weight: bold; } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left; } ul.keywordmatches li.goodmatch a { font-weight: bold; } /* -- index page ------------------------------------------------------------ */ table.contentstable { width: 90%; } table.contentstable p.biglink { line-height: 150%; } a.biglink { font-size: 1.3em; } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90%; } /* -- general index --------------------------------------------------------- */ table.indextable td { text-align: left; vertical-align: top; } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0; } table.indextable tr.pcap { height: 10px; } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2; } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer; } /* -- general body styles --------------------------------------------------- */ a.headerlink { visibility: hidden; } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible; } div.body p.caption { text-align: inherit; } div.body td { text-align: left; } .field-list ul { padding-left: 1em; } .first { margin-top: 0 !important; } p.rubric { margin-top: 30px; font-weight: bold; } /* -- sidebars -------------------------------------------------------------- */ div.sidebar { margin: 0 0 0.5em 1em; border: 1px solid #ddb; padding: 7px 7px 0 7px; background-color: #ffe; width: 40%; float: right; } p.sidebar-title { font-weight: bold; } /* -- topics ---------------------------------------------------------------- */ div.topic { border: 1px solid #ccc; padding: 7px 7px 0 7px; margin: 10px 0 10px 0; } p.topic-title { font-size: 1.1em; font-weight: bold; margin-top: 10px; } /* -- admonitions ----------------------------------------------------------- */ div.admonition { margin-top: 10px; margin-bottom: 10px; padding: 7px; background-color: #FFFFCC; } div.admonition.warning { background-color: #FFCCCC; } div.admonition dt { font-weight: bold; } div.admonition dl { margin-bottom: 0; } p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } div.body p.centered { text-align: center; margin-top: 25px; } /* -- tables ---------------------------------------------------------------- */ table.docutils { border: 0; border-collapse: collapse; } table.docutils td, table.docutils th { padding: 1px 8px 1px 0; border-top: 0; border-left: 0; border-right: 0; border-bottom: 1px solid #aaa; } table.field-list td, table.field-list th { border: 0 !important; } table.footnote td, table.footnote th { border: 0 !important; } th { text-align: left; padding-right: 5px; } /* -- other body styles ----------------------------------------------------- */ dl { margin-bottom: 15px; } dd p { margin-top: 0px; } dd ul, dd table { margin-bottom: 10px; } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px; } dt:target, .highlight { /* background-color: #fbe54e; */ background-color: #eeffcc; } dl.glossary dt { font-weight: bold; font-size: 1.1em; } .field-list ul { margin: 0; padding-left: 1em; } .field-list p { margin: 0; } .refcount { color: #060; } .optional { font-size: 1.3em; } .versionmodified { font-style: italic; } .system-message { background-color: #fda; padding: 5px; border: 3px solid red; } .footnote:target { background-color: #ffa } .line-block { display: block; margin-top: 1em; margin-bottom: 1em; } .line-block .line-block { margin-top: 0; margin-bottom: 0; margin-left: 1.5em; } /* -- code displays --------------------------------------------------------- */ pre { overflow: auto; /* optional? */ padding: 10px; } td.linenos pre { padding: 5px 0px; border: 0; background-color: transparent; color: #aaa; } table.highlighttable { margin-left: 0.5em; } table.highlighttable td { padding: 0 0.5em 0 0.5em; } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; } tt.descclassname { background-color: transparent; } tt.xref, a tt { background-color: transparent; font-weight: bold; } h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { background-color: transparent; } /* -- math display ---------------------------------------------------------- */ img.math { vertical-align: middle; } div.body div.math p { text-align: center; } span.eqno { float: right; } /* -- printout stylesheet --------------------------------------------------- */ /* ----- @media print { div.document, div.documentwrapper, div.bodywrapper { margin: 0 !important; width: 100%; } div.sphinxsidebar, div.related, div.footer, #top-link { display: none; } } ----- */ div.documentwrapper { float: left; /* padding: 0 30px; */ /* use padding here? or margin in bodywrapper? */ /* width: 70%; */ margin-right: -300px; width: 100%; } div.bodywrapper { margin-right: 300px; padding-left: 40px; } a.api { font-family: monospace; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/_themes/twistedtrac/theme.conf0000644000175100001710000000011600000000000021437 0ustar00runnerdocker[theme] inherit = basic stylesheet = twistedtrac.css pygments_style = sphinx ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/api/0000755000175100001710000000000000000000000014260 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/api/index.rst0000644000175100001710000000034600000000000016124 0ustar00runnerdockerAPI Reference ============= This file will be overwritten by the pydoctor build triggered at the end of the Sphinx build. It's a hack to be able to reference the API index page from inside Sphinx and have it as part of the TOC. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/0000755000175100001710000000000000000000000014601 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/benchmarks/0000755000175100001710000000000000000000000016716 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/benchmarks/README0000644000175100001710000000127500000000000017603 0ustar00runnerdockerThis directory contains various simple programs intended to exercise various features of Twisted Conch as a way to learn about and track their performance characteristics. As there is currently no record of past benchmark results, the tracking aspect of this is currently somewhat fantastic. However, the intent is for this to change at some future point. All (one) of the programs in this directory are currently intended to be invoked directly and to report some timing information on standard out. The following benchmarks are currently available: buffering_mixin.py: This deals with twisted.conch.mixin.BufferingMixin which provides Nagle-like write coalescing for Protocol classes. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/benchmarks/buffering_mixin.py0000755000175100001710000001164500000000000022455 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Benchmarks comparing the write performance of a "normal" Protocol instance and an instance of a Protocol class which has had L{twisted.conch.mixin}'s L{BufferingMixin} mixed in to perform Nagle-like write coalescing. """ from pprint import pprint from sys import stdout from time import time from twisted.conch.mixin import BufferingMixin from twisted.internet import reactor from twisted.internet.defer import Deferred from twisted.internet.protocol import ClientCreator, Protocol, ServerFactory from twisted.python.log import startLogging from twisted.python.usage import Options class BufferingBenchmark(Options): """ Options for configuring the execution parameters of a benchmark run. """ optParameters = [ ( "scale", "s", "1", "Work multiplier (bigger takes longer, might resist noise better)", ) ] def postOptions(self): self["scale"] = int(self["scale"]) class ServerProtocol(Protocol): """ A silent protocol which only waits for a particular amount of input and then fires a Deferred. """ def __init__(self, expected, finished): self.expected = expected self.finished = finished def dataReceived(self, bytes): self.expected -= len(bytes) if self.expected == 0: finished, self.finished = self.finished, None finished.callback(None) class BufferingProtocol(Protocol, BufferingMixin): """ A protocol which uses the buffering mixin to provide a write method. """ class UnbufferingProtocol(Protocol): """ A protocol which provides a naive write method which simply passes through to the transport. """ def connectionMade(self): """ Bind write to the transport's write method and flush to a no-op function in order to provide the same API as is provided by BufferingProtocol. """ self.write = self.transport.write self.flush = lambda: None def _write(proto, byteCount): write = proto.write flush = proto.flush for i in range(byteCount): write("x") flush() def _benchmark(byteCount, clientProtocol): result = {} finished = Deferred() def cbFinished(ignored): result["disconnected"] = time() result["duration"] = result["disconnected"] - result["connected"] return result finished.addCallback(cbFinished) f = ServerFactory() f.protocol = lambda: ServerProtocol(byteCount, finished) server = reactor.listenTCP(0, f) f2 = ClientCreator(reactor, clientProtocol) proto = f2.connectTCP("127.0.0.1", server.getHost().port) def connected(proto): result["connected"] = time() return proto proto.addCallback(connected) proto.addCallback(_write, byteCount) return finished def _benchmarkBuffered(byteCount): return _benchmark(byteCount, BufferingProtocol) def _benchmarkUnbuffered(byteCount): return _benchmark(byteCount, UnbufferingProtocol) def benchmark(scale=1): """ Benchmark and return information regarding the relative performance of a protocol which does not use the buffering mixin and a protocol which does. @type scale: C{int} @param scale: A multiplier to the amount of work to perform @return: A Deferred which will fire with a dictionary mapping each of the two unicode strings C{u'buffered'} and C{u'unbuffered'} to dictionaries describing the performance of a protocol of each type. These value dictionaries will map the unicode strings C{u'connected'} and C{u'disconnected'} to the times at which each of those events occurred and C{u'duration'} two the difference between these two values. """ overallResult = {} byteCount = 1024 bufferedDeferred = _benchmarkBuffered(byteCount * scale) def didBuffered(bufferedResult): overallResult["buffered"] = bufferedResult unbufferedDeferred = _benchmarkUnbuffered(byteCount * scale) def didUnbuffered(unbufferedResult): overallResult["unbuffered"] = unbufferedResult return overallResult unbufferedDeferred.addCallback(didUnbuffered) return unbufferedDeferred bufferedDeferred.addCallback(didBuffered) return bufferedDeferred def main(args=None): """ Perform a single benchmark run, starting and stopping the reactor and logging system as necessary. """ startLogging(stdout) options = BufferingBenchmark() options.parseOptions(args) d = benchmark(options["scale"]) def cbBenchmark(result): pprint(result) def ebBenchmark(err): print(err.getTraceback()) d.addCallbacks(cbBenchmark, ebBenchmark) def stopReactor(ign): reactor.stop() d.addBoth(stopReactor) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/examples/0000755000175100001710000000000000000000000016417 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo.tac0000644000175100001710000000174400000000000020042 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo.tac """Nearly pointless demonstration of the manhole interactive interpreter. This does about the same thing as demo_manhole, but uses the tap module's makeService method instead. The only interesting difference is that in this version, the telnet server also requires authentication. Note, you will have to create a file named \"passwd\" and populate it with credentials (in the format of passwd(5)) to use this demo. """ from twisted.application import service application = service.Application("TAC Demo") from twisted.conch import manhole_tap manhole_tap.makeService( { "telnetPort": "tcp:6023", "sshPort": "tcp:6022", "namespace": {"foo": "bar"}, "passwd": "passwd", "sshKeyDir": "", "sshKeyName": "server.key", "sshKeySize": 4096, } ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo_draw.tac0000644000175100001710000000605200000000000021054 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo_draw.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ A trivial drawing application. Clients are allowed to connect and spew various characters out over the terminal. Spacebar changes the drawing character, while the arrow keys move the cursor. """ from twisted.application import internet, service from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol class Draw(insults.TerminalProtocol): """Protocol which accepts arrow key and spacebar input and places the requested characters onto the terminal. """ cursors = list("!@#$%^&*()_+-=") def connectionMade(self): self.terminal.eraseDisplay() self.terminal.resetModes([insults.modes.IRM]) self.cursor = self.cursors[0] def keystrokeReceived(self, keyID, modifier): if keyID == self.terminal.UP_ARROW: self.terminal.cursorUp() elif keyID == self.terminal.DOWN_ARROW: self.terminal.cursorDown() elif keyID == self.terminal.LEFT_ARROW: self.terminal.cursorBackward() elif keyID == self.terminal.RIGHT_ARROW: self.terminal.cursorForward() elif keyID == " ": self.cursor = self.cursors[ (self.cursors.index(self.cursor) + 1) % len(self.cursors) ] else: return self.terminal.write(self.cursor) self.terminal.cursorBackward() def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Insults Demo App") makeService({"protocolFactory": Draw, "telnet": 6023, "ssh": 6022}).setServiceParent( application ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo_insults.tac0000644000175100001710000001615500000000000021625 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo_insults.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ Various simple terminal manipulations using the insults module. This demo sets up two listening ports: one on 6022 which accepts ssh connections; one on 6023 which accepts telnet connections. No login for the telnet server is required; for the ssh server, \"username\" is the username and \"password\" is the password. The TerminalProtocol subclass defined here ignores most user input (except to print it out to the server log) and spends the duration of the connection drawing (the author's humble approximation of) raindrops at random locations on the client's terminal. +, -, *, and / are respected and each adjusts an aspect of the timing of the animation process. """ import random import string from twisted.application import internet, service from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol, task from twisted.python import log class DrawingFinished(Exception): """Sentinel exception, raised when no \"frames\" for a particular \"animation\" remain to be drawn. """ class Drawable: """Representation of an animation. Constructed with a protocol instance and a coordinate on the screen, waits for invocations of iterate() at which point it erases the previous frame of the animation and draws the next one, using its protocol instance and always placing the upper left hand corner of the frame at the given coordinates. Frames are defined with draw_ prefixed methods. Erasure is performed by erase_ prefixed methods. """ n = 0 def __init__(self, proto, col, line): self.proto = proto self.col = col self.line = line def drawLines(self, s): lines = s.splitlines() c = self.col line = self.line for l in lines: self.proto.cursorPosition(c - len(lines) / 2, line) self.proto.write(l) line += 1 def iterate(self): getattr(self, "erase_" + str(self.n))() self.n += 1 f = getattr(self, "draw_" + str(self.n), None) if f is None: raise DrawingFinished() f() def erase_0(self): pass class Splat(Drawable): HEIGHT = 5 WIDTH = 11 def draw_1(self): # . . # . . . # . . self.drawLines(" . .\n. . .\n . .") def erase_1(self): self.drawLines(" \n \n ") def draw_2(self): # . . . . # . o o o . # . o o o o . # . o o o . # . . . . self.drawLines(" . . . .\n . o o o .\n. o o o o .\n . o o o .\n . . . .") def erase_2(self): self.drawLines(" \n \n \n \n ") def draw_3(self): # o o o o # o O O O o # o O O O O o # o O O O o # o o o o self.drawLines(" o o o o\n o O O O o\no O O O O o\n o O O O o\n o o o o") erase_3 = erase_2 def draw_4(self): # O O O O # O . . . O # O . . . . O # O . . . O # O O O O self.drawLines(" O O O O\n O . . . O\nO . . . . O\n O . . . O\n O O O O") erase_4 = erase_3 def draw_5(self): # . . . . # . . # . . # . . # . . . . self.drawLines(" . . . .\n . .\n. .\n . .\n . . . .") erase_5 = erase_4 class Drop(Drawable): WIDTH = 3 HEIGHT = 4 def draw_1(self): # o self.drawLines(" o") def erase_1(self): self.drawLines(" ") def draw_2(self): # _ # / \ # \./ self.drawLines(" _ \n/ \\\n\\./") def erase_2(self): self.drawLines(" \n \n ") def draw_3(self): # O self.drawLines(" O") def erase_3(self): self.drawLines(" ") class DemoProtocol(insults.TerminalProtocol): """Draws random things at random places on the screen.""" width = 80 height = 24 interval = 0.1 rate = 0.05 def connectionMade(self): self.run() def connectionLost(self, reason): self._call.stop() del self._call def run(self): # Clear the screen, matey self.terminal.eraseDisplay() self._call = task.LoopingCall(self._iterate) self._call.start(self.interval) def _iterate(self): cls = random.choice((Splat, Drop)) # Move to a random location on the screen col = random.randrange(self.width - cls.WIDTH) + cls.WIDTH line = random.randrange(self.height - cls.HEIGHT) + cls.HEIGHT s = cls(self.terminal, col, line) c = task.LoopingCall(s.iterate) c.start(self.rate).addErrback(lambda f: f.trap(DrawingFinished)).addErrback( log.err ) # ITerminalListener def terminalSize(self, width, height): self.width = width self.height = height def unhandledControlSequence(self, seq): log.msg(f"Client sent something weird: {seq!r}") def keystrokeReceived(self, keyID, modifier): if keyID == "+": self.interval /= 1.1 elif keyID == "-": self.interval *= 1.1 elif keyID == "*": self.rate /= 1.1 elif keyID == "/": self.rate *= 1.1 else: log.msg(f"Client sent: {keyID!r}") return self._call.stop() self._call = task.LoopingCall(self._iterate) self._call.start(self.interval) def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Insults Demo App") makeService( {"protocolFactory": DemoProtocol, "telnet": 6023, "ssh": 6022} ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo_manhole.tac0000644000175100001710000000431300000000000021540 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo_manhole.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ An interactive Python interpreter with syntax coloring. Nothing interesting is actually defined here. Two listening ports are set up and attached to protocols which know how to properly set up a ColoredManhole instance. """ from twisted.application import internet, service from twisted.conch.insults import insults from twisted.conch.manhole import ColoredManhole from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Interactive Python Interpreter") makeService( { "protocolFactory": ColoredManhole, "protocolArgs": (None,), "telnet": 6023, "ssh": 6022, } ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo_recvline.tac0000644000175100001710000000566700000000000021741 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo_recvline.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ Demonstrates line-at-a-time handling with basic line-editing support. This is a variation on the echo server. It sets up two listening ports: one on 6022 which accepts ssh connections; one on 6023 which accepts telnet connections. No login for the telnet server is required; for the ssh server, \"username\" is the username and \"password\" is the password. The demo protocol defined in this module is handed a line of input at a time, which it simply writes back to the connection. HistoricRecvline, which the demo protocol subclasses, provides basic line editing and input history features. """ from twisted.application import internet, service from twisted.conch import recvline from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol class DemoRecvLine(recvline.HistoricRecvLine): """Simple echo protocol. Accepts lines of input and writes them back to its connection. If a line consisting solely of \"quit\" is received, the connection is dropped. """ def lineReceived(self, line): if line == "quit": self.terminal.loseConnection() self.terminal.write(line) self.terminal.nextLine() self.terminal.write(self.ps[self.pn]) def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Insults RecvLine Demo") makeService( {"protocolFactory": DemoRecvLine, "telnet": 6023, "ssh": 6022} ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/demo_scroll.tac0000644000175100001710000000717600000000000021425 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny demo_scroll.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ Simple echo-ish server that uses the scroll-region. This demo sets up two listening ports: one on 6022 which accepts ssh connections; one on 6023 which accepts telnet connections. No login for the telnet server is required; for the ssh server, \"username\" is the username and \"password\" is the password. The TerminalProtocol subclass defined here sets up a scroll-region occupying most of the screen. It positions the cursor at the bottom of the screen and then echos back printable input. When return is received, the line is copied to the upper area of the screen (scrolling anything older up) and clears the input line. """ import string from twisted.application import internet, service from twisted.conch.insults import insults from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol from twisted.python import log class DemoProtocol(insults.TerminalProtocol): """Copies input to an upwards scrolling region.""" width = 80 height = 24 def connectionMade(self): self.buffer = [] self.terminalSize(self.width, self.height) # ITerminalListener def terminalSize(self, width, height): self.width = width self.height = height self.terminal.setScrollRegion(0, height - 1) self.terminal.cursorPosition(0, height) self.terminal.write("> ") def unhandledControlSequence(self, seq): log.msg(f"Client sent something weird: {seq!r}") def keystrokeReceived(self, keyID, modifier): if keyID == "\r": self.terminal.cursorPosition(0, self.height - 2) self.terminal.nextLine() self.terminal.write("".join(self.buffer)) self.terminal.cursorPosition(0, self.height - 1) self.terminal.eraseToLineEnd() self.terminal.write("> ") self.buffer = [] elif keyID in list(string.printable): self.terminal.write(keyID) self.buffer.append(keyID) def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Scroll Region Demo App") makeService( {"protocolFactory": DemoProtocol, "telnet": 6023, "ssh": 6022} ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/index.rst0000644000175100001710000000207600000000000020265 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Examples ======== Simple SSH server and client ---------------------------- - :download:`sshsimpleclient.py` - simple SSH client - :download:`sshsimpleserver.py` - simple SSH server Simple telnet server -------------------- - :download:`telnet_echo.tac` - A telnet server which echoes data and events back to the client twisted.conch.insults examples ------------------------------ - :download:`demo.tac` - Nearly pointless demonstration of the manhole interactive interpreter - :download:`demo_draw.tac` - A trivial drawing application - :download:`demo_insults.tac` - Various simple terminal manipulations using the insults module - :download:`demo_recvline.tac` - Demonstrates line-at-a-time handling with basic line-editing support - :download:`demo_scroll.tac` - Simple echo-ish server that uses the scroll-region - :download:`demo_manhole.tac` - An interactive Python interpreter with syntax coloring - :download:`window.tac` - An example of various widgets ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/sshsimpleclient.py0000644000175100001710000001036100000000000022200 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import getpass import os import struct import sys from twisted.conch.ssh import channel, common, connection, keys, transport, userauth from twisted.internet import defer, protocol, reactor from twisted.python import log """ Example of using a simple SSH client. It will try to authenticate with a SSH key or ask for a password. Re-using a private key is dangerous, generate one. For this example you can use: $ ckeygen -t rsa -f ssh-keys/client_rsa """ # Replace this with your username. # Default username and password will match the sshsimpleserver.py USER = b"user" HOST = "localhost" PORT = 5022 SERVER_FINGERPRINT = b"55:55:66:24:6b:03:0e:f1:ec:f8:66:c3:51:df:27:4b" # Path to RSA SSH keys accepted by the server. CLIENT_RSA_PUBLIC = "ssh-keys/client_rsa.pub" # Set CLIENT_RSA_PUBLIC to empty to not use SSH key auth. # CLIENT_RSA_PUBLIC = '' CLIENT_RSA_PRIVATE = "ssh-keys/client_rsa" class SimpleTransport(transport.SSHClientTransport): def verifyHostKey(self, hostKey, fingerprint): print("Server host key fingerprint: %s" % fingerprint) if SERVER_FINGERPRINT == fingerprint: return defer.succeed(True) else: print("Bad host key. Expecting: %s" % SERVER_FINGERPRINT) return defer.fail(Exception("Bad server key")) def connectionSecure(self): self.requestService(SimpleUserAuth(USER, SimpleConnection())) class SimpleUserAuth(userauth.SSHUserAuthClient): def getPassword(self): return defer.succeed(getpass.getpass(f"{USER}@{HOST}'s password: ")) def getGenericAnswers(self, name, instruction, questions): print(name) print(instruction) answers = [] for prompt, echo in questions: if echo: answer = input(prompt) else: answer = getpass.getpass(prompt) answers.append(answer) return defer.succeed(answers) def getPublicKey(self): if ( not CLIENT_RSA_PUBLIC or not os.path.exists(CLIENT_RSA_PUBLIC) or self.lastPublicKey ): # the file doesn't exist, or we've tried a public key return return keys.Key.fromFile(filename=CLIENT_RSA_PUBLIC) def getPrivateKey(self): """ A deferred can also be returned. """ return defer.succeed(keys.Key.fromFile(CLIENT_RSA_PRIVATE)) class SimpleConnection(connection.SSHConnection): def serviceStarted(self): self.openChannel(TrueChannel(2 ** 16, 2 ** 15, self)) self.openChannel(FalseChannel(2 ** 16, 2 ** 15, self)) self.openChannel(CatChannel(2 ** 16, 2 ** 15, self)) class TrueChannel(channel.SSHChannel): name = b"session" # needed for commands def openFailed(self, reason): print("true failed", reason) def channelOpen(self, ignoredData): self.conn.sendRequest(self, "exec", common.NS("true")) def request_exit_status(self, data): status = struct.unpack(">L", data)[0] print("true status was: %s" % status) self.loseConnection() class FalseChannel(channel.SSHChannel): name = b"session" def openFailed(self, reason): print("false failed", reason) def channelOpen(self, ignoredData): self.conn.sendRequest(self, "exec", common.NS("false")) def request_exit_status(self, data): status = struct.unpack(">L", data)[0] print("false status was: %s" % status) self.loseConnection() class CatChannel(channel.SSHChannel): name = b"session" def openFailed(self, reason): print("echo failed", reason) def channelOpen(self, ignoredData): self.data = b"" d = self.conn.sendRequest(self, "exec", common.NS("cat"), wantReply=1) d.addCallback(self._cbRequest) def _cbRequest(self, ignored): self.write(b"hello conch\n") self.conn.sendEOF(self) def dataReceived(self, data): self.data += data def closed(self): print("got data from cat: %s" % repr(self.data)) self.loseConnection() reactor.stop() log.startLogging(sys.stdout) protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, PORT) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/sshsimpleserver.py0000755000175100001710000002306000000000000022233 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import sys from zope.interface import implementer from twisted.conch import avatar from twisted.conch.checkers import InMemorySSHKeyDB, SSHPublicKeyChecker from twisted.conch.ssh import connection, factory, keys, session, userauth from twisted.conch.ssh.transport import SSHServerTransport from twisted.cred import portal from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse from twisted.internet import protocol, reactor from twisted.python import components, log log.startLogging(sys.stderr) """ Example of running a custom protocol as a shell session over an SSH channel. Warning! This implementation is here to help you understand how Conch SSH server works. You should not use this code in production. Re-using a private key is dangerous, generate one. For this example you can use: $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key $ ckeygen -t rsa -f ssh-keys/client_rsa Re-using DH primes and having such a short primes list is dangerous, generate your own primes. In this example the implemented SSH server identifies itself using an RSA host key and authenticates clients using username "user" and password "password" or using a SSH RSA key. # Clean the previous server key as we should now have a new one $ ssh-keygen -f ~/.ssh/known_hosts -R [localhost]:5022 # Connect with password $ ssh -p 5022 -i ssh-keys/client_rsa user@localhost # Connect with the SSH client key. $ ssh -p 5022 -i ssh-keys/client_rsa user@localhost """ # Path to RSA SSH keys used by the server. SERVER_RSA_PRIVATE = "ssh-keys/ssh_host_rsa_key" SERVER_RSA_PUBLIC = "ssh-keys/ssh_host_rsa_key.pub" # Path to RSA SSH keys accepted by the server. CLIENT_RSA_PUBLIC = "ssh-keys/client_rsa.pub" # Pre-computed big prime numbers used in Diffie-Hellman Group Exchange as # described in RFC4419. # This is a short list with a single prime member and only for keys of size # 1024 and 2048. # You would need a list for each SSH key size that you plan to support in your # server implementation. # You can use OpenSSH ssh-keygen to generate these numbers. # See the MODULI GENERATION section from the ssh-keygen man pages. # See moduli man pages to find out more about the format used by the file # generated using ssh-keygen. # For Conch SSH server we only need the last 3 values: # * size # * generator # * modulus # # The format required by the Conch SSH server is: # # { # size1: [(generator1, modulus1), (generator1, modulus2)], # size2: [(generator4, modulus3), (generator1, modulus4)], # } # # twisted.conch.openssh_compat.primes.parseModuliFile provides a parser for # reading OpenSSH moduli file. # # Warning! Don't use these numbers in production. # Generate your own data. # Avoid 1024 bit primes https://weakdh.org # PRIMES = { 2048: [ ( 2, int( "2426544657763384657581346888965894474823693600310397077868393" "3705240497295505367703330163384138799145013634794444597785054" "5748125479903006919561762337599059762229781976243372717454710" "2176446353691318838172478973705741394375893696394548769093992" "1001501857793275011598975080236860899147312097967655185795176" "0369411418341859232907692585123432987448282165305950904719704" "0150626897691190726414391069716616579597245962241027489028899" "9065530463691697692913935201628660686422182978481412651196163" "9303832327425472811802778094751292202887555413353357988371733" "1585493104019994344528544370824063974340739661083982041893657" "4217939" ), ) ], 4096: [ ( 2, int( "8896338360072960666956554817320692705506152988585223623564629" "6621399423965037053201590845758609032962858914980344684974286" "2797136176274424808060302038380613106889959709419621954145635" "9745645498927756607640582597997083132103281857166287942205359" "2801914659358387079970048537106776322156933128608032240964629" "7706526831155237865417316423347898948704639476720848300063714" "8566690545913773564541481658565082079196378755098613844498856" "5501586550793900950277896827387976696265031832817503062386128" "5062331536562421699321671967257712201155508206384317725827233" "6142027687719225475523981798875719894413538627861634212487092" "7314303979577604977153889447845420392409945079600993777225912" "5621285287516787494652132525370682385152735699722849980820612" "3709076387834615230428138807577711774231925592999456202847308" "3393989687120016431260548916578950183006118751773893012324287" "3304901483476323853308396428713114053429620808491032573674192" "3854889258666071928702496194370274594569914312983133822049809" "8897129264121785413015683094180147494066773606688103698028652" "0892090232096545650051755799297658390763820738295370567143697" "6176702912637347103928738239565891710671678397388962498919556" "8943711148674858788771888256438487058313550933969509621845117" "4112035938859" ), ) ], } class ExampleAvatar(avatar.ConchUser): """ The avatar is used to configure SSH services/sessions/subsystems for an account. This account will use L{session.SSHSession} to handle a channel of type I{session}. """ def __init__(self, username): avatar.ConchUser.__init__(self) self.username = username self.channelLookup.update({b"session": session.SSHSession}) @implementer(portal.IRealm) class ExampleRealm: """ When using Twisted Cred, the pluggable authentication framework, the C{requestAvatar} method should return a L{avatar.ConchUser} instance as required by the Conch SSH server. """ def requestAvatar(self, avatarId, mind, *interfaces): """ See: L{portal.IRealm.requestAvatar} """ return interfaces[0], ExampleAvatar(avatarId), lambda: None class EchoProtocol(protocol.Protocol): """ This is our protocol that we will run over the shell session. """ def dataReceived(self, data): """ Called when client send data over the shell session. Just echo the received data and and if Ctrl+C is received, close the session. """ if data == b"\r": data = b"\r\n" elif data == b"\x03": # ^C self.transport.loseConnection() return self.transport.write(data) @implementer(session.ISession, session.ISessionSetEnv) class ExampleSession: """ This selects what to do for each type of session which is requested by the client via the SSH channel of type I{session}. """ def __init__(self, avatar): """ In this example the avatar argument is not used for session selection, but for example you can use it to limit I{shell} or I{exec} access only to specific accounts. """ def getPty(self, term, windowSize, attrs): """ We don't support pseudo-terminal sessions. """ def setEnv(self, name, value): """ We don't support setting environment variables. """ def execCommand(self, proto, cmd): """ We don't support command execution sessions. """ raise Exception("not executing commands") def openShell(self, transport): """ Use our protocol as shell session. """ protocol = EchoProtocol() # Connect the new protocol to the transport and the transport # to the new protocol so they can communicate in both directions. protocol.makeConnection(transport) transport.makeConnection(session.wrapProtocol(protocol)) def eofReceived(self): pass def closed(self): pass components.registerAdapter( ExampleSession, ExampleAvatar, session.ISession, session.ISessionSetEnv ) class ExampleFactory(factory.SSHFactory): """ This is the entry point of our SSH server implementation. The SSH transport layer is implemented by L{SSHTransport} and is the protocol of this factory. Here we configure the server's identity (host keys) and handlers for the SSH services: * L{connection.SSHConnection} handles requests for the channel multiplexing service. * L{userauth.SSHUserAuthServer} handlers requests for the user authentication service. """ protocol = SSHServerTransport # Server's host keys. # To simplify the example this server is defined only with a host key of # type RSA. publicKeys = {b"ssh-rsa": keys.Key.fromFile(SERVER_RSA_PUBLIC)} privateKeys = {b"ssh-rsa": keys.Key.fromFile(SERVER_RSA_PRIVATE)} # Service handlers. services = { b"ssh-userauth": userauth.SSHUserAuthServer, b"ssh-connection": connection.SSHConnection, } def __init__(self): passwdDB = InMemoryUsernamePasswordDatabaseDontUse(user="password") sshDB = SSHPublicKeyChecker( InMemorySSHKeyDB({b"user": [keys.Key.fromFile(CLIENT_RSA_PUBLIC)]}) ) self.portal = portal.Portal(ExampleRealm(), [passwdDB, sshDB]) def getPrimes(self): """ See: L{factory.SSHFactory} """ return PRIMES if __name__ == "__main__": reactor.listenTCP(5022, ExampleFactory()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/telnet_echo.tac0000644000175100001710000000267600000000000021414 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Simple echo server that echoes back client input. You can run this .tac file directly with: twistd -ny telnet_echo.tac This demo sets up a listening port on 6023 which accepts telnet connections. No login for the telnet server is required. """ from twisted.application.internet import TCPServer from twisted.application.service import Application from twisted.conch.telnet import TelnetProtocol, TelnetTransport from twisted.internet.protocol import ServerFactory class TelnetEcho(TelnetProtocol): def enableRemote(self, option): self.transport.write(b"You tried to enable " + option + b" (I rejected it)\r\n") return False def disableRemote(self, option): self.transport.write(b"You disabled " + option + b"\r\n") def enableLocal(self, option): self.transport.write( b"You tried to make me enable " + option + b" (I rejected it)\r\n" % (option,) ) return False def disableLocal(self, option): self.transport.write(b"You asked me to disable " + option + "\r\n") def dataReceived(self, data): self.transport.write(b"I received " + data + b" from you\r\n") factory = ServerFactory() factory.protocol = lambda: TelnetTransport(TelnetEcho) service = TCPServer(6023, factory) application = Application("Telnet Echo Server") service.setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/examples/window.tac0000644000175100001710000001474200000000000020427 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # You can run this .tac file directly with: # twistd -ny window.tac # # Re-using a private key is dangerous, generate one. # # For this example you can use: # # $ ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key """ Widgets demo. You can run this .tac file directly with: twistd -ny window.tac Demonstrates various widgets or buttons, such as scrollable regions, drawable canvas, etc. This demo sets up two listening ports: one on 6022 which accepts ssh connections; one on 6023 which accepts telnet connections. No login for the telnet server is required; for the ssh server, "username" is the username and "password" is the password. """ import random import string from twisted.application import internet, service from twisted.conch.insults import insults, window from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm from twisted.conch.ssh import keys from twisted.conch.telnet import TelnetBootstrapProtocol, TelnetTransport from twisted.cred import checkers, portal from twisted.internet import protocol, reactor, task from twisted.python import log class DrawableCanvas(window.Canvas): x = 0 y = 0 def func_LEFT_ARROW(self, modifier): self.x -= 1 self.repaint() def func_RIGHT_ARROW(self, modifier): self.x += 1 self.repaint() def func_UP_ARROW(self, modifier): self.y -= 1 self.repaint() def func_DOWN_ARROW(self, modifier): self.y += 1 self.repaint() def characterReceived(self, keyID, modifier): self[self.x, self.y] = keyID self.x += 1 self.repaint() def keystrokeReceived(self, keyID, modifier): if keyID == b"\r" or keyID == b"\v": return window.Canvas.keystrokeReceived(self, keyID, modifier) if self.x >= self.width: self.x = 0 elif self.x < 0: self.x = self.width - 1 if self.y >= self.height: self.y = 0 elif self.y < 0: self.y = self.height - 1 self.repaint() def render(self, width, height, terminal): window.Canvas.render(self, width, height, terminal) if self.focused: terminal.cursorPosition(self.x, self.y) ch = chr(self[self.x, self.y]) window.cursor(terminal, ch) class ButtonDemo(insults.TerminalProtocol): width = 80 height = 24 def _draw(self): self.window.draw(self.width, self.height, self.terminal) def _redraw(self): self.window.filthy() self._draw() def _schedule(self, f): reactor.callLater(0, f) def connectionMade(self): self.terminal.eraseDisplay() self.terminal.resetPrivateModes([insults.privateModes.CURSOR_MODE]) self.window = window.TopWindow(self._draw, self._schedule) self.output = window.TextOutput((15, 1)) self.input = window.TextInput(15, self._setText) selections = [num.encode("utf-8") for num in map(str, range(100))] self.select1 = window.Selection(selections, self._setText, 10) selections = [num.encode("utf-8") for num in map(str, range(200, 300))] self.select2 = window.Selection(selections, self._setText, 10) self.button = window.Button(b"Clear", self._clear) self.canvas = DrawableCanvas() hbox = window.HBox() hbox.addChild(self.input) hbox.addChild(self.output) hbox.addChild(window.Border(self.button)) hbox.addChild(window.Border(self.select1)) hbox.addChild(window.Border(self.select2)) t1 = window.TextOutputArea(longLines=window.TextOutputArea.WRAP) t2 = window.TextOutputArea(longLines=window.TextOutputArea.TRUNCATE) t3 = window.TextOutputArea(longLines=window.TextOutputArea.TRUNCATE) t4 = window.TextOutputArea(longLines=window.TextOutputArea.TRUNCATE) for _t in t1, t2, t3, t4: _t.setText(((b"This is a very long string. " * 3) + b"\n") * 3) vp = window.Viewport(t3) d = [1] def spin(): vp.xOffset += d[0] if vp.xOffset == 0 or vp.xOffset == 25: d[0] *= -1 self.call = task.LoopingCall(spin) self.call.start(0.25, now=False) hbox.addChild(window.Border(vp)) vp2 = window.ScrolledArea(t4) hbox.addChild(vp2) texts = window.VBox() texts.addChild(window.Border(t1)) texts.addChild(window.Border(t2)) areas = window.HBox() areas.addChild(window.Border(self.canvas)) areas.addChild(texts) vbox = window.VBox() vbox.addChild(hbox) vbox.addChild(areas) self.window.addChild(vbox) self.terminalSize(self.width, self.height) def connectionLost(self, reason): self.call.stop() insults.TerminalProtocol.connectionLost(self, reason) def terminalSize(self, width, height): self.width = width self.height = height self.terminal.eraseDisplay() self._redraw() def keystrokeReceived(self, keyID, modifier): self.window.keystrokeReceived(keyID, modifier) def _clear(self): self.canvas.clear() def _setText(self, text): self.input.setText(b"") self.output.setText(text) def makeService(args): checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(username=b"password") f = protocol.ServerFactory() f.protocol = lambda: TelnetTransport( TelnetBootstrapProtocol, insults.ServerProtocol, args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) tsvc = internet.TCPServer(args["telnet"], f) def chainProtocolFactory(): return insults.ServerProtocol( args["protocolFactory"], *args.get("protocolArgs", ()), **args.get("protocolKwArgs", {}), ) rlm = TerminalRealm() rlm.chainedProtocolFactory = chainProtocolFactory ptl = portal.Portal(rlm, [checker]) f = ConchFactory(ptl) f.publicKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key.pub") f.privateKeys[b"ssh-rsa"] = keys.Key.fromFile("ssh-keys/ssh_host_rsa_key") csvc = internet.TCPServer(args["ssh"], f) m = service.MultiService() tsvc.setServiceParent(m) csvc.setServiceParent(m) return m application = service.Application("Window Demo") makeService( {"protocolFactory": ButtonDemo, "telnet": 6023, "ssh": 6022} ).setServiceParent(application) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/howto/0000755000175100001710000000000000000000000015741 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/howto/conch_client.rst0000644000175100001710000004073200000000000021131 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Writing a client with Twisted Conch =================================== Introduction ------------ In the original days of computing, rsh/rlogin were used to connect to remote computers and execute commands. These commands had the problem that the passwords and commands were sent in the clear. To solve this problem, the SSH protocol was created. Twisted Conch implements the second version of this protocol. Using an SSH Command Endpoint ----------------------------- If your objective is to execute a command on a remote host over an SSH connection, then the easiest approach may be to use :py:class:`twisted.conch.endpoints.SSHCommandClientEndpoint` . If you haven't used endpoints before, first take a look at :doc:`the endpoint howto <../../core/howto/endpoints>` to get an idea of how endpoints work in general. Conch provides an endpoint implementation which establishes an SSH connection, performs necessary authentication, opens a channel, and launches a command in that channel. It then associates the output of that command with the input of a protocol you supply, and associates output from that protocol with the input of that command. Effectively, this lets you ignore most of the complexity of SSH and just interact with a remote process as though it were any other stream-oriented connection - such as TCP or SSL. Conch also provides an endpoint that is initialized with an already established SSH connection. This endpoint just opens a new channel on the existing connection and launches a command in that. Using the ``SSHCommandClientEndpoint`` is about as simple as using any other stream-oriented client endpoint. Just create the endpoint defining where the SSH server to connect to is and a factory defining what kind of protocol to use to interact with the command and let them get to work using the endpoint's ``connect`` method. :download:`echoclient_ssh.py ` .. literalinclude:: listings/echoclient_ssh.py For completeness, this example includes a lot of code to support different styles of authentication, reading (and possibly updating) existing *known_hosts* files, and parsing command line options. Focus on the latter half of the ``main`` function to see the code that is most directly responsible for actually doing the necessary SSH connection setup. ``SSHCommandClientEndpoint`` accepts quite a few options, since there is a lot of flexibility in SSH and many possible different server configurations, but once the endpoint object itself is created, its use is no more complicated than the use of any other endpoint: pass a factory to its ``connect`` method and attach a callback to the resulting ``Deferred`` to do something with the protocol instance. If you use an endpoint that creates new connections, the connection attempt can be cancelled by calling ``cancel()`` on this ``Deferred`` . In this case, the connected protocol instance is only used to make the example wait until the client has finished talking to the server, which happens after the small amount of example data has been sent to the server and bounced back by the ``/bin/cat`` process the protocol is interacting with. Several of the options accepted by ``SSHCommandClientEndpoint.newConnection`` should be easy to understand. The endpoint takes a reactor which it uses to do any and all I/O it needs to do. It also takes a command which it executes on the remote server once the SSH connection is established and authenticated; this command is a single string, perhaps including spaces or other special shell symbols, and is interpreted by a shell on the server. It takes a username with which it identifies itself to the server for authentication purposes. It takes an optional password argument which will also be used for authentication - if the server supports password authentication (prefer keys instead where possible, see below). It takes a host (either a name or an IP address) and a port number, defining where to connect. Some of the other options may bear further explanation. The ``keys`` argument gives any SSH :py:class:`Key ` objects which may be useful for authentication. These keys are available to the endpoint for authentication, but only keys that the server indicates are useful will actually be used. This argument is optional. If key authentication against the server is either unnecessary or undesired, it may be omitted entirely. The ``agentEndpoint`` argument gives the ``SSHCommandClientEndpoint`` an opportunity to connect to an SSH authentication agent. The agent may already be loaded with keys, or may have some other way to authenticate a connection. Using the agent can mean the process actually establishing the SSH connection doesn't need to load any authentication material (passwords or keys) itself (often convenient in case keys are encrypted and potentially more secure, since only the agent process ever actually holds the secrets). The value for this argument is another ``IStreamClientEndpoint`` . Often in a typical *NIX desktop environment, the *SSH_AUTH_SOCK* environment variable will give the location of an AF_UNIX socket. This explains the value ``echoclient_ssh.py`` assigns this parameter when *--no-agent* is not given. The ``knownHosts`` argument accepts a :py:class:`KnownHostsFile ` instance and controls how server keys are checked and stored. This object has the opportunity to reject server keys if they differ from expectations. It can also save server keys when they are first observed. Finally, there is one option that is not demonstrated in the example - the ``ui`` argument. This argument is closely related to the ``knownHosts`` argument described above. ``KnownHostsFile`` may require user-input under certain circumstances - for example, to ask if it should accept a server key the first time it is observed. The ``ui`` object is how this user-input is obtained. By default, a :py:class:`ConsoleUI ` instance associated with */dev/tty* will be used. This gives about the same behavior as is seen in a standard command-line ssh client. See :py:meth:`SSHCommandClientEndpoint.newConnection ` for details about how edge cases are handled for this default value. For use of ``SSHCommandClientEndpoint`` that is intended to be completely autonomous, applications will probably want to specify a custom ``ui`` object which can make the necessary decisions without user-input. It is also possible to run commands (one or more) over an already-established connection. This is done using the alternate constructor ``SSHCommandClientEndpoint.existingConnection`` . The ``connection`` argument to that function can be obtained by accessing ``transport.conn`` on an already connected protocol. :download:`echoclient_shared_ssh.py ` .. literalinclude:: listings/echoclient_shared_ssh.py Writing a client ---------------- In case the endpoint is missing some necessary functionality, or in case you want to interact with a different part of an SSH server - such as one of its *subsystems* (for example, SFTP), you may need to use the lower-level Conch client interface. This is described below. Writing a client with Conch involves sub-classing 4 classes: :py:class:`twisted.conch.ssh.transport.SSHClientTransport` , :py:class:`twisted.conch.ssh.userauth.SSHUserAuthClient` , :py:class:`twisted.conch.ssh.connection.SSHConnection` , and :py:class:`twisted.conch.ssh.channel.SSHChannel` . We'll start out with ``SSHClientTransport`` because it's the base of the client. The Transport ------------- .. code-block:: python from twisted.conch import error from twisted.conch.ssh import transport from twisted.internet import defer class ClientTransport(transport.SSHClientTransport): def verifyHostKey(self, pubKey, fingerprint): if fingerprint != 'b1:94:6a:c9:24:92:d2:34:7c:62:35:b4:d2:61:11:84': return defer.fail(error.ConchError('bad key')) else: return defer.succeed(1) def connectionSecure(self): self.requestService(ClientUserAuth('user', ClientConnection())) See how easy it is? ``SSHClientTransport`` handles the negotiation of encryption and the verification of keys for you. The one security element that you as a client writer need to implement is ``verifyHostKey()`` . This method is called with two strings: the public key sent by the server and its fingerprint. You should verify the host key the server sends, either by checking against a hard-coded value as in the example, or by asking the user. ``verifyHostKey`` returns a :py:class:`twisted.internet.defer.Deferred` which gets a callback if the host key is valid, or an errback if it is not. Note that in the above, replace 'user' with the username you're attempting to ssh with, for instance a call to ``os.getlogin()`` for the current user. The second method you need to implement is ``connectionSecure()`` . It is called when the encryption is set up and other services can be run. The example requests that the ``ClientUserAuth`` service be started. This service will be discussed next. The Authorization Client ------------------------ .. code-block:: python from twisted.conch.ssh import keys, userauth # these are the public/private keys from test_conch publicKey = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tfBEvLi8DVPrJ3\ /c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTTYLh5KmRpslkYHR\ ivcJSkbh/C+BR3utDS555mV' privateKey = """-----BEGIN RSA PRIVATE KEY----- MIIByAIBAAJhAK8ycfDmDpyZs3+LXwRLy4vA1T6yd/3PZNiPwM+uH8Yx3/YpskSW 4sbUIZR/ZXzY1CMfuC5qyR+UDUbBaaK3Bwyjk8E02C4eSpkabJZGB0Yr3CUpG4fw vgUd7rQ0ueeZlQIBIwJgbh+1VZfr7WftK5lu7MHtqE1S1vPWZQYE3+VUn8yJADyb Z4fsZaCrzW9lkIqXkE3GIY+ojdhZhkO1gbG0118sIgphwSWKRxK0mvh6ERxKqIt1 xJEJO74EykXZV4oNJ8sjAjEA3J9r2ZghVhGN6V8DnQrTk24Td0E8hU8AcP0FVP+8 PQm/g/aXf2QQkQT+omdHVEJrAjEAy0pL0EBH6EVS98evDCBtQw22OZT52qXlAwZ2 gyTriKFVoqjeEjt3SZKKqXHSApP/AjBLpF99zcJJZRq2abgYlf9lv1chkrWqDHUu DZttmYJeEfiFBBavVYIF1dOlZT0G8jMCMBc7sOSZodFnAiryP+Qg9otSBjJ3bQML pSTqy7c3a2AScC/YyOwkDaICHnnD3XyjMwIxALRzl0tQEKMXs6hH8ToUdlLROCrP EhQ0wahUTCk1gKA4uPD6TMTChavbh4K63OvbKg== -----END RSA PRIVATE KEY-----""" class ClientUserAuth(userauth.SSHUserAuthClient): def getPassword(self, prompt = None): return # this says we won't do password authentication def getPublicKey(self): return keys.Key.fromString(data = publicKey).blob() def getPrivateKey(self): return defer.succeed(keys.Key.fromString(data = privateKey).keyObject) Again, fairly simple. The ``SSHUserAuthClient`` takes care of most of the work, but the actual authentication data needs to be supplied. ``getPassword()`` asks for a password, ``getPublicKey()`` and ``getPrivateKey()`` get public and private keys, respectively. ``getPassword()`` returns a ``Deferred`` that is called back with the password to use. ``getPublicKey()`` returns the SSH key data for the public key to use. :py:meth:`Key.fromString() ` will take a key in OpenSSH, LSH or any supported format, as a string, and generate a new :py:class:`Key `. Alternatively, ``keys.Key.fromFile()`` can be used instead, which will take the filename of a key in the supported format, and and generate a new :py:class:`Key `. ``getPrivateKey()`` returns a ``Deferred`` which is called back with the private :py:class:`Key `. ``getPassword()`` and ``getPrivateKey()`` return ``Deferreds`` because they may need to ask the user for input. Once the authentication is complete, ``SSHUserAuthClient`` takes care of starting the ``SSHConnection`` object given to it. Next, we'll look at how to use the ``SSHConnection``. The Connection -------------- .. code-block:: python from twisted.conch.ssh import connection class ClientConnection(connection.SSHConnection): def serviceStarted(self): self.openChannel(CatChannel(conn = self)) ``SSHConnection`` is the easiest, as it's only responsible for starting the channels. It has other methods, those will be examined when we look at ``SSHChannel`` . The Channel ----------- .. code-block:: python from twisted.conch.ssh import channel, common class CatChannel(channel.SSHChannel): name = 'session' def channelOpen(self, data): d = self.conn.sendRequest(self, 'exec', common.NS('cat'), wantReply = 1) d.addCallback(self._cbSendRequest) self.catData = '' def _cbSendRequest(self, ignored): self.write('This data will be echoed back to us by "cat."\r\n') self.conn.sendEOF(self) self.loseConnection() def dataReceived(self, data): self.catData += data def closed(self): print('We got this from "cat":', self.catData) Now that we've spent all this time getting the server and client connected, here is where that work pays off. ``SSHChannel`` is the interface between you and the other side. This particular channel opens a session and plays with the 'cat' program, but your channel can implement anything, so long as the server supports it. The ``channelOpen()`` method is where everything gets started. It gets passed a chunk of data; however, this chunk is usually nothing and can be ignored. Our ``channelOpen()`` initializes our channel, and sends a request to the other side, using the ``sendRequest()`` method of the ``SSHConnection`` object. Requests are used to send events to the other side. We pass the method self so that it knows to send the request for this channel. The 2nd argument of 'exec' tells the server that we want to execute a command. The third argument is the data that accompanies the request. :py:func:`common.NS ` encodes the data as a length-prefixed string, which is how the server expects the data. We also say that we want a reply saying that the process has a been started. ``sendRequest()`` then returns a ``Deferred`` which we add a callback for. Once the callback fires, we send the data. ``SSHChannel`` supports the :py:class:`twisted.internet.interfaces.ITransport` interface, so it can be given to Protocols to run them over the secure connection. In our case, we just write the data directly. ``sendEOF()`` does not follow the interface, but Conch uses it to tell the other side that we will write no more data. ``loseConnection()`` shuts down our side of the connection, but we will still receive data through ``dataReceived()`` . The ``closed()`` method is called when both sides of the connection are closed, and we use it to display the data we received (which should be the same as the data we sent.) Finally, let's actually invoke the code we've set up. The main() function ------------------- .. code-block:: python from twisted.internet import protocol, reactor def main(): factory = protocol.ClientFactory() factory.protocol = ClientTransport reactor.connectTCP('localhost', 22, factory) reactor.run() if __name__ == "__main__": main() We call ``connectTCP()`` to connect to localhost, port 22 (the standard port for ssh), and pass it an instance of :py:class:`twisted.internet.protocol.ClientFactory` . This instance has the attribute ``protocol`` set to our earlier ``ClientTransport`` class. Note that the protocol attribute is set to the class ``ClientTransport`` , not an instance of ``ClientTransport`` ! When the ``connectTCP`` call completes, the protocol will be called to create a ``ClientTransport()`` object - this then invokes all our previous work. It's worth noting that in the example ``main()`` routine, the ``reactor.run()`` call never returns. If you want to make the program exit, call ``reactor.stop()`` in the earlier ``closed()`` method. If you wish to observe the interactions in more detail, adding a call to ``log.startLogging(sys.stdout, setStdout=0)`` before the ``reactor.run()`` call will send all logging to stdout. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/howto/index.rst0000644000175100001710000000041000000000000017575 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Developer Guides ================ .. toctree:: :hidden: conch_client - Tutorial - :doc:`Writing an SSH client with Conch ` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/howto/listings/0000755000175100001710000000000000000000000017575 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/howto/listings/echoclient_shared_ssh.py0000644000175100001710000000310200000000000024463 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. if __name__ == "__main__": import sys import echoclient_shared_ssh from twisted.internet.task import react react(echoclient_shared_ssh.main, sys.argv[1:]) from echoclient_ssh import ConnectionParameters from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.internet.defer import Deferred, gatherResults from twisted.internet.protocol import Factory, Protocol from twisted.internet.task import cooperate class PrinterProtocol(Protocol): def dataReceived(self, data): print("Got some data:", data, end=" ") def connectionLost(self, reason): print("Lost my connection") self.factory.done.callback(None) def main(reactor, *argv): parameters = ConnectionParameters.fromCommandLine(reactor, argv) endpoint = parameters.endpointForCommand(b"/bin/cat") done = [] factory = Factory() factory.protocol = Protocol d = endpoint.connect(factory) def gotConnection(proto): conn = proto.transport.conn for i in range(50): factory = Factory() factory.protocol = PrinterProtocol factory.done = Deferred() done.append(factory.done) e = SSHCommandClientEndpoint.existingConnection( conn, b"/bin/echo %d" % (i,) ) yield e.connect(factory) d.addCallback(gotConnection) d.addCallback(lambda work: cooperate(work).whenDone()) d.addCallback(lambda ignored: gatherResults(done)) return d ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/howto/listings/echoclient_ssh.py0000644000175100001710000001036100000000000023142 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. if __name__ == "__main__": import sys import echoclient_ssh from twisted.internet.task import react react(echoclient_ssh.main, sys.argv[1:]) import getpass import os from twisted.conch.client.knownhosts import KnownHostsFile from twisted.conch.endpoints import SSHCommandClientEndpoint from twisted.conch.ssh.keys import EncryptedKeyError, Key from twisted.internet.defer import Deferred from twisted.internet.endpoints import UNIXClientEndpoint from twisted.internet.protocol import Factory, Protocol from twisted.python.filepath import FilePath from twisted.python.usage import Options class EchoOptions(Options): optParameters = [ ("host", "h", "localhost", "hostname of the SSH server to which to connect"), ("port", "p", 22, "port number of SSH server to which to connect", int), ( "username", "u", getpass.getuser(), "username with which to authenticate with the SSH server", ), ( "identity", "i", None, "file from which to read a private key to use for authentication", ), ("password", None, None, "password to use for authentication"), ( "knownhosts", "k", "~/.ssh/known_hosts", "file containing known ssh server public key data", ), ] optFlags = [ ["no-agent", None, "Disable use of key agent"], ] class NoiseProtocol(Protocol): def connectionMade(self): self.finished = Deferred() self.strings = ["bif", "pow", "zot"] self.sendNoise() def sendNoise(self): if self.strings: self.transport.write(self.strings.pop(0) + "\n") else: self.transport.loseConnection() def dataReceived(self, data): print("Server says:", data) self.sendNoise() def connectionLost(self, reason): self.finished.callback(None) def readKey(path): try: return Key.fromFile(path) except EncryptedKeyError: passphrase = getpass.getpass(f"{path!r} keyphrase: ") return Key.fromFile(path, passphrase=passphrase) class ConnectionParameters: def __init__( self, reactor, host, port, username, password, keys, knownHosts, agent ): self.reactor = reactor self.host = host self.port = port self.username = username self.password = password self.keys = keys self.knownHosts = knownHosts self.agent = agent @classmethod def fromCommandLine(cls, reactor, argv): config = EchoOptions() config.parseOptions(argv) keys = [] if config["identity"]: keyPath = os.path.expanduser(config["identity"]) if os.path.exists(keyPath): keys.append(readKey(keyPath)) knownHostsPath = FilePath(os.path.expanduser(config["knownhosts"])) if knownHostsPath.exists(): knownHosts = KnownHostsFile.fromPath(knownHostsPath) else: knownHosts = None if config["no-agent"] or "SSH_AUTH_SOCK" not in os.environ: agentEndpoint = None else: agentEndpoint = UNIXClientEndpoint(reactor, os.environ["SSH_AUTH_SOCK"]) return cls( reactor, config["host"], config["port"], config["username"], config["password"], keys, knownHosts, agentEndpoint, ) def endpointForCommand(self, command): return SSHCommandClientEndpoint.newConnection( self.reactor, command, self.username, self.host, port=self.port, keys=self.keys, password=self.password, agentEndpoint=self.agent, knownHosts=self.knownHosts, ) def main(reactor, *argv): parameters = ConnectionParameters.fromCommandLine(reactor, argv) endpoint = parameters.endpointForCommand(b"/bin/cat") factory = Factory() factory.protocol = NoiseProtocol d = endpoint.connect(factory) d.addCallback(lambda proto: proto.finished) return d ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/index.rst0000644000175100001710000000065000000000000016443 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Twisted Conch (SSH and Telnet) ============================== .. toctree:: :hidden: howto/index examples/index - :doc:`Developer guides `: documentation on using Twisted Conch to develop your own applications - :doc:`Examples `: short code examples using Twisted Conch ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/conch/man/0000755000175100001710000000000000000000000015354 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/man/cftp.10000644000175100001710000000465700000000000016406 0ustar00runnerdocker.Dd October 8, 2005 .Dt CFTP 1 .Os .Sh NAME .Nm cftp .Nd Conch command-line SFTP client .Sh SYNOPSIS .Nm cftp .Op Fl B Ar buffer_size .Op Fl b Ar command_file .Op Fl R Ar num_requests .Op Fl s Ar subsystem .Os .Sh DESCRIPTION .Nm cftp is a client for logging into a remote machine and executing commands to send and receive file information. It can wrap a number of file transfer subsystems .Pp The options are as follows: .Bl -tag -width Ds .It Fl B Specifies the default size of the buffer to use for sending and receiving. (Default value: 32768 bytes.) .It Fl b File to read commands from, '-' for stdin. (Default value: interactive/stdin.) .It Fl R Number of requests to make before waiting for a reply. .It Fl s Subsystem/server program to connect to. .El .Pp The following commands are recognised by .Nm cftp : .Bl -tag -width Ds .It Ic cd Ar path Change the remote directory to 'path'. .It Ic chgrp Ar gid Ar path Change the gid of 'path' to 'gid'. .It Ic chmod Ar mode Ar path Change mode of 'path' to 'mode'. .It Ic chown Ar uid Ar path Change uid of 'path' to 'uid'. .It Ic exit Disconnect from the server. .It Ic get Ar remote-path Op Ar local-path Get remote file and optionally store it at specified local path. .It Ic help Get a list of available commands. .It Ic lcd Ar path Change local directory to 'path'. .It Ic lls Op Ar ls-options Op Ar path Display local directory listing. .It Ic lmkdir Ar path Create local directory. .It Ic ln Ar linkpath Ar targetpath Symlink remote file. .It Ic lpwd Print the local working directory. .It Ic ls Op Ar -l Op Ar path Display remote directory listing. .It Ic mkdir Ar path Create remote directory. .It Ic progress Toggle progress bar. .It Ic put Ar local-path Op Ar remote-path Transfer local file to remote location .It Ic pwd Print the remote working directory. .It Ic quit Disconnect from the server. .It Ic rename Ar oldpath Ar newpath Rename remote file. .It Ic rmdir Ar path Remove remote directory. .It Ic rm Ar path Remove remote file. .It Ic version Print the SFTP version. .It Ic ? Synonym for 'help'. .El .Sh AUTHOR cftp by Paul Swartz . Man page by Mary Gardiner . .Sh "REPORTING BUGS" Report bugs to \fIhttps://twistedmatrix.com/trac/\fR .Sh COPYRIGHT Copyright \(co 2005-2020 Twisted Matrix Laboratories .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/man/ckeygen.10000644000175100001710000000316600000000000017071 0ustar00runnerdocker.TH CKEYGEN "1" "October 2002" "" "" .SH NAME ckeygen \- manipulate public/private keys .SH SYNOPSIS .B ckeygen [-b \fIbits\fR] [-f \fIfilename\fR] [-t \fItype\fR] .B [-C \fIcomment\fR] [-N \fInew passphrase\fR] [-P \fIold passphrase\fR] .B [-l] [-p] [-q] [-y] .SH DESCRIPTION .PP The \fB\--help\fR prints out a usage message to standard output. .TP \fB-b\fR, \fB--bits\fR Number of bits in the key to create (default: 1024) .TP \fB-f\fR, \fB--filename\fR Filename of the key file. .TP \fB-t\fR, \fB--type\fR Type of key (rsa or dsa). .TP \fB-C\fR, \fB--comment\fR Provide a new comment. .TP \fB-N\fR, \fB--newpass\fR Provide new passphrase. .TP \fB-P\fR, \fB--pass\fR Provide old passphrase. .TP \fB-l\fR, \fB--fingerprint\fR Show fingerprint of key file. .TP \fB-p\fR, \fB--changepass\fR Change passphrase of private key file. .TP \fB-q\fR, \fB--quiet\fR Be quiet. .TP \fB--no-passphrase\fR Create the key with no passphrase. .TP \fB-y\fR, \fB--showpub\fR Read private key file and print public key. .TP \fB--version\fR Display version number only. .SH DESCRIPTION Manipulate public/private keys in various ways. If no filename is given, a file name will be requested interactively. .SH AUTHOR Written by Moshe Zadka, based on ckeygen's help messages .SH "REPORTING BUGS" To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .SH COPYRIGHT Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" ssh(1), conch(1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/man/conch.10000644000175100001710000001437400000000000016541 0ustar00runnerdocker.Dd May 22, 2004 .Dt CONCH 1 .Os .Sh NAME .Nm conch .Nd Conch SSH client .Sh SYNOPSIS .Nm conch .Op Fl AaCfINnrsTtVvx .Op Fl c Ar cipher_spec .Op Fl e Ar escape_char .Op Fl i Ar identity_file .Op Fl K Ar connection_spec .Bk -words .Oo Fl L Xo .Sm off .Ar port : .Ar host : .Ar hostport .Sm on .Xc .Oc .Ek .Op Fl l Ar user .Op Fl m Ar mac_spec .Op Fl o Ar openssh_option .Op Fl p Ar port .Bk -words .Oo Fl R Xo .Sm off .Ar port : .Ar host : .Ar hostport .Sm on .Xc .Oc .Ek .Oo Ar user Ns @ Ns Oc Ar hostname .Op Ar command .Sh DESCRIPTION .Nm is a SSHv2 client for logging into a remote machine and executing commands. It provides encrypted and secure communications across a possibly insecure network. Arbitrary TCP/IP ports can also be forwarded over the secure connection. .Pp .Nm connects and logs into .Ar hostname (as .Ar user or the current username). The user must prove her/his identity through a public\-key or a password. Alternatively, if a connection is already open to a server, a new shell can be opened over the connection without having to reauthenticate. .Pp If .Ar command is specified, .Ar command is executed instead of a shell. If the .Fl s option is given, .Ar command is treated as an SSHv2 subsystem name. .Ss Authentication Conch supports the public-key, keyboard-interactive, and password authentications. .Pp The public-key method allows the RSA or DSA algorithm to be used. The client uses his/her private key, .Pa $HOME/.ssh/id_rsa or .Pa $HOME/.ssh/id_dsa to sign the session identifier, known only by the client and server. The server checks that the matching public key is valid for the user, and that the signature is correct. .Pp If public-key authentication fails, .Nm can authenticate by sending an encrypted password over the connection. .Ss Connection sharing .Nm has the ability to multiplex multiple shells, commands and TCP/IP ports over the same secure connection. To disable multiplexing for a connection, use the .Fl I flag. .Pp The .Fl K option determines how the client connects to the remote host. It is a comma-separated list of the methods to use, in order of preference. The two connection methods are .Ql unix (for connecting over a multiplexed connection) and .Ql direct (to connect directly). To disable connecting over a multiplexed connection, do not include .Ql unix in the preference list. .Pp As an example of how connection sharing works, to speed up CVS over SSH: .Pp .Nm --noshell --fork -l cvs_user cvs_host .br set CVS_RSH=\fBconch\fR .Pp Now, when CVS connects to cvs_host as cvs_user, instead of making a new connection to the server, .Nm will add a new channel to the existing connection. This saves the cost of repeatedly negotiating the cryptography and authentication. .Pp The options are as follows: .Bl -tag -width Ds .It Fl A Enables authentication agent forwarding. .It Fl a Disables authentication agent forwarding (default). .It Fl C Enable compression. .It Fl c Ar cipher_spec Selects encryption algorithms to be used for this connection, as a comma-separated list of ciphers in order of preference. The list that .Nm supports is (in order of default preference): aes256-ctr, aes256-cbc, aes192-ctr, aes192-cbc, aes128-ctr, aes128-cbc, cast128-ctr, cast128-cbc, blowfish-ctr, blowfish, idea-ctr, idea-cbc, 3des-ctr, 3des-cbc. .It Fl e Ar ch | ^ch | none Sets the escape character for sessions with a PTY (default: .Ql ~ ) . The escape character is only recognized at the beginning of a line (after a newline). The escape character followed by a dot .Pq Ql \&. closes the connection; followed by ^Z suspends the connection; and followed by the escape character sends the escape character once. Setting the character to .Dq none disables any escapes. .It Fl f Fork to background after authentication. .It Fl I Do not allow connection sharing over this connection. .It Fl i Ar identity_spec The file from which the identity (private key) for RSA or DSA authentication is read. The defaults are .Pa $HOME/.ssh/id_rsa and .Pa $HOME/.ssh/id_dsa . It is possible to use this option more than once to use more than one private key. .It Fl K Ar connection_spec Selects methods for connection to the server, as a comma-separated list of methods in order of preference. See .Cm Connection sharing for more information. .It Fl L Xo .Sm off .Ar port : host : hostport .Sm on .Xc Specifies that the given port on the client host is to be forwarded to the given host and port on the remote side. This allocates a socket to listen to .Ar port on the local side, and when connections are made to that socket, they are forwarded over the secure channel and a connection is made to .Ar host port .Ar hostport from the remote machine. Only root can forward privieged ports. .It Fl l Ar user Log in using this username. .It Fl m Ar mac_spec Selects MAC (message authentication code) algorithms, as a comma-separated list in order of preference. The list that .Nm supports is (in order of preference): hmac-sha2-512, hmac-sha2-384, hmac-sha2-256, hmac-sha1, hmac-md5. .It Fl N Do not execute a shell or command. .It Fl n Redirect input from /dev/null. .It Fl o Ar openssh_option Ignored OpenSSH options. .It Fl p Ar port The port to connect to on the server. .It Fl R Xo .Sm off .Ar port : host : hostport .Sm on .Xc Specifies that the given port on the remote host is to be forwarded to the given host and port on the local side. This allocates a socket to listen to .Ar port on the remote side, and when connections are made to that socket, they are forwarded over the secure channel and a connection is made to .Ar host port .Ar hostport from the client host. Only root can forward privieged ports. .It Fl s Reconnect to the server if the connection is lost. .It Fl s Invoke .Ar command (mandatory) as a SSHv2 subsystem. .It Fl T Do not allocate a TTY. .It Fl t Allocate a TTY even if command is given. .It Fl V Display version number only. .It Fl v Log to stderr. .It Fl x Disable X11 connection forwarding (default). .El .Sh AUTHOR Written by Paul Swartz . .Sh "REPORTING BUGS" To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .Sh COPYRIGHT Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .Sh SEE ALSO ssh(1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conch/man/tkconch.10000644000175100001710000000402200000000000017065 0ustar00runnerdocker.TH CONCH "1" "October 2002" "" "" .SH NAME tkconch \- connect to SSH servers graphically .SH SYNOPSIS .B conch [-l \fIuser\fR] [-i \fIidentity\fR [ -i \fIidentity\fR ... ]] [-c \fIcipher\fR] [-m \fIMAC\fR] [-p \fIport\fR] [-n] [-t] [-T] [-V] [-C] [-N] [-s] [arg [...]] .PP .B conch --help .SH DESCRIPTION .PP The \fB\--help\fR prints out a usage message to standard output. .TP \fB-l\fR, \fB--user\fR Log in using this user name. .TP \fB-e\fR, \fB--escape\fR Set escape character; 'none' = disable (default: ~) .TP \fB-i\fR, \fB--identity\fR Add an identity file for public key authentication (default: ~/.ssh/identity) .TP \fB-c\fR, \fB--cipher\fR Cipher algorithm to use. .TP \fB-m\fR, \fB--macs\fR Specify MAC algorithms for protocol version 2. .TP \fB-p\fR, \fB--port\fR Port to connect to. .TP \fB-L\fR, \fB--localforward\fR Forward local port to remote address. .TP \fB-R\fR, \fB--remoteforward\fR Forward remote port to local address. .TP \fB-t\fR, \fB--tty\fR Allocate a tty even if command is given. .TP \fB-n\fR, \fB--notty\fR Do not allocate a tty. .TP \fB-V\fR, \fB--version\fR Display version number only. .TP \fB-C\fR, \fB--compress\fR Enable compression. .TP \fB-a\fR, \fB--ansilog\fR Print the received data to stdout. .TP \fB-N\fR, \fB--noshell\fR Do not execute a shell or command. .TP \fB-s\fR, \fB--subsystem\fR Invoke command (mandatory) as SSH2 subsystem. .TP \fB--log\fR Print the receieved data to stderr. .SH DESCRIPTION Open an SSH connection to specified server, and either run the command given there or open a remote interactive shell. .SH AUTHOR Written by Moshe Zadka, based on conch's help messages .SH "REPORTING BUGS" To report a bug, visit \fIhttps://twistedmatrix.com/trac/\fR .SH COPYRIGHT Copyright \(co 2002-2020 Twisted Matrix Laboratories. .br This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. .SH "SEE ALSO" ssh(1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/conf.py0000644000175100001710000002120700000000000015010 0ustar00runnerdocker# # Twisted documentation build configuration file, created by # sphinx-quickstart on Tue Jan 14 11:31:15 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import pathlib import subprocess import sys from datetime import date from pprint import pprint # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("./_extensions")) sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ # See setup.cfg for the Sphinx version required to build the documentation. # needs_sphinx is not use to avoid duplication and getting these values # out of sync. # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.intersphinx", "pydoctor.sphinx_ext.build_apidocs", ] try: import rst2pdf.pdfbuilder extensions.append("rst2pdf.pdfbuilder") except ImportError: pass extensions.append("traclinks") from twisted import version as twisted_version_object # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" _today = date.today() # General information about the project. project = "Twisted" copyright = "{}, Twisted Matrix Labs. Ver {}. Built on {}".format( _today.year, twisted_version_object.public(), _today.isoformat(), ) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "{major}.{minor}".format( major=twisted_version_object.major, minor=twisted_version_object.minor ) # The full version, including alpha/beta/rc tags. release = twisted_version_object.short() # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # See Read The Docs environment variables # https://docs.readthedocs.io/en/stable/builds.html#build-environment on_rtd = os.environ.get("READTHEDOCS", None) == "True" if not on_rtd: html_theme = "twistedtrac" # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ["_themes"] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # These paths are either relative to html_static_path # or fully qualified paths (eg. https://...) html_js_files = [ "js/custom.js", # Here we have a Sphinx HTML injection hack to make the JS script load without blocking. 'https://sidecar.gitter.im/dist/sidecar.v1.js" defer hack="', ] # Output file base name for HTML help builder. htmlhelp_basename = "Twisteddoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ("index", "Twisted.tex", "Twisted Documentation", "Twisted Matrix Labs", "manual"), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [("index", "twisted", "Twisted Documentation", ["Twisted Matrix Labs"], 1)] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "Twisted", "Twisted Documentation", "Twisted Matrix Labs", "Twisted", "One line description of project.", "Miscellaneous", ), ] # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = "Twisted" epub_author = "Twisted Matrix Labs" epub_publisher = "Twisted Matrix Labs" epub_copyright = "2020, Twisted Matrix Labs" # -- Extension configuration ---------------------------------------------- _git_reference = subprocess.run( ["git", "rev-parse", "--abbrev-ref", "HEAD"], text=True, encoding="utf8", capture_output=True, check=True, ).stdout print(f"== Environment dump for {_git_reference} ===") pprint(dict(os.environ)) print("======") # Base url for API docs api_base_url = f"/documents/{release}/api/" if on_rtd: # For a PR the link is like: # https://twisted--1422.org.readthedocs.build/en/1422/ # For a release: # https://docs.twistedmatrix.com/en/twisted-20.3.0/ # https://docs.twistedmatrix.com/en/latest/ api_base_url = "/{}/{}/api/".format( os.environ["READTHEDOCS_LANGUAGE"], os.environ["READTHEDOCS_VERSION"], ) # Try to find URL fragment for the GitHub source page based on current # branch or tag. if _git_reference == "HEAD": # It looks like the branch has no name. # Fallback to commit ID. _git_reference = subprocess.getoutput("git rev-parse HEAD") if os.environ.get("READTHEDOCS", "") == "True": rtd_version = os.environ.get("READTHEDOCS_VERSION", "") if "." in rtd_version: # It looks like we have a tag build. _git_reference = rtd_version _source_root = pathlib.Path(__file__).parent.parent / "src" pydoctor_args = [ "--quiet", f"--html-viewsource-base=https://github.com/twisted/twisted/tree/{_git_reference}/src", "--project-name=Twisted", "--project-url=https://twistedmatrix.com/", "--system-class=twisted.python._pydoctor.TwistedSystem", "--docformat=epytext", "--intersphinx=https://docs.python.org/3/objects.inv", "--intersphinx=https://cryptography.io/en/latest/objects.inv", "--intersphinx=https://pyopenssl.readthedocs.io/en/stable/objects.inv", "--intersphinx=https://hyperlink.readthedocs.io/en/stable/objects.inv", "--intersphinx=https://twisted.org/constantly/docs/objects.inv", "--intersphinx=https://twisted.org/incremental/docs/objects.inv", "--intersphinx=https://python-hyper.org/projects/hyper-h2/en/stable/objects.inv", "--intersphinx=https://priority.readthedocs.io/en/stable/objects.inv", "--intersphinx=https://zopeinterface.readthedocs.io/en/latest/objects.inv", "--intersphinx=https://automat.readthedocs.io/en/latest/objects.inv", f"--project-base-dir={_source_root}", "--html-output={outdir}/api", str(_source_root / "twisted"), ] pydoctor_url_path = "/en/{rtd_version}/api/" traclinks_base_url = "https://twistedmatrix.com/trac" # A dict mapping unique IDs (which can be used to disambiguate references) to a # tuple of (, ). # The inventory file may be None to use the default location at the given URI. intersphinx_mapping = { "py3": ("https://docs.python.org/3", None), "zopeinterface": ("https://zopeinterface.readthedocs.io/en/latest", None), } # How long to cache remote inventories. Positive is a number of days, # negative means infinite. The default is 5 days, which should be fine # for standard library documentation that does not typically change # significantly after release. # intersphinx_cache_limit = 5 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/core/0000755000175100001710000000000000000000000014437 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/core/benchmarks/0000755000175100001710000000000000000000000016554 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/banana.py0000644000175100001710000000043600000000000020351 0ustar00runnerdocker#!/usr/bin/python from timer import timeit from twisted.spread.banana import b1282int ITERATIONS = 100000 for length in (1, 5, 10, 50, 100): elapsed = timeit(b1282int, ITERATIONS, "\xff" * length) print("b1282int %3d byte string: %10d cps" % (length, ITERATIONS / elapsed)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/deferreds.py0000644000175100001710000001000400000000000021064 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ See how fast deferreds are. This is mainly useful to compare cdefer.Deferred to defer.Deferred """ from timer import timeit from twisted.internet import defer from twisted.python.compat import range benchmarkFuncs = [] def benchmarkFunc(iter, args=()): """ A decorator for benchmark functions that measure a single iteration count. Registers the function with the given iteration count to the global benchmarkFuncs list """ def decorator(func): benchmarkFuncs.append((func, args, iter)) return func return decorator def benchmarkNFunc(iter, ns): """ A decorator for benchmark functions that measure multiple iteration counts. Registers the function with the given iteration count to the global benchmarkFuncs list. """ def decorator(func): for n in ns: benchmarkFuncs.append((func, (n,), iter)) return func return decorator def instantiate(): """ Only create a deferred """ d = defer.Deferred() instantiate = benchmarkFunc(100000)(instantiate) def instantiateShootCallback(): """ Create a deferred and give it a normal result """ d = defer.Deferred() d.callback(1) instantiateShootCallback = benchmarkFunc(100000)(instantiateShootCallback) def instantiateShootErrback(): """ Create a deferred and give it an exception result. To avoid Unhandled Errors, also register an errback that eats the error """ d = defer.Deferred() try: 1 / 0 except BaseException: d.errback() d.addErrback(lambda x: None) instantiateShootErrback = benchmarkFunc(200)(instantiateShootErrback) ns = [10, 1000, 10000] def instantiateAddCallbacksNoResult(n): """ Creates a deferred and adds a trivial callback/errback/both to it the given number of times. """ d = defer.Deferred() def f(result): return result for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f, f) instantiateAddCallbacksNoResult = benchmarkNFunc(20, ns)( instantiateAddCallbacksNoResult ) def instantiateAddCallbacksBeforeResult(n): """ Create a deferred and adds a trivial callback/errback/both to it the given number of times, and then shoots a result through all of the callbacks. """ d = defer.Deferred() def f(result): return result for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f) d.callback(1) instantiateAddCallbacksBeforeResult = benchmarkNFunc(20, ns)( instantiateAddCallbacksBeforeResult ) def instantiateAddCallbacksAfterResult(n): """ Create a deferred, shoots it and then adds a trivial callback/errback/both to it the given number of times. The result is processed through the callbacks as they are added. """ d = defer.Deferred() def f(result): return result d.callback(1) for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f) instantiateAddCallbacksAfterResult = benchmarkNFunc(20, ns)( instantiateAddCallbacksAfterResult ) def pauseUnpause(n): """ Adds the given number of callbacks/errbacks/both to a deferred while it is paused, and unpauses it, trigerring the processing of the value through the callbacks. """ d = defer.Deferred() def f(result): return result d.callback(1) d.pause() for i in range(n): d.addCallback(f) d.addErrback(f) d.addBoth(f) d.addCallbacks(f) d.unpause() pauseUnpause = benchmarkNFunc(20, ns)(pauseUnpause) def benchmark(): """ Run all of the benchmarks registered in the benchmarkFuncs list """ print(defer.Deferred.__module__) for func, args, iter in benchmarkFuncs: print(func.__name__, args, timeit(func, iter, *args)) if __name__ == "__main__": benchmark() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/failure.py0000644000175100001710000000267000000000000020562 0ustar00runnerdocker"""See how slow failure creation is""" import random from twisted.python import failure random.seed(10050) O = [0, 20, 40, 60, 80, 10, 30, 50, 70, 90] DEPTH = 30 def pickVal(): return random.choice([None, 1, "Hello", [], {1: 1}, (1, 2, 3)]) def makeLocals(n): return ";".join(["x%d = %s" % (i, pickVal()) for i in range(n)]) for nLocals in O: for i in range(DEPTH): s = """ def deepFailure%d_%d(): %s deepFailure%d_%d() """ % ( nLocals, i, makeLocals(nLocals), nLocals, i + 1, ) exec(s) exec( """ def deepFailure%d_%d(): 1 / 0 """ % (nLocals, DEPTH) ) R = range(5000) def fail(n): for i in R: try: eval("deepFailure%d_0" % n)() except BaseException: failure.Failure() def fail_str(n): for i in R: try: eval("deepFailure%d_0" % n)() except BaseException: str(failure.Failure()) class PythonException(Exception): pass def fail_easy(n): for i in R: try: failure.Failure(PythonException()) except BaseException: pass from timer import timeit # for i in O: # timeit(fail, 1, i) # for i in O: # print('easy failing', i, timeit(fail_easy, 1, i)) for i in O: print("failing", i, timeit(fail, 1, i)) # for i in O: # print('string failing', i, timeit(fail_str, 1, i)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/linereceiver.py0000644000175100001710000000257600000000000021614 0ustar00runnerdockerimport time from twisted.protocols import basic from twisted.python.compat import range class CollectingLineReceiver(basic.LineReceiver): def __init__(self): self.lines = [] self.lineReceived = self.lines.append def deliver(proto, chunks): return [proto.dataReceived(chunk) for chunk in chunks] def benchmark(chunkSize, lineLength, numLines): bytes = (b"x" * lineLength + b"\r\n") * numLines chunkCount = len(bytes) // chunkSize + 1 chunks = [] for n in range(chunkCount): chunks.append(bytes[n * chunkSize : (n + 1) * chunkSize]) assert b"".join(chunks) == bytes, (chunks, bytes) p = CollectingLineReceiver() before = time.clock() deliver(p, chunks) after = time.clock() assert bytes.splitlines() == p.lines, (bytes.splitlines(), p.lines) print("chunkSize:", chunkSize, end=" ") print("lineLength:", lineLength, end=" ") print("numLines:", numLines, end=" ") print("CPU Time: ", after - before) def main(): for numLines in 100, 1000: for lineLength in (10, 100, 1000): for chunkSize in (1, 500, 5000): benchmark(chunkSize, lineLength, numLines) for numLines in 10000, 50000: for lineLength in (1000, 2000): for chunkSize in (51, 500, 5000): benchmark(chunkSize, lineLength, numLines) if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/netstringreceiver.py0000644000175100001710000001726300000000000022701 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import gc import math import os import sys import time from twisted.protocols.test import test_basic from twisted.python.compat import range, raw_input from twisted.test import proto_helpers NETSTRING_POSTFIX = b"," USAGE = """\ Usage: %s This script creates up to 2 ** chunks with up to 2 ** characters and sends them to the NetstringReceiver. The sorted performance data for all combination is written to afterwards. You might want to start with a small number, maybe 10 or 12, and slowly increase it. Stop when the performance starts to deteriorate ;-). """ class PerformanceTester: """ A class for testing the performance of some """ headers = [] lineFormat = "" performanceData = {} def __init__(self, filename): """ Initializes C{self.filename}. If a file with this name already exists, asks if it should be overwritten. Terminates with exit status 1, if the user does not accept. """ if os.path.isfile(filename): response = raw_input( ("A file named %s exists. " "Overwrite it (y/n)? ") % filename ) if response.lower() != "y": print("Performance test cancelled.") sys.exit(1) self.filename = filename def testPerformance(self, number): """ Drives the execution of C{performTest} with arguments between 0 and C{number - 1}. @param number: Defines the number of test runs to be performed. @type number: C{int} """ for iteration in range(number): self.performTest(iteration) def performTest(self, iteration): """ Performs one test iteration. Overwrite this. @param iteration: The iteration number. Can be used to configure the test. @type iteration: C{int} @raise NotImplementedError: because this method has to be implemented by the subclass. """ raise NotImplementedError def createReport(self): """ Creates a file and writes a table with performance data. The performance data are ordered by the total size of the netstrings. In addition they show the chunk size, the number of chunks and the time (in seconds) that elapsed while the C{NetstringReceiver} received the netstring. @param filename: The name of the report file that will be written. @type filename: C{str} """ self.outputFile = open(self.filename, "w") self.writeHeader() self.writePerformanceData() self.writeLineSeparator() print("The report was written to %s." % self.filename) def writeHeader(self): """ Writes the table header for the report. """ self.writeLineSeparator() self.outputFile.write("| {} |\n".format(" | ".join(self.headers))) self.writeLineSeparator() def writeLineSeparator(self): """ Writes a 'line separator' made from '+' and '-' characters. """ dashes = ("-" * (len(header) + 2) for header in self.headers) self.outputFile.write("+%s+\n" % "+".join(dashes)) def writePerformanceData(self): """ Writes one line for each item in C{self.performanceData}. """ for combination, elapsed in sorted(self.performanceData.items()): totalSize, chunkSize, numberOfChunks = combination self.outputFile.write( self.lineFormat % (totalSize, chunkSize, numberOfChunks, elapsed) ) class NetstringPerformanceTester(PerformanceTester): """ A class for determining the C{NetstringReceiver.dataReceived} performance. Instantiates a C{NetstringReceiver} and calls its C{dataReceived()} method with different chunks sizes and numbers of chunks. Presents a table showing the relation between input data and time to process them. """ headers = ["Chunk size", "Number of chunks", "Total size", "Time to receive"] lineFormat = "| %%%dd | %%%dd | %%%dd | %%%d.4f |\n" % tuple( len(header) for header in headers ) def __init__(self, filename): """ Sets up the output file and the netstring receiver that will be used for receiving data. @param filename: The name of the file for storing the report. @type filename: C{str} """ PerformanceTester.__init__(self, filename) transport = proto_helpers.StringTransport() self.netstringReceiver = test_basic.TestNetstring() self.netstringReceiver.makeConnection(transport) def performTest(self, number): """ Tests the performance of C{NetstringReceiver.dataReceived}. Feeds netstrings of various sizes in different chunk sizes to a C{NetstringReceiver} and stores the elapsed time in C{self.performanceData}. @param number: The maximal chunks size / number of chunks to be checked. @type number: C{int} """ chunkSize = 2 ** number numberOfChunks = chunkSize while numberOfChunks: self.testCombination(chunkSize, numberOfChunks) numberOfChunks = numberOfChunks // 2 def testCombination(self, chunkSize, numberOfChunks): """ Tests one combination of chunk size and number of chunks. @param chunkSize: The size of one chunk to be sent to the C{NetstringReceiver}. @type chunkSize: C{int} @param numberOfChunks: The number of C{chunkSize}-sized chunks to be sent to the C{NetstringReceiver}. @type numberOfChunks: C{int} """ chunk, dataSize = self.configureCombination(chunkSize, numberOfChunks) elapsed = self.receiveData(chunk, numberOfChunks, dataSize) key = (chunkSize, numberOfChunks, dataSize) self.performanceData[key] = elapsed def configureCombination(self, chunkSize, numberOfChunks): """ Updates C{MAX_LENGTH} for {self.netstringReceiver} (to avoid C{NetstringParseErrors} that might be raised if the size exceeds the default C{MAX_LENGTH}). Calculates and returns one 'chunk' of data and the total size of the netstring. @param chunkSize: The size of chunks that will be received. @type chunkSize: C{int} @param numberOfChunks: The number of C{chunkSize}-sized chunks that will be received. @type numberOfChunks: C{int} @return: A tuple consisting of string of C{chunkSize} 'a' characters and the size of the netstring data portion. """ chunk = b"a" * chunkSize dataSize = chunkSize * numberOfChunks self.netstringReceiver.MAX_LENGTH = dataSize numberOfDigits = math.ceil(math.log10(dataSize)) + 1 return chunk, dataSize def receiveData(self, chunk, numberOfChunks, dataSize): dr = self.netstringReceiver.dataReceived now = time.time() dr(f"{dataSize}:".encode("ascii")) for idx in range(numberOfChunks): dr(chunk) dr(NETSTRING_POSTFIX) elapsed = time.time() - now assert self.netstringReceiver.received, "Didn't receive string!" return elapsed def disableGarbageCollector(): gc.disable() print("Disabled Garbage Collector.") def main(number, filename): disableGarbageCollector() npt = NetstringPerformanceTester(filename) npt.testPerformance(int(number)) npt.createReport() if __name__ == "__main__": if len(sys.argv) < 3: print(USAGE % sys.argv[0]) sys.exit(1) main(*sys.argv[1:3]) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/task.py0000644000175100001710000000075400000000000020076 0ustar00runnerdocker""" Benchmarks for L{twisted.internet.task}. """ from timer import timeit from twisted.internet import task def test_performance(): """ L{LoopingCall} should not take long to skip a lot of iterations. """ clock = task.Clock() call = task.LoopingCall(lambda: None) call.clock = clock call.start(0.1) clock.advance(1000000) def main(): print("LoopingCall large advance takes", timeit(test_performance, iter=1)) if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/timer.py0000644000175100001710000000100000000000000020235 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Helper stuff for benchmarks. """ import gc gc.disable() print("Disabled GC") def timeit(func, iter=1000, *args, **kwargs): """ timeit(func, iter = 1000 *args, **kwargs) -> elapsed time calls func iter times with args and kwargs, returns time elapsed """ from time import time as currentTime r = range(iter) t = currentTime() for i in r: func(*args, **kwargs) return currentTime() - t ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/tpclient.py0000644000175100001710000000240000000000000020744 0ustar00runnerdocker"""Throughput test.""" import sys import time from twisted.internet import protocol, reactor from twisted.python import log TIMES = 10000 S = "0123456789" * 1240 toReceive = len(S) * TIMES class Sender(protocol.Protocol): def connectionMade(self): start() self.numSent = 0 self.received = 0 self.transport.registerProducer(self, 0) def stopProducing(self): pass def pauseProducing(self): pass def resumeProducing(self): self.numSent += 1 self.transport.write(S) if self.numSent == TIMES: self.transport.unregisterProducer() self.transport.loseConnection() def connectionLost(self, reason): shutdown(self.numSent == TIMES) started = None def start(): global started started = time.time() def shutdown(success): if not success: raise SystemExit("failure or something") passed = time.time() - started print("Throughput (send): %s kbytes/sec" % ((toReceive / passed) / 1024)) reactor.stop() def main(): f = protocol.ClientFactory() f.protocol = Sender reactor.connectTCP(sys.argv[1], int(sys.argv[2]), f) reactor.run() if __name__ == "__main__": # log.startLogging(sys.stdout) main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/tpclient_nt.py0000644000175100001710000000076300000000000021457 0ustar00runnerdocker"""Non-twisted throughput client.""" import socket import sys import time TIMES = 50000 S = "0123456789" * 1024 sent = len(S) * TIMES def main(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((sys.argv[1], int(sys.argv[2]))) start = time.time() i = 0 while i < TIMES: i += 1 s.sendall(S) passed = time.time() - start print("Throughput: %s kbytes/sec" % ((sent / passed) / 1024)) s.close() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/tpserver.py0000644000175100001710000000050000000000000020773 0ustar00runnerdocker"""Throughput server.""" import sys from twisted.internet import protocol, reactor from twisted.protocols.wire import Discard from twisted.python import log def main(): f = protocol.ServerFactory() f.protocol = Discard reactor.listenTCP(8000, f) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/benchmarks/tpserver_nt.py0000644000175100001710000000067700000000000021513 0ustar00runnerdocker"""Non-twisted throughput server.""" import signal import socket import sys def signalhandler(*args): print("alarm!") sys.stdout.flush() signal.signal(signal.SIGALRM, signalhandler) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("", 8001)) s.listen(1) while 1: c, (h, p) = s.accept() c.settimeout(30) signal.alarm(5) while 1: d = c.recv(16384) if not d: break c.close() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/core/development/0000755000175100001710000000000000000000000016761 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/index.rst0000644000175100001710000000101400000000000020616 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Development of Twisted ====================== .. toctree:: :hidden: naming philosophy security policy/index This documentation is for people who work on the Twisted codebase itself, rather than for people who want to use Twisted in their own projects. - :doc:`Naming ` - :doc:`Philosophy ` - :doc:`Security ` - :doc:`Twisted development policy ` ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4699442 Twisted-22.1.0/docs/core/development/listings/0000755000175100001710000000000000000000000020615 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/listings/new_module_template.py0000644000175100001710000000023500000000000025220 0ustar00runnerdocker# -*- test-case-name: -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Docstring goes here. """ __all__ = [] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/naming.rst0000644000175100001710000000313100000000000020762 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Naming Conventions ================== While this may sound like a small detail, clear method naming is important to provide an API that developers familiar with event-based programming can pick up quickly. Since the idea of a method call maps very neatly onto that of a received event, all event handlers are simply methods named after past-tense verbs. All class names are descriptive nouns, designed to mirror the is-a relationship of the abstractions they implement. All requests for notification or transmission are present-tense imperative verbs. Here are some examples of this naming scheme: - An event notification of data received from peer:``dataReceived(data)`` - A request to send data: ``write(data)`` - A class that implements a protocol: ``Protocol`` The naming is platform neutral. This means that the names are equally appropriate in a wide variety of environments, as long as they can publish the required events. It is self-consistent. Things that deal with TCP use the acronym TCP, and it is always capitalized. Dropping, losing, terminating, and closing the connection are all referred to as "losing" the connection. This symmetrical naming allows developers to easily locate other API calls if they have learned a few related to what they want to do. It is semantically clear. The semantics of dataReceived are simple: there are some bytes available for processing. This remains true even if the lower-level machinery to get the data is highly complex. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/philosophy.rst0000644000175100001710000000266000000000000021715 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Philosophy ========== Abstraction Levels ------------------ When implementing interfaces to the operating system or the network, provide two interfaces: - One that doesn't hide platform specific or library specific functionality. For example, you can use file descriptors on Unix, and Win32 events on Windows. - One that provides a high level interface hiding platform specific details. E.g. process running uses same API on Unix and Windows, although the implementation is very different. Restated in a more general way: - Provide all low level functionality for your specific domain, without limiting the policies and decisions the user can make. - Provide a high level abstraction on top of the low level implementation (or implementations) which implements the common use cases and functionality that is used in most cases. Learning Curves --------------- Require the minimal amount of work and learning on part of the user to get started. If this means they have less functionality, that's OK, when they need it they can learn a bit more. This will also lead to a cleaner, easier to test design. For example - using twistd is a great way to deploy applications. But to get started you don't need to know about it. Later on you can start using twistd, but its usage is optional. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4739444 Twisted-22.1.0/docs/core/development/policy/0000755000175100001710000000000000000000000020260 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/code-dev.rst0000644000175100001710000001731600000000000022510 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Working from Twisted's code repository ============================================ If you're going to be doing development on Twisted itself, or if you want to take advantage of bleeding-edge features (or bug fixes) that are not yet available in a numbered release, you'll probably want to check out a tree from the Twisted Git repository. The Trunk is where all current development takes place. This document lists some useful tips for working on this cutting edge. Checkout -------- Git tutorials can be found elsewhere, see in particular `Git and GitHub learning resources `_ . The relevant data you need to check out a copy of the Twisted tree is available on the `development page `_ , and is as follows: .. code-block:: console $ git clone https://github.com/twisted/twisted Twisted The output of ``git blame`` `will be better `_ if you configure it to use our ignore file: .. code-block:: console $ cd Twisted $ git config blame.ignoreRevsFile .git-blame-ignore-revs Alternate tree names -------------------- By using ``git clone https://github.com/twisted/twisted otherdir`` , you can put the workspace tree in a directory other than "Twisted" . I do this (with a name like "Twisted-Git" ) to remind myself that this tree comes from Git and not from a released version (like "Twisted-1.0.5" ). This practice can cause a few problems, because there are a few places in the Twisted tree that need to know where the tree starts, so they can add it to ``sys.path`` without requiring the user manually set their PYTHONPATH. These functions walk the current directory up to the root, looking for a directory named "Twisted" (sometimes exactly that, sometimes with a ``.startswith`` test). Generally these are test scripts or other administrative tools which expect to be launched from somewhere inside the tree (but not necessarily from the top). If you rename the tree to something other than ``Twisted`` , these tools may wind up trying to use Twisted source files from /usr/lib/python2.5 or elsewhere on the default ``sys.path`` . Normally this won't matter, but it is good to be aware of the issue in case you run into problems. ``twisted/test/process_twisted.py`` is one of these programs. Compiling C extensions ---------------------- There are currently several C extension modules in Twisted: ``twisted.internet.cfsupport`` , ``twisted.internet.iocpreactor._iocp`` , and ``twisted.python._epoll`` . These modules are optional, but you'll have to compile them if you want to experience their features, performance improvements, or bugs. There are two approaches. The first is to do a regular distutils ``./setup.py build`` , which will create a directory under ``build/`` to hold both the generated ``.so`` files as well as a copy of the 600-odd ``.py`` files that make up Twisted. If you do this, you will need to set your PYTHONPATH to something like ``MyDir/Twisted/build/lib.linux-i686-2.5`` in order to run code against the Git twisted (as opposed to whatever's installed in ``/usr/lib/python2.5`` or wherever python usually looks). In addition, you will need to re-run the ``build`` command *every time* you change a ``.py`` file. The ``build/lib.foo`` directory is a copy of the main tree, and that copy is only updated when you re-run ``setup.py build`` . It is easy to forget this and then wonder why your code changes aren't being expressed. The second technique is to build the C modules in place, and point your PYTHONPATH at the top of the tree, like ``MyDir/Twisted`` . This way you're using the .py files in place too, removing the confusion a forgotten rebuild could cause with the separate build/ directory above. To build the C modules in place, do ``./setup.py build_ext -i`` . You only need to re-run this command when you change the C files. Note that ``setup.py`` is not Make, it does not always get the dependencies right (``.h`` files in particular), so if you are hacking on the cReactor you may need to manually delete the ``.o`` files before doing a rebuild. Also note that doing a ``setup.py clean`` will remove the ``.o`` files but not the final ``.so`` files, they must be deleted by hand. Running tests ------------- To run the full unit-test suite, do: .. code-block:: console ./bin/trial twisted To run a single test file (like ``twisted/test/test_defer.py`` ), do one of: .. code-block:: console ./bin/trial twisted.test.test_defer or .. code-block:: console ./bin/trial twisted/test/test_defer.py To run any tests that are related to a code file, like ``twisted/protocols/imap4.py`` , do: .. code-block:: console ./bin/trial --testmodule twisted/mail/imap4.py This depends upon the ``.py`` file having an appropriate "test-case-name" tag that indicates which test cases provide coverage. See the :doc:`Test Standards ` document for details about using "test-case-name" . In this example, the ``twisted.mail.test.test_imap`` test will be run. Many tests create temporary files in /tmp or ./_trial_temp, but everything in /tmp should be deleted when the test finishes. Sometimes these cleanup calls are commented out by mistake, so if you see a stray ``/tmp/@12345.1`` directory, it is probably from ``test_dirdbm`` or ``test_popsicle`` . Look for an ``rmtree`` that has been commented out and complain to the last developer who touched that file. Building docs ------------- Twisted documentation (not including the automatically-generated API docs) is generated by `Sphinx `_ . The docs are written in Restructured Text (``.rst``) and translated into ``.html`` files by the ``bin/admin/build-docs`` script. To build the HTML form of the docs into the ``doc/`` directory, do the following: .. code-block:: console ./bin/admin/build-docs . Committing and Post-commit Hooks -------------------------------- Twisted's Trac installation is notified when the Git repository changes, and will update the ticket depending on the Git commit logs. When making a branch for a ticket, the branch name should end in ``-`` , for example ``my-branch-9999`` . This will add a ticket comment containing a changeset link and branch name. To make your commit message show up as a comment on a Trac ticket, add a ``refs #`` line at the bottom of your commit message. To automatically close a ticket on Trac as ``Fixed`` and add a comment with the closing commit message, add a ``Fixes: #`` line to your commit message. In general, a commit message closing a ticket looks like this: :: Merge my-branch-9999: A single-line summary. Author: jesstess Reviewers: exarkun, glyph Fixes: #9999 My longer description of the changes made. The :doc:`Twisted Coding Standard ` elaborates on commit messages and source control. Emacs ----- A minor mode for development with Twisted using Emacs is available. See ``twisted-dev.el`` , provided by `twisted-emacs `_ , for several utility functions which make it easier to grep for methods, run test cases, etc. Building Debian packages ------------------------ Our support for building Debian packages has fallen into disrepair. We would very much like to restore this functionality, but until we do so, if you are interested in this, you are on your own. See `stdeb `_ for one possible approach to this. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/coding-standard.rst0000644000175100001710000007276100000000000024070 0ustar00runnerdockerTwisted Coding Standard ======================= Naming ------ Try to choose names which are both easy to remember and meaningful. Some silliness is OK at the module naming level (see :py:mod:`twisted.spread` ...) but when choosing class names, be as precise as possible. Try to avoid terms that may have existing definitions or uses. This rule is often broken, since it is incredibly difficult, as most normal words have already been taken by some other software. As an example, using the term "reactor" elsewhere in Twisted for something that is not an implementor of ``IReactor`` adds additional meaning to the word and will cause confusion. More importantly, try to avoid meaningless words. In particular, words like "handler", "processor", "engine", "manager", and "component" don't really indicate what something does, only that it does *something*. Use American spelling in both names and docstrings. For compound technical terms such as 'filesystem', use a non-hyphenated spelling in both docstrings and code in order to avoid unnecessary capitalization. Testing ------- Overview ~~~~~~~~ Twisted development should always be `test-driven `_ . The complete test suite in the head of the Git trunk is required to be passing on `supported platforms `_ at all times. Regressions in the test suite are addressed by reverting whatever revisions introduced them. Test Suite ~~~~~~~~~~ .. note:: The :doc:`test standard ` contains more in-depth information on this topic. What follows is intended to be a synopsis of the most important points. The Twisted test suite is spread across many subpackages of the ``twisted`` package. Many older tests are in ``twisted.test`` . Others can be found at places such as ``twisted.web.test`` (for ``twisted.web`` tests) or ``twisted.internet.test`` (for ``twisted.internet`` tests). The latter arrangement, ``twisted.somepackage.test``, is preferred for new tests except when a test module already exists in ``twisted.test`` . Parts of the Twisted test suite may serve as good examples of how to write tests for Twisted or for Twisted-based libraries (newer parts of the test suite are generally better examples than older parts - check when the code you are looking at was written before you use it as an example of what you should write). The names of test modules must begin with ``test_`` so that they are automatically discoverable by test runners such as Trial. Twisted's unit tests are written using :py:mod:`twisted.trial`, an xUnit library which has been extensively customized for use in testing Twisted and Twisted-based libraries. Implementation (i.e., non-test) source files should begin with a ``test-case-name`` tag which gives the name of any test modules or packages which exercise them. This lets tools discover a subset of the entire test suite which they can run first to find tests which might be broken by a particular change. All unit test methods should have docstrings specifying at a high level the intent of the test. That is, a description that users of the method would understand. If you modify, or write a new, HOWTO, please read the :doc:`documentation writing standard `. Copyright Header ---------------- Whenever a new file is added to the repository, add the following license header at the top of the file: .. code-block:: python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. When you update existing files, if there is no copyright header, add one. Whitespace ---------- Code must be formatted according to the `The Black Code Style `_. This entire source tree can be reformatted by running: .. code-block:: console tox -e lint Only changed files can be reformatted by running: .. code-block:: console pipx run pre-commit run Modules ------- Modules must be named in all lower-case, preferably short, single words. If a module name contains multiple words, they may be separated by underscores or not separated at all. Modules must have a copyright message, a docstring, and a reference to a test module that contains the bulk of its tests. New modules must have the ``absolute_import``, ``division``, and optionally the ``print_function`` imports from the ``__future__`` module. Use this template: :download:`new_module_template.py <../listings/new_module_template.py>` .. literalinclude:: ../listings/new_module_template.py In most cases, modules should contain more than one class, function, or method; if a module contains only one object, consider refactoring to include more related functionality in that module. Depending on the situation, it is acceptable to have imports that look like this: .. code-block:: python from twisted.internet.defer import Deferred or like this: .. code-block:: python from twisted.internet import defer That is, modules should import *modules* or *classes and functions*, but not *packages*. Wildcard import syntax may not be used by code in Twisted. These imports lead to code which is difficult to read and maintain by introducing complexity which strains human readers and automated tools alike. If you find yourself with many imports to make from a single module and wish to save typing, consider importing the module itself, rather than its attributes. *Relative imports* (or *sibling imports*) may not be used by code in Twisted. Relative imports allow certain circularities to be introduced which can ultimately lead to unimportable modules or duplicate instances of a single module. Relative imports also make the task of refactoring more difficult. In case of local names conflicts due to import, use the ``as`` syntax, for example: .. code-block:: python from twisted.trial import util as trial_util The encoding must always be ASCII, so no coding cookie is necessary. Packages -------- Package names follow the same conventions as module names. All modules must be encapsulated in some package. Nested packages may be used to further organize related modules. ``__init__.py`` must never contain anything other than a docstring and (optionally) an ``__all__`` attribute. Packages are not modules and should be treated differently. This rule may be broken to preserve backwards compatibility if a module is made into a nested package as part of a refactoring. If you wish to promote code from a module to a package, for example, to break a large module out into several smaller files, the accepted way to do this is to promote from within the module. For example, .. code-block:: python # parent/ # --- __init__.py --- import child # --- child.py --- import parent class Foo: pass parent.Foo = Foo Packages must not depend circularly upon each other. To simplify maintaining this state, packages must also not import each other circularly. While this applies to all packages within Twisted, one ``twisted.python`` deserves particular attention, as it may not depend on any other Twisted package. Strings ------- All strings in Twisted which are not interfacing directly with Python (e.g. ``sys.path`` contents, module names, and anything which returns ``str`` on both Python 2 and 3) should be marked explicitly as "bytestrings" or "text/Unicode strings". This is done by using the ``b`` (for bytestrings) or ``u`` (for Unicode strings) prefixes when using string literals. String literals not marked with this are "native/bare strings", and have a different meaning on Python 2 (where a bare string is a bytestring) and Python 3 (where a bare string is a Unicode string). .. code-block:: python u"I am text, also known as a Unicode string!" b"I am a bytestring!" "I am a native bare string, and therefore may be either!" Bytestrings and text must not be implicitly concatenated, as this causes an invisible ASCII encode/decode on Python 2, and causes an exception on Python 3. Use ``+`` to combine bytestrings, not string formatting (either "percent formatting" or ``.format()``). .. code-block:: python HTTPVersion = b"1.1" transport.write(b"HTTP/" + HTTPVersion) Utilities are available in :py:mod:`twisted.python.compat` to paper over some use cases where other Python code (especially the standard library) expects a "native string", or provides a native string where a bytestring is actually required (namely :py:mod:`twisted.python.compat.nativeString ` and :py:mod:`twisted.python.compat.networkString `) String Formatting Operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When using "percent formatting", you should always use a tuple if you're using non-mapping ``values``. This is to avoid unexpected behavior when you think you're passing in a single value, but the value is unexpectedly a tuple, e.g.: .. code-block:: python def foo(x): return "Hi %s\n" % x The example shows you can pass in ``foo("foo")`` or ``foo(3)`` fine, but if you pass in ``foo((1,2))``, it raises a ``TypeError``. You should use this instead: .. code-block:: python def foo(x): return "Hi %s\n" % (x,) Docstrings ---------- Docstrings should always be used to describe the purpose of methods, functions, classes, and modules. Moreover, all methods, functions, classes, and modules must have docstrings. In addition to documenting the purpose of the object, the docstring must document all of parameters or attributes of the object. When documenting a class with one or more attributes which are initialized directly from the value of a ``__init__`` argument by the same name (or differing only in that the attribute is private), it is sufficient to document the ``__init__`` parameter (in the ``__init__`` docstring). For example: .. code-block:: python class Ninja(object): """ @ivar speed: See L{__init__} @ivar _stealth: See C{stealth} parameter of L{__init__} """ def __init__(self, speed, stealth): """ @param speed: The maximum rate at which this ninja can travel (m/s) @type speed: L{int} or L{float} @param stealth: This ninja's ability to avoid being noticed in its activities, as a percentage modifier. @type: L{int} """ self.speed = speed self._stealth = stealth It is not necessary to have a second copy of the documentation for the attribute in the class docstring, only a reference to the method (typically ``__init__`` which does document the attribute's meaning). Of course, if there is any interesting additional behavior about the attribute that does not apply to the ``__init__`` argument, that behavior should be documented in the class docstring. Docstrings are *never* to be used to provide semantic information about an object; this rule may be violated if the code in question is to be used in a system where this is a requirement (such as Zope). Docstrings should be indented to the level of the code they are documenting. Docstrings must be triple-quoted, with opening and the closing of the docstrings being on a line by themselves. For example: .. code-block:: python class Ninja(object): """ A L{Ninja} is a warrior specializing in various unorthodox arts of war. """ def attack(self, someone): """ Attack C{someone} with this L{Ninja}'s shuriken. """ Docstrings are written in epytext format; more documentation is available in the `Epytext Markup Language documentation `_. When you are referring to a type, you should use `L{}`, whether it's in the stdlib , in Twisted or somewhere else. `NoneType` is an exception and we are referring it just as `L{None}`. Pydoctor, the software we use to generate the documentation, links to the Python standard library if you use ``L{}`` with standard Python types (e.g. ``L{str}``). For the API doc `C{something}` means "I made up a new word, and I want it to be monospaced, like it's an identifier in code and not an English noun" `L{something}` means "I am referring to the previously-defined concept/package/module/class/function/method/attribute identified as `something`" Additionally, to accommodate emacs users, single quotes of the type of the docstring's triple-quote should be escaped. This will prevent font-lock from accidentally fontifying large portions of the file as a string. For example, .. code-block:: python def foo2bar(f): """ Convert L{foo}s to L{bar}s. A function that should be used when you have a C{foo} but you want a C{bar}; note that this is a non-destructive operation. If this method can't convert the C{foo} to a C{bar} it will raise a L{FooException}. @param f: C{foo} @type f: L{str} For example:: import wombat def sample(something): f = something.getFoo() f.doFooThing() b = wombat.foo2bar(f) b.doBarThing() return b """ # Optionally, actual code can go here. Comments -------- Start by reading the `PEP8 Comments section `_. Ignore `Documentation Strings` section from PEP8 as Twisted uses a different docstring standard. `FIXME/TODO` comments must have an associated ticket and contain a reference to it in the form of a full URL to the ticket. A brief amount of text should provide info about why the FIXME was added. It does not have to be the full ticket description, just enough to help readers decide if their next step should be reading the ticket or continue reading the code. .. code-block:: python # FIXME: https://twistedmatrix.com/trac/ticket/1235 # Threads that have died before calling stop() are not joined. for thread in threads: thread.join() Versioning ---------- The API documentation should be marked up with version information. When a new API is added the class should be marked with the epytext `@since: field `_ to include the version number when the change was introduced. The placeholder string ``Twisted NEXT`` will be `replaced at release time `_ with the appropriate version number. For example: .. code-block:: python def bar2baz(): """ Bazify a bar. @since: Twisted NEXT """ The ``@since`` tag cannot be applied to arguments. When adding a new argument, indicate the version of introduction on a separate line. For example, if the ``swizzle`` keyword argument is added after the release of the above function in Twisted 18.5.0: .. code-block:: python def bar2baz(swizzle=False): """ Bazify a bar, with optional swizzling. @param swizzle: Activate swizzling. Present Since Twisted NEXT @type swizzle: L{bool} @since: Twisted 18.5.0 """ Scripts ------- For each "script" , that is, a program you expect a Twisted user to run from the command-line, the following things must be done: #. Write a module in :py:mod:`twisted.scripts` which contains a callable global named ``run``. This will be called by the command line part with no arguments (it will usually read ``sys.argv`` ). Feel free to write more functions or classes in this module, if you feel they are useful to others. #. Create a file which contains a shebang line for Python. This file should be placed in the ``bin/`` directory; for example, ``bin/twistd``. .. code-block:: python #!/usr/bin/env python To make sure that the script is portable across different UNIX like operating systems we use the ``/usr/bin/env`` command. The env command allows you to run a program in a modified environment. That way you don't have to search for a program via the ``PATH`` environment variable. This makes the script more portable but note that it is not a foolproof method. Always make sure that ``/usr/bin/env`` exists or use a softlink/symbolic link to point it to the correct path. Python's distutils will rewrite the shebang line upon installation so this policy only covers the source files in version control. #. For core scripts, add an entry point to ``"console_scripts"`` in ``setup.cfg``: .. code-block:: cfg [options.entry_points] console_scripts = ... twistd = twisted.scripts.twistd:run yourmodule = twisted.scripts.yourmodule:run #. And end with: .. code-block:: python from twisted.scripts.yourmodule import run run() #. Write a manpage and add it to the ``man`` folder of a subproject's ``doc`` folder. On Debian systems you can find a skeleton example of a manpage in ``/usr/share/doc/man-db/examples/manpage.example``. This will ensure that your program will have a proper ``console_scripts`` entry point, which ``setup.py`` will use to generate a console script which will work correctly for users of Git, Windows releases and Debian packages. Examples -------- For example scripts you expect a Twisted user to run from the command-line, add this Python shebang line at the top of the file: .. code-block:: python #!/usr/bin/env python Standard Library Extension Modules ---------------------------------- When using the extension version of a module for which there is also a Python version, place the import statement inside a try/except block, and import the Python version if the import fails. This allows code to work on platforms where the extension version is not available. For example: .. code-block:: python try: import cPickle as pickle except ImportError: import pickle Use the "as" syntax of the import statement as well, to set the name of the extension module to the name of the Python module. Some modules don't exist across all supported Python versions. For example, Python 2.3's ``sets`` module was deprecated in Python 2.6 in favor of the ``set`` and ``frozenset`` builtins. :py:mod:`twisted.python.compat` would be the place to add ``set`` and ``frozenset`` implementations that work across Python versions. Classes ------- Classes are to be named in mixed case, with the first letter capitalized; each word separated by having its first letter capitalized. Acronyms should be capitalized in their entirety. Class names should not be prefixed with the name of the module they are in. Examples of classes meeting this criteria: - ``twisted.spread.pb.ViewPoint`` - ``twisted.parser.patterns.Pattern`` Examples of classes **not** meeting this criteria: - ``event.EventHandler`` - ``main.MainGadget`` An effort should be made to prevent class names from clashing with each other between modules, to reduce the need for qualification when importing. For example, a Service subclass for Forums might be named ``twisted.forum.service.ForumService``, and a Service subclass for Words might be ``twisted.words.service.WordsService``. Since neither of these modules are volatile *(see above)* the classes may be imported directly into the user's namespace and not cause confusion. New-style Classes ~~~~~~~~~~~~~~~~~ Classes and instances in Python come in two flavors: old-style or classic, and new-style. Up to Python 2.1, old-style classes were the only flavour available to the user, new-style classes were introduced in Python 2.2 to unify classes and types. All classes added to Twisted must be written as new-style classes. If ``x`` is an instance of a new-style class, then ``type(x)`` is the same as ``x.__class__``. Methods ------- Methods should be in mixed case, with the first letter lower case, each word separated by having its first letter capitalized. For example, ``someMethodName``, ``method``. Sometimes, a class will dispatch to a specialized sort of method using its name; for example, ``twisted.reflect.Accessor``. In those cases, the type of method should be a prefix in all lower-case with a trailing underscore, so method names will have an underscore in them. For example, ``get_someAttribute``. Underscores in method names in twisted code are therefore expected to have some semantic associated with them. Some methods, in particular ``addCallback`` and its cousins return self to allow for chaining calls. In this case, wrap the chain in parenthesis, and start each chained call on a separate line, for example: .. code-block:: python return (foo() .addCallback(bar) .addCallback(thud) .addCallback(wozers)) Using the Global Reactor ------------------------ Even though it may be convenient, module-level imports of the global Twisted reactor (``from twisted.internet import reactor``) should be avoided. Importing the reactor at the module level means that reactor selection occurs on initial import, and not at the request of the code that originally imported the module. Applications may wish to import their own reactor, or otherwise use a reactor different than Twisted's default (for example, using the experimental cfreactor on macOS); importing at the module level means they would have to monkeypatch in the different reactor, or use similar hacks. This is especially apparent in Twisted's own test suite; many tests wish to provide their own reactor which controls the passage of time and simulates timeouts. Below is an example of the pattern for accepting the user's choice of reactor -- importing the global one if none is specified -- taken (and trimmed for brevity) from existing Twisted source code. .. code-block:: python class Session(object): """ A user's session with a system. @ivar _reactor: An object providing L{IReactorTime} to use for scheduling expiration. """ def __init__(self, site, uid, reactor=None): """ Initialize a session with a unique ID for that session. """ if reactor is None: from twisted.internet import reactor self._reactor = reactor # ... other code ... The reactor attribute should be private by default, but if it is useful to the users of the code, there is no reason why it can not be public. Callback Arguments ------------------ There are several methods whose purpose is to help the user set up callback functions, for example :py:meth:`Deferred.addCallback ` or the reactor's :py:meth:`callLater ` method. To make access to the callback as transparent as possible, most of these methods use ``**kwargs`` to capture arbitrary arguments that are destined for the user's callback. This allows the call to the setup function to look very much like the eventual call to the target callback function. In these methods, take care to not have other argument names that will "steal" the user's callback's arguments. When sensible, prefix these "internal" argument names with an underscore. For example, :py:meth:`RemoteReference.callRemote ` is meant to be called like this: .. code-block:: python myref.callRemote("addUser", "bob", "555-1212") # on the remote end, the following method is invoked: def addUser(name, phone): ... where "addUser" is the remote method name. The user might also choose to call it with named parameters like this: .. code-block:: python myref.callRemote("addUser", name="bob", phone="555-1212") In this case, ``callRemote`` (and any code that uses the ``**kwargs`` syntax) must be careful to not use "name", "phone", or any other name that might overlap with a user-provided named parameter. Therefore, ``callRemote`` is implemented with the following signature: .. code-block:: python class SomeClass(object): def callRemote(self, _name, *args, **kw): ... Do whatever you can to reduce user confusion. It may also be appropriate to ``assert`` that the kwargs dictionary does not contain parameters with names that will eventually cause problems. Special Methods --------------- The augmented assignment protocol, defined by ``__iadd__`` and other similarly named methods, can be used to allow objects to be modified in place or to rebind names if an object is immutable -- both through use of the same operator. This can lead to confusing code, which in turn leads to buggy code. For this reason, methods of the augmented assignment protocol should not be used in Twisted. Functions --------- Functions should be named similarly to methods. Functions or methods which are responding to events to complete a callback or errback should be named ``_cbMethodName`` or ``_ebMethodName``, in order to distinguish them from normal methods. Attributes ---------- Attributes should be named similarly to functions and methods. Attributes should be named descriptively; attribute names like ``mode``, ``type``, and ``buf`` are generally discouraged. Instead, use ``displayMode``, ``playerType``, or ``inputBuffer``. Do not use Python's "private" attribute syntax; prefix non-public attributes with a single leading underscore. Since several classes have the same name in Twisted, and they are distinguished by which package they come from, Python's double-underscore name mangling will not work reliably in some cases. Also, name-mangled private variables are more difficult to address when unit testing or persisting a class. An attribute (or function, method or class) should be considered private when one or more of the following conditions are true: - The attribute represents intermediate state which is not always kept up-to-date. - Referring to the contents of the attribute or otherwise maintaining a reference to it may cause resources to leak. - Assigning to the attribute will break internal assumptions. - The attribute is part of a known-to-be-sub-optimal interface and will certainly be removed in a future release. Python 3 -------- Twisted was ported to Python 3. Please see :doc:`Porting to Python 3 ` for details. Database -------- Database tables will be named with plural nouns. Database columns will be named with underscores between words, all lower case, since most databases do not distinguish between case. Any attribute, method argument, or method name that corresponds *directly* to a column in the database will be named exactly the same as that column, regardless of other coding conventions surrounding that circumstance. All SQL keywords should be in upper case. C Code ------ C code must be optional, and work across multiple platforms (MSVC++14 for Python 3 on Windows, as well as recent GCCs and Clangs for Linux, macOS, and FreeBSD). C code should be kept in external bindings packages which Twisted depends on. If creating new C extension modules, using `cffi `_ is highly encouraged, as it will perform well on PyPy and CPython, and be easier to use on Python 2 and 3. Consider optimizing for `PyPy `_ instead of creating bespoke C code. Commit Messages --------------- The commit messages are being distributed in a myriad of ways. Because of that, you need to observe a few simple rules when writing a commit message. The first line of the message is being used as both the subject of the commit email and the announcement on #twisted. Therefore, it should be short (aim for < 80 characters) and descriptive -- and must be able to stand alone (it is best if it is a complete sentence). The rest of the e-mail should be separated with *hard line breaks* into short lines (< 70 characters). This is free-format, so you can do whatever you like here. Commit messages should be about *what*, not *how*: we can get how from Git diff. Explain reasons for commits, and what they affect. Each commit should be a single logical change, which is internally consistent. If you can't summarize your changes in one short line, this is probably a sign that they should be broken into multiple checkins. Source Control -------------- Twisted currently uses Git for source control. All development must occur using branches; when a task is considered complete another Twisted developer may review it and if no problems are found, it may be merged into trunk. The Twisted wiki has `a start `_. If you wish to ignore certain files, create a ``.gitignore`` file, or edit it if it exists. For example: .. code-block:: console dropin.cache *.pyc *.pyo *.o *.lo *.la #*# .*.rej *.rej .*~ Fallback -------- In case of conventions not enforced in this document, the reference documents to use in fallback are: * `The Black code style `_ * `PEP 8 `_ for Python code * `PEP 7 `_ for C code Recommendations --------------- These things aren't necessarily standardizeable (in that code can't be easily checked for compliance) but are a good idea to keep in mind while working on Twisted. If you're going to work on a fragment of the Twisted codebase, please consider finding a way that you would *use* such a fragment in daily life. Using a Twisted Web server on your website encourages you to actively maintain and improve your code, as the little everyday issues with using it become apparent. Twisted is a **big** codebase! If you're refactoring something, please make sure to recursively grep for the names of functions you're changing. You may be surprised to learn where something is called. Especially if you are moving or renaming a function, class, method, or module, make sure that it won't instantly break other code. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/compatibility-policy.rst0000644000175100001710000006370300000000000025171 0ustar00runnerdockerTwisted Compatibility Policy ============================ Motivation ---------- The Twisted project has a small development team, and we cannot afford to provide anything but critical bug-fix support for multiple version branches of Twisted. However, we all want Twisted to provide a positive experience during development, deployment, and usage. Therefore we need to provide the most trouble-free upgrade process possible, so that Twisted application developers will not shy away from upgrades that include necessary bugfixes and feature enhancements. Twisted is used by a wide variety of applications, many of which are proprietary or otherwise inaccessible to the Twisted development team. Each of these applications is developed against a particular version of Twisted. The most important compatibility to preserve is at the Python API level. Python does not provide us with a strict way to partition **public** and **private** objects (methods, classes, modules), so it is unfortunately quite likely that many of those applications are using arbitrary parts of Twisted. Our compatibility strategy needs to take this into account, and be comprehensive across our entire codebase. Exceptions can be made for modules aggressively marked **unstable** or **experimental**, but even experimental modules will start being used in production code if they have been around for long enough. The purpose of this document is to to lay out rules for Twisted application developers who wish to weather the changes when Twisted upgrades, and procedures for Twisted engine developers - both contributors and core team members - to follow when who want to make changes which may be incompatible to Twisted itself. Defining Compatibility ---------------------- The word "compatibility" is itself difficult to define. While comprehensive compatibility is good, total compatibility is neither feasible nor desirable. Total compatibility requires that nothing ever change, since any change to Python code is detectable by a sufficiently determined program. There is some folk knowledge around which kind of changes **obviously** won't break other programs, but this knowledge is spotty and inconsistent. Rather than attempt to disallow specific kinds of changes, here we will lay out a list of changes which are considered compatible. Throughout this document, **compatible** changes are those which meet these specific criteria. Although a change may be broadly considered backward compatible, as long as it does not meet this official standard, it will be officially deemed **incompatible** and put through the process for incompatible changes. The compatibility policy described here is 99% about changes to **interface**, not changes to functionality. .. note:: Ultimately we want to make the user happy but we cannot put every possible thing that will make every possible user happy into this policy. Brief notes for developers -------------------------- Here is a summary of the things that need to be done for deprecating code. This is not an exhaustive read and beside this list you should continue reading the rest of this document: * Do not change the function's behavior as part of the deprecation process. * Cause imports or usage of the class/function/method to emit a DeprecationWarning either call warnings.warn or use one of the helper APIs * The warning text must include the version of Twisted in which the function is first deprecated (which will always be a version in the future) * The warning text should recommend a replacement, if one exists. * The warning must "point to" the code which called the function. For example, in the normal case, this means stacklevel=2 passed to warnings.warn. * There must be a unit test which verifies the deprecation warning. * A .removal news fragment must be added to announce the deprecation. Procedure for Incompatible Changes ---------------------------------- Any change specifically described in the next section as **compatible** may be made at any time, in any release. The First One's Always Free ^^^^^^^^^^^^^^^^^^^^^^^^^^^ The general purpose of this document is to provide a pleasant upgrade experience for Twisted application developers and users. The specific purpose of this procedure is to achieve that experience by making sure that any application which runs without warnings may be upgraded one minor version of twisted (y to y+1 in x.y.z) or from the last minor revision of a major release to the first minor revision of the next major release (x to x + 1 in x.y.z to x.0.z, when there will be no x.y+1.z). In other words, any application which runs its tests without triggering any warnings from Twisted should be able to have its Twisted version upgraded at least once with no ill effects except the possible production of new warnings. Incompatible Changes ^^^^^^^^^^^^^^^^^^^^ Any change which is not specifically described as **compatible** must be made in 2 phases. If a change is made in release R, the timeline is: 1. Release R: New functionality is added and old functionality is deprecated with a DeprecationWarning. 2. At the earliest, release R+2 and one year after release R, but often much later: Old functionality is completely removed. Removal should happen once the deprecated API becomes an additional maintenance burden. For example, if it makes implementation of a new feature more difficult, if it makes documentation of non-deprecated APIs more confusing, or if its unit tests become an undue burden on the continuous integration system. Removal should not be undertaken just to follow a timeline. Twisted should strive, as much as practical, not to break applications relying on it. Procedure for Exceptions to this Policy --------------------------------------- **Every change is unique.** Sometimes, we'll want to make a change that fits with the spirit of this document (keeping Twisted working for applications which rely upon it) but may not fit with the letter of the procedure described above (the change modifies behavior of an existing API sufficiently that something might break). Generally, the reason that one would want to do this is to give applications a performance enhancement or bug fix that could break behavior in unintended hypothetical uses of an existing API, but we don't want well-behaved applications to pay the penalty of a deprecation/adopt-a-new-API/removal cycle in order to get the benefits of the improvement if they don't need to. If this is the case for your change, it's possible to make such a modification without a deprecation/removal cycle. However, we must give users an opportunity to discover whether a particular incompatible change affects them: we should not trust our own assessments of how code uses the API. In order to propose an incompatible change, start a discussion on the mailing list. Make sure that it is eye-catching, so those who don't read all list messages in depth will notice it, by prefixing the subject with **INCOMPATIBLE CHANGE:** (capitalized like so). Always include a link to the ticket, and branch (if relevant). In order to **conclude** such a discussion, there must be a branch available so that developers can run their unit tests against it to mechanically verify that their understanding of their own code is correct. If nobody can produce a failing test or broken application within **a week's time** from such a branch being both 1. available and 2. announced, and at least **three committers** agree that the change is worthwhile, then the branch can be considered approved for the incompatible change in question. Since some codebases that use Twisted are presumably proprietary and confidential, there should be a good-faith presumption if someone says they have broken tests but cannot immediately produce code to share. The branch must be available for one week's time. .. note:: The announcement forum for incompatible changes and the waiting period required are subject to change as we discover how effective this method is; the important aspect of this policy is that users have some way of finding out in advance about changes which might affect them. Compatible Changes. Changes not Covered by the Compatibility Policy ------------------------------------------------------------------- Here is a non-exhaustive list of changes which are not covered by the compatibility policy. These changes can be made without having to worry about the compatibility policy. Test Changes ^^^^^^^^^^^^ No code or data in a test package should be imported or used by a non-test package within Twisted. By doing so, there's no chance anything could access these objects by going through the public API. Test code and test helpers are considered private API and should not be imported outside of the Twisted testing infrastructure. Private Changes ^^^^^^^^^^^^^^^ Code is considered *private* if the user would have to type a leading underscore to access it. In other words, a function, module, method, attribute or class whose name begins with an underscore may be arbitrarily changed. Bug Fixes and Gross Violation of Specifications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If Twisted documents an object as complying with a published specification, and there are inputs which can cause Twisted to behave in obvious violation of that specification, then changes may be made to correct the behavior in the face of those inputs. If application code must support multiple versions of Twisted, and work around violations of such specifications, then it must test for the presence of such a bug before compensating for it. For example, Twisted supplies a DOM implementation in twisted.web.microdom. If an issue were discovered where parsing the string `Hello` and then serializing it again resulted in `>xml/xml<`, that would grossly violate the XML specification for well-formedness. Such code could be fixed with no warning other than release notes detailing that this error is now fixed. Raw Source Code ^^^^^^^^^^^^^^^ The most basic thing that can happen between Twisted versions, of course, is that the code may change. That means that no application may ever rely on, for example, the value of any **func_code** object's **co_code** attribute remaining stable, or the **checksum** of a .py file remaining stable. **Docstrings** may also change at any time. Applications must not depend on any Twisted class, module, or method's metadata attributes such as ``__module__``, ``__name__``, ``__qualname__``, ``__annotations__`` and ``__doc__`` to remain the same. New Attributes ^^^^^^^^^^^^^^ New code may also be added. Applications must not depend on the output of the ``dir()`` function on any object remaining stable, nor on any object's ``__all__`` attribute, nor on any object's ``__dict__`` not having new keys added to it. These may happen in any maintenance or bugfix release, no matter how minor. Pickling ^^^^^^^^ Even though Python objects can be pickled and unpickled without explicit support for this, whether a particular pickled object can be unpickled after any particular change to the implementation of that object is less certain. Because of this, applications must not depend on any object defined by Twisted to provide pickle compatibility between any release unless the object explicitly documents this as a feature it has. Representations ^^^^^^^^^^^^^^^ The printable representations of objects, as returned by ``repr()`` and defined by ``def __repr__(self):`` are for debugging and informational purposes. Because of this, applications must not depend on any object defined by Twisted to provide repr compatibility between any release. Attribute Access ^^^^^^^^^^^^^^^^ How an object's attributes are defined and accessed is considered an implementation detail. To allow backwards compatibility, an attribute may be moved from the instance ``__dict__`` into an ``@property`` or other descriptor based accessor. Adding new attributes to a constructed object, or monkey patching, is not considered a public use. This restriction allows both creating and converting to slotted classes. Because of this, applications must not depend on any object defined by Twisted to provide ``__dict__`` or ``__slots__`` compatibility between any release. Changes Covered by the Compatibility Policy ------------------------------------------- Here is a non-exhaustive list of changes which are not covered by the compatibility policy. Some changes appear to be in keeping with the above rules describing what is compatible, but are in fact not. Interface Changes ^^^^^^^^^^^^^^^^^ Although methods may be added to implementations, adding those methods to interfaces may introduce an unexpected requirement in user code. .. note:: There is currently no way to express, in zope.interface, that an interface may optionally provide certain features which need to be tested for. Although we can add new code, we can't add new requirements on user code to implement new methods. This is easier to deal with in a system which uses abstract base classes because new requirements can provide default implementations which provide warnings. Something could also be put in place to do the same with interfaces, since they already install a metaclass, but this is tricky territory. The only example I'm aware of here is the Microsoft tradition of ISomeInterfaceN where N is a monotonically ascending number for each release. Private Objects Available via Public Entry Points ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If a **public** entry point returns a **private** object, that **private** object must preserve its **public** attributes. In the following example, ``_ProtectedClass`` can no longer be arbitrarily changed. Specifically, ``getUsers()`` is now a public method, thanks to ``get_users_database()`` exposing it. However, ``_checkPassword()`` can still be arbitrarily changed or removed. For example: .. code-block:: python class _ProtectedClass: """ A private class which is initialized only by an entry point. """ def getUsers(self): """ A public method covered by the compatibility policy. """ return [] def _checkPassword(self): """ A private method not covered by the compatibility policy. """ return False def get_users_database(): """ A method guarding the initialization of the private class. Since the method is public and it returns an instance of the C{_ProtectedClass}, this makes the _ProtectedClass a public class. """ return _ProtectedClass() Private Class Inherited by Public Subclass ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A **private** class which is inherited or exposed in any way by **public** subclass will make the inherited class **public**. The **private** is still protected against direct instantiation. .. code-block:: python class _Base(object): """ A class which should not be directly instantiated. """ def getActiveUsers(self): return [] def getExpiredusers(self): return [] class Users(_Base): """ Public class inheriting from a private class. """ pass In the following example ``_Base`` is effectively **public**, since ``getActiveUsers()`` and ``getExpiredusers()`` are both exposed via the **public** ``Users`` class. Documented and Tested Gross Violation of Specifications ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If the behaviour of a what was later found as a bug was documented, or fixing it caused existing tests to break, then the change should be considered incompatible, regardless of how gross its violation. It may be that such violations are introduced specifically to deal with other grossly non-compliant implementations of said specification. If it is determined that those reasons are invalid or ought to be exposed through a different API, the change is compatible. Application Developer Upgrade Procedure --------------------------------------- When an application wants to be upgraded to a new version of Twisted, it can do so immediately. However, if the application wants to get the same **for free** behavior for the next upgrade, the application's tests should be run treating warnings as errors, and fixed. Supporting and de-supporting Python versions -------------------------------------------- Twisted does not have a formal policy around supporting new versions of Python or de-supporting old versions of Python. We strive to support Twisted on any version of Python that is the default Python for a vendor-supported release from a major platform, namely Debian, Ubuntu, the latest release of Windows, or the latest release of macOS. The versions of Python currently supported are listed in the ​INSTALL file for each release. A distribution release + Python version is only considered supported when a `buildbot builder `_ exists for it. Removing support for a Python version will be announced at least 1 release prior to the removal. How to deprecate APIs --------------------- Classes ^^^^^^^ Classes are deprecated by raising a warning when they are access from within their module, using the :py:func:`deprecatedModuleAttribute ` helper. .. code-block:: python class SSLContextFactory: """ An SSL context factory. """ deprecatedModuleAttribute( Version("Twisted", 12, 2, 0), "Use twisted.internet.ssl.DefaultOpenSSLContextFactory instead.", "twisted.mail.protocols", "SSLContextFactory") Functions and methods ^^^^^^^^^^^^^^^^^^^^^ To deprecate a function or a method, add a call to warnings.warn to the beginning of the implementation of that method. The warning should be of type ``DeprecationWarning`` and the stack level should be set so that the warning refers to the code which is invoking the deprecated function or method. The deprecation message must include the name of the function which is deprecated, the version of Twisted in which it was first deprecated, and a suggestion for a replacement. If the API provides functionality which it is determined is beyond the scope of Twisted or it has no replacement, then it may be deprecated without a replacement. There is also a :py:func:`deprecated ` decorator which works for new-style classes. For example: .. code-block:: python import warnings from twisted.python.deprecate import deprecated from twisted.python.versions import Version @deprecated(Version("Twisted", 1, 2, 0), "twisted.baz") def some_function(bar): """ Function deprecated using a decorator. """ return bar * 3 @deprecated(Version("Twisted", 1, 2, 0)) def some_function(bar): """ Function deprecated using a decorator and which has no replacement. """ return bar * 3 def some_function(bar): """ Function with a direct call to warnings. """ warnings.warn( 'some_function is deprecated since Twisted 1.2.0. ' 'Use twisted.baz instead.', category=DeprecationWarning, stacklevel=2) return bar * 3 Instance attributes ^^^^^^^^^^^^^^^^^^^ To deprecate an attribute on instances of a new-type class, make the attribute into a property and call ``warnings.warn`` from the getter and/or setter function for that property. You can also use the :py:func:`deprecatedProperty ` decorator which works for new-style classes. .. code-block:: python from twisted.python.deprecate import deprecated from twisted.python.versions import Version class SomeThing(object): """ A class for which the C{user} ivar is not yet deprecated. """ def __init__(self, user): self.user = user class SomeThingWithDeprecation(object): """ A class for which the C{user} ivar is now deprecated. """ def __init__(self, user=None): self._user = user @deprecatedProperty(Version("Twisted", 1, 2, 0)) def user(self): return self._user @user.setter def user(self, value): self._user = value Module attributes ^^^^^^^^^^^^^^^^^ Modules cannot have properties, so module attributes should be deprecated using the :py:func:`deprecatedModuleAttribute ` helper. .. code-block:: python from twisted.python import _textattributes from twisted.python.deprecate import deprecatedModuleAttribute from twisted.python.versions import Version flatten = _textattributes.flatten deprecatedModuleAttribute( Version('Twisted', 13, 1, 0), 'Use twisted.conch.insults.text.assembleFormattedText instead.', 'twisted.conch.insults.text', 'flatten') Modules ^^^^^^^ To deprecate an entire module, :py:func:`deprecatedModuleAttribute ` can be used on the parent package's ``__init__.py``. There are two other options: * Put a warnings.warn() call into the top-level code of the module. * Deprecate all of the attributes of the module. Testing Deprecation Code ------------------------ Like all changes in Twisted, deprecations must come with associated automated tests. Due to a bug in Trial (`#6348 `_), unhandled deprecation warnings will not cause test failures or show in test results. While the Trial bug is not fixed, to trigger test failures on unhandled deprecation warnings use: .. code-block:: console python -Werror::DeprecationWarning ./bin/trial twisted.conch There are several options for checking that a code is deprecated and that using it raises a ``DeprecationWarning``. There are helper methods available for handling deprecated callables (:py:meth:`callDeprecated `) and deprecated classes or module attributes (:py:meth:`getDeprecatedModuleAttribute `). If the deprecation warning has a customized message or cannot be caught using these helpers, you can use :py:meth:`assertWarns ` to specify the exact warning you expect. Lastly, you can use :py:meth:`flushWarnings ` after performing any deprecated activity. This is the most precise, but also the most verbose, way to assert that you've raised a ``DeprecationWarning``. .. code-block:: python from twisted.trial import unittest class DeprecationTests(unittest.TestCase): """ Tests for deprecated code. """ def test_deprecationUsingFlushWarnings(self): """ flushWarnings() is the recommended way of checking for deprecations. Make sure you only flushWarning from the targeted code, and not all warnings. """ db.getUser('some-user') message = ( 'twisted.Identity.getUser was deprecated in Twisted 15.0.0: ' 'Use twisted.get_user instead.' ) warnings = self.flushWarnings( [self.test_deprecationUsingFlushWarnings]) self.assertEqual(1, len(warnings)) self.assertEqual(DeprecationWarning, warnings[0]['category']) self.assertEqual(message, warnings[0]['message']) def test_deprecationUsingCallDeprecated(self): """ callDeprecated() assumes that the DeprecationWarning message follows Twisted's standard format. """ self.callDeprecated( Version("Twisted", 1, 2, 0), db.getUser, 'some-user') def test_deprecationUsingAssertWarns(self): """ assertWarns() is designed as a general helper to check any type of warnings and can be used for DeprecationsWarnings. """ self.assertWarns( DeprecationWarning, 'twisted.Identity.getUser was deprecated in Twisted 15.0.0 ' 'Use twisted.get_user instead.', __file__, db.getUser, 'some-user') When code is deprecated, all previous tests in which the code is called and tested will now raise ``DeprecationWarning``\ s. Making calls to the deprecated code without raising these warnings can be done using the :py:meth:`callDeprecated ` helper. .. code-block:: python from twisted.trial import unittest class IdentityTests(unittest.TestCase): """ Tests for our Identity behavior. """ def test_getUserHomePath(self): """ This is a test in which we check the returned value of C{getUser} but we also explicitly handle the deprecations warnings emitted during its execution. """ user = self.callDeprecated( Version("Twisted", 1, 2, 0), db.getUser, 'some-user') self.assertEqual('some-value', user.homePath) Tests which need to use deprecated classes should use the :py:meth:`getDeprecatedModuleAttribute ` helper. .. code-block:: python from twisted.trial import unittest class UsernameHashedPasswordTests(unittest.TestCase): """ Tests for L{UsernameHashedPassword}. """ def test_initialisation(self): """ The initialisation of L{UsernameHashedPassword} will set C{username} and C{hashed} on it. """ UsernameHashedPassword = self.getDeprecatedModuleAttribute( 'twisted.cred.credentials', 'UsernameHashedPassword', Version('Twisted', 20, 3, 0)) creds = UsernameHashedPassword(b"foo", b"bar") self.assertEqual(creds.username, b"foo") self.assertEqual(creds.hashed, b"bar") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/index.rst0000644000175100001710000000130000000000000022113 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Twisted Development Policy ========================== .. toctree:: :hidden: coding-standard writing-standard test-standard compatibility-policy code-dev release-process This series of documents is designed for people who wish to contribute to the Twisted codebase. - :doc:`Coding standard ` - :doc:`Documentation writing standard ` - :doc:`Testing standard ` - :doc:`Compatibility Policy ` - :doc:`Working from Twisted's Git repository ` - :doc:`Releasing Twisted ` ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/release-process.rst0000644000175100001710000002000300000000000024101 0ustar00runnerdockerTwisted Release Process ======================= This document describes the Twisted release process. Although it is still incomplete, every effort has been made to ensure that it is accurate and up-to-date. If you want to make changes to the release process, follow the normal Twisted development process (contribute release automation software that has documentation and unit tests demonstrating that it works). Outcomes -------- By the end of a Twisted release we'll have: - Wheel and sdist package published on `PyPI Twisted project `_. - Updated documentation (API & howtos) on `Twisted Read The Docs `_ - Announcement email sent to Twisted main list - A `GitHub Release `_ with the associated tag in our Git repository Prerequisites ------------- To release Twisted, you will need: - Commit privileges to Twisted GitHub repository. Version numbers --------------- Twisted releases use a time-based numbering scheme following PEP440 convention. Releases versions like YY.MM.mm, where YY is the last two digits of the year of the release, MM is the month of release, and mm is the number of the bugfix release. There are 3 release types: - Major release when YY.MM is updated. - Bugfix / patch / point release when the mm number is updated - Release candidates which are pre-releases as YY.MM.mmrc1 For example: - A release in Jan 2017 is 17.1.0 - A release in Nov 2017 is 17.11.0 - If 17.11.0 has some critical defects, then a bugfix 17.11.1 - The first release candidate of 17.1.0 is 17.1.0rc1, the second is 17.1.0rc2 Every release of Twisted includes the whole project. Throughout this document, we'll refer to the version number of the release as $RELEASE. Examples of $RELEASE include 10.0.0, 10.1.0, 10.1.1 etc. We'll refer to the first two components of the release as $API, since all releases that share those numbers are mutually API compatible. e.g. for 10.0.0, $API is 10.0; for 10.1.0 and 10.1.1, $API is 10.1. Incremental automatically picks the correct version number for you. Please retrieve it after you run it. Overview -------- To release Twisted, we 1. Prepare for a release 2. Release one or more release candidates 3. Release the final release Prepare for a release --------------------- #. Check for any ​regressions using `Trac regression report `_ #. Any regression should be fixed and merged trunk before making the release branch #. Choose a version number. #. File a ticket in Trac called "Release $RELEASE" and assign it to yourself. #. Make a branch for the release and include the ticket number in the name (4290 is Trac ticket ID): - ``git fetch origin`` - ``git checkout origin/trunk`` - ``git checkout -b release-$RELEASE-4290`` How to do a release candidate ----------------------------- Prepare the branch ~~~~~~~~~~~~~~~~~~ #. In your Git repo, fetch and check out the new release branch. #. Run ``python -m incremental.update Twisted --rc`` #. Commit the changes made by Incremental. #. Run ``tox -e towncrier``. #. Commit the changes made by towncrier - this automatically removes the newsfragment files. #. Bump copyright dates in ``LICENSE``, ``src/twisted/copyright.py``, and ``README.rst`` if required #. Push the changes up to GitHub and create a new release PR. #. The GitHub PR is dedicated to the final release and the same PR is used to release the candidate and final version. #. Use the `GitHub Create Release UI `_ the make a new release. #. Create a tag using the format `twisted-VERSION` based on the latest commit on the release branch that was approved after the review. #. Use `Twisted VERSION` as the name of the release. #. Add the release NEWS to GitHub Release page. #. Make sure 'This is a pre-release` is checked. #. Github Actions will upload the dist to PyPI when a new tag is pushed to the repo. #. Read the Docs hooks will publish a new version of the docs. Announce ~~~~~~~~ #. Write the release announcement #. Announce the release candidate on - the twisted-python mailing list by sending the content of latest release NEWS - on IRC in the ``#twisted-dev`` topic by sending the version number The release candidate announcement might mention the important changes since the last release, and ask readers to test this release candidate. Here's what the $RELEASErc1 release announcement might look like:: Live from PyCon Atlanta, I'm pleased to herald the approaching footsteps of the $API release. Wheels for Twisted $RELEASE release candidate are now available on PyPI. Highlights include: * Improved documentation, including "Twisted Web in 60 seconds" * Faster Perspective Broker applications * A new Windows installer that ships without zope.interface * Twisted no longer supports Python 2.3 * Over one hundred closed tickets For more information, see the NEWS file. Please download the tarballs and test them as much as possible. Thanks, jml A week is a generally good length of time to wait before doing the final release. How to do a final release ------------------------- Prepare the branch ~~~~~~~~~~~~~~~~~~ #. Have the release branch, previously used to generate a release candidate, checked out #. Run ``python -m incremental.update Twisted --newversion $RELEASE`` #. Manually update the release date if necessary. #. Commit and push. #. Submit the ticket for review #. Pause until the ticket is reviewed and accepted. #. Use the `GitHub Create Release UI `_ the make a new release. #. Create a tag using the format `twisted-VERSION` based on the latest commit on the release branch that was approved after the review. #. Use `Twisted VERSION` as the name of the release. #. Add the release NEWS to GitHub Release page. #. Make sure 'This is a pre-release` is not checked. #. Github Actions will upload the dist to PyPI when a new tag is pushed to the repo. PyPI is the only canonical source for Twisted packages. #. Read the Docs hooks will publish a new version of the docs. Announce ~~~~~~~~ #. Write the release announcement that should be similar to the release candidate, with the updated version and release date. #. Announce the release - Send a text version of the announcement to: twisted-python@twistedmatrix.com, python-announce-list@python.org, twisted-web@twistedmatrix.com - ​http://labs.twistedmatrix.com (Post a web version of the announcements, with links instead of literal URLs) - Twitter, if you feel like it - ``#twisted`` topic on IRC (you'll need ops) Post release ~~~~~~~~~~~~ #. Run ``python -m incremental.update Twisted --post`` to add a `post` postfix. #. Commit the post0 update change. #. Merge the release branch into trunk, closing the release ticket at the same time. When things go wrong -------------------- If you discover a showstopper bug during the release process, you have three options. 1. Abort the release, make a new point release (e.g. abort 10.0.0, make 10.0.1 after the bug is fixed) 2. Abort the release, make a new release candidate (e.g. abort 10.0.0, make 10.0.0pre3 after the bug is fixed) Don't delete a tag that was already pushed for a release. Create a new tag with incremented version. Bug fix releases ---------------- Sometimes, bugs happen, and sometimes these are regressions in the current released version. This section goes over doing these "bugfix" releases. 1. Ensure all bugfixes are in trunk. 2. Make a branch off the affected version. 3. Cherry-pick the merge commits that merge the bugfixes into trunk, onto the new release branch. 4. Go through the rest of the process for a full release from "How to do a release candidate", merging the release branch into trunk as normal as the end of the process. - Instead of just ``--rc`` when running the change-versions script, add the patch flag, making it ``--patch --rc``. - Instead of waiting a week, a shorter pause is acceptable for a patch release. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/test-standard.rst0000644000175100001710000004207300000000000023575 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Unit Tests in Twisted ===================== Each *unit test* tests one bit of functionality in the software. Unit tests are entirely automated and complete quickly. Unit tests for the entire system are gathered into one test suite, and may all be run in a single batch. The result of a unit test is simple: either it passes, or it doesn't. All this means you can test the entire system at any time without inconvenience, and quickly see what passes and what fails. Unit Tests in the Twisted Philosophy ------------------------------------ The Twisted development team adheres to the practice of `Extreme Programming `_ (XP), and the usage of unit tests is a cornerstone XP practice. Unit tests are a tool to give you increased confidence. You changed an algorithm -- did you break something? Run the unit tests. If a test fails, you know where to look, because each test covers only a small amount of code, and you know it has something to do with the changes you just made. If all the tests pass, you're good to go, and you don't need to second-guess yourself or worry that you just accidentally broke someone else's program. What to Test, What Not to Test ------------------------------ You don't have to write a test for every single method you write, only production methods that could possibly break. -- Kent Beck, Extreme Programming Explained Running the Tests ----------------- How ~~~ From the root of the Twisted source tree, run `Trial `_ : .. code-block:: console $ bin/trial twisted You'll find that having something like this in your emacs init files is quite handy: :: (defun runtests () (interactive) (compile "python /somepath/Twisted/bin/trial /somepath/Twisted")) (global-set-key [(alt t)] 'runtests) When ~~~~ Always, always, *always* be sure `all the tests pass `_ before committing any code. If someone else checks out code at the start of a development session and finds failing tests, they will not be happy and may decide to *hunt you down* . Since this is a geographically dispersed team, the person who can help you get your code working probably isn't in the room with you. You may want to share your work in progress over the network, but you want to leave the main Git tree in good working order. So `use a branch `_ , and merge your changes back in only after your problem is solved and all the unit tests pass again. Adding a Test ------------- Please don't add new modules to Twisted without adding tests for them too. Otherwise we could change something which breaks your module and not find out until later, making it hard to know exactly what the change that broke it was, or until after a release, and nobody wants broken code in a release. Tests go into dedicated test packages such as ``twisted/test/`` or ``twisted/conch/test/`` , and are named ``test_foo.py`` , where ``foo`` is the name of the module or package being tested. Extensive documentation on using the PyUnit framework for writing unit tests can be found in the :ref:`links section ` below. One deviation from the standard PyUnit documentation: To ensure that any variations in test results are due to variations in the code or environment and not the test process itself, Twisted ships with its own, compatible, testing framework. That just means that when you import the unittest module, you will ``from twisted.trial import unittest`` instead of the standard ``import unittest`` . As long as you have followed the module naming and placement conventions, ``trial`` will be smart enough to pick up any new tests you write. PyUnit provides a large number of assertion methods to be used when writing tests. Many of these are redundant. For consistency, Twisted unit tests should use the ``assert`` forms rather than the ``fail`` forms. Also, use ``assertEqual`` , ``assertNotEqual`` , and ``assertAlmostEqual`` rather than ``assertEquals`` , ``assertNotEquals`` , and ``assertAlmostEquals`` . ``assertTrue`` is also preferred over ``assert_`` . You may notice this convention is not followed everywhere in the Twisted codebase. If you are changing some test code and notice the wrong method being used in nearby code, feel free to adjust it. When you add a unit test, make sure all methods have docstrings specifying at a high level the intent of the test. That is, a description that users of the method would understand. Test Implementation Guidelines ------------------------------ Here are some guidelines to follow when writing tests for the Twisted test suite. Many tests predate these guidelines and so do not follow them. When in doubt, follow the guidelines given here, not the example of old unit tests. Naming Test Classes ~~~~~~~~~~~~~~~~~~~ When writing tests for the Twisted test suite, test classes are named ``FooTests``, where ``Foo`` is the name of the component being tested. Here is an example: .. code-block:: python class SSHClientTests(unittest.TestCase): def test_sshClient(self): foo() # the actual test Real I/O ~~~~~~~~ Most unit tests should avoid performing real, platform-implemented I/O operations. Real I/O is slow, unreliable, and unwieldy. When implementing a protocol, :py:class:`twisted.internet.testing.StringTransport` can be used instead of a real TCP transport. ``StringTransport`` is fast, deterministic, and can easily be used to exercise all possible network behaviors. If you need pair a client to a server and have them talk to each other, use ``twisted.test.iosim.connect`` with ``twisted.test.iosim.FakeTransport`` transports. Real Time ~~~~~~~~~ Most unit tests should also avoid waiting for real time to pass. Unit tests which construct and advance a :py:class:`twisted.internet.task.Clock` are fast and deterministic. When designing your code allow for the reactor to be injected during tests. .. code-block:: python from twisted.internet.task import Clock def test_timeBasedFeature(self): """ In this test a Clock scheduler is used. """ clock = Clock() yourThing = SomeThing() yourThing._reactor = clock state = yourThing.getState() clock.advance(10) # Get state after 10 seconds. state = yourThing.getState() Test Data ~~~~~~~~~ Keeping test data in the source tree should be avoided where possible. In some cases it can not be avoided, but where it's obvious how to do so, do it. Test data can be generated at run time or stored inside the test modules as constants. When file system access is required, dumping data into a temporary path during the test run opens up more testing opportunities. Inside the temporary path you can control various path properties or permissions. You should design your code so that data can be read from arbitrary input streams. Tests should be able to run even if they are run inside an installed copy of Twisted. .. code-block:: python publicRSA_openssh = ("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEArzJx8OYOnJmzf4tf" "vLi8DVPrJ3/c9k2I/Az64fxjHf9imyRJbixtQhlH9lfNjUIx+4LmrJH5QNRsFporcHDKOTwTT" "h5KmRpslkYHRivcJSkbh/C+BR3utDS555mV comment") def test_loadOpenSSHRSAPublic(self): """ L{keys.Key.fromStrea} can load RSA keys serialized in OpenSSH format. """ keys.Key.fromStream(StringIO(publicRSA_openssh)) The Global Reactor ~~~~~~~~~~~~~~~~~~ Since unit tests are avoiding real I/O and real time, they can usually avoid using a real reactor. The only exceptions to this are unit tests for a real reactor implementation. Unit tests for protocol implementations or other application code should not use a reactor. Unit tests for real reactor implementations should not use the global reactor, but should instead use ``twisted.internet.test.reactormixins.ReactorBuilder`` so they can be applied to all of the reactor implementations automatically. In no case should new unit tests use the global reactor. Skipping Tests -------------- Trial, the Twisted unit test framework, has some extensions which are designed to encourage developers to add new tests. One common situation is that a test exercises some optional functionality: maybe it depends upon certain external libraries being available, maybe it only works on certain operating systems. The important common factor is that nobody considers these limitations to be a bug. To make it easy to test as much as possible, some tests may be skipped in certain situations. Individual test cases can raise the ``SkipTest`` exception to indicate that they should be skipped, and the remainder of the test is not run. In the summary (the very last thing printed, at the bottom of the test output) the test is counted as a"skip" instead of a "success" or "fail" . This should be used inside a conditional which looks for the necessary prerequisites: .. code-block:: python class SSHClientTests(unittest.TestCase): def test_sshClient(self): if not ssh_path: raise unittest.SkipTest("cannot find ssh, nothing to test") foo() # do actual test after the SkipTest You can also set the ``.skip`` attribute on the method, with a string to indicate why the test is being skipped. This is convenient for temporarily turning off a test case, but it can also be set conditionally (by manipulating the class attributes after they've been defined): .. code-block:: python class SomeThingTests(unittest.TestCase): def test_thing(self): dotest() test_thing.skip = "disabled locally" .. code-block:: python class MyTests(unittest.TestCase): def test_one(self): ... def test_thing(self): dotest() if not haveThing: MyTests.test_thing.im_func.skip = "cannot test without Thing" # but test_one() will still run Finally, you can turn off an entire TestCase at once by setting the .skip attribute on the class. If you organize your tests by the functionality they depend upon, this is a convenient way to disable just the tests which cannot be run. .. code-block:: python class TCPTests(unittest.TestCase): ... class SSLTests(unittest.TestCase): if not haveSSL: skip = "cannot test without SSL support" # but TCPTests will still run ... Testing New Functionality ------------------------- Two good practices which arise from the "XP" development process are sometimes at odds with each other: - Unit tests are a good thing. Good developers recoil in horror when they see a failing unit test. They should drop everything until the test has been fixed. - Good developers write the unit tests first. Once tests are done, they write implementation code until the unit tests pass. Then they stop. These two goals will sometimes conflict. The unit tests that are written first, before any implementation has been done, are certain to fail. We want developers to commit their code frequently, for reliability and to improve coordination between multiple people working on the same problem together. While the code is being written, other developers (those not involved in the new feature) should not have to pay attention to failures in the new code. We should not dilute our well-indoctrinated Failing Test Horror Syndrome by crying wolf when an incomplete module has not yet started passing its unit tests. To do so would either teach the module author to put off writing or committing their unit tests until *after* all the functionality is working, or it would teach the other developers to ignore failing test cases. Both are bad things. ".todo" is intended to solve this problem. When a developer first starts writing the unit tests for functionality that has not yet been implemented, they can set the ``.todo`` attribute on the test methods that are expected to fail. These methods will still be run, but their failure will not be counted the same as normal failures: they will go into an "expected failures" category. Developers should learn to treat this category as a second-priority queue, behind actual test failures. As the developer implements the feature, the tests will eventually start passing. This is surprising: after all those tests are marked as being expected to fail. The .todo tests which nevertheless pass are put into a"unexpected success" category. The developer should remove the .todo tag from these tests. At that point, they become normal tests, and their failure is once again cause for immediate action by the entire development team. The life cycle of a test is thus: #. Test is created, marked ``.todo`` . Test fails: "expected failure" . #. Code is written, test starts to pass. "unexpected success" . #. ``.todo`` tag is removed. Test passes. "success" . #. Code is broken, test starts to fail. "failure" . Developers spring into action. #. Code is fixed, test passes once more. "success" . ``.todo`` may be of use while you are developing a feature, but by the time you are ready to commit anything all the tests you have written should be passing. In other words **never** commit to trunk tests marked as ``.todo``. For unfinished tests you should create a follow up ticket and add the tests to the ticket's description. You can also ignore the ``.todo`` marker and just make sure you write test first to see them failing before starting to work on the fix. Line Coverage Information ------------------------- Trial provides line coverage information, which is very useful to ensure old code has decent coverage. Passing the ``--coverage`` option to Trial will generate the coverage information in a file called ``coverage`` which can be found in the ``_trial_temp`` folder. Associating Test Cases With Source Files ---------------------------------------- Please add a ``test-case-name`` tag to the source file that is covered by your new test. This is a comment at the beginning of the file which looks like one of the following: .. code-block:: python # -*- test-case-name: twisted.test.test_defer -*- or .. code-block:: python #!/usr/bin/env python # -*- test-case-name: twisted.test.test_defer -*- This format is understood by emacs to mark "File Variables" . The intention is to accept ``test-case-name`` anywhere emacs would on the first or second line of the file (but not in the ``File Variables:`` block that emacs accepts at the end of the file). If you need to define other emacs file variables, you can either put them in the ``File Variables:`` block or use a semicolon-separated list of variable definitions: .. code-block:: python # -*- test-case-name: twisted.test.test_defer; fill-column: 75; -*- If the code is exercised by multiple test cases, those may be marked by using a comma-separated list of tests, as follows: (NOTE: not all tools can handle this yet.. ``trial --testmodule`` does, though) .. code-block:: python # -*- test-case-name: twisted.test.test_defer,twisted.test.test_tcp -*- The ``test-case-name`` tag will allow ``trial --testmodule twisted/dir/myfile.py`` to determine which test cases need to be run to exercise the code in ``myfile.py`` . Several tools (as well as https://launchpad.net/twisted-emacs's ``twisted-dev.el`` 's F9 command) use this to automatically run the right tests. Links ----- .. _core-development-policy-test-standard-links: - A chapter on `Unit Testing `_ in Mark Pilgrim's `Dive Into Python `_ . - :mod:`unittest` module documentation, in the `Python Library Reference `_ . - `UnitTest `__ on the `PortlandPatternRepository Wiki `_ , where all the cool `ExtremeProgramming `_ kids hang out. - `Unit Tests `_ in `Extreme Programming: A Gentle Introduction `_ . - Ron Jeffries expounds on the importance of `Unit Tests at 100% `_ . - Ron Jeffries writes about the `Unit Test `_ in the `Extreme Programming practices of C3 `_ . - `PyUnit's homepage `_ . - The top-level tests directory, `twisted/test `_. See also :doc:`Tips for writing tests for Twisted code <../../howto/testing>` . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/policy/writing-standard.rst0000644000175100001710000003216100000000000024276 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Twisted Writing Standard ======================== The Twisted writing standard describes the documentation writing styles we prefer in our narrative documentation. This standard applies particularly to howtos and other descriptive documentation. For writing API documentation, please refer to :doc:`Docstrings section in our coding standard `. This document is meant to help Twisted documentation authors produce documentation that does not have the following problems: - misleads users about what is good Twisted style; - misleads users into thinking that an advanced howto is an introduction to writing their first Twisted server; and - misleads users about whether they fit the document's target audience: for example, that they are able to use enterprise without knowing how to write SQL queries. General style ------------- Documents should aim to be clear and concise, allowing the API documentation and the example code to tell as much of the story as they can. Demonstrations and where necessary supported arguments should always preferred to simple statements ("here is how you would simplify this code with Deferreds" rather than "Deferreds make code simpler"). Documents should be clearly delineated into sections and subsections. Each of these sections, like the overall document, should have a single clear purpose. This is most easily tested by trying to have meaningful headings: a section which is headed by "More details" or "Advanced stuff" is not purposeful enough. There should be fairly obvious ways to split a document. The two most common are task based sectioning and sectioning which follows module and class separations. Documentation must use American English spelling, and where possible avoid any local variants of either vocabulary or grammar. Grammatically complex sentences should ideally be avoided: these make reading unnecessarily difficult, particularly for non-native speakers. When referring to a hypothetical person, (such as "a user of a website written with twisted.web"), gender neutral pronouns (they/their/them) should be used. For reStructuredText documents which are handled by the Sphinx documentation generator make lines short, and break lines at natural places, such as after commas and semicolons, rather than after the 79th column. We call this *semantic newlines*. This rule **does not** apply to docstrings. .. code-block:: text :linenos: Sometimes when editing a narrative documentation file, I wrap the lines semantically. Instead of inserting a newline at 79 columns (or whatever), or making paragraphs one long line, I put in newlines at a point that seems logical to me. Modern code-oriented text editors are very good at wrapping and arranging long lines. Evangelism and usage documents ------------------------------ The Twisted documentation should maintain a reasonable distinction between "evangelism" documentation, which compares the Twisted design or Twisted best practice with other approaches and argues for the Twisted approach, and "usage" documentation, which describes the Twisted approach in detail without comparison to other possible approaches. While both kinds of documentation are useful, they have different audiences. The first kind of document, evangelical documents, is useful to a reader who is researching and comparing approaches and seeking to understand the Twisted approach or Twisted functionality in order to decide whether it is useful to them. The second kind of document, usage documents, are useful to a reader who has decided to use Twisted and simply wants further information about available functions and architectures they can use to accomplish their goal. Since they have distinct audiences, evangelism and detailed usage documentation belongs in separate files. There should be links between them in 'Further reading' or similar sections. Descriptions of features ------------------------ Descriptions of any feature added since release 2.0 of Twisted core must have a note describing which release of which Twisted project they were added in at the first mention in each document. If they are not yet released, give them the number of the next minor release. For example, a substantial change might have a version number added in the introduction: This document describes the Application infrastructure for deploying Twisted applications *(added in Twisted 1.3)* . The version does not need to be mentioned elsewhere in the document except for specific features which were added in subsequent releases, which might should be mentioned separately. The simplest way to create a ``.tac`` file, SuperTac *(added in Twisted Core 99.7)* ... In the case where the usage of a feature has substantially changed, the number should be that of the release in which the current usage became available. For example: This document describes the Application infrastructure for deploying Twisted applications *(updated[/substantially updated] in Twisted 2.7)* . Linking ------- The first occurrence of the name of any module, class or function should always link to the API documents. Subsequent mentions may or may not link at the author's discretion: discussions which are very closely bound to a particular API should probably link in the first mention in the given section. Links between howtos are encouraged. Overview documents and tutorials should always link to reference documents and in depth documents. These documents should link among themselves wherever it's needed: if you're tempted to re-describe the functionality of another module, you should certainly link instead. Linking to standard library documentation is also encouraged when referencing standard library objects. `Intersphinx `_ is supported in Twisted documentation, with prefixes for linking to either the Python 2 standard library documentation (via ``py2``) or Python 3 (via ``py3``) as needed. Introductions ------------- The introductory section of a Twisted howto should immediately follow the top-level heading and precede any subheadings. The following items should be present in the introduction to Twisted howtos: the introductory paragraph and the description of the target audience. Introductory paragraph ~~~~~~~~~~~~~~~~~~~~~~ The introductory paragraph of a document should summarize what the document is designed to present. It should use the both proper names for the Twisted technologies and simple non-Twisted descriptions of the technologies. For example, in this paragraph both the name of the technology ("Conch") and a description ("SSH server") are used: This document describes setting up a SSH server to serve data from the file system using Conch, the Twisted SSH implementation. The introductory paragraph should be relatively short, but should, like the above, somewhere define the document's objective: what the reader should be able to do using instructions in the document. Description of target audience ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Subsequent paragraphs in the introduction should describe the target audience of the document: who would want to read it, and what they should know before they can expect to use your document. For example: The target audience of this document is a Twisted user who has a set of filesystem like data objects that they would like to make available to authenticated users over SFTP. Following the directions in this document will require that you are familiar with managing authentication via the Twisted Cred system. Use your discretion about the extent to which you list assumed knowledge. Very introductory documents that are going to be among a reader's first exposure to Twisted will even need to specify that they rely on knowledge of Python and of certain networking concepts (ports, servers, clients, connections) but documents that are going to be sought out by existing Twisted users for particular purposes only need to specify other Twisted knowledge that is assumed. Any knowledge of technologies that wouldn't be considered "core Python" and/or "simple networking" need to be explicitly specified, no matter how obvious they seem to someone familiar with the technology. For example, it needs to be stated that someone using enterprise should know SQL and should know how to set up and populate databases for testing purposes. Where possible, link to other documents that will fill in missing knowledge for the reader. Linking to documents in the Twisted repository is preferred but not essential. Goals of document ~~~~~~~~~~~~~~~~~ The introduction should finish with a list of tasks that the user can expect to see the document accomplish. These tasks should be concrete rather than abstract, so rather than telling the user that they will "understand Twisted Conch", you would list the specific tasks that they will see the document do. For example: This document will demonstrate the following tasks using Twisted Conch: - creating an anonymous access read-only SFTP server using a filesystem backend; - creating an anonymous access read-only SFTP server using a proxy backend connecting to an HTTP server; and - creating an anonymous access read and write SFTP server using a filesystem backend. In many cases this will essentially be a list of your code examples, but it need not be. If large sections of your code are devoted to design discussions, your goals might resemble the following: This document will discuss the following design aspects of writing Conch servers: - authentication of users; and - choice of data backends. Example code ------------ Wherever possible, example code should be provided to illustrate a certain technique or piece of functionality. Example code should try and meet as many of the following requirements as possible: - example code should be a complete working example suitable for copying and pasting and running by the reader (where possible, provide a link to a file to download); - example code should be short; - example code should be commented very extensively, with the assumption that this code may be read by a Twisted newcomer; - example code should conform to the :doc:`coding standard ` ; and - example code should exhibit 'best practice', not only for dealing with the target functionality, but also for use of the application framework and so on. The requirement to have a complete working example will occasionally impose upon authors the need to have a few dummy functions: in Twisted documentation the most common example is where a function is needed to generate a Deferred and fire it after some time has passed. An example might be this, where :py:func:`deferLater ` is used to fire a callback after a period of time: .. code-block:: python from twisted.internet import task, reactor def getDummyDeferred(): """ Dummy method which returns a deferred that will fire in 5 seconds with a result """ return task.deferLater(reactor, 5, lambda x: "RESULT") As in the above example, it is imperative to clearly mark that the function is a dummy in as many ways as you can: using ``Dummy`` in the function name, explaining that it is a dummy in the docstring, and marking particular lines as being required to create an effect for the purposes of demonstration. In most cases, this will save the reader from mistaking this dummy method for an idiom they should use in their Twisted code. Conclusions ----------- The conclusion of a howto should follow the very last section heading in a file. This heading would usually be called "Conclusion". The conclusion of a howto should remind the reader of the tasks that they have done while reading the document. For example: In this document, you have seen how to: #. set up an anonymous read-only SFTP server; #. set up a SFTP server where users authenticate; #. set up a SFTP server where users are restricted to some parts of the filesystem based on authentication; and #. set up a SFTP server where users have write access to some parts of the filesystem based on authentication. If appropriate, the howto could follow this description with links to other documents that might be of interest to the reader with their newfound knowledge. However, these links should be limited to fairly obvious extensions of at least one of the listed tasks. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/development/security.rst0000644000175100001710000000211000000000000021354 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Security ======== We need to do a full audit of Twisted, module by module. This document list the sort of things you want to look for when doing this, or when writing your own code. Bad input --------- Any place we receive untrusted data, we need to be careful. In some cases we are not careful enough. For example, in HTTP there are many places where strings need to be converted to ints, so we use ``int()`` . The problem is that this well accept negative numbers as well, whereas the protocol should only be accepting positive numbers. Resource Exhaustion and DoS --------------------------- Make sure we never allow users to create arbitrarily large strings or files. Some of the protocols still have issues like this. Place a limit which allows reasonable use but will cut off huge requests, and allow changing of this limit. Another operation to look out for are exceptions. They can fill up logs and take a lot of CPU time to render in web pages. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4779444 Twisted-22.1.0/docs/core/examples/0000755000175100001710000000000000000000000016255 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ampclient.py0000644000175100001710000000217000000000000020603 0ustar00runnerdockerfrom ampserver import Divide, Sum from twisted.internet import defer, reactor from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol from twisted.protocols.amp import AMP def doMath(): destination = TCP4ClientEndpoint(reactor, "127.0.0.1", 1234) sumDeferred = connectProtocol(destination, AMP()) def connected(ampProto): return ampProto.callRemote(Sum, a=13, b=81) sumDeferred.addCallback(connected) def summed(result): return result["total"] sumDeferred.addCallback(summed) divideDeferred = connectProtocol(destination, AMP()) def connected(ampProto): return ampProto.callRemote(Divide, numerator=1234, denominator=0) divideDeferred.addCallback(connected) def trapZero(result): result.trap(ZeroDivisionError) print("Divided by zero: returning INF") return 1e1000 divideDeferred.addErrback(trapZero) def done(result): print("Done with math:", result) reactor.stop() defer.DeferredList([sumDeferred, divideDeferred]).addCallback(done) if __name__ == "__main__": doMath() reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ampserver.py0000644000175100001710000000200400000000000020627 0ustar00runnerdockerfrom twisted.protocols import amp class Sum(amp.Command): arguments = [(b"a", amp.Integer()), (b"b", amp.Integer())] response = [(b"total", amp.Integer())] class Divide(amp.Command): arguments = [(b"numerator", amp.Integer()), (b"denominator", amp.Integer())] response = [(b"result", amp.Float())] errors = {ZeroDivisionError: b"ZERO_DIVISION"} class Math(amp.AMP): def sum(self, a, b): total = a + b print(f"Did a sum: {a} + {b} = {total}") return {"total": total} Sum.responder(sum) def divide(self, numerator, denominator): result = float(numerator) / denominator print(f"Divided: {numerator} / {denominator} = {result}") return {"result": result} Divide.responder(divide) def main(): from twisted.internet import reactor from twisted.internet.protocol import Factory pf = Factory() pf.protocol = Math reactor.listenTCP(1234, pf) print("started") reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/bananabench.py0000644000175100001710000000445700000000000021061 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import time from io import BytesIO from twisted.internet import protocol # Twisted Imports from twisted.spread import banana iterationCount = 10000 class BananaBench: r = range(iterationCount) def setUp(self, encClass): self.io = BytesIO() self.enc = encClass() self.enc.makeConnection(protocol.FileWrapper(self.io)) self.enc._selectDialect("none") self.enc.expressionReceived = self.putResult def putResult(self, result): self.result = result def tearDown(self): self.enc.connectionLost() del self.enc def testEncode(self, value): starttime = time.time() for i in self.r: self.enc.sendEncoded(value) self.io.truncate(0) endtime = time.time() print(f" Encode took {endtime - starttime} seconds") return endtime - starttime def testDecode(self, value): self.enc.sendEncoded(value) encoded = self.io.getvalue() starttime = time.time() for i in self.r: self.enc.dataReceived(encoded) endtime = time.time() print(f" Decode took {endtime - starttime} seconds") return endtime - starttime def performTest(self, method, data, encClass): self.setUp(encClass) method(data) self.tearDown() def runTests(self, testData): print(f"Test data is: {testData}") print(" Using Pure Python Banana:") self.performTest(self.testEncode, testData, banana.Banana) self.performTest(self.testDecode, testData, banana.Banana) bench = BananaBench() print(f"Doing {iterationCount} iterations of each test.") print("") testData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] bench.runTests(testData) testData = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] bench.runTests(testData) testData = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]] bench.runTests(testData) testData = [ b"one", b"two", b"three", b"four", b"five", b"six", b"seven", b"eight", b"nine", b"ten", ] bench.runTests(testData) testData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] bench.runTests(testData) testData = [1, 2, [3, 4], [30.5, 40.2], 5, [b"six", b"seven", [b"eight", 9]], [10], []] bench.runTests(testData) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/chatserver.py0000644000175100001710000000165300000000000021002 0ustar00runnerdocker"""The most basic chat protocol possible. run me with twistd -y chatserver.py, and then connect with multiple telnet clients to port 1025 """ from twisted.protocols import basic class MyChat(basic.LineReceiver): def connectionMade(self): print("Got new client!") self.factory.clients.append(self) def connectionLost(self, reason): print("Lost a client!") self.factory.clients.remove(self) def lineReceived(self, line): print("received", repr(line)) for c in self.factory.clients: c.message(line) def message(self, message): self.transport.write(message + b"\n") from twisted.application import internet, service from twisted.internet import protocol factory = protocol.ServerFactory() factory.protocol = MyChat factory.clients = [] application = service.Application("chatserver") internet.TCPServer(1025, factory).setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/courier.py0000644000175100001710000000566000000000000020306 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Example of an interface to Courier's mail filter. """ LOGFILE = "/tmp/filter.log" # Setup log file from twisted.python import log log.startLogging(open(LOGFILE, "a")) import sys sys.stderr = log.logfile # Twisted imports from twisted.internet import reactor, stdio from twisted.internet.protocol import Factory, Protocol from twisted.protocols import basic FILTERS = "/var/lib/courier/filters" ALLFILTERS = "/var/lib/courier/allfilters" FILTERNAME = "twistedfilter" import email.message import email.parser import os import os.path from syslog import LOG_MAIL, openlog, syslog def trace_dump(): t, v, tb = sys.exc_info() openlog(FILTERNAME, 0, LOG_MAIL) syslog(f"Unhandled exception: {v} - {t}") while tb: syslog( "Trace: {}:{} {}".format( tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_name, tb.tb_lineno ) ) tb = tb.tb_next # just to be safe del tb def safe_del(file): try: if os.path.isdir(file): os.removedirs(file) else: os.remove(file) except OSError: pass class DieWhenLost(Protocol): def connectionLost(self, reason=None): reactor.stop() class MailProcessor(basic.LineReceiver): """ I process a mail message. Override filterMessage to do any filtering you want. """ messageFilename = None delimiter = "\n" def connectionMade(self): log.msg(f"Connection from {self.transport}") self.state = "connected" self.metaInfo = [] def lineReceived(self, line): if self.state == "connected": self.messageFilename = line self.state = "gotMessageFilename" if self.state == "gotMessageFilename": if line: self.metaInfo.append(line) else: if not self.metaInfo: self.transport.loseConnection() return self.filterMessage() def filterMessage(self): """Override this. A trivial example is included. """ try: emailParser = email.parser.Parser() with open(self.messageFilename) as f: emailParser.parse(f) self.sendLine(b"200 Ok") except BaseException: trace_dump() self.sendLine(b"435 " + FILTERNAME.encode("ascii") + b" processing error") def main(): # Listen on the UNIX socket f = Factory() f.protocol = MailProcessor safe_del(f"{ALLFILTERS}/{FILTERNAME}") reactor.listenUNIX(f"{ALLFILTERS}/{FILTERNAME}", f, 10) # Once started, close fd 3 to let Courier know we're ready reactor.callLater(0, os.close, 3) # When stdin is closed, it's time to exit. stdio.StandardIO(DieWhenLost()) # Go! reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/cred.py0000644000175100001710000001075400000000000017553 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import sys from zope.interface import Interface, implementer from twisted.cred import checkers, credentials, error, portal from twisted.internet import protocol from twisted.protocols import basic from twisted.python import log class IProtocolUser(Interface): def getPrivileges(): """Return a list of privileges this user has.""" def logout(): """Cleanup per-login resources allocated to this avatar""" @implementer(IProtocolUser) class AnonymousUser: def getPrivileges(self): return [1, 2, 3] def logout(self): print("Cleaning up anonymous user resources") @implementer(IProtocolUser) class RegularUser: def getPrivileges(self): return [1, 2, 3, 5, 6] def logout(self): print("Cleaning up regular user resources") @implementer(IProtocolUser) class Administrator: def getPrivileges(self): return range(50) def logout(self): print("Cleaning up administrator resources") class Protocol(basic.LineReceiver): user = None portal = None avatar = None logout = None def connectionMade(self): self.sendLine(b"Login with USER followed by PASS or ANON") self.sendLine(b"Check privileges with PRIVS") def connectionLost(self, reason): if self.logout: self.logout() self.avatar = None self.logout = None def lineReceived(self, line): f = getattr(self, "cmd_" + line.decode("ascii").upper().split()[0]) if f: try: f(*line.split()[1:]) except TypeError: self.sendLine(b"Wrong number of arguments.") except BaseException: self.sendLine(b"Server error (probably your fault)") def cmd_ANON(self): if self.portal: self.portal.login( credentials.Anonymous(), None, IProtocolUser ).addCallbacks(self._cbLogin, self._ebLogin) else: self.sendLine(b"DENIED") def cmd_USER(self, name): self.user = name self.sendLine(b"Alright. Now PASS?") def cmd_PASS(self, password): if not self.user: self.sendLine(b"USER required before PASS") else: if self.portal: self.portal.login( credentials.UsernamePassword(self.user, password), None, IProtocolUser, ).addCallbacks(self._cbLogin, self._ebLogin) else: self.sendLine(b"DENIED") def cmd_PRIVS(self): self.sendLine(b"You have the following privileges: ") self.sendLine( b" ".join( [str(priv).encode("ascii") for priv in self.avatar.getPrivileges()] ) ) def _cbLogin(self, result): (interface, avatar, logout) = result assert interface is IProtocolUser self.avatar = avatar self.logout = logout self.sendLine(b"Login successful. Available commands: PRIVS") def _ebLogin(self, failure): failure.trap(error.UnauthorizedLogin) self.sendLine(b"Login denied! Go away.") class ServerFactory(protocol.ServerFactory): protocol = Protocol def __init__(self, portal): self.portal = portal def buildProtocol(self, addr): p = protocol.ServerFactory.buildProtocol(self, addr) p.portal = self.portal return p @implementer(portal.IRealm) class Realm: def requestAvatar(self, avatarId, mind, *interfaces): if IProtocolUser in interfaces: if avatarId == checkers.ANONYMOUS: av = AnonymousUser() elif avatarId.isupper(): # Capitalized usernames are administrators. av = Administrator() else: av = RegularUser() return IProtocolUser, av, av.logout raise NotImplementedError( "Only IProtocolUser interface is supported by this realm" ) def main(): r = Realm() p = portal.Portal(r) c = checkers.InMemoryUsernamePasswordDatabaseDontUse() c.addUser(b"auser", b"thepass") c.addUser(b"SECONDUSER", b"secret") p.registerChecker(c) p.registerChecker(checkers.AllowAnonymousAccess()) f = ServerFactory(p) log.startLogging(sys.stdout) from twisted.internet import reactor reactor.listenTCP(4738, f) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/dbcred.py0000755000175100001710000001524500000000000020064 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Simple example of a db checker: define a L{ICredentialsChecker} implementation that deals with a database backend to authenticate a user. """ from zope.interface import implementer from twisted.cred import error from twisted.cred.checkers import ICredentialsChecker from twisted.cred.credentials import IUsernameHashedPassword, IUsernamePassword from twisted.internet.defer import Deferred @implementer(ICredentialsChecker) class DBCredentialsChecker: """ This class checks the credentials of incoming connections against a user table in a database. """ def __init__( self, runQuery, query="SELECT username, password FROM user WHERE username = %s", customCheckFunc=None, caseSensitivePasswords=True, ): """ @param runQuery: This will be called to get the info from the db. Generally you'd want to create a L{twisted.enterprice.adbapi.ConnectionPool} and pass it's runQuery method here. Otherwise pass a function with the same prototype. @type runQuery: C{callable} @type query: query used to authenticate user. @param query: C{str} @param customCheckFunc: Use this if the passwords in the db are stored as hashes. We'll just call this, so you can do the checking yourself. It takes the following params: (username, suppliedPass, dbPass) and must return a boolean. @type customCheckFunc: C{callable} @param caseSensitivePasswords: If true requires that every letter in C{credentials.password} is exactly the same case as the it's counterpart letter in the database. This is only relevant if C{customCheckFunc} is not used. @type caseSensitivePasswords: C{bool} """ self.runQuery = runQuery self.caseSensitivePasswords = caseSensitivePasswords self.customCheckFunc = customCheckFunc # We can't support hashed password credentials if we only have a hash # in the DB if customCheckFunc: self.credentialInterfaces = (IUsernamePassword,) else: self.credentialInterfaces = ( IUsernamePassword, IUsernameHashedPassword, ) self.sql = query def requestAvatarId(self, credentials): """ Authenticates the kiosk against the database. """ # Check that the credentials instance implements at least one of our # interfaces for interface in self.credentialInterfaces: if interface.providedBy(credentials): break else: raise error.UnhandledCredentials() # Ask the database for the username and password dbDeferred = self.runQuery(self.sql, (credentials.username,)) # Setup our deferred result deferred = Deferred() dbDeferred.addCallbacks( self._cbAuthenticate, self._ebAuthenticate, callbackArgs=(credentials, deferred), errbackArgs=(credentials, deferred), ) return deferred def _cbAuthenticate(self, result, credentials, deferred): """ Checks to see if authentication was good. Called once the info has been retrieved from the DB. """ if len(result) == 0: # Username not found in db deferred.errback(error.UnauthorizedLogin("Username unknown")) else: username, password = result[0] if self.customCheckFunc: # Let the owner do the checking if self.customCheckFunc(username, credentials.password, password): deferred.callback(credentials.username) else: deferred.errback(error.UnauthorizedLogin("Password mismatch")) else: # It's up to us or the credentials object to do the checking # now if IUsernameHashedPassword.providedBy(credentials): # Let the hashed password checker do the checking if credentials.checkPassword(password): deferred.callback(credentials.username) else: deferred.errback(error.UnauthorizedLogin("Password mismatch")) elif IUsernamePassword.providedBy(credentials): # Compare the passwords, deciging whether or not to use # case sensitivity if self.caseSensitivePasswords: passOk = password.lower() == credentials.password.lower() else: passOk = password == credentials.password # See if they match if passOk: deferred.callback(credentials.username) else: deferred.errback(error.UnauthorizedLogin("Password mismatch")) else: # OK, we don't know how to check this deferred.errback(error.UnhandledCredentials()) def _ebAuthenticate(self, message, credentials, deferred): """ The database lookup failed for some reason. """ deferred.errback(error.LoginFailed(message)) def main(): """ Run a simple echo pb server to test the checker. It defines a custom query for dealing with sqlite special quoting, but otherwise it's a straightforward use of the object. You can test it running C{pbechoclient.py}. """ import sys from twisted.python import log log.startLogging(sys.stdout) import os if os.path.isfile("testcred"): os.remove("testcred") from twisted.enterprise import adbapi pool = adbapi.ConnectionPool("pysqlite2.dbapi2", "testcred") # Create the table that will be used query1 = """CREATE TABLE user ( username string, password string )""" # Insert a test user query2 = """INSERT INTO user VALUES ('guest', 'guest')""" def cb(res): pool.runQuery(query2) pool.runQuery(query1).addCallback(cb) checker = DBCredentialsChecker( pool.runQuery, query="SELECT username, password FROM user WHERE username = ?" ) import pbecho from twisted.cred.portal import Portal from twisted.spread import pb portal = Portal(pbecho.SimpleRealm()) portal.registerChecker(checker) reactor.listenTCP(pb.portno, pb.PBServerFactory(portal)) if __name__ == "__main__": from twisted.internet import reactor reactor.callWhenRunning(main) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoclient.py0000644000175100001710000000231600000000000020746 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import task from twisted.internet.defer import Deferred from twisted.internet.protocol import ClientFactory from twisted.protocols.basic import LineReceiver class EchoClient(LineReceiver): end = b"Bye-bye!" def connectionMade(self): self.sendLine(b"Hello, world!") self.sendLine(b"What a fine day it is.") self.sendLine(self.end) def lineReceived(self, line): print("receive:", line) if line == self.end: self.transport.loseConnection() class EchoClientFactory(ClientFactory): protocol = EchoClient def __init__(self): self.done = Deferred() def clientConnectionFailed(self, connector, reason): print("connection failed:", reason.getErrorMessage()) self.done.errback(reason) def clientConnectionLost(self, connector, reason): print("connection lost:", reason.getErrorMessage()) self.done.callback(None) def main(reactor): factory = EchoClientFactory() reactor.connectTCP("localhost", 8000, factory) return factory.done if __name__ == "__main__": task.react(main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoclient_ssl.py0000755000175100001710000000153600000000000021635 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import echoclient from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.python.modules import getModule @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(echoclient.EchoClient) certData = getModule(__name__).filePath.sibling("public.pem").getContent() authority = ssl.Certificate.loadPEM(certData) options = ssl.optionsForClientTLS("example.com", authority) endpoint = endpoints.SSL4ClientEndpoint(reactor, "localhost", 8000, options) echoClient = yield endpoint.connect(factory) done = defer.Deferred() echoClient.connectionLost = lambda reason: done.callback(None) yield done if __name__ == "__main__": import echoclient_ssl task.react(echoclient_ssl.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoclient_udp.py0000644000175100001710000000157200000000000021621 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import reactor from twisted.internet.protocol import DatagramProtocol class EchoClientDatagramProtocol(DatagramProtocol): strings = [b"Hello, world!", b"What a fine day it is.", b"Bye-bye!"] def startProtocol(self): self.transport.connect("127.0.0.1", 8000) self.sendDatagram() def sendDatagram(self): if len(self.strings): datagram = self.strings.pop(0) self.transport.write(datagram) else: reactor.stop() def datagramReceived(self, datagram, host): print("Datagram received: ", repr(datagram)) self.sendDatagram() def main(): protocol = EchoClientDatagramProtocol() reactor.listenUDP(0, protocol) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoserv.py0000644000175100001710000000111300000000000020441 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import reactor from twisted.internet.protocol import Factory, Protocol ### Protocol Implementation # This is just about the simplest possible protocol class Echo(Protocol): def dataReceived(self, data): """ As soon as any data is received, write it back. """ self.transport.write(data) def main(): f = Factory() f.protocol = Echo reactor.listenTCP(8000, f) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoserv_ssl.py0000644000175100001710000000125200000000000021326 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import sys import echoserv from twisted.internet import defer, protocol, ssl, task from twisted.python import log from twisted.python.modules import getModule def main(reactor): log.startLogging(sys.stdout) certData = getModule(__name__).filePath.sibling("server.pem").getContent() certificate = ssl.PrivateCertificate.loadPEM(certData) factory = protocol.Factory.forProtocol(echoserv.Echo) reactor.listenSSL(8000, factory, certificate.options()) return defer.Deferred() if __name__ == "__main__": import echoserv_ssl task.react(echoserv_ssl.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/echoserv_udp.py0000644000175100001710000000075300000000000021322 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import reactor from twisted.internet.protocol import DatagramProtocol # Here's a UDP version of the simplest possible protocol class EchoUDP(DatagramProtocol): def datagramReceived(self, datagram, address): self.transport.write(datagram, address) def main(): reactor.listenUDP(8000, EchoUDP()) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/filewatch.py0000644000175100001710000000056700000000000020605 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. # from twisted.application import internet def watch(fp): fp.seek(fp.tell()) for line in fp.readlines(): sys.stdout.write(line) import sys from twisted.internet import reactor s = internet.TimerService(0.1, watch, open(sys.argv[1])) s.startService() reactor.run() s.stopService() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ftpclient.py0000644000175100001710000000573500000000000020631 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ An example of using the FTP client """ # Standard library imports from io import BytesIO from twisted.internet import reactor from twisted.internet.protocol import ClientCreator, Protocol # Twisted imports from twisted.protocols.ftp import FTPClient, FTPFileListProtocol from twisted.python import usage class BufferingProtocol(Protocol): """Simple utility class that holds all data written to it in a buffer.""" def __init__(self): self.buffer = BytesIO() def dataReceived(self, data): self.buffer.write(data) # Define some callbacks def success(response): print("Success! Got response:") print("---") if response is None: print(None) else: print("\n".join(response)) print("---") def fail(error): print("Failed. Error was:") print(error) def showFiles(result, fileListProtocol): print("Processed file listing:") for file in fileListProtocol.files: print( " {}: {} bytes, {}".format(file["filename"], file["size"], file["date"]) ) print(f"Total: {len(fileListProtocol.files)} files") def showBuffer(result, bufferProtocol): print("Got data:") print(bufferProtocol.buffer.getvalue()) class Options(usage.Options): optParameters = [ ["host", "h", "localhost"], ["port", "p", 21], ["username", "u", "anonymous"], ["password", None, "twisted@"], ["passive", None, 0], ["debug", "d", 1], ] def run(): # Get config config = Options() config.parseOptions() config.opts["port"] = int(config.opts["port"]) config.opts["passive"] = int(config.opts["passive"]) config.opts["debug"] = int(config.opts["debug"]) # Create the client FTPClient.debug = config.opts["debug"] creator = ClientCreator( reactor, FTPClient, config.opts["username"], config.opts["password"], passive=config.opts["passive"], ) creator.connectTCP(config.opts["host"], config.opts["port"]).addCallback( connectionMade ).addErrback(connectionFailed) reactor.run() def connectionFailed(f): print("Connection Failed:", f) reactor.stop() def connectionMade(ftpClient): # Get the current working directory ftpClient.pwd().addCallbacks(success, fail) # Get a detailed listing of the current directory fileList = FTPFileListProtocol() d = ftpClient.list(".", fileList) d.addCallbacks(showFiles, fail, callbackArgs=(fileList,)) # Change to the parent directory ftpClient.cdup().addCallbacks(success, fail) # Create a buffer proto = BufferingProtocol() # Get short listing of current directory, and quit when done d = ftpClient.nlst(".", proto) d.addCallbacks(showBuffer, fail, callbackArgs=(proto,)) d.addCallback(lambda result: reactor.stop()) # this only runs if the module was *not* imported if __name__ == "__main__": run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ftpserver.py0000644000175100001710000000377100000000000020657 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ An example FTP server with minimal user authentication. """ from twisted.cred.checkers import AllowAnonymousAccess, FilePasswordDB from twisted.cred.portal import Portal from twisted.internet import reactor from twisted.protocols.ftp import FTPFactory, FTPRealm # # First, set up a portal (twisted.cred.portal.Portal). This will be used # to authenticate user logins, including anonymous logins. # # Part of this will be to establish the "realm" of the server - the most # important task in this case is to establish where anonymous users will # have default access to. In a real world scenario this would typically # point to something like '/pub' but for this example it is pointed at the # current working directory. # # The other important part of the portal setup is to point it to a list of # credential checkers. In this case, the first of these is used to grant # access to anonymous users and is relatively simple; the second is a very # primitive password checker. This example uses a plain text password file # that has one username:password pair per line. This checker *does* provide # a hashing interface, and one would normally want to use it instead of # plain text storage for anything remotely resembling a 'live' network. In # this case, the file "pass.dat" is used, and stored in the same directory # as the server. BAD. # # Create a pass.dat file which looks like this: # # ===================== # jeff:bozo # grimmtooth:bozo2 # ===================== # p = Portal(FTPRealm("./"), [AllowAnonymousAccess(), FilePasswordDB("pass.dat")]) # # Once the portal is set up, start up the FTPFactory and pass the portal to # it on startup. FTPFactory will start up a twisted.protocols.ftp.FTP() # handler for each incoming OPEN request. Business as usual in Twisted land. # f = FTPFactory(p) # # You know this part. Point the reactor to port 21 coupled with the above factory, # and start the event loop. # reactor.listenTCP(21, f) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/index.rst0000644000175100001710000000732700000000000020127 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Examples ======== Simple Echo server and client ----------------------------- - :download:`simpleclient.py` - simple TCP client - :download:`simpleserv.py` - simple TCP echo server Chat ---- - :download:`chatserver.py` - shows how to communicate between clients Echo server & client variants ----------------------------- - :download:`echoserv.py` - variant on a simple TCP echo server - :download:`echoclient.py` - variant on a simple TCP client - :download:`echoserv_udp.py` - simplest possible UDP server - :download:`echoclient_udp.py` - simple UDP client - :download:`echoserv_ssl.py` - simple SSL server - :download:`echoclient_ssl.py` - simple SSL client AMP server & client variants ---------------------------- - :download:`ampserver.py` - do math using AMP - :download:`ampclient.py` - do math using AMP Perspective Broker ------------------ - :download:`pbsimple.py` - simplest possible PB server - :download:`pbsimpleclient.py` - simplest possible PB client - :download:`pbbenchclient.py` - benchmarking client - :download:`pbbenchserver.py` - benchmarking server - :download:`pbecho.py` - echo server that uses login - :download:`pbechoclient.py` - echo client using login - :download:`pb_exceptions.py` - example of exceptions over PB - :download:`pbgtk2.py` - example of using GTK2 with PB - :download:`pbinterop.py` - shows off various types supported by PB - :download:`bananabench.py` - benchmark for banana Cred ---- - :download:`cred.py` - Authenticate a user with an in-memory username/password database - :download:`dbcred.py` - Using a database backend to authenticate a user GUI --- - :download:`wxdemo.py` - demo of wxPython integration with Twisted - :download:`pbgtk2.py` - example of using GTK2 with PB - :download:`pyuidemo.py` - PyUI FTP examples ------------ - :download:`ftpclient.py` - example of using the FTP client - :download:`ftpserver.py` - create an FTP server which serves files for anonymous users from the working directory and serves files for authenticated users from ``/home``. Logging ------- - :download:`twistd-logging.tac` - logging example using ILogObserver - :download:`testlogging.py` - use twisted.python.log to log errors to standard out - :download:`rotatinglog.py` - example of log file rotation POSIX Specific Tricks --------------------- - :download:`sendfd.py`, :download:`recvfd.py` - send and receive file descriptors over UNIX domain sockets Miscellaneous ------------- - :download:`shaper.py` - example of rate-limiting your web server - :download:`stdiodemo.py` - example using stdio, Deferreds, LineReceiver and twisted.web.client. - :download:`ptyserv.py` - serve shells in pseudo-terminals over TCP - :download:`courier.py` - example of interfacing to Courier's mail filter interface - :download:`longex.py` - example of doing arbitrarily long calculations nicely in Twisted - :download:`longex2.py` - using generators to do long calculations - :download:`stdin.py` - reading a line at a time from standard input without blocking the reactor - :download:`streaming.py` - example of a push producer/consumer system - :download:`filewatch.py` - write the content of a file to standard out one line at a time - :download:`shoutcast.py` - example Shoutcast client - :download:`wxacceptance.py` - acceptance tests for wxreactor - :download:`postfix.py` - test application for PostfixTCPMapServer - :download:`udpbroadcast.py` - broadcasting using UDP - :download:`tls_alpn_npn_client.py` - example of TLS next-protocol negotiation on the client side using NPN and ALPN. - :download:`tls_alpn_npn_server.py` - example of TLS next-protocol negotiation on the server side using NPN and ALPN. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/longex.py0000644000175100001710000000415100000000000020124 0ustar00runnerdocker"""Simple example of doing arbitrarily long calculations nicely in Twisted. This is also a simple demonstration of twisted.protocols.basic.LineReceiver. """ from twisted.internet import reactor from twisted.internet.protocol import ServerFactory from twisted.protocols import basic class LongMultiplicationProtocol(basic.LineReceiver): """A protocol for doing long multiplications. It receives a list of numbers (separated by whitespace) on a line, and writes back the answer. The answer is calculated in chunks, so no one calculation should block for long enough to matter. """ def connectionMade(self): self.workQueue = [] def lineReceived(self, line): try: numbers = [int(num) for num in line.split()] except ValueError: self.sendLine(b"Error.") return if len(numbers) <= 1: self.sendLine(b"Error.") return self.workQueue.append(numbers) reactor.callLater(0, self.calcChunk) def calcChunk(self): # Make sure there's some work left; when multiple lines are received # while processing is going on, multiple calls to reactor.callLater() # can happen between calls to calcChunk(). if self.workQueue: # Get the first bit of work off the queue work = self.workQueue[0] # Do a chunk of work: [a, b, c, ...] -> [a*b, c, ...] work[:2] = [work[0] * work[1]] # If this piece of work now has only one element, send it. if len(work) == 1: self.sendLine(str(work[0]).encode("ascii")) del self.workQueue[0] # Schedule this function to do more work, if there's still work # to be done. if self.workQueue: reactor.callLater(0, self.calcChunk) class LongMultiplicationFactory(ServerFactory): protocol = LongMultiplicationProtocol if __name__ == "__main__": import sys from twisted.python import log log.startLogging(sys.stdout) reactor.listenTCP(1234, LongMultiplicationFactory()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/longex2.py0000644000175100001710000000745300000000000020216 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Example of doing arbitrarily long calculations nicely in Twisted. This is also a simple demonstration of twisted.protocols.basic.LineReceiver. This example uses generators to do the calculation. It also tries to be a good example in division of responsibilities: - The protocol handles the wire layer, reading in lists of numbers and writing out the result. - The factory decides on policy, and has relatively little knowledge of the details of the protocol. Other protocols can use the same factory class by intantiating and setting .protocol - The factory does little job itself: it is mostly a policy maker. The 'smarts' are in free-standing functions which are written for flexibility. The goal is for minimal dependencies: - You can use runIterator to run any iterator inside the Twisted main loop. - You can use multiply whenever you need some way of multiplying numbers such that the multiplications will happen asynchronously, but it is your responsibility to schedule the multiplications. - You can use the protocol with other factories to implement other functions that apply to arbitrary lists of longs. - You can use the factory with other protocols for support of legacy protocols. In fact, the factory does not even have to be used as a protocol factory. Here are easy ways to support the operation over XML-RPC and PB. class Multiply(xmlrpc.XMLRPC): def __init__(self): self.factory = Multiplication() def xmlrpc_multiply(self, *numbers): return self.factory.calc(map(long, numbers)) class Multiply(pb.Referencable): def __init__(self): self.factory = Multiplication() def remote_multiply(self, *numbers): return self.factory.calc(map(long, numbers)) Note: Multiplying zero numbers is a perfectly sensible operation, and the result is 1. In that, this example departs from doc/examples/longex.py, which errors out when trying to do this. """ from twisted.internet import defer, protocol from twisted.protocols import basic def runIterator(reactor, iterator): try: next(iterator) except StopIteration: pass else: reactor.callLater(0, runIterator, reactor, iterator) def multiply(numbers): d = defer.Deferred() def _(): acc = 1 while numbers: acc *= numbers.pop() yield None d.callback(acc) return d, _() class Numbers(basic.LineReceiver): """Protocol for reading lists of numbers and manipulating them. It receives a list of numbers (separated by whitespace) on a line, and writes back the answer. The exact algorithm to use depends on the factory. It should return an str-able Deferred. """ def lineReceived(self, line): try: numbers = [int(num) for num in line.split()] except ValueError: self.sendLine(b"Error.") return deferred = self.factory.calc(numbers) def encodeNumber(num): return str(num).encode("ascii") deferred.addCallback(encodeNumber) deferred.addCallback(self.sendLine) class Multiplication(protocol.ServerFactory): """Factory for multiplying numbers. It provides a function which calculates the multiplication of a list of numbers. The function destroys its input. Note that instances of this factory can use other formats for transmitting the number lists, as long as they set correct protoocl values. """ protocol = Numbers def calc(self, numbers): deferred, iterator = multiply(numbers) from twisted.internet import reactor runIterator(reactor, iterator) return deferred if __name__ == "__main__": from twisted.internet import reactor reactor.listenTCP(1234, Multiplication()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pb_exceptions.py0000644000175100001710000000213200000000000021467 0ustar00runnerdockerfrom twisted.cred import checkers, credentials, portal from twisted.python import util from twisted.spread import pb class Avatar(pb.Avatar): def perspective_exception(self, x): return x / 0 class Realm: def requestAvatar(self, interface, mind, *interfaces): if pb.IPerspective in interfaces: return pb.IPerspective, Avatar(), lambda: None def cbLogin(avatar): avatar.callRemote(b"exception", 10).addCallback(str).addCallback(util.println) def ebLogin(failure): print(failure) def main(): c = checkers.InMemoryUsernamePasswordDatabaseDontUse(user=b"pass") p = portal.Portal(Realm(), [c]) server = pb.PBServerFactory(p) server.unsafeTracebacks = True client = pb.PBClientFactory() login = client.login(credentials.UsernamePassword(b"user", b"pass")) login.addCallback(cbLogin).addErrback(ebLogin).addBoth(lambda: reactor.stop()) from twisted.internet import reactor p = reactor.listenTCP(0, server) c = reactor.connectTCP("127.0.0.1", p.getHost().port, client) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbbenchclient.py0000644000175100001710000000244700000000000021436 0ustar00runnerdockerimport time from twisted.cred.credentials import UsernamePassword from twisted.internet import defer, reactor from twisted.spread import pb class PBBenchClient: hostname = "localhost" portno = pb.portno calledThisSecond = 0 def callLoop(self, ignored): d1 = self.persp.callRemote(b"simple") d2 = self.persp.callRemote(b"complexTypes") defer.DeferredList([d1, d2]).addCallback(self.callLoop) self.calledThisSecond += 1 thisSecond = int(time.time()) if thisSecond != self.lastSecond: if thisSecond - self.lastSecond > 1: print("WARNING it took more than one second") print("cps:", self.calledThisSecond) self.calledThisSecond = 0 self.lastSecond = thisSecond def _cbPerspective(self, persp): self.persp = persp self.lastSecond = int(time.time()) self.callLoop(None) def runTest(self): factory = pb.PBClientFactory() reactor.connectTCP(self.hostname, self.portno, factory) factory.login(UsernamePassword(b"benchmark", b"benchmark")).addCallback( self._cbPerspective ) def main(): PBBenchClient().runTest() from twisted.internet import reactor reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbbenchserver.py0000644000175100001710000000265000000000000021462 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """Server for PB benchmark.""" from zope.interface import implementer from twisted.cred.portal import IRealm from twisted.internet import reactor from twisted.spread import pb class PBBenchPerspective(pb.Avatar): callsPerSec = 0 def __init__(self): pass def perspective_simple(self): self.callsPerSec = self.callsPerSec + 1 return None def printCallsPerSec(self): print("(s) cps:", self.callsPerSec) self.callsPerSec = 0 reactor.callLater(1, self.printCallsPerSec) def perspective_complexTypes(self): return ["a", 1, 1, 1.0, [], ()] @implementer(IRealm) class SimpleRealm: def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective in interfaces: p = PBBenchPerspective() p.printCallsPerSec() return pb.IPerspective, p, lambda: None else: raise NotImplementedError("no interface") def main(): from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse from twisted.cred.portal import Portal portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser(b"benchmark", b"benchmark") portal.registerChecker(checker) reactor.listenTCP(8787, pb.PBServerFactory(portal)) reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbecho.py0000644000175100001710000000252000000000000020066 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. if __name__ == "__main__": # Avoid using any names defined in the "__main__" module. from pbecho import main raise SystemExit(main()) from zope.interface import implementer from twisted.cred.portal import IRealm from twisted.spread import pb class DefinedError(pb.Error): pass class SimplePerspective(pb.Avatar): def perspective_echo(self, text): print("echoing", text) return text def perspective_error(self): raise DefinedError("exception!") def logout(self): print(self, "logged out") @implementer(IRealm) class SimpleRealm: def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective in interfaces: avatar = SimplePerspective() return pb.IPerspective, avatar, avatar.logout else: raise NotImplementedError("no interface") def main(): from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse from twisted.cred.portal import Portal from twisted.internet import reactor portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser("guest", "guest") portal.registerChecker(checker) reactor.listenTCP(pb.portno, pb.PBServerFactory(portal)) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbechoclient.py0000644000175100001710000000144400000000000021271 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from pbecho import DefinedError from twisted.cred.credentials import UsernamePassword from twisted.internet import reactor from twisted.spread import pb def success(message): print("Message received:", message) # reactor.stop() def failure(error): t = error.trap(DefinedError) print("error received:", t) reactor.stop() def connected(perspective): perspective.callRemote("echo", "hello world").addCallbacks(success, failure) perspective.callRemote("error").addCallbacks(success, failure) print("connected.") factory = pb.PBClientFactory() reactor.connectTCP("localhost", pb.portno, factory) factory.login(UsernamePassword("guest", "guest")).addCallbacks(connected, failure) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbgtk2.py0000644000175100001710000000733400000000000020027 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import gtk2reactor gtk2reactor.install() import gtk from gtk import glade from twisted.cred.credentials import UsernamePassword from twisted.internet import defer, reactor from twisted.python import failure, util from twisted.spread import pb class LoginDialog: def __init__(self, deferred): self.deferredResult = deferred gladefile = util.sibpath(__file__, "pbgtk2login.glade") self.glade = glade.XML(gladefile) self.glade.signal_autoconnect(self) self.setWidgetsFromGladefile() self._loginDialog.show() def setWidgetsFromGladefile(self): widgets = ( "hostEntry", "portEntry", "userNameEntry", "passwordEntry", "statusBar", "loginDialog", ) gw = self.glade.get_widget for widgetName in widgets: setattr(self, "_" + widgetName, gw(widgetName)) self._statusContext = self._statusBar.get_context_id("Login dialog.") def on_loginDialog_response(self, widget, response): handlers = { gtk.RESPONSE_NONE: self.windowClosed, gtk.RESPONSE_DELETE_EVENT: self.windowClosed, gtk.RESPONSE_OK: self.doLogin, gtk.RESPONSE_CANCEL: self.cancelled, } handlers.get(response)() def on_loginDialog_close(self, widget, userdata=None): self.windowClosed() def cancelled(self): if not self.deferredResult.called: self.deferredResult.errback() self._loginDialog.destroy() def windowClosed(self, reason=None): if not self.deferredResult.called: self.deferredResult.errback() def doLogin(self): host = self._hostEntry.get_text() port = int(self._portEntry.get_text()) userName = self._userNameEntry.get_text() password = self._passwordEntry.get_text() client_factory = pb.PBClientFactory() reactor.connectTCP(host, port, client_factory) creds = UsernamePassword(userName, password) client_factory.login(creds).addCallbacks( self._cbGotPerspective, self._ebFailedLogin ) self.statusMsg("Contacting server...") def _cbGotPerspective(self, perspective): self.statusMsg("Connected to server.") self.deferredResult.callback(perspective) self._loginDialog.destroy() def _ebFailedLogin(self, reason): if isinstance(reason, failure.Failure): text = str(reason.value) else: text = str(reason) self.statusMsg(text) msg = gtk.MessageDialog( self._loginDialog, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, text, ) msg.show_all() msg.connect("response", lambda *a: msg.destroy()) def statusMsg(self, text): self._statusBar.push(self._statusContext, text) class EchoClient: def __init__(self, echoer): self.echoer = echoer w = gtk.Window(gtk.WINDOW_TOPLEVEL) vb = gtk.VBox() b = gtk.Button("Echo:") self.entry = gtk.Entry() self.outry = gtk.Entry() w.add(vb) map(vb.add, [b, self.entry, self.outry]) b.connect("clicked", self.clicked) w.connect("destroy", self.stop) w.show_all() def clicked(self, b): txt = self.entry.get_text() self.entry.set_text("") self.echoer.callRemote("echo", txt).addCallback(self.outry.set_text) def stop(self, b): reactor.stop() d = defer.Deferred() LoginDialog(d) d.addCallbacks(EchoClient, lambda _: reactor.stop()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbgtk2login.glade0000644000175100001710000003041300000000000021476 0ustar00runnerdocker Login GTK_WINDOW_TOPLEVEL GTK_WIN_POS_NONE False True True True True False 0 True GTK_BUTTONBOX_END True True True gtk-cancel True GTK_RELIEF_NORMAL -6 True True True True GTK_RELIEF_NORMAL -5 True 0.5 0.5 0 0 True False 2 True gtk-ok 4 0.5 0.5 0 0 0 False False True _Login True False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 0 False False 0 False True GTK_PACK_END True False 0 False False GTK_PACK_END True 3 2 False 0 0 True _Host: True False GTK_JUSTIFY_LEFT False False 0.9 0.5 0 0 hostEntry 0 1 0 1 fill True False 0 True The name of a host to connect to. True True True True 0 localhost True * True 0 True True True The number of a port to connect on. True True True 0 8787 True * True 5 0 False True 1 2 0 1 fill True _Name: True False GTK_JUSTIFY_LEFT False False 0.9 0.5 0 0 userNameEntry 0 1 1 2 fill True An identity to log in as. True True True 0 guest True * True 1 2 1 2 True The Identity's log-in password. True True False 0 guest True * True 1 2 2 3 True _Password: True False GTK_JUSTIFY_LEFT False False 0.9 0.5 0 0 passwordEntry 0 1 2 3 fill 0 False False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbinterop.py0000644000175100001710000000271400000000000020635 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """PB interop server.""" from twisted.internet import reactor from twisted.spread import flavors, pb class Interop(pb.Root): """Test object for PB interop tests.""" def __init__(self): self.o = pb.Referenceable() def remote_int(self): return 1 def remote_string(self): return "string" def remote_unicode(self): return "string" def remote_float(self): return 1.5 def remote_list(self): return [1, 2, 3] def remote_recursive(self): l = [] l.append(l) return l def remote_dict(self): return {1: 2} def remote_reference(self): return self.o def remote_local(self, obj): d = obj.callRemote("hello") d.addCallback(self._local_success) def _local_success(self, result): if result != "hello, world": raise ValueError("{} != {}".format(result, "hello, world")) def remote_receive(self, obj): expected = [1, 1.5, "hi", "hi", {1: 2}] if obj != expected: raise ValueError(f"{obj} != {expected}") def remote_self(self, obj): if obj != self: raise ValueError(f"{obj} != {self}") def remote_copy(self, x): o = flavors.Copyable() o.x = x return o if __name__ == "__main__": reactor.listenTCP(8789, pb.PBServerFactory(Interop())) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbsimple.py0000644000175100001710000000053600000000000020446 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import reactor from twisted.spread import pb class Echoer(pb.Root): def remote_echo(self, st): print("echoing:", st) return st if __name__ == "__main__": reactor.listenTCP(8789, pb.PBServerFactory(Echoer())) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pbsimpleclient.py0000644000175100001710000000104300000000000021637 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import reactor from twisted.python import util from twisted.spread import pb factory = pb.PBClientFactory() reactor.connectTCP("localhost", 8789, factory) d = factory.getRootObject() d.addCallback(lambda object: object.callRemote("echo", "hello network")) d.addCallback(lambda echo: "server echoed: " + echo) d.addErrback(lambda reason: "error: " + str(reason.value)) d.addCallback(util.println) d.addCallback(lambda _: reactor.stop()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/postfix.py0000644000175100001710000000107200000000000020323 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Test app for PostfixTCPMapServer. Call with parameters KEY1=VAL1 KEY2=VAL2 ... """ import sys from twisted.internet import reactor from twisted.protocols import postfix from twisted.python import log log.startLogging(sys.stdout) d = {} for arg in sys.argv[1:]: try: k, v = arg.split("=", 1) except ValueError: k = arg v = "" d[k] = v f = postfix.PostfixTCPMapDictServerFactory(d) port = reactor.listenTCP(4242, f, interface="127.0.0.1") reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ptyserv.py0000644000175100001710000000243500000000000020347 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories # See LICENSE for details """ A PTY server that spawns a shell upon connection. Run this example by typing in: > python ptyserv.py Telnet to the server once you start it by typing in: > telnet localhost 5823 """ from twisted.internet import protocol, reactor class FakeTelnet(protocol.Protocol): commandToRun = ["/bin/sh"] # could have args too dirToRunIn = "/tmp" def connectionMade(self): print("connection made") self.propro = ProcessProtocol(self) reactor.spawnProcess( self.propro, self.commandToRun[0], self.commandToRun, {}, self.dirToRunIn, usePTY=1, ) def dataReceived(self, data): self.propro.transport.write(data) def connectionLost(self, reason): print("connection lost") self.propro.tranport.loseConnection() class ProcessProtocol(protocol.ProcessProtocol): def __init__(self, pr): self.pr = pr def outReceived(self, data): self.pr.transport.write(data) def processEnded(self, reason): print("protocol connection lost") self.pr.transport.loseConnection() f = protocol.Factory() f.protocol = FakeTelnet reactor.listenTCP(5823, f) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/public.pem0000644000175100001710000000166400000000000020245 0ustar00runnerdocker-----BEGIN CERTIFICATE----- MIICjzCCAfgCAQEwDQYJKoZIhvcNAQEEBQAwgY8xEDAOBgNVBAsTB2V4YW1wbGUx EDAOBgNVBAoTB2V4YW1wbGUxFDASBgNVBAMTC2V4YW1wbGUuY29tMRAwDgYDVQQI EwdleGFtcGxlMQswCQYDVQQGEwJVUzEiMCAGCSqGSIb3DQEJARYTZXhhbXBsZUBl eGFtcGxlLmNvbTEQMA4GA1UEBxMHZXhhbXBsZTAeFw0xNDAyMTIwMDMxMzlaFw0x NTAyMTIwMDMxMzlaMIGPMRAwDgYDVQQLEwdleGFtcGxlMRAwDgYDVQQKEwdleGFt cGxlMRQwEgYDVQQDEwtleGFtcGxlLmNvbTEQMA4GA1UECBMHZXhhbXBsZTELMAkG A1UEBhMCVVMxIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVAZXhhbXBsZS5jb20xEDAO BgNVBAcTB2V4YW1wbGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKkRahIc Fp0V44QYpO9ue7mjZNbZYPAC8caoQC1jUgL42CT40PcoOiZWLgRk+Qw6P7PoJzO/ T4ufK0qoPUJm1jErDRWy9eWlGLE0grPECM+jxFfLXxJLKdPtuwMA8Ip72JMirFN5 Y/JTBZOR3j5a/mbY5tcRqgffKxm4QQegnhiBAgMBAAEwDQYJKoZIhvcNAQEEBQAD gYEAWANPpp985nXMoIwHlsSMm8ijkk7XQU3oioCYDcM6pLT+mvBDe1mZc8mUlrWy Zo/lT6HF44SHIZ0zCgPYwTpWV6C0K+/kKlYBERZ3ajrzGf5ACfUTNyk5P81C68mc 9fQ7lhq1iuNKzVh8b746Z4ufn6iI1VygnyOQ1hZ/lOX56TA= -----END CERTIFICATE----- ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pyui_bg.png0000644000175100001710000007233100000000000020427 0ustar00runnerdockerPNG  IHDRV+tEXtCreation TimeTue 3 Sep 2002 12:48:18 -0700gtIME /@r pHYs  ~gAMA a!PLTEff333}tIDATxbȎvk$v_pp'"ѝ({NDԯK;Q.ڎDDl;ND;;QnѾ\'[J[qo:WJDݲ)y+]+u-tD.+ۑХQpoe:WJD=Ľ z ]*5tD5c;ХQq/OrLD w~DDw~DDssK^45-yD1p7K "Q}&-MDsr^65oqWn"/yD=ܫ(uQ}&NDs½r^6-ٙ[hV}&53.sK^4-/yΌLD HKDUԠ;%~j/hqv<""VڈR8ʩlhN}DnjZ>\"Z7S/-ϗM_LDk}&5S? ђ[ ъ[!т[!т[!т["!z[#)r[#)G>gN05zˤ>hN1yˤ>hN2y >Nh⭔E;STN$OZO};<5w>ngA(MjVK}NHjK|NHjK{NHjLzѸ<(t+&x[XtWoGt= I4)6j>H2&Fo3 =H2%v/$_O=XG &RvpQN]=WGO1nwɄwpO6xa/ra{J2ZDF'0}\~qWfɳqܯ{݁Wϔj ɯQ}5m>wCիzTSH~EMkns1cgJ6U'J9T о6g;($GCRGGcza|RSa$">t9zR#pdQqJ2dҾ6;吏4IFEyn3{)@I/: A,Wyz2M$p} |oգj" Ѿ6S qWRy5tOe.wIA+m4tWoU)J8t;snd81~[wX#I=f&)fO2S'pqz_呤9ܿIAbg'L|XM\w;ܗ]=;ɧnzjwq/{&-}tWN{i>n^G|K]it;O⾨I?t3dz}Y;&}A3Sb0V'6/n'{qSf2N~&]>_2SEzMߩE=7g=۷n4  sole{/NӁM] }P_~h_S9'%.sj+u3._+n^V%yt BoOWML8H5~l=GmpF D[j19?2n[.hhq.f5-t?&(-CJW:ϥp~d㾧ˋFwp3t3Q\h௟qE Sىi{O4D w7^D(0r{j(o}}nwk7_Bͬ0w+yjmS1MyJڞ3Ƶ{nt\t7-.3kjk+k~q^i' 5։qw}7>fk_Q۪OD5֩m}}p7<-|)OM3Ǿ:GzaԲ{>}mOj}ݽϺvI Yq>nSKs}SS/`Wp;RL_]ͺ?Nm=>q8[?)54绿[~qtIa-wf'Nw_tlIܟ >zqQ^Z%q}wO6-sa߯~wII 3+5]zYY_;Jbƽ(+= wtmZ_:#Ts-5z>x>vtZ^_}/> pF6;+sH`{,h~wwt!⮾6Rs㜴`-5ԠCmcQhKKpweܯَ@'c¶L hoO.5T!pצ?@Vj]vEL!ZȺUj]Zbk1&npOcvp]d5]MqXj'K]} pO')׺V ag3n>[rղZ0>w=CmW*ܱtԸ.iaB.pWwo讞͘l^ʊĶa q_Wb,+.Qp0pWw7ٝ*{f~r݇ˈfnt纫A58_^-e]X>#h/>5j) -]ql~y5rZwslu5CwMnW f?=U@hh﫫+wcBt7;LWCY2pwIw!:j)kс{E͎Qbɢ{tnL3q_Q/Flo+(~]uv ږv>~c_Ae,mGZ讞ObY½R5_WP{Y,;4p7竞}ݟ/ !~W+f\qDܟBelGR᮱{>?V/\VaWa':PkY[Q~̽pjd]|uwǮbw=n"L~P&Ei{>9[^KM`^᮲Gjnyj]f{Gwq曪Fo/>X܅h;,̄XYp^FwY [%]m]pTҼO`)pWWeA()PCرt,j+{K@-a^^}_}jO3ɷN{嬨 7ӗTcحtIp.{LJxݶx!] x? c+f&f_Sa^}iv*i.j;7UǶ7>%UÈ4BQjwXX2%u}ޫ.vGۮY{mejn~Uߍ+/̳{1՗Ʊ|{f%ƶku\B/AcxSlW #bҿs5CMnPހ~ǤlC=O.c3>{v6T<5t=Kz}5 B ]WWA5=%귪A辱TMttSM5 Z\VҧbgC:s?nxGZY05wSen`2v5\Tn\7u].oM/o>5=jgE;ۿROOjNP՘&lவݍS$p3jI6p>߽}+fKK~sՒf5g`*v!M]'3`&vpY۞cB?eloilP+dNo[OM*Rؠ> ۝6k۳ >=vpYӞg@pÙ: aHhZ_v|u{Ԍ&۞ij;ow|}wRXNG ZѤ4p໚ u^gvpٻiF?NZܴ?M> V}jLMx.gy]>5ū;j_aˇwm4C6ԇīut5ulm4vM4j\婁,@wmMܭu_wrh}ஞ45rh󾭉;ۦr~ m9p]45pVh֭uw}W MMd޽%q;l&n~ݽ5qxx]YwxVHtW +Ldն'LG{oҝnfhྟ*۶S;Yrq,M\w_^mevL=tWY~pxw:};7Gn*]Gf`VNj[Ԑ ܷRY{Oܝկ{j.kuv7WfR KUeNʸ?]ӯ΢@]+|Yt?ᬻzK醴~6Yuw]-e긿;{8ff7ܿ؂Qb{wt7?g;Ӹg,$>6gp{]pw5R=;zƂ2]-fzڸ[vEG7ܷ;ǝgwپ0j[up翟t^Mfؾj[up}9 SA{=tW{ت ~ƻZRF=lշowwRKMy m_iΝCƼk,5޴; ]=◿Zd;Ƿ+U۾~%xW)RKnFz>O۞w\2=FH-{g^2 "v_RTwIڴ=YČ.l?UV}Kԋ0]b>#>Peo U7p7=<W珼_Ylцҟ3d{wd3Gړ/g+y_t"_]bvxR|:DN`N" QGPS'5Eu>zߟXK_oUQq&qWX?kpYa nj>u&N;I^Axxoo뙀;mv*{ xxi#iځ$=$w&ܣw?Ό5:r]] dm½JLPW9%y!O5QFǫ~=8pucwr?N;KG  ls{qB d{<+!wYW27f; YL*JtOY_8} OJw)u3w*]={nK:[uK_m?KTH;u=ݛ5{.G7j;vA*u>e] k|N?V7uP;Zhûz.EO_|EnYb~ ,S^~9{OjP>s;WK<l'c;'wU blāͺ^ou}4WĩP?ȤhZ^k9RۿMML)U^c/{HnQ+t>/bsV%G)׼KǣlN0Gd;ҡ\ƶO6Qѥ}.po݋ުK3)qk{9]=t\٥}&fO;>wO'SMǕ^:tvpϘ@wTqgѴ!+t>! *t>%^wXCn>{/Tc~vW8`};M=gsN5X:ݷxl1g'kt辕;s_n-g\=y渿p:ҡq9ྦྷ+=xxW &Kx}|p2e|ӣ,?k[^FKs{<~^S^nKo;,w*Q{+ܯp GqvVǥ{ ~t tλi:g9wznwuRV[\Lʴ|;۹Ki=4p޸0lt% ܩ?o-{vpqitlIsxUt7+kwp?o[nԁ=}wQxk fҤ)se{k;nR Wme݌v{6Ufk۾ħvSj+ϛ&p_?;G{{}}^wÏнj\,؏Z~vGwpW:K? O~tW*{Xz~vp7ڭl}1{4f?yvgw0>t݈߷'fԽq,Q~{nod|X7vvA}cNjn{|56V Mcw@Vv3u=o@Q|;c[nA{x^<^-p}wos1pr~z:cYVwS]ݙt,Eq7w>F +ǞTwkp7ݣʱ%&\ƞv̭y^7vĨ{~{v{ *{(O'YW0QPxEc)*GG讞N{X g{0<8c+퇸pr U\cs)p/3֧JlJ{Xܛ_-S,)p^{X}vp % E{X讳kb[SB//^**̨d{X];m׊L^*?%qkbC*Ӑ*H^(?J^(?GuȀ{N Ppܛubʎ6p/Ӄ*5ֲ{۰J ~XyqwEbz$݇+{H}\pw2^!pSX&ܝeF;+GB{=} gDKz\VIL.h%)?vU1DvpwۓƬV=؞*`r$=9!S>V=>0t')\wcۍqzKs퉊=q>^=t ܣ'_}z]Oz>'TF:oq?K[L*^E>n ~z{S̾9u]=̽s!ڳmz_[O1i`&wo;d;{axn~w{yRŜV$= ~x!꣧w~PF|i4 j?]%sܙwU~7pz!Ƽk;zd3#Ѹw]̻6pќҸzpg&mdo̻8pܬ]p?2Vy:͛SVpg֕Ȍ]̺2p%=UA.; ܯ=wjY3]%38~nYW3{m]"vy>}=CzG}8 =Aᶛ^'3Q}w>OcLܙEOy,mw]:-p.og*:'H'}+ʌ' ܷe`7mgUXwebۯ^{>e3Զ_rk{L$1oڞwlOz楅7$ep)5mS1=lS uڜ#5*GLzz'"aT7.z~AڶOw'IyF2G5pg./G{ݼd+xd['Iy bjݣwe ' eބjl0S}'5)*62ےw% wrc_WǺԞid~zr{=c7}tݽ3Qnk;b~M矹[>|^EP 9h7vG]EPB,UWߪD39*zq#-oUF;76ٍxk#ֶ}O/;5/z2ՙrnj9~lu3Ύ4{2P;4jhX i)}?wO؝zҴu3PV 'pO_4CP7FIj 9pwjb3_wI=uvg>y;?jj% [cܱ9 RSޥuRA\wñP,Cv7HMmtWJ=YfpE2ƞֽܝ".zmk4"u}senSylXQK=mjqf؝3$5n.z2ߺ'lwv꺻a90 j;Y'82}ec90z<vpjԾ*X2(箜ZAJχ|LJ.-Sρps|m}XzVuv{U;|ILIp|`cЍR˪ps }h:]=,AWJ}ΗKi,vֳ s2dY?zgݨRc*c"RU} xԚʚ܋8gm7in~5͔;6ikpp7ioԖpixY#6W?aDڶ~Vaݱ;kpՖgjn&ZwLS[fDڶ[>xZݩ{7 5Ŗ~>9\YehƩ_ G*0de=_mV/@C˦_ Gh/~w E~7f,f8 W>o{[wӊ>^j6/̘[56垭-ݰ򸏭ڀKN3fyMIg ܝ~ Y 4c{oP|ݹzW/̘7K>[\_'~i}"=fP6nVwL3s=櫓f;5_t [}&½Tޣ|=p,o\ցN=[|4k-xyI':l{@]n˗mK}9ҹG z=/ osT w;o_ t, e-;i[O Uw_\L6Xڈk.gRw˹RUjP}Qۻ-H>^5hPF={1S{25(྄[=!C1\ zq/RؘٛWOhqWFu[@pu2_R u[@pv2v7w>1pGk|ம|ek w?<%]{.%kwMJ{^OXSOg<{L X}eSf9b@S}woײ SĄfhvpO$l~"F4Aöq_mAq}6f'MiB9l? Ĝf(?c?w41)f;?:7Ǩ'(K؞'4Elbx~)lO3\;L1I,΢lbfqER1iJeoh'\d$pϳ{ox׬6*kIx׬q eY۳fvɺuw gK+,jO?',M+l G|ۓtɨzF{P>ek:dT]=B',e7? ZpOV&/k hU&+[j=dT}]xGpǚ k]+o)Tm2Ҷ}fW{^؞M[ۮWpF*sɡ^e7f߿]}ȋrɣVtGB۟G79Ծo)mj0]:Qʀ{7T ǵ~J?̈z|Kp_{8W]^IQ-0d]ܯg쥶ˎu;M3Zpɶ̸/o老ugʋ;->dV:/xܥ{\L; ۴wvs;Ÿmpi?pO3cɼTCײ|3=cɾ긫w.=|]"i`rF簝ʃ;9_( 9\'{5ɱ,3ܻw&J;CcWm_z/h/?Kb;&3_^N;S,Oh_}+h]ctgb;{t՗.c{NYlwpڦݝwy/lwxh6]wםAoIܛtvvG f\}?Կ;%.[6A_wxhpН).҇RX+)3E}X!lq.a:6b5 s^v nPwC2}<%5=N n;\'pӇݙ} tLj-kaw>3ו}l'A3וzpW4,wV~)M] Ý.VB3 ܙje}kxwQv5|O3;ew&d>.H'/R}t^WlO0EÝT_1W$lsO>eٳ>h;sOq?^GyAބ=c3$w`/_&'v9YC{<5T=[>F"KݱCzxpEாFZUʃ; w\o&3Vwp=/=wpYM唾&)N3ۛt9|mSޡw_wlo,=a{ݱ+X&=Bxlg)y(gݗx>o|v.M9/w/ޡ8W_/w۩Jb{H ;Sm̶w;lWf7t6;yn;7+{% ;S.|JԪ+g_rT3Muv*جWzN+lNExv/4Ua]pw8eWf*l΃;- ^xig;SNp2!;n㮾)ᮾLn~d^cw]]Ɖ>珻 wj ܩMθ/h.p.TYh/W7z+E$G՗6|NwjҺO/SN5+pp*}H%au_3M%pGw:m4{e݉eD԰\a?Qp ޞہ;Ul=l^}EDD_Zyj;._Qpj_Tt'-kEw"ʞه+~̻r՞~f"ZEqk;Q缫 G!MlGw"Jǧkھ>syOx?hݎQN>т3"J%|0sD?܉H)}0??鮾D`G ~]}hݤgp'Lu=Mܿ|B5]l;T%"q=q}wJDLYH>ܽ&w?g7׋d*3NYD52V]`wO'Q9G0]ѝ2eD5f0tQQ9Rѝ44J63<X ڨ-YI9pqN.}ӽ\i޶{ 곧5>wN 7|w}+P=-ѝ#{;HhkUȾgO+tvftN>{*ڟ+3Dvuz^>/Wp},GEq; ?mnzȣ~ wtd6Ļ:ZpO}pGwe߿/g-{>??Yĝ ܷi]O?բ{设&y&.;>hOa>~ }m7pЗsL_|쏦pr vtƹU= ?c4ӗqn{rvt/֩{[ճQY={N)Cö-}etYR2jKvtfث `w}z%F~»iãc^)uWI派Sgh?^}*9ytuε]Mh]}oAYw\8*9vks44${t߿O^ovZe:`s~ XqcAGRȞ25_ ivt^W_>]9m֚`GUj{R?k+;IkOs^;3ټWp>uꇺ`G-ej7qd]=K?#.& kx'c3p_(Ma:R{֓Gug_>V?GaGBw '_E|< juWj-l[޾}cM>q~ /s}}wGR>;UJweIg>ڞZkVZ3;}}z+>kUTwve3&]&po 37m7Y45~)jeE]m_vs&Ԅ_]=FV_qoȮ멍]=v.Ԃa&*]}kZ0 xWDTW'5վ 7i ,MPZ{`+kxpw+Ivch {U;釁{f~q9jyWGjЁ̶tƸ~g8ԈOpvզCim*=w}՘cxieLqsF|^whX57.-)ouJᾖѰj~]V^}$GYգaե٭ []ïMӺ730`3[Sgp=llԆ_]=lds2m5tW ci; |l3FjWޭU+EOZKz*2PA^K+0lm"tDXqR}lzjrbVIGJ"۞:TquX]Il*5ߗyWؽ6wX xoGڧJ$15ެ* w?~i%v3N8R, l4`~_pW(|PUWgU=+S&/wEE7gu) ͓ˌZjuY7:!N2;{m?IwW脀{4gRdW(:[n9tq! Kawߠ8^Fs8}4zsI?1{-/[/Owp`ye]-NskŻzϧ+'=SA1ד9~LUܛ=C@1W= ?O'-P]L'˽a당#?=pGC}PU|{o".6p[`oK=_z͐skJ;q{vỗ7US lz׿d^9Q_!_ObL߽tWO>3\a;z|y?bRNͬtWd>SR\I;x|q?bRN݌tW;cT:R OzӷSi?ww =wwvem>#%}vGrk`RzF/@6sL"ޛn?~/t']=rנ~A9Uٖ-_B=NymO=\~{8]ٖ-=_@=N{~I6̞ܽ_ॲ̶|,yq `q&ǥQ-߫\ٖ/$4LfH3qZélonf勄1zw[wztVvl 3;C{g=e?^,pY$pϚ_pW"a{x<>VRٖ/.w8ٵϔ.=O e}{=S޺gIǛ>J ]iw=QgI;>G]iw=Y~+xTfG5{>) hF>v+DyGE*aψw5r]=.KUwFKktM^+{6A{.CnEz[)WpߢuX^~ ٻҌ p7ޢ}B}:,c-~fhY3~y R̥PpnD,;3}QX6ʇPlƸ/;mw Ьiwp_iqgWܝ"p'eGle]}?(6WZ9p'eG;Iw5bH;|GiqgZ9! ""Sĝ9m_Y%YNnX*C ! ܍OA;Alolժ]}3HVrCzVrCqS:J L!} o.ӟ׬ioՒ))a;J !mfk;WK/p@N-SH%b-20t_]"^lwJ_\"^2ז|?U'|jߟKMT%bows-+pD,Ov.KN-TO/;nmgs>EԪeA/Sv{J-YjۙD/bNEbV ܩZ>Hӊ;U2j2Xgi廬;t)fecviR ?IҺ~&λ`;˰J˖dH:&-Zg$ko6XHlb{F[&-UHIm]m0D%xfNWHZg&$pH2 <3IݱjY$, 3Iǁ;Dvfg"cھ:󣸲Tp"rIgERE vfκm;; Y$:q&tvpmzgR}(I~oؤe}&wrd`;S2ۤI\e{Y57iRDZ8U}K=G,RUmzOfR}쳦=`{_D>K Gw,SR?Yaw~J-VwܣDTomN u#/k5jKVXoif *uŗz"E~W ϫ٪̿&Ygp`.wΰ^+Tf=6ѯZ=suFm] .3Km]uqW9԰YU̿5['zWn4jxՅn{WoM5J]47Y'T0jςq^}hz`՜U:N49,{u uiTc]K_vp_zS]}ՖWXWFe2_b"UӨ>F%}[92{jSأԔUtmnTtn/QtGq6L }^ p[ՙS⛮V7pwoUGR%x};LRޭ}﹀o UۻUu]q]!׀ެ[>\טޫx8ZS/;{t3qX]@}i­G&ћ;c{ npwg/f;4p{rQ}%W{n]MYUzu?_xKMYUzR/&\%>ml3f1.~pnVcN=A˃-+>qWk?[n., ]uWБsXlZAnZfE/%m>=qW#!5Zvp_Qk_q5m՞^M]j#~ִWz?d. om?$Sty$&[VhWo:Ǩ|ALQ6Jd.Em?өwO-VxZaRY+&vuꙻ5Sx@RkF>[ݚLh { ~}0PL~mc?l?=휮z.`šݰ wh]=nhxcwu,R Kj;}zCڏl?K54$g7螥߰] i]=Ue;'il3wi RM =k;ghps79#sօaBjW(C>QûU$ ZJь ?tLlwqep#p']\ǞwR- iTTFU JԄ?zlGwmq?\_5')pY*;][uX %k\TiՔP|Mqi?l?+[]YrܧijH(_ؾWܸ֬_^^WN?RAErݥ;o.#.j"j#rܥnh'b9ێr7} K9Na܅enl'pW~j؎2>@;ɠd8JMUoiO.,l;9vz#+wtm;]=I;KJ7NkEml'VIl;n\;]=K7:ߏ rlQ۟Ĵ{t[Oz=l9*lmkmwwx+{=Ç2#Why!.J-݊{=G./=ۋJ ݊f?p!d>^͝=ϕ H;lo~EwƯ}?os[-tq߿ϙڿũ?lԃ#5}r\gM_~TP%ߗ< O4TyZ}>tsxQ=Ž,Ti~ڿ֕^\7JenX^*O `㾻x߭RYc*#- yRzRYbc*U&۟\mbK gSYSlru㋩={A)>bێ>׍/j~?羥Jm;o޻֯_K=&ʉ*޿_L=!\[Ը}>7_ꉎI-hsTqGw*nrzR 8܇-UnݺGRܷt~{&4k>l=⾡ I6jDaV*n#1_twzFGhR}FzT Z)K1Q Uw > {v4e)f?tRVe~j]!MX]ܸMlo9^4_I?r&7jJd#*{]P.۟NlqzmԔ+-P)]e42qKwx]2qwU\5J3aKUwt-k4Yi?lJf7V[\R== p;*hh4UZ*5é,?TJCx7,,JnWp_b3?/↔Ψ}BUGj,}gn_}QT%υ*[j1 U_qG #w˵ U_,0DSjoL?׌@p*.8-Zf|wQ߫qO 8"귘tPMpEpwAw>^mpX>^U?ND3oJ 2Wݘ~)R1vHiR&ѵ'JK'5J9)pFCr/,JͰSjO3~f]E#ۡ~YʸKV/YJb?ƦP,=vRRMjo㮦ƪP,{Դ:9lFCsS._#{~I}uxVƶP,eݏKf7*².{6N/󲾧\od'^M}Su`fU=܂o#+jk⾦i6j_ װn걗zG- J;pwcCNݗU#{yѝW^MRk9] /~Y$;_oÚw/Vs4^_kپkzޛ3}$~=&{71^_wl%j9nځW)\5__SiG3DU2q#Ox핋;l܀*TqWp~׻m3vig쎋F9wFMn}FpϵMKc']=ys.׊. lJ}FKDZ~\RU=C!Σ׉.TѲ;78&_$:dycw+(GNF;[76y&{T /> ܣS[*K1w6S !Gv6l>M!wHw-h;360z=;>]=m ]1{Sݪ [ԳSBwpՉFo{/{gnN ݟYfٞlmcS});G_fSOO{Jn1 ߕ[,J5ԧC&F\f=w%{˪⮰}Q$^4pYM%q➤ݴ$$7%=нeqnbiw&{)h}Cxcjdݛr&OE{J>C-I;,wI%Gɮ5$+޳ö;wBoR=Rvw'UpGݹ+{-bޠ斀;rvNuNsHf{Qw|oQa۝;57-ޮ9ojgl%W\\;'39hvWj9wەsО}45:x>}s<ѝ4W{劸{Np^&^v DO+/CjWst6w /=q?YMɼ^)v]Btfu \a{\} SSUո;w ;Euvp?}#7k*6VѼ^)ö}]]NcfNmtG{=lSwt ΟQmUa./GzܟSņmKw{⾊ptjNq-q_DzNec}mo;*-dߵSDwJ?pGc;zȾk_WN)=; !}85Ot]_[-;kޥ"?A;ߵRoޮy;U)VpoՊؽ~]& &{MSxUx]Pwpj)euoXr]F)wto;Jj=F=]N#˦ޥSutd-^{>So.wpMM3*; 65wc~?ϗ_{ |*v ܛ2~TVf-Up?tW誩h7poݱHT#XϿwp =@w fݝw5 }5;}pܭuZp;_(Rj;FL~gqWw_fH9w]=]yWEC)wqw]dkVw/Xdr^:?wC{Ը?zl^{jnK~~7k]ܕ]~[Eqn_=^=н[ ⮆:_vݳ2j~Gw5CjCLtW~_= K~77UW w V뺫;w KOzq}Ը)p\E/V[^ޓ"WuWNu]}'pwZ޻epWd;ٽN{*>GqG^yu]-ݭ츫7f'poޣyPݫ0jr>eӽ.a{=CNpK>p1}\%poGrfϵ?u)O~D%p_$pw߁^b}>Σfl;u/KؾJEwpG;]ܱsEwN]}^WJEwctY7).a2? 讖}ͮ>;61:]̇m[}j 6ty_yyte~9E` שPa,߄~{>EשT;gov>M̏A p_:ce`9 \htw)PcK^} {6fl=EI{$p_:؞̽)y{O} ^ݛn;SG%؛2_g\e=]Z=zp^~/7ɵԕ:[fזbWl{ԛqV5ܳ kzKU~wX@@EbWjзhx+CwywW؀5C`m25vU~w} .K\:ޜUmBߺǯtajVj{x+_.yT~o}:n؞ zz#FzT:^JqGFCj gra֨b(p_p7. +3^%5ym U*9izwvF Áj~>ZwVF Áj_Is}o^}Va 7: w*ˏ}jTWQ^!I ÁjVp/Dt7 ܩX5UF\Ju[WsX K_>5PNŪ9ʝկ" ~{!p_-p=v+&{jǽݽ^\-/WM܃>Y|jAWrI>npC5wS^EW-@qTݑzU\5]c 59ڻ;׫ZGdG_;խGe}Ճ)p_ë6<=ھގLԧK/|} [jcp~~wp ݗY(?{9p5>\)VJs S_nwp5-kk.}}߾X9 iU?߃_{W>9}󇖧}{ێ+w?]k688`[{-oxjզ܃/W'AG~W~WA}K5F;g~[nWA}$65ξff~w&` <;tDWRC^Dy{}M/Q=΁ƥӱǨ&uo_JnT|ᾄ^zKٍI}0ezw~WV[0.<* ܋iO1שFސoyyފk,я}*]K}-,XAZ}]U>cއsZje 3Ӟ#&gW,|Nz(m7z#NurgEu^|dOj>Ypj;}N /z>w"{xr;},cgH܉cq?|f=NXq~v=NZq7~vw=N\ &^NwsUp[=`5ND돻;=:jU [ѓ܉o9Non}ԂDͫ^qՆ.:G"'~q%ՆO.:G";5yuOtDD?N%uw"ڨˣ;QP}!w"Tx/Q攨O)g/>܉hR7%;Y]|p'5K!=].b%(t܉(QIloDTp7w"ʳ{}!"X܋<;UJ{P?DDe=CD4[ ܕ/?DD]ND9['хsڈvWhs_㿡=""*݄NDT%p'"j5 ܉DDw";QcND1p'"u ܉:DDw";Qh;_lIENDB`././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/pyuidemo.py0000755000175100001710000000144200000000000020466 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Displays a frame with two buttons and a background image, using pyui library. Run this example by typing in: python pyuidemo.py Select "Quit" button to exit demo. """ import pyui from twisted.internet import pyuisupport, reactor def onButton(self): print("got a button") def onQuit(self): reactor.stop() def main(): pyuisupport.install(args=(640, 480), kw={"renderer": "2d"}) w = pyui.widgets.Frame(50, 50, 400, 400, "clipme") b = pyui.widgets.Button("A button is here", onButton) q = pyui.widgets.Button("Quit!", onQuit) w.addChild(b) w.addChild(q) w.pack() w.setBackImage("pyui_bg.png") reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/recvfd.py0000644000175100001710000000523400000000000020104 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Client-side of an example for sending file descriptors between processes over UNIX sockets. This client connects to a server listening on a UNIX socket and waits for one file descriptor to arrive over the connection. It displays the name of the file and the first 80 bytes it contains, then exits. To run this example, run this program with one argument: a path giving the UNIX socket the server side of this example is already listening on. For example: $ python recvfd.py /tmp/sendfd.sock See sendfd.py for the server side of this example. """ if __name__ == "__main__": import recvfd raise SystemExit(recvfd.main()) import os import sys from zope.interface import implementer from twisted.internet import reactor from twisted.internet.defer import Deferred from twisted.internet.endpoints import UNIXClientEndpoint from twisted.internet.interfaces import IFileDescriptorReceiver from twisted.internet.protocol import Factory from twisted.protocols.basic import LineOnlyReceiver from twisted.python.filepath import FilePath from twisted.python.log import startLogging @implementer(IFileDescriptorReceiver) class ReceiveFDProtocol(LineOnlyReceiver): descriptor = None def __init__(self): self.whenDisconnected = Deferred() def fileDescriptorReceived(self, descriptor): # Record the descriptor sent to us self.descriptor = descriptor def lineReceived(self, line): if self.descriptor is None: print(f"Received {line} without receiving descriptor!") else: # Use the previously received descriptor, along with the newly # provided information about which file it is, to present some # information to the user. data = os.read(self.descriptor, 80) print(f"Received {line} from the server.") print(f"First 80 bytes are:\n{data}\n") os.close(self.descriptor) self.transport.loseConnection() def connectionLost(self, reason): self.whenDisconnected.callback(None) def main(): address = FilePath(sys.argv[1]) startLogging(sys.stdout) factory = Factory() factory.protocol = ReceiveFDProtocol factory.quiet = True endpoint = UNIXClientEndpoint(reactor, address.path) connected = endpoint.connect(factory) def succeeded(client): return client.whenDisconnected def failed(reason): print("Could not connect:", reason.getErrorMessage()) def disconnected(ignored): reactor.stop() connected.addCallbacks(succeeded, failed) connected.addCallback(disconnected) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/rotatinglog.py0000644000175100001710000000071500000000000021163 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ An example of using the rotating log. """ from twisted.python import log, logfile # rotate every 100 bytes f = logfile.LogFile("test.log", "/tmp", rotateLength=100) # setup logging to use our new logfile log.startLogging(f) # print a few message for i in range(10): log.msg(f"this is a test of the logfile: {i}") # rotate the logfile manually f.rotate() log.msg("goodbye") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/sendfd.py0000644000175100001710000000560000000000000020073 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Server-side of an example for sending file descriptors between processes over UNIX sockets. This server accepts connections on a UNIX socket and sends one file descriptor to them, along with the name of the file it is associated with. To run this example, run this program with two arguments: a path giving a UNIX socket to listen on (must not exist) and a path to a file to send to clients which connect (must exist). For example: $ python sendfd.py /tmp/sendfd.sock /etc/motd It will listen for client connections until stopped (eg, using Control-C). Most interesting behavior happens on the client side. See recvfd.py for the client side of this example. """ if __name__ == "__main__": import sendfd raise SystemExit(sendfd.main()) import sys from twisted.internet import reactor from twisted.internet.protocol import Factory from twisted.protocols.basic import LineOnlyReceiver from twisted.python.filepath import FilePath from twisted.python.log import startLogging class SendFDProtocol(LineOnlyReceiver): def connectionMade(self): # Open the desired file and keep a reference to it - keeping it open # until we know the other side has it. Closing it early will prevent # it from actually being sent. self.fObj = self.factory.content.open() # Tell the transport to send it. It is not necessarily sent when this # method returns. The reactor may need to run for a while longer before # that happens. self.transport.sendFileDescriptor(self.fObj.fileno()) # Send along *at least* one byte, since one file descriptor was sent. # In this case, send along the name of the file to let the other side # have some idea what they're getting. encoding = sys.getfilesystemencoding() self.sendLine(self.factory.content.path.encode(encoding)) # Give the other side a minute to deal with this. If they don't close # the connection by then, we will do it for them. self.timeoutCall = reactor.callLater(60, self.transport.loseConnection) def connectionLost(self, reason): # Clean up the file object, it is no longer needed. self.fObj.close() self.fObj = None # Clean up the timeout, if necessary. if self.timeoutCall.active(): self.timeoutCall.cancel() self.timeoutCall = None def main(): address = FilePath(sys.argv[1]) content = FilePath(sys.argv[2]) if address.exists(): raise SystemExit("Cannot listen on an existing path") if not content.isfile(): raise SystemExit("Content file must exist") startLogging(sys.stdout) serverFactory = Factory() serverFactory.content = content serverFactory.protocol = SendFDProtocol reactor.listenUNIX(address.path, serverFactory) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/server.pem0000644000175100001710000000345300000000000020273 0ustar00runnerdocker-----BEGIN CERTIFICATE----- MIICjzCCAfgCAQEwDQYJKoZIhvcNAQEEBQAwgY8xEDAOBgNVBAsTB2V4YW1wbGUx EDAOBgNVBAoTB2V4YW1wbGUxFDASBgNVBAMTC2V4YW1wbGUuY29tMRAwDgYDVQQI EwdleGFtcGxlMQswCQYDVQQGEwJVUzEiMCAGCSqGSIb3DQEJARYTZXhhbXBsZUBl eGFtcGxlLmNvbTEQMA4GA1UEBxMHZXhhbXBsZTAeFw0xNDAyMTIwMDMxMzlaFw0x NTAyMTIwMDMxMzlaMIGPMRAwDgYDVQQLEwdleGFtcGxlMRAwDgYDVQQKEwdleGFt cGxlMRQwEgYDVQQDEwtleGFtcGxlLmNvbTEQMA4GA1UECBMHZXhhbXBsZTELMAkG A1UEBhMCVVMxIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVAZXhhbXBsZS5jb20xEDAO BgNVBAcTB2V4YW1wbGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKkRahIc Fp0V44QYpO9ue7mjZNbZYPAC8caoQC1jUgL42CT40PcoOiZWLgRk+Qw6P7PoJzO/ T4ufK0qoPUJm1jErDRWy9eWlGLE0grPECM+jxFfLXxJLKdPtuwMA8Ip72JMirFN5 Y/JTBZOR3j5a/mbY5tcRqgffKxm4QQegnhiBAgMBAAEwDQYJKoZIhvcNAQEEBQAD gYEAWANPpp985nXMoIwHlsSMm8ijkk7XQU3oioCYDcM6pLT+mvBDe1mZc8mUlrWy Zo/lT6HF44SHIZ0zCgPYwTpWV6C0K+/kKlYBERZ3ajrzGf5ACfUTNyk5P81C68mc 9fQ7lhq1iuNKzVh8b746Z4ufn6iI1VygnyOQ1hZ/lOX56TA= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQCpEWoSHBadFeOEGKTvbnu5o2TW2WDwAvHGqEAtY1IC+Ngk+ND3 KDomVi4EZPkMOj+z6Cczv0+LnytKqD1CZtYxKw0VsvXlpRixNIKzxAjPo8RXy18S SynT7bsDAPCKe9iTIqxTeWPyUwWTkd4+Wv5m2ObXEaoH3ysZuEEHoJ4YgQIDAQAB AoGBAIHNSO6WehYolAD7GsZowL0J4YXCZ1ZeLFolGwC93F1DyE66aVUYoWyFhdcB 3uOwZPAvMMnd+6hqj8ZF3KJ6ab8eSq3hQQqm4EKHPN2DHHQPWppjWsFtUjG6P4FJ tuHu0JD0u10LxdyW240fcptdorAl+e9g6uphCpQ/9h5TgehBAkEA2iLbbp/MM1O8 Foz+JXY1FfNIgh67Di1BuG8asnX7lsPXI/HsNHPevvOxBbJLh0NEB4AW7Lva0pHH DaE+YPPLPQJBAMZqJ87+6iC8V3pRRDi+pfjZ8cRFBIJtbKMloP02/k9dEHeNepZO 5EcztYPZbgNC6U01ZD5Gyhnc9t5tPfSg5pUCQDASnH9NsifhnULvAZdp7JsQyXr7 oMeoC6LEwYJw4+g+8qvWRfLtUjqM5AdYWrLNjTGF9gdoAvqC6/ZCAchGEhUCQE/A P3v+DlFWIrsxiwBb8Q5TW9AOBb//B5mT+F+PCS0RNRs4rLtZvnu4Fw+GB6gb7vZv rXkyru0yWbARrMN1IPkCQQCXNEoKmNSgFOCruE6i7vjkdllkv+KdXQ13l305qlHC OzEHlEY+wXPu5R9v3tt2Ktfvfm2Mpfr9Gn+6CUXYn/+P -----END RSA PRIVATE KEY----- ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/shaper.py0000644000175100001710000000311400000000000020110 0ustar00runnerdocker# -*- Python -*- """Example of rate-limiting your web server. Caveat emptor: While the transfer rates imposed by this mechanism will look accurate with wget's rate-meter, don't forget to examine your network interface's traffic statistics as well. The current implementation tends to create lots of small packets in some conditions, and each packet carries with it some bytes of overhead. Check to make sure this overhead is not costing you more bandwidth than you are saving by limiting the rate! """ # for picklability import shaper from twisted.protocols import htb serverFilter = htb.HierarchicalBucketFilter() serverBucket = htb.Bucket() # Cap total server traffic at 20 kB/s serverBucket.maxburst = 20000 serverBucket.rate = 20000 serverFilter.buckets[None] = serverBucket # Web service is also limited per-host: class WebClientBucket(htb.Bucket): # Your first 10k is free maxburst = 10000 # One kB/s thereafter. rate = 1000 webFilter = htb.FilterByHost(serverFilter) webFilter.bucketFactory = shaper.WebClientBucket servertype = "web" # "chargen" if servertype == "web": from twisted.web import server, static site = server.Site(static.File("/var/www")) site.protocol = htb.ShapedProtocolFactory(site.protocol, webFilter) elif servertype == "chargen": from twisted.internet import protocol from twisted.protocols import wire site = protocol.ServerFactory() site.protocol = htb.ShapedProtocolFactory(wire.Chargen, webFilter) # site.protocol = wire.Chargen from twisted.internet import reactor reactor.listenTCP(8000, site) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/shoutcast.py0000644000175100001710000000102100000000000020636 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Example Shoutcast client. Run with: python shoutcast.py localhost 8080 """ import sys from twisted.internet import protocol, reactor from twisted.protocols.shoutcast import ShoutcastClient class Test(ShoutcastClient): def gotMetaData(self, data): print("meta:", data) def gotMP3Data(self, data): pass host = sys.argv[1] port = int(sys.argv[2]) protocol.ClientCreator(reactor, Test).connectTCP(host, port) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/simple.tac0000644000175100001710000000227500000000000020245 0ustar00runnerdocker# You can run this .tac file directly with: # twistd -ny simple.tac from twisted.application import internet, service from twisted.internet import protocol from twisted.protocols import wire from twisted.python import util application = service.Application("test") s = service.IServiceCollection(application) factory = protocol.ServerFactory() factory.protocol = wire.Echo internet.TCPServer(8080, factory).setServiceParent(s) internet.TCPServer(8081, factory).setServiceParent(s) internet.TimerService(5, util.println, "--MARK--").setServiceParent(s) class Foo(protocol.Protocol): def connectionMade(self): self.transport.write(b"lalala\n") def dataReceived(self, data): print(data) factory = protocol.ClientFactory() factory.protocol = Foo internet.TCPClient("localhost", 8081, factory).setServiceParent(s) class FooService(service.Service): def startService(self): service.Service.startService(self) print("lala, starting") def stopService(self): service.Service.stopService(self) print("lala, stopping") print(self.parent.getServiceNamed(self.name) is self) foo = FooService() foo.setName("foo") foo.setServiceParent(s) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/simpleclient.py0000644000175100001710000000227200000000000021322 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ An example client. Run simpleserv.py first before running this. """ from twisted.internet import protocol, reactor # a client protocol class EchoClient(protocol.Protocol): """Once connected, send a message, then print the result.""" def connectionMade(self): self.transport.write(b"hello, world!") def dataReceived(self, data): "As soon as any data is received, write it back." print("Server said:", data) self.transport.loseConnection() def connectionLost(self, reason): print("connection lost") class EchoFactory(protocol.ClientFactory): protocol = EchoClient def clientConnectionFailed(self, connector, reason): print("Connection failed - goodbye!") reactor.stop() def clientConnectionLost(self, connector, reason): print("Connection lost - goodbye!") reactor.stop() # this connects the protocol to a server running on port 8000 def main(): f = EchoFactory() reactor.connectTCP("localhost", 8000, f) reactor.run() # this only runs if the module was *not* imported if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/simpleserv.py0000644000175100001710000000114600000000000021022 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. from twisted.internet import protocol, reactor class Echo(protocol.Protocol): """This is just about the simplest possible protocol""" def dataReceived(self, data): "As soon as any data is received, write it back." self.transport.write(data) def main(): """This runs the protocol on port 8000""" factory = protocol.ServerFactory() factory.protocol = Echo reactor.listenTCP(8000, factory) reactor.run() # this only runs if the module was *not* imported if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ssl_clientauth_client.py0000644000175100001710000000201700000000000023206 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import echoclient from twisted.internet import defer, endpoints, protocol, ssl, task from twisted.python.modules import getModule @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(echoclient.EchoClient) certData = getModule(__name__).filePath.sibling("public.pem").getContent() authData = getModule(__name__).filePath.sibling("server.pem").getContent() clientCertificate = ssl.PrivateCertificate.loadPEM(authData) authority = ssl.Certificate.loadPEM(certData) options = ssl.optionsForClientTLS("example.com", authority, clientCertificate) endpoint = endpoints.SSL4ClientEndpoint(reactor, "localhost", 8000, options) echoClient = yield endpoint.connect(factory) done = defer.Deferred() echoClient.connectionLost = lambda reason: done.callback(None) yield done if __name__ == "__main__": import ssl_clientauth_client task.react(ssl_clientauth_client.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/ssl_clientauth_server.py0000644000175100001710000000150600000000000023240 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. import sys import echoserv from twisted.internet import defer, protocol, ssl, task from twisted.python import log from twisted.python.modules import getModule def main(reactor): log.startLogging(sys.stdout) certData = getModule(__name__).filePath.sibling("public.pem").getContent() authData = getModule(__name__).filePath.sibling("server.pem").getContent() authority = ssl.Certificate.loadPEM(certData) certificate = ssl.PrivateCertificate.loadPEM(authData) factory = protocol.Factory.forProtocol(echoserv.Echo) reactor.listenSSL(8000, factory, certificate.options(authority)) return defer.Deferred() if __name__ == "__main__": import ssl_clientauth_server task.react(ssl_clientauth_server.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/starttls_client.py0000644000175100001710000000222600000000000022047 0ustar00runnerdockerfrom twisted.internet import defer, endpoints, protocol, ssl, task from twisted.protocols.basic import LineReceiver from twisted.python.modules import getModule class StartTLSClient(LineReceiver): def connectionMade(self): self.sendLine(b"plain text") self.sendLine(b"STARTTLS") def lineReceived(self, line): print("received: ", line) if line == b"READY": self.transport.startTLS(self.factory.options) self.sendLine(b"secure text") self.transport.loseConnection() @defer.inlineCallbacks def main(reactor): factory = protocol.Factory.forProtocol(StartTLSClient) certData = getModule(__name__).filePath.sibling("server.pem").getContent() factory.options = ssl.optionsForClientTLS( "example.com", ssl.PrivateCertificate.loadPEM(certData) ) endpoint = endpoints.HostnameEndpoint(reactor, "localhost", 8000) startTLSClient = yield endpoint.connect(factory) done = defer.Deferred() startTLSClient.connectionLost = lambda reason: done.callback(None) yield done if __name__ == "__main__": import starttls_client task.react(starttls_client.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/starttls_server.py0000644000175100001710000000156200000000000022101 0ustar00runnerdockerfrom twisted.internet import defer, endpoints, protocol, ssl, task from twisted.protocols.basic import LineReceiver from twisted.python.modules import getModule class TLSServer(LineReceiver): def lineReceived(self, line): print("received: ", line) if line == b"STARTTLS": print("-- Switching to TLS") self.sendLine(b"READY") self.transport.startTLS(self.factory.options) def main(reactor): certData = getModule(__name__).filePath.sibling("server.pem").getContent() cert = ssl.PrivateCertificate.loadPEM(certData) factory = protocol.Factory.forProtocol(TLSServer) factory.options = cert.options() endpoint = endpoints.TCP4ServerEndpoint(reactor, 8000) endpoint.listen(factory) return defer.Deferred() if __name__ == "__main__": import starttls_server task.react(starttls_server.main) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/stdin.py0000644000175100001710000000123200000000000017746 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ An example of reading a line at a time from standard input without blocking the reactor. """ from os import linesep from twisted.internet import stdio from twisted.protocols import basic class Echo(basic.LineReceiver): delimiter = linesep.encode("ascii") def connectionMade(self): self.transport.write(b">>> ") def lineReceived(self, line): self.sendLine(b"Echo: " + line) self.transport.write(b">>> ") def main(): stdio.StandardIO(Echo()) from twisted.internet import reactor reactor.run() if __name__ == "__main__": main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/stdiodemo.py0000644000175100001710000000543500000000000020625 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Example using stdio, Deferreds, LineReceiver and twisted.web.client. Note that the WebCheckerCommandProtocol protocol could easily be used in e.g. a telnet server instead; see the comments for details. Based on an example by Abe Fettig. """ from twisted.internet import reactor, stdio from twisted.protocols import basic from twisted.web import client class WebCheckerCommandProtocol(basic.LineReceiver): delimiter = b"\n" # unix terminal style newlines. remove this line # for use with Telnet def connectionMade(self): self.sendLine(b"Web checker console. Type 'help' for help.") def lineReceived(self, line): # Ignore blank lines if not line: return line = line.decode("ascii") # Parse the command commandParts = line.split() command = commandParts[0].lower() args = commandParts[1:] # Dispatch the command to the appropriate method. Note that all you # need to do to implement a new command is add another do_* method. try: method = getattr(self, "do_" + command) except AttributeError: self.sendLine(b"Error: no such command.") else: try: method(*args) except Exception as e: self.sendLine(b"Error: " + str(e).encode("ascii")) def do_help(self, command=None): """help [command]: List commands, or show help on the given command""" if command: doc = getattr(self, "do_" + command).__doc__ self.sendLine(doc.encode("ascii")) else: commands = [ cmd[3:].encode("ascii") for cmd in dir(self) if cmd.startswith("do_") ] self.sendLine(b"Valid commands: " + b" ".join(commands)) def do_quit(self): """quit: Quit this session""" self.sendLine(b"Goodbye.") self.transport.loseConnection() def do_check(self, url): """check : Attempt to download the given web page""" url = url.encode("ascii") client.Agent(reactor).request(b"GET", url).addCallback( client.readBody ).addCallback(self.__checkSuccess).addErrback(self.__checkFailure) def __checkSuccess(self, pageData): msg = f"Success: got {len(pageData)} bytes." self.sendLine(msg.encode("ascii")) def __checkFailure(self, failure): msg = "Failure: " + failure.getErrorMessage() self.sendLine(msg.encode("ascii")) def connectionLost(self, reason): # stop the reactor, only because this is meant to be run in Stdio. reactor.stop() if __name__ == "__main__": stdio.StandardIO(WebCheckerCommandProtocol()) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/streaming.py0000644000175100001710000000617700000000000020633 0ustar00runnerdocker#!/usr/bin/env python # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ This is a sample implementation of a Twisted push producer/consumer system. It consists of a TCP server which asks the user how many random integers they want, and it sends the result set back to the user, one result per line, and finally closes the connection. """ from random import randrange from sys import stdout from zope.interface import implementer from twisted.internet import interfaces, reactor from twisted.internet.protocol import Factory from twisted.protocols.basic import LineReceiver from twisted.python.log import startLogging @implementer(interfaces.IPushProducer) class Producer: """ Send back the requested number of random integers to the client. """ def __init__(self, proto, count): self._proto = proto self._goal = count self._produced = 0 self._paused = False def pauseProducing(self): """ When we've produced data too fast, pauseProducing() will be called (reentrantly from within resumeProducing's sendLine() method, most likely), so set a flag that causes production to pause temporarily. """ self._paused = True print(f"Pausing connection from {self._proto.transport.getPeer()}") def resumeProducing(self): """ Resume producing integers. This tells the push producer to (re-)add itself to the main loop and produce integers for its consumer until the requested number of integers were returned to the client. """ self._paused = False while not self._paused and self._produced < self._goal: next_int = randrange(0, 10000) line = f"{next_int}" self._proto.sendLine(line.encode("ascii")) self._produced += 1 if self._produced == self._goal: self._proto.transport.unregisterProducer() self._proto.transport.loseConnection() def stopProducing(self): """ When a consumer has died, stop producing data for good. """ self._produced = self._goal class ServeRandom(LineReceiver): """ Serve up random integers. """ def connectionMade(self): """ Once the connection is made we ask the client how many random integers the producer should return. """ print(f"Connection made from {self.transport.getPeer()}") self.sendLine(b"How many random integers do you want?") def lineReceived(self, line): """ This checks how many random integers the client expects in return and tells the producer to start generating the data. """ count = int(line.strip()) print(f"Client requested {count} random integers!") producer = Producer(self, count) self.transport.registerProducer(producer, True) producer.resumeProducing() def connectionLost(self, reason): print(f"Connection lost from {self.transport.getPeer()}") startLogging(stdout) factory = Factory() factory.protocol = ServeRandom reactor.listenTCP(1234, factory) reactor.run() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/testlogging.py0000644000175100001710000000133600000000000021160 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """Test logging. Message should only be printed second time around. """ import sys import warnings from twisted.internet import reactor from twisted.python import log def test(i): print("printed", i) log.msg(f"message {i}") warnings.warn(f"warning {i}") try: raise RuntimeError(f"error {i}") except BaseException: log.err() def startlog(): log.startLogging(sys.stdout) def end(): reactor.stop() # pre-reactor run test(1) # after reactor run reactor.callLater(0.1, test, 2) reactor.callLater(0.2, startlog) # after startLogging reactor.callLater(0.3, test, 3) reactor.callLater(0.4, end) reactor.run() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4779444 Twisted-22.1.0/docs/core/examples/threadedselect/0000755000175100001710000000000000000000000021235 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4579442 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/0000755000175100001710000000000000000000000022261 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4779444 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/0000755000175100001710000000000000000000000025307 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4579442 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/0000755000175100001710000000000000000000000030025 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4779444 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/0000755000175100001710000000000000000000000032305 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000021500000000000010213 xustar00119 path=Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/classes.nib 22 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/cl0000644000175100001710000000061300000000000032626 0ustar00runnerdocker{ IBClasses = ( {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { ACTIONS = {doTwistzillaFetch = id; }; CLASS = MyAppDelegate; LANGUAGE = ObjC; OUTLETS = {messageTextField = id; progressIndicator = id; resultTextField = id; }; SUPERCLASS = NSObject; } ); IBVersion = 1; }././@PaxHeader0000000000000000000000000000021200000000000010210 xustar00116 path=Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/info.nib 22 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/in0000644000175100001710000000110600000000000032634 0ustar00runnerdocker IBEditorPositions 29 127 344 318 44 0 0 1600 1002 IBFramework Version 291.0 IBLockedObjects 204 IBOpenObjects 21 29 IBSystem Version 6L60 ././@PaxHeader0000000000000000000000000000022200000000000010211 xustar00124 path=Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/keyedobjects.nib 22 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/threadedselect/Cocoa/SimpleWebClient/English.lproj/MainMenu.nib/ke0000644000175100001710000003506000000000000032633 0ustar00runnerdockerbplist00Y$archiverX$versionX$objectsT$top_NSKeyedArchiver (,07:<AYZ[\^emvw !"#$%&'()-.7>ABFHLNPWx`bcefilmnq-rxys~   #$%'*-./148;<=?BEIJLPTXYZ\_abdgklmnqtluvy{|}%()*-./24589:;=?BCDHLMNOQRSVMWXZ]M^_abefghkMklnqtuv:Myz}M~MMM1         L ~U6U$null  !"#$%&']NSFontManagerV$classYNSNextOid_NSVisibleWindowsVNSRoot\NSOidsValues_NSClassesValues]NSConnections]NSNamesValues]NSObjectsKeysZNSOidsKeys]NSClassesKeys[NSFramework[NSNamesKeys_NSObjectsValuesԀy`_^ )*+[NSClassName -./YNS.string]NSApplication1234Z$classnameX$classes_NSMutableString356XNSStringXNSObject1289^NSCustomObject86 -.;_IBCocoaFramework =>?ZNS.objectsA@B CDEFGHIJKLMNOPQRSTUVWX\NSWindowViewYNSWTFlags]NSWindowTitleYNSMinSizeYNSMaxSize\NSWindowRect_NSWindowStyleMask_NSWindowBacking\NSScreenRect]NSWindowClass[NSViewClass xpx vw u _{{335, 343}, {684, 424}}ZTwistzillaXNSWindow -.]TView _`abcd_NSNextResponderZNSSubviewsWNSFramets =fgIhijkl +0l _noapqrMsUtuMVNSCellXNSvFlagsYNSEnabled[NSSuperview _{{72, 20}, {488, 22}} xyz{|}~hu_NSBackgroundColorYNSSupport]NSControlView\NSCellFlags2[NSTextColor_NSDrawsBackground[NSCellFlagsZNSContents@qAP VNSSizeVNSNameXNSfFlags"AP\LucidaGrande12VNSFont6 WNSColor]NSCatalogName[NSColorName\NSColorSpaceVSystem_textBackgroundColor WNSWhiteB1126 YtextColor B012_NSTextFieldCelln6\NSActionCell12[NSTextField6\%NSTextFieldYNSControlVNSView[NSResponder _napqrMuM"!_{{17, 22}, {53, 14}} xyz{|~i&$)#XAddress: "A0% ('\controlColor I0.666667 *_controlTextColor _oaqMMZNSMaxValue\NSDrawMatrixYNSpiFlags/#@Y,0 . ڀ-12ZNSPSMatrix6_{{568, 20}, {16, 16}}12_NSProgressIndicator6 _`oaqMM]NSContentView[NSVScrollerXNSsFlags]NSNextKeyView[NSHScrollerk12cjg =f _`oaqkkXNSCursorYNScvFlagsYNSBGColorXNSBoundsYNSDocViewb3[ 4YZ =f _Foaq     YNSTVFlagsZNSDelegateXNSMinize_NSTextContainer\NSSharedData[NSDragTypesXVBWCL5 =>6789:;<=>?@_*NeXT Rich Text Format v1.0 pasteboard type_Apple HTML pasteboard type_NSStringPboardType_NSColor pasteboard type_Apple PDF pasteboard type_NeXT TIFF v4.0 pasteboard type_#CorePasteboardFlavorType 0x6D6F6F76_NSFilenamesPboardType_NeXT RTFD pasteboard type_Apple PICT pasteboard type_1NeXT Encapsulated PostScript v1.2 pasteboard type12*+\NSMutableSet*,6UNSSet_{{0, 24}, {631, 344}} /0123456WNSWidth_NSLayoutManagerZNSTextViewYNSTCFlagsK"DD 89:;<=]NSTextStorage_NSTextContainersYNSLMFlagsJEH5 ?@FG -.128C8DE6_NSMutableAttributedString_NSAttributedString =fG12IJ^NSMutableArrayIK6WNSArray120M0612O6 xQRSTUV_NSSelectedAttributes_NSInsertionColorWNSFlagsUM =XYZ]WNS.keysT[\PR^_NO aQ_selectedTextBackgroundColor dS_selectedTextColor12gh\NSDictionaryg612jk_NSTextViewSharedDataj6\{631, 1e+07}Y{424, 54}121o1p6VNSText_{{1, 1}, {631, 344}} stuvwWNSImageYNSHotSpota]\W{4, -5} )z{|}^NSResourceName`^_]NSIBeamCursor12_NSCustomResource6_%NSCustomResource12612ZNSClipView6 _oaqkkkXNSActionXNSTargetZNSCurValuefe"?d_{{632, 1}, {11, 344}}\_doScroller:12ZNSScroller6 _oaqkkkYNSPercentih"?rC_{{-100, -100}, {87, 18}}_{{20, 58}, {644, 346}}12\NSScrollView6 _noapqMUuMrnm_{{586, 12}, {84, 32}} yz{~l6_NSKeyEquivalent_NSAlternateContents_NSPeriodicDelay^NSButtonFlags2]NSButtonFlags_NSPeriodicInterval_NSAlternateImageqp@oUFetchQ 12\NSButtonCelln6]%NSButtonCell12XNSButton6_{{1, 9}, {684, 424}}126_{{0, 0}, {1600, 1002}}Z{213, 129}_{3.40282e+38, 3.40282e+38}12_NSWindowTemplate6 =fȯzÀȀ̀Ҁ׀݀ WNSLabelXNSSource{ ]NSMnemonicLoc\NSMixedImage_NSKeyEquivModMaskWNSTitleVNSMenuZNSKeyEquivYNSOnImage}|~ [NSMenuItems XMinimizeQm )z{|_NSMenuCheckmark )z{|_NSMenuMixedState12  ZNSMenuItem 6 -. _performMiniaturize:12_NSNibControlConnector6^NSNibConnector  _Bring All to Front -._arrangeInFront:   !"XPrint...Qp -.&Vprint: () +,]Page Setup...QP -.0^runPageLayout: 23 567 9:  _NewApplication HelpQ? -.>YshowHelp: @A CD FGH !ZClear Menu -.K_clearRecentDocuments: MNO]NSDestination QRS UVWTVS_Quit NewApplicationQq -.[Zterminate: M]^ `R_About NewApplication -.c_orderFrontStandardAboutPanel: Mef hiRj[Hide OthersQh_hideOtherApplications: Mop rRs_Hide NewApplicationUhide: Mwx zRXShow All_unhideAllApplications: ~  76SCutQx -.Tcut: € UPasteQv -.Vpaste: ǀ ŀTRedoQZ -.Uredo: ̀ ʀZSelect AllQa -.ZselectAll: р πTUndoQz -.Uundo: ր ԀTCopyQc -.Ucopy: ܀ ڀـ Ɓ*)[Spelling...Q: -._showGuessPanel: ̀ ߀^Check SpellingQ; -.^checkSpelling: ׀ _Check Spelling as You Type_toggleContinuousSpellChecking: ހ UCloseQw]performClose:  VDeleteWdelete: Mh )*]MyAppDelegate_messageTextField12_NSNibOutletConnector6 MXdelegate M_resultTextField Mj_progressIndicator Ml_doTwistzillaFetch: =]E  6 A  DMkhil)jRp^O@3 !f"#$x &+/23:KL%,?"W9OEH8UB &'SNewQn +R,^Preferences...Q, 01uu\NSIsDisabled]NSIsSeparator 3VRevert 67ZSave As...QSTHelp =f<312>6 @A WOpen...Qo EFG"YNSSubmenu IJK[\ZVWindow^submenuAction: -.L =fP  01uu^_NSWindowsMenu ETU"TFile -.V =fY   ) ED[\[Open Recent -.] =f`A__NSRecentDocumentsMenu cd#$TSaveQs 01uu Eij'(XSpelling =fm op-. rs>=WFind...Qf E6wx"01 01uuR E{|"45TEdit -.} =f! 01uu 01uu E;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}ÁāŁƁǁȁɁʁˁ́́΁ρЁсҁӁԁՁցׁ؁فځہ܁݁ށ߁|NRWp:'\oJHMS9}OzgIK8%PQ~jV12^NSIBObjectData6]IB.objectdata#,5:LQAG!-9KMPRTVY\^acfiknqz .7BDGIz  "$&(CNW`ev  #H\ft     ! & ; C Q ] j l n p r t {    & 2 ? L V ] i     ! + @ B U v  : H T ] k w y { }        G Q \ e w !6Pl8ANU[s"+4Penqz "')+@B`uw!)3579AN]_acqz !#;HQ\g4i{)7@ITkt{!#%')+-/13579;=?ACEGIKMOQSUbjsuwy #&),57DFXegz2;MZ\^ "CEGIVY\rt}"$&GIKM^adg}=BDFRTm~*,.OQSUbehlnw|#%'HJLWYbmz|~79;=JMP\^gy  )JWY[|~  % < C T V _ p r !l!n!q!t!w!z!}!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"""")"+"T"a"o"""""""""""""# # ####C#M#P#S#d#g#j#m#t#######$$$$$$1$Z$]$`$l$u$~$$$$$$$$%%% %)%2%9%Z%]%`%m%p%s%{%}%%%%%&&& &&&2&[&&&&&&&&&&&''')','/'='?'`'c'f''''''''((((!(2(5(8(;(D(M(N(`(o(x((((((())))$)))2))*1*:*******************************++++ + +++++++!+$+'+*+-+0+4+=+?+H+K+T+V+_+a+d+p+y+++++++++++++++,,,, ,-,6,8,E,N,[,h,t,},,,,-i-r.7.:.=.@.C.F.I.L.O.R.U.X.[.^.a.d.g.j.m.p.s.v.y.|...........................................//// / ///////!/$/'/*/-/0/3/6/9/", self.port)) log.msg(b"SEND " + pingMsg) def datagramReceived(self, datagram, addr): if datagram[:4] == b"PING": uuid = datagram[5:] pongMsg = f"PONG {uuid}" pongMsg = pongMsg.encode("ascii") self.transport.write(pongMsg, ("", self.port)) log.msg(b"RECV " + datagram) elif datagram[:4] == b"PONG": log.msg(b"RECV " + datagram) class Broadcaster: def ping(self, proto): proto.sendPing() def makeService(self): application = service.Application("Broadcaster") root = service.MultiService() root.setServiceParent(application) proto = PingPongProtocol(controller=self, port=8555) root.addService(internet.UDPServer(8555, proto)) root.addService(internet.TimerService(1, self.ping, proto)) return application application = Broadcaster().makeService() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/wxacceptance.py0000644000175100001710000000633200000000000021300 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Acceptance tests for wxreactor. Please test on Linux, Win32 and macOS: 1. Startup event is called at startup. 2. Scheduled event is called after 2 seconds. 3. Shutdown takes 3 seconds, both when quiting from menu and when closing window (e.g. Alt-F4 in metacity). This tests reactor.stop() and wxApp.ExitEventLoop(). 4. 'hello, world' continues to be printed even when modal dialog is open (use dialog menu item), when menus are held down, when window is being dragged. """ import sys import time try: from wx import ( EVT_MENU, App as wxApp, DefaultPosition as wxDefaultPosition, Frame as wxFrame, Menu as wxMenu, MenuBar as wxMenuBar, MessageDialog as wxMessageDialog, Size as wxSize, ) except ImportError: from wxPython.wx import * from twisted.internet import wxreactor from twisted.python import log wxreactor.install() from twisted.internet import defer, reactor # set up so that "hello, world" is printed continuously dc = None def helloWorld(): global dc print("hello, world", time.time()) dc = reactor.callLater(0.1, helloWorld) dc = reactor.callLater(0.1, helloWorld) def twoSecondsPassed(): print("two seconds passed") def printer(s): print(s) def shutdown(): print("shutting down in 3 seconds") if dc.active(): dc.cancel() reactor.callLater(1, printer, "2...") reactor.callLater(2, printer, "1...") reactor.callLater(3, printer, "0...") d = defer.Deferred() reactor.callLater(3, d.callback, 1) return d def startup(): print("Start up event!") reactor.callLater(2, twoSecondsPassed) reactor.addSystemEventTrigger("after", "startup", startup) reactor.addSystemEventTrigger("before", "shutdown", shutdown) ID_EXIT = 101 ID_DIALOG = 102 class MyFrame(wxFrame): def __init__(self, parent, ID, title): wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(300, 200)) menu = wxMenu() menu.Append(ID_DIALOG, "D&ialog", "Show dialog") menu.Append(ID_EXIT, "E&xit", "Terminate the program") menuBar = wxMenuBar() menuBar.Append(menu, "&File") self.SetMenuBar(menuBar) EVT_MENU(self, ID_EXIT, self.DoExit) EVT_MENU(self, ID_DIALOG, self.DoDialog) # you really ought to do this instead of reactor.stop() in # DoExit, but for the sake of testing we'll let closing the # window shutdown wx without reactor.stop(), to make sure that # still does the right thing. # EVT_CLOSE(self, lambda evt: reactor.stop()) def DoDialog(self, event): dl = wxMessageDialog( self, "Check terminal to see if messages are still being " "printed by Twisted.", ) dl.ShowModal() dl.Destroy() def DoExit(self, event): reactor.stop() class MyApp(wxApp): def OnInit(self): frame = MyFrame(None, -1, "Hello, world") frame.Show(True) self.SetTopWindow(frame) return True def demo(): log.startLogging(sys.stdout) app = MyApp(0) reactor.registerWxApp(app) reactor.run() if __name__ == "__main__": demo() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/examples/wxdemo.py0000644000175100001710000000277200000000000020142 0ustar00runnerdocker# Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """Demo of wxPython integration with Twisted.""" import sys from wx import EVT_CLOSE, EVT_MENU, App, DefaultPosition, Frame, Menu, MenuBar, Size from twisted.internet import wxreactor from twisted.python import log wxreactor.install() # import t.i.reactor only after installing wxreactor: from twisted.internet import reactor ID_EXIT = 101 class MyFrame(Frame): def __init__(self, parent, ID, title): Frame.__init__(self, parent, ID, title, DefaultPosition, Size(300, 200)) menu = Menu() menu.Append(ID_EXIT, "E&xit", "Terminate the program") menuBar = MenuBar() menuBar.Append(menu, "&File") self.SetMenuBar(menuBar) EVT_MENU(self, ID_EXIT, self.DoExit) # make sure reactor.stop() is used to stop event loop: EVT_CLOSE(self, lambda evt: reactor.stop()) def DoExit(self, event): reactor.stop() class MyApp(App): def twoSecondsPassed(self): print("two seconds passed") def OnInit(self): frame = MyFrame(None, -1, "Hello, world") frame.Show(True) self.SetTopWindow(frame) # look, we can use twisted calls! reactor.callLater(2, self.twoSecondsPassed) return True def demo(): log.startLogging(sys.stdout) # register the App instance with Twisted: app = MyApp(0) reactor.registerWxApp(app) # start the event loop: reactor.run() if __name__ == "__main__": demo() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1644239601.4819446 Twisted-22.1.0/docs/core/howto/0000755000175100001710000000000000000000000015577 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/amp.rst0000644000175100001710000001354600000000000017117 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Asynchronous Messaging Protocol Overview ======================================== The purpose of this guide is to describe the uses for and usage of :py:mod:`twisted.protocols.amp` beyond what is explained in the API documentation. It will show you how to implement an AMP server which can respond to commands or interact directly with individual messages. It will also show you how to implement an AMP client which can issue commands to a server. AMP is a bidirectional command/response-oriented protocol intended to be extended with application-specific request types and handlers. Various simple data types are supported and support for new data types can be added by applications. Setting Up ---------- AMP runs over a stream-oriented connection-based protocol, such as TCP or SSL. Before you can use any features of the AMP protocol, you need a connection. The protocol class to use to establish an AMP connection is :py:class:`AMP ` . Connection setup works as it does for almost all protocols in Twisted. For example, you can set up a listening AMP server using a server endpoint: :download:`basic_server.tac ` .. literalinclude:: listings/amp/basic_server.tac And you can connect to an AMP server using a client endpoint: :download:`basic_client.py ` .. literalinclude:: listings/amp/basic_client.py Commands -------- Either side of an AMP connection can issue a command to the other side. Each kind of command is represented as a subclass of :py:class:`Command ` . A ``Command`` defines arguments, response values, and error conditions. .. code-block:: python from twisted.protocols.amp import Integer, String, Unicode, Command class UsernameUnavailable(Exception): pass class RegisterUser(Command): arguments = [('username', Unicode()), ('publickey', String())] response = [('uid', Integer())] errors = {UsernameUnavailable: 'username-unavailable'} The definition of the command's signature - its arguments, response, and possible error conditions - is separate from the implementation of the behavior to execute when the command is received. The ``Command`` subclass only defines the former. Commands are issued by calling ``callRemote`` on either side of the connection. This method returns a ``Deferred`` which eventually fires with the result of the command. :download:`command_client.py ` .. literalinclude:: listings/amp/command_client.py Locators -------- The logic for handling a command can be specified as an object separate from the ``AMP`` instance which interprets and formats bytes over the network. .. code-block:: python from twisted.protocols.amp import CommandLocator from twisted.python.filepath import FilePath class UsernameUnavailable(Exception): pass class UserRegistration(CommandLocator): uidCounter = 0 @RegisterUser.responder def register(self, username, publickey): path = FilePath(username) if path.exists(): raise UsernameUnavailable() self.uidCounter += 1 path.setContent('%d %s\n' % (self.uidCounter, publickey)) return self.uidCounter When you define a separate ``CommandLocator`` subclass, use it by passing an instance of it to the ``AMP`` initializer. .. code-block:: python factory = Factory() factory.protocol = lambda: AMP(locator=UserRegistration()) If no locator is passed in, ``AMP`` acts as its own locator. Command responders can be defined on an ``AMP`` subclass, just as the responder was defined on the ``UserRegistration`` example above. Box Receivers ------------- AMP conversations consist of an exchange of messages called *boxes* . A *box* consists of a sequence of pairs of key and value (for example, the pair ``username`` and ``alice`` ). Boxes are generally represented as ``dict`` instances. Normally boxes are passed back and forth to implement the command request/response features described above. The logic for handling each box can be specified as an object separate from the ``AMP`` instance. .. code-block:: python from zope.interface import implementer from twisted.protocols.amp import IBoxReceiver @implementer(IBoxReceiver) class BoxReflector(object): def startReceivingBoxes(self, boxSender): self.boxSender = boxSender def ampBoxReceived(self, box): self.boxSender.sendBox(box) def stopReceivingBoxes(self, reason): self.boxSender = None These methods parallel those of ``IProtocol`` . Startup notification is given by ``startReceivingBoxes`` . The argument passed to it is an ``IBoxSender`` provider, which can be used to send boxes back out over the network. ``ampBoxReceived`` delivers notification for a complete box having been received. And last, ``stopReceivingBoxes`` notifies the object that no more boxes will be received and no more can be sent. The argument passed to it is a ``Failure`` which may contain details about what caused the conversation to end. To use a custom ``IBoxReceiver`` , pass it to the ``AMP`` initializer. .. code-block:: python factory = Factory() factory.protocol = lambda: AMP(boxReceiver=BoxReflector()) If no box receiver is passed in, ``AMP`` acts as its own box receiver. It handles boxes by treating them as command requests or responses and delivering them to the appropriate responder or as a result to a ``callRemote`` ``Deferred`` . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/application.rst0000644000175100001710000002404400000000000020640 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Using the Twisted Application Framework ======================================= Introduction ------------ Audience ~~~~~~~~ The target audience of this document is a Twisted user who wants to deploy a significant amount of Twisted code in a re-usable, standard and easily configurable fashion. A Twisted user who wishes to use the Application framework needs to be familiar with developing Twisted :doc:`servers ` and/or :doc:`clients `. Goals ~~~~~ - To introduce the Twisted Application infrastructure. - To explain how to deploy your Twisted application using ``.tac`` files and ``twistd``. - To outline the existing Twisted services. Overview -------- The Twisted Application infrastructure takes care of running and stopping your application. Using this infrastructure frees you from from having to write a large amount of boilerplate code by hooking your application into existing tools that manage daemonization, logging, :doc:`choosing a reactor ` and more. The major tool that manages Twisted applications is a command-line utility called ``twistd``. ``twistd`` is cross platform, and is the recommended tool for running Twisted applications. The core component of the Twisted Application infrastructure is the :py:func:`twisted.application.service.Application` object -- an object which represents your application. However, Application doesn't provide anything that you'd want to manipulate directly. Instead, Application acts as a container of any "Services" (objects implementing :py:class:`IService `) that your application provides. Most of your interaction with the Application infrastructure will be done through Services. By "Service", we mean anything in your application that can be started and stopped. Typical services include web servers, FTP servers and SSH clients. Your Application object can contain many services, and can even contain structured hierarchies of Services using :py:class:`MultiService ` or your own custom :py:class:`IServiceCollection ` implementations. You will most likely want to use these to manage Services which are dependent on other Services. For example, a proxying Twisted application might want its server Service to only start up after the associated Client service. An :py:class:`IService ` has two basic methods, ``startService()`` which is used to start the service, and ``stopService()`` which is used to stop the service. The latter can return a :py:class:`Deferred `, indicating service shutdown is not over until the result fires. For example: .. code-block:: python from twisted.internet import reactor from twisted.application import service from somemodule import EchoFactory class EchoService(service.Service): def __init__(self, portNum): self.portNum = portNum def startService(self): self._port = reactor.listenTCP(self.portNum, EchoFactory()) def stopService(self): return self._port.stopListening() See :doc:`Writing Servers ` for an explanation of ``EchoFactory`` and ``listenTCP``. Using Services and Application ------------------------------ twistd and tac ~~~~~~~~~~~~~~ .. _core-howto-application-twistd: To handle start-up and configuration of your Twisted application, the Twisted Application infrastructure uses ``.tac`` files. ``.tac`` are Python files which configure an :py:func:`Application ` object and assign this object to the top-level variable "``application``" . The following is a simple example of a ``.tac`` file: :download:`service.tac ` .. literalinclude:: listings/application/service.tac ``twistd`` is a program that runs Twisted applications using a ``.tac`` file. In its most simple form, it takes a single argument ``-y`` and a tac file name. For example, you can run the above server with the command ``twistd -y service.tac``. By default, ``twistd`` daemonizes and logs to a file called ``twistd.log``. More usually, when debugging, you will want your application to run in the foreground and log to the command line. To run the above file like this, use the command ``twistd -noy service.tac``. For more information, see the ``twistd`` man page. Customizing ``twistd`` logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``twistd`` logging can be customized using the command line. This requires that a *log observer factory* be importable. Given a file named ``my.py`` with the code: .. code-block:: python from twisted.logger import textFileLogObserver def logger(): return textFileLogObserver(open("/tmp/my.log", "w")) Invoking ``twistd --logger my.logger ...`` will log to a file named ``/tmp/my.log`` (this simple example could easily be replaced with use of the ``--logfile`` parameter to twistd). Alternatively, the logging behavior can be customized through an API accessible from ``.tac`` files. The :py:class:`ILogObserver ` component can be set on an Application in order to customize the default log observer that ``twistd`` will use. Here is an example of how to use :py:class:`DailyLogFile `, which rotates the log once per day. .. code-block:: python from twisted.application.service import Application from twisted.logger import ILogObserver, textFileLogObserver from twisted.python.logfile import DailyLogFile application = Application("myapp") logfile = DailyLogFile("my.log", "/tmp") application.setComponent(ILogObserver, textFileLogObserver(logfile)) Invoking ``twistd -y my.tac`` will create a log file at ``/tmp/my.log``. Services provided by Twisted ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Twisted also provides pre-written :py:class:`IService ` implementations for common cases like listening on a TCP port, in the :py:mod:`twisted.application.internet` module. Here's a simple example of constructing a service that runs an echo server on TCP port 7001: .. code-block:: python from twisted.application import internet, service from somemodule import EchoFactory port = 7001 factory = EchoFactory() echoService = internet.TCPServer(port, factory) # create the service Each of these services (except TimerService) has a corresponding "connect" or "listen" method on the reactor, and the constructors for the services take the same arguments as the reactor methods. The "connect" methods are for clients and the "listen" methods are for servers. For example, ``TCPServer`` corresponds to ``reactor.listenTCP`` and ``TCPClient`` corresponds to ``reactor.connectTCP``. ``TCPServer`` ``TCPClient`` Services which allow you to make connections and listen for connections on TCP ports. - :py:meth:`listenTCP ` - :py:meth:`connectTCP ` ``UNIXServer`` ``UNIXClient`` Services which listen and make connections over UNIX sockets. - :py:meth:`listenUNIX ` - :py:meth:`connectUNIX ` ``SSLServer`` ``SSLClient`` Services which allow you to make SSL connections and run SSL servers. - :py:meth:`listenSSL ` - :py:meth:`connectSSL ` ``UDPServer`` A service which allows you to send and receive data over UDP. - :py:meth:`listenUDP ` See also the :doc:`UDP documentation `. ``UNIXDatagramServer`` ``UNIXDatagramClient`` Services which send and receive data over UNIX datagram sockets. - :py:meth:`listenUNIXDatagram ` - :py:meth:`connectUNIXDatagram ` ``MulticastServer`` A server for UDP socket methods that support multicast. - :py:meth:`listenMulticast ` ``TimerService`` A service to periodically call a function. - :py:class:`TimerService ` Service Collection ~~~~~~~~~~~~~~~~~~ :py:class:`IServiceCollection ` objects contain :py:class:`IService ` objects. IService objects can be added to IServiceCollection by calling :py:meth:`setServiceParent ` and detached by using :py:meth:`disownServiceParent `. The standard implementation of IServiceCollection is :py:class:`MultiService `, which also implements IService. MultiService is useful for creating a new Service which combines two or more existing Services. For example, you could create a DNS Service as a MultiService which has a TCP and a UDP Service as children. .. code-block:: python from twisted.application import internet, service from twisted.names import server, dns, hosts port = 53 # Create a MultiService, and hook up a TCPServer and a UDPServer to it as # children. dnsService = service.MultiService() hostsResolver = hosts.Resolver('/etc/hosts') tcpFactory = server.DNSServerFactory([hostsResolver]) internet.TCPServer(port, tcpFactory).setServiceParent(dnsService) udpFactory = dns.DNSDatagramProtocol(tcpFactory) internet.UDPServer(port, udpFactory).setServiceParent(dnsService) # Create an application as normal application = service.Application("DNSExample") # Connect our MultiService to the application, just like a normal service. dnsService.setServiceParent(application) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/basics.rst0000644000175100001710000000511600000000000017600 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ The Basics ========== Application ----------- Twisted programs usually work with :py:func:`twisted.application.service.Application`. This class usually holds all persistent configuration of a running server, such as: - ports to bind to, - places where connections to must be kept or attempted, - periodic actions to do, - and almost everything else to do with your :py:func:`Application `. It is the root object in a tree of services implementing :py:class:`twisted.application.service.IService`. Other howtos describe how to write custom code for ``Application``\ s, but this one describes how to use already written code (which can be part of Twisted or from a third-party Twisted plugin developer). The Twisted distribution comes with an important tool to deal with ``Application``\ s: ``twistd(1)``. ``Application``\ s are just Python objects, which can be created and manipulated in the same ways as any other object. twistd ------ The Twisted Daemon is a program that knows how to run :py:func:`Application `\ s. Strictly speaking, ``twistd`` is not necessary. Fetching the application, getting the ``IService`` component, calling ``startService()``, scheduling ``stopService()`` when the reactor shuts down, and then calling ``reactor.run()`` could be done manually. However, ``twistd`` supplies many options which are highly useful for program set up: - choosing a reactor (for more on reactors, see :doc:`Choosing a Reactor `), - logging configuration (see the :doc:`logger ` documentation for more), - daemonizing (forking to the background), - and :doc:`more `. ``twistd`` supports all Applications mentioned above -- and an additional one. Sometimes it is convenient to write the code for building a class in straight Python. One big source of such Python files is the :doc:`examples <../examples/index>` directory. When a straight Python file which defines an ``Application`` object called ``application`` is used, use the ``-y`` option. When ``twistd`` runs, it records its process id in a ``twistd.pid`` file (this can be configured via a command line switch). In order to shutdown the ``twistd`` process, kill that pid. The usual way to do this would be:: kill `cat twistd.pid` To prevent ``twistd`` from daemonizing, you can pass it the ``--no-daemon`` option (or ``-n``, in conjunction with other short options). As always, the gory details are in the manual page. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/choosing-reactor.rst0000644000175100001710000003310200000000000021576 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Choosing a Reactor and GUI Toolkit Integration ============================================== Overview -------- Twisted provides a variety of implementations of the :py:mod:`twisted.internet.reactor` . The specialized implementations are suited for different purposes and are designed to integrate better with particular platforms. The :ref:`epoll()-based reactor ` is Twisted's default on Linux. Other platforms use :ref:`poll() ` , or the most cross-platform reactor, :ref:`select() ` . Platform-specific reactor implementations exist for: - :ref:`Poll for Linux ` - :ref:`Epoll for Linux 2.6 ` - :ref:`WaitForMultipleObjects (WFMO) for Win32 ` - :ref:`Input/Output Completion Port (IOCP) for Win32 ` - :ref:`KQueue for FreeBSD and macOS ` - :ref:`CoreFoundation for macOS ` The remaining custom reactor implementations provide support for integrating with the native event loops of various graphical toolkits. This lets your Twisted application use all of the usual Twisted APIs while still being a graphical application. Twisted currently integrates with the following graphical toolkits: - :ref:`GTK+ 2.0 ` - :ref:`GTK+ 3.0 and GObject Introspection ` - :ref:`Tkinter ` - :ref:`wxPython ` - :ref:`Win32 ` - :ref:`CoreFoundation ` - :ref:`PyUI ` When using applications that are runnable using ``twistd`` , e.g. TACs or plugins, there is no need to choose a reactor explicitly, since this can be chosen using ``twistd`` 's -r option. In all cases, the event loop is started by calling ``reactor.run()`` . In all cases, the event loop should be stopped with ``reactor.stop()`` . **IMPORTANT:** installing a reactor should be the first thing done in the app, since any code that does ``from twisted.internet import reactor`` will automatically install the default reactor if the code hasn't already installed one. Reactor Functionality --------------------- +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | \ | Status | TCP | SSL | UDP | Threading | Processes | Scheduling | Platforms | +===============================================+==============+=====+=====+=====+===========+===========+============+=============+ | select() | Stable | Y | Y | Y | Y | Y | Y | Unix, Win32 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | poll | Stable | Y | Y | Y | Y | Y | Y | Unix | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | WaitForMultipleObjects (WFMO) for Win32 | Experimental | Y | Y | Y | Y | Y | Y | Win32 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | Input/Output Completion Port (IOCP) for Win32 | Experimental | Y | Y | Y | Y | Y | Y | Win32 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | CoreFoundation | Unmaintained | Y | Y | Y | Y | Y | Y | macOS | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | epoll | Stable | Y | Y | Y | Y | Y | Y | Linux 2.6 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | GTK+ | Stable | Y | Y | Y | Y | Y | Y | Unix, Win32 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | wx | Experimental | Y | Y | Y | Y | Y | Y | Unix, Win32 | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ | kqueue | Stable | Y | Y | Y | Y | Y | Y | FreeBSD | +-----------------------------------------------+--------------+-----+-----+-----+-----------+-----------+------------+-------------+ General Purpose Reactors ------------------------ Select()-based Reactor ~~~~~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-select: The ``select`` reactor is the default on platforms that don't provide a better alternative that covers all use cases. If the ``select`` reactor is desired, it may be installed via: .. code-block:: python from twisted.internet import selectreactor selectreactor.install() from twisted.internet import reactor Platform-Specific Reactors -------------------------- Poll-based Reactor ~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-poll: The PollReactor will work on any platform that provides ``select.poll`` . With larger numbers of connected sockets, it may provide for better performance than the SelectReactor. .. code-block:: python from twisted.internet import pollreactor pollreactor.install() from twisted.internet import reactor KQueue ~~~~~~ .. _core-howto-choosing-reactor-kqueue: The KQueue Reactor allows Twisted to use FreeBSD's kqueue mechanism for event scheduling. See instructions in the :py:mod:`twisted.internet.kqreactor` 's docstring for installation notes. .. code-block:: python from twisted.internet import kqreactor kqreactor.install() from twisted.internet import reactor WaitForMultipleObjects (WFMO) for Win32 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-win32_wfmo: The Win32 reactor is not yet complete and has various limitations and issues that need to be addressed. The reactor supports GUI integration with the win32gui module, so it can be used for native Win32 GUI applications. .. code-block:: python from twisted.internet import win32eventreactor win32eventreactor.install() from twisted.internet import reactor Input/Output Completion Port (IOCP) for Win32 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-win32_iocp: Windows provides a fast, scalable event notification system known as IO Completion Ports, or IOCP for short. Twisted includes a reactor based on IOCP which is nearly complete. .. code-block:: python from twisted.internet import iocpreactor iocpreactor.install() from twisted.internet import reactor Epoll-based Reactor ~~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-epoll: The EPollReactor will work on any platform that provides ``epoll`` , today only Linux 2.6 and over. The implementation of the epoll reactor currently uses the Level Triggered interface, which is basically like poll() but scales much better. .. code-block:: python from twisted.internet import epollreactor epollreactor.install() from twisted.internet import reactor GUI Integration Reactors ------------------------ GTK+ ~~~~ .. _core-howto-choosing-reactor-gtk: Twisted integrates with `PyGTK `_ version 2.0 using the ``gtk2reactor`` . An example Twisted application that uses GTK+ can be found in ``doc/core/examples/pbgtk2.py`` . GTK-2.0 split the event loop out of the GUI toolkit and into a separate module called "glib" . To run an application using the glib event loop, use the ``glib2reactor`` . This will be slightly faster than ``gtk2reactor`` (and does not require a working X display), but cannot be used to run GUI applications. .. code-block:: python from twisted.internet import gtk2reactor # for gtk-2.0 gtk2reactor.install() from twisted.internet import reactor .. code-block:: python from twisted.internet import glib2reactor # for non-GUI apps glib2reactor.install() from twisted.internet import reactor GTK+ 3.0 and GObject Introspection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-gtk3: Twisted integrates with `GTK+ 3 `_ and GObject through `PyGObject's `_ introspection using the ``gtk3reactor`` and ``gireactor`` reactors. .. code-block:: python from twisted.internet import gtk3reactor gtk3reactor.install() from twisted.internet import reactor .. code-block:: python from twisted.internet import gireactor # for non-GUI apps gireactor.install() from twisted.internet import reactor GLib 3.0 introduces the concept of ``GApplication`` , a class that handles application uniqueness in a cross-platform way and provides its own main loop. Its counterpart ``GtkApplication`` also handles application lifetime with respect to open windows. Twisted supports registering these objects with the event loop, which should be done before running the reactor: .. code-block:: python from twisted.internet import gtk3reactor gtk3reactor.install() from gi.repository import Gtk app = Gtk.Application(...) from twisted import reactor reactor.registerGApplication(app) reactor.run() wxPython ~~~~~~~~ .. _core-howto-choosing-reactor-wxpython: Twisted currently supports two methods of integrating wxPython. Unfortunately, neither method will work on all wxPython platforms (such as GTK2 or Windows). It seems that the only portable way to integrate with wxPython is to run it in a separate thread. One of these methods may be sufficient if your wx app is limited to a single platform. As with :ref:`Tkinter ` , the support for integrating Twisted with a `wxPython `_ application uses specialized support code rather than a simple reactor. .. code-block:: python from wxPython.wx import * from twisted.internet import wxsupport, reactor myWxAppInstance = wxApp(0) wxsupport.install(myWxAppInstance) However, this has issues when running on Windows, so Twisted now comes with alternative wxPython support using a reactor. Using this method is probably better. Initialization is done in two stages. In the first, the reactor is installed: .. code-block:: python from twisted.internet import wxreactor wxreactor.install() from twisted.internet import reactor Later, once a ``wxApp`` instance has been created, but before ``reactor.run()`` is called: .. code-block:: python from twisted.internet import reactor myWxAppInstance = wxApp(0) reactor.registerWxApp(myWxAppInstance) An example Twisted application that uses wxPython can be found in ``doc/core/examples/wxdemo.py`` . CoreFoundation ~~~~~~~~~~~~~~ .. _core-howto-choosing-reactor-cfreactor: Twisted integrates with `PyObjC `_ version 1.0. Sample applications using Cocoa and Twisted are available in the examples directory under ``doc/core/examples/threadedselect/Cocoa`` . .. code-block:: python from twisted.internet import cfreactor cfreactor.install() from twisted.internet import reactor Non-Reactor GUI Integration --------------------------- Tkinter ~~~~~~~ .. _core-howto-choosing-reactor-tkinter: The support for `Tkinter `_ doesn't use a specialized reactor. Instead, there is some specialized support code: .. code-block:: python from tkinter import * from twisted.internet import tksupport, reactor root = Tk() # Install the Reactor support tksupport.install(root) # at this point build Tk app as usual using the root object, # and start the program with "reactor.run()", and stop it # with "reactor.stop()". PyUI ~~~~ .. _core-howto-choosing-reactor-pyui: As with :ref:`Tkinter ` , the support for integrating Twisted with a `PyUI `_ application uses specialized support code rather than a simple reactor. .. code-block:: python from twisted.internet import pyuisupport, reactor pyuisupport.install(args=(640, 480), kw={'renderer': 'gl'}) An example Twisted application that uses PyUI can be found in ``doc/core/examples/pyuidemo.py`` . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/clients.rst0000644000175100001710000003314700000000000020002 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Writing Clients =============== Overview -------- Twisted is a framework designed to be very flexible, and let you write powerful clients. The cost of this flexibility is a few layers in the way to writing your client. This document covers creating clients that can be used for TCP, SSL and Unix sockets. UDP is covered :doc:`in a different document ` . At the base, the place where you actually implement the protocol parsing and handling, is the ``Protocol`` class. This class will usually be descended from :py:class:`twisted.internet.protocol.Protocol` . Most protocol handlers inherit either from this class or from one of its convenience children. An instance of the protocol class will be instantiated when you connect to the server and will go away when the connection is finished. This means that persistent configuration is not saved in the ``Protocol`` . The persistent configuration is kept in a ``Factory`` class, which usually inherits from :py:class:`twisted.internet.protocol.Factory` (or :py:class:`twisted.internet.protocol.ClientFactory` : see below). The default factory class just instantiates the ``Protocol`` and then sets the protocol's ``factory`` attribute to point to itself (the factory). This lets the ``Protocol`` access, and possibly modify, the persistent configuration. Protocol -------- As mentioned above, this and auxiliary classes and functions are where most of the code is. A Twisted protocol handles data in an asynchronous manner. This means that the protocol never waits for an event, but rather responds to events as they arrive from the network. Here is a simple example: .. code-block:: python from twisted.internet.protocol import Protocol from sys import stdout class Echo(Protocol): def dataReceived(self, data): stdout.write(data) This is one of the simplest protocols. It just writes whatever it reads from the connection to standard output. There are many events it does not respond to. Here is an example of a ``Protocol`` responding to another event: .. code-block:: python from twisted.internet.protocol import Protocol class WelcomeMessage(Protocol): def connectionMade(self): self.transport.write("Hello server, I am the client!\r\n") self.transport.loseConnection() This protocol connects to the server, sends it a welcome message, and then terminates the connection. The :py:meth:`connectionMade ` event is usually where set up of the ``Protocol`` object happens, as well as any initial greetings (as in the ``WelcomeMessage`` protocol above). Any tearing down of ``Protocol`` -specific objects is done in :py:meth:`connectionLost ` . Simple, single-use clients -------------------------- In many cases, the protocol only needs to connect to the server once, and the code just wants to get a connected instance of the protocol. In those cases :py:mod:`twisted.internet.endpoints` provides the appropriate API, and in particular :py:func:`connectProtocol ` which takes a protocol instance rather than a factory. .. code-block:: python from twisted.internet import reactor from twisted.internet.protocol import Protocol from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol class Greeter(Protocol): def sendMessage(self, msg): self.transport.write("MESSAGE %s\n" % msg) def gotProtocol(p): p.sendMessage("Hello") reactor.callLater(1, p.sendMessage, "This is sent in a second") reactor.callLater(2, p.transport.loseConnection) point = TCP4ClientEndpoint(reactor, "localhost", 1234) d = connectProtocol(point, Greeter()) d.addCallback(gotProtocol) reactor.run() Regardless of the type of client endpoint, the way to set up a new connection is simply pass it to :py:func:`connectProtocol ` along with a protocol instance. This means it's easy to change the mechanism you're using to connect, without changing the rest of your program. For example, to run the greeter example over SSL, the only change required is to instantiate an :py:class:`SSL4ClientEndpoint ` instead of a ``TCP4ClientEndpoint`` . To take advantage of this, functions and methods which initiates a new connection should generally accept an endpoint as an argument and let the caller construct it, rather than taking arguments like 'host' and 'port' and constructing its own. For more information on different ways you can make outgoing connections to different types of endpoints, as well as parsing strings into endpoints, see :doc:`the documentation for the endpoints API ` . You may come across code using :py:class:`ClientCreator ` , an older API which is not as flexible as the endpoint API. Rather than calling ``connect`` on an endpoint, such code will look like this: .. code-block:: python from twisted.internet.protocol import ClientCreator ... creator = ClientCreator(reactor, Greeter) d = creator.connectTCP("localhost", 1234) d.addCallback(gotProtocol) reactor.run() In general, the endpoint API should be preferred in new code, as it lets the caller select the method of connecting. ClientFactory ------------- Still, there's plenty of code out there that uses lower-level APIs, and a few features (such as automatic reconnection) have not been re-implemented with endpoints yet, so in some cases they may be more convenient to use. To use the lower-level connection APIs, you will need to call one of the *reactor.connect** methods directly. For these cases, you need a :py:class:`ClientFactory ` . The ``ClientFactory`` is in charge of creating the ``Protocol`` and also receives events relating to the connection state. This allows it to do things like reconnect in the event of a connection error. Here is an example of a simple ``ClientFactory`` that uses the ``Echo`` protocol (above) and also prints what state the connection is in. .. code-block:: python from twisted.internet.protocol import Protocol, ClientFactory from sys import stdout class Echo(Protocol): def dataReceived(self, data): stdout.write(data) class EchoClientFactory(ClientFactory): def startedConnecting(self, connector): print('Started to connect.') def buildProtocol(self, addr): print('Connected.') return Echo() def clientConnectionLost(self, connector, reason): print('Lost connection. Reason:', reason) def clientConnectionFailed(self, connector, reason): print('Connection failed. Reason:', reason) To connect this ``EchoClientFactory`` to a server, you could use this code: .. code-block:: python from twisted.internet import reactor reactor.connectTCP(host, port, EchoClientFactory()) reactor.run() Note that :py:meth:`clientConnectionFailed ` is called when a connection could not be established, and that :py:meth:`clientConnectionLost ` is called when a connection was made and then disconnected. Reactor Client APIs ~~~~~~~~~~~~~~~~~~~ connectTCP '''''''''' :py:meth:`IReactorTCP.connectTCP ` provides support for IPv4 and IPv6 TCP clients. The ``host`` argument it accepts can be either a hostname or an IP address literal. In the case of a hostname, the reactor will automatically resolve the name to an IP address before attempting the connection. This means that for a hostname with multiple address records, reconnection attempts may not always go to the same server (see below). It also means that there is name resolution overhead for each connection attempt. If you are creating many short-lived connections (typically around hundreds or thousands per second) then you may want to resolve the hostname to an address first and then pass the address to ``connectTCP`` instead. Reconnection ~~~~~~~~~~~~ Often, the connection of a client will be lost unintentionally due to network problems. One way to reconnect after a disconnection would be to call ``connector.connect()`` when the connection is lost: .. code-block:: python from twisted.internet.protocol import ClientFactory class EchoClientFactory(ClientFactory): def clientConnectionLost(self, connector, reason): connector.connect() The connector passed as the first argument is the interface between a connection and a protocol. When the connection fails and the factory receives the ``clientConnectionLost`` event, the factory can call ``connector.connect()`` to start the connection over again from scratch. However, most programs that want this functionality should implement :py:class:`ReconnectingClientFactory ` instead, which tries to reconnect if a connection is lost or fails and which exponentially delays repeated reconnect attempts. Here is the ``Echo`` protocol implemented with a ``ReconnectingClientFactory`` : .. code-block:: python from twisted.internet.protocol import Protocol, ReconnectingClientFactory from sys import stdout class Echo(Protocol): def dataReceived(self, data): stdout.write(data) class EchoClientFactory(ReconnectingClientFactory): def startedConnecting(self, connector): print('Started to connect.') def buildProtocol(self, addr): print('Connected.') print('Resetting reconnection delay') self.resetDelay() return Echo() def clientConnectionLost(self, connector, reason): print('Lost connection. Reason:', reason) ReconnectingClientFactory.clientConnectionLost(self, connector, reason) def clientConnectionFailed(self, connector, reason): print('Connection failed. Reason:', reason) ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) A Higher-Level Example: ircLogBot --------------------------------- Overview of ircLogBot ~~~~~~~~~~~~~~~~~~~~~ The clients so far have been fairly simple. A more complicated example comes with Twisted Words in the ``doc/words/examples`` directory. :download:`ircLogBot.py <../../words/examples/ircLogBot.py>` .. literalinclude:: ../../words/examples/ircLogBot.py ``ircLogBot.py`` connects to an IRC server, joins a channel, and logs all traffic on it to a file. It demonstrates some of the connection-level logic of reconnecting on a lost connection, as well as storing persistent data in the ``Factory`` . Persistent Data in the Factory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since the ``Protocol`` instance is recreated each time the connection is made, the client needs some way to keep track of data that should be persisted. In the case of the logging bot, it needs to know which channel it is logging, and where to log it. .. code-block:: python from twisted.words.protocols import irc from twisted.internet import protocol class LogBot(irc.IRCClient): def connectionMade(self): irc.IRCClient.connectionMade(self) self.logger = MessageLogger(open(self.factory.filename, "a")) self.logger.log("[connected at %s]" % time.asctime(time.localtime(time.time()))) def signedOn(self): self.join(self.factory.channel) class LogBotFactory(protocol.ClientFactory): def __init__(self, channel, filename): self.channel = channel self.filename = filename def buildProtocol(self, addr): p = LogBot() p.factory = self return p When the protocol is created, it gets a reference to the factory as ``self.factory`` . It can then access attributes of the factory in its logic. In the case of ``LogBot`` , it opens the file and connects to the channel stored in the factory. Factories have a default implementation of ``buildProtocol``. It does the same thing the example above does using the ``protocol`` attribute of the factory to create the protocol instance. In the example above, the factory could be rewritten to look like this: .. code-block:: python class LogBotFactory(protocol.ClientFactory): protocol = LogBot def __init__(self, channel, filename): self.channel = channel self.filename = filename Further Reading --------------- The :py:class:`Protocol ` class used throughout this document is a base implementation of :py:class:`IProtocol ` used in most Twisted applications for convenience. To learn about the complete ``IProtocol`` interface, see the API documentation for :py:class:`IProtocol ` . The ``transport`` attribute used in some examples in this document provides the :py:class:`ITCPTransport ` interface. To learn about the complete interface, see the API documentation for :py:class:`ITCPTransport ` . Interface classes are a way of specifying what methods and attributes an object has and how they behave. See the :doc:`Components: Interfaces and Adapters ` document for more information on using interfaces in Twisted. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/components.rst0000644000175100001710000003724200000000000020526 0ustar00runnerdocker:LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Components: Interfaces and Adapters =================================== Object oriented programming languages allow programmers to reuse portions of existing code by creating new "classes" of objects which subclass another class. When a class subclasses another, it is said to *inherit* all of its behaviour. The subclass can then "override" and "extend" the behavior provided to it by the superclass. Inheritance is very useful in many situations, but because it is so convenient to use, often becomes abused in large software systems, especially when multiple inheritance is involved. One solution is to use *delegation* instead of "inheritance" where appropriate. Delegation is simply the act of asking *another* object to perform a task for an object. To support this design pattern, which is often referred to as the *components* pattern because it involves many small interacting components, *interfaces* and *adapters* were created by the Zope 3 team. "Interfaces" are simply markers which objects can use to say "I implement this interface". Other objects may then make requests like "Please give me an object which implements interface X for object type Y". Objects which implement an interface for another object type are called "adapters". The superclass-subclass relationship is said to be an *is-a* relationship. When designing object hierarchies, object modellers use subclassing when they can say that the subclass *is* the same class as the superclass. For example: .. code-block:: python class Shape: sideLength = 0 def getSideLength(self): return self.sideLength def setSideLength(self, sideLength): self.sideLength = sideLength def area(self): raise NotImplementedError("Subclasses must implement area") class Triangle(Shape): def area(self): return (self.sideLength * self.sideLength) / 2 class Square(Shape): def area(self): return self.sideLength * self.sideLength In the above example, a Triangle *is-a* Shape, so it subclasses Shape, and a Square *is-a* Shape, so it also subclasses Shape. However, subclassing can get complicated, especially when Multiple Inheritance enters the picture. Multiple Inheritance allows a class to inherit from more than one base class. Software which relies heavily on inheritance often ends up having both very wide and very deep inheritance trees, meaning that one class inherits from many superclasses spread throughout the system. Since subclassing with Multiple Inheritance means *implementation inheritance*, locating a method's actual implementation and ensuring the correct method is actually being invoked becomes a challenge. For example: .. code-block:: python class Area: sideLength = 0 def getSideLength(self): return self.sideLength def setSideLength(self, sideLength): self.sideLength = sideLength def area(self): raise NotImplementedError("Subclasses must implement area") class Color: color = None def setColor(self, color): self.color = color def getColor(self): return self.color class Square(Area, Color): def area(self): return self.sideLength * self.sideLength The reason programmers like using implementation inheritance is because it makes code easier to read since the implementation details of Area are in a separate place than the implementation details of Color. This is nice, because conceivably an object could have a color but not an area, or an area but not a color. The problem, though, is that Square is not really an Area or a Color, but has an area and color. Thus, we should really be using another object oriented technique called *composition*, which relies on delegation rather than inheritance to break code into small reusable chunks. Let us continue with the Multiple Inheritance example, though, because it is often used in practice. What if both the Color and the Area base class defined the same method, perhaps ``calculate``? Where would the implementation come from? The implementation that is located for ``Square().calculate()`` depends on the method resolution order, or MRO, and can change when programmers change seemingly unrelated things by refactoring classes in other parts of the system, causing obscure bugs. Our first thought might be to change the calculate method name to avoid name clashes, to perhaps ``calculateArea`` and ``calculateColor``. While explicit, this change could potentially require a large number of changes throughout a system, and is error-prone, especially when attempting to integrate two systems which you didn't write. Let's imagine another example. We have an electric appliance, say a hair dryer. The hair dryer is American voltage. We have two electric sockets, one of them an American 120 Volt socket, and one of them a United Kingdom 240 Volt socket. If we plug the hair dryer into the 240 Volt socket, it is going to expect 120 Volt current and errors will result. Going back and changing the hair dryer to support both ``plug120Volt`` and ``plug240Volt`` methods would be tedious, and what if we decided we needed to plug the hair dryer into yet another type of socket? For example: .. code-block:: python class HairDryer: def plug(self, socket): if socket.voltage() == 120: print("I was plugged in properly and am operating.") else: print("I was plugged in improperly and ") print("now you have no hair dryer any more.") class AmericanSocket: def voltage(self): return 120 class UKSocket: def voltage(self): return 240 Given these classes, the following operations can be performed: .. code-block:: pycon >>> hd = HairDryer() >>> am = AmericanSocket() >>> hd.plug(am) I was plugged in properly and am operating. >>> uk = UKSocket() >>> hd.plug(uk) I was plugged in improperly and now you have no hair dryer any more. We are going to attempt to solve this problem by writing an Adapter for the ``UKSocket`` which converts the voltage for use with an American hair dryer. An Adapter is a class which is constructed with one and only one argument, the "adaptee" or "original" object. In this example, we will show all code involved for clarity: .. code-block:: python class AdaptToAmericanSocket: def __init__(self, original): self.original = original def voltage(self): return self.original.voltage() / 2 Now, we can use it as so: .. code-block:: pycon >>> hd = HairDryer() >>> uk = UKSocket() >>> adapted = AdaptToAmericanSocket(uk) >>> hd.plug(adapted) I was plugged in properly and am operating. So, as you can see, an adapter can 'override' the original implementation. It can also 'extend' the interface of the original object by providing methods the original object did not have. Note that an Adapter must explicitly delegate any method calls it does not wish to modify to the original, otherwise the Adapter cannot be used in places where the original is expected. Usually this is not a problem, as an Adapter is created to conform an object to a particular interface and then discarded. Interfaces and Components in Twisted code ----------------------------------------- Adapters are a useful way of using multiple classes to factor code into discrete chunks. However, they are not very interesting without some more infrastructure. If each piece of code which wished to use an adapted object had to explicitly construct the adapter itself, the coupling between components would be too tight. We would like to achieve "loose coupling", and this is where :py:mod:`twisted.python.components` comes in. First, we need to discuss Interfaces in more detail. As we mentioned earlier, an Interface is nothing more than a class which is used as a marker. Interfaces should be subclasses of ``zope.interface.Interface``, and have a very odd look to python programmers not used to them: .. code-block:: python from zope.interface import Interface class IAmericanSocket(Interface): def voltage(): """ Return the voltage produced by this socket object, as an integer. """ Notice how it looks just like a regular class definition, other than inheriting from ``Interface``? However, the method definitions inside the class block do not have any method body! Since Python does not have any native language-level support for Interfaces like Java does, this is what distinguishes an Interface definition from a Class. Now that we have a defined Interface, we can talk about objects using terms like this: "The ``AmericanSocket`` class implements the ``IAmericanSocket`` interface" and "Please give me an object which adapts ``UKSocket`` to the ``IAmericanSocket`` interface". We can make *declarations* about what interfaces a certain class implements, and we can request adapters which implement a certain interface for a specific class. Let's look at how we declare that a class implements an interface: .. code-block:: python from zope.interface import implementer @implementer(IAmericanSocket) class AmericanSocket: def voltage(self): return 120 So, to declare that a class implements an interface, we simply decorate it with ``zope.interface.implementer``. Now, let's say we want to rewrite the ``AdaptToAmericanSocket`` class as a real adapter. In this case we also specify it as implementing ``IAmericanSocket``: .. code-block:: python from zope.interface import implementer @implementer(IAmericanSocket) class AdaptToAmericanSocket: def __init__(self, original): """ Pass the original UKSocket object as original """ self.original = original def voltage(self): return self.original.voltage() / 2 Notice how we placed the implements declaration on this adapter class. So far, we have not achieved anything by using components other than requiring us to type more. In order for components to be useful, we must use the *component registry*. Since ``AdaptToAmericanSocket`` implements ``IAmericanSocket`` and regulates the voltage of a ``UKSocket`` object, we can register ``AdaptToAmericanSocket`` as an ``IAmericanSocket`` adapter for the ``UKSocket`` class. It is easier to see how this is done in code than to describe it: .. code-block:: python from zope.interface import Interface, implementer from twisted.python import components class IAmericanSocket(Interface): def voltage(): """ Return the voltage produced by this socket object, as an integer. """ @implementer(IAmericanSocket) class AmericanSocket: def voltage(self): return 120 class UKSocket: def voltage(self): return 240 @implementer(IAmericanSocket) class AdaptToAmericanSocket: def __init__(self, original): self.original = original def voltage(self): return self.original.voltage() / 2 components.registerAdapter( AdaptToAmericanSocket, UKSocket, IAmericanSocket) Now, if we run this script in the interactive interpreter, we can discover a little more about how to use components. The first thing we can do is discover whether an object implements an interface or not: .. code-block:: pycon >>> IAmericanSocket.implementedBy(AmericanSocket) True >>> IAmericanSocket.implementedBy(UKSocket) False >>> am = AmericanSocket() >>> uk = UKSocket() >>> IAmericanSocket.providedBy(am) True >>> IAmericanSocket.providedBy(uk) False As you can see, the ``AmericanSocket`` instance claims to implement ``IAmericanSocket``, but the ``UKSocket`` does not. If we wanted to use the ``HairDryer`` with the ``AmericanSocket``, we could know that it would be safe to do so by checking whether it implements ``IAmericanSocket``. However, if we decide we want to use ``HairDryer`` with a ``UKSocket`` instance, we must *adapt* it to ``IAmericanSocket`` before doing so. We use the interface object to do this: .. code-block:: pycon >>> IAmericanSocket(uk) <__main__.AdaptToAmericanSocket instance at 0x1a5120> When calling an interface with an object as an argument, the interface looks in the adapter registry for an adapter which implements the interface for the given instance's class. If it finds one, it constructs an instance of the Adapter class, passing the constructor the original instance, and returns it. Now the ``HairDryer`` can safely be used with the adapted ``UKSocket`` . But what happens if we attempt to adapt an object which already implements ``IAmericanSocket``? We simply get back the original instance: .. code-block:: pycon >>> IAmericanSocket(am) <__main__.AmericanSocket instance at 0x36bff0> So, we could write a new "smart" ``HairDryer`` which automatically looked up an adapter for the socket you tried to plug it into: .. code-block:: python class HairDryer: def plug(self, socket): adapted = IAmericanSocket(socket) assert adapted.voltage() == 120, "BOOM" print("I was plugged in properly and am operating") Now, if we create an instance of our new "smart" ``HairDryer`` and attempt to plug it in to various sockets, the ``HairDryer`` will adapt itself automatically depending on the type of socket it is plugged in to: .. code-block:: pycon >>> am = AmericanSocket() >>> uk = UKSocket() >>> hd = HairDryer() >>> hd.plug(am) I was plugged in properly and am operating >>> hd.plug(uk) I was plugged in properly and am operating Voila; the magic of components. Components and Inheritance ~~~~~~~~~~~~~~~~~~~~~~~~~~ If you inherit from a class which implements some interface, and your new subclass declares that it implements another interface, the implements will be inherited by default. For example, :py:class:`pb.Root ` is a class which implements :py:class:`IPBRoot `. This interface indicates that an object has remotely-invokable methods and can be used as the initial object served by a new Broker instance. It has an ``implements`` setting like: .. code-block:: python from zope.interface import implementer @implementer(IPBRoot) class Root(Referenceable): pass Suppose you have your own class which implements your ``IMyInterface`` interface: .. code-block:: python from zope.interface import implementer, Interface class IMyInterface(Interface): pass @implementer(IMyInterface) class MyThing: pass Now if you want to make this class inherit from ``pb.Root``, the interfaces code will automatically determine that it also implements ``IPBRoot``: .. code-block:: python from twisted.spread import pb from zope.interface import implementer, Interface class IMyInterface(Interface): pass @implementer(IMyInterface) class MyThing(pb.Root): pass .. code-block:: pycon >>> from twisted.spread.flavors import IPBRoot >>> IPBRoot.implementedBy(MyThing) True If you want ``MyThing`` to inherit from ``pb.Root`` but *not* implement ``IPBRoot`` like ``pb.Root`` does, use ``@implementer_only``: .. code-block:: python from twisted.spread import pb from zope.interface import implementer_only, Interface class IMyInterface(Interface): pass @implementer_only(IMyInterface) class MyThing(pb.Root): pass .. code-block:: pycon >>> from twisted.spread.pb import IPBRoot >>> IPBRoot.implementedBy(MyThing) False ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/constants.rst0000644000175100001710000000054600000000000020352 0ustar00runnerdockerSymbolic Constants ================== .. note:: ``twisted.python.constants`` has been deprecated in Twisted, and the same code has been spun out into `Constantly `_\. The API is identical, just install it from PyPI and replace ``import twisted.python.constants`` with ``import constantly`` in your code. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/cred.rst0000644000175100001710000003753400000000000017262 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Cred: Pluggable Authentication ============================== Goals ----- Cred is a pluggable authentication system for servers. It allows any number of network protocols to connect and authenticate to a system, and communicate to those aspects of the system which are meaningful to the specific protocol. For example, Twisted's POP3 support passes a "username and password" set of credentials to get back a mailbox for the specified email account. IMAP does the same, but retrieves a slightly different view of the same mailbox, enabling those features specific to IMAP which are not available in other mail protocols. Cred is designed to allow both the backend implementation of the business logic - called the *avatar* - and the authentication database - called the *credential checker* - to be decided during deployment. For example, the same POP3 server should be able to authenticate against the local UNIX password database or an LDAP server without having to know anything about how or where mail is stored. To sketch out how this works - a "Realm" corresponds to an application domain and is in charge of avatars, which are network-accessible business logic objects. To connect this to an authentication database, a top-level object called a :py:class:`Portal ` stores a realm, and a number of credential checkers. Something that wishes to log in, such as a :py:class:`Protocol ` , stores a reference to the portal. Login consists of passing credentials and a request interface (e.g. POP3's :py:class:`IMailboxPOP3 ` ) to the portal. The portal passes the credentials to the appropriate credential checker, which returns an avatar ID. The ID is passed to the realm, which returns the appropriate avatar. For a Portal that has a realm that creates mailbox objects and a credential checker that checks /etc/passwd, login consists of passing in a username/password and the IMailboxPOP3 interface to the portal. The portal passes this to the /etc/passwd credential checker, gets back a avatar ID corresponding to an email account, passes that to the realm and gets back a mailbox object for that email account. Putting all this together, here's how a login request will typically be processed: .. image:: ../img/cred-login.png Cred objects ------------ The Portal ~~~~~~~~~~ This is the core of login, the point of integration between all the objects in the cred system. There is one concrete implementation of Portal, and no interface - it does a very simple task. A :py:class:`Portal ` associates one (1) Realm with a collection of CredentialChecker instances. (More on those later.) If you are writing a protocol that needs to authenticate against something, you will need a reference to a Portal, and to nothing else. This has only 2 methods - - :py:meth:`login ` ``(credentials, mind, *interfaces)`` The docstring is quite expansive (see :py:mod:`twisted.cred.portal` ), but in brief, this is what you call when you need to call in order to connect a user to the system. Typically you only pass in one interface, and the mind is ``None`` . The interfaces are the possible interfaces the returned avatar is expected to implement, in order of preference. The result is a deferred which fires a tuple of: - interface the avatar implements (which was one of the interfaces passed in the ``*interfaces`` tuple) - an object that implements that interface (an avatar) - logout, a 0-argument callable which disconnects the connection that was established by this call to login The logout method has to be called when the avatar is logged out. For POP3 this means when the protocol is disconnected or logged out, etc.. - :py:meth:`registerChecker ` ``(checker, *credentialInterfaces)`` which adds a CredentialChecker to the portal. The optional list of interfaces are interfaces of credentials that the checker is able to check. The CredentialChecker ~~~~~~~~~~~~~~~~~~~~~ This is an object implementing :py:class:`ICredentialsChecker ` which resolves some credentials to an avatar ID. Whether the credentials are stored in an in-memory data structure, an Apache-style htaccess file, a UNIX password database, an SSH key database, or any other form, an implementation of ``ICredentialsChecker`` is how this data is connected to cred. A credential checker stipulates some requirements of the credentials it can check by specifying a credentialInterfaces attribute, which is a list of interfaces. Credentials passed to its requestAvatarId method must implement one of those interfaces. For the most part, these things will just check usernames and passwords and produce the username as the result, but hopefully we will be seeing some public-key, challenge-response, and certificate based credential checker mechanisms soon. A credential checker should raise an error if it cannot authenticate the user, and return ``twisted.cred.checkers.ANONYMOUS`` for anonymous access. The Credentials ~~~~~~~~~~~~~~~ Oddly enough, this represents some credentials that the user presents. Usually this will just be a small static blob of data, but in some cases it will actually be an object connected to a network protocol. For example, a username/password pair is static, but a challenge/response server is an active state-machine that will require several method calls in order to determine a result. Twisted comes with a number of credentials interfaces and implementations in the :py:mod:`twisted.cred.credentials` module, such as :py:class:`IUsernamePassword ` and :py:class:`IUsernameHashedPassword ` . The Realm ~~~~~~~~~ A realm is an interface which connects your universe of "business objects" to the authentication system. :py:class:`IRealm ` is another one-method interface: - :py:meth:`requestAvatar ` ``(avatarId, mind, *interfaces)`` This method will typically be called from 'Portal.login'. The avatarId is the one returned by a CredentialChecker. .. note:: Note that ``avatarId`` must always be a string. In particular, do not use unicode strings. If internationalized support is needed, it is recommended to use UTF-8, and take care of decoding in the realm. The important thing to realize about this method is that if it is being called, *the user has already authenticated* . Therefore, if possible, the Realm should create a new user if one does not already exist whenever possible. Of course, sometimes this will be impossible without more information, and that is the case that the interfaces argument is for. Since requestAvatar should be called from a Deferred callback, it may return a Deferred or a synchronous result. The Avatar ~~~~~~~~~~ An avatar is a business logic object for a specific user. For POP3, it's a mailbox, for a first-person-shooter it's the object that interacts with the game, the actor as it were. Avatars are specific to an application, and each avatar represents a single "user" . The Mind ~~~~~~~~ As mentioned before, the mind is usually ``None`` , so you can skip this bit if you want. Masters of Perspective Broker already know this object as the ill-named "client object" . There is no "mind" class, or even interface, but it is an object which serves an important role - any notifications which are to be relayed to an authenticated client are passed through a 'mind'. In addition, it allows passing more information to the realm during login in addition to the avatar ID. The name may seem rather unusual, but considering that a Mind is representative of the entity on the "other end" of a network connection that is both receiving updates and issuing commands, I believe it is appropriate. Although many protocols will not use this, it serves an important role. It is provided as an argument both to the Portal and to the Realm, although a CredentialChecker should interact with a client program exclusively through a Credentials instance. Unlike the original Perspective Broker "client object" , a Mind's implementation is most often dictated by the protocol that is connecting rather than the Realm. A Realm which requires a particular interface to issue notifications will need to wrap the Protocol's mind implementation with an adapter in order to get one that conforms to its expected interface - however, Perspective Broker will likely continue to use the model where the client object has a pre-specified remote interface. (If you don't quite understand this, it's fine. It's hard to explain, and it's not used in simple usages of cred, so feel free to pass None until you find yourself requiring something like this.) Responsibilities ---------------- Server protocol implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The protocol implementor should define the interface the avatar should implement, and design the protocol to have a portal attached. When a user logs in using the protocol, a credential object is created, passed to the portal, and an avatar with the appropriate interface is requested. When the user logs out or the protocol is disconnected, the avatar should be logged out. The protocol designer should not hardcode how users are authenticated or the realm implemented. For example, a POP3 protocol implementation would require a portal whose realm returns avatars implementing IMailbox and whose credential checker accepts username/password credentials, but that is all. Here's a sketch of how the code might look - note that USER and PASS are the protocol commands used to login, and the DELE command can only be used after you are logged in: :download:`pop3_server.py ` .. literalinclude:: listings/cred/pop3_server.py Application implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~ The application developer can implement realms and credential checkers. For example, they might implement a realm that returns IMailbox implementing avatars, using MySQL for storage, or perhaps a credential checker that uses LDAP for authentication. In the following example, the Realm for a simple remote object service (using Twisted's Perspective Broker protocol) is implemented: .. code-block:: python from zope.interface import implementer from twisted.spread import pb from twisted.cred.portal import IRealm class SimplePerspective(pb.Avatar): def perspective_echo(self, text): print('echoing',text) return text def logout(self): print(self, "logged out") @implementer(IRealm) class SimpleRealm: def requestAvatar(self, avatarId, mind, *interfaces): if pb.IPerspective in interfaces: avatar = SimplePerspective() return pb.IPerspective, avatar, avatar.logout else: raise NotImplementedError("no interface") Deployment ~~~~~~~~~~ Deployment involves tying together a protocol, an appropriate realm and a credential checker. For example, a POP3 server can be constructed by attaching to it a portal that wraps the MySQL-based realm and an /etc/passwd credential checker, or perhaps the LDAP credential checker if that is more useful. The following example shows how the SimpleRealm in the previous example is deployed using an in-memory credential checker: .. code-block:: python from twisted.spread import pb from twisted.internet import reactor from twisted.cred.portal import Portal from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse portal = Portal(SimpleRealm()) checker = InMemoryUsernamePasswordDatabaseDontUse() checker.addUser("guest", "password") portal.registerChecker(checker) reactor.listenTCP(9986, pb.PBServerFactory(portal)) reactor.run() Cred plugins ------------ Authentication with cred plugins ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cred offers a plugin architecture for authentication methods. The primary API for this architecture is the command-line; the plugins are meant to be specified by the end-user when deploying a TAP (twistd plugin). For more information on writing a twistd plugin and using cred plugins for your application, please refer to the :doc:`Writing a twistd plugin ` document. Building a cred plugin ~~~~~~~~~~~~~~~~~~~~~~ To build a plugin for cred, you should first define an ``authType`` , a short one-word string that defines your plugin to the command-line. Once you have this, the convention is to create a file named ``myapp_plugins.py`` in the :py:mod:`twisted.plugins` module path. Below is an example file structure for an application that defines such a plugin: - MyApplication/ - setup.py - myapp/ - __init__.py - cred.py - server.py - twisted/ - plugins/ - myapp_plugins.py Once you have created this structure within your application, you can create the code for your cred plugin by building a factory class which implements :py:class:`ICheckerFactory ` . These factory classes should not consist of a tremendous amount of code. Most of the real application logic should reside in the cred checker itself. (For help on building those, scroll up.) The core purpose of the CheckerFactory is to translate an ``argstring`` , which is passed on the command line, into a suitable set of initialization parameters for a Checker class. In most cases this should be little more than constructing a dictionary or a tuple of arguments, then passing them along to a new checker instance. .. code-block:: python from zope.interface import implementer from twisted import plugin from twisted.cred.strcred import ICheckerFactory from myapp.cred import SpecialChecker # The class needs to implement both of these interfaces # for the plugin system to find our factory. @implementer(ICheckerFactory, plugin.IPlugin) class SpecialCheckerFactory(object): """ A checker factory for a specialized (fictional) API. """ # This tells AuthOptionsMixin how to find this factory. authType = "special" # This is a one-line explanation of what arguments, if any, # your particular cred plugin requires at the command-line. argStringFormat = "A colon-separated key=value list." # This help text can be multiple lines. It will be displayed # when someone uses the "--help-auth-type special" command. authHelp = """Some help text goes here ...""" # This will be called once per command-line. def generateChecker(self, argstring=""): argdict = dict((x.split('=') for x in argstring.split(':'))) return SpecialChecker(**argdict) # We need to instantiate our class for the plugin to work. theSpecialCheckerFactory = SpecialCheckerFactory() For more information on how your plugin can be used in your application (and by other application developers), please see the :doc:`Writing a twistd plugin ` document. Conclusion ---------- After reading through this tutorial, you should be able to - Understand how the cred architecture applies to your application - Integrate your application with cred's object model - Deploy an application that uses cred for authentication - Allow your users to use command-line authentication plugins ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/debug-with-emacs.rst0000644000175100001710000000350000000000000021454 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Debugging Python(Twisted) with Emacs ==================================== - Open up your project files. sometimes emacs can't find them if you don't have them open before-hand. - Make sure you have a program called ``pdb`` somewhere in your PATH, with the following contents: .. code-block:: console #!/bin/sh exec python -m pdb $1 $2 $3 $4 $5 $6 $7 $8 $9 - Run ``M-x pdb`` in emacs. If you usually run your program as ``python foo.py`` , your command line should be ``pdb foo.py`` , for ``twistd`` and ``trial`` just add -b to the command line, e.g.: ``twistd -b -y my.tac`` - While pdb waits for your input, go to a place in your code and hit ``C-x SPC`` to insert a break-point. pdb should say something happy. Do this in as many points as you wish. - Go to your pdb buffer and hit ``c`` ; this runs as normal until a break-point is found. - Once you get to a breakpoint, use ``s`` to step, ``n`` to run the current line without stepping through the functions it calls, ``w`` to print out the current stack, ``u`` and ``d`` to go up and down a level in the stack, ``p foo`` to print result of expression ``foo`` . - Recommendations for effective debugging: - use ``p self`` a lot; just knowing the class where the current code is isn't enough most of the time. - use ``w`` to get your bearings, it'll re-display the current-line/arrow - after you use ``w`` , use ``u`` and ``d`` and lots more ``p self`` on the different stack-levels. - If you've got a big code-path that you need to grok, keep another buffer open and list the code-path there (e.g., I had a nasty-evil Deferred recursion, and this helped me tons) .. rubric:: Footnotes ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/defer-intro.rst0000644000175100001710000004505200000000000020555 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Introduction to Deferreds ========================= This document introduces :py:class:`Deferred `\s, Twisted's preferred mechanism for controlling the flow of asynchronous code. Don't worry if you don't know what that means yet -- that's why you are here! It is intended for newcomers to Twisted, and was written particularly to help people read and understand code that already uses :py:class:`Deferred `\s. This document assumes you have a good working knowledge of Python. It assumes no knowledge of Twisted. By the end of the document, you should understand what :py:class:`Deferred `\s are and how they can be used to coordinate asynchronous code. In particular, you should be able to: - Read and understand code that uses :py:class:`Deferred `\s - Translate from synchronous code to asynchronous code and back again - Implement any sort of error-handling for asynchronous code that you wish The joy of order ---------------- When you write Python code, one prevailing, deep, unassailled assumption is that a line of code within a block is only ever executed after the preceding line is finished. .. code-block:: python pod_bay_doors.open() pod.launch() The pod bay doors open, and only *then* does the pod launch. That's wonderful. One-line-after-another is a built-in mechanism in the language for encoding the order of execution. It's clear, terse, and unambiguous. Exceptions make things more complicated. If ``pod_bay_doors.open()`` raises an exception, then we cannot know with certainty that it completed, and so it would be wrong to proceed blithely to the next line. Thus, Python gives us ``try``, ``except``, ``finally``, and ``else``, which together model almost every conceivable way of handling a raised exception, and tend to work really well. Function application is the other way we encode order of execution:: pprint(sorted(x.get_names())) First ``x.get_names()`` gets called, then ``sorted`` is called with its return value, and then ``pprint`` with whatever ``sorted`` returns. It can also be written as:: names = x.get_names() sorted_names = sorted(names) pprint(sorted_names) Sometimes it leads us to encode the order when we don't need to, as in this example:: from __future__ import print_function total = 0 for account in accounts: total += account.get_balance() print("Total balance ${}".format(total)) But that's normally not such a big deal. All in all, things are pretty good, and all of the explanation above is laboring familiar and obvious points. One line comes after another and one thing happens after another, and both facts are inextricably tied. But what if we had to do it differently? A hypothetical problem ---------------------- What if we could no longer rely on the previous line of code being finished (whatever that means) before we started to interpret & execute the next line of code? What if ``pod_bay_doors.open()`` returned immediately, triggering something somewhere else that would eventually open the pod bay doors, recklessly sending the Python interpreter plunging into ``pod.launch()`` ? That is, what would we do if the order of execution did not match the order of lines of Python? If "returning" no longer meant "finishing"? *Asynchronous operations*? How would we prevent our pod from hurtling into the still-closed doors? How could we respond to a potential failure to open the doors at all? What if opening the doors gave us some crucial information that we needed in order to launch the pod? How would we get access to that information? And, crucially, since we are writing code, how can we write our code so that we can build *other* code on top of it? The components of a solution ---------------------------- We would still need a way of saying "do *this* only when *that* has finished". We would need a way of distinguishing between successful completion and interrupted processing, normally modeled with ``try``, ``except``, ``else``, and ``finally``. We need a mechanism for getting return failures and exception information from the thing that just executed to the thing that needs to happen next. We need somehow to be able to operate on results that we don't have yet. Instead of acting, we need to make and encode plans for how we would act if we could. Unless we hack the interpreter somehow, we would need to build this with the Python language constructs we are given: methods, functions, objects, and the like. Perhaps we want something that looks a little like this:: placeholder = pod_bay_doors.open() placeholder.when_done(pod.launch) One solution: Deferred ---------------------- Twisted tackles this problem with :py:class:`Deferred `\s, a type of object designed to do one thing, and one thing only: encode an order of execution separately from the order of lines in Python source code. It doesn't deal with threads, parallelism, signals, or subprocesses. It doesn't know anything about an event loop, greenlets, or scheduling. All it knows about is what order to do things in. How does it know that? Because we explicitly tell it the order that we want. Thus, instead of writing:: pod_bay_doors.open() pod.launch() We write:: d = pod_bay_doors.open() d.addCallback(lambda ignored: pod.launch()) That introduced a dozen new concepts in a couple of lines of code, so let's break it down. If you think you've got it, you might want to skip to the next section. Here, ``pod_bay_doors.open()`` is returning a :py:class:`Deferred `, which we assign to ``d``. We can think of ``d`` as a placeholder, representing the value that ``open()`` will eventually return when it finally gets around to finishing. To "do this next", we add a *callback* to ``d``. A callback is a function that will be called with whatever ``open()`` eventually returns. In this case, we don't care, so we make a function with a single, ignored parameter that just calls ``pod.launch()``. So, we've replaced the "order of lines is order of execution" with a deliberate, in-Python encoding of the order of execution, where ``d`` represents the particular flow and ``d.addCallback`` replaces "new line". Of course, programs generally consist of more than two lines, and we still don't know how to deal with failure. Getting it right: The failure cases ----------------------------------- In what follows, we are going to take each way of expressing order of operations in normal Python (using lines of code and ``try``/``except``) and translate them into an equivalent code built with :py:class:`Deferred ` objects. This is going to be a bit painstaking, but if you want to really understand how to use :py:class:`Deferred `\s and maintain code that uses them, it is worth understanding each example below. One thing, then another, then another ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Recall our example from earlier:: pprint(sorted(x.get_names())) Also written as:: names = x.get_names() sorted_names = sorted(names) pprint(sorted_names) What if neither ``get_names`` nor ``sorted`` can be relied on to finish before they return? That is, if both are asynchronous operations? Well, in Twisted-speak they would return :py:class:`Deferred `\s and so we would write:: d = x.get_names() d.addCallback(sorted) d.addCallback(pprint) Eventually, ``sorted`` will get called with whatever ``get_names`` finally delivers. When ``sorted`` finishes, ``pprint`` will be called with whatever it delivers. We could also write this as:: x.get_names().addCallback(sorted).addCallback(pprint) Since ``d.addCallback`` returns ``d``. Simple failure handling ~~~~~~~~~~~~~~~~~~~~~~~ We often want to write code equivalent to this:: try: x.get_names() except Exception as e: report_error(e) How would we write this with :py:class:`Deferred `\s? .. code-block:: python d = x.get_names() d.addErrback(report_error) *errback* is the Twisted name for a callback that is called when an error is received. This glosses over an important detail. Instead of getting the exception object ``e``, ``report_error`` would get a :py:class:`Failure ` object, which has all of the useful information that ``e`` does, but is optimized for use with :py:class:`Deferred `\s. We'll dig into that a bit later, after we've dealt with all of the other combinations of exceptions. Handle an error, but do something else on success ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What if we want to do something after our ``try`` block if it actually worked? Abandoning our contrived examples and reaching for generic variable names, we get:: try: y = f() except Exception as e: g(e) else: h(y) Well, we'd write it like this with :py:class:`Deferred `\s:: d = f() d.addCallbacks(h, g) Where ``addCallbacks`` means "add a callback and an errback at the same time". ``h`` is the callback, ``g`` is the errback. Now that we have ``addCallbacks`` along with ``addErrback`` and ``addCallback``, we can match any possible combination of ``try``, ``except``, ``else``, and ``finally`` by varying the order in which we call them. Explaining exactly how it works is tricky (although the :doc:`Deferred reference ` does rather a good job), but once we're through all of the examples it ought to be clearer. Handle an error, then proceed anyway ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What if we want to do something after our ``try``/``except`` block, regardless of whether or not there was an exception? That is, what if we wanted to do the equivalent of this generic code:: try: y = f() except Exception as e: y = g(e) h(y) And with :py:class:`Deferred `\s:: d = f() d.addErrback(g) d.addCallback(h) Because ``addErrback`` returns ``d``, we can chain the calls like so:: f().addErrback(g).addCallback(h) The order of ``addErrback`` and ``addCallback`` matters. In the next section, we can see what would happen when we swap them around. Handle an error for the entire operation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ What if we want to wrap up a multi-step operation in one exception handler? .. code-block:: python try: y = f() z = h(y) except Exception as e: g(e) With :py:class:`Deferred `\s, it would look like this:: d = f() d.addCallback(h) d.addErrback(g) Or, more succinctly:: d = f().addCallback(h).addErrback(g) Do something regardless ~~~~~~~~~~~~~~~~~~~~~~~ What about ``finally``? How do we do something regardless of whether or not there was an exception? How do we translate this:: try: y = f() finally: g() Well, roughly we do this:: d = f() d.addBoth(g) This adds ``g`` as both the callback and the errback. It is equivalent to:: d.addCallbacks(g, g) Why "roughly"? Because if ``f`` raises, ``g`` will be passed a :py:class:`Failure ` object representing the exception. Otherwise, ``g`` will be passed the asynchronous equivalent of the return value of ``f()`` (i.e. ``y``). Coroutines with async/await ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: .. versionadded:: 16.4 Python 3.5 introduced :pep:`492` ("Coroutines with async and await syntax") and native coroutines. :py:meth:`Deferred.fromCoroutine ` allows you to write coroutines with the ``async def`` syntax and ``await`` on Deferreds, similar to ``inlineCallbacks``. Rather than decorating every function that may ``await`` a Deferred (as you would with functions that ``yield`` Deferreds with ``inlineCallbacks``), you only need to call ``fromCoroutine`` with the outer-most coroutine object to schedule it for execution. Coroutines can ``await`` other coroutines once running without needing to use this function themselves. .. note:: The :py:func:`ensureDeferred ` function also provides a way to convert a coroutine to a Deferred, but it's interface is more type-ambiguous; ``Deferred.fromCoroutine`` is meant to replace it. Awaiting on a Deferred which fires with a Failure will raise the exception inside your coroutine as if it were regular Python. If your coroutine raises an exception, it will be translated into a Failure fired on the Deferred that ``Deferred.fromCoroutine`` returns for you. Calling ``return`` will cause the Deferred that ``Deferred.fromCoroutine`` returned for you to fire with a result. .. code-block:: python3 import json from twisted.internet.defer import Deferred from twisted.logger import Logger log = Logger() async def getUsers(): try: return json.loads(await makeRequest("GET", "/users")) except ConnectionError: log.failure("makeRequest failed due to connection error") return [] def do(): d = Deferred.fromCoroutine(getUsers()) d.addCallback(print) return d When writing coroutines, you do not need to use :py:meth:`Deferred.fromCoroutine ` when you are writing a coroutine which calls other coroutines which await on Deferreds; you can just ``await`` on it directly. For example: .. code-block:: python3 async def foo(): res = await someFunctionThatReturnsADeferred() return res async def bar(): baz = await someOtherDeferredFunction() fooResult = await foo() return baz + fooResult def myDeferredReturningFunction(): coro = bar() return Deferred.fromCoroutine(coro) Even though Deferreds were used in both coroutines, only ``bar`` had to be wrapped in :py:meth:`Deferred.fromCoroutine ` to return a Deferred. Inline callbacks - using 'yield' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: Unless your code supports Python 2 (and therefore needs compatibility with older versions of Twisted), writing coroutines with the functionality described in "Coroutines with async/await" is preferred over ``inlineCallbacks``. Coroutines are supported by dedicated Python syntax, are compatible with ``asyncio``, and provide higher performance. Twisted features a decorator named ``inlineCallbacks`` which allows you to work with Deferreds without writing callback functions. This is done by writing your code as generators, which *yield* ``Deferred``\ s instead of attaching callbacks. Consider the following function written in the traditional ``Deferred`` style: .. code-block:: python def getUsers(): d = makeRequest("GET", "/users") d.addCallback(json.loads) return d using ``inlineCallbacks``, we can write this as: .. code-block:: python from twisted.internet.defer import inlineCallbacks, returnValue @inlineCallbacks def getUsers(self): responseBody = yield makeRequest("GET", "/users") returnValue(json.loads(responseBody)) a couple of things are happening here: #. instead of calling ``addCallback`` on the ``Deferred`` returned by ``makeRequest``, we *yield* it. This causes Twisted to return the ``Deferred``\ 's result to us. #. we use ``returnValue`` to propagate the final result of our function. Because this function is a generator, we cannot use the return statement; that would be a syntax error. .. note:: .. versionadded:: 15.0 On Python 3, instead of writing ``returnValue(json.loads(responseBody))`` you can instead write ``return json.loads(responseBody)``. This can be a significant readability advantage, but unfortunately if you need compatibility with Python 2, this isn't an option. Both versions of ``getUsers`` present exactly the same API to their callers: both return a ``Deferred`` that fires with the parsed JSON body of the request. Though the ``inlineCallbacks`` version looks like synchronous code, which blocks while waiting for the request to finish, each ``yield`` statement allows other code to run while waiting for the ``Deferred`` being yielded to fire. ``inlineCallbacks`` become even more powerful when dealing with complex control flow and error handling. For example, what if ``makeRequest`` fails due to a connection error? For the sake of this example, let's say we want to log the exception and return an empty list. .. code-block:: python def getUsers(): d = makeRequest("GET", "/users") def connectionError(failure): failure.trap(ConnectionError) log.failure("makeRequest failed due to connection error", failure) return [] d.addCallbacks(json.loads, connectionError) return d With ``inlineCallbacks``, we can rewrite this as: .. code-block:: python @inlineCallbacks def getUsers(self): try: responseBody = yield makeRequest("GET", "/users") except ConnectionError: log.failure("makeRequest failed due to connection error") returnValue([]) returnValue(json.loads(responseBody)) Our exception handling is simplified because we can use Python's familiar ``try`` / ``except`` syntax for handling ``ConnectionError``\ s. Conclusion ---------- You have been introduced to asynchronous code and have seen how to use :py:class:`Deferred `\s to: - Do something after an asynchronous operation completes successfully - Use the result of a successful asynchronous operation - Catch errors in asynchronous operations - Do one thing if an operation succeeds, and a different thing if it fails - Do something after an error has been handled successfully - Wrap multiple asynchronous operations with one error handler - Do something after an asynchronous operation, regardless of whether it succeeded or failed - Write code without callbacks using ``inlineCallbacks`` - Write coroutines that interact with Deferreds using ``Deferred.fromCoroutine`` These are very basic uses of :py:class:`Deferred `. For detailed information about how they work, how to combine multiple Deferreds, and how to write code that mixes synchronous and asynchronous APIs, see the :doc:`Deferred reference `. Alternatively, read about how to write functions that :doc:`generate Deferreds `. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/defer.rst0000644000175100001710000011360600000000000017425 0ustar00runnerdockerDeferred Reference ================== This document is a guide to the behaviour of the :py:class:`twisted.internet.defer.Deferred` object, and to various ways you can use them when they are returned by functions. This document assumes that you are familiar with the basic principle that the Twisted framework is structured around: asynchronous, callback-based programming, where instead of having blocking code in your program or using threads to run blocking code, you have functions that return immediately and then begin a callback chain when data is available. After reading this document, the reader should expect to be able to deal with most simple APIs in Twisted and Twisted-using code that return Deferreds. - what sorts of things you can do when you get a Deferred from a function call; and - how you can write your code to robustly handle errors in Deferred code. .. _core-howto-defer-deferreds: Deferreds --------- Twisted uses the :py:class:`Deferred ` object to manage the callback sequence. The client application attaches a series of functions to the deferred to be called in order when the results of the asynchronous request are available (this series of functions is known as a series of **callbacks**, or a **callback chain**), together with a series of functions to be called if there is an error in the asynchronous request (known as a series of **errbacks** or an **errback chain**). The asynchronous library code calls the first callback when the result is available, or the first errback when an error occurs, and the ``Deferred`` object then hands the results of each callback or errback function to the next function in the chain. Callbacks --------- A :py:class:`twisted.internet.defer.Deferred` is a promise that a function will at some point have a result. We can attach callback functions to a Deferred, and once it gets a result these callbacks will be called. In addition Deferreds allow the developer to register a callback for an error, with the default behavior of logging the error. The deferred mechanism standardizes the application programmer's interface with all sorts of blocking or delayed operations. .. code-block:: python from twisted.internet import reactor, defer def getDummyData(inputData): """ This function is a dummy which simulates a delayed result and returns a Deferred which will fire with that result. Don't try too hard to understand this. """ print('getDummyData called') deferred = defer.Deferred() # simulate a delayed result by asking the reactor to fire the # Deferred in 2 seconds time with the result inputData * 3 reactor.callLater(2, deferred.callback, inputData * 3) return deferred def cbPrintData(result): """ Data handling function to be added as a callback: handles the data by printing the result """ print('Result received: {}'.format(result)) deferred = getDummyData(3) deferred.addCallback(cbPrintData) # manually set up the end of the process by asking the reactor to # stop itself in 4 seconds time reactor.callLater(4, reactor.stop) # start up the Twisted reactor (event loop handler) manually print('Starting the reactor') reactor.run() Multiple callbacks ~~~~~~~~~~~~~~~~~~ Multiple callbacks can be added to a Deferred. The first callback in the Deferred's callback chain will be called with the result, the second with the result of the first callback, and so on. Why do we need this? Well, consider a Deferred returned by :py:mod:`twisted.enterprise.adbapi` - the result of a SQL query. A web widget might add a callback that converts this result into HTML, and pass the Deferred onwards, where the callback will be used by twisted to return the result to the HTTP client. The callback chain will be bypassed in case of errors or exceptions. .. code-block:: python from twisted.internet import reactor, defer class Getter: def gotResults(self, x): """ The Deferred mechanism provides a mechanism to signal error conditions. In this case, odd numbers are bad. This function demonstrates a more complex way of starting the callback chain by checking for expected results and choosing whether to fire the callback or errback chain """ if self.d is None: print("Nowhere to put results") return d = self.d self.d = None if x % 2 == 0: d.callback(x*3) else: d.errback(ValueError("You used an odd number!")) def _toHTML(self, r): """ This function converts r to HTML. It is added to the callback chain by getDummyData in order to demonstrate how a callback passes its own result to the next callback """ return "Result: %s" % r def getDummyData(self, x): """ The Deferred mechanism allows for chained callbacks. In this example, the output of gotResults is first passed through _toHTML on its way to printData. Again this function is a dummy, simulating a delayed result using callLater, rather than using a real asynchronous setup. """ self.d = defer.Deferred() # simulate a delayed result by asking the reactor to schedule # gotResults in 2 seconds time reactor.callLater(2, self.gotResults, x) self.d.addCallback(self._toHTML) return self.d def cbPrintData(result): print(result) def ebPrintError(failure): import sys sys.stderr.write(str(failure)) # this series of callbacks and errbacks will print an error message g = Getter() d = g.getDummyData(3) d.addCallback(cbPrintData) d.addErrback(ebPrintError) # this series of callbacks and errbacks will print "Result: 12" g = Getter() d = g.getDummyData(4) d.addCallback(cbPrintData) d.addErrback(ebPrintError) reactor.callLater(4, reactor.stop) reactor.run() .. note:: Pay particular attention to the handling of ``self.d`` in the ``gotResults`` method. Before the ``Deferred`` is fired with a result or an error, the attribute is set to ``None`` so that the ``Getter`` instance no longer has a reference to the ``Deferred`` about to be fired. This has several benefits. First, it avoids any chance ``Getter.gotResults`` will accidentally fire the same ``Deferred`` more than once (which would result in an ``AlreadyCalledError`` exception). Second, it allows a callback on that ``Deferred`` to call ``Getter.getDummyData`` (which sets a new value for the ``d`` attribute) without causing problems. Third, it makes the Python garbage collector's job easier by eliminating a reference cycle. Visual Explanation ~~~~~~~~~~~~~~~~~~ .. image:: ../img/deferred-attach.png #. Requesting method (data sink) requests data, gets Deferred object. #. Requesting method attaches callbacks to Deferred object. .. image:: ../img/deferred-process.png #. When the result is ready, give it to the Deferred object. ``.callback(result)`` if the operation succeeded, ``.errback(failure)`` if it failed. Note that ``failure`` is typically an instance of a :py:class:`twisted.python.failure.Failure` instance. #. Deferred object triggers previously-added (call/err)back with the ``result`` or ``failure``. Execution then follows the following rules, going down the chain of callbacks to be processed. - Result of the callback is always passed as the first argument to the next callback, creating a chain of processors. - If a callback raises an exception, switch to errback. - An unhandled failure gets passed down the line of errbacks, this creating an asynchronous analog to a series to a series of ``except:`` statements. - If an errback doesn't raise an exception or return a :py:class:`twisted.python.failure.Failure` instance, switch to callback. Errbacks -------- Deferred's error handling is modeled after Python's exception handling. In the case that no errors occur, all the callbacks run, one after the other, as described above. If the errback is called instead of the callback (e.g. because a DB query raised an error), then a :py:class:`twisted.python.failure.Failure` is passed into the first errback (you can add multiple errbacks, just like with callbacks). You can think of your errbacks as being like ``except`` blocks of ordinary Python code. Unless you explicitly ``raise`` an error in an except block, the ``Exception`` is caught and stops propagating, and normal execution continues. The same thing happens with errbacks: unless you explicitly ``return`` a ``Failure`` or (re-)raise an exception, the error stops propagating, and normal callbacks continue executing from that point (using the value returned from the errback). If the errback does return a ``Failure`` or raise an exception, then that is passed to the next errback, and so on. *Note:* If an errback doesn't return anything, then it effectively returns ``None``, meaning that callbacks will continue to be executed after this errback. This may not be what you expect to happen, so be careful. Make sure your errbacks return a ``Failure`` (probably the one that was passed to it), or a meaningful return value for the next callback. Also, :py:class:`twisted.python.failure.Failure` instances have a useful method called trap, allowing you to effectively do the equivalent of: .. code-block:: python try: # code that may throw an exception cookSpamAndEggs() except (SpamException, EggException): # Handle SpamExceptions and EggExceptions ... You do this by: .. code-block:: python def errorHandler(failure): failure.trap(SpamException, EggException) # Handle SpamExceptions and EggExceptions d.addCallback(cookSpamAndEggs) d.addErrback(errorHandler) If none of arguments passed to ``failure.trap`` match the error encapsulated in that ``Failure``, then it re-raises the error. There's another potential "gotcha" here. There's a method :py:meth:`twisted.internet.defer.Deferred.addCallbacks` which is similar to, but not exactly the same as, ``addCallback`` followed by ``addErrback``. In particular, consider these two cases: .. code-block:: python # Case 1 d = getDeferredFromSomewhere() d.addCallback(callback1) # A d.addErrback(errback1) # B d.addCallback(callback2) d.addErrback(errback2) # Case 2 d = getDeferredFromSomewhere() d.addCallbacks(callback1, errback1) # C d.addCallbacks(callback2, errback2) If an error occurs in ``callback1``, then for Case 1 ``errback1`` will be called with the failure. For Case 2, ``errback2`` will be called. Be careful with your callbacks and errbacks. What this means in a practical sense is in Case 1, the callback in line A will handle a success condition from ``getDeferredFromSomewhere``, and the errback in line B will handle any errors that occur *from either the upstream source, or that occur in A*. In Case 2, the errback in line C *will only handle an error condition raised by* ``getDeferredFromSomewhere``, it will not do any handling of errors raised in ``callback1``. Unhandled Errors ~~~~~~~~~~~~~~~~ If a Deferred is garbage-collected with an unhandled error (i.e. it would call the next errback if there was one), then Twisted will write the error's traceback to the log file. This means that you can typically get away with not adding errbacks and still get errors logged. Be careful though; if you keep a reference to the Deferred around, preventing it from being garbage-collected, then you may never see the error (and your callbacks will mysteriously seem to have never been called). If unsure, you should explicitly add an errback after your callbacks, even if all you do is: .. code-block:: python # Make sure errors get logged from twisted.python import log d.addErrback(log.err) Handling either synchronous or asynchronous results --------------------------------------------------- In some applications, there are functions that might be either asynchronous or synchronous. For example, a user authentication function might be able to check in memory whether a user is authenticated, allowing the authentication function to return an immediate result, or it may need to wait on network data, in which case it should return a Deferred to be fired when that data arrives. However, a function that wants to check if a user is authenticated will then need to accept both immediate results *and* Deferreds. In this example, the library function ``authenticateUser`` uses the application function ``isValidUser`` to authenticate a user: .. code-block:: python def authenticateUser(isValidUser, user): if isValidUser(user): print("User is authenticated") else: print("User is not authenticated") However, it assumes that ``isValidUser`` returns immediately, whereas ``isValidUser`` may actually authenticate the user asynchronously and return a Deferred. It is possible to adapt this trivial user authentication code to accept either a synchronous ``isValidUser`` or an asynchronous ``isValidUser``, allowing the library to handle either type of function. It is, however, also possible to adapt synchronous functions to return Deferreds. This section describes both alternatives: handling functions that might be synchronous or asynchronous in the library function (``authenticateUser``) or in the application code. Handling possible Deferreds in the library code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is an example of a synchronous user authentication function that might be passed to ``authenticateUser``: :download:`synch-validation.py ` .. literalinclude:: listings/deferred/synch-validation.py However, here's an ``asynchronousIsValidUser`` function that returns a Deferred: .. code-block:: python from twisted.internet import reactor, defer def asynchronousIsValidUser(user): d = defer.Deferred() reactor.callLater(2, d.callback, user in ["Alice", "Angus", "Agnes"]) return d Our original implementation of ``authenticateUser`` expected ``isValidUser`` to be synchronous, but now we need to change it to handle both synchronous and asynchronous implementations of ``isValidUser``. For this, we use :py:func:`maybeDeferred ` to call ``isValidUser``, ensuring that the result of ``isValidUser`` is a Deferred, even if ``isValidUser`` is a synchronous function: .. code-block:: python from twisted.internet import defer def printResult(result): if result: print("User is authenticated") else: print("User is not authenticated") def authenticateUser(isValidUser, user): d = defer.maybeDeferred(isValidUser, user) d.addCallback(printResult) Now ``isValidUser`` could be either ``synchronousIsValidUser`` or ``asynchronousIsValidUser``. It is also possible to modify ``synchronousIsValidUser`` to return a Deferred, see :doc:`Generating Deferreds ` for more information. .. _core-howto-defer-deferreds-cancellation: Cancellation ------------ Motivation ~~~~~~~~~~ A Deferred may take any amount of time to be called back; in fact, it may never be called back. Your users may not be that patient. Since all actions taken when the Deferred completes are in your application or library's callback code, you always have the option of simply disregarding the result when you receive it, if it's been too long. However, while you're ignoring it, the underlying operation represented by that Deferred is still chugging along in the background, possibly consuming resources such as CPU time, memory, network bandwidth and maybe even disk space. So, when the user has closed the window, hit the cancel button, disconnected from your server or sent a "stop" network message, you will want to announce your indifference to the result of that operation so that the originator of the Deferred can clean everything up and free those resources to be put to better use. Cancellation for Applications which Consume Deferreds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here's a simple example. You're connecting to an external host with an :doc:`endpoint `, but that host is really slow. You want to put a "cancel" button into your application to terminate the connection attempt, so the user can try connecting to a different host instead. Here's a simple sketch of such an application, with the actual user interface left as an exercise for the reader: .. code-block:: python def startConnecting(someEndpoint): def connected(it): "Do something useful when connected." return someEndpoint.connect(myFactory).addCallback(connected) # ... connectionAttempt = startConnecting(endpoint) def cancelClicked(): connectionAttempt.cancel() Obviously (I hope), startConnecting is meant to be called by some UI element that lets the user choose what host to connect to and then constructs an appropriate endpoint (perhaps using ``twisted.internet.endpoints.clientFromString``). Then, a cancel button, or similar, is hooked up to the ``cancelClicked``. When ``connectionAttempt.cancel`` is invoked, that will: #. cause the underlying connection operation to be terminated, if it is still ongoing #. cause the connectionAttempt Deferred to be completed, one way or another, in a timely manner #. *likely* cause the connectionAttempt Deferred to be errbacked with :py:class:`CancelledError ` You may notice that that set of consequences is very heavily qualified. Although cancellation indicates the calling API's *desire* for the underlying operation to be stopped, the underlying operation cannot necessarily react immediately. Even in this very simple example, there is already one thing that might not be interruptible: platform-native name resolution blocks, and therefore needs to be executed in a thread; the connection operation can't be cancelled if it's stuck waiting for a name to be resolved in this manner. So, the Deferred that you are cancelling may not callback or errback right away. A Deferred may wait upon another Deferred at any point in its callback chain (see "Handling...asynchronous results", above). There's no way for a particular point in the callback chain to know if everything is finished. Since multiple layers of the callback chain may wish to cancel the same Deferred, any layer may call ``.cancel()`` at any time. The ``.cancel()`` method never raises any exception or returns any value; you may call it repeatedly, even on a Deferred which has already fired, or which has no remaining callbacks. The main reason for all these qualifications, aside from specific examples, is that anyone who instantiates a Deferred may supply it with a cancellation function; that function can do absolutely anything that it wants to. Ideally, anything it does will be in the service of stopping the operation your requested, but there's no way to guarantee any exact behavior across all Deferreds that might be cancelled. Cancellation of Deferreds is best effort. This may be the case for a number of reasons: #. The ``Deferred`` doesn't know how to cancel the underlying operation. #. The underlying operation may have reached an uncancellable state, because some irreversible operation has been done. #. The ``Deferred`` may already have a result, and so there's nothing to cancel. Calling ``cancel()`` will always succeed without an error regardless of whether or not cancellation was possible. In cases 1 and 2 the ``Deferred`` may well errback with a ``twisted.internet.defer.CancelledError`` while the underlying operation continues. ``Deferred`` s that support cancellation should document what they do when cancelled, if they are uncancellable in certain edge cases, etc.. If the cancelled ``Deferred`` is waiting on another ``Deferred``, the cancellation will be forwarded to the other ``Deferred``. Default Cancellation Behavior ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ All Deferreds support cancellation. However, by default, they support a very rudimentary form of cancellation which doesn't free any resources. Consider this example of a Deferred which is ignorant of cancellation: .. code-block:: python operation = Deferred() def x(result): print("Hooray, a result:" + repr(x)) operation.addCallback(x) # ... def operationDone(): operation.callback("completed") A caller of an API that receives ``operation`` may call ``cancel`` on it. Since ``operation`` does not have a cancellation function, one of two things will happen. #. If ``operationDone`` has been called, and the operation has completed, nothing much will change. ``operation`` will still have a result, and there are no more callbacks, so there's no observable change in behavior. #. If ``operationDone`` has *not* yet been invoked, then ``operation`` will be immediately errbacked with a ``CancelledError``. However, once it's cancelled, there's no way to tell ``operationDone`` not to run; it will eventually call ``operation.callback`` later. In normal operation, issuing ``callback`` on a ``Deferred`` that has already called back results in an ``AlreadyCalledError``, and this would cause an ugly traceback that could not be caught. Therefore, ``.callback`` can be invoked exactly once, causing a no-op, on a ``Deferred`` which has been cancelled but has no canceller. If you call it multiple times, you will still get an ``AlreadyCalledError`` exception. Creating Cancellable Deferreds: Custom Cancellation Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Let's imagine you are implementing an HTTP client, which returns a Deferred firing with the response from the server. Cancellation is best achieved by closing the connection. In order to make cancellation do that, all you have to do is pass a function to the constructor of the Deferred (it will get called with the Deferred that is being cancelled): .. code-block:: python class HTTPClient(Protocol): def request(self, method, path): self.resultDeferred = Deferred( lambda ignore: self.transport.abortConnection()) request = b"%s %s HTTP/1.0\r\n\r\n" % (method, path) self.transport.write(request) return self.resultDeferred def dataReceived(self, data): # ... parse HTTP response ... # ... eventually call self.resultDeferred.callback() ... Now if someone calls ``cancel()`` on the ``Deferred`` returned from ``HTTPClient.request()``, the HTTP request will be cancelled (assuming it's not too late to do so). Care should be taken not to ``callback()`` a Deferred that has already been cancelled. .. _core-howto-defer-deferreds-timeouts: Timeouts -------- Timeouts are a special case of :ref:`Cancellation `. Let's say we have a :py:class:`Deferred ` representing a task that may take a long time. We want to put an upper bound on that task, so we want the :py:class:`Deferred ` to time out X seconds in the future. A convenient API to do so is :py:meth:`Deferred.addTimeout `. By default, it will fail with a :py:class:`TimeoutError ` if the :py:class:`Deferred ` hasn't fired (with either an errback or a callback) within ``timeout`` seconds. .. code-block:: python import random from twisted.internet import task def f(): return "Hopefully this will be called in 3 seconds or less" def main(reactor): delay = random.uniform(1, 5) def called(result): print("{0} seconds later:".format(delay), result) d = task.deferLater(reactor, delay, f) d.addTimeout(3, reactor).addBoth(called) return d # f() will be timed out if the random delay is greater than 3 seconds task.react(main) :py:meth:`Deferred.addTimeout ` uses the :py:meth:`Deferred.cancel ` function under the hood, but can distinguish between a user's call to :py:meth:`Deferred.cancel ` and a cancellation due to a timeout. By default, :py:meth:`Deferred.addTimeout ` translates a :py:class:`CancelledError ` produced by the timeout into a :py:class:`TimeoutError `. However, if you provided a custom :ref:`cancellation ` when creating the :py:class:`Deferred `, then cancelling it may not produce a :py:class:`CancelledError `. In this case, the default behavior of :py:meth:`Deferred.addTimeout ` is to preserve whatever callback or errback value your custom cancellation function produced. This can be useful if, for instance, a cancellation or timeout should produce a default value instead of an error. :py:meth:`Deferred.addTimeout ` also takes an optional callable ``onTimeoutCancel`` which is called immediately after the deferred times out. ``onTimeoutCancel`` is not called if it the deferred is otherwise cancelled before the timeout. It takes an arbitrary value, which is the value of the deferred at that exact time (probably a :py:class:`CancelledError ` :py:class:`Failure `), and the ``timeout``. This can be useful if, for instance, the cancellation or timeout does not result in an error but you want to log the timeout anyway. It can also be used to alter the return value. .. code-block:: python from twisted.internet import task, defer def logTimeout(result, timeout): print("Got {0!r} but actually timed out after {1} seconds".format( result, timeout)) return result + " (timed out)" def main(reactor): # generate a deferred with a custom canceller function, and never # never callback or errback it to guarantee it gets timed out d = defer.Deferred(lambda c: c.callback("Everything's ok!")) d.addTimeout(2, reactor, onTimeoutCancel=logTimeout) d.addBoth(print) return d task.react(main) Note that the exact place in the callback chain that :py:meth:`Deferred.addTimeout ` is added determines how much of the callback chain should be timed out. The timeout encompasses all the callbacks and errbacks added to the :py:class:`Deferred ` before the call to :py:meth:`addTimeout `, and none of the callbacks and errbacks added after the call. The timeout also starts counting down as soon as soon as it's invoked. .. _core-howto-defer-deferredlist: DeferredList ------------ Sometimes you want to be notified after several different events have all happened, rather than waiting for each one individually. For example, you may want to wait for all the connections in a list to close. :py:class:`twisted.internet.defer.DeferredList` is the way to do this. To create a DeferredList from multiple Deferreds, you simply pass a list of the Deferreds you want it to wait for: .. code-block:: python # Creates a DeferredList dl = defer.DeferredList([deferred1, deferred2, deferred3]) You can now treat the DeferredList like an ordinary Deferred; you can call ``addCallbacks`` and so on. The DeferredList will call its callback when all the deferreds have completed. The callback will be called with a list of the results of the Deferreds it contains, like so: .. code-block:: python # A callback that unpacks and prints the results of a DeferredList def printResult(result): for (success, value) in result: if success: print('Success:', value) else: print('Failure:', value.getErrorMessage()) # Create three deferreds. deferred1 = defer.Deferred() deferred2 = defer.Deferred() deferred3 = defer.Deferred() # Pack them into a DeferredList dl = defer.DeferredList([deferred1, deferred2, deferred3], consumeErrors=True) # Add our callback dl.addCallback(printResult) # Fire our three deferreds with various values. deferred1.callback('one') deferred2.errback(Exception('bang!')) deferred3.callback('three') # At this point, dl will fire its callback, printing: # Success: one # Failure: bang! # Success: three # (note that defer.SUCCESS == True, and defer.FAILURE == False) A standard DeferredList will never call errback, but failures in Deferreds passed to a DeferredList will still errback unless ``consumeErrors`` is passed ``True``. See below for more details about this and other flags which modify the behavior of DeferredList. .. note:: If you want to apply callbacks to the individual Deferreds that go into the DeferredList, you should be careful about when those callbacks are added. The act of adding a Deferred to a DeferredList inserts a callback into that Deferred (when that callback is run, it checks to see if the DeferredList has been completed yet). The important thing to remember is that it is *this callback* which records the value that goes into the result list handed to the DeferredList's callback. .. TODO: add picture here: three columns of callback chains, with a value being snarfed out of the middle of each and handed off to the DeferredList Therefore, if you add a callback to the Deferred *after* adding the Deferred to the DeferredList, the value returned by that callback will not be given to the DeferredList's callback. To avoid confusion, we recommend not adding callbacks to a Deferred once it has been used in a DeferredList. .. code-block:: python def printResult(result): print(result) def addTen(result): return result + " ten" # Deferred gets callback before DeferredList is created deferred1 = defer.Deferred() deferred2 = defer.Deferred() deferred1.addCallback(addTen) dl = defer.DeferredList([deferred1, deferred2]) dl.addCallback(printResult) deferred1.callback("one") # fires addTen, checks DeferredList, stores "one ten" deferred2.callback("two") # At this point, dl will fire its callback, printing: # [(1, 'one ten'), (1, 'two')] # Deferred gets callback after DeferredList is created deferred1 = defer.Deferred() deferred2 = defer.Deferred() dl = defer.DeferredList([deferred1, deferred2]) deferred1.addCallback(addTen) # will run *after* DeferredList gets its value dl.addCallback(printResult) deferred1.callback("one") # checks DeferredList, stores "one", fires addTen deferred2.callback("two") # At this point, dl will fire its callback, printing: # [(1, 'one), (1, 'two')] Other behaviours ~~~~~~~~~~~~~~~~ DeferredList accepts three keyword arguments that modify its behaviour: ``fireOnOneCallback`` , ``fireOnOneErrback`` and ``consumeErrors``. If ``fireOnOneCallback`` is set, the DeferredList will immediately call its callback as soon as any of its Deferreds call their callback. Similarly, ``fireOnOneErrback`` will call errback as soon as any of the Deferreds call their errback. Note that DeferredList is still one-shot, like ordinary Deferreds, so after a callback or errback has been called the DeferredList will do nothing further (it will just silently ignore any other results from its Deferreds). The ``fireOnOneErrback`` option is particularly useful when you want to wait for all the results if everything succeeds, but also want to know immediately if something fails. The ``consumeErrors`` argument will stop the DeferredList from propagating any errors along the callback chains of any Deferreds it contains (usually creating a DeferredList has no effect on the results passed along the callbacks and errbacks of their Deferreds). Stopping errors at the DeferredList with this option will prevent "Unhandled error in Deferred" warnings from the Deferreds it contains without needing to add extra errbacks [#]_ . Passing a true value for the ``consumeErrors`` parameter will not change the behavior of ``fireOnOneCallback`` or ``fireOnOneErrback``. gatherResults ~~~~~~~~~~~~~ A common use for DeferredList is to "join" a number of parallel asynchronous operations, finishing successfully if all of the operations were successful, or failing if any one of the operations fails. In this case, :py:func:`twisted.internet.defer.gatherResults` is a useful shortcut: .. code-block:: python from twisted.internet import defer d1 = defer.Deferred() d2 = defer.Deferred() d = defer.gatherResults([d1, d2], consumeErrors=True) def cbPrintResult(result): print(result) d.addCallback(cbPrintResult) d1.callback("one") # nothing is printed yet; d is still awaiting completion of d2 d2.callback("two") # printResult prints ["one", "two"] The ``consumeErrors`` argument has the same meaning as it does for :ref:`DeferredList `: if true, it causes ``gatherResults`` to consume any errors in the passed-in Deferreds. Always use this argument unless you are adding further callbacks or errbacks to the passed-in Deferreds, or unless you know that they will not fail. Otherwise, a failure will result in an unhandled error being logged by Twisted. This argument is available since Twisted 11.1.0. .. _core-howto-defer-class: Class Overview -------------- This is an overview API reference for Deferred from the point of using a Deferred returned by a function. It is not meant to be a substitute for the docstrings in the Deferred class, but can provide guidelines for its use. There is a parallel overview of functions used by the Deferred's *creator* in :ref:`Generating Deferreds `. Basic Callback Functions ~~~~~~~~~~~~~~~~~~~~~~~~ - ``addCallbacks(self, callback[, errback, callbackArgs, callbackKeywords, errbackArgs, errbackKeywords])`` This is the method you will use to interact with Deferred. It adds a pair of callbacks "parallel" to each other (see diagram above) in the list of callbacks made when the Deferred is called back to. The signature of a method added using addCallbacks should be ``myMethod(result, *methodAsrgs, **methodKeywords)``. If your method is passed in the callback slot, for example, all arguments in the tuple ``callbackArgs`` will be passed as ``*methodArgs`` to your method. There are various convenience methods that are derivative of addCallbacks. I will not cover them in detail here, but it is important to know about them in order to create concise code. - ``addCallback(callback, *callbackArgs, **callbackKeywords)`` Adds your callback at the next point in the processing chain, while adding an errback that will re-raise its first argument, not affecting further processing in the error case. Note that, while addCallbacks (plural) requires the arguments to be passed in a tuple, addCallback (singular) takes all its remaining arguments as things to be passed to the callback function. The reason is obvious: addCallbacks (plural) cannot tell whether the arguments are meant for the callback or the errback, so they must be specifically marked by putting them into a tuple. addCallback (singular) knows that everything is destined to go to the callback, so it can use Python's "*" and "**" syntax to collect the remaining arguments. - ``addErrback(errback, *errbackArgs, **errbackKeywords)`` Adds your errback at the next point in the processing chain, while adding a callback that will return its first argument, not affecting further processing in the success case. - ``addBoth(callbackOrErrback, *callbackOrErrbackArgs, **callbackOrErrbackKeywords)`` This method adds the same callback into both sides of the processing chain at both points. Keep in mind that the type of the first argument is indeterminate if you use this method! Use it for ``finally:`` style blocks. Chaining Deferreds ~~~~~~~~~~~~~~~~~~ If you need one Deferred to wait on another, all you need to do is return a Deferred from a method added to addCallbacks. Specifically, if you return Deferred B from a method added to Deferred A using A.addCallbacks, Deferred A's processing chain will stop until Deferred B's .callback() method is called; at that point, the next callback in A will be passed the result of the last callback in Deferred B's processing chain at the time. .. note:: If a Deferred is somehow returned from its *own* callbacks (directly or indirectly), the behavior is undefined. The Deferred code will make an attempt to detect this situation and produce a warning. In the future, this will become an exception. If this seems confusing, don't worry about it right now -- when you run into a situation where you need this behavior, you will probably recognize it immediately and realize why this happens. If you want to chain deferreds manually, there is also a convenience method to help you. - ``chainDeferred(otherDeferred)`` Add ``otherDeferred`` to the end of this Deferred's processing chain. When self.callback is called, the result of my processing chain up to this point will be passed to ``otherDeferred.callback``. Further additions to my callback chain do not affect ``otherDeferred``. This is the same as ``self.addCallbacks(otherDeferred.callback, otherDeferred.errback)``. See also -------- #. :doc:`Generating Deferreds `, an introduction to writing asynchronous functions that return Deferreds. .. rubric:: Footnotes .. [#] Unless of course a later callback starts a fresh error — but as we've already noted, adding callbacks to a Deferred after its used in a DeferredList is confusing and usually avoided. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/design.rst0000644000175100001710000001065700000000000017613 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Designing Twisted Applications ============================== Goals ----- This document describes how a good Twisted application is structured. It should be useful for beginning Twisted developers who want to structure their code in a clean, maintainable way that reflects current best practices. Readers will want to be familiar with writing :doc:`servers ` and :doc:`clients ` using Twisted. Example of a modular design: TwistedQuotes ------------------------------------------ ``TwistedQuotes`` is a very simple plugin which is a great demonstration of Twisted's power. It will export a small kernel of functionality -- Quote of the Day -- which can be accessed through every interface that Twisted supports: web pages, e-mail, instant messaging, a specific Quote of the Day protocol, and more. Set up the project directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See the description of :doc:`setting up the TwistedQuotes example ` . A Look at the Heart of the Application ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :download:`quoters.py ` .. literalinclude:: listings/TwistedQuotes/quoters.py This code listing shows us what the Twisted Quotes system is all about. The code doesn't have any way of talking to the outside world, but it provides a library which is a clear and uncluttered abstraction: "give me the quote of the day" . Note that this module does not import any Twisted functionality at all! The reason for doing things this way is integration. If your "business objects" are not stuck to your user interface, you can make a module that can integrate those objects with different protocols, GUIs, and file formats. Having such classes provides a way to decouple your components from each other, by allowing each to be used independently. In this manner, Twisted itself has minimal impact on the logic of your program. Although the Twisted "dot products" are highly interoperable, they also follow this approach. You can use them independently because they are not stuck to each other. They communicate in well-defined ways, and only when that communication provides some additional feature. Thus, you can use :py:mod:`twisted.web` with :py:mod:`twisted.enterprise` , but neither requires the other, because they are integrated around the concept of :doc:`Deferreds ` . Your Twisted applications should follow this style as much as possible. Have (at least) one module which implements your specific functionality, independent of any user-interface code. Next, we're going to need to associate this abstract logic with some way of displaying it to the user. We'll do this by writing a Twisted server protocol, which will respond to the clients that connect to it by sending a quote to the client and then closing the connection. Note: don't get too focused on the details of this -- different ways to interface with the user are 90% of what Twisted does, and there are lots of documents describing the different ways to do it. :download:`quoteproto.py ` .. literalinclude:: listings/TwistedQuotes/quoteproto.py This is a very straightforward ``Protocol`` implementation, and the pattern described above is repeated here. The Protocol contains essentially no logic of its own, just enough to tie together an object which can generate quotes (a ``Quoter`` ) and an object which can relay bytes to a TCP connection (a ``Transport`` ). When a client connects to this server, a ``QOTD`` instance is created, and its ``connectionMade`` method is called. The ``QOTDFactory`` 's role is to specify to the Twisted framework how to create a ``Protocol`` instance that will handle the connection. Twisted will not instantiate a ``QOTDFactory`` ; you will do that yourself later, in a ``twistd`` plug-in. Note: you can read more specifics of ``Protocol`` and ``Factory`` in the :doc:`Writing Servers ` HOWTO. Once we have an abstraction -- a ``Quoter`` -- and we have a mechanism to connect it to the network -- the ``QOTD`` protocol -- the next thing to do is to put the last link in the chain of functionality between abstraction and user. This last link will allow a user to choose a ``Quoter`` and configure the protocol. Writing this configuration is covered in the :doc:`Application HOWTO ` . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/dirdbm.rst0000644000175100001710000000407500000000000017600 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ DirDBM: Directory-based Storage =============================== dirdbm.DirDBM ------------- :py:class:`twisted.persisted.dirdbm.DirDBM` is a DBM-like storage system. That is, it stores mappings between keys and values, like a Python dictionary, except that it stores the values in files in a directory - each entry is a different file. The keys must always be strings, as are the values. Other than that, :py:class:`DirDBM ` objects act just like Python dictionaries. :py:class:`DirDBM ` is useful for cases when you want to store small amounts of data in an organized fashion, without having to deal with the complexity of a RDBMS or other sophisticated database. It is simple, easy to use, cross-platform, and doesn't require any external C libraries, unlike Python's built-in DBM modules. .. code-block:: pycon >>> from twisted.persisted import dirdbm >>> d = dirdbm.DirDBM("/tmp/dir") >>> d["librarian"] = "ook" >>> d["librarian"] 'ook' >>> d.keys() ['librarian'] >>> del d["librarian"] >>> d.items() [] dirdbm.Shelf ------------ Sometimes it is necessary to persist more complicated objects than strings. With some care, :py:class:`dirdbm.Shelf ` can transparently persist them. ``Shelf`` works exactly like ``DirDBM`` , except that the values (but not the keys) can be arbitrary picklable objects. However, notice that mutating an object after it has been stored in the ``Shelf`` has no effect on the Shelf. When mutating objects, it is necessary to explicitly store them back in the ``Shelf`` afterwards: .. code-block:: pycon >>> from twisted.persisted import dirdbm >>> d = dirdbm.Shelf("/tmp/dir2") >>> d["key"] = [1, 2] >>> d["key"] [1, 2] >>> l = d["key"] >>> l.append(3) >>> d["key"] [1, 2] >>> d["key"] = l >>> d["key"] [1, 2, 3] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/endpoints.rst0000644000175100001710000006222500000000000020343 0ustar00runnerdocker:LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Getting Connected with Endpoints ================================ Introduction ------------ On a network, one can think of any given connection as a long wire, stretched between two points. Lots of stuff can happen along the length of that wire - routers, switches, network address translation, and so on, but that is usually invisible to the application passing data across it. Twisted strives to make the nature of the "wire" as transparent as possible, with highly abstract interfaces for passing and receiving data, such as :py:class:`ITransport ` and :py:class:`IProtocol `. However, the application can't be completely ignorant of the wire. In particular, it must do something to *start* the connection, and to do so, it must identify the *end points* of the wire. There are different names for the roles of each end point - "initiator" and "responder", "connector" and "listener", or "client" and "server" - but the common theme is that one side of the connection waits around for someone to connect to it, and the other side does the connecting. In Twisted 10.1, several new interfaces were introduced to describe each of these roles for stream-oriented connections: :py:class:`IStreamServerEndpoint ` and :py:class:`IStreamClientEndpoint `. The word "stream", in this case, refers to endpoints which treat a connection as a continuous stream of bytes, rather than a sequence of discrete datagrams: TCP is a "stream" protocol whereas UDP is a "datagram" protocol. Constructing and Using Endpoints -------------------------------- In both :doc:`Writing Servers ` and :doc:`Writing Clients `, we covered basic usage of endpoints; you construct an appropriate type of server or client endpoint, and then call ``listen`` (for servers) or ``connect`` (for clients). In both of those tutorials, we constructed specific types of endpoints directly. However, in most programs, you will want to allow the user to specify where to listen or connect, in a way which will allow the user to request different strategies, without having to adjust your program. In order to allow this, you should use :py:func:`clientFromString ` or :py:func:`serverFromString `. There's Not Much To It ~~~~~~~~~~~~~~~~~~~~~~ Each type of endpoint is just an interface with a single method that takes an argument. ``serverEndpoint.listen(factory)`` will start listening on that endpoint with your protocol factory, and ``clientEndpoint.connect(factory)`` will start a single connection attempt. Each of these APIs returns a value, though, which can be important. However, if you are not already, you *should* be very familiar with :doc:`Deferreds `, as they are returned by both ``connect`` and ``listen`` methods, to indicate when the connection has connected or the listening port is up and running. Servers and Stopping ~~~~~~~~~~~~~~~~~~~~ :py:meth:`IStreamServerEndpoint.listen ` returns a :py:class:`Deferred ` that fires with an :py:class:`IListeningPort `. Note that this deferred may errback. The most common cause of such an error would be that another program is already using the requested port number, but the exact cause may vary depending on what type of endpoint you are listening on. If you receive such an error, it means that your application is not actually listening, and will not receive any incoming connections. It's important to somehow alert an administrator of your server, in this case, especially if you only have one listening port! Note also that once this has succeeded, it will continue listening forever. If you need to *stop* listening for some reason, in response to anything other than a full server shutdown (``reactor.stop`` and / or ``twistd`` will usually handle that case for you), make sure you keep a reference around to that listening port object so you can call :py:meth:`IListeningPort.stopListening ` on it. Finally, keep in mind that ``stopListening`` itself returns a ``Deferred``, and the port may not have fully stopped listening until that ``Deferred`` has fired. Most server applications will not need to worry about these details. One example of a case where you would need to be concerned with all of these events would be an implementation of a protocol like non-``PASV`` FTP, where new listening ports need to be bound for the lifetime of a particular action, then disposed of. Clients and Cancelling ~~~~~~~~~~~~~~~~~~~~~~ :py:func:`connectProtocol ` connects a :py:class:`Protocol ` instance to a given :py:class:`IStreamClientEndpoint `. It returns a ``Deferred`` which fires with the ``Protocol`` once the connection has been made. Connection attempts may fail, and so that :py:class:`Deferred ` may also errback. If it does so, you will have to try again; no further attempts will be made. See the :doc:`client documentation ` for an example use. :py:func:`connectProtocol ` is a wrapper around a lower-level API: :py:meth:`IStreamClientEndpoint.connect ` will use a protocol factory for a new outgoing connection attempt. It returns a ``Deferred`` which fires with the ``IProtocol`` returned from the factory's ``buildProtocol`` method, or errbacks with the connection failure. Connection attempts may also take a long time, and your users may become bored and wander off. If this happens, and your code decides, for whatever reason, that you've been waiting for the connection too long, you can call :py:meth:`Deferred.cancel ` on the ``Deferred`` returned from :py:meth:`connect ` or :py:func:`connectProtocol `, and the underlying machinery should give up on the connection. This should cause the ``Deferred`` to errback, usually with :py:class:`CancelledError `; although you should consult the documentation for your particular endpoint type to see if it may do something different. Although some endpoint types may imply a built-in timeout, the interface does not guarantee one. If you don't have any way for the application to cancel a wayward connection attempt, the attempt may just keep waiting forever. For example, a very simple 30-second timeout could be implemented like this: .. code-block:: python attempt = connectProtocol(myEndpoint, myProtocol) reactor.callLater(30, attempt.cancel) .. note:: If you've used ``ClientFactory`` before, keep in mind that the ``connect`` method takes a ``Factory``, not a ``ClientFactory``. Even if you pass a ``ClientFactory`` to ``endpoint.connect``, its ``clientConnectionFailed`` and ``clientConnectionLost`` methods will not be called. In particular, clients that extend ``ReconnectingClientFactory`` won't reconnect. The next section describes how to set up reconnecting clients on endpoints. Persistent Client Connections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :py:class:`twisted.application.internet.ClientService` can maintain a persistent outgoing connection to a server which can be started and stopped along with your application. One popular protocol to maintain a long-lived client connection to is IRC, so for an example of ``ClientService``, here's how you would make a long-lived encrypted connection to an IRC server (other details, like how to authenticate, omitted for brevity): .. code-block:: python from twisted.internet.protocol import Factory from twisted.internet.endpoints import clientFromString from twisted.words.protocols.irc import IRCClient from twisted.application.internet import ClientService from twisted.internet import reactor myEndpoint = clientFromString(reactor, "tls:example.com:6997") myFactory = Factory.forProtocol(IRCClient) myReconnectingService = ClientService(myEndpoint, myFactory) If you already have a parent service, you can add the reconnecting service as a child service: .. code-block:: python parentService.addService(myReconnectingService) If you do not have a parent service, you can start and stop the reconnecting service using its ``startService`` and ``stopService`` methods. ``ClientService.stopService`` returns a ``Deferred`` that fires once the current connection closes or the current connection attempt is cancelled. Getting The Active Client ------------------------- When maintaining a long-lived connection, it's often useful to be able to get the current connection (if the connection is active) or wait for the next connection (if a connection attempt is currently in progress). For example, we might want to pass our ``ClientService`` from the previous example to some code that can send IRC notifications in response to some external event. The ``ClientService.whenConnected`` method returns a ``Deferred`` that fires with the next available ``Protocol`` instance. You can use it like so: .. code-block:: python waitForConnection = myReconnectingService.whenConnected() def connectedNow(clientForIRC): clientForIRC.say("#bot-test", "hello, world!") waitForConnection.addCallback(connectedNow) Keep in mind that you may need to wrap this up for your particular application, since when no existing connection is available, the callback is executed just as soon as the connection is established. For example, that little snippet is slightly oversimplified: at the time ``connectedNow`` is run, the bot hasn't authenticated or joined the channel yet, so its message will be refused. A real-life IRC bot would need to have its own method for waiting until the connection is fully ready for chat before chatting. Reporting an Initial Failure ---------------------------- Often times, a failure of the very first connection attempt is special. It may indicate a problem that won't go away by just trying harder. The service may be configured with the wrong hostname, or the user may not have an internet connection at all (perhaps they forgot to turn on their wifi adapter). Applications can ask ``whenConnected`` to make their ``Deferred`` fail if the service makes one or more connection attempts in a row without success. You can pass the ``failAfterFailures`` parameter into ``ClientService`` to set this threshold. By calling ``whenConnected(failAfterFailures=1)`` when the service is first started (just before or just after ``startService``), your application will get notification of an initial connection failure. Setting it to 1 makes it fail after a single connection failure. Setting it to 2 means it will try once, wait a bit, try again, and then either fail or succeed depending upon the outcome of the second connection attempt. You can use 3 or more too, if you're feeling particularly patient. The default of ``None`` means it will wait forever for a successful connection. Regardless of ``failAfterFailures``, the ``Deferred`` will always fail with :py:class:`CancelledError ` if the service is stopped before a connection is made. .. code-block:: python waitForConnection = myReconnectingService.whenConnected(failAfterFailures=1) def connectedNow(clientForIRC): clientForIRC.say("#bot-test", "hello, world!") def failed(f): print("initial connection failed: %s" % (f,)) # now you should stop the service and report the error upwards waitForConnection.addCallbacks(connectedNow, failed) Retry Policies -------------- ``ClientService`` will immediately attempt an outgoing connection when ``startService`` is called. If that connection attempt fails for any reason (name resolution, connection refused, network unreachable, and so on), it will retry according to the policy specified in the ``retryPolicy`` constructor argument. By default, ``ClientService`` will use an exponential backoff algorithm with a minimum delay of 1 second and a maximum delay of 1 minute, and a jitter of up to 1 additional second to prevent stampeding-herd performance cascades. This is a good default, and if you do not have highly specialized requirements, you probably want to use it. If you need to tune these parameters, you have two options: 1. You can pass your own timeout policy to ``ClientService``'s constructor. A timeout policy is a callable that takes the number of failed attempts, and computes a delay until the next connection attempt. So, for example, if you are *really really sure* that you want to reconnect *every single second* if the service you are talking to goes down, you can do this: .. code-block:: python myReconnectingService = ClientService(myEndpoint, myFactory, retryPolicy=lambda ignored: 1) Of course, unless you have only one client and only one server and they're both on localhost, this sort of policy is likely to cause massive performance degradation and thundering herd resource contention in the event of your server's failure, so you probably want to take the second option... 2. You can tweak the default exponential backoff policy with a few parameters by passing the result of :py:func:`twisted.application.internet.backoffPolicy` to the ``retryPolicy`` argument. For example, if you want to make it triple the delay between attempts, but start with a faster connection interval (half a second instead of one second), you could do it like so: .. code-block:: python myReconnectingService = ClientService( myEndpoint, myFactory, retryPolicy=backoffPolicy(initialDelay=0.5, factor=3.0) ) .. note:: Before endpoints, reconnecting clients were created as subclasses of ``ReconnectingClientFactory``. These subclasses were required to call ``resetDelay``. One of the many advantages of using endpoints is that these special subclasses are no longer needed. ``ClientService`` accepts ordinary ``IProtocolFactory`` providers. Maximizing the Return on your Endpoint Investment ------------------------------------------------- Directly constructing an endpoint in your application is rarely the best option, because it ties your application to a particular type of transport. The strength of the endpoints API is in separating the construction of the endpoint (figuring out where to connect or listen) and its activation (actually connecting or listening). If you are implementing a library that needs to listen for connections or make outgoing connections, when possible, you should write your code to accept client and server endpoints as parameters to functions or to your objects' constructors. That way, application code that calls your library can provide whatever endpoints are appropriate. If you are writing an application and you need to construct endpoints yourself, you can allow users to specify arbitrary endpoints described by a string using the :py:func:`clientFromString ` and :py:func:`serverFromString ` APIs. Since these APIs just take a string, they provide flexibility: if Twisted adds support for new types of endpoints (for example, IPv6 endpoints, or WebSocket endpoints), your application will automatically be able to take advantage of them with no changes to its code. Endpoints Aren't Always the Answer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For many use-cases, especially the common case of a ``twistd`` plugin which runs a long-running server that just binds a simple port, you might not want to use the endpoints APIs directly. Instead, you may want to construct an :py:class:`IService `, using :py:func:`strports.service `, which will fit neatly into the required structure of :doc:`the twistd plugin API `. This doesn't give your application much control - the port starts listening at startup and stops listening at shutdown - but it does provide the same flexibility in terms of what type of server endpoint your application will support. It is, however, almost always preferable to use an endpoint rather than calling a lower-level APIs like :py:meth:`connectTCP `, :py:meth:`listenTCP `, etc, directly. By accepting an arbitrary endpoint rather than requiring a specific reactor interface, you leave your application open to lots of interesting transport-layer extensibility for the future. Endpoint Types Included With Twisted ------------------------------------ The parser used by ``clientFromString`` and ``serverFromString`` is extensible via third-party plugins, so the endpoints available on your system depend on what packages you have installed. However, Twisted itself includes a set of basic endpoints that will always be available. Clients ~~~~~~~ TCP Supported arguments: ``host``, ``port``, ``timeout``. ``timeout`` is optional. For example, ``tcp:host=twistedmatrix.com:port=80:timeout=15``. TLS Required arguments: ``host``, ``port``. Optional arguments: ``timeout``, ``bindAddress``, ``certificate``, ``privateKey``, ``trustRoots``, ``endpoint``. - ``host`` is a (UTF-8 encoded) hostname to connect to, as well as the host name to verify against. - ``port`` is a numeric port number to connect to. - ``timeout`` and ``bindAddress`` have the same meaning as the ``timeout`` and ``bindAddress`` for TCP clients. - ``certificate`` is the certificate to use for the client; it should be the path name of a PEM file containing a certificate for which ``privateKey`` is the private key. - ``privateKey`` is the client's private key, matching the certificate specified by ``certificate``. It should be the path name of a PEM file containing an X.509 client certificate. If ``certificate`` is specified but ``privateKey`` is unspecified, Twisted will look for the certificate in the same file as specified by ``certificate``. - ``trustRoots`` specifies a path to a directory of PEM-encoded certificate files. If you leave this unspecified, Twisted will do its best to use the platform default set of trust roots, which should be the default WebTrust set. - the optional ``endpoint`` parameter changes the meaning of the ``tls:`` endpoint slightly. Rather than the default of connecting over TCP with the same hostname used for verification, you can connect over *any* endpoint type. If you specify the endpoint here, ``host`` and ``port`` are used for certificate verification purposes only. Bear in mind you will need to backslash-escape the colons in the endpoint description here. This client connects to the supplied hostname, validates the server's hostname against the supplied hostname, and then upgrades to TLS immediately after validation succeeds. The simplest example of this would be: ``tls:example.com:443``. You can use the ``endpoint:`` feature with TCP if you want to connect to a host name; for example, if your DNS is not working, but you know that the IP address 7.6.5.4 points to ``awesome.site.example.com``, you could specify: ``tls:awesome.site.example.com:443:endpoint=tcp\:7.6.5.4\:443``. You can use it with any other endpoint type as well, though; for example, if you had a local UNIX socket that established a tunnel to ``awesome.site.example.com`` in ``/var/run/awesome.sock``, you could instead do ``tls:awesome.site.example.com:443:endpoint=unix\:/var/run/awesome.sock``. Or, from python code:: wrapped = HostnameEndpoint('example.com', 443) contextFactory = optionsForClientTLS(hostname=u'example.com') endpoint = wrapClientTLS(contextFactory, wrapped) conn = endpoint.connect(Factory.forProtocol(Protocol)) UNIX Supported arguments: ``path``, ``timeout``, ``checkPID``. ``path`` gives a filesystem path to a listening UNIX domain socket server. ``checkPID`` (optional) enables a check of the lock file Twisted-based UNIX domain socket servers use to prove they are still running. For example, ``unix:path=/var/run/web.sock``. TCP (Hostname) Supported arguments: ``host``, ``port``, ``timeout``. ``host`` is a hostname to connect to. ``timeout`` is optional. It is a name-based TCP endpoint that returns the connection which is established first amongst the resolved addresses. For example, .. code-block:: python endpoint = HostnameEndpoint(reactor, "twistedmatrix.com", 80) conn = endpoint.connect(Factory.forProtocol(Protocol)) SSL (Deprecated) .. note:: You should generally prefer the "TLS" client endpoint, above, unless you need to work with versions of Twisted older than 16.0. Among other things: - the ``ssl:`` client endpoint requires that you pass ''both'' ``hostname=`` (for hostname verification) as well as ``host=`` (for a TCP connection address) in order to get hostname verification, which is required for security, whereas ``tls:`` does the correct thing by default by using the same hostname for both. - the ``ssl:`` client endpoint doesn't work with IPv6, and the ``tls:`` endpoint does. All TCP arguments are supported, plus: ``certKey``, ``privateKey``, ``caCertsDir``. ``certKey`` (optional) gives a filesystem path to a certificate (PEM format). ``privateKey`` (optional) gives a filesystem path to a private key (PEM format). ``caCertsDir`` (optional) gives a filesystem path to a directory containing trusted CA certificates to use to verify the server certificate. For example, ``ssl:host=twistedmatrix.com:port=443:caCertsDir=/etc/ssl/certs``. Servers ~~~~~~~ TCP (IPv4) Supported arguments: ``port``, ``interface``, ``backlog``. ``interface`` and ``backlog`` are optional. ``interface`` is an IP address (belonging to the IPv4 address family) to bind to. For example, ``tcp:port=80:interface=192.168.1.1``. TCP (IPv6) All TCP (IPv4) arguments are supported, with ``interface`` taking an IPv6 address literal instead. For example, ``tcp6:port=80:interface=2001\:0DB8\:f00e\:eb00\:\:1``. SSL All TCP arguments are supported, plus: ``certKey``, ``privateKey``, ``extraCertChain``, ``sslmethod``, and ``dhParameters``. ``certKey`` (optional, defaults to the value of privateKey) gives a filesystem path to a certificate (PEM format). ``privateKey`` gives a filesystem path to a private key (PEM format). ``extraCertChain`` gives a filesystem path to a file with one or more concatenated certificates in PEM format that establish the chain from a root CA to the one that signed your certificate. ``sslmethod`` indicates which SSL/TLS version to use (a value like ``TLSv1_METHOD``). ``dhParameters`` gives a filesystem path to a file in PEM format with parameters that are required for Diffie-Hellman key exchange. Since the this is required for the ``DHE``-family of ciphers that offer perfect forward secrecy (PFS), it is recommended to specify one. Such a file can be created using ``openssl dhparam -out dh_param_1024.pem -2 1024``. Please refer to `OpenSSL's documentation on dhparam `_ for further details. For example, ``ssl:port=443:privateKey=/etc/ssl/server.pem:extraCertChain=/etc/ssl/chain.pem:sslmethod=SSLv3_METHOD:dhParameters=dh_param_1024.pem``. UNIX Supported arguments: ``address``, ``mode``, ``backlog``, ``lockfile``. ``address`` gives a filesystem path to listen on with a UNIX domain socket server. ``mode`` (optional) gives the filesystem permission/mode (in octal) to apply to that socket. ``lockfile`` enables use of a separate lock file to prove the server is still running. For example, ``unix:address=/var/run/web.sock:lockfile=1``. systemd Supported arguments: ``domain``, ``index``. ``domain`` indicates which socket domain the inherited file descriptor belongs to (eg INET, INET6). ``index`` indicates an offset into the array of file descriptors which have been inherited from systemd. For example, ``systemd:domain=INET6:index=3``. See also :doc:`Deploying Twisted with systemd `. PROXY The PROXY protocol is a stream wrapper and can be applied any of the other server endpoints by placing ``haproxy:`` in front of a normal port definition. For example, ``haproxy:tcp:port=80:interface=192.168.1.1`` or ``haproxy:ssl:port=443:privateKey=/etc/ssl/server.pem:extraCertChain=/etc/ssl/chain.pem:sslmethod=SSLv3_METHOD:dhParameters=dh_param_1024.pem``. The PROXY protocol provides a way for load balancers and reverse proxies to send down the real IP of a connection's source and destination without relying on X-Forwarded-For headers. A Twisted service using this endpoint wrapper must run behind a service that sends valid PROXY protocol headers. For more on the protocol see `the formal specification `_. Both version one and two of the protocol are currently supported. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/gendefer.rst0000644000175100001710000002512300000000000020113 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Generating Deferreds ==================== .. status of document: INCOMPLETE, DRAFT :py:class:`Deferred ` objects are signals that a function you have called does not yet have the data you want available. When a function returns a Deferred object, your calling function attaches callbacks to it to handle the data when available. This document addresses the other half of the question: writing functions that return Deferreds, that is, constructing Deferred objects, arranging for them to be returned immediately without blocking until data is available, and firing their callbacks when the data is available. This document assumes that you are familiar with the asynchronous model used by Twisted, and with :doc:`using deferreds returned by functions ` . .. _core-howto-gendefer-class: Class overview -------------- This is an overview API reference for Deferred from the point of creating a Deferred and firing its callbacks and errbacks. It is not meant to be a substitute for the docstrings in the Deferred class, but can provide guidelines for its use. There is a parallel overview of functions used by calling function which the Deferred is returned to at :ref:`Using Deferreds ` . Basic Callback Functions ~~~~~~~~~~~~~~~~~~~~~~~~ - ``callback(result)`` Run success callbacks with the given result. *This can only be run once.* Later calls to this or ``errback`` will raise :py:class:`twisted.internet.defer.AlreadyCalledError` . If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately. - ``errback(failure)`` Run error callbacks with the given failure. *This can only be run once.* Later calls to this or ``callback`` will raise :py:class:`twisted.internet.defer.AlreadyCalledError` . If further callbacks or errbacks are added after this point, addCallbacks will run the callbacks immediately. What Deferreds don't do: make your code asynchronous ---------------------------------------------------- *Deferreds do not make the code magically not block.* Let's take this function as an example: .. code-block:: python from twisted.internet import defer TARGET = 10000 def largeFibonnaciNumber(): # create a Deferred object to return: d = defer.Deferred() # calculate the ten thousandth Fibonnaci number first = 0 second = 1 for i in range(TARGET - 1): new = first + second first = second second = new if i % 100 == 0: print("Progress: calculating the %dth Fibonnaci number" % i) # give the Deferred the answer to pass to the callbacks: d.callback(second) # return the Deferred with the answer: return d import time timeBefore = time.time() # call the function and get our Deferred d = largeFibonnaciNumber() timeAfter = time.time() print("Total time taken for largeFibonnaciNumber call: %0.3f seconds" % \ (timeAfter - timeBefore)) # add a callback to it to print the number def printNumber(number): print("The %dth Fibonacci number is %d" % (TARGET, number)) print("Adding the callback now.") d.addCallback(printNumber) You will notice that despite creating a Deferred in the ``largeFibonnaciNumber`` function, these things happened: - the "Total time taken for largeFibonnaciNumber call" output shows that the function did not return immediately as asynchronous functions are expected to do; and - rather than the callback being added before the result was available and called after the result is available, it isn't even added until after the calculation has been completed. The function completed its calculation before returning, blocking the process until it had finished, which is exactly what asynchronous functions are not meant to do. Deferreds are not a non-blocking talisman: they are a signal for asynchronous functions to *use* to pass results onto callbacks, but using them does not guarantee that you have an asynchronous function. Advanced Processing Chain Control --------------------------------- - ``pause()`` Cease calling any methods as they are added, and do not respond to ``callback`` , until ``self.unpause()`` is called. - ``unpause()`` If ``callback`` has been called on this Deferred already, call all the callbacks that have been added to this Deferred since ``pause`` was called. Whether it was called or not, this will put this Deferred in a state where further calls to ``addCallbacks`` or ``callback`` will work as normal. Returning Deferreds from synchronous functions ---------------------------------------------- Sometimes you might wish to return a Deferred from a synchronous function. There are several reasons why, the major two are maintaining API compatibility with another version of your function which returns a Deferred, or allowing for the possibility that in the future your function might need to be asynchronous. In the :doc:`Using Deferreds ` reference, we gave the following example of a synchronous function: :download:`synch-validation.py ` .. literalinclude:: listings/deferred/synch-validation.py While we can require that callers of our function wrap our synchronous result in a Deferred using :py:func:`maybeDeferred ` , for the sake of API compatibility it is better to return a Deferred ourselves using :py:func:`defer.succeed ` : .. code-block:: python from twisted.internet import defer def immediateIsValidUser(user): ''' Returns a Deferred resulting in true if user is a valid user, false otherwise ''' result = user in ["Alice", "Angus", "Agnes"] # return a Deferred object already called back with the value of result return defer.succeed(result) There is an equivalent :py:func:`defer.fail ` method to return a Deferred with the errback chain already fired. Integrating blocking code with Twisted -------------------------------------- At some point, you are likely to need to call a blocking function: many functions in third party libraries will have long running blocking functions. There is no way to 'force' a function to be asynchronous: it must be written that way specifically. When using Twisted, your own code should be asynchronous, but there is no way to make third party functions asynchronous other than rewriting them. In this case, Twisted provides the ability to run the blocking code in a separate thread rather than letting it block your application. The :py:func:`twisted.internet.threads.deferToThread` function will set up a thread to run your blocking function, return a Deferred and later fire that Deferred when the thread completes. Let's assume our ``largeFibonnaciNumber`` function from above is in a third party library (returning the result of the calculation, not a Deferred) and is not easily modifiable to be finished in discrete blocks. This example shows it being called in a thread, unlike in the earlier section we'll see that the operation does not block our entire program: .. code-block:: python def largeFibonnaciNumber(): """ Represent a long running blocking function by calculating the TARGETth Fibonnaci number """ TARGET = 10000 first = 0 second = 1 for i in range(TARGET - 1): new = first + second first = second second = new return second from twisted.internet import threads, reactor def fibonacciCallback(result): """ Callback which manages the largeFibonnaciNumber result by printing it out """ print("largeFibonnaciNumber result =", result) # make sure the reactor stops after the callback chain finishes, # just so that this example terminates reactor.stop() def run(): """ Run a series of operations, deferring the largeFibonnaciNumber operation to a thread and performing some other operations after adding the callback """ # get our Deferred which will be called with the largeFibonnaciNumber result d = threads.deferToThread(largeFibonnaciNumber) # add our callback to print it out d.addCallback(fibonacciCallback) print("1st line after the addition of the callback") print("2nd line after the addition of the callback") if __name__ == '__main__': run() reactor.run() Possible sources of error ------------------------- Deferreds greatly simplify the process of writing asynchronous code by providing a standard for registering callbacks, but there are some subtle and sometimes confusing rules that you need to follow if you are going to use them. This mostly applies to people who are writing new systems that use Deferreds internally, and not writers of applications that just add callbacks to Deferreds produced and processed by other systems. Nevertheless, it is good to know. Firing Deferreds more than once is impossible ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Deferreds are one-shot. You can only call ``Deferred.callback`` or ``Deferred.errback`` once. The processing chain continues each time you add new callbacks to an already-called-back-to Deferred. Synchronous callback execution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a Deferred already has a result available, ``addCallback`` **may** call the callback synchronously: that is, immediately after it's been added. In situations where callbacks modify state, it is might be desirable for the chain of processing to halt until all callbacks are added. For this, it is possible to ``pause`` and ``unpause`` a Deferred's processing chain while you are adding lots of callbacks. Be careful when you use these methods! If you ``pause`` a Deferred, it is *your* responsibility to make sure that you unpause it. The function adding the callbacks must unpause a paused Deferred, it should *never* be the responsibility of the code that actually fires the callback chain by calling ``callback`` or ``errback`` as this would negate its usefulness! ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/glossary.rst0000644000175100001710000003031700000000000020200 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Twisted Glossary ================ .. _core-howto-glossary-adaptee: adaptee An object that has been adapted, also called "original" . See :ref:`Adapter ` . .. _core-howto-glossary-adapter: :py:class:`Adapter ` An object whose sole purpose is to implement an Interface for another object. See :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-application: :py:func:`Application ` A :py:func:`twisted.application.service.Application` . There are HOWTOs on :doc:`creating and manipulating ` them as a system-administrator, as well as :doc:`using ` them in your code. .. _core-howto-glossary-avatar: Avatar (from :ref:`Twisted Cred ` ) business logic for specific user. For example, in :ref:`PB ` these are perspectives, in POP3 these are mailboxes, and so on. .. _core-howto-glossary-banana: :py:class:`Banana ` The low-level data marshalling layer of :ref:`Twisted Spread ` . See :py:mod:`twisted.spread.banana` . .. _core-howto-glossary-broker: :py:class:`Broker ` A :py:class:`twisted.spread.pb.Broker` , the object request broker for :ref:`Twisted Spread ` . .. _core-howto-glossary-cache: cache A way to store data in readily accessible place for later reuse. Caching data is often done because the data is expensive to produce or access. Caching data risks being stale, or out of sync with the original data. .. _core-howto-glossary-component: component A special kind of (persistent) :py:class:`Adapter ` that works with a :py:class:`twisted.python.components.Componentized` . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-componentized: :py:class:`Componentized ` A Componentized object is a collection of information, separated into domain-specific or role-specific instances, that all stick together and refer to each other. Each object is an :py:class:`Adapter ` , which, in the context of Componentized, we call "components" . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-conch: :py:mod:`conch ` Twisted's SSH implementation. .. _core-howto-glossary-connector: Connector Object used to interface between client connections and protocols, usually used with a :py:class:`twisted.internet.protocol.ClientFactory` to give you control over how a client connection reconnects. See :py:class:`twisted.internet.interfaces.IConnector` and :doc:`Writing Clients ` . .. _core-howto-glossary-consumer: Consumer An object that consumes data from a :ref:`Producer ` . See :py:class:`twisted.internet.interfaces.IConsumer` . .. _core-howto-glossary-cred: Cred Twisted's authentication API, :py:mod:`twisted.cred` . See :doc:`Introduction to Twisted Cred ` and :doc:`Twisted Cred usage ` . .. _core-howto-glossary-credentials: credentials A username/password, public key, or some other information used for authentication. .. _core-howto-glossary-credential-checker: credential checker Where authentication actually happens. See :py:class:`ICredentialsChecker ` . .. _core-howto-glossary-cvstoys: CVSToys A nifty set of tools for CVS, available at `http://twistedmatrix.com/users/acapnotic/wares/code/CVSToys/ `_ . .. _core-howto-glossary-daemon: Daemon A background process that does a job or handles client requests. *Daemon* is a Unix term; *service* is the Windows equivalent. .. _core-howto-glossary-deferred: :py:class:`Deferred ` An instance of :py:class:`twisted.internet.defer.Deferred` , an abstraction for handling chains of callbacks and error handlers ("errbacks" ). See the :doc:`Deferring Execution ` HOWTO. .. _core-howto-glossary-enterprise: Enterprise Twisted's RDBMS support. It contains :py:mod:`twisted.enterprise.adbapi` for asynchronous access to any standard DB-API 2.0 module. See :doc:`Introduction to Twisted Enterprise ` for more details. .. _core-howto-glossary-errback: errback A callback attached to a :ref:`Deferred ` with ``.addErrback`` to handle errors. .. _core-howto-glossary-factory: :py:class:`Factory ` In general, an object that constructs other objects. In Twisted, a Factory usually refers to a :py:class:`twisted.internet.protocol.Factory` , which constructs :ref:`Protocol ` instances for incoming or outgoing connections. See :doc:`Writing Servers ` and :doc:`Writing Clients ` . .. _core-howto-glossary-failure: :py:class:`Failure ` Basically, an asynchronous exception that contains traceback information; these are used for passing errors through asynchronous callbacks. .. _core-howto-glossary-im: im Abbreviation of "(Twisted) :ref:`Instance Messenger `" . .. _core-howto-glossary-instancemessenger: Instance Messenger Instance Messenger is a multi-protocol chat program that comes with Twisted. It can communicate via TOC with the AOL servers, via IRC, as well as via :ref:`PB ` with :ref:`Twisted Words ` . See :py:mod:`twisted.words.im` . .. _core-howto-glossary-interface: Interface A class that defines and documents methods that a class conforming to that interface needs to have. A collection of core :py:mod:`twisted.internet` interfaces can be found in :py:mod:`twisted.internet.interfaces` . See also :doc:`Interfaces and Adapters ` . .. _core-howto-glossary-jelly: Jelly The serialization layer for :ref:`Twisted Spread ` , although it can be used separately from Twisted Spread as well. It is similar in purpose to Python's standard ``pickle`` module, but is more network-friendly, and depends on a separate marshaller (:ref:`Banana ` , in most cases). See :py:mod:`twisted.spread.jelly` . .. _core-howto-glossary-manhole: Manhole A debugging/administration interface to a Twisted application. .. _core-howto-glossary-microdom: Microdom A partial DOM implementation using :ref:`SUX ` . It is simple and pythonic, rather than strictly standards-compliant. See :py:mod:`twisted.web.microdom` . .. _core-howto-glossary-names: Names Twisted's DNS server, found in :py:mod:`twisted.names` . .. _core-howto-glossary-nevow: Nevow The successor to :ref:`Woven ` ; available from `Divmod `_ . .. _core-howto-glossary-pb: PB Abbreviation of ":ref:`Perspective Broker `" . .. _core-howto-glossary-perspectivebroker: Perspective Broker The high-level object layer of Twisted :ref:`Spread ` , implementing semantics for method calling and object copying, caching, and referencing. See :py:mod:`twisted.spread.pb` . .. _core-howto-glossary-portal: Portal Glues :ref:`credential checkers ` and :ref:`realm ` s together. .. _core-howto-glossary-producer: Producer An object that generates data a chunk at a time, usually to be processed by a :ref:`Consumer ` . See :py:class:`twisted.internet.interfaces.IProducer` . .. _core-howto-glossary-protocol: :py:class:`Protocol ` In general each network connection has its own Protocol instance to manage connection-specific state. There is a collection of standard protocol implementations in :py:mod:`twisted.protocols` . See also :doc:`Writing Servers ` and :doc:`Writing Clients ` . .. _core-howto-glossary-psu: PSU There is no PSU. .. _core-howto-glossary-reactor: Reactor The core event-loop of a Twisted application. See :doc:`Reactor Basics ` . .. _core-howto-glossary-reality: Reality See ":ref:`Twisted Reality `" .. _core-howto-glossary-realm: realm (in :ref:`Twisted Cred ` ) stores :ref:`avatars ` and perhaps general business logic. See :py:class:`IRealm ` . .. _core-howto-glossary-resource: :py:class:`Resource ` A :py:class:`twisted.web.resource.Resource` , which are served by Twisted Web. Resources can be as simple as a static file on disk, or they can have dynamically generated content. .. _core-howto-glossary-service: Service A :py:class:`twisted.application.service.Service` . See :doc:`Application howto ` for a description of how they relate to :ref:`Applications ` . .. _core-howto-glossary-spread: Spread Twisted Spread is Twisted's remote-object suite. It consists of three layers: :ref:`Perspective Broker ` , :ref:`Jelly ` and :ref:`Banana. ` See :doc:`Writing Applications with Perspective Broker ` . .. _core-howto-glossary-sux: SUX *S* mall *U* ncomplicated *X* ML, Twisted's simple XML parser written in pure Python. See :py:mod:`twisted.web.sux` . .. _core-howto-glossary-tac: TAC A *T* wisted *A* pplication *C* onfiguration is a Python source file, generally with the *.tac* extension, which defines configuration to make an application runnable using ``twistd`` . .. _core-howto-glossary-tap: TAP *T* wisted *A* pplication *P* ickle (no longer supported), or simply just a*T* wisted *AP* plication. A serialised application that was created with ``mktap`` (no longer supported) and runnable by ``twistd`` . See:doc:`Using the Utilities ` . .. _core-howto-glossary-trial: Trial :py:mod:`twisted.trial` , Twisted's unit-testing framework, based on the ``unittest`` standard library module. See also :doc:`Writing tests for Twisted code ` . .. _core-howto-glossary-twistedmatrixlaboratories: Twisted Matrix Laboratories The team behind Twisted. `http://twistedmatrix.com/ `_ . .. _core-howto-glossary-twistedreality: Twisted Reality In days of old, the Twisted Reality multiplayer text-based interactive-fiction system was the main focus of Twisted Matrix Labs; Twisted, the general networking framework, grew out of Reality's need for better network functionality. Twisted Reality has been superseded by the `Imaginary `_ project. .. _core-howto-glossary-usage: :py:mod:`usage ` The :py:mod:`twisted.python.usage` module, a replacement for the standard ``getopt`` module for parsing command-lines which is much easier to work with. See :doc:`Parsing command-lines ` . .. _core-howto-glossary-words: Words Twisted Words is a multi-protocol chat server that uses the :ref:`Perspective Broker ` protocol as its native communication style. See :py:mod:`twisted.words` . .. _core-howto-glossary-woven: Woven *W* eb *O* bject *V* isualization *En* vironment. A templating system previously, but no longer, included with Twisted. Woven has largely been superseded by `Divmod Nevow `_ . ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/howto.tidyrc0000644000175100001710000000013500000000000020156 0ustar00runnerdockeroutput-xml: yes output-xhtml: yes tidy-mark: no indent: auto gnu-emacs: yes add-xml-decl: yes././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1644239535.0 Twisted-22.1.0/docs/core/howto/index.rst0000644000175100001710000001573300000000000017451 0ustar00runnerdocker :LastChangedDate: $LastChangedDate$ :LastChangedRevision: $LastChangedRevision$ :LastChangedBy: $LastChangedBy$ Developer Guides ================ .. toctree:: :hidden: vision servers clients trial tutorial/index tutorial/intro tutorial/protocol tutorial/style tutorial/components tutorial/backends tutorial/web tutorial/pb tutorial/factory tutorial/client tutorial/library tutorial/configuration quotes design internet-overview reactor-basics ssl udp process defer-intro defer gendefer time threading producers choosing-reactor endpoints components cred plugin basics application tap systemd logger logging constants rdbms options dirdbm testing sendmsg amp pb pb-intro pb-usage pb-clients pb-copyable pb-cred pb-limits python3 positioning glossary debug-with-emacs - .. _core-howto-index-introduction: Introduction - :doc:`Executive summary ` Connecting your software - and having fun too! - .. _core-howto-index-tutorials: Getting Started - :doc:`Writing a TCP server ` Basic network servers with Twisted. - :doc:`Writing a TCP client ` And basic clients. - :doc:`Test-driven development with Twisted ` Code without tests is broken by definition; Twisted makes it easy to test your network code. - :doc:`Tutorial: Twisted From Scratch ` #. :doc:`The Evolution of Finger: building a simple finger service ` #. :doc:`The Evolution of Finger: adding features to the finger service ` #. :doc:`The Evolution of Finger: cleaning up the finger code ` #. :doc:`The Evolution of Finger: moving to a component based architecture ` #. :doc:`The Evolution of Finger: pluggable backends ` #. :doc:`The Evolution of Finger: a clean web frontend ` #. :doc:`The Evolution of Finger: Twisted client support using Perspective Broker ` #. :doc:`The Evolution of Finger: using a single factory for multiple protocols ` #. :doc:`The Evolution of Finger: a Twisted finger client ` #. :doc:`The Evolution of Finger: making a finger library ` #. :doc:`The Evolution of Finger: configuration and packaging of the finger service ` - :doc:`Setting up the TwistedQuotes application ` - :doc:`Designing a Twisted application ` - .. _core-howto-index-events: Networking and Other Event Sources - :doc:`Twisted Internet ` A brief overview of the ``twisted.internet`` package. - :doc:`Reactor basics ` The event loop at the core of your program. - :doc:`Using SSL in Twisted ` Add some security to your network transport. - :doc:`UDP Networking ` How to use Twisted's UDP implementation, including multicast and broadcast functionality. - :doc:`Using processes ` Launching sub-processes, the correct way. - :doc:`Introduction to Deferreds ` Like callback functions, only a lot better. - :doc:`Deferred reference ` In-depth information on Deferreds. - :doc:`Generating deferreds ` More about Deferreds. - :doc:`Scheduling