From c236bd082aae458d4b43c86bdc86bd8699638000 Mon Sep 17 00:00:00 2001 From: Evan Hubinger Date: Sun, 28 May 2023 18:11:01 -0700 Subject: [PATCH] Add type param constraint support Refs #757. --- DOCS.md | 6 +- __coconut__/__init__.pyi | 60 ++++--- _coconut/__init__.pyi | 154 +++++++----------- coconut/compiler/compiler.py | 68 +++++--- coconut/compiler/grammar.py | 4 +- coconut/compiler/header.py | 2 +- coconut/compiler/matching.py | 6 +- coconut/compiler/util.py | 52 +++--- coconut/constants.py | 104 ++++++------ coconut/root.py | 2 +- .../tests/src/cocotest/agnostic/suite.coco | 1 + coconut/tests/src/cocotest/agnostic/util.coco | 12 +- coconut/tests/src/extras.coco | 6 + 13 files changed, 244 insertions(+), 233 deletions(-) diff --git a/DOCS.md b/DOCS.md index e94109ce5..5eabc019c 100644 --- a/DOCS.md +++ b/DOCS.md @@ -427,7 +427,7 @@ To distribute your code with checkable type annotations, you'll need to include To explicitly annotate your code with types to be checked, Coconut supports: * [Python 3 function type annotations](https://www.python.org/dev/peps/pep-0484/), * [Python 3.6 variable type annotations](https://www.python.org/dev/peps/pep-0526/), -* [PEP 695 type parameter syntax](#type-parameter-syntax) for easily adding type parameters to classes, functions, [`data` types](#data), and type aliases, +* [Python 3.12 type parameter syntax](#type-parameter-syntax) for easily adding type parameters to classes, functions, [`data` types](#data), and type aliases, * Coconut's own [enhanced type annotation syntax](#enhanced-type-annotation), and * Coconut's [protocol intersection operator](#protocol-intersection). @@ -2579,7 +2579,7 @@ _Can't be done without a long series of checks in place of the destructuring ass ### Type Parameter Syntax -Coconut fully supports [PEP 695](https://peps.python.org/pep-0695/) type parameter syntax (with the caveat that all type variables are invariant rather than inferred). +Coconut fully supports [Python 3.12 PEP 695](https://peps.python.org/pep-0695/) type parameter syntax on all Python versions. That includes type parameters for classes, [`data` types](#data), and [all types of function definition](#function-definition). For different types of function definition, the type parameters always come in brackets right after the function name. Coconut's [enhanced type annotation syntax](#enhanced-type-annotation) is supported for all type parameter bounds. @@ -2587,6 +2587,8 @@ _Warning: until `mypy` adds support for `infer_variance=True` in `TypeVar`, `Typ Additionally, Coconut supports the alternative bounds syntax of `type NewType[T <: bound] = ...` rather than `type NewType[T: bound] = ...`, to make it more clear that it is an upper bound rather than a type. In `--strict` mode, `<:` is required over `:` for all type parameter bounds. _DEPRECATED: `<=` can also be used as an alternative to `<:`._ +Note that the `<:` syntax should only be used for [type bounds](https://peps.python.org/pep-0695/#upper-bound-specification), not [type constraints](https://peps.python.org/pep-0695/#constrained-type-specification)—for type constraints, Coconut style prefers the vanilla Python `:` syntax, which helps to disambiguate between the two cases, as they are functionally different but otherwise hard to tell apart at a glance. This is enforced in `--strict` mode. + _Note that, by default, all type declarations are wrapped in strings to enable forward references and improve runtime performance. If you don't want that—e.g. because you want to use type annotations at runtime—simply pass the `--no-wrap-types` flag._ ##### PEP 695 Docs diff --git a/__coconut__/__init__.pyi b/__coconut__/__init__.pyi index 75c660612..b85237ebc 100644 --- a/__coconut__/__init__.pyi +++ b/__coconut__/__init__.pyi @@ -8,13 +8,13 @@ License: Apache 2.0 Description: MyPy stub file for __coconut__.py. """ -import sys -import typing as _t - # ----------------------------------------------------------------------------------------------------------------------- # TYPE VARS: # ----------------------------------------------------------------------------------------------------------------------- +import sys +import typing as _t + _Callable = _t.Callable[..., _t.Any] _Iterable = _t.Iterable[_t.Any] _Tuple = _t.Tuple[_t.Any, ...] @@ -55,21 +55,14 @@ _P = _t.ParamSpec("_P") class _SupportsIndex(_t.Protocol): def __index__(self) -> int: ... - # ----------------------------------------------------------------------------------------------------------------------- # IMPORTS: # ----------------------------------------------------------------------------------------------------------------------- -if sys.version_info >= (3, 11): - from typing import dataclass_transform as _dataclass_transform +if sys.version_info >= (3,): + import builtins as _builtins else: - try: - from typing_extensions import dataclass_transform as _dataclass_transform - except ImportError: - dataclass_transform = ... - -import _coconut as __coconut # we mock _coconut as a package since mypy doesn't handle namespace classes very well -_coconut = __coconut + import __builtin__ as _builtins if sys.version_info >= (3, 2): from functools import lru_cache as _lru_cache @@ -81,13 +74,24 @@ if sys.version_info >= (3, 7): from dataclasses import dataclass as _dataclass else: @_dataclass_transform() - def _dataclass(cls: t_coype[_T], **kwargs: _t.Any) -> type[_T]: ... + def _dataclass(cls: type[_T], **kwargs: _t.Any) -> type[_T]: ... + +if sys.version_info >= (3, 11): + from typing import dataclass_transform as _dataclass_transform +else: + try: + from typing_extensions import dataclass_transform as _dataclass_transform + except ImportError: + dataclass_transform = ... try: from typing_extensions import deprecated as _deprecated # type: ignore except ImportError: def _deprecated(message: _t.Text) -> _t.Callable[[_T], _T]: ... # type: ignore +import _coconut as __coconut # we mock _coconut as a package since mypy doesn't handle namespace classes very well +_coconut = __coconut + # ----------------------------------------------------------------------------------------------------------------------- # STUB: @@ -153,18 +157,18 @@ py_repr = repr py_breakpoint = breakpoint # all py_ functions, but not py_ types, go here -chr = chr -hex = hex -input = input -map = map -oct = oct -open = open -print = print -range = range -zip = zip -filter = filter -reversed = reversed -enumerate = enumerate +chr = _builtins.chr +hex = _builtins.hex +input = _builtins.input +map = _builtins.map +oct = _builtins.oct +open = _builtins.open +print = _builtins.print +range = _builtins.range +zip = _builtins.zip +filter = _builtins.filter +reversed = _builtins.reversed +enumerate = _builtins.enumerate _coconut_py_str = py_str @@ -435,6 +439,9 @@ def recursive_iterator(func: _T_iter_func) -> _T_iter_func: return func +# if sys.version_info >= (3, 12): +# from typing import override +# else: try: from typing_extensions import override as _override # type: ignore override = _override @@ -442,6 +449,7 @@ except ImportError: def override(func: _Tfunc) -> _Tfunc: return func + def _coconut_call_set_names(cls: object) -> None: ... diff --git a/_coconut/__init__.pyi b/_coconut/__init__.pyi index 9788e9083..38433b7ac 100644 --- a/_coconut/__init__.pyi +++ b/_coconut/__init__.pyi @@ -31,6 +31,11 @@ import multiprocessing as _multiprocessing import pickle as _pickle from multiprocessing import dummy as _multiprocessing_dummy +if sys.version_info >= (3,): + import builtins as _builtins +else: + import __builtin__ as _builtins + if sys.version_info >= (3,): import copyreg as _copyreg else: @@ -68,41 +73,6 @@ else: # ----------------------------------------------------------------------------------------------------------------------- typing = _t - -from typing_extensions import TypeVar -typing.TypeVar = TypeVar # type: ignore - -if sys.version_info < (3, 8): - try: - from typing_extensions import Protocol - except ImportError: - Protocol = ... # type: ignore - typing.Protocol = Protocol # type: ignore - -if sys.version_info < (3, 10): - try: - from typing_extensions import TypeAlias, ParamSpec, Concatenate - except ImportError: - TypeAlias = ... # type: ignore - ParamSpec = ... # type: ignore - Concatenate = ... # type: ignore - typing.TypeAlias = TypeAlias # type: ignore - typing.ParamSpec = ParamSpec # type: ignore - typing.Concatenate = Concatenate # type: ignore - -if sys.version_info < (3, 11): - try: - from typing_extensions import TypeVarTuple, Unpack - except ImportError: - TypeVarTuple = ... # type: ignore - Unpack = ... # type: ignore - typing.TypeVarTuple = TypeVarTuple # type: ignore - typing.Unpack = Unpack # type: ignore - -# ----------------------------------------------------------------------------------------------------------------------- -# STUB: -# ----------------------------------------------------------------------------------------------------------------------- - collections = _collections copy = _copy functools = _functools @@ -141,62 +111,62 @@ tee_type: _t.Any = ... reiterables: _t.Any = ... fmappables: _t.Any = ... -Ellipsis = Ellipsis -NotImplemented = NotImplemented -NotImplementedError = NotImplementedError -Exception = Exception -AttributeError = AttributeError -ImportError = ImportError -IndexError = IndexError -KeyError = KeyError -NameError = NameError -TypeError = TypeError -ValueError = ValueError -StopIteration = StopIteration -RuntimeError = RuntimeError -callable = callable -classmethod = classmethod -complex = complex -all = all -any = any -bool = bool -bytes = bytes -dict = dict -enumerate = enumerate -filter = filter -float = float -frozenset = frozenset -getattr = getattr -hasattr = hasattr -hash = hash -id = id -int = int -isinstance = isinstance -issubclass = issubclass -iter = iter +Ellipsis = _builtins.Ellipsis +NotImplemented = _builtins.NotImplemented +NotImplementedError = _builtins.NotImplementedError +Exception = _builtins.Exception +AttributeError = _builtins.AttributeError +ImportError = _builtins.ImportError +IndexError = _builtins.IndexError +KeyError = _builtins.KeyError +NameError = _builtins.NameError +TypeError = _builtins.TypeError +ValueError = _builtins.ValueError +StopIteration = _builtins.StopIteration +RuntimeError = _builtins.RuntimeError +callable = _builtins.callable +classmethod = _builtins.classmethod +complex = _builtins.complex +all = _builtins.all +any = _builtins.any +bool = _builtins.bool +bytes = _builtins.bytes +dict = _builtins.dict +enumerate = _builtins.enumerate +filter = _builtins.filter +float = _builtins.float +frozenset = _builtins.frozenset +getattr = _builtins.getattr +hasattr = _builtins.hasattr +hash = _builtins.hash +id = _builtins.id +int = _builtins.int +isinstance = _builtins.isinstance +issubclass = _builtins.issubclass +iter = _builtins.iter len: _t.Callable[..., int] = ... # pattern-matching needs an untyped _coconut.len to avoid type errors -list = list -locals = locals -globals = globals -map = map -min = min -max = max -next = next -object = object -print = print -property = property -range = range -reversed = reversed -set = set -setattr = setattr -slice = slice -str = str -sum = sum -super = super -tuple = tuple -type = type -zip = zip -vars = vars -repr = repr +list = _builtins.list +locals = _builtins.locals +globals = _builtins.globals +map = _builtins.map +min = _builtins.min +max = _builtins.max +next = _builtins.next +object = _builtins.object +print = _builtins.print +property = _builtins.property +range = _builtins.range +reversed = _builtins.reversed +set = _builtins.set +setattr = _builtins.setattr +slice = _builtins.slice +str = _builtins.str +sum = _builtins.sum +super = _builtins.super +tuple = _builtins.tuple +type = _builtins.type +zip = _builtins.zip +vars = _builtins.vars +repr = _builtins.repr if sys.version_info >= (3,): - bytearray = bytearray + bytearray = _builtins.bytearray diff --git a/coconut/compiler/compiler.py b/coconut/compiler/compiler.py index faa646acc..bc5a800b8 100644 --- a/coconut/compiler/compiler.py +++ b/coconut/compiler/compiler.py @@ -3609,32 +3609,22 @@ def funcname_typeparams_handle(self, tokens): def type_param_handle(self, original, loc, tokens): """Compile a type param into an assignment.""" - bounds = "" - kwargs = "" + args = "" + bound_op = None + bound_op_type = "" if "TypeVar" in tokens: TypeVarFunc = "TypeVar" + bound_op_type = "bound" if len(tokens) == 2: name_loc, name = tokens else: name_loc, name, bound_op, bound = tokens - if bound_op == "<=": - self.strict_err_or_warn( - "use of " + repr(bound_op) + " as a type parameter bound declaration operator is deprecated (Coconut style is to use '<:' operator)", - original, - loc, - ) - elif bound_op == ":": - self.strict_err( - "found use of " + repr(bound_op) + " as a type parameter bound declaration operator (Coconut style is to use '<:' operator)", - original, - loc, - ) - else: - self.internal_assert(bound_op == "<:", original, loc, "invalid type_param bound_op", bound_op) - bounds = ", bound=" + self.wrap_typedef(bound, for_py_typedef=False) - # uncomment this line whenever mypy adds support for infer_variance in TypeVar - # (and remove the warning about it in the DOCS) - # kwargs = ", infer_variance=True" + args = ", bound=" + self.wrap_typedef(bound, for_py_typedef=False) + elif "TypeVar constraint" in tokens: + TypeVarFunc = "TypeVar" + bound_op_type = "constraint" + name_loc, name, bound_op, constraints = tokens + args = ", " + ", ".join(self.wrap_typedef(c, for_py_typedef=False) for c in constraints) elif "TypeVarTuple" in tokens: TypeVarFunc = "TypeVarTuple" name_loc, name = tokens @@ -3644,6 +3634,27 @@ def type_param_handle(self, original, loc, tokens): else: raise CoconutInternalException("invalid type_param tokens", tokens) + kwargs = "" + if bound_op is not None: + self.internal_assert(bound_op_type in ("bound", "constraint"), original, loc, "invalid type_param bound_op", bound_op) + # # uncomment this line whenever mypy adds support for infer_variance in TypeVar + # # (and remove the warning about it in the DOCS) + # kwargs = ", infer_variance=True" + if bound_op == "<=": + self.strict_err_or_warn( + "use of " + repr(bound_op) + " as a type parameter " + bound_op_type + " declaration operator is deprecated (Coconut style is to use '<:' for bounds and ':' for constaints)", + original, + loc, + ) + else: + self.internal_assert(bound_op in (":", "<:"), original, loc, "invalid type_param bound_op", bound_op) + if bound_op_type == "bound" and bound_op != "<:" or bound_op_type == "constraint" and bound_op != ":": + self.strict_err( + "found use of " + repr(bound_op) + " as a type parameter " + bound_op_type + " declaration operator (Coconut style is to use '<:' for bounds and ':' for constaints)", + original, + loc, + ) + name_loc = int(name_loc) internal_assert(name_loc == loc if TypeVarFunc == "TypeVar" else name_loc >= loc, "invalid name location for " + TypeVarFunc, (name_loc, loc, tokens)) @@ -3661,10 +3672,10 @@ def type_param_handle(self, original, loc, tokens): typevar_info["typevar_locs"][name] = name_loc name = temp_name - return '{name} = _coconut.typing.{TypeVarFunc}("{name}"{bounds}{kwargs})\n'.format( + return '{name} = _coconut.typing.{TypeVarFunc}("{name}"{args}{kwargs})\n'.format( name=name, TypeVarFunc=TypeVarFunc, - bounds=bounds, + args=args, kwargs=kwargs, ) @@ -3707,11 +3718,14 @@ def type_alias_stmt_handle(self, tokens): paramdefs = () else: name, paramdefs, typedef = tokens - return "".join(paramdefs) + self.typed_assign_stmt_handle([ - name, - "_coconut.typing.TypeAlias", - self.wrap_typedef(typedef, for_py_typedef=False), - ]) + if self.target_info >= (3, 12): + return "type " + name + " = " + self.wrap_typedef(typedef, for_py_typedef=True) + else: + return "".join(paramdefs) + self.typed_assign_stmt_handle([ + name, + "_coconut.typing.TypeAlias", + self.wrap_typedef(typedef, for_py_typedef=False), + ]) def with_stmt_handle(self, tokens): """Process with statements.""" diff --git a/coconut/compiler/grammar.py b/coconut/compiler/grammar.py index 4cac82243..e5943da66 100644 --- a/coconut/compiler/grammar.py +++ b/coconut/compiler/grammar.py @@ -1356,8 +1356,10 @@ class Grammar(object): type_param = Forward() type_param_bound_op = lt_colon | colon | le type_var_name = stores_loc_item + setname + type_param_constraint = lparen.suppress() + Group(tokenlist(typedef_test, comma, require_sep=True)) + rparen.suppress() type_param_ref = ( - (type_var_name + Optional(type_param_bound_op + typedef_test))("TypeVar") + (type_var_name + type_param_bound_op + type_param_constraint)("TypeVar constraint") + | (type_var_name + Optional(type_param_bound_op + typedef_test))("TypeVar") | (star.suppress() + type_var_name)("TypeVarTuple") | (dubstar.suppress() + type_var_name)("ParamSpec") ) diff --git a/coconut/compiler/header.py b/coconut/compiler/header.py index 61eb1897e..39ff27fca 100644 --- a/coconut/compiler/header.py +++ b/coconut/compiler/header.py @@ -793,7 +793,7 @@ def getheader(which, use_hash, target, no_tco, strict, no_wrap): if which == "sys": return header + '''from coconut.__coconut__ import * from coconut.__coconut__ import {underscore_imports} -'''.format(**format_dict) +'''.format(**format_dict) + section("Compiled Coconut") # __coconut__, code, file diff --git a/coconut/compiler/matching.py b/coconut/compiler/matching.py index 2bc2e5a8d..96765f91a 100644 --- a/coconut/compiler/matching.py +++ b/coconut/compiler/matching.py @@ -1064,7 +1064,7 @@ def match_class(self, tokens, item): match_args_var = other_cls_matcher.get_temp_var() other_cls_matcher.add_def( handle_indentation(""" -{match_args_var} = _coconut.getattr({cls_name}, '__match_args__', ()) +{match_args_var} = _coconut.getattr({cls_name}, '__match_args__', ()) {type_any} {type_ignore} if not _coconut.isinstance({match_args_var}, _coconut.tuple): raise _coconut.TypeError("{cls_name}.__match_args__ must be a tuple") if _coconut.len({match_args_var}) < {num_pos_matches}: @@ -1073,6 +1073,8 @@ def match_class(self, tokens, item): cls_name=cls_name, match_args_var=match_args_var, num_pos_matches=len(pos_matches), + type_any=self.comp.wrap_comment(" type: _coconut.typing.Any"), + type_ignore=self.comp.type_ignore_comment(), ), ) with other_cls_matcher.down_a_level(): @@ -1161,7 +1163,7 @@ def match_data_or_class(self, tokens, item): self.add_def( handle_indentation( """ -{is_data_result_var} = _coconut.getattr({cls_name}, "{is_data_var}", False) or _coconut.isinstance({cls_name}, _coconut.tuple) and _coconut.all(_coconut.getattr(_coconut_x, "{is_data_var}", False) for _coconut_x in {cls_name}){type_ignore} +{is_data_result_var} = _coconut.getattr({cls_name}, "{is_data_var}", False) or _coconut.isinstance({cls_name}, _coconut.tuple) and _coconut.all(_coconut.getattr(_coconut_x, "{is_data_var}", False) for _coconut_x in {cls_name}) {type_ignore} """, ).format( is_data_result_var=is_data_result_var, diff --git a/coconut/compiler/util.py b/coconut/compiler/util.py index dc6ebe84b..bebec4a09 100644 --- a/coconut/compiler/util.py +++ b/coconut/compiler/util.py @@ -703,24 +703,6 @@ def maybeparens(lparen, item, rparen, prefer_parens=False): return item | lparen.suppress() + item + rparen.suppress() -@memoize() -def tokenlist(item, sep, suppress=True, allow_trailing=True, at_least_two=False, require_sep=False): - """Create a list of tokens matching the item.""" - if suppress: - sep = sep.suppress() - if not require_sep: - out = item + (OneOrMore if at_least_two else ZeroOrMore)(sep + item) - if allow_trailing: - out += Optional(sep) - elif not allow_trailing: - out = item + OneOrMore(sep + item) - elif at_least_two: - out = item + OneOrMore(sep + item) + Optional(sep) - else: - out = OneOrMore(item + sep) + Optional(item) - return out - - def interleaved_tokenlist(required_item, other_item, sep, allow_trailing=False, at_least_two=False): """Create a grammar to match interleaved required_items and other_items, where required_item must show up at least once.""" @@ -751,6 +733,30 @@ def interleaved_tokenlist(required_item, other_item, sep, allow_trailing=False, return out +@memoize() +def tokenlist(item, sep, suppress=True, allow_trailing=True, at_least_two=False, require_sep=False, suppress_trailing=False): + """Create a list of tokens matching the item.""" + if suppress: + sep = sep.suppress() + if suppress_trailing: + trailing_sep = sep.suppress() + else: + trailing_sep = sep + if not require_sep: + out = item + (OneOrMore if at_least_two else ZeroOrMore)(sep + item) + if allow_trailing: + out += Optional(trailing_sep) + elif not allow_trailing: + out = item + OneOrMore(sep + item) + elif at_least_two: + out = item + OneOrMore(sep + item) + Optional(trailing_sep) + elif suppress_trailing: + out = item + OneOrMore(sep + item) + Optional(trailing_sep) | item + trailing_sep + else: + out = OneOrMore(item + sep) + Optional(item) + return out + + def add_list_spacing(tokens): """Parse action to add spacing after seps but not elsewhere.""" out = [] @@ -765,21 +771,19 @@ def add_list_spacing(tokens): add_list_spacing.ignore_one_token = True -def itemlist(item, sep, suppress_trailing=True): +def itemlist(item, sep, suppress_trailing=True, **kwargs): """Create a list of items separated by seps with comma-like spacing added. A trailing sep is allowed.""" return attach( - item - + ZeroOrMore(sep + item) - + Optional(sep.suppress() if suppress_trailing else sep), + tokenlist(item, sep, suppress=False, suppress_trailing=suppress_trailing, **kwargs), add_list_spacing, ) -def exprlist(expr, op): +def exprlist(expr, op, **kwargs): """Create a list of exprs separated by ops with plus-like spacing added. No trailing op is allowed.""" - return addspace(expr + ZeroOrMore(op + expr)) + return addspace(tokenlist(expr, op, suppress=False, allow_trailing=False, **kwargs)) def stores_loc_action(loc, tokens): diff --git a/coconut/constants.py b/coconut/constants.py index adbfa2e9f..2b397f4a7 100644 --- a/coconut/constants.py +++ b/coconut/constants.py @@ -437,58 +437,58 @@ def get_bool_env_var(env_var, default=False): "sys.get_asyncgen_hooks": ("async_generator./get_asyncgen_hooks", (3, 6)), "sys.set_asyncgen_hooks": ("async_generator./set_asyncgen_hooks", (3, 6)), - # # typing_extensions (not needed since _coconut.typing has them - # # and mypy is happy to accept that they always live in typing) - # "typing.AsyncContextManager": ("typing_extensions./AsyncContextManager", (3, 6)), - # "typing.AsyncGenerator": ("typing_extensions./AsyncGenerator", (3, 6)), - # "typing.AsyncIterable": ("typing_extensions./AsyncIterable", (3, 6)), - # "typing.AsyncIterator": ("typing_extensions./AsyncIterator", (3, 6)), - # "typing.Awaitable": ("typing_extensions./Awaitable", (3, 6)), - # "typing.ChainMap": ("typing_extensions./ChainMap", (3, 6)), - # "typing.ClassVar": ("typing_extensions./ClassVar", (3, 6)), - # "typing.ContextManager": ("typing_extensions./ContextManager", (3, 6)), - # "typing.Coroutine": ("typing_extensions./Coroutine", (3, 6)), - # "typing.Counter": ("typing_extensions./Counter", (3, 6)), - # "typing.DefaultDict": ("typing_extensions./DefaultDict", (3, 6)), - # "typing.Deque": ("typing_extensions./Deque", (3, 6)), - # "typing.NamedTuple": ("typing_extensions./NamedTuple", (3, 6)), - # "typing.NewType": ("typing_extensions./NewType", (3, 6)), - # "typing.NoReturn": ("typing_extensions./NoReturn", (3, 6)), - # "typing.overload": ("typing_extensions./overload", (3, 6)), - # "typing.Text": ("typing_extensions./Text", (3, 6)), - # "typing.Type": ("typing_extensions./Type", (3, 6)), - # "typing.TYPE_CHECKING": ("typing_extensions./TYPE_CHECKING", (3, 6)), - # "typing.get_type_hints": ("typing_extensions./get_type_hints", (3, 6)), - # "typing.OrderedDict": ("typing_extensions./OrderedDict", (3, 7)), - # "typing.final": ("typing_extensions./final", (3, 8)), - # "typing.Final": ("typing_extensions./Final", (3, 8)), - # "typing.Literal": ("typing_extensions./Literal", (3, 8)), - # "typing.Protocol": ("typing_extensions./Protocol", (3, 8)), - # "typing.runtime_checkable": ("typing_extensions./runtime_checkable", (3, 8)), - # "typing.TypedDict": ("typing_extensions./TypedDict", (3, 8)), - # "typing.get_origin": ("typing_extensions./get_origin", (3, 8)), - # "typing.get_args": ("typing_extensions./get_args", (3, 8)), - # "typing.Annotated": ("typing_extensions./Annotated", (3, 9)), - # "typing.Concatenate": ("typing_extensions./Concatenate", (3, 10)), - # "typing.ParamSpec": ("typing_extensions./ParamSpec", (3, 10)), - # "typing.ParamSpecArgs": ("typing_extensions./ParamSpecArgs", (3, 10)), - # "typing.ParamSpecKwargs": ("typing_extensions./ParamSpecKwargs", (3, 10)), - # "typing.TypeAlias": ("typing_extensions./TypeAlias", (3, 10)), - # "typing.TypeGuard": ("typing_extensions./TypeGuard", (3, 10)), - # "typing.is_typeddict": ("typing_extensions./is_typeddict", (3, 10)), - # "typing.assert_never": ("typing_extensions./assert_never", (3, 11)), - # "typing.assert_type": ("typing_extensions./assert_type", (3, 11)), - # "typing.clear_overloads": ("typing_extensions./clear_overloads", (3, 11)), - # "typing.dataclass_transform": ("typing_extensions./dataclass_transform", (3, 11)), - # "typing.get_overloads": ("typing_extensions./get_overloads", (3, 11)), - # "typing.LiteralString": ("typing_extensions./LiteralString", (3, 11)), - # "typing.Never": ("typing_extensions./Never", (3, 11)), - # "typing.NotRequired": ("typing_extensions./NotRequired", (3, 11)), - # "typing.reveal_type": ("typing_extensions./reveal_type", (3, 11)), - # "typing.Required": ("typing_extensions./Required", (3, 11)), - # "typing.Self": ("typing_extensions./Self", (3, 11)), - # "typing.TypeVarTuple": ("typing_extensions./TypeVarTuple", (3, 11)), - # "typing.Unpack": ("typing_extensions./Unpack", (3, 11)), + # typing_extensions (even though we have special support for getting + # these from typing, we need to do this for the sake of type checkers) + "typing.AsyncContextManager": ("typing_extensions./AsyncContextManager", (3, 6)), + "typing.AsyncGenerator": ("typing_extensions./AsyncGenerator", (3, 6)), + "typing.AsyncIterable": ("typing_extensions./AsyncIterable", (3, 6)), + "typing.AsyncIterator": ("typing_extensions./AsyncIterator", (3, 6)), + "typing.Awaitable": ("typing_extensions./Awaitable", (3, 6)), + "typing.ChainMap": ("typing_extensions./ChainMap", (3, 6)), + "typing.ClassVar": ("typing_extensions./ClassVar", (3, 6)), + "typing.ContextManager": ("typing_extensions./ContextManager", (3, 6)), + "typing.Coroutine": ("typing_extensions./Coroutine", (3, 6)), + "typing.Counter": ("typing_extensions./Counter", (3, 6)), + "typing.DefaultDict": ("typing_extensions./DefaultDict", (3, 6)), + "typing.Deque": ("typing_extensions./Deque", (3, 6)), + "typing.NamedTuple": ("typing_extensions./NamedTuple", (3, 6)), + "typing.NewType": ("typing_extensions./NewType", (3, 6)), + "typing.NoReturn": ("typing_extensions./NoReturn", (3, 6)), + "typing.overload": ("typing_extensions./overload", (3, 6)), + "typing.Text": ("typing_extensions./Text", (3, 6)), + "typing.Type": ("typing_extensions./Type", (3, 6)), + "typing.TYPE_CHECKING": ("typing_extensions./TYPE_CHECKING", (3, 6)), + "typing.get_type_hints": ("typing_extensions./get_type_hints", (3, 6)), + "typing.OrderedDict": ("typing_extensions./OrderedDict", (3, 7)), + "typing.final": ("typing_extensions./final", (3, 8)), + "typing.Final": ("typing_extensions./Final", (3, 8)), + "typing.Literal": ("typing_extensions./Literal", (3, 8)), + "typing.Protocol": ("typing_extensions./Protocol", (3, 8)), + "typing.runtime_checkable": ("typing_extensions./runtime_checkable", (3, 8)), + "typing.TypedDict": ("typing_extensions./TypedDict", (3, 8)), + "typing.get_origin": ("typing_extensions./get_origin", (3, 8)), + "typing.get_args": ("typing_extensions./get_args", (3, 8)), + "typing.Annotated": ("typing_extensions./Annotated", (3, 9)), + "typing.Concatenate": ("typing_extensions./Concatenate", (3, 10)), + "typing.ParamSpec": ("typing_extensions./ParamSpec", (3, 10)), + "typing.ParamSpecArgs": ("typing_extensions./ParamSpecArgs", (3, 10)), + "typing.ParamSpecKwargs": ("typing_extensions./ParamSpecKwargs", (3, 10)), + "typing.TypeAlias": ("typing_extensions./TypeAlias", (3, 10)), + "typing.TypeGuard": ("typing_extensions./TypeGuard", (3, 10)), + "typing.is_typeddict": ("typing_extensions./is_typeddict", (3, 10)), + "typing.assert_never": ("typing_extensions./assert_never", (3, 11)), + "typing.assert_type": ("typing_extensions./assert_type", (3, 11)), + "typing.clear_overloads": ("typing_extensions./clear_overloads", (3, 11)), + "typing.dataclass_transform": ("typing_extensions./dataclass_transform", (3, 11)), + "typing.get_overloads": ("typing_extensions./get_overloads", (3, 11)), + "typing.LiteralString": ("typing_extensions./LiteralString", (3, 11)), + "typing.Never": ("typing_extensions./Never", (3, 11)), + "typing.NotRequired": ("typing_extensions./NotRequired", (3, 11)), + "typing.reveal_type": ("typing_extensions./reveal_type", (3, 11)), + "typing.Required": ("typing_extensions./Required", (3, 11)), + "typing.Self": ("typing_extensions./Self", (3, 11)), + "typing.TypeVarTuple": ("typing_extensions./TypeVarTuple", (3, 11)), + "typing.Unpack": ("typing_extensions./Unpack", (3, 11)), } import_existing = { diff --git a/coconut/root.py b/coconut/root.py index 41ef276e0..46ef915e3 100644 --- a/coconut/root.py +++ b/coconut/root.py @@ -26,7 +26,7 @@ VERSION = "3.0.1" VERSION_NAME = None # False for release, int >= 1 for develop -DEVELOP = 13 +DEVELOP = 14 ALPHA = False # for pre releases rather than post releases assert DEVELOP is False or DEVELOP >= 1, "DEVELOP must be False or an int >= 1" diff --git a/coconut/tests/src/cocotest/agnostic/suite.coco b/coconut/tests/src/cocotest/agnostic/suite.coco index cb4f2b6c1..666fb773f 100644 --- a/coconut/tests/src/cocotest/agnostic/suite.coco +++ b/coconut/tests/src/cocotest/agnostic/suite.coco @@ -1052,6 +1052,7 @@ forward 2""") == 900 assert ret_args_kwargs(...=really_long_var) == ((), {"really_long_var": 10}) == ret_args_kwargs$(...=really_long_var)() assert ret_args_kwargs(123, ...=really_long_var, abc="abc") == ((123,), {"really_long_var": 10, "abc": "abc"}) == ret_args_kwargs$(123, ...=really_long_var, abc="abc")() assert "Coconut version of typing" in typing.__doc__ + numlist: NumList = [1, 2.3, 5] # must come at end assert fibs_calls[0] == 1 diff --git a/coconut/tests/src/cocotest/agnostic/util.coco b/coconut/tests/src/cocotest/agnostic/util.coco index eee8c2de5..38cbadc26 100644 --- a/coconut/tests/src/cocotest/agnostic/util.coco +++ b/coconut/tests/src/cocotest/agnostic/util.coco @@ -243,6 +243,8 @@ if sys.version_info >= (3, 5) or TYPE_CHECKING: type TextMap[T <: typing.Text, U] = typing.Mapping[T, U] + type NumList[T : (int, float)] = typing.List[T] + class HasT: T = 1 @@ -297,7 +299,7 @@ def qsort4(l: int[]) -> int[]: return None # type: ignore def qsort5(l: int$[]) -> int$[]: """Iterator Match Quick Sort.""" - match (head,) :: tail in l: # type: ignore + match (head,) :: tail in l: tail, tail_ = tee(tail) return (qsort5((x for x in tail if x <= head)) :: (head,) # The pivot is a tuple @@ -306,8 +308,8 @@ def qsort5(l: int$[]) -> int$[]: else: return iter(()) def qsort6(l: int$[]) -> int$[]: - match [head] :: tail in l: # type: ignore - tail = reiterable(tail) # type: ignore + match [head] :: tail in l: + tail = reiterable(tail) yield from ( qsort6(x for x in tail if x <= head) :: (head,) @@ -619,11 +621,11 @@ def factorial5(value): return None raise TypeError() -match def fact(n) = fact(n, 1) # type: ignore +match def fact(n) = fact(n, 1) match addpattern def fact(0, acc) = acc # type: ignore addpattern match def fact(n, acc) = fact(n-1, acc*n) # type: ignore -addpattern def factorial(0, acc=1) = acc # type: ignore +addpattern def factorial(0, acc=1) = acc addpattern def factorial(int() as n, acc=1 if n > 0) = # type: ignore """this is a docstring""" factorial(n-1, acc*n) diff --git a/coconut/tests/src/extras.coco b/coconut/tests/src/extras.coco index bb333ac69..fb46c2e99 100644 --- a/coconut/tests/src/extras.coco +++ b/coconut/tests/src/extras.coco @@ -369,6 +369,12 @@ async def async_map_test() = setup(target="3.11") assert parse("a[x, *y]") + setup(target="3.12") + assert parse("type Num = int | float").strip().endswith(""" +# Compiled Coconut: ----------------------------------------------------------- + +type Num = int | float""".strip()) + setup(minify=True) assert parse("123 # derp", "lenient") == "123# derp"