{"id":46305304,"url":"https://github.com/astropenguin/typespecs","last_synced_at":"2026-05-01T08:03:37.412Z","repository":{"id":321755056,"uuid":"1074124188","full_name":"astropenguin/typespecs","owner":"astropenguin","description":"Data specifications by type hints","archived":false,"fork":false,"pushed_at":"2026-04-11T09:09:23.000Z","size":4291,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-11T10:28:12.247Z","etag":null,"topics":["annotation","dataclass","dataframe","namedtuple","python","specification","typeddict","typing"],"latest_commit_sha":null,"homepage":"https://astropenguin.github.io/typespecs/","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/astropenguin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-11T07:29:06.000Z","updated_at":"2026-04-11T09:07:31.000Z","dependencies_parsed_at":"2025-12-11T21:07:55.046Z","dependency_job_id":null,"html_url":"https://github.com/astropenguin/typespecs","commit_stats":null,"previous_names":["astropenguin/typespecs"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/astropenguin/typespecs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astropenguin%2Ftypespecs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astropenguin%2Ftypespecs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astropenguin%2Ftypespecs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astropenguin%2Ftypespecs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astropenguin","download_url":"https://codeload.github.com/astropenguin/typespecs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astropenguin%2Ftypespecs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31677819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-11T08:18:19.405Z","status":"ssl_error","status_checked_at":"2026-04-11T08:17:08.892Z","response_time":54,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["annotation","dataclass","dataframe","namedtuple","python","specification","typeddict","typing"],"created_at":"2026-03-04T12:03:36.500Z","updated_at":"2026-05-01T08:03:37.407Z","avatar_url":"https://github.com/astropenguin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Typespecs\n\n[![Release](https://img.shields.io/pypi/v/typespecs?label=Release\u0026color=cornflowerblue\u0026style=flat-square)](https://pypi.org/project/typespecs/)\n[![Python](https://img.shields.io/pypi/pyversions/typespecs?label=Python\u0026color=cornflowerblue\u0026style=flat-square)](https://pypi.org/project/typespecs/)\n[![Downloads](https://img.shields.io/pypi/dm/typespecs?label=Downloads\u0026color=cornflowerblue\u0026style=flat-square)](https://pepy.tech/project/typespecs)\n[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.17681195-cornflowerblue?style=flat-square)](https://doi.org/10.5281/zenodo.17681195)\n[![Tests](https://img.shields.io/github/actions/workflow/status/astropenguin/typespecs/tests.yaml?label=Tests\u0026style=flat-square)](https://github.com/astropenguin/typespecs/actions)\n\nData specifications by type hints\n\n## Overview\n\nTypespecs is a lightweight Python library that leverages `typing.Annotated` to embed, extract, and manage metadata (such as units, categories, and descriptions) directly within your data structures.\nIt keeps your code clean by binding specifications directly to your type hints.\nThe extracted specifications are returned as a transparent subclass of `pandas.DataFrame`, making it instantly compatible with the rich PyData ecosystem.\n\n## Installation\n\n```bash\npip install typespecs\n```\n\n## Basic Usage\n\nYou can attach metadata to your class fields using `Annotated` and the `typespecs.Spec` object.\nThe `Spec` object acts as a read-only dictionary, ensuring your metadata remains immutable and safe from runtime modifications.\nOnce your data structure is defined, use `typespecs.from_annotated` to parse the instance and extract both the actual data and its associated metadata into a `DataFrame` object.\n\n```python\nimport typespecs as ts\nfrom dataclasses import dataclass\nfrom typing import Annotated as Ann, TypeVar\n\n\n@dataclass\nclass Weather:\n    temp: Ann[list[float], ts.Spec(category=\"data\", name=\"Temperature\", units=\"K\")]\n    wind: Ann[list[float], ts.Spec(category=\"data\", name=\"Wind speed\", units=\"m/s\")]\n    loc: Ann[str, ts.Spec(category=\"info\", name=\"Observed location\")]\n\n\nweather = Weather([273.15, 280.15], [5.0, 10.0], \"Tokyo\")\nspecs = ts.from_annotated(weather)\nprint(specs)\n```\n```\n      category              data               name           type units\ntemp      data  [273.15, 280.15]        Temperature    list[float]     K\nwind      data       [5.0, 10.0]         Wind speed    list[float]   m/s\nloc       info             Tokyo  Observed location  \u003cclass 'str'\u003e  \u003cNA\u003e\n```\n\n## Advanced Usage\n\n### Handling Sub-annotations\n\nTypespecs simplifies working with nested types.\nYou can easily create reusable type aliases with built-in specifications.\nFurthermore, by using the special `typespecs.ITSELF` object, the library dynamically captures the subtype (e.g., `float` in `list[float]`) as one of metadata.\n\n```python\nT = TypeVar(\"T\")\nDtype = Ann[T, ts.Spec(dtype=ts.ITSELF)]\n\n\n@dataclass\nclass Weather:\n    temp: Ann[list[Dtype[float]], ts.Spec(category=\"data\", name=\"Temperature\", units=\"K\")]\n    wind: Ann[list[Dtype[float]], ts.Spec(category=\"data\", name=\"Wind speed\", units=\"m/s\")]\n    loc: Ann[str, ts.Spec(category=\"info\", name=\"Observed location\")]\n\n\nweather = Weather([273.15, 280.15], [5.0, 10.0], \"Tokyo\")\nspecs = ts.from_annotated(weather)\nprint(specs)\n```\n```\n      category              data            dtype               name           type units\ntemp      data  [273.15, 280.15]  \u003cclass 'float'\u003e        Temperature    list[float]     K\nwind      data       [5.0, 10.0]  \u003cclass 'float'\u003e         Wind speed    list[float]   m/s\nloc       info             Tokyo             \u003cNA\u003e  Observed location  \u003cclass 'str'\u003e  \u003cNA\u003e\n```\n\n### Handling Missing Values\n\nBy default, missing metadata values are filled with `pandas.NA`.\nYou can override this behavior and specify a custom fallback value by using the `default` parameter in `from_annotated`.\n\n```python\nspecs = ts.from_annotated(weather, default=None)\nprint(specs)\n```\n```\n      category              data            dtype               name           type units\ntemp      data  [273.15, 280.15]  \u003cclass 'float'\u003e        Temperature    list[float]     K\nwind      data       [5.0, 10.0]  \u003cclass 'float'\u003e         Wind speed    list[float]   m/s\nloc       info             Tokyo             None  Observed location  \u003cclass 'str'\u003e  None\n```\n\n### Handling Full Specification\n\nBy default, typespecs neatly merges nested metadata (e.g., `float` in `list[float]`) into a single parent row.\nIf you need to inspect the exact structural hierarchy of your annotations, set `merge=False` in `from_annotated`.\nThis unpacks the tree, distinguishing between the parent collection and its elements.\n\n```python\nspecs = ts.from_annotated(weather, merge=False)\nprint(specs)\n```\n```\n        category              data            dtype               name             type units\ntemp        data  [273.15, 280.15]             \u003cNA\u003e        Temperature      list[float]     K\ntemp/0      \u003cNA\u003e              \u003cNA\u003e  \u003cclass 'float'\u003e               \u003cNA\u003e  \u003cclass 'float'\u003e  \u003cNA\u003e\nwind        data       [5.0, 10.0]             \u003cNA\u003e         Wind speed      list[float]   m/s\nwind/0      \u003cNA\u003e              \u003cNA\u003e  \u003cclass 'float'\u003e               \u003cNA\u003e  \u003cclass 'float'\u003e  \u003cNA\u003e\nloc         info             Tokyo             \u003cNA\u003e  Observed location    \u003cclass 'str'\u003e  \u003cNA\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastropenguin%2Ftypespecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastropenguin%2Ftypespecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastropenguin%2Ftypespecs/lists"}