René's URL Explorer Experiment


Title: Make Version properties change from read-only to read-write? · Issue #327 · python-semver/python-semver · GitHub

Open Graph Title: Make Version properties change from read-only to read-write? · Issue #327 · python-semver/python-semver

X Title: Make Version properties change from read-only to read-write? · Issue #327 · python-semver/python-semver

Description: Situation After having invested a lot of time in #317 with __init__() and validating our data, I did some research and here is another of my crazy ideas. 😄 Sorry if the text is a bit longer, but I think it could be worth. It might simpli...

Open Graph Description: Situation After having invested a lot of time in #317 with __init__() and validating our data, I did some research and here is another of my crazy ideas. 😄 Sorry if the text is a bit longer, but I ...

X Description: Situation After having invested a lot of time in #317 with __init__() and validating our data, I did some research and here is another of my crazy ideas. 😄 Sorry if the text is a bit longer, but I ...

Opengraph URL: https://github.com/python-semver/python-semver/issues/327

X: @github

direct link

Domain: patch-diff.githubusercontent.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Make Version properties change from read-only to read-write?","articleBody":"# Situation\r\n\r\nAfter having invested a lot of time in #317 with `__init__()` and validating our data, I did some research and here is another of my crazy ideas. :smile:  Sorry if the text is a bit longer, but I think it could be worth.\r\n\r\nIt might simplify `__init__()` but it would change the way our properties are handled a bit.\r\n\r\nAccording to the section [Accessing Parts of a Version Through Names](https://python-semver.readthedocs.io/en/latest/usage.html#accessing-parts-of-a-version-through-names) in our documentation, the `Version` class contains the properties `major`, `minor` etc.\r\n\r\nHowever, at the time of writing, these properties can be read only. They do not allow write access:\r\n\r\n```python\r\n\u003e\u003e\u003e v = Version.parse(\"3.4.5-pre.2+build.4\")\r\n\u003e\u003e\u003e v.minor = 5\r\nTraceback (most recent call last):\r\n...\r\nAttributeError: attribute 'minor' is readonly\r\n```\r\n\r\n# The Idea\r\n\r\nI'm wondering if this behavior should be changed. :thinking:  Is that still contemporary? I can't exactly remember why we did that. :wink: \r\n\r\nMaybe our users have a use case which they would like to write something like this:\r\n\r\n```python\r\n\u003e\u003e\u003e v.major = 4\r\n\u003e\u003e\u003e v.minor = \"10\"\r\n```\r\n\r\nWhy shouldn't they? Would that make sense?\r\n\r\nOf course, the assignment should only be successful if we pass the right type. Invalid values are still rejected and would lead to an exception:\r\n\r\n```python\r\n\u003e\u003e\u003e v.major = \"x\"\r\nTraceback (most recent call last):\r\n...\r\nValueError: major allows only integers or strings that can be converted to integers.\r\n```\r\n\r\n\r\n# The Implementation\r\n\r\nThe idea is to start with some validating functions. We distinguish basically between two types:\r\n\r\n* the **main parts** consists of major, minor, and patch. \r\n  They must be of type `int` or `str`. Furthermore, if it is an integer, the value must be greater than zero.\r\n* the **\"other\" parts** are prerelease and build.\r\n  Here we need to check if it is `None` (allowed) or a of type `int` or `str` (also allowed).\r\n\r\nWe end up with this:\r\n\r\n```python\r\ndef validate_mainparts(value: Union[str, int]):\r\n    \"\"\"\r\n    Validate arguments for major, minor, or patch.\r\n\r\n    :param value: the value.\r\n        * Must be of type int, str.\r\n        * If it's an int, value must be value \u003e 0\r\n    :return: Nothing, if everything is okay, otherwise raise exceptions.\r\n    \"\"\"\r\n    if not isinstance(value, (int, str)):\r\n        raise TypeError(f\"The value {value} has wrong type {type(value)}. \"\r\n                        \"Expected int or str.\")\r\n    value = int(value)  # could also raise an exception\r\n    if value \u003c 0:\r\n        raise ValueError(f\"The value {value} cannot be negative. \"\r\n                         \"A version is always positive.\")\r\n\r\ndef validate_otherparts(value: Optional[Union[str, int]]):\r\n    \"\"\"\r\n    Validate arguments for prerelease and build.\r\n\r\n    :param value: the value. Can be None, int, or str.\r\n    :return: Nothing, if everything is okay, otherwise raise TypeError exception.\r\n    \"\"\"\r\n    if isinstance(value, (int, str, type(None))):\r\n        return\r\n    raise TypeError(f\"The value {value} has wrong type {type(value)}. \"\r\n                        \"Expected int or str.\")\r\n```\r\n\r\nFurthermore, we need a decorator/function that creates this property automatically for us. That decorator needs to be called with a name (like `major`), a callable to check the value (see above), and another callable to convert it to the target type. One implementation could look like:\r\n\r\n```python\r\ndef typed_property(name: str, validate: Callable, convert: Callable):\r\n    \"\"\"\r\n    Create properties for version parts.\r\n\r\n    :param name: The name of the property.\r\n    :param validate: a function which validates the value that is going to be set.\r\n        Returns nothing if all is good, otherwise raise ValueError or TypeError\r\n        exceptions if the value couldn't be validated.\r\n    :param: convert: a function which converts the input data name to the target\r\n        type.\r\n    :return: the property\r\n    \"\"\"\r\n    private_name = f\"_{name}\"\r\n\r\n    def fget(self):\r\n        return getattr(self, private_name)\r\n\r\n    def fset(self, value):\r\n        validate(value)  # May raise exceptions here\r\n        setattr(self, private_name, convert(value))  # all fine, so set the private name\r\n\r\n    return property(\r\n        fget=fget, fset=fset, doc=f\"\"\"The {name} part of a version (read and write).\"\"\"\r\n    )\r\n```\r\n\r\nTo combine it in our class, we need to add it at the class level:\r\n\r\n```python\r\nclass Version:\r\n    \"\"\" ... \"\"\"\r\n    major = typed_property(\"major\", validate_mainparts, int)\r\n    minor = typed_property(\"minor\", validate_mainparts, int)\r\n    patch = typed_property(\"patch\", validate_mainparts, int)\r\n    prerelease = typed_property(\"prerelease\", validate_otherparts, lambda x: x if x is None else str(x))\r\n    build = typed_property(\"build\", validate_otherparts, lambda x: x if x is None else str(x))\r\n\r\n   # ... no other @property...\r\n```\r\n\r\n----\r\n\r\nI see here some benefits:\r\n\r\n* We don't need to repeat code. All is hidden inside `typed_property` and the validating functions.\r\n* The initializer `__init__` is free from validating the inputs. This is all done in the validating functions. Should simplify the initializer method a bit.\r\n\r\nI see also some design issues:\r\n\r\n* Having two property names.\r\n  The `typed_property` decorator needs to have the property name twice, as an assignment on left side and as a string in the argument. It's probably not much, but it feels a bit un-pythonic.\r\n\r\n* Need two callables for the `typed_property` decorator.\r\n  Although this is not much either, it is also feels a bit awkward, especially with the lambda for prerelease and build.\r\n\r\n* Validating functions outside the class\r\n   Currently, the validating functions are _outside_ of the `Version` class. I see currently no solution to integrate that into the class as that a forward reference would be needed. Theoretically, we could do that in `__init__` (or even `__new__`) but that would complicate the matter.\r\n\r\n# Going Beyond (Descriptor Protocol)\r\nThe above issues could be solved. Python has for this kind of problems the descriptor protocol.\r\n\r\nProperties are just an implementation of the more general descriptor protocol. That is a class which contains one of the methods `__get__`, `__set__` or `__delete__`, see https://docs.python.org/3/howto/descriptor.html\r\n\r\nTheoretically, we could add an additional class which implements the above behavior in the said methods. This would be a charming way as all the checking/validating is hidden inside the class.\r\n\r\nHowever, I'm not sure to introduce another class just to implement that. It seems also a bit of an overkill to me, although it's quite pythonic. Not sure if we would introduce other problems as it raises the complexity a bit.\r\nOn the other side, it could be beneficial if we (or another user) want to derive the `Version` class with some other validating parts.\r\n\r\n# Related Information\r\n\r\n* https://realpython.com/python-descriptors/\r\n* https://www.datacamp.com/community/tutorials/python-descriptors\r\n* https://deepsource.io/blog/demystifying-python-descriptor-protocol/\r\n\r\n----\r\n\r\n@python-semver/reviewers @tlaferriere  Any comments? Thoughts?\r\n\r\n","author":{"url":"https://github.com/tomschr","@type":"Person","name":"tomschr"},"datePublished":"2020-11-22T11:59:16.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/327/python-semver/issues/327"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:2e6c7277-364e-b047-3a10-7e56347e0761
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idD864:26DD3A:1B3A3F:255E6E:696E6B6D
html-safe-nonce742d0b5ba48cda2ac264c6ad1c5e8230b7f6cb14306b7203bb1a5585ab01928b
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEODY0OjI2REQzQToxQjNBM0Y6MjU1RTZFOjY5NkU2QjZEIiwidmlzaXRvcl9pZCI6IjUyODMxODU5ODA2MjQyMzUzNzMiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ==
visitor-hmac5b3ea865b734380b36f9ffe6fbabc3b1e96b09aa987630f881f72cb354201eff
hovercard-subject-tagissue:748227314
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/python-semver/python-semver/327/issue_layout
twitter:imagehttps://opengraph.githubassets.com/ec54dfe548fd904b8fa56e8b1831148034e546094a5a48fd4c87b015b31323e1/python-semver/python-semver/issues/327
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/ec54dfe548fd904b8fa56e8b1831148034e546094a5a48fd4c87b015b31323e1/python-semver/python-semver/issues/327
og:image:altSituation After having invested a lot of time in #317 with __init__() and validating our data, I did some research and here is another of my crazy ideas. 😄 Sorry if the text is a bit longer, but I ...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernametomschr
hostnamegithub.com
expected-hostnamegithub.com
Nonedd24262d9e4947a6c63df018b01e9a6da49caaf8259148a56a04b665d15d66b3
turbo-cache-controlno-preview
go-importgithub.com/python-semver/python-semver git https://github.com/python-semver/python-semver.git
octolytics-dimension-user_id57228994
octolytics-dimension-user_loginpython-semver
octolytics-dimension-repository_id3375726
octolytics-dimension-repository_nwopython-semver/python-semver
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id3375726
octolytics-dimension-repository_network_root_nwopython-semver/python-semver
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release307ab96ec5f21762ff6c530a60b1ef6b1e74dd5a
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327#start-of-content
https://patch-diff.githubusercontent.com/
Sign in https://patch-diff.githubusercontent.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython-semver%2Fpython-semver%2Fissues%2F327
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://patch-diff.githubusercontent.com/login?return_to=https%3A%2F%2Fgithub.com%2Fpython-semver%2Fpython-semver%2Fissues%2F327
Sign up https://patch-diff.githubusercontent.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=python-semver%2Fpython-semver
Reloadhttps://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327
Reloadhttps://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327
Reloadhttps://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327
python-semver https://patch-diff.githubusercontent.com/python-semver
python-semverhttps://patch-diff.githubusercontent.com/python-semver/python-semver
Notifications https://patch-diff.githubusercontent.com/login?return_to=%2Fpython-semver%2Fpython-semver
Fork 96 https://patch-diff.githubusercontent.com/login?return_to=%2Fpython-semver%2Fpython-semver
Star 514 https://patch-diff.githubusercontent.com/login?return_to=%2Fpython-semver%2Fpython-semver
Code https://patch-diff.githubusercontent.com/python-semver/python-semver
Issues 12 https://patch-diff.githubusercontent.com/python-semver/python-semver/issues
Pull requests 3 https://patch-diff.githubusercontent.com/python-semver/python-semver/pulls
Discussions https://patch-diff.githubusercontent.com/python-semver/python-semver/discussions
Actions https://patch-diff.githubusercontent.com/python-semver/python-semver/actions
Projects 0 https://patch-diff.githubusercontent.com/python-semver/python-semver/projects
Security Uh oh! There was an error while loading. Please reload this page. https://patch-diff.githubusercontent.com/python-semver/python-semver/security
Please reload this pagehttps://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327
Insights https://patch-diff.githubusercontent.com/python-semver/python-semver/pulse
Code https://patch-diff.githubusercontent.com/python-semver/python-semver
Issues https://patch-diff.githubusercontent.com/python-semver/python-semver/issues
Pull requests https://patch-diff.githubusercontent.com/python-semver/python-semver/pulls
Discussions https://patch-diff.githubusercontent.com/python-semver/python-semver/discussions
Actions https://patch-diff.githubusercontent.com/python-semver/python-semver/actions
Projects https://patch-diff.githubusercontent.com/python-semver/python-semver/projects
Security https://patch-diff.githubusercontent.com/python-semver/python-semver/security
Insights https://patch-diff.githubusercontent.com/python-semver/python-semver/pulse
New issuehttps://patch-diff.githubusercontent.com/login?return_to=https://github.com/python-semver/python-semver/issues/327
New issuehttps://patch-diff.githubusercontent.com/login?return_to=https://github.com/python-semver/python-semver/issues/327
Make Version properties change from read-only to read-write?https://patch-diff.githubusercontent.com/python-semver/python-semver/issues/327#top
DesignIdeas, suggestions, musings about design questionshttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Design%22
EnhancementNot a bug, but increases or improves in value, quality, desirability, or attractivenesshttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Enhancement%22
QuestionUnclear or open issue subject for debatehttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Question%22
Release_3.x.yOnly for the major release 3https://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Release_3.x.y%22
https://github.com/tomschr
https://github.com/tomschr
tomschrhttps://github.com/tomschr
on Nov 22, 2020https://github.com/python-semver/python-semver/issues/327#issue-748227314
#317https://github.com/python-semver/python-semver/pull/317
Accessing Parts of a Version Through Nameshttps://python-semver.readthedocs.io/en/latest/usage.html#accessing-parts-of-a-version-through-names
https://docs.python.org/3/howto/descriptor.htmlhttps://docs.python.org/3/howto/descriptor.html
https://realpython.com/python-descriptors/https://realpython.com/python-descriptors/
https://www.datacamp.com/community/tutorials/python-descriptorshttps://www.datacamp.com/community/tutorials/python-descriptors
https://deepsource.io/blog/demystifying-python-descriptor-protocol/https://deepsource.io/blog/demystifying-python-descriptor-protocol/
@tlaferrierehttps://github.com/tlaferriere
DesignIdeas, suggestions, musings about design questionshttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Design%22
EnhancementNot a bug, but increases or improves in value, quality, desirability, or attractivenesshttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Enhancement%22
QuestionUnclear or open issue subject for debatehttps://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Question%22
Release_3.x.yOnly for the major release 3https://github.com/python-semver/python-semver/issues?q=state%3Aopen%20label%3A%22Release_3.x.y%22
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.