{"id":16889397,"url":"https://github.com/dcbaker/constprotocol","last_synced_at":"2025-03-20T07:17:46.082Z","repository":{"id":67595512,"uuid":"300983624","full_name":"dcbaker/constprotocol","owner":"dcbaker","description":"A set of Immutable Protocols for python","archived":false,"fork":false,"pushed_at":"2020-10-09T19:13:23.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-25T08:27:38.652Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dcbaker.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2020-10-03T21:27:37.000Z","updated_at":"2020-10-09T19:13:20.000Z","dependencies_parsed_at":"2023-02-26T18:45:31.943Z","dependency_job_id":null,"html_url":"https://github.com/dcbaker/constprotocol","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcbaker%2Fconstprotocol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcbaker%2Fconstprotocol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcbaker%2Fconstprotocol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dcbaker%2Fconstprotocol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dcbaker","download_url":"https://codeload.github.com/dcbaker/constprotocol/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244566948,"owners_count":20473451,"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":[],"created_at":"2024-10-13T16:57:03.918Z","updated_at":"2025-03-20T07:17:46.077Z","avatar_url":"https://github.com/dcbaker.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nConst Protocols\n===============\n\nPython is inherently mutable, only a handful of builtin classes enjoy true\nimmutability such as ``int``\\ , ``str``\\ , and ``tuple``\\ ; everything else is up for\ngrabs. This allows a lot of clever tricks, but it also leads to some\ndifficult bugs. Just think about code like this:\n\n.. code-block:: python\n\n   class MyClass:\n\n       def __init__(self) -\u003e None:\n           self._mylist: List[str] = []\n\n       def get_mylist(self) -\u003e List[str]:\n           return self._mylist\n\nWhat happens when someone gets that list and mutates it? Of course, you get\nbugs because MyClass now has something it doesn't expect. The easiest way to\nfix that is of course to do this:\n\n.. code-block:: python\n\n   class MyClass:\n\n       def __init__(self) -\u003e None:\n           self._mylist: List[str] = []\n\n       def get_mylist(self) -\u003e List[str]:\n           return self._mylist.copy()\n\nProblem solved, right? What if _mylist has a million entires? That can\nquickly become a huge bottle neck in your program. In most statically typed\nlanguages you have some kind of \"const\" or \"immutable\" modifier that tells\nthe compiler/interpreter \"don't let anyone modify this\". Normally that would\nbe impossible in python, but we have static type checkers like mypy, and\nProtocols. We can create a protocol that implements all of the methods that\ndon't mutate a class, and expose only those. Then the static type checker can\ncatch the mutation for us.\n\n.. code-block:: python\n\n   from constprotocol import ConstList\n\n   class MyClass:\n\n       def __init__(self) -\u003e None:\n           self._mylist: List[str] = []\n\n       def get_mylist(self) -\u003e ConstList[str]:\n           return self._mylist\n\n   c = MyClass()\n   c.get_mylist().append('foo')  # Error: ConstList has not method append!\n\nOf course, the underlying python values have not actually become immutable,\nbut like C and C++ it's more of a promise that if you take a ConstList or\nreturn one that you're not going to modify it.\n\nOne of the goals of const protocol is to have 0 runtime performance impact. You\ncould create an immutable proxy, that wraps a value and only exposes it's const\nmethods, and all of it's attributes as read only (using properties and more\nproxies). But that has runtime overhead and adds code complexity. This approach\nadds zero run time overhead and very little complexity to the code.\n\nWhat if I want to mutate the value after all?\n---------------------------------------------\n\nYou don't.\n\nNo seriously, if you say you're not going to modify it, don't.\n\nWhat you probably want to do is copy the constified value, which will give\nyou a mutable value:\n\n.. code-block:: python\n\n   l: ConstList[str] = ['a', 'b', 'c']\n   ml = l.copy()\n   reveal_type(ml)\n\nWhich will be ``List[str]``\n\nIf you really, really, need to, you can use ``typing.cast``. of course, you get\nto keep the pieces.\n\nStatus\n------\n\nRight now there are four classes ``ConstSet`` for ``set``\\ , ``ConstList`` for\n``list``\\ , ``ConstMapping`` for ``mappings``\\ , and ``ContDict`` for ``Dict``. There's\nlikely bugs, this is alpha quality software, and a kind of crazy idea to get\nbetter error checking in cases where the author knows that someone shouldn't\nbe mutating their data.\n\nI found a bug\n-------------\n\nCool, file an issue.\n\nI want fix something or add something\n-------------------------------------\n\nEven better, open a Merge Request\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcbaker%2Fconstprotocol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcbaker%2Fconstprotocol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcbaker%2Fconstprotocol/lists"}