#12108 Speed up the twisted.web HTTP client#12109
Conversation
for more information, see https://pre-commit.ci
…lient' into 12108-speed-up-twistedweb-http-client
|
please review |
There was a problem hiding this comment.
Many thanks for the changes.
For now, I just left this is a comment, and put back the team for review.
Happy to see performance improvements in the HTTP client.
I left a few comments inline.
As you commented in the description of the PR, my biggest concern is related to mutating the failure instance.
I remember seing instances in which a failure is mutated, but I don't remember the exact code or the ticket/issue number.
I think that for the purpose of the current code, it's ok to this change.
I expect that at least some downstream application/library that relies on Twisted is mutating failures
I think I have some places in my applications in which I mutate Failure.value. This was done for convenience...but I think the right thing to do is to have someting like newFailure = oldFailure.copy(Exception('new exception')) in which the traceback is kept.
I am doing this type of things mostly in high level application code.
The error messsage text generated by Twisted is not good enought for end users... but I still want to keep the traceback for troubleshoting
Maybe we can create a new FrozenFailure class and use it here.
The FrozenFailure will raise an error is someone tries to mutate it.
|
It's not clear to me there is a traceback in this case, since it's directly creating exceptions. Will validate tomorrow. |
You are right. Still, maybe it's a good idea to have different text messages, depending on the place from where this failure is returned. |
|
So, the main issue is mutable On the other hand, here it's just |
|
Actually, there's a number of places that just assume immutable exceptions already, e.g. the |
|
OK, added a Failure subclass. |
This is probably fine, but thinking about it does send a chill down my spine. I don't think that an immutable failure subclass is actually substitutable, or thereby compliant with the letter of the compatibility policy, as it stands. I'd kinda rather make all Failures be immutable if that is practical, and initially enforce it at type-time, which is not covered by the compatibility policy? |
cough CopyableFailure cough
It's certainly not. I think only enforcing it at type-time is probably good enough. There will never be a 100% immutable Python object. If the type checker tells someone they're not allowed to change something and they go ahead and run code that changes it anyway, that seems like a choice that's up to them. Ideally, there'd be a generic like |
|
(This PR also seems like a weird place to be solving this 24 year old design mistake) |
|
python/typing#1123 has an example of how to define a Protocol with read-only attributes. But actually these days you can just use |
|
Given the Failure object is going to end up in places, e.g. |
Okay, perhaps a comparable amount of chills, but it is a proper Liskov substitute ;) |
My suggestion would be to just have
If my first assumption is wrong (this would not affect much code) then we should probably do another approach. Although then I'd really want to know what the common failure-mutation patterns are. |
adiroiban
left a comment
There was a problem hiding this comment.
Thanks for the changes.
I only have a minor comment regarding the release notes wording.
Based on the latest discussions, I think that we can merge this PR as it is.
Also, I am also ok with merging this code with a standard Failure, just like it was in the first review request.
I have approved this PR, but maybe wait one day to receive more feedback from Jean-Paul and Glyph.
(This PR also seems like a weird place to be solving this 24 year old design mistake)
We don't have to make big API changes in this PR.
I think it is still usefull to discuss this issue.
I was not able to find an existing ticket/GitHub Issue discussing the read only design for Failure.
I guess we should create a new one now :)
I'd kinda rather make all Failures be immutable if that is practical,
I think that such a change should be done in a separate PR.
So my suggestion for this PR:
- Create a new GitHub issue dedicated to the Failure read-only design (or find the existing one, if such an issue was already raised)
- Commit this PR using using
_FrozenFailureand add a note to remove it once the Failure read-only changes are made
Thanks again!
|
There is one existing Failure API that mutates it, https://docs.twisted.org/en/stable/api/twisted.python.failure.Failure.html#cleanFailure |
…lient' into 12108-speed-up-twistedweb-http-client
|
please review |
|
MyPy failures don't appear to have anything to do with this branch... |
for more information, see https://pre-commit.ci
Confirmed, fresh trunk checkout gives similar errors here. Investigating… |
filed #12113 to track and discuss |
|
OK, fix attempt seems to have worked, hopefully that can unblock this |
|
Merging in the approved fix, so maybe we can get a successful run. |
glyph
left a comment
There was a problem hiding this comment.
just some coding-standard compliance suggestions
| typeInfo = failureDict["type"] | ||
| failureDict["type"] = type(typeInfo["__name__"], (), typeInfo) | ||
| f.__dict__ = failureDict | ||
| f.__setstate__(failureDict) |
There was a problem hiding this comment.
Is this API used ?
In src/twisted/logger/__init__.py I can see only these as public API
from ._json import (
eventAsJSON,
eventFromJSON,
jsonFileLogObserver,
eventsFromJSONLogFile,
)
and I don't see any test for this code.
There was a problem hiding this comment.
The tests were failing without this, so I think so.
| tb = tb.tb_next | ||
|
|
||
| @property | ||
| def parents(self): |
There was a problem hiding this comment.
I know that in trunk we alrady have Failure.parents as public API and without documentation.
Can we add a few words about Failure.parents in this PR?
The documentation should go in Failure.__doc__ and not here... but I can't leave a comment there.
There was a problem hiding this comment.
It's an implementation detail, it's better if people don't use it. The fact it's public is a historic quirk...
There was a problem hiding this comment.
Thanks for the info.
If this should be private, then maybe we need a followup issue to mark it private and raise a deprecation warning for any public usage of the API ?
Co-authored-by: Glyph <glyph@twistedmatrix.com>
Scope and purpose
Fixes #12108
Fixes #12112
Why this is faster:
Failure.parentson demand, since it's expensive and often not used.Failure.__init__does a huge amount of unnecessary work for simple cases, so avoided it with alternative constructor. The constructor results in different pickling behavior, because of subclassingBaseExceptionit seems!!!! See https://gist.github.com/itamarst/4e984bddef23a96619b37bfb929e5b24I run my benchmark script with
hyperfineto reduce noise from dictionary ordering randomness and the like.Before:
After:
ORIGINAL APPROACH, NOW OBSOLETE
Important: This relies on
Failureobjects being effectively immutable, which I think is the case but should be something to consider when reviewing.I run my benchmark script with
hyperfineto reduce noise from dictionary ordering randomness and the like.Before:
After:
Test script:
Follow-up PRs will:
Contributor Checklist:
This process applies to all pull requests - no matter how small.
Have a look at our developer documentation before submitting your Pull Request.
Below is a non-exhaustive list (as a reminder):
please review.Our bot will trigger the review process, by applying the pending review label
and requesting a review from the Twisted dev team.