{"id":16346230,"url":"https://github.com/jorenham/unpy","last_synced_at":"2025-03-16T15:30:58.452Z","repository":{"id":256838870,"uuid":"856579506","full_name":"jorenham/unpy","owner":"jorenham","description":"Python stub backporter","archived":false,"fork":false,"pushed_at":"2024-10-05T02:52:59.000Z","size":448,"stargazers_count":4,"open_issues_count":29,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-14T13:37:03.190Z","etag":null,"topics":["code-generation","pep484","python","python-typing"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jorenham.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"jorenham"}},"created_at":"2024-09-12T20:23:59.000Z","updated_at":"2024-10-05T02:53:02.000Z","dependencies_parsed_at":"2024-09-15T18:59:18.847Z","dependency_job_id":"0c4dc384-9fcc-4d40-aba4-94d3979b1408","html_url":"https://github.com/jorenham/unpy","commit_stats":null,"previous_names":["jorenham/pythoff","jorenham/unpy"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorenham%2Funpy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorenham%2Funpy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorenham%2Funpy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jorenham%2Funpy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jorenham","download_url":"https://codeload.github.com/jorenham/unpy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221665203,"owners_count":16860194,"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":["code-generation","pep484","python","python-typing"],"created_at":"2024-10-11T00:34:49.710Z","updated_at":"2024-10-27T10:49:20.233Z","avatar_url":"https://github.com/jorenham.png","language":"Python","readme":"\u003ch1 align=\"center\"\u003eunpy\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ci\u003eUnified Python\u003c/i\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003eTranspiles \u003ccode\u003e.pyi\u003c/code\u003e stubs from Python 3.13 to 3.10\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://pypi.org/project/unpy/\"\u003e\n        \u003cimg\n            alt=\"unpy - PyPI\"\n            src=\"https://img.shields.io/pypi/v/unpy?style=flat\u0026color=olive\"\n        /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/jorenham/unpy\"\u003e\n        \u003cimg\n            alt=\"unpy - Python Versions\"\n            src=\"https://img.shields.io/pypi/pyversions/unpy?style=flat\"\n        /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/jorenham/unpy\"\u003e\n        \u003cimg\n            alt=\"unpy - license\"\n            src=\"https://img.shields.io/github/license/jorenham/unpy?style=flat\"\n        /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/jorenham/unpy/actions?query=workflow%3ACI\"\u003e\n        \u003cimg\n            alt=\"unpy - CI\"\n            src=\"https://github.com/jorenham/unpy/workflows/CI/badge.svg\"\n        /\u003e\n    \u003c/a\u003e\n    \u003c!-- TODO --\u003e\n    \u003ca href=\"https://github.com/pre-commit/pre-commit\"\u003e\n        \u003cimg\n            alt=\"unpy - pre-commit\"\n            src=\"https://img.shields.io/badge/pre--commit-enabled-teal?logo=pre-commit\"\n        /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/KotlinIsland/basedmypy\"\u003e\n        \u003cimg\n            alt=\"unpy - basedmypy\"\n            src=\"https://img.shields.io/badge/basedmypy-checked-fd9002\"\n        /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://detachhead.github.io/basedpyright\"\u003e\n        \u003cimg\n            alt=\"unpy - basedpyright\"\n            src=\"https://img.shields.io/badge/basedpyright-checked-42b983\"\n        /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/astral-sh/ruff\"\u003e\n        \u003cimg\n            alt=\"unpy - ruff\"\n            src=\"https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json\"\n        /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n\u003e [!IMPORTANT]\n\u003e This project is in the alpha stage:\n\u003e You probably shouldn't use it in production.\n\n## Installation\n\n```console\n$ pip install unpy\n```\n\n## Usage\n\n```console\n$ unpy --help\nUsage: unpy [OPTIONS] SOURCE [OUTPUT]\n\nArguments:\n  SOURCE    Path to the input .pyi file or '-' to read from stdin.  [required]\n  [OUTPUT]  Path to the output .pyi file. Defaults to stdout.\n\nOptions:\n  --version                       Show the version and exit\n  --diff                          Show the changes between the input and\n                                  output in unified diff format\n  --target [3.10|3.11|3.12|3.13]  The minimum Python version that should be\n                                  supported.  [default: 3.10]\n  --help                          Show this message and exit.\n```\n\n## Examples\n\nSome simple examples of Python 3.13 stubs that are backported to Python 3.10.\n\n### Imports\n\n```console\n$ unpy --target 3.10 --diff examples/imports.pyi\n```\n\n```diff\n+++ -\n@@ -1,6 +1,4 @@\n- from types import CapsuleType\n- from typing import override\n- from warnings import deprecated\n+ from typing_extensions import CapsuleType, deprecated, override\n\n  @deprecated(\"RTFM\")\n  class Spam:\n      __pyx_capi__: dict[str, CapsuleType]\n      @override\n      def __hash__(self, /) -\u003e int: ...\n```\n\nNote the alphabetical order of the generated imports.\n\n### Type Aliases\n\n```console\n$ unpy --target 3.10 --diff examples/type_aliases.pyi\n```\n\n```diff\n+++ -\n@@ -1,7 +1,15 @@\n  from collections.abc import Callable\n+ from typing import ParamSpec, TypeAlias, TypeVar\n+ from typing_extensions import TypeAliasType, TypeVarTuple, Unpack\n\n- type Binary = bytes | bytearray | memoryview\n- type Vector[R: float] = tuple[R, ...]\n- type tciD[V, K] = dict[K, V]\n- type Things[*Ts] = tuple[*Ts]\n- type Callback[**Tss] = Callable[Tss, None]\n+ _R = TypeVar(\"_R\", bound=float)\n+ _V = TypeVar(\"_V\")\n+ _K = TypeVar(\"_K\")\n+ _Ts = TypeVarTuple(\"_Ts\")\n+ _Tss = ParamSpec(\"_Tss\")\n+\n+ Binary: TypeAlias = bytes | bytearray | memoryview\n+ Vector: TypeAlias = tuple[_R, ...]\n+ tciD = TypeAliasType(\"tciD\", dict[_K, _V], type_params=(_V, _K))\n+ Things: TypeAlias = tuple[Unpack[_Ts]]\n+ Callback: TypeAlias = Callable[_Tss, None]\n```\n\nNote that `TypeAlias` cannot be used with `tciD` because the definition order of the\ntype parameters (at the left-hand side) does not match the order in which they are\naccessed (at the right-hand side), and the backported `TypeAliasType` must be used\ninstead.\n\n### Functions\n\n```console\n$ unpy --target 3.10 --diff examples/functions.pyi\n```\n\n```diff\n+++ -\n@@ -1,6 +1,11 @@\n+ _T = TypeVar(\"_T\")\n+ _S = TypeVar(\"_S\", str, bytes)\n+ _X = TypeVar(\"_X\")\n+ _Theta = ParamSpec(\"_Theta\")\n+ _Y = TypeVar(\"_Y\")\n  from collections.abc import Callable as Def\n- from typing import Concatenate as Concat\n+ from typing import Concatenate as Concat, ParamSpec, TypeVar\n\n- def noop[T](x: T, /) -\u003e T: ...\n- def concat[S: (str, bytes)](left: S, right: S) -\u003e S: ...\n- def curry[X, **Theta, Y](f: Def[Concat[X, Theta], Y], /) -\u003e Def[[X], Def[Theta, Y]]: ...\n+ def noop(x: _T, /) -\u003e _T: ...\n+ def concat(left: _S, right: _S) -\u003e _S: ...\n+ def curry(f: Def[Concat[_X, _Theta], _Y], /) -\u003e Def[[_X], Def[_Theta, _Y]]: ...\n```\n\n### Generic classes and protocols\n\n```console\n$ unpy --target 3.10 --diff examples/generics.pyi\n```\n\n```diff\n+++ -\n@@ -1,17 +1,25 @@\n- from typing import Protocol, overload\n+ from typing import Generic, Protocol, overload\n+ from typing_extensions import TypeVar\n+\n+ _T_contra = TypeVar(\"_T_contra\", contravariant=True)\n+ _T_co = TypeVar(\"_T_co\", covariant=True)\n+ _T = TypeVar(\"_T\", infer_variance=True)\n+ _D = TypeVar(\"_D\")\n+ _NameT = TypeVar(\"_NameT\", infer_variance=True, bound=str)\n+ _QualNameT = TypeVar(\"_QualNameT\", infer_variance=True, bound=str, default=_NameT)\n\n  class Boring: ...\n\n- class CanGetItem[T_contra, T_co](Protocol):\n-     def __getitem__(self, k: T_contra, /) -\u003e T_co: ...\n+ class CanGetItem(Protocol[_T_contra, _T_co]):\n+     def __getitem__(self, k: _T_contra, /) -\u003e _T_co: ...\n\n- class Stack[T]:\n-     def push(self, value: T, /) -\u003e None: ...\n+ class Stack(Generic[_T, _D]):\n+     def push(self, value: _T, /) -\u003e None: ...\n      @overload\n-     def pop(self, /) -\u003e T: ...\n+     def pop(self, /) -\u003e _T: ...\n      @overload\n-     def pop[D](self, default: D, /) -\u003e T | D: ...\n+     def pop(self, default: _D, /) -\u003e _T | _D: ...\n\n- class Named[NameT: str, QualNameT: str = NameT]:\n-     __name__: NameT\n-     __qualname__: QualNameT\n+ class Named(Generic[_NameT, _QualNameT]):\n+     __name__: _NameT\n+     __qualname__: _QualNameT\n```\n\nNote how `TypeVar` is (only) imported from `typing_extensions` here, which wasn't the\ncase in the previous example. This is a consequence of the `infer_variance` parameter,\nwhich has been added in Python 3.12.\n\n## Project goals\n\nHere's the alpha version of a prototype of a rough sketch of some initial ideas for the\npotential goals of `unpy`:\n\n1. Towards the past\n    - [x] Get frustrated while [stubbing scipy](https://github.com/jorenham/scipy-stubs)\n    - [x] Transpile Python 3.13 `.pyi` stubs to Python 3.10 stubs\n    - [ ] Package-level analysis and conversion\n    - [ ] Tooling for stub-only project integration\n    - [ ] Use this in [`scipy-stubs`](https://github.com/jorenham/scipy-stubs)\n    - [ ] Gradually introduce this into [`numpy`](https://github.com/numpy/numpy)\n2. Towards the future\n    - [ ] Beyond Python: $\\text{Unpy} \\supset \\text{Python}$\n    - [ ] Language support \u0026 tooling for *all* `.py` projects\n3. Towards each other\n    - [ ] Unified typechecking: Fast, reasonable, and language-agnostic\n\n## Features\n\n### Tooling\n\n- Target Python versions\n    - [x] `3.13`\n    - [x] `3.12`\n    - [x] `3.11`\n    - [x] `3.10`\n    - [ ] `3.9`\n- Language support\n    - [x] `.pyi`\n    - [ ] `.py`\n- Conversion\n    - [x] stdin =\u003e stdout\n    - [x] module =\u003e module\n    - [ ] package =\u003e package\n    - [ ] project =\u003e project (including the `pyproject.toml`)\n- Configuration\n    - [x] `--diff`: Unified diffs\n    - [x] `--target`: Target Python version, defaults to `3.10`\n    - [ ] Project-based config in `pyproject.toml` under `[tools.unpy]`\n    - [ ] ...\n- Integration\n    - [ ] File watcher\n    - [ ] Pre-commit\n    - [ ] LSP\n    - [ ] UV\n    - [ ] VSCode extension\n    - [ ] (based)mypy plugin\n    - [ ] Project build tools\n    - [ ] Configurable type-checker integration\n    - [ ] Configurable formatter integration, e.g. `ruff format`\n- Performance\n    - [ ] Limit conversion to changed files\n\n### Stub backporting\n\n- Python 3.13 =\u003e 3.12\n    - [PEP 742][PEP742]\n        - `typing.TypeIs` =\u003e `typing_extensions.TypeIs`\n    - [PEP 705][PEP705]\n        - `typing.ReadOnly` =\u003e `typing_extensions.ReadOnly`\n    - [PEP 702][PEP702]\n        - `warnings.deprecated` =\u003e `typing_extensions.deprecated`\n    - [PEP 696][PEP696]\n        - Backport [PEP 695][PEP695] type signatures with a default\n        - `typing.NoDefault` =\u003e `typing_extensions.NoDefault`\n    - Exceptions\n        - `asyncio.QueueShutDown` =\u003e `builtins.Exception`\n        - `pathlib.UnsupportedOperation` =\u003e `builtins.NotImplementedError`\n        - `queue.ShutDown` =\u003e `builtins.Exception`\n        - `re.PatternError` =\u003e `re.error`\n    - Typing\n        - `types.CapsuleType` =\u003e `typing_extensions.CapsuleType`\n        - `typing.{ClassVar,Final}` =\u003e `typing_extensions.{ClassVar,Final}` when\n        nested\n- Python 3.12 =\u003e 3.11\n    - [PEP 698][PEP698]\n        - `typing.override` =\u003e `typing_extensions.override`\n    - [PEP 695][PEP695]\n        - Backport `type _` aliases\n        - Backport generic functions\n        - Backport generic classes and protocols\n        - `typing.TypeAliasType` =\u003e `typing_extensions.TypeAliasType`\n    - [PEP 688][PEP688]\n        - `collections.abc.Buffer` =\u003e `typing_extensions.Buffer`\n        - `inspect.BufferFlags` =\u003e `int`\n- Python 3.11 =\u003e 3.10\n    - [PEP 681][PEP681]\n        - `typing.dataclass_transform` =\u003e `typing_extensions.dataclass_transform`\n    - [PEP 675][PEP675]\n        - `typing.LiteralString` =\u003e `typing_extensions.LiteralString`\n    - [PEP 673][PEP673]\n        - `typing.Self` =\u003e `typing_extensions.Self`\n    - [PEP 655][PEP655]\n        - `typing.[Not]Required` =\u003e `typing_extensions.[Not]Required`\n    - [PEP 654][PEP654]\n        - ~`builtins.BaseExceptionGroup`~\n        - ~`builtins.ExceptionGroup`~\n    - [PEP 646][PEP646]\n        - `typing.TypeVarTuple` =\u003e `typing_extensions.TypeVarTuple`\n        - `typing.Unpack` =\u003e `typing_extensions.Unpack`\n        - `*Ts` =\u003e `typing_extensions.Unpack[Ts]` with `Ts: TypeVarTuple`\n    - `asyncio`\n        - ~`asyncio.TaskGroup`~\n    - `enum`\n        - `enum.ReprEnum` =\u003e `enum.Enum`\n        - `enum.StrEnum` =\u003e `str \u0026 enum.Enum`\n    - `typing`\n        - `typing.Any` =\u003e `typing_extensions.Any` if subclassed (not recommended)\n- Generated `TypeVar`s\n    - [x] De-duplicate extracted typevar-likes with same name if equivalent\n    - [x] Prefix the names of extracted typevar-likes with `_`\n    - [ ] Rename incompatible typevar-likes with the same name (jorenham/unpy#86)\n\n### Simplification and refactoring\n\n- Generic type parameters\n    - [x] Convert `default=Any` with `bound=T` to `default=T`\n    - [x] Remove `bound=Any` and `bound=object`\n    - [ ] Infer variance of PEP 695 type parameters (jorenham/unpy#44)\n        - [ ] If never used, it's redundant (and bivariant) (jorenham/unpy#46)\n        - [x] If constraints are specified, it's `invariant`\n        - [x] If suffixed with `_co`/`_contra`, it's `covariant`/`contravariant`\n        - [ ] If used as public instance attribute, it's `invariant`\n        - [ ] If only used as return-type (excluding `__init__` and `__new__`), or for\n        read-only attributes, it's `covariant`\n        - [ ] If only used as parameter-type, it's `contravariant`\n        - [ ] Otherwise, assume it's `invariant`\n- Methods\n    - [ ] Default return types for specific \"special method\" (jorenham/unpy#55)\n    - [ ] Transform `self` method parameters to be positional-only\n- Typing operators\n    - [ ] `type[S] | type[T]` =\u003e `type[S | T]`\n    - [ ] Flatten \u0026 de-duplicate unions of literals\n    - [ ] Remove redundant union values, e.g. `bool | int` =\u003e `int`\n\n### Beyond Python\n\n- [ ] `@sealed` types (jorenham/unpy#42)\n- [ ] Unified type-ignore comments (jorenham/unpy#68)\n- [ ] Set-based `Literal` syntax (jorenham/unpy#76)\n- [ ] Reusable method signature definitions (jorenham/unpy#97, jorenham/unpy#98)\n- [ ] Type-mappings, a DRY alternative to `@overload`\n- [ ] Intersection types (as implemented in [basedmypy][BMP-ISEC])\n- [ ] Higher-kinded types (see python/typing#548)\n- [ ] Inline callable types (inspired by [PEP 677][PEP677])\n\n[PEP646]: https://peps.python.org/pep-0646/\n[PEP654]: https://peps.python.org/pep-0654/\n[PEP655]: https://peps.python.org/pep-0655/\n[PEP673]: https://peps.python.org/pep-0673/\n[PEP675]: https://peps.python.org/pep-0675/\n[PEP677]: https://peps.python.org/pep-0677/\n[PEP681]: https://peps.python.org/pep-0681/\n[PEP688]: https://peps.python.org/pep-0688/\n[PEP695]: https://peps.python.org/pep-0695/\n[PEP696]: https://peps.python.org/pep-0696/\n[PEP698]: https://peps.python.org/pep-0698/\n[PEP702]: https://peps.python.org/pep-0702/\n[PEP705]: https://peps.python.org/pep-0705/\n[PEP742]: https://peps.python.org/pep-0705/\n[BMP-ISEC]: https://github.com/KotlinIsland/basedmypy#intersection-types\n","funding_links":["https://github.com/sponsors/jorenham"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorenham%2Funpy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjorenham%2Funpy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjorenham%2Funpy/lists"}