René's URL Explorer Experiment


Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython · GitHub

Open Graph Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython

X Title: Fix version_info cache invalidation, typing, parsing, and serialization by EliahKagan · Pull Request #1838 · gitpython-developers/GitPython

Description: Fixes #1829 - cache invalidation Fixes #1830 - typing Fixes #1833 - parsing Fixes #1836 - serialization Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes sense to include them here with #1829, is already largely covered in the discussion/comments in those three issues. Design goals for #1829 For #1829, which I would regard to be the biggest of the changes here, and which is the change for which the most additional code is added to the test suite, I made sure to preserve three important properties of the code: Refreshing is global but affects all Git instances. Changing the way refreshing itself fundamentally works is beyond the scope of this PR and would also be a breaking change that I think could only be made in a new major release, and there are good reasons for refreshing to be global. Allowing a per-instance overriding refresh might be useful, but would be much more complicated than the current system unless accompanied by other design changes that would likely be breaking. Caching is performed for version_info attributes. This has been explicitly promised in the version_info docstring for a long time, with the intention that accessing version_info be understood as cheap, and it is likely that substantial code exists that assumes it is cheap (or that throttles parts of GitPython that use it and benefit from it being cheap). Caching does not take place across Git instances, and accessing version_info on one Git instance never has any effect on what happens when version_info is accessed on another Git instance. Since the actual validity of cached version information depends on when the global operation of refreshing was last done, it may seem like the caching should be made global as well. But this can lead to some new problems, such that I suggest it either be put off until later or never done. Why not cache globally? To expand on why the that third property is something I've taken care to preserve, these are the three problems I anticipate will arise if the caching were to be made global, in increasing order of significance: Although GitPython is not in general thread-safe (#1052), caching globally may still make it less capable of concurrency than before, and the interaction with multiprocessing would also have to be considered. That the cache is per-instance is a long-standing behavior, which existing code may rely on. It is not explicitly stated in the property docstring, and I've taken care not to start stating it, in case it is to be changed. But I think it is how readers would have understood the docstring, since it is documenting an instance property and does not describe the cache as global or shared. Not sharing the cache ensures that the code Git().version_info always makes a subprocess call to get fresh information. Besides being one of the ways this behavior may have been relied on already, changing it would also, in practice, lead to code that uses GitPython being written to refresh more often, which would have the opposite effect on speed and resource usage than is intended by caching. The cache invalidation implementation Rather than having the refresh operation reach out to all Git instances to invalidate their caches, I've added a _refresh_token class attribute that is set and reset along with GIT_PYTHON_GIT_EXECUTABLE during a refresh, and added a _version_info_token instance attribute (that, like _version_info, is excluded from pickle serialization). When version_info is accessed, if _version_info_token is the same object as _refresh_token, then the cache is valid and the cached value in _version_info is returned. Otherwise, the cache is invalid, and git version is run to obtain the version; the version information is cached in _version_info, _version_info_token is updated to _refresh_token, and the newly computed value is returned. The _version_info_token instance attribute starts out as None to simplify debugging and unpickling. Its value is always either None or a value that has at some point been the value of the _refresh_token class attribute. The _refresh_token class attribute is never None, but always a direct instance of object, even before the first refresh, which ensures it never wrongly matches the initial None value of a _version_info_token instance attribute. A successful refresh always causes _refresh_token to differ from any _version_info_token that exists at that time. This is important--and, in particular, these tokens must not be tied to the value of Git.GIT_PYTHON_GIT_EXECUTABLE--because refreshing must properly update for a change to which git implementation is accessed by the same command or even via the same path. The most common and important such case is probably when GIT_PYTHON_GIT_EXECUTABLE is the default value of "git", and git has been upgraded or a new version installed in a $PATH directory. Dropping the LazyMixin base class An aspect of this PR that I recommend be double-checked in review is my assumption that Git having LazyMixin as a base class can be considered a mere implementation detail of Git, and thus that it is non-breaking to remove inheritance from that base class. I removed it because it was only being used to implement caching for the version_info property. It is well-suited to some forms of caching elsewhere in GitPython, but not well-suited to the changes needed in version_info for #1829 and #1836 (as discussed in #1836). Because I stopped using LazyMixin for this, it was no longer used for anything. Removing it simplified the code, though if necessary it could be brought back and just not used for anything. (The new version_info implementation would still work with LazyMixin present but unused.) Other considerations Keeping the super().__init__ call Git.__init__ contains this line: GitPython/git/cmd.py Line 778 in afa5754 super().__init__() This superficially appears related to the use of LazyMixin, but I believe that is not the case. LazyMixin, which is provided by the gitdb dependency, at least currently does not define an __init__ method. Furthermore, such a super() call was present even before LazyMixin was brought in as a base class in e583a9e to provide caching for the version_info property (which was also introduced at that time). At that time, Git had no base class besides object, yet this was still present. Calling super().__init__() helps with cooperative multiple inheritance in some cases. It is sometimes done even in the absence of a specific need, though sometimes it's not the correct logic, if the next class in the MRO has an __init__ that is not callable with no arguments (or, rather, with only a self argument). Furthermore, you've given feedback that I have understood to mean that having Git support inheritance is not a design priority. So I don't think this super().__init__() is present based on a general preference for it. Instead, looking back to when it was first introduced, this was in the first commit that had the Git class, 039bfe3. At that time, Git did have a base class, MethodMissingMixin. It may be that it should either be removed or commented with an explanation. But I think it is sufficiently unconnected to LazyMixin (and version_info) that no change should be made to it in connection with the changes in this PR. Effect of directly accessing _version_info has changed When LazyMixin was used, reading from the nonpublic _version_info attribute behaved the same as reading from version_info. Code outside the Git class should not do that, but there is one place in GitPython's own tests where it did: GitPython/test/test_index.py Line 531 in afa5754 if rw_repo.git._version_info < (2, 29): That had to be changed to use version_info instead. If necessary, a different backing attribute could be used, and _version_info could be made an undocumented alternative to version_info that issues a warning but otherwise behaves the same. However, it seems to me that this is not necessary; _version_info was never advertised as part of the public interface of Git, as far as I can tell. GIT_PYTHON_GIT_EXECUTABLE as an instance attribute is not supported If Git instances could have their own GIT_PYTHON_GIT_EXECUTABLE attributes, then they would be used instead of the same-named class attribute that is written in a refresh. This cannot happen on a direct Git instance, because Git is slotted and has no such slot (and cannot, since the descriptor would conflict with the class attribute). But it is possible to make a subclass of Git where GIT_PYTHON_GIT_EXECUTABLE is an instance attribute. This has never been supported and should not be done, and will likely work poorly in other ways, but it is possible for caching to "support" it by detecting that this is the case and just never caching version_info. I have neither done this nor commented about it, and even this mention of it may be gratuitous. However, in general the effect of attempting to set what is assumed to be a class attribute on an instance should be considered. My reasoning for not dealing with this at all is that, even if a reasonable use case arises to derive from Git and shadow base-class class attributes as instance attributes (which seems unlikely), the onus is then on the author of the derived class to override base-class members like version_info accordingly.

Open Graph Description: Fixes #1829 - cache invalidation Fixes #1830 - typing Fixes #1833 - parsing Fixes #1836 - serialization Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ...

X Description: Fixes #1829 - cache invalidation Fixes #1830 - typing Fixes #1833 - parsing Fixes #1836 - serialization Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ...

Opengraph URL: https://github.com/gitpython-developers/GitPython/pull/1838

X: @github

direct link

Domain: github.com

route-pattern/:user_id/:repository/pull/:id/checks(.:format)
route-controllerpull_requests
route-actionchecks
fetch-noncev2:ed2cf9fc-e1bb-22b8-7b95-c4a0dc5de8dc
current-catalog-service-hash87dc3bc62d9b466312751bfd5f889726f4f1337bdff4e8be7da7c93d6c00a25a
request-idAE3E:3E0E7:45138E:5EFF3E:6968C992
html-safe-nonce95ff2c757609859be42b029bff052aa3bcf6284e2e06c2dc07b5dccccac32c75
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBRTNFOjNFMEU3OjQ1MTM4RTo1RUZGM0U6Njk2OEM5OTIiLCJ2aXNpdG9yX2lkIjoiODIxNjgwMDM0OTM2MzEwMTYyIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0=
visitor-hmac935b0fb6745ce089728a307da86fc89516e45692f545af183c2c38d31555462c
hovercard-subject-tagpull_request:1739883635
github-keyboard-shortcutsrepository,pull-request-list,pull-request-conversation,pull-request-files-changed,checks,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///pull_requests/show/checks
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/gitpython-developers/GitPython/pull/1838/checks
twitter:imagehttps://avatars.githubusercontent.com/u/1771172?s=400&v=4
twitter:cardsummary_large_image
og:imagehttps://avatars.githubusercontent.com/u/1771172?s=400&v=4
og:image:altFixes #1829 - cache invalidation Fixes #1830 - typing Fixes #1833 - parsing Fixes #1836 - serialization Much of the design in the fixes for #1830, #1833, and #1836, as well as why I think it makes ...
og:site_nameGitHub
og:typeobject
hostnamegithub.com
expected-hostnamegithub.com
None2ad1b0ea56d6705db45f5db2c83c79d467c52ec2bb1a9b2ec250b964fcb7fb71
turbo-cache-controlno-preview
go-importgithub.com/gitpython-developers/GitPython git https://github.com/gitpython-developers/GitPython.git
octolytics-dimension-user_id503709
octolytics-dimension-user_logingitpython-developers
octolytics-dimension-repository_id1126087
octolytics-dimension-repository_nwogitpython-developers/GitPython
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id1126087
octolytics-dimension-repository_network_root_nwogitpython-developers/GitPython
turbo-body-classeslogged-out env-production page-responsive full-width full-width-p-0
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release3e85bdc155d44512be88257f76953bc86fe88a6b
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/gitpython-developers/GitPython/pull/1838/checks#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fgitpython-developers%2FGitPython%2Fpull%2F1838%2Fchecks
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fgitpython-developers%2FGitPython%2Fpull%2F1838%2Fchecks
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fpull_requests%2Fshow%2Fchecks&source=header-repo&source_repo=gitpython-developers%2FGitPython
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Reloadhttps://github.com/gitpython-developers/GitPython/pull/1838/checks
gitpython-developers https://github.com/gitpython-developers
GitPythonhttps://github.com/gitpython-developers/GitPython
Please reload this pagehttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Notifications https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython
Fork 964 https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython
Star 5k https://github.com/login?return_to=%2Fgitpython-developers%2FGitPython
Code https://github.com/gitpython-developers/GitPython
Issues 169 https://github.com/gitpython-developers/GitPython/issues
Pull requests 8 https://github.com/gitpython-developers/GitPython/pulls
Discussions https://github.com/gitpython-developers/GitPython/discussions
Actions https://github.com/gitpython-developers/GitPython/actions
Security Uh oh! There was an error while loading. Please reload this page. https://github.com/gitpython-developers/GitPython/security
Please reload this pagehttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Insights https://github.com/gitpython-developers/GitPython/pulse
Code https://github.com/gitpython-developers/GitPython
Issues https://github.com/gitpython-developers/GitPython/issues
Pull requests https://github.com/gitpython-developers/GitPython/pulls
Discussions https://github.com/gitpython-developers/GitPython/discussions
Actions https://github.com/gitpython-developers/GitPython/actions
Security https://github.com/gitpython-developers/GitPython/security
Insights https://github.com/gitpython-developers/GitPython/pulse
Sign up for GitHub https://github.com/signup?return_to=%2Fgitpython-developers%2FGitPython%2Fissues%2Fnew%2Fchoose
terms of servicehttps://docs.github.com/terms
privacy statementhttps://docs.github.com/privacy
Sign inhttps://github.com/login?return_to=%2Fgitpython-developers%2FGitPython%2Fissues%2Fnew%2Fchoose
Byronhttps://github.com/Byron
gitpython-developers:mainhttps://github.com/gitpython-developers/GitPython/tree/main
EliahKagan:refresh-versionhttps://github.com/EliahKagan/GitPython/tree/refresh-version
Conversation 1 https://github.com/gitpython-developers/GitPython/pull/1838
Commits 19 https://github.com/gitpython-developers/GitPython/pull/1838/commits
Checks 0 https://github.com/gitpython-developers/GitPython/pull/1838/checks
Files changed https://github.com/gitpython-developers/GitPython/pull/1838/files
Please reload this pagehttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Please reload this pagehttps://github.com/gitpython-developers/GitPython/pull/1838/checks
Fix version_info cache invalidation, typing, parsing, and serialization https://github.com/gitpython-developers/GitPython/pull/1838/checks#top
Please reload this pagehttps://github.com/gitpython-developers/GitPython/pull/1838/checks
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.