{"id":16696021,"url":"https://github.com/hexdecimal/python-tcod-ec","last_synced_at":"2026-02-23T23:37:33.675Z","repository":{"id":65263385,"uuid":"585399908","full_name":"HexDecimal/python-tcod-ec","owner":"HexDecimal","description":"Entity/Component containers for implementing composition over inheritance.  Works well with type hinting.","archived":false,"fork":false,"pushed_at":"2025-04-07T21:30:37.000Z","size":97,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T21:42:41.082Z","etag":null,"topics":["entity-component","python","python-library","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/HexDecimal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"HexDecimal"}},"created_at":"2023-01-05T04:20:47.000Z","updated_at":"2025-04-07T21:30:38.000Z","dependencies_parsed_at":"2023-09-26T11:47:08.109Z","dependency_job_id":"4f79d672-e518-4533-8260-7c8d067a6399","html_url":"https://github.com/HexDecimal/python-tcod-ec","commit_stats":{"total_commits":31,"total_committers":1,"mean_commits":31.0,"dds":0.0,"last_synced_commit":"26e3d44b5cde6daae101444ac9ee18697c0a76b4"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-ec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-ec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-ec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/HexDecimal%2Fpython-tcod-ec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/HexDecimal","download_url":"https://codeload.github.com/HexDecimal/python-tcod-ec/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248990469,"owners_count":21194761,"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":["entity-component","python","python-library","python3"],"created_at":"2024-10-12T17:25:25.826Z","updated_at":"2026-02-23T23:37:33.631Z","avatar_url":"https://github.com/HexDecimal.png","language":"Python","readme":"# About\n\n[![PyPI](https://img.shields.io/pypi/v/tcod-ec)](https://pypi.org/project/tcod-ec/)\n[![PyPI - License](https://img.shields.io/pypi/l/tcod-ec)](https://github.com/HexDecimal/python-tcod-ec/blob/main/LICENSE)\n[![Documentation Status](https://readthedocs.org/projects/python-tcod-ec/badge/?version=latest)](https://python-tcod-ec.readthedocs.io)\n[![codecov](https://codecov.io/gh/HexDecimal/python-tcod-ec/branch/main/graph/badge.svg?token=UP161WEo0s)](https://codecov.io/gh/HexDecimal/python-tcod-ec)\n\nEntity/Component containers for implementing composition over inheritance.\n\n# Installation\n\nUse pip to install this library:\n```\npip install tcod-ec\n```\n\nIf `tcod` is installed and the version is less than `14.0.0` then `import tcod.ec` will fail.\nRemove or update `tcod` to fix this issue.\n\n# Examples\n\n`tcod.ec.ComponentDict` is a container for anonymous components all with a unique class.\nThe key is the class of the component and can only be assigned one instance of that class.\n\n```py\n\u003e\u003e\u003e import attrs\n\u003e\u003e\u003e import tcod.ec\n\n# Anonymous components don't need special treatment.\n\u003e\u003e\u003e @attrs.define\n... class Position:\n...     x: int = 0\n...     y: int = 0\n\u003e\u003e\u003e @attrs.define\n... class Graphic:\n...     ch: str = \"@\"\n\n# ComponentDict stores a single instance for every unique class, in this case: [str, Position, Graphic]\n\u003e\u003e\u003e entity = tcod.ec.ComponentDict([\"Hello world\", Position(1, 2), Graphic(\"!\")])\n\u003e\u003e\u003e {Position, Graphic} in entity  # Check if an entity has a set of components.\nTrue\n\u003e\u003e\u003e entity[str]  # Access components using the class as the key.\n'Hello world'\n\u003e\u003e\u003e entity[Position].y = 10\n\u003e\u003e\u003e entity[Position]\nPosition(x=1, y=10)\n\u003e\u003e\u003e entity[Graphic] = Graphic(\"?\")  # Explicit setting of the component.\n\u003e\u003e\u003e entity\nComponentDict(['Hello world', Position(x=1, y=10), Graphic(ch='?')])\n\u003e\u003e\u003e entity.set(Graphic(\"#\"))  # Implicit setting.\nComponentDict(['Hello world', Position(x=1, y=10), Graphic(ch='#')])\n\u003e\u003e\u003e del entity[Graphic]  # Components can be deleted.\n\u003e\u003e\u003e entity\nComponentDict(['Hello world', Position(x=1, y=10)])\n\n# Abstract components can be registered with tcod.ec.abstract_component.\n\u003e\u003e\u003e @tcod.ec.abstract_component\n... @attrs.define\n... class Base:\n...     pass\n\u003e\u003e\u003e @attrs.define\n... class Derived(Base):\n...     pass\n\u003e\u003e\u003e entity.set(Derived())  # Derived classes may be set implicitly.\nComponentDict(['Hello world', Position(x=1, y=10), Derived()])\n\u003e\u003e\u003e entity[Base] = Derived()  # Or explicitly assigned to the abstract key.\n\u003e\u003e\u003e Base in entity\nTrue\n\u003e\u003e\u003e entity[Base]  # Any derived classes use the base class as the key.\nDerived()\n\u003e\u003e\u003e entity\nComponentDict(['Hello world', Position(x=1, y=10), Derived()])\n\n```\n\n`tcod.ec.Composite` is a collection of anonymous components.\nUnlike `ComponentDict` this can store multiple components with the same class.\nComponents can also be accessed using the parent class.\nThis works with multiple inheritance.\n\nWhile this class looks like an all around upgrade to `ComponentDict` it's far more complex to work with.\nThe ways it mixes class inheritance with composition can lead to anti-patterns if used carelessly.\nAny class used as a key will return zero, one, or more instances which must be accounted for.\nIf in doubt then the simpler `ComponentDict` should be used instead.\n\n```py\n\n\u003e\u003e\u003e @attrs.define\n... class Body:\n...     name: str\n...     hp: int\n\u003e\u003e\u003e entity = tcod.ec.Composite([Position(1, 2), Graphic(\"!\"), Body(\"torso\", 10), Body(\"head\", 5)])\n\u003e\u003e\u003e {Position, Graphic, Body} in entity\nTrue\n\u003e\u003e\u003e (pos,) = entity[Position]  # Use unpacking logic to verify the number of elements.\n\u003e\u003e\u003e pos.y = 10\n\u003e\u003e\u003e entity[Position]\n[Position(x=1, y=10)]\n\u003e\u003e\u003e entity[Graphic] = [Graphic(\"?\")]  # New sequences can be assigned, this deletes all previous instances of that key.\n\u003e\u003e\u003e entity[Graphic]\n[Graphic(ch='?')]\n\u003e\u003e\u003e del entity[Graphic]\n\u003e\u003e\u003e entity[Graphic]  # Missing keys return an empty sequence instead of KeyError.\n()\n\n\u003e\u003e\u003e entity[Body]\n[Body(name='torso', hp=10), Body(name='head', hp=5)]\n\u003e\u003e\u003e entity.extend([Body(\"legs\", 10), Body(\"arms\", 10)])  # Use append or extend to add new instances.\n\u003e\u003e\u003e for body in list(entity[Body]):  # Make a copy of the sequence if you intend to remove values during iteration.\n...     body.hp -= 2\n...     if body.name == \"torso\":\n...         entity.remove(body)\n\u003e\u003e\u003e entity[Body]\n[Body(name='head', hp=3), Body(name='legs', hp=8), Body(name='arms', hp=8)]\n\n# All objects can be accessed at once using `object`.\n\u003e\u003e\u003e entity[object]\n[Position(x=1, y=10), Body(name='head', hp=3), Body(name='legs', hp=8), Body(name='arms', hp=8)]\n\u003e\u003e\u003e entity[object] = (\"Hello\", \"world\")\n\u003e\u003e\u003e entity\nComposite(['Hello', 'world'])\n\n```\n","funding_links":["https://github.com/sponsors/HexDecimal"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhexdecimal%2Fpython-tcod-ec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhexdecimal%2Fpython-tcod-ec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhexdecimal%2Fpython-tcod-ec/lists"}