draft pep: backwards compatibility
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt: PEP: 387 Title: Backwards Compatibility Policy Version: $Revision$ Last-Modified: $Date$ Author: Benjamin Peterson <benjamin@python.org> Status: Draft Type: Process Content-Type: text/x-rst Created: 18-Jun-2009 Abstract ======== This PEP outlines Python's backwards compatibility policy. Rationale ========= As one of the most used programming languages today [#tiobe]_, the Python core language and its standard library play a critcal role in thousands of applications and libraries. This is fantastic; it is probably one of a language designer's most wishful dreams. However, it means the development team must be very careful not to break this existing 3rd party code with new releases. Backwards Compatibility Rules ============================= This policy applys to all public APIs. These include the C-API, the standard library, and the core language including syntax and operation as defined by the reference manual. This is the basic policy for backwards compatibility: * The behavior of an API *must* not change between any two consecutive releases. * A feature cannot be removed without notice between any two consecutive releases. * Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code. Making Incompatible Changes =========================== It's a fact: design mistakes happen. Thus it is important to be able to change APIs or remove misguided features. This is accomplished through a gradual process over several releases: 1. Discuss the change. Depending on the size of the incompatibility, this could be on the bug tracker, python-dev, python-list, or the appropriate SIG. A PEP or similar document may be written. Hopefully users of the affected API will pipe up to comment. 2. Add a warning [#warnings_]_. If behavior is changing, a the API may gain a new function or method to perform the new behavior; old usage should raise the warning. If an API is being removed, simply warn whenever it is entered. DeprecationWarning is the usual warning category to use, but PendingDeprecationWarning may be used in special cases were the old and new versions of the API will coexist for many releases. 3. Wait for a release. 4. See if there's any feedback. Users not involved in the original discussions may comment now after seeing the warning. Perhaps reconsider. 5. The behavior change or feature removal may now be made default or permanent in the next release. Remove the old version and warning. References ========== .. [#tiobe] TIOBE Programming Community Index http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html .. [#warnings] The warnings module http://docs.python.org/library/warnings.html Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 coding: utf-8 End:
Benjamin Peterson <benjamin <at> python.org> writes:
This policy applys to all public APIs.
applies?
* The behavior of an API *must* not change between any two consecutive releases.
* A feature cannot be removed without notice between any two consecutive releases.
By induction, this would mean no API could change and no feature could be removed without notice between any N consecutive releases. Do you really mean it?
* Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code.
There is always the possibility that a new feature breaks existing code, for example because it relies on a similarly named attribute, or on some obscure internal condition. I think this should be qualified so that it only applies when e.g. a fair number of third-party apps or libraries are broken.
2009/6/19 Antoine Pitrou <solipsis@pitrou.net>:
Benjamin Peterson <benjamin <at> python.org> writes:
This policy applys to all public APIs.
applies?
Yes, thanks.
* The behavior of an API *must* not change between any two consecutive releases.
* A feature cannot be removed without notice between any two consecutive releases.
By induction, this would mean no API could change and no feature could be removed without notice between any N consecutive releases. Do you really mean it?
No, I'll reword.
* Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code.
There is always the possibility that a new feature breaks existing code, for example because it relies on a similarly named attribute, or on some obscure internal condition. I think this should be qualified so that it only applies when e.g. a fair number of third-party apps or libraries are broken.
I add a few examples. -- Regards, Benjamin
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Antoine Pitrou wrote:
There is always the possibility that a new feature breaks existing code, for example because it relies on a similarly named attribute, or on some obscure internal condition. I think this should be qualified so that it only applies when e.g. a fair number of third-party apps or libraries are broken.
I think the recent asyncore changes are a good test case here: they broke major consumers of the package (Zope, supervisord), but it wasn't obvious that the breakage would occur, because the API of the package wasn't clear: the apps broken by the change were forced to rely on stuff that the subsequent maintainer considered "implementation details." Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFKO88q+gerLs4ltQ4RAmmkAJ4ghgszBWGruCqONtouAXp82G+blgCgySsL 4ywgpU5137D5isiJ8+d6KtM= =OksB -----END PGP SIGNATURE-----
2009/6/19 Benjamin Peterson <benjamin@python.org>:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
Nice :-) A general point - it's probably worth clarifying that you're referring to major releases (2.6 -> 2.7 etc.) here. Minor releases have a strict bugfixes-only policy.
applications and libraries. This is fantastic; it is probably one of a language designer's most wishful dreams.
That's a slightly odd wording. I'm not sure I can think of a better one, though...
This policy applys to all public APIs. These include the C-API, the standard
... applies ...
* The behavior of an API *must* not change between any two consecutive releases.
With the exception of compatibility warnings as described below. In practice, I think APIs *do* change, so presumably there must have been a pair of releases between which the change happened. I see what you're trying to say, but I think you overstated things a little. Paul.
On 02:17 am, benjamin@python.org wrote:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
PEP: 387 Title: Backwards Compatibility Policy
Thanks for getting started on this. This is indeed a bikeshed that there would be less discussion around if there were a clearer PEP to point to. However, this draft PEP points to the problem rather than the solution. In order to really be useful this needs to answer a whole slew of very very specific questions, because the real problem is that everybody thinks they know what they mean but in fact we all have slightly different definitions of these words. I've taken a stab at this myself in the past while defining a policy for Twisted, which is here: <http://twistedmatrix.com/trac/wiki/CompatibilityPolicy>. I think we might be a bit stricter than Python, but I believe our attempt to outline these terms specifically is worth taking a look at. The questions that follow might seem satirical or parodic but I am completely serious and each of these terms really does have a variable definition.
* The behavior of an API *must* not change between any two consecutive releases.
What is "behavior"? Software is composed of behavior. If no behavior changes, then nothing can ever happen. What is an "API"? Are you talking about a public name in a Python module? A name covered in documentation? A name covered by a specific piece of documentation? Is "subclassing" considered an API, or just invoking? What about isinstance()? What about repr()? Are string formats allowed to change? For example, certain warnings have caused the Twisted test suite to fail because with the introduction of warnings at the C level it becomes progressively harder for a Python process to control its own stderr stream. I can't remember the specifics right now, but on more than one occasion a python upgrade caused our test suite to fail because the expectation was that a process would be able to successfully terminate without putting anything on any FD but stdout. In order for any changes to be possible, there needs to be a clearly labeled mine-field: don't touch or depend on these things in your Python application or it *will* break every time Python is released. What are "consecutive" releases? If we have releases 1, 2, 3, 4, and behavior can't change 1->2, 2->3, or 3->4, then no change may ever be made. What does "must not" mean here? My preference would be "once there is agreement that an incompatible change has occurred, there should be an immediate revert with no discussion". Unless of course the change has already been released.
* A feature cannot be removed without notice between any two consecutive releases.
Presumably removal of a feature is a change in behavior, so isn't this redundant with the previous point?
* Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code.
How do you propose to measure the benefit to breakage ratio? How do you propose to define "trivial" to fix? Most projects, including Python, Twisted, Django, and Zope, have an ever-increasing bug count, which means that trivial issues fall off the radar pretty easily. One of the big problems here in detecting and fixing "trivial" changes is that they can occur in test code as well. So if the answer is "run your tests", that's not much help if you can't call the actual APIs whose behavior has changed. The standard library would need to start providing a lot more verified test stubs for things like I/O in order for this to be feasible.
Making Incompatible Changes ===========================
It's a fact: design mistakes happen. Thus it is important to be able to change APIs or remove misguided features. This is accomplished through a gradual process over several releases:
1. Discuss the change. Depending on the size of the incompatibility, this could be on the bug tracker, python-dev, python-list, or the appropriate SIG. A PEP or similar document may be written. Hopefully users of the affected API will pipe up to comment.
There are a very large number of users of Python, the large percentage of whom do not read python-dev. A posting on python-list is likely to provoke an unproductive pile-on. I suggest a dedicated list, which should ideally be low traffic, "python-incompatible-announce", or something like that, so that *everyone* who maintains Python software can subscribe to it, even if they're not otherwise interested in Python development.
2. Add a warning [#warnings_]_. If behavior is changing, a the API may gain a new function or method to perform the new behavior; old usage should raise the warning. If an API is being removed, simply warn whenever it is entered. DeprecationWarning is the usual warning category to use, but PendingDeprecationWarning may be used in special cases were the old and new versions of the API will coexist for many releases.
Why is PendingDeprecationWarning the special case? As outlined in the Twisted compatibility proposal, I'd prefer it if every warning went through multiple phases: pending, deprecated, gone: so that careful developers could release new versions of their software to quash noisy warnings *before* the new version of Python that actually made them user-visible was released.
3. Wait for a release.
How does this affect the parallel 2.x/3.x release cycle?
4. See if there's any feedback. Users not involved in the original discussions may comment now after seeing the warning. Perhaps reconsider.
At this point, many developers may already have been porting over to the new, non-deprecated API. I realize that there may be no way around that, but it's worth considering. Thanks for tackling this thorny problem. Good luck!
<glyph <at> divmod.com> writes:
In order for any changes to be possible, there needs to be a clearly labeled mine-field: don't touch or depend on these things in your Python application or it *will* break every time Python is released.
I think the "frozen area" should be defined positively (explicit public APIs and explicitly guaranteed behaviour) rather than negatively (an explicit "mine field"). Otherwise, we will forget to put some important things in the minefield and get bitten later when we need to change those things in a backwards-incompatible way. For example, I think it was wrong to change the name of a test-skipping unittest method just so that it wouldn't clash with a method created by Twisted subclassing unittest (besides, self.skip() was much nicer than self.skipTest() ;-)). Just because some class is public shouldn't prevent us to add new public methods or attributes to it. Regards Antoine.
On 09:21 am, solipsis@pitrou.net wrote:
<glyph <at> divmod.com> writes:
In order for any changes to be possible, there needs to be a clearly labeled mine-field: don't touch or depend on these things in your Python application or it *will* break every time Python is released.
I think the "frozen area" should be defined positively (explicit public APIs and explicitly guaranteed behaviour) rather than negatively (an explicit "mine field"). Otherwise, we will forget to put some important things in the minefield and get bitten later when we need to change those things in a backwards-incompatible way.
This is a false dichotomy; for core developers, the list needs to be exhaustive. Everything that can change needs to be described as either compatible or incompatible. However, the reason I say that the list needs to be phrased as a whitelist is that we have to assume *all* people writing Python code, who ever want to be able to upgrade Python, need to *completely* understand the list of things which they should not depend on. Every piece of documentation they read and every API they find, the assumption is that it's going to be compatible and it's not something they need to worry about. It's important to remember that this PEP has a "public" (python application programmers) and "private" (python core developer) interfaces, too ;-).
For example, I think it was wrong to change the name of a test-skipping unittest method just so that it wouldn't clash with a method created by Twisted subclassing unittest (besides, self.skip() was much nicer than self.skipTest() ;-)). Just because some class is public shouldn't prevent us to add new public methods or attributes to it.
I think it would be wrong to have a blanket prohibition on such changes, by which I mean additions of names to public namespaces. Twisted's own compatibility possibility would not forbid a similar change. But in that specific case I think it was the right thing to do. Like it or not, a lot of people use Twisted, a lot of people run tests with Trial, and we beat stdlib unittest to the punch on the 'skip' testing feature by a good 5 years. We caught the change well before the release, we reported it and discussed it. IMHO this is the best way to deal with incompatible changes, especially in the case of superclasses, given Python's subtle and complex inheritance semantics. It's not feasible to provide a policy that somehow prohibits subclasses from adding names which may eventually be used on a superclass. Projects which notice test failures with new versions of Python should report them so that the features can be adjusted to be compatible, assuming the project in question hasn't done anything in egregious violation of the compatibility policy (like invoking a private method). This lets users, system administrators, and application authors upgrade components individually, without worrying about the components further down the stack flaking out on them. It also provides a feedback loop for the compatibility policy: if there are things that initially seem reasonable, but projects report compatibility issues when they are changed, they might need to be added later.
<glyph <at> divmod.com> writes:
This is a false dichotomy; for core developers, the list needs to be exhaustive. Everything that can change needs to be described as either compatible or incompatible.
How do you enumerate "everything that can change"? It does not look like a finite set to me (but perhaps I'm wrong); and certainly not like a set of a size reasonable enough to be enumerated in a human-readable way :)
Antoine Pitrou wrote:
<glyph <at> divmod.com> writes:
This is a false dichotomy; for core developers, the list needs to be exhaustive. Everything that can change needs to be described as either compatible or incompatible.
How do you enumerate "everything that can change"? It does not look like a finite set to me (but perhaps I'm wrong); and certainly not like a set of a size reasonable enough to be enumerated in a human-readable way :)
And this is why expressing a finite list of things we guarantee won't change is a virtually impossible task - unless one of you is volunteering to write an official spec for Python and its libraries... :-) (Something that would not be bad IMO - just a long and difficult task, *especially* if you include the library along with language semantics and APIs). Michael
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
Michael Foord <fuzzyman <at> voidspace.org.uk> writes:
And this is why expressing a finite list of things we guarantee won't change is a virtually impossible task - unless one of you is volunteering to write an official spec for Python and its libraries...
Well, we *can* enumerate a list of things that are guaranteed not to change - it's just that the list will be "completely incomplete", and the rest will rely on application of common sense rather than formal guarantees. And it actually more or less exists: it's Python's official documentation, combined with a bit of common sense when interpreting its written text. Regards Antoine.
On 11:11 am, solipsis@pitrou.net wrote:
<glyph <at> divmod.com> writes:
This is a false dichotomy; for core developers, the list needs to be exhaustive. Everything that can change needs to be described as either compatible or incompatible.
How do you enumerate "everything that can change"? It does not look like a finite set to me (but perhaps I'm wrong); and certainly not like a set of a size reasonable enough to be enumerated in a human-readable way :)
Sorry. You're right. To be clear, I mean, "everything that can change" as classified by some arbitrary classification system yet to be defined :-). For example, the number of different programmatic entities in Python is pretty small: you've got classes, methods, modules, and constants. The ways you can change them is also not too huge: you can add to them, remove them, or rename them. I realize that given Python's flexibility when loading code, any system of such classification is going to have edge cases, but I doubt those are going to matter that much.
glyph@divmod.com wrote:
[snip...]
For example, I think it was wrong to change the name of a test-skipping unittest method just so that it wouldn't clash with a method created by Twisted subclassing unittest (besides, self.skip() was much nicer than self.skipTest() ;-)). Just because some class is public shouldn't prevent us to add new public methods or attributes to it.
I think it would be wrong to have a blanket prohibition on such changes, by which I mean additions of names to public namespaces. Twisted's own compatibility possibility would not forbid a similar change. But in that specific case I think it was the right thing to do. Like it or not, a lot of people use Twisted, a lot of people run tests with Trial, and we beat stdlib unittest to the punch on the 'skip' testing feature by a good 5 years. We caught the change well before the release, we reported it and discussed it.
Just to note that Twisted (along with Bazaar) are one of the few 'good citizens' in the Python community running their tests on Python trunk. Both projects have caught incompatibilities *before* release of new versions which is greatly preferable to discovering them after a release. Thanks for this. Michael Foordt
IMHO this is the best way to deal with incompatible changes, especially in the case of superclasses, given Python's subtle and complex inheritance semantics. It's not feasible to provide a policy that somehow prohibits subclasses from adding names which may eventually be used on a superclass.
Projects which notice test failures with new versions of Python should report them so that the features can be adjusted to be compatible, assuming the project in question hasn't done anything in egregious violation of the compatibility policy (like invoking a private method). This lets users, system administrators, and application authors upgrade components individually, without worrying about the components further down the stack flaking out on them. It also provides a feedback loop for the compatibility policy: if there are things that initially seem reasonable, but projects report compatibility issues when they are changed, they might need to be added later. _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
On 11:13 am, fuzzyman@voidspace.org.uk wrote:
Just to note that Twisted (along with Bazaar) are one of the few 'good citizens' in the Python community running their tests on Python trunk. Both projects have caught incompatibilities *before* release of new versions which is greatly preferable to discovering them after a release. Thanks for this.
And thank *you* for being so responsive to the issues we've reported as a result of that. It's definitely an incentive to remain engaged in core development :).
2009/6/19 <glyph@divmod.com>:
On 02:17 am, benjamin@python.org wrote:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
PEP: 387 Title: Backwards Compatibility Policy
Thanks for getting started on this. This is indeed a bikeshed that there would be less discussion around if there were a clearer PEP to point to. However, this draft PEP points to the problem rather than the solution. In order to really be useful this needs to answer a whole slew of very very specific questions, because the real problem is that everybody thinks they know what they mean but in fact we all have slightly different definitions of these words.
I don't expect this document at all to be the definitive policy all at once. I expect it will evolve and be filled in as it is applied in real life.
I've taken a stab at this myself in the past while defining a policy for Twisted, which is here: <http://twistedmatrix.com/trac/wiki/CompatibilityPolicy>. I think we might be a bit stricter than Python, but I believe our attempt to outline these terms specifically is worth taking a look at.
Yes, that's helpful. Thanks.
The questions that follow might seem satirical or parodic but I am completely serious and each of these terms really does have a variable definition.
And will always no matter what we do. It's how natural language works.
* The behavior of an API *must* not change between any two consecutive releases.
What is "behavior"? Software is composed of behavior. If no behavior changes, then nothing can ever happen.
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it. (Obviously, this is somewhat simplified, but I hope it helps.)
* A feature cannot be removed without notice between any two consecutive releases.
Presumably removal of a feature is a change in behavior, so isn't this redundant with the previous point?
I wanted to qualify differently from explicit behavior change as I defined above in this message.
* Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code.
How do you propose to measure the benefit to breakage ratio? How do you propose to define "trivial" to fix? Most projects, including Python, Twisted, Django, and Zope, have an ever-increasing bug count, which means that trivial issues fall off the radar pretty easily.
Well, if you're tests aren't failing from it, is it an incompatibility?
One of the big problems here in detecting and fixing "trivial" changes is that they can occur in test code as well. So if the answer is "run your tests", that's not much help if you can't call the actual APIs whose behavior has changed. The standard library would need to start providing a lot more verified test stubs for things like I/O in order for this to be feasible.
Making Incompatible Changes ===========================
It's a fact: design mistakes happen. Thus it is important to be able to change APIs or remove misguided features. This is accomplished through a gradual process over several releases:
1. Discuss the change. Depending on the size of the incompatibility, this could be on the bug tracker, python-dev, python-list, or the appropriate SIG. A PEP or similar document may be written. Hopefully users of the affected API will pipe up to comment.
There are a very large number of users of Python, the large percentage of whom do not read python-dev. A posting on python-list is likely to provoke an unproductive pile-on. I suggest a dedicated list, which should ideally be low traffic, "python-incompatible-announce", or something like that, so that *everyone* who maintains Python software can subscribe to it, even if they're not otherwise interested in Python development.
And that won't generate a pile-on?
DeprecationWarning is the usual warning category to use, but PendingDeprecationWarning may be used in special cases were the old and new versions of the API will coexist for many releases.
Why is PendingDeprecationWarning the special case?
It should be used for things like string exceptions which are deprecated but are not removed for a while.
As outlined in the Twisted compatibility proposal, I'd prefer it if every warning went through multiple phases: pending, deprecated, gone: so that careful developers could release new versions of their software to quash noisy warnings *before* the new version of Python that actually made them user-visible was released.
3. Wait for a release.
How does this affect the parallel 2.x/3.x release cycle?
I just added something about this.
4. See if there's any feedback. Users not involved in the original discussions may comment now after seeing the warning. Perhaps reconsider.
At this point, many developers may already have been porting over to the new, non-deprecated API. I realize that there may be no way around that, but it's worth considering.
Thanks for tackling this thorny problem. Good luck!
I think I'll need it. Thanks. :) -- Regards, Benjamin
Benjamin Peterson <benjamin <at> python.org> writes:
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it.
Well, except if returning Z rather than Z' was a bug. Regards Antoine.
On Fri, 19 Jun 2009 at 14:15, Antoine Pitrou wrote:
Benjamin Peterson <benjamin <at> python.org> writes:
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it.
Well, except if returning Z rather than Z' was a bug.
I'm pretty sure there have been cases of keeping buggy behavior in point releases for backward compatibility reasons. I think the decision has depended on the nature, severity, and age of the bug, and the estimated likelihood that code in the wild would break if the bug were fixed. --David
R. David Murray schrieb:
On Fri, 19 Jun 2009 at 14:15, Antoine Pitrou wrote:
Benjamin Peterson <benjamin <at> python.org> writes:
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it.
Well, except if returning Z rather than Z' was a bug.
I'm pretty sure there have been cases of keeping buggy behavior in point releases for backward compatibility reasons. I think the decision has depended on the nature, severity, and age of the bug, and the estimated likelihood that code in the wild would break if the bug were fixed.
That is always a difficult issue. There are tons of issues in the tracker that would be quite easy to fix, but are not touched because nobody wants to take the blame if it is considered "not buggy enough" for an incompatible change. But they won't be closed either, because the current behavior clearly is wrong. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.
On 02:09 pm, benjamin@python.org wrote:
2009/6/19 <glyph@divmod.com>:
On 02:17 am, benjamin@python.org wrote:
I've taken a stab at this myself in the past while defining a policy for Twisted
Yes, that's helpful. Thanks.
Glad you found it useful.
The questions that follow might seem satirical or parodic but I am completely serious and each of these terms really does have a variable definition.
And will always no matter what we do. It's how natural language works.
Well, one cannot proceed from the informal to the formal by formal means. I'm pretty sure we can nail down the definitions of these terms for the scope of Python core development.
What is "behavior"? �Software is composed of behavior. �If no behavior changes, then nothing can ever happen.
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it. (Obviously, this is somewhat simplified, but I hope it helps.)
This definition only makes sense if the interesting thing about a function is its return value, and if the only sort of thing you have are functions. What about side-effects, or exceptional conditions? What about interactions with subclasses? (Changing a class in a library from old-style to new-style has serious repercussions, as MRO conflicts can result in applications that subclass it.)
How do you propose to measure the benefit to breakage ratio? �How do you propose to define "trivial" to fix? �Most projects, including Python, Twisted, Django, and Zope, have an ever-increasing bug count, which means that trivial issues fall off the radar pretty easily.
Well, if you're tests aren't failing from it, is it an incompatibility?
Well, let's say the tests do fail from it. Right now, even Twisted trunk still doesn't *quite* support Python 2.6, but only on Windows, due to stricter checking of the 'mode' argument for files. But this failing test is just not that high priority, so our recommendation is "don't use python 2.6 yet on Windows, 2.5 works fine". My point is that triviality is a matter of perspective :). Eventually somebody will get around to it, but 2.6 has been out for a while now.
There are a very large number of users of Python, the large percentage of whom do not read python-dev. �A posting on python-list is likely to provoke an unproductive pile-on. �I suggest a dedicated list, which should ideally be low traffic, "python-incompatible-announce", or something like that, so that *everyone* who maintains Python software can subscribe to it, even if they're not otherwise interested in Python development.
And that won't generate a pile-on?
Well, the etiquette for that specific list could be "keep this low- traffic unless you have a serious problem with this change". Or, better yet, make it an announce-only list: the pile-on can still happen on python-list, but only the results of the discussion will be announced on the incompatible-announce list. The point is, the notifications about compatibility are really important, and sometimes people need to offer feedback about them, but not everyone who needs to know about the changes needs to hear about the feedback.
2009/6/19 <glyph@divmod.com>:
On 02:09 pm, benjamin@python.org wrote:
2009/6/19 <glyph@divmod.com>:
What is "behavior"? Software is composed of behavior. If no behavior changes, then nothing can ever happen.
I mean that if you pass X and Y into a function and get Z in 2.6, then you should be able to get Z from passing X and Y in 2.7 even if there's a new argument that returns Z' if you pass True to it. (Obviously, this is somewhat simplified, but I hope it helps.)
This definition only makes sense if the interesting thing about a function is its return value, and if the only sort of thing you have are functions. What about side-effects, or exceptional conditions? What about interactions with subclasses? (Changing a class in a library from old-style to new-style has serious repercussions, as MRO conflicts can result in applications that subclass it.)
I've added a little more about this to the PEP. See what you think.
How do you propose to measure the benefit to breakage ratio? How do you propose to define "trivial" to fix? Most projects, including Python, Twisted, Django, and Zope, have an ever-increasing bug count, which means that trivial issues fall off the radar pretty easily.
Well, if you're tests aren't failing from it, is it an incompatibility?
Well, let's say the tests do fail from it. Right now, even Twisted trunk still doesn't *quite* support Python 2.6, but only on Windows, due to stricter checking of the 'mode' argument for files. But this failing test is just not that high priority, so our recommendation is "don't use python 2.6 yet on Windows, 2.5 works fine". My point is that triviality is a matter of perspective :). Eventually somebody will get around to it, but 2.6 has been out for a while now.
I understand. I think we will just have to provide guidelines for triviality and decide on a case by case basis.
There are a very large number of users of Python, the large percentage of whom do not read python-dev. A posting on python-list is likely to provoke an unproductive pile-on. I suggest a dedicated list, which should ideally be low traffic, "python-incompatible-announce", or something like that, so that *everyone* who maintains Python software can subscribe to it, even if they're not otherwise interested in Python development.
And that won't generate a pile-on?
Well, the etiquette for that specific list could be "keep this low- traffic unless you have a serious problem with this change". Or, better yet, make it an announce-only list: the pile-on can still happen on python-list, but only the results of the discussion will be announced on the incompatible-announce list.
I think that's a fine idea, but the PEP is dealing with policy as our mailing list infrastructure is now. -- Regards, Benjamin
On 19 Jun, 09:08 pm, benjamin@python.org wrote:
2009/6/19 <glyph@divmod.com>:
On 02:09 pm, benjamin@python.org wrote:
2009/6/19 �<glyph@divmod.com>:
�What about side-effects, or exceptional conditions? �What about interactions with subclasses? �(Changing a class in a library from old-style to new-style has serious repercussions, as MRO conflicts can result in applications that subclass it.)
I've added a little more about this to the PEP. See what you think.
Finally had a chance to take a look. It's a big improvement, certainly: much more specific. Although I think I have a few quibbles with the wording. For one thing, I don't like the use of the word "reasonable". Everybody thinks *their* exception to the rules is reasonable, but nobody else's is. Besides, if the BDFL thinks a change is really reasonable, do you think a PEP is going to stop him? :) For another, I think it's actually a bit too strict, as worded. The side-effects of a function includes the warnings that it emits; any method-call can have side-effects, so you can't change an algorithm *at all*. The only side-effects that I think are important are the ones that get invoked on objects that an application gets to inject somewhere, or inspect later via a public API. The word "releases" also needs to be qualified. Most importantly, minor-version and micro-version releases need to be described distinctly. Finally, the PEP should mention its participation in the universe of compatibility process PEPs. It should describe its relationship to at least some of PEP 3002, 291, 5, 6, 2, 3001, and 384.
My point is that triviality is a matter of perspective :).
I understand. I think we will just have to provide guidelines for triviality and decide on a case by case basis.
A simple litmus test, or some examples, of triviality would be helpful.
the pile-on can still happen on python-list, but only the results of the discussion will be announced on the incompatible-announce list.
I think that's a fine idea, but the PEP is dealing with policy as our mailing list infrastructure is now.
Hmm... well, maybe everybody should just run their tests against Python trunk. The commits list is a reliable notification mechanism for potentially incompatible changes ;-). Perhaps it would be good to mention this specifically in the PEP? For example, "third party projects may request that an incompatible change be reverted, by providing evidence of tests failing on x.y+1 that passed on x.y, where the code in question does not rely on any details not specified as 'public' in the section above"? If more projects did this when there *was* a problem, then it would actually be a lot easier to break the policy with confidence. With an incompatible change, you could know, "we checked it in, and nobody complained". Whereas right now is nobody complains it's more likely that nobody is paying attention.
Benjamin Peterson wrote:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
Should this PEP include a note about features which require new syntax, particularly new keywords? The policy (AFAICT) is that if new keywords are created they are enabled with a future import (with a warning?) in the version they are introduced and then enabled by default in the next version. All the best, Michael Foord
PEP: 387 Title: Backwards Compatibility Policy Version: $Revision$ Last-Modified: $Date$ Author: Benjamin Peterson <benjamin@python.org> Status: Draft Type: Process Content-Type: text/x-rst Created: 18-Jun-2009
Abstract ========
This PEP outlines Python's backwards compatibility policy.
Rationale =========
As one of the most used programming languages today [#tiobe]_, the Python core language and its standard library play a critcal role in thousands of applications and libraries. This is fantastic; it is probably one of a language designer's most wishful dreams. However, it means the development team must be very careful not to break this existing 3rd party code with new releases.
Backwards Compatibility Rules =============================
This policy applys to all public APIs. These include the C-API, the standard library, and the core language including syntax and operation as defined by the reference manual.
This is the basic policy for backwards compatibility:
* The behavior of an API *must* not change between any two consecutive releases.
* A feature cannot be removed without notice between any two consecutive releases.
* Addition of a feature which breaks 3rd party libraries or applications should have a large benefit to breakage ratio, and/or the incompatibility should be trival to fix in broken code.
Making Incompatible Changes ===========================
It's a fact: design mistakes happen. Thus it is important to be able to change APIs or remove misguided features. This is accomplished through a gradual process over several releases:
1. Discuss the change. Depending on the size of the incompatibility, this could be on the bug tracker, python-dev, python-list, or the appropriate SIG. A PEP or similar document may be written. Hopefully users of the affected API will pipe up to comment.
2. Add a warning [#warnings_]_. If behavior is changing, a the API may gain a new function or method to perform the new behavior; old usage should raise the warning. If an API is being removed, simply warn whenever it is entered. DeprecationWarning is the usual warning category to use, but PendingDeprecationWarning may be used in special cases were the old and new versions of the API will coexist for many releases.
3. Wait for a release.
4. See if there's any feedback. Users not involved in the original discussions may comment now after seeing the warning. Perhaps reconsider.
5. The behavior change or feature removal may now be made default or permanent in the next release. Remove the old version and warning.
References ==========
.. [#tiobe] TIOBE Programming Community Index
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
.. [#warnings] The warnings module
http://docs.python.org/library/warnings.html
Copyright =========
This document has been placed in the public domain.
.. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 coding: utf-8 End: _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
Not sure why we need yet another pep on the subject. Just update PEP 5 if needed. Also, I think there is a certain amount of wishful thinking here. Ideally, we could approve a tiny PEP with just a few bullet points and it would eliminate the need for all of the complicated decision making that we usually go through. Ideally, we could make all decisions in advance of seeing the facts for a given situation. ISTM, this pep is wishing away the actual complexity of making software design decisions. The policy for 2.x should probably be different than for 3.x. ISTM that 3.x has not been fully shaken out and that possibly many things will need to change as users start to report problems. The text vs bytes issue is lurking throughout the release. The JSON module in particular was affected by a half thought out port to 3.0. And yesterday on #python IRC, one developer reported the email package in 3.1 to be unusable and one of its maintainers characterized it as being in need of a serious overhaul (meaning major API changes). In the end, there is going to have to be some thoughtful balancing between making the needed changes and not hurting the existing users. I don't think a small, general purpose PEP like this one can wish that away. Another sticking point about preserving features across releases arises if the feature itself is hazardous in some way (like have a security hole or some such). The contextlib.nested() function was an example. It didn't ever really work as advertised for its intended purpose (it wasn't truly equivalent to two nested with-statements) and it presented users with the possibility of hard-to-spot bugs. The bugfix for it was to replace it with new syntax. Unfortunately, the new syntax didn't provide all of the functionality of the original. So, the question arises about whether this particular mine should be left on the battlefield. We resolved the question after a long and thoughtful discussion; I don't think that decision making process could have been solved in advance by a bullet-point in a general purpose process PEP. Raymond
2009/6/19 Raymond Hettinger <python@rcn.com>:
Not sure why we need yet another pep on the subject. Just update PEP 5 if needed.
Hmm. I didn't know about that one.
Also, I think there is a certain amount of wishful thinking here. Ideally, we could approve a tiny PEP with just a few bullet points and it would eliminate the need for all of the complicated decision making that we usually go through. Ideally, we could make all decisions in advance of seeing the facts for a given situation. ISTM, this pep is wishing away the actual complexity of making software design decisions.
I think it should just serve as a backbone and basis for decisions. It will certainly not eliminate any complex thinking, but hopefully it will More importantly, it shows to the community what our policies are and what they should expect. Exceptions should be exceptional not the norm.
The policy for 2.x should probably be different than for 3.x. ISTM that 3.x has not been fully shaken out and that possibly many things will need to change as users start to report problems. The text vs bytes issue is lurking throughout the release. The JSON module in particular was affected by a half thought out port to 3.0. And yesterday on #python IRC, one developer reported the email package in 3.1 to be unusable and one of its maintainers characterized it as being in need of a serious overhaul (meaning major API changes). In the end, there is going to have to be some thoughtful balancing between making the needed changes and not hurting the existing users. I don't think a small, general purpose PEP like this one can wish that away.
That is very unfortunate. 2.7 may be the last 2.x release, so this PEP primarily applies to 3.x anyway. We could add a provision for experimental or unstable modules, but I don't think those should be released in the first place.
Another sticking point about preserving features across releases arises if the feature itself is hazardous in some way (like have a security hole or some such). The contextlib.nested() function was an example. It didn't ever really work as advertised for its intended purpose (it wasn't truly equivalent to two nested with-statements) and it presented users with the possibility of hard-to-spot bugs. The bugfix for it was to replace it with new syntax. Unfortunately, the new syntax didn't provide all of the functionality of the original. So, the question arises about whether this particular mine should be left on the battlefield. We resolved the question after a long and thoughtful discussion; I don't think that decision making process could have been solved in advance by a bullet-point in a general purpose process PEP.
I would say that's pretty close to the procedure in the PEP actually: "Discuss the change. This may be on python-dev, the tracker...." -- Regards, Benjamin
On 07:06 pm, python@rcn.com wrote:
Not sure why we need yet another pep on the subject. Just update PEP 5 if needed.
Hmm. This is a good point; it might make sense to have a more specific PEP for library compatibility as opposed to language compatibility though, since language compatibility is necessarily a vaguer concept.
Also, I think there is a certain amount of wishful thinking here. Ideally, we could approve a tiny PEP with just a few bullet points and it would eliminate the need for all of the complicated decision making that we usually go through. Ideally, we could make all decisions in advance of seeing the facts for a given situation. ISTM, this pep is wishing away the actual complexity of making software design decisions.
This is not about making design decisions. This is about how to treat the *output* of design decisions. A really important part of this PEP, as I alluded to previously, is the part that tells developers what *they* should be doing if they want their python code to function on the next release of the interpreter. Right now, the rule to write software that will not break with the next Python release is "read the minds of the python core committers and hope that you do not do anything with the stdlib that they don't like". Along with this there are several rules you can infer that are probably true most of the time: don't call stuff starting with "_", don't monkey- patch anything, don't use dynamic class replacement on objects from classes other than your own, don't depend on the depth of inheritance hierarchies (for example, no ".__bases__[0].__bases__[0]"), make sure your tests run without producing any DeprecationWarnings, be mindful of potential namespace conflicts when adding attributes to classes that inherit from other libraries. I don't think all these things are written down in one place though.
Another sticking point about preserving features across releases arises if the feature itself is hazardous in some way (like have a security hole or some such). The contextlib.nested() function was an example. (...) We resolved the question after a long and thoughtful discussion; I don't think that decision making process could have been solved in advance by a bullet-point in a general purpose process PEP.
You are correct that nothing in Python's policy could have dictated how this problem could be resolved. However, the policy can most definitely state how to deal with code using contextlib.nested in the interim before it has been changed to use the new syntax: to wit, that contextlib.nested has to stick around, continue to do the (broken) thing that it did before, and emit a DeprecationWarning indicating the new syntax. The existing policy in PEP 5 already does this, but doesn't offer guidelines on *how* to do this for lots of different types of changes. For example, how do you issue a deprecation warning for a new parameter you want to require application code to accept? How do you change the name of a parameter, to wit, do you need to expect that all arguments can validly be used as keyword arguments? How do you determine an appropriate stack-depth, etc?
glyph@divmod.com wrote:
On 07:06 pm, python@rcn.com wrote:
Not sure why we need yet another pep on the subject. Just update PEP 5 if needed.
I agree. The draft covers the same ground. Two PEPs on the same subject would be redundant where they agree but would create confusion where they do not. To the extent that the new PEP intends to change existing policy by severely curtailing language change, as it appears to, then that *idea* should be directly presented and discussed, perhaps on python-list, before worrying about wording (bikeshed) details. In other words, I think the discussion should have start out "Here is existing policy (PEP 5). I propose to change it like so..." or possibly "Here is existing policy in PEP 5. I believe it has defacto changed as evidenced by ... " In other words, discuss and decide whether the bikeshed should be re-painted before worrying about the exact shade.
Right now, the rule to write software that will not break with the next Python release is "read the minds of the python core committers and hope that you do not do anything with the stdlib that they don't like".
A bit harsh ;-)
Along with this there are several rules you can infer that are probably true most of the time: don't call stuff starting with "_", don't monkey- patch anything, don't use dynamic class replacement on objects from classes other than your own, don't depend on the depth of inheritance hierarchies (for example, no ".__bases__[0].__bases__[0]"), make sure your tests run without producing any DeprecationWarnings, be mindful of potential namespace conflicts when adding attributes to classes that inherit from other libraries. I don't think all these things are written down in one place though.
This could be the core of a new PEP Keeeping up with Language Changes. I think that would be a good thing. Terry Jan Reedy
Benjamin Peterson schrieb:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
Yet another rather pointless bikeshed: if this becomes policy, maybe it should get a PEP number < 100, like PEP 5 or 6. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.
2009/6/19 Georg Brandl <g.brandl@gmx.net>:
Benjamin Peterson schrieb:
Backwards compatibility seems to be an issue that arises a lot here. I think we all have an idea of it is, but we need some hard place to point to. So here's my attempt:
Yet another rather pointless bikeshed: if this becomes policy, maybe it should get a PEP number < 100, like PEP 5 or 6.
The point is to avoid a little bike shedding later. -- Regards, Benjamin
On Fri, Jun 19, 2009, Georg Brandl wrote:
Yet another rather pointless bikeshed: if this becomes policy, maybe it should get a PEP number < 100, like PEP 5 or 6.
+1 -- I was debating whether to say something. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "as long as we like the same operating system, things are cool." --piranha
2009/6/19 Aahz <aahz@pythoncraft.com>:
On Fri, Jun 19, 2009, Georg Brandl wrote:
Yet another rather pointless bikeshed: if this becomes policy, maybe it should get a PEP number < 100, like PEP 5 or 6.
+1 -- I was debating whether to say something.
Is that a +1 to numbering or bike shedding? -- Regards, Benjamin
On Fri, Jun 19, 2009, Benjamin Peterson wrote:
2009/6/19 Aahz <aahz@pythoncraft.com>:
On Fri, Jun 19, 2009, Georg Brandl wrote:
Yet another rather pointless bikeshed: if this becomes policy, maybe it should get a PEP number < 100, like PEP 5 or 6.
+1 -- I was debating whether to say something.
Is that a +1 to numbering or bike shedding?
+1 to changing the PEP number -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "as long as we like the same operating system, things are cool." --piranha
On Thu, Jun 18, 2009 at 7:17 PM, Benjamin Peterson<benjamin@python.org> wrote: [snip]
Backwards Compatibility Rules =============================
This policy applys to all public APIs. These include the C-API, the standard library, and the core language including syntax and operation as defined by the reference manual.
This is the basic policy for backwards compatibility:
* The behavior of an API *must* not change between any two consecutive releases.
Is this intended to include performance changes? Clearly no-one will complain if things simply get faster, but I'm thinking about cases where, say, a function runs in half the time but uses double the memory (or vice versa). Collin
2009/6/20 Collin Winter <collinw@gmail.com>:
Is this intended to include performance changes? Clearly no-one will complain if things simply get faster, but I'm thinking about cases where, say, a function runs in half the time but uses double the memory (or vice versa).
I don't think we can say anything about those cases before hand. We'll have to decide on a case by case basis. -- Regards, Benjamin
participants (12)
-
Aahz
-
Antoine Pitrou
-
Benjamin Peterson
-
Collin Winter
-
Georg Brandl
-
glyph@divmod.com
-
Michael Foord
-
Paul Moore
-
R. David Murray
-
Raymond Hettinger
-
Terry Reedy
-
Tres Seaver