{"id":15629006,"url":"https://github.com/pwwang/attr_property","last_synced_at":"2025-07-09T14:36:40.854Z","repository":{"id":76855157,"uuid":"228240748","full_name":"pwwang/attr_property","owner":"pwwang","description":"Property support for attrs","archived":false,"fork":false,"pushed_at":"2020-05-12T05:09:58.000Z","size":72,"stargazers_count":5,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-29T09:57:18.872Z","etag":null,"topics":["attr-property","attrs","python-classes"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pwwang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-15T19:39:09.000Z","updated_at":"2023-07-11T16:29:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"1ccff7f0-bfb6-4588-b8fa-2bb44a3c2bf5","html_url":"https://github.com/pwwang/attr_property","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fattr_property","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fattr_property/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fattr_property/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwwang%2Fattr_property/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pwwang","download_url":"https://codeload.github.com/pwwang/attr_property/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251480070,"owners_count":21596016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attr-property","attrs","python-classes"],"created_at":"2024-10-03T10:25:18.534Z","updated_at":"2025-04-29T09:57:24.745Z","avatar_url":"https://github.com/pwwang.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# attr_property\n\n[![Pypi][3]][4] [![Github][5]][6] [![PythonVers][8]][4] [![Travis building][10]][11] [![Codacy][12]][13] [![Codacy coverage][14]][13]\n\nProperty support for attrs\n\n## Installation\n```shell\npip install attr_property\n```\n\n## Usage\n### Definitaion of class with `attr_property`s\n```python\nimport attr\nfrom attr_property import attr_property, attr_property_class\n\n@attr_property_class\n@attr.s\nclass A:\n\n  a = attr_property()\n  b = attr.ib() # compatible with original attr.ib\n```\n\n### Specification of getter, setter, and deleter\n```python\ndef a_getter(this, value):\n    print('Property a has been accessed!')\n    return value + 1\ndef a_setter(this, value):\n    print(f'Property a has been set with value {value!r}')\ndef a_deleter(this):\n    print('Property has been deleted!')\n\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(getter = a_getter, setter = a_setter, deleter = a_deleter)\n\na = A(a = 1)\n# Property a has been set with value 1\n# Property a has been accessed!\nprint('a.a =', a.a)\n# a.a = 2\ndel a.a\n# Property has been deleted!\na.a\n# Error\n```\n\n### Disabling deleter\n\n```python\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(deleter = False)\n\na = A(1)\ndel a.a\n# AttributeError: can't delete attribute\n```\n\n### Disabling setter\n\n```python\n@attr_property_class\n@attr.s\nclass A:\n    # remember you can set init = True or\n    # set any default values for it,\n    # as setter will be called in __init__\n    # this will cause AttributeError\n    a = attr_property(init = False, setter = False, getter = lambda this, value: 2)\n\na = A()\na.a = 1\n# AttributeError: can't set attribute\n\n# OK to call getter\na.a == 2\n```\n\n### Run `attr.ib`'s converter and validator in setter\n\n```python\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(converter = int, validator_runtime = True, converter_runtime = True)\n\n    @a.validator\n    def lessthan20(self, attribute, value):\n        if value \u003e= 20:\n            raise ValueError(\"d should be less than 20.\")\n\na = A('3')\n# a.a == 3\na.a = '30'\n# ValueError\n```\n\nOrder of execution of setter:\n\n- Delete cached value\n- Run converter\n- Run validator\n- Run specified setter\n- Save value as raw value\n\n### Caching getter results\n\n```python\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(getter = lambda this, value: value + 1, cache = True)\n\na = A(1)\n# a.a == 2\n# will not do value + again\n# validators and converters will be skipped, as well.\n```\n\n### Accessing raw values before getter calculation\n\n```python\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(getter = lambda this, value: value + 1, convert  = int, raw = True)\n\na = A('1')\n# a.a == 2\n# a._a == 1 # converted value\na._a = 9\n# AttributeError, it's readonly\n```\n\nUsing a different prefix\n```python\n@attr_property_class\n@attr.s\nclass A:\n    a = attr_property(getter = lambda this, value: value + 1, convert  = int, raw = 'raw_')\n\na = A('1')\n# a.raw_a == 1\n```\n\n## How does it work?\n\n- Hack attrs' `_attrs_to_init_script` function to insert codes to initiate `self.__attrs_property_raw__` to save raw values and `__attrs_property_cached__` to save cached values.\n- Create `property`s for each attribute in class decorator `attr_property_class`.\n\n[1]: https://github.com/pwwang/attr_property\n[3]: https://img.shields.io/pypi/v/attr_property?style=flat-square\n[4]: https://pypi.org/project/attr_property/\n[5]: https://img.shields.io/github/tag/pwwang/attr_property?style=flat-square\n[6]: https://github.com/pwwang/attr_property\n[8]: https://img.shields.io/pypi/pyversions/attr_property?style=flat-square\n[10]: https://img.shields.io/travis/pwwang/attr_property?style=flat-square\n[11]: https://travis-ci.org/pwwang/attr_property\n[12]: https://img.shields.io/codacy/grade/41140ad263bc435a822777bed8a41b8d?style=flat-square\n[13]: https://app.codacy.com/manual/pwwang/attr_property/dashboard\n[14]: https://img.shields.io/codacy/coverage/41140ad263bc435a822777bed8a41b8d?style=flat-square\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwwang%2Fattr_property","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpwwang%2Fattr_property","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwwang%2Fattr_property/lists"}