wiki:CompatibilityPolicy

Version 18 (modified by habnabit, 3 years ago) (diff)

updating the deprecation policy (AIUI)

Compatibility Policy

NB: This is a work in progress.

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. 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.

Non-Incompatibilties

  • test changes. No code in a test package should be imported by a non-test package within Twisted, so there's no chance anything could access these objects by going through the public API.
  • "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, unless:
    • a "public" entry point returns a "private" object, that "private" object must preserve its "public" attributes. For example:
      class _y:
          def z(self): return 1
          def _q(self): return 2
      def x(): return _y()
      
      in this example, _y can no longer be arbitrarily changed. Specifically, 'z' is now a public method, thanks to 'x' exposing it. However, '_q' can still be arbitrarily changed or removed.
    • a "private" class is exposed by way of a "public" subclass. For example,
      class _a:
          def b(self): return 1
          def c(self): return 2
      class d(_a): pass
      
      in this example _a is effectively public, since 'b' and 'c' are both exposed via d.
  • Source
    • 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. No application code may expect any Twisted class, module, or method's __doc__ attribute to remain the same.
  • Attributes: New code may also be added. No application may ever rely 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, no application may 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.

Fixing 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 <xml>Hello</xml> and then serializing it again resulted in >xml<Hello>/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.

However, if even such a gross violation were 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.

Explicitly Incompatible Changes

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.

[XXX TODO]: 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.

Procedure for Incompatible Changes

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.

Compatible Changes

Any change specifically described as "compatible" above may be made at any time, in any release.

Incompatible Changes

Any change which is not specifically described as "compatible" must be made in 3 phases. If a change is made in release R, the timeline is:

  1. Release R:
    • New functionality is added.
    • Old functionality is deprecated with a DeprecationWarning.
  2. Release R+1 (in circumstances when the API is particularly well-used):
    • Old functionality will raise a new exception type DeprecationError. This allows application authors to upgrade and provide an ugly work-around to silence this error in this particular place, to keep their code working for one more version.
  3. Release R+2:
    • Old functionality is completely removed.

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 OS X. 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 buildslave exists for it.

Removing support for a Python version will be announced at least 1 release prior to the removal.

Open Issues

Warnings Currently in Twisted

Taken from a recent test run:

/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_cftp.py:283: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_cftp.py:423: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:104: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:137: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:140: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:142: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:250: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:251: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:258: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:259: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:265: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:26: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:347: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:348: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:41: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:433: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:45: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:502: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:503: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:529: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:544: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:545: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:572: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:585: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:586: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:587: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:588: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:638: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:639: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:669: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:81: exceptions.DeprecationWarning: unittest.assert_ is deprecated.  Instead use the 'assert_' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:90: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:91: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/conch/test/test_ssh.py:96: exceptions.DeprecationWarning: unittest.assertEquals is deprecated.  Instead use the 'assertEquals' method on unittest.TestCase
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/internet/base.py:669: exceptions.DeprecationWarning: reactor.crash cannot be used inside unit tests. By Twisted 2.7, using crash will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/internet/defer.py:107: exceptions.DeprecationWarning: loopback() is deprecated (since Twisted 2.5). Use loopbackAsync() instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/mail/alias.py:213: exceptions.DeprecationWarning: Deferred.setTimeout is deprecated.  Look for timeout support specific to the API you are using instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/protocols/loopback.py:265: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/protocols/loopback.py:275: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/python/reflect.py:361: PendingDeprecationWarning: twisted.flow is unmaintained.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/scripts/mktap.py:24: PendingDeprecationWarning: mktap is obsolete as of Twisted 2.5, and will be officially deprecated in Twisted 2.6. Use Twisted Application Plugins with the 'twistd' command  directly, as described in 'Writing a Twisted Application Plugin for twistd' chapter of the Developer Guide.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_ident.py:101: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_import.py:71: exceptions.DeprecationWarning: As of Twisted 2.1, twisted.protocols.telnet is deprecated.  See twisted.conch.telnet for the current, supported API.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1036: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1082: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1148: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1270: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1278: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1279: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1280: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:1288: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:844: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:846: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_internet.py:848: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_ssl.py:253: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_ssl.py:344: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_sslverify.py:328: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_sslverify.py:350: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_tcp.py:1295: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/test_tcp.py:1302: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/time_helpers.py:63: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/test/time_helpers.py:64: exceptions.DeprecationWarning: reactor.iterate cannot be used inside unit tests. By Twisted 2.7, using iterate will fail the test and may crash or hang the test run.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/trial/reporter.py:219: twisted.trial.reporter.BrokenTestCaseWarning: REACTOR UNCLEAN! traceback(s) follow: 
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/web2/test/test_stream.py:451: exceptions.DeprecationWarning: log.flushErrors is deprecated since Twisted 2.5. If you need to flush errors from within a unittest, use TestCase.flushLoggedErrors instead.
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/web2/vhost.py:145: exceptions.UserWarning: No host was obtained either from Host or X-Forwarded-Host headers.  If your proxy does not send either of these headers use VHostURIRewrite. If your proxy sends the real host as the Host header use AutoVHostURIRewrite(resrc, sendsRealHost=True)
/home/buildbot/run/buildbot-two/full2.4/Twisted/twisted/words/im/__init__.py:8: UserWarning: twisted.im will be undergoing a rewrite at some point in the future.
  warnings.warn("twisted.im will be undergoing a rewrite at some point in the future.")

That's 69 warnings on Python 2.4, with the select reactor, arguably our best-supported test configuration. These warnings need to be eliminated so that application developers can treat warnings as errors.

Implied New APIs

DeprecationError does not currently exist and would need to be introduced.

Trial has support for suppressing warnings, but not for asserting about them. That means it is not really feasible to verify that deprecations have been done correctly, although it is possible to quash them. (trial now provides TestCase.assertWarnsflushWarnings)

Trial also needs a way to do the equivalent of -Werror that was specific to Twisted warnings, since it may not be possible to avoid certain kinds of warning while remaining compatible between all Python versions.

How to deprecate APIs

Functions and methods

To deprecate a free 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 PendingDeprecationWarning or DeprecationWarning and the stack level should be set so that the warning refers to the code which is invoking the deprecated function or method. For example:

def foo(bar):
    warnings.warn("foo is deprecated since Twisted 1.2.  Use baz instead.", category=DeprecationWarning, stacklevel=2)
    return bar * 3

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, then it may be deprecated without a replacement).

Instance attributes

To deprecate an attribute on instances of a class, make the attribute into a property and call warnings.warn from the getter and/or setter function for that property.

Module attributes

Modules cannot have properties, so module attributes should be deprecated using twisted.python.deprecate.deprecatedModuleAttribute.

Modules

To deprecate an entire module, twisted.python.deprecate.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

Unit testing

Like all changes in Twisted, deprecations must be unit tested. There are several options for this. In order of decreasing preference:

  • twisted.trial.unittest.TestCase.flushWarnings
  • twisted.trial.unittest.TestCase.assertWarns
  • twisted.trial.unittest.TestCase.callDeprecated

Weaknesses of the Warnings System

Warnings are, for a variety of unfortunate reasons, often displayed to end-users before they are displayed to developers. There should be some mechanism for specifying what warnings one wants to see and how they should be reported. For example, if Twisted continues to have some warnings against a particular version of Python, a Twisted application developer doesn't necessarily care. They should be able to declare that they are a developer for particular packages and see warnings only for those packages. (as of Python 2.7, DeprecationWarning will not be emitted by default)