[Twisted-Python] Twisted v21.2.0 breaks Crossbar.io
Hi all, rgd Twisted v21.2.0, thanks a lot to all involved in this release! the release, mypy, more py3, .. +1! really appreciated. now, fwiw, just wanted to let you know (sorry, too late) about a regression with https://github.com/crossbario/crossbar that actually originates from https://github.com/crossbario/autobahn-python/issues/1470 => with Twisted v20.3.0 calling twisted.internet.protocol.ProcessProtocol.getPeer raises an AttributeError with Twisted v21.2.0 calling the same method raises an NotImplementedError => this fix broke our workaround for the bug now fixed;) https://github.com/crossbario/autobahn-python/pull/1469/files and released in: https://pypi.org/project/autobahn/21.3.1/ https://pypi.org/project/crossbar/21.3.1/ Cheers, /Tobias
On Tue, Mar 2, 2021 at 12:12 PM Tobias Oberstein <tobias.oberstein@gmail.com> wrote:
this fix broke our workaround for the bug now fixed;)
I had to read that sentence a few times before I fully understood it, and my mind did a backflip. :) Is there a quick and dirty way to run Crossbar's CI against a pre-release version of Twisted? For Buildbot (a project that I work with a lot), I just tweaked their CI like this to run against a branch of Twisted: https://github.com/buildbot/buildbot/pull/5739 When those tests failed, that helped give some info, and helped me isolate one thing to fix in Twisted before doing the release. Povilas Kanapickas and Pierre Tardy from the Buildbot project were very helpful with this. I did not run the test suite for Matrix Synapse, but Richard van der Hoff was very helpful in terms of giving feedback. Twisted's internal test suite is quite good and comprehensive, but it doesn't catch everything. I find it useful to do quick tests against existing projects where possible. Twisted has been around for around 20 years, and is used *all over the place*! -- Craig
Hi Craig, thanks for your answer, tips and questions! much appreciated Am 03.03.21 um 02:58 schrieb Craig Rodrigues:
On Tue, Mar 2, 2021 at 12:12 PM Tobias Oberstein <tobias.oberstein@gmail.com <mailto:tobias.oberstein@gmail.com>> wrote:
this fix broke our workaround for the bug now fixed;)
I had to read that sentence a few times before I fully understood it, and my mind did a backflip. :)
I hope the grammar is fine (not a native speaker), but it seems to express what I wanted to say .. and that has some recursive, mind stretching aspect to it, yeah ;) there are a couple of layers .. eg the specific problem is for sth where Twisted itself defines an interface (zope-interface), but then fails to implement it. However, declaring a zope.interface without CI testing it is just "documentation". best effort. But that isn't the last layer: without declaring exactly all interfaces formally in the first place, there will always be a gray area, where users accidently rely on behavior-as-is, even when that behavior is a bug. Then how to reconceile fixing the bug vs stability? IOW: how can one declare "stability" if the object of that (the API) isn't exhaustively and formally defined? it took me 10min to figure out and fix the bug, but I am still thinking about the "why" and the "should" .. and about Rust ;)
Is there a quick and dirty way to run Crossbar's CI against a pre-release version of Twisted?
Actually, there was a time when we ran Twisted trunk as part of the CI. Due to complexity managing our deps (~130), and CI run-time, we stopped that .. well, not so good. I think this would have warned us early on. https://github.com/crossbario/crossbar/blob/master/tox.ini
For Buildbot (a project that I work with a lot), I just tweaked their CI like this to run against a branch of Twisted: https://github.com/buildbot/buildbot/pull/5739 When those tests failed, that helped give some info, and helped me isolate one thing to fix in Twisted before doing the release. Povilas Kanapickas and Pierre Tardy from the Buildbot project were very helpful with this.
That sounds pretty good! congrats.
I did not run the test suite for Matrix Synapse, but Richard van der Hoff was very helpful in terms of giving feedback.
Twisted's internal test suite is quite good and comprehensive, but it doesn't catch everything. I find it useful to do quick tests against existing projects where possible.
Twisted has been around for around 20 years, and is used *all over the place*! -- Craig
-- Tobias Oberstein - phone +49 176 2375 2055 - tobias.oberstein@crossbario.com Crossbar.io GmbH - Waldstrasse 18 - 91054 Erlangen HRB 15870 - Amtsgericht Fuerth - Geschäftsfuehrer/CEO - Tobias Oberstein https://crossbar.io https://crossbario.com
On Tue, Mar 2, 2021 at 3:09 PM Tobias Oberstein <tobias.oberstein@gmail.com> wrote:
Hi all,
rgd Twisted v21.2.0, thanks a lot to all involved in this release! the release, mypy, more py3, .. +1! really appreciated.
now, fwiw, just wanted to let you know (sorry, too late) about a regression with
https://github.com/crossbario/crossbar
that actually originates from
https://github.com/crossbario/autobahn-python/issues/1470
=>
with Twisted v20.3.0 calling twisted.internet.protocol.ProcessProtocol.getPeer raises an AttributeError
with Twisted v21.2.0 calling the same method raises an NotImplementedError
=>
this fix broke our workaround for the bug now fixed;)
https://github.com/crossbario/autobahn-python/pull/1469/files
and released in:
https://pypi.org/project/autobahn/21.3.1/ https://pypi.org/project/crossbar/21.3.1/
Cheers, /Tobias
It seems like this change does not actually violate Twisted's compatibility policy. This is not to say introducing such regressions is desirable, but when constructing such work-arounds it might be worth paying closer-than-average attention to what the policy actually is. The policy is currently documented at https://twistedmatrix.com/documents/current/core/development/policy/compatib... (and that anchor takes you to the section that I think allows the change). Policy aside, this change doesn't seem like much of an improvement to me. If I were to guess, I would guess the change was made to satisfy some check Mypy is now being asked to make about Twisted. If that's the case, it seems unfortunate that real-world software is suffering so that a synthetic goal can be achieved. I do recognize there is a perception that practical value can come from attending to the errors Mypy reports. It would probably benefit everyone if more care were taken to consider the real-world consequences of changes that are made to satisfy the non-real-world goalposts set by tools like Mypy. Jean-Paul
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
Hi Jean-Paul,
It seems like this change does not actually violate Twisted's compatibility policy. This is not to say introducing such regressions is desirable, but when constructing such work-arounds it might be worth paying closer-than-average attention to what the policy actually is.
The policy is currently documented at https://twistedmatrix.com/documents/current/core/development/policy/compatib...
Thanks! I wasn't aware (well, I was, but I never read all the docs;) That indeed quite precisely hints at sth we did in our workaround: relying on the output of dir() IOW, we should have done except (AttributeError, NotImplementedError): in the first place, because that catches both. on an different angle, I wonder about the relation between both exceptions independent of Twisted. eg "attribute not defined" certainly is an "attribute error"? and in any case, a missing attribute means it cannot be implemented? so it cannot be implemented without making attribute access succeed, right? but even if that would all be defined, it could not be expressed in python. or even statically checked. because that would require some machine readable language / decorators / whatever to exhaustively state what exceptions can be raised in the first place. and then, an app developer (crossbar in this case) can make sure and test that it does actually have code to treat _all_ exceptions that can ever be raised. anyways, just some ramblings .. python won't give me that, yes.
(and that anchor takes you to the section that I think allows the change).
Policy aside, this change doesn't seem like much of an improvement to me. If I were to guess, I would guess the change was made to satisfy some check Mypy is now being asked to make about Twisted. If that's the case, it seems unfortunate that real-world software is suffering so that a synthetic goal can be achieved. I do recognize there is a perception that practical value can come from attending to the errors Mypy reports. It would probably benefit everyone if more care were taken to consider the real-world consequences of changes that are made to satisfy the non-real-world goalposts set by tools like Mypy.
fwiw, I highly welcome the use of mypy! even with some fallout;) we use it too (in crossbarfx). but ^ might indeed be what happened practically. np, I can deal with it. Twisted has been a very reliable, and central piece of our stack - and is here to stay! happy user=) and, I guess, all workarounds (mine) eventually come back and bite. that's life;) Cheers, /Tobias
Jean-Paul
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com <mailto:Twisted-Python@twistedmatrix.com> https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
-- Tobias Oberstein - phone +49 176 2375 2055 - tobias.oberstein@crossbario.com Crossbar.io GmbH - Waldstrasse 18 - 91054 Erlangen HRB 15870 - Amtsgericht Fuerth - Geschäftsfuehrer/CEO - Tobias Oberstein https://crossbar.io https://crossbario.com
On Mar 2, 2021, at 6:10 PM, Jean-Paul Calderone <exarkun@twistedmatrix.com> wrote:
Policy aside, this change doesn't seem like much of an improvement to me. If I were to guess, I would guess the change was made to satisfy some check Mypy is now being asked to make about Twisted. If that's the case, it seems unfortunate that real-world software is suffering so that a synthetic goal can be achieved. I do recognize there is a perception that practical value can come from attending to the errors Mypy reports. It would probably benefit everyone if more care were taken to consider the real-world consequences of changes that are made to satisfy the non-real-world goalposts set by tools like Mypy.
I do think that Mypy was likely highlighting a real issue here, and it should have been fixed. We had documented interfaces already, and we were failing to adhere to them properly, and Mypy was complaining about that. For easy reference, the change that made these fixes is here https://github.com/twisted/twisted/pull/1298 <https://github.com/twisted/twisted/pull/1298> This genre of fix is definitely something we should unwind over time, although it does make it easier to start mypy-clean rather than have hundreds of exclusion lines that need to be manually adjusted, so on balance I agree with this tradeoff at the point it was made. Specifically what I mean by "this genre of fix" is that you can always make mypy happy by transforming code like this: class Thing: pass Thing().stuff() into code like this: class Thing: @property def stuff(self): raise AttributeError("no such thing as `stuff`") Thing().stuff() The runtime behavior is the same, but now Mypy can't tell you about this class of bug any more. So, at some point, we should slowly unwind all `NotImplementedError` methods and find ways to either implement them for real or make their required use raise at import time. Finally: let's not develop an aversion to new tooling and change because it might create problems; experience over the last few years has shown me that Mypy can catch tons of real bugs and it's well worth getting the codebase to type check. If we want to prevent breakages like this in the future, the answer is not to stop trying to get linters and typecheckers to run cleanly with arbitrary changes, but to figure out some kind of continuous integration solution that's actually continuous with our downstream dependencies If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great. -g
On Wed, Mar 3, 2021 at 3:08 AM Glyph <glyph@twistedmatrix.com> wrote:
On Mar 2, 2021, at 6:10 PM, Jean-Paul Calderone <exarkun@twistedmatrix.com> wrote:
Policy aside, this change doesn't seem like much of an improvement to me. If I were to guess, I would guess the change was made to satisfy some check Mypy is now being asked to make about Twisted. If that's the case, it seems unfortunate that real-world software is suffering so that a synthetic goal can be achieved. I do recognize there is a perception that practical value can come from attending to the errors Mypy reports. It would probably benefit everyone if more care were taken to consider the real-world consequences of changes that are made to satisfy the non-real-world goalposts set by tools like Mypy.
I do think that Mypy was likely highlighting a real issue here, and it should have been fixed. We had documented interfaces already, and we were failing to adhere to them properly, and Mypy was complaining about that.
For easy reference, the change that made these fixes is here https://github.com/twisted/twisted/pull/1298
This genre of fix is definitely something we should unwind over time, although it does make it easier to start mypy-clean rather than have hundreds of exclusion lines that need to be manually adjusted, so on balance I agree with this tradeoff at the point it was made.
Broadly, I agree. But not with this part. It seems like there is clearly a trade-off that is better for everyone. The trade-off represented by #1298: - Breaks application code without providing any new functionality or fixing any bugs - Captures a long list of TODOs as an arbitrarily complex collection of sources spread across the entire codebase - All the work of addressing the problems still remains to be done Contrast this with any basic ratchet-style approach (for example, capture the list of mypy errors, then allow any PR that either removes some or doesn't add any new ones): - At the outset, no application code breaks because nothing has actually changed - As work towards *fixing* the TODOs progresses, if it *does* break application code then at least applications have a chance at some benefit - As the work towards fixing the TODOs progresses, maintainers can easily look up what issues remain by consulting the list of errors that feeds the ratchet-style job. The exact mechanism for the ratchet isn't the point here. Maybe Mypy plays more nicely with inline comments telling it to ignore something, I don't know. The point is: - A centralized list of stuff left to do is better than a mess smeared across the whole codebase - If we're going to risk breaking applications we should at least try to offer some value to them - We shouldn't make applications deal with every change twice when we could just disturb them once Jean-Paul
Specifically what I mean by "this genre of fix" is that you can always make mypy happy by transforming code like this:
class Thing: pass
Thing().stuff()
into code like this:
class Thing: @property def stuff(self): raise AttributeError("no such thing as `stuff`")
Thing().stuff()
The runtime behavior is the same, but now Mypy can't tell you about this class of bug any more.
So, at some point, we should slowly unwind all `NotImplementedError` methods and find ways to either implement them for real or make their required use raise at import time.
Finally: let's not develop an aversion to new tooling and change because it might create problems; experience over the last few years has shown me that Mypy can catch *tons* of real bugs and it's well worth getting the codebase to type check. If we want to prevent breakages like this in the future, the answer is not to stop trying to get linters and typecheckers to run cleanly with arbitrary changes, but to figure out some kind of *continuous integration *solution that's actually continuous with our downstream dependencies
If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great.
-g _______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com https://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
On Mar 3, 2021, at 4:58 AM, Jean-Paul Calderone <exarkun@twistedmatrix.com> wrote:
Broadly, I agree. But not with this part. It seems like there is clearly a trade-off that is better for everyone. The trade-off represented by #1298: Breaks application code without providing any new functionality or fixing any bugs Captures a long list of TODOs as an arbitrarily complex collection of sources spread across the entire codebase All the work of addressing the problems still remains to be done Contrast this with any basic ratchet-style approach (for example, capture the list of mypy errors, then allow any PR that either removes some or doesn't add any new ones):
In principle, I agree. And some coarse-grained ratchets are absolutely worth doing, when they're supported by the tools - for example, we have one of these with the way we're handling `disallow_untyped_defs` right now. I wish tools like Mypy would build in ratcheting infrastructure that was easy to use and convenient to deploy. But in practice this type of ratchet involves a "# type: ignore" at every single call site, as well as delaying shipping stubs or maintaining separate stubs. As well as building the maladaptive habit that #type:ignore or cast()ing your way out of type errors is the right way to address them in individual features and getting every code reviewer used to that.
At the outset, no application code breaks because nothing has actually changed As work towards fixing the TODOs progresses, if it does break application code then at least applications have a chance at some benefit As the work towards fixing the TODOs progresses, maintainers can easily look up what issues remain by consulting the list of errors that feeds the ratchet-style job. The exact mechanism for the ratchet isn't the point here. Maybe Mypy plays more nicely with inline comments telling it to ignore something, I don't know. The point is:
The "exact mechanism for the ratchet" introduces enough hard problems that it inevitably delays tooling adoption for months or years, reduces the benefits of the tooling adoption (what Twisted users really want are comprehensive stubs, less than Twisted to type-check internally, I think, and this lets us ship them sooner in at least a mostly-usable state).
A centralized list of stuff left to do is better than a mess smeared across the whole codebase If we're going to risk breaking applications we should at least try to offer some value to them We shouldn't make applications deal with every change twice when we could just disturb them once
Overall I very much appreciate your criticism and I wish that I could provide clear and concrete steps towards a functioning ratchet for all of these types of tools, but in practice the minor issues caused by the boil-the-ocean approach are an order of magnitude easier to address (and cheaper to leave un-addressed) than the effort required to build ratchets for our contributors that aren't a total nightmare to maintain and interact with. Case in point: running `black` was a giant earthquake in the codebase but the pain associated with that was a drop in the bucket compared to the lingering, years-long nightmare of our attempt to build a style ratchet with twistedchecker. In principle what we were trying to do with twistedchecker was very simple, but the practicalities ate us alive. You could of course quite rightly argue that there were multiple conflated issues at play there! But that's also the point: there's always a lot of complexity that comes along with trying to do codemod migrations "the right way" when that's going against the grain of the rest of the ecosystem. I don't love that we've foisted this off onto our users, but I suspect Tobias spent a lot less time fixing the bug here than we've spent writing emails about it at this point ;-). I look forward to the day where I'm wrong about all of this and every new code quality tool comes with a super easy-to-deploy ratchet, though. -g
On 03/03/2021 08:07, Glyph wrote:
If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great.
I'll take this to the Synapse team to discuss further, but we could probably easily arrange for one of our CI runs to install Twisted trunk from git instead of pypi, which might be a good start.
On Mar 3, 2021, at 8:25 AM, Richard van der Hoff <richard@matrix.org> wrote:
On 03/03/2021 08:07, Glyph wrote:
If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great.
I'll take this to the Synapse team to discuss further, but we could probably easily arrange for one of our CI runs to install Twisted trunk from git instead of pypi, which might be a good start.
This is specifically the approach I'd really rather not take :) and here's why: You want to provide stability for you contributors so that if a problem is introduced, you don't halt development on that unrelated feature to diagnose the upstream issue. You want to ensure that when users install your software, it works with everything that's currently released. Ideally your CI configuration would be a timed job which builds against trunk and PR builds that build against a release, and then some process in place for reacting to regressions and filing a ticket upstream with us so we can work out what to do. Easier said than done though, I know! I wish we had something like this on Twisted for all of our dependencies, but we have had enough difficulty keeping our CI configuration current based on what cloud provider is falling over this month ;-). -g
On 03/03/2021 18:47, Glyph wrote:
I'll take this to the Synapse team to discuss further, but we could probably easily arrange for one of our CI runs to install Twisted trunk from git instead of pypi, which might be a good start.
This is specifically the approach I'd really rather /not/ take :) and here's why:
1. You want to provide stability for you contributors so that if a problem is introduced, you don't halt development on that unrelated feature to diagnose the upstream issue.
2. You want to ensure that when /users/ install your software, it works with everything that's currently released.
I'm not sure I follow this. We'd still have CI runs that test against *release* Twisted; I'm just proposing that we would *also* test against development Twisted. Your point about not stopping development when there's a problem is well noted though. A CI pipeline that runs on a timer might work fine. I'll discuss with the team.
but we have had enough difficulty keeping our CI configuration current based on what cloud provider is falling over this month ;-).
Yes, CI seems to be universally one of those things that is conceptually simple but somehow takes hours and hours to maintain.
but we have had enough difficulty keeping our CI configuration current based on what cloud provider is falling over this month ;-).
Yes, CI seems to be universally one of those things that is conceptually simple but somehow takes hours and hours to maintain.
Absolutely agreed, glad that it's not only me feeling like that=) If I'd sum up the time I just recently spent on pip, tox, docker, github actions, terraform, .. the list goes on, and compare to time actually spent on coding, it's depressing. for our cloud stuff with Crossbar.io, from code-to-live looks like: GitHub branch (code change) -> GitHub actions (CI main) -> GitHub master (code merge) -> Python wheel (CD "deploy") -> Docker image (CD "docker") -> downstream repo: AMI (via Packer) -> Terraform (live) for devices, it looks like: GitHub branch (code change) -> GitHub actions (CI main) -> GitHub master (code merge) -> Python wheel (CD "deploy") -> snap (CD "snap") -> downstream repo: Yocto -> S3 -> CDN -> RAUC (live) ---- obviously, a lot of things can go wrong. one decision we made: all CI/CD pipelines run through a common set of Python wheels we build.
on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great.
ok, I'll take that to the team: https://github.com/crossbario/crossbar/issues/1867 tldr: we could run our CI in 2 sets, one using pinned deps, and one with open-ended deps, our latest own master branches _and_ latest Twisted (trunk).
Hi folks, On 03/03/2021 08:07, Glyph wrote:
Finally: let's not develop an aversion to new tooling and change because it might create problems; experience over the last few years has shown me that Mypy can catch /tons/ of real bugs and it's well worth getting the codebase to type check. If we want to prevent breakages like this in the future, the answer is not to stop trying to get linters and typecheckers to run cleanly with arbitrary changes, but to figure out some kind of /continuous integration /solution that's actually continuous with our downstream dependencies
If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great.
Just to let you know, that I have (not before time) set up a Github Actions run which runs a subset of Synapse's CI against Twisted trunk. If it fails, it opens an issue against our repo, where it'll be triaged via our normal process. Hopefully this will bring benefits to everyone by giving early warning of any potential regressions! If you're interested, the GHA config is at https://github.com/matrix-org/synapse/blob/develop/.github/workflows/twisted.... Cheers Richard
I also know what pyopenssl did this until recently when it was discovered that the tests were skipped
On Aug 23, 2021, at 3:40 AM, Richard van der Hoff <richard@matrix.org> wrote:
Hi folks,
On 03/03/2021 08:07, Glyph wrote:
Finally: let's not develop an aversion to new tooling and change because it might create problems; experience over the last few years has shown me that Mypy can catch tons of real bugs and it's well worth getting the codebase to type check. If we want to prevent breakages like this in the future, the answer is not to stop trying to get linters and typecheckers to run cleanly with arbitrary changes, but to figure out some kind of continuous integration solution that's actually continuous with our downstream dependencies
If dependencies could start testing against Twisted trunk in some capacity, we could get notified close to when unintentionally breaking changes occur, and dependencies can let us know well before the release happens, and we can either revert or they can fix things if the error is on their end. If initially, say, crossbar and matrix would like to work with us to set up some kind of repeatable pattern we can suggest to others, that would be great. Just to let you know, that I have (not before time) set up a Github Actions run which runs a subset of Synapse's CI against Twisted trunk. If it fails, it opens an issue against our repo, where it'll be triaged via our normal process. Hopefully this will bring benefits to everyone by giving early warning of any potential regressions!
If you're interested, the GHA config is at https://github.com/matrix-org/synapse/blob/develop/.github/workflows/twisted... <https://github.com/matrix-org/synapse/blob/develop/.github/workflows/twisted...>.
Cheers
Richard
Awesome, thank you for doing this Richard! I see the ticket-opening action there too, so I look forward to hearing about bugs immediately when we screw things up in the future :) -g
participants (7)
-
Craig Rodrigues
-
Glyph
-
Jean-Paul Calderone
-
Richard van der Hoff
-
Thomas Grainger
-
Tobias Oberstein
-
Tobias Oberstein