-
-
Notifications
You must be signed in to change notification settings - Fork 908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SymbolicReference.set_reference may not roll back properly on error #1669
Comments
This is a great catch, thanks for documenting your findings! Even though having more tests for resource-leakage (or the lack thereof due to correct implementation) would be valuable, I dare to ask if it's possible to fix the usage of Thinking further, all implementations of |
This fixes the initialization of the "ok" flag by setting it to False before the operation that might fail is attempted. (This is a fix for the *specific* problem reported in gitpython-developers#1669 about how the pattern SymbolicReference.set_reference attempts to use with LockedFD is not correctly implemented.)
I'm pleased to hear that turning classes with I've opened a PR, #1675, that does the much more narrow change of (1) fixing the specific bug described here that arises from the incorrectly used flag variable, and also (2) doing some refactoring to make it and those like it less likely and more generally to make (I'm not sure how best to proceed in this issue, but my inclination would be to treat this issue as specifically about |
There definitely is more subtle cases of leakage, but this one is particularly low-hanging I think.
Yes, I agree - this issue can stay and the way I see it will be closed very soon anyway. If you choose to go ahead and fix |
If done currently, it would be multiple PRs across multiple repositories, right? I am myself unlikely to do this right away: I would at minimum want to add native Windows testing to CI before beginning on it, and there are some static analysis tools that may help find places where something can be used as a context manager but isn't, once that thing is actually a context manager. |
Oh, right! I totally forgot about
That definitely makes perfect sense. I wonder if it would also help to bring These are just loose thoughts though, you should do as you see fit. |
I was reminded of this by the discussion in #1767, and I am curious: should attention to the long-idle gitpython-developers/smmap#33 be renewed at some point, with the hope of merging it (perhaps with changes to account for the multiple versions through which smmap has gone since it was last updated)? If context managers are going to be added to the types that currently do all or most of their cleanup via This also seems like something that should be considered before an attempt is made to move the gitdb and smmap repositories into the GitPython repository, since I don't believe pull requests can be effectively transferred. (I just mean I think GitHub PRs cannot be made to reflect that. Obviously commits from any branch of any repository can be merged into, or rebased onto, any branch of any repository.) Of course I understand that, even if and when such a thing were to be done, all the |
That is a lovely idea, and a method that would be required to make the use of context managers work at all. Thanks for pointing me at the indeed long-stale
Yes, that's a valid point and it's very true as well. Maybe, if a breaking change is considered to get rid of It seems like two birds one stone to me, especially if migrating to existing code is effectively simple. Of course, this can be simplified by allowing people to keep using the I am quite intrigued about that idea! |
SymbolicReference.set_reference
contains this code, which is meant to attempt a transactional write:GitPython/git/refs/symbolic.py
Lines 373 to 380 in a5a6464
However, because the
ok
flag is initialized toTrue
instead ofFalse
--which strongly appears unintentional--it is alwaysTrue
even if theok = True
statement in thetry
block is not reached. Consequently,lfd.rollback()
in thefinally
block is never called. This seems on first glance like a serious bug, but as detailed below I believe it might actually be quite minor in the context of what kind of cleanup GitPython generally does or does not guarantee.This can be contrasted to another place where that pattern is used correctly:
GitPython/git/index/base.py
Lines 227 to 233 in a5a6464
This is very simple to fix, since it is sufficient to change
True
toFalse
in the initial assignment. So most of the work to fix it would be figuring out where a regression test should go and how it should manage to produce a case where cleanup doesn't happen immediately.(Besides that simple fix, other fixes that also reduce the risk of such bugs coming back and allow the code to be simplified are possible. In particular, the
LockedFD
object represents a non-memory resource that always can and should be managed deterministically according to where control flow is, so it ought to implement the context manager protocol.)I am unsure if this bug causes any problems in practice. If I understand
LockedFD
correctly, the write that is not rolled back is to a temporary file separate from the real target, accessed through a file object specific to thatLockedFD
object. The lock file name is determined from the target file name, so if a separate attempt to write to the target is made, that could cause problems.LockedFD
has a__del__
method that should be called eventually and performs the rollback. On CPython, which has reference counting as its primary garbage collection strategy, cleanup would often happen immediately as a result... though not always, even in the absence of reference cycles, because, in exception handling, an exception object holds a reference to a traceback object which holds references to all stack frames in its call stack, through which local variables thus remain accessible until control leaves the handlingexcept
block.Still, it may be that this shall not be considered a bug--or at least not a bug of its own, or at least not a bug that merits a regression test accompanying its bugfix--on the grounds that it might be considered a special case of the Leakage of System Resources limitation documented prominently in the readme. In support of this view, the third place in the code of GitPython where
ldf.rollback
is called uses a different pattern and its comments suggest that cleanup viaLockedFD.__del__
is considered acceptable to rely on:GitPython/git/refs/log.py
Lines 261 to 268 in a5a6464
(Interestingly, that is not exactly equivalent to the behavior of the
try
-finally
pattern used ingit/index/base.py
. If it is intended to be, then it should catchBaseException
rather thanException
.)The text was updated successfully, but these errors were encountered: