From f4e3ebdedbe0222ae448ed22f7046c8510520615 Mon Sep 17 00:00:00 2001 From: Evan Hubinger Date: Fri, 22 Sep 2023 22:46:09 -0700 Subject: [PATCH] Add attritemgetter partials Resolves #787. --- .pre-commit-config.yaml | 4 +- __coconut__/__init__.pyi | 6 +++ coconut/__coconut__.pyi | 2 +- coconut/compiler/compiler.py | 21 +++++--- coconut/compiler/grammar.py | 42 +++++++++------ coconut/compiler/header.py | 2 +- coconut/compiler/templates/header.py_template | 53 ++++++++++++------- coconut/root.py | 2 +- .../tests/src/cocotest/agnostic/primary.coco | 5 ++ .../tests/src/cocotest/agnostic/suite.coco | 12 +++++ coconut/tests/src/cocotest/agnostic/util.coco | 19 +++++++ 11 files changed, 120 insertions(+), 48 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df224ace7..8f79c7238 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,13 +24,13 @@ repos: args: - --autofix - repo: https://github.com/pycqa/flake8 - rev: 6.0.0 + rev: 6.1.0 hooks: - id: flake8 args: - --ignore=W503,E501,E265,E402,F405,E305,E126 - repo: https://github.com/pre-commit/mirrors-autopep8 - rev: v2.0.2 + rev: v2.0.4 hooks: - id: autopep8 args: diff --git a/__coconut__/__init__.pyi b/__coconut__/__init__.pyi index d4b4ff4a6..c75480c00 100644 --- a/__coconut__/__init__.pyi +++ b/__coconut__/__init__.pyi @@ -640,6 +640,12 @@ def _coconut_iter_getitem( ... +def _coconut_attritemgetter( + attr: _t.Optional[_t.Text], + *is_iter_and_items: _t.Tuple[_t.Tuple[bool, _t.Any], ...], +) -> _t.Callable[[_t.Any], _t.Any]: ... + + def _coconut_base_compose( func: _t.Callable[[_T], _t.Any], *func_infos: _t.Tuple[_Callable, int, bool], diff --git a/coconut/__coconut__.pyi b/coconut/__coconut__.pyi index 45d413ea3..91be385cb 100644 --- a/coconut/__coconut__.pyi +++ b/coconut/__coconut__.pyi @@ -1,2 +1,2 @@ from __coconut__ import * -from __coconut__ import _coconut_tail_call, _coconut_tco, _coconut_call_set_names, _coconut_handle_cls_kwargs, _coconut_handle_cls_stargs, _namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_Expected, _coconut_MatchError, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in +from __coconut__ import _coconut_tail_call, _coconut_tco, _coconut_call_set_names, _coconut_handle_cls_kwargs, _coconut_handle_cls_stargs, _namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_Expected, _coconut_MatchError, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter diff --git a/coconut/compiler/compiler.py b/coconut/compiler/compiler.py index f3dae9c7e..fa7f611d9 100644 --- a/coconut/compiler/compiler.py +++ b/coconut/compiler/compiler.py @@ -2747,7 +2747,7 @@ def pipe_item_split(self, tokens, loc): - (expr,) for expression - (func, pos_args, kwd_args) for partial - (name, args) for attr/method - - (op, args)+ for itemgetter + - (attr, [(op, args)]) for itemgetter - (op, arg) for right op partial """ # list implies artificial tokens, which must be expr @@ -2762,8 +2762,12 @@ def pipe_item_split(self, tokens, loc): name, args = attrgetter_atom_split(tokens) return "attrgetter", (name, args) elif "itemgetter" in tokens: - internal_assert(len(tokens) >= 2, "invalid itemgetter pipe item tokens", tokens) - return "itemgetter", tokens + if len(tokens) == 1: + attr = None + ops_and_args, = tokens + else: + attr, ops_and_args = tokens + return "itemgetter", (attr, ops_and_args) elif "op partial" in tokens: inner_toks, = tokens if "left partial" in inner_toks: @@ -2853,12 +2857,13 @@ def pipe_handle(self, original, loc, tokens, **kwargs): elif name == "itemgetter": if stars: raise CoconutDeferredSyntaxError("cannot star pipe into item getting", loc) - self.internal_assert(len(split_item) % 2 == 0, original, loc, "invalid itemgetter pipe tokens", split_item) - out = subexpr - for i in range(0, len(split_item), 2): - op, args = split_item[i:i + 2] + attr, ops_and_args = split_item + out = "(" + subexpr + ")" + if attr is not None: + out += "." + attr + for op, args in ops_and_args: if op == "[": - fmtstr = "({x})[{args}]" + fmtstr = "{x}[{args}]" elif op == "$[": fmtstr = "_coconut_iter_getitem({x}, ({args}))" else: diff --git a/coconut/compiler/grammar.py b/coconut/compiler/grammar.py index adff7a9c2..48e22713d 100644 --- a/coconut/compiler/grammar.py +++ b/coconut/compiler/grammar.py @@ -437,22 +437,24 @@ def subscriptgroup_handle(tokens): def itemgetter_handle(tokens): """Process implicit itemgetter partials.""" - if len(tokens) == 2: - op, args = tokens + if len(tokens) == 1: + attr = None + ops_and_args, = tokens + else: + attr, ops_and_args = tokens + if attr is None and len(ops_and_args) == 1: + (op, args), = ops_and_args if op == "[": return "_coconut.operator.itemgetter((" + args + "))" elif op == "$[": return "_coconut.functools.partial(_coconut_iter_getitem, index=(" + args + "))" else: raise CoconutInternalException("invalid implicit itemgetter type", op) - elif len(tokens) > 2: - internal_assert(len(tokens) % 2 == 0, "invalid itemgetter composition tokens", tokens) - itemgetters = [] - for i in range(0, len(tokens), 2): - itemgetters.append(itemgetter_handle(tokens[i:i + 2])) - return "_coconut_forward_compose(" + ", ".join(itemgetters) + ")" else: - raise CoconutInternalException("invalid implicit itemgetter tokens", tokens) + return "_coconut_attritemgetter({attr}, {is_iter_and_items})".format( + attr=repr(attr), + is_iter_and_items=", ".join("({is_iter}, ({item}))".format(is_iter=op == "$[", item=args) for op, args in ops_and_args), + ) def class_suite_handle(tokens): @@ -1300,11 +1302,21 @@ class Grammar(object): lparen + Optional(methodcaller_args) + rparen.suppress() ) attrgetter_atom = attach(attrgetter_atom_tokens, attrgetter_atom_handle) - itemgetter_atom_tokens = dot.suppress() + OneOrMore(condense(Optional(dollar) + lbrack) + subscriptgrouplist + rbrack.suppress()) + + itemgetter_atom_tokens = ( + dot.suppress() + + Optional(unsafe_dotted_name) + + Group(OneOrMore(Group( + condense(Optional(dollar) + lbrack) + + subscriptgrouplist + + rbrack.suppress() + ))) + ) itemgetter_atom = attach(itemgetter_atom_tokens, itemgetter_handle) + implicit_partial_atom = ( - attrgetter_atom - | itemgetter_atom + itemgetter_atom + | attrgetter_atom | fixto(dot + lbrack + rbrack, "_coconut.operator.getitem") | fixto(dot + dollar + lbrack + rbrack, "_coconut_iter_getitem") ) @@ -1485,8 +1497,8 @@ class Grammar(object): pipe_item = ( # we need the pipe_op since any of the atoms could otherwise be the start of an expression labeled_group(keyword("await"), "await") + pipe_op - | labeled_group(attrgetter_atom_tokens, "attrgetter") + pipe_op | labeled_group(itemgetter_atom_tokens, "itemgetter") + pipe_op + | labeled_group(attrgetter_atom_tokens, "attrgetter") + pipe_op | labeled_group(partial_atom_tokens, "partial") + pipe_op | labeled_group(partial_op_atom_tokens, "op partial") + pipe_op # expr must come at end @@ -1495,8 +1507,8 @@ class Grammar(object): pipe_augassign_item = ( # should match pipe_item but with pipe_op -> end_simple_stmt_item and no expr labeled_group(keyword("await"), "await") + end_simple_stmt_item - | labeled_group(attrgetter_atom_tokens, "attrgetter") + end_simple_stmt_item | labeled_group(itemgetter_atom_tokens, "itemgetter") + end_simple_stmt_item + | labeled_group(attrgetter_atom_tokens, "attrgetter") + end_simple_stmt_item | labeled_group(partial_atom_tokens, "partial") + end_simple_stmt_item | labeled_group(partial_op_atom_tokens, "op partial") + end_simple_stmt_item ) @@ -1505,8 +1517,8 @@ class Grammar(object): # we need longest here because there's no following pipe_op we can use as above | longest( keyword("await")("await"), - attrgetter_atom_tokens("attrgetter"), itemgetter_atom_tokens("itemgetter"), + attrgetter_atom_tokens("attrgetter"), partial_atom_tokens("partial"), partial_op_atom_tokens("op partial"), comp_pipe_expr("expr"), diff --git a/coconut/compiler/header.py b/coconut/compiler/header.py index 3be75c8fd..409929e50 100644 --- a/coconut/compiler/header.py +++ b/coconut/compiler/header.py @@ -586,7 +586,7 @@ async def __anext__(self): # (extra_format_dict is to keep indentation levels matching) extra_format_dict = dict( # when anything is added to this list it must also be added to *both* __coconut__ stub files - underscore_imports="{tco_comma}{call_set_names_comma}{handle_cls_args_comma}_namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in".format(**format_dict), + underscore_imports="{tco_comma}{call_set_names_comma}{handle_cls_args_comma}_namedtuple_of, _coconut, _coconut_Expected, _coconut_MatchError, _coconut_SupportsAdd, _coconut_SupportsMinus, _coconut_SupportsMul, _coconut_SupportsPow, _coconut_SupportsTruediv, _coconut_SupportsFloordiv, _coconut_SupportsMod, _coconut_SupportsAnd, _coconut_SupportsXor, _coconut_SupportsOr, _coconut_SupportsLshift, _coconut_SupportsRshift, _coconut_SupportsMatmul, _coconut_SupportsInv, _coconut_iter_getitem, _coconut_base_compose, _coconut_forward_compose, _coconut_back_compose, _coconut_forward_star_compose, _coconut_back_star_compose, _coconut_forward_dubstar_compose, _coconut_back_dubstar_compose, _coconut_pipe, _coconut_star_pipe, _coconut_dubstar_pipe, _coconut_back_pipe, _coconut_back_star_pipe, _coconut_back_dubstar_pipe, _coconut_none_pipe, _coconut_none_star_pipe, _coconut_none_dubstar_pipe, _coconut_bool_and, _coconut_bool_or, _coconut_none_coalesce, _coconut_minus, _coconut_map, _coconut_partial, _coconut_get_function_match_error, _coconut_base_pattern_func, _coconut_addpattern, _coconut_sentinel, _coconut_assert, _coconut_raise, _coconut_mark_as_match, _coconut_reiterable, _coconut_self_match_types, _coconut_dict_merge, _coconut_exec, _coconut_comma_op, _coconut_multi_dim_arr, _coconut_mk_anon_namedtuple, _coconut_matmul, _coconut_py_str, _coconut_flatten, _coconut_multiset, _coconut_back_none_pipe, _coconut_back_none_star_pipe, _coconut_back_none_dubstar_pipe, _coconut_forward_none_compose, _coconut_back_none_compose, _coconut_forward_none_star_compose, _coconut_back_none_star_compose, _coconut_forward_none_dubstar_compose, _coconut_back_none_dubstar_compose, _coconut_call_or_coefficient, _coconut_in, _coconut_not_in, _coconut_attritemgetter".format(**format_dict), import_typing=pycondition( (3, 5), if_ge=''' diff --git a/coconut/compiler/templates/header.py_template b/coconut/compiler/templates/header.py_template index 845f6265b..0f8e4e58c 100644 --- a/coconut/compiler/templates/header.py_template +++ b/coconut/compiler/templates/header.py_template @@ -103,6 +103,12 @@ class _coconut_baseclass{object}: if getitem is None: raise _coconut.NotImplementedError return getitem(index) +class _coconut_base_callable(_coconut_baseclass): + __slots__ = () + def __get__(self, obj, objtype=None): + if obj is None: + return self +{return_method_of_self} class _coconut_Sentinel(_coconut_baseclass): __slots__ = () def __reduce__(self): @@ -354,7 +360,26 @@ def _coconut_iter_getitem(iterable, index): return () iterable = _coconut.itertools.islice(iterable, 0, n) return _coconut.tuple(iterable)[i::step] -class _coconut_compostion_baseclass(_coconut_baseclass):{COMMENT.no_slots_to_allow_update_wrapper}{COMMENT.must_use_coconut_attrs_to_avoid_interacting_with_update_wrapper} +class _coconut_attritemgetter(_coconut_base_callable): + __slots__ = ("attr", "is_iter_and_items") + def __init__(self, attr, *is_iter_and_items): + self.attr = attr + self.is_iter_and_items = is_iter_and_items + def __call__(self, obj): + out = obj + if self.attr is not None: + out = _coconut.getattr(out, self.attr) + for is_iter, item in self.is_iter_and_items: + if is_iter: + out = _coconut_iter_getitem(out, item) + else: + out = out[item] + return out + def __repr__(self): + return "." + (self.attr or "") + "".join(("$" if is_iter else "") + "[" + _coconut.repr(item) + "]" for is_iter, item in self.is_iter_and_items) + def __reduce__(self): + return (self.__class__, (self.attr,) + self.is_iter_and_items) +class _coconut_compostion_baseclass(_coconut_base_callable):{COMMENT.no_slots_to_allow_update_wrapper}{COMMENT.must_use_coconut_attrs_to_avoid_interacting_with_update_wrapper} def __init__(self, func, *func_infos): try: _coconut.functools.update_wrapper(self, func) @@ -376,10 +401,6 @@ class _coconut_compostion_baseclass(_coconut_baseclass):{COMMENT.no_slots_to_all self._coconut_func_infos = _coconut.tuple(self._coconut_func_infos) def __reduce__(self): return (self.__class__, (self._coconut_func,) + self._coconut_func_infos) - def __get__(self, obj, objtype=None): - if obj is None: - return self -{return_method_of_self} class _coconut_base_compose(_coconut_compostion_baseclass): __slots__ = () def __call__(self, *args, **kwargs): @@ -1278,7 +1299,7 @@ class groupsof(_coconut_has_iter): return (self.__class__, (self.group_size, self.iter)) def __copy__(self): return self.__class__(self.group_size, self.get_new_iter()) -class recursive_iterator(_coconut_baseclass): +class recursive_iterator(_coconut_base_callable): """Decorator that memoizes a recursive function that returns an iterator (e.g. a recursive generator).""" __slots__ = ("func", "reit_store", "backup_reit_store") def __init__(self, func): @@ -1312,10 +1333,6 @@ class recursive_iterator(_coconut_baseclass): return "recursive_iterator(%r)" % (self.func,) def __reduce__(self): return (self.__class__, (self.func,)) - def __get__(self, obj, objtype=None): - if obj is None: - return self -{return_method_of_self} class _coconut_FunctionMatchErrorContext(_coconut_baseclass): __slots__ = ("exc_class", "taken") threadlocal_ns = _coconut.threading.local() @@ -1340,7 +1357,7 @@ def _coconut_get_function_match_error(): return {_coconut_}MatchError ctx.taken = True return ctx.exc_class -class _coconut_base_pattern_func(_coconut_baseclass):{COMMENT.no_slots_to_allow_func_attrs} +class _coconut_base_pattern_func(_coconut_base_callable):{COMMENT.no_slots_to_allow_func_attrs} _coconut_is_match = True def __init__(self, *funcs): self.FunctionMatchError = _coconut.type(_coconut_py_str("MatchError"), ({_coconut_}MatchError,), {empty_py_dict}) @@ -1378,10 +1395,6 @@ class _coconut_base_pattern_func(_coconut_baseclass):{COMMENT.no_slots_to_allow_ return "addpattern(%r)(*%r)" % (self.patterns[0], self.patterns[1:]) def __reduce__(self): return (self.__class__, _coconut.tuple(self.patterns)) - def __get__(self, obj, objtype=None): - if obj is None: - return self -{return_method_of_self} def _coconut_mark_as_match(base_func):{COMMENT._coconut_is_match_is_used_above_and_in_main_coco} base_func._coconut_is_match = True return base_func @@ -1401,7 +1414,7 @@ def addpattern(base_func, *add_funcs, **kwargs): return _coconut.functools.partial(_coconut_base_pattern_func, base_func) _coconut_addpattern = addpattern {def_prepattern} -class _coconut_partial(_coconut_baseclass): +class _coconut_partial(_coconut_base_callable): __slots__ = ("func", "_argdict", "_arglen", "_pos_kwargs", "_stargs", "keywords") def __init__(self, _coconut_func, _coconut_argdict, _coconut_arglen, _coconut_pos_kwargs, *args, **kwargs): self.func = _coconut_func @@ -1779,7 +1792,7 @@ class Expected(_coconut.collections.namedtuple("Expected", ("result", "error")){ if not self: raise self.error return self.result -class flip(_coconut_baseclass): +class flip(_coconut_base_callable): """Given a function, return a new function with inverse argument order. If nargs is passed, only the first nargs arguments are reversed.""" __slots__ = ("func", "nargs") @@ -1801,7 +1814,7 @@ class flip(_coconut_baseclass): return self.func(*(args[self.nargs-1::-1] + args[self.nargs:]), **kwargs) def __repr__(self): return "flip(%r%s)" % (self.func, "" if self.nargs is None else ", " + _coconut.repr(self.nargs)) -class const(_coconut_baseclass): +class const(_coconut_base_callable): """Create a function that, whatever its arguments, just returns the given value.""" __slots__ = ("value",) def __init__(self, value): @@ -1812,7 +1825,7 @@ class const(_coconut_baseclass): return self.value def __repr__(self): return "const(%s)" % (_coconut.repr(self.value),) -class _coconut_lifted(_coconut_baseclass): +class _coconut_lifted(_coconut_base_callable): __slots__ = ("func", "func_args", "func_kwargs") def __init__(self, _coconut_func, *func_args, **func_kwargs): self.func = _coconut_func @@ -1824,7 +1837,7 @@ class _coconut_lifted(_coconut_baseclass): return self.func(*(g(*args, **kwargs) for g in self.func_args), **_coconut_py_dict((k, h(*args, **kwargs)) for k, h in self.func_kwargs.items())) def __repr__(self): return "lift(%r)(%s%s)" % (self.func, ", ".join(_coconut.repr(g) for g in self.func_args), ", ".join(k + "=" + _coconut.repr(h) for k, h in self.func_kwargs.items())) -class lift(_coconut_baseclass): +class lift(_coconut_base_callable): """Lifts a function up so that all of its arguments are functions. For a binary function f(x, y) and two unary functions g(z) and h(z), lift works as the S' combinator: diff --git a/coconut/root.py b/coconut/root.py index be751cb2b..f30ef27ae 100644 --- a/coconut/root.py +++ b/coconut/root.py @@ -26,7 +26,7 @@ VERSION = "3.0.3" VERSION_NAME = None # False for release, int >= 1 for develop -DEVELOP = 7 +DEVELOP = 8 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/primary.coco b/coconut/tests/src/cocotest/agnostic/primary.coco index 26192f408..20218a28b 100644 --- a/coconut/tests/src/cocotest/agnostic/primary.coco +++ b/coconut/tests/src/cocotest/agnostic/primary.coco @@ -1653,4 +1653,9 @@ def primary_test() -> bool: def g() = x where: x = 5 assert nested()()() == 5 + class HasPartial: + def f(self, x) = (self, x) + g = f$(?, 1) + has_partial = HasPartial() + assert has_partial.g() == (has_partial, 1) return True diff --git a/coconut/tests/src/cocotest/agnostic/suite.coco b/coconut/tests/src/cocotest/agnostic/suite.coco index 7e0440630..89db9b0a4 100644 --- a/coconut/tests/src/cocotest/agnostic/suite.coco +++ b/coconut/tests/src/cocotest/agnostic/suite.coco @@ -1053,6 +1053,18 @@ forward 2""") == 900 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] + assert hasloc([[1, 2]]).loc[0][1] == 2 == hasloc([[1, 2]]) |> .loc[0][1] + locgetter = .loc[0][1] + assert hasloc([[1, 2]]) |> locgetter == 2 == (hasloc([[1, 2]]) |> .loc[0])[1] + haslocobj = hasloc([[1, 2]]) + haslocobj |>= .loc[0][1] + assert haslocobj == 2 + assert hasloc([[1, 2]]).iloc$[0]$[1] == 2 == hasloc([[1, 2]]) |> .iloc$[0]$[1] + locgetter = .iloc$[0]$[1] + assert hasloc([[1, 2]]) |> locgetter == 2 == (hasloc([[1, 2]]) |> .iloc$[0])$[1] + haslocobj = hasloc([[1, 2]]) + haslocobj |>= .iloc$[0]$[1] + assert haslocobj == 2 # 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 fbbcc2e4d..c2c1c8553 100644 --- a/coconut/tests/src/cocotest/agnostic/util.coco +++ b/coconut/tests/src/cocotest/agnostic/util.coco @@ -1059,6 +1059,25 @@ class unrepresentable: def __repr__(self): raise Fail("unrepresentable") +class hasloc: + def __init__(self, arr): + self.arr = arr + class Loc: + def __init__(inner, outer): + inner.outer = outer + def __getitem__(inner, item) = + inner.outer.arr[item] + @property + def loc(self) = self.Loc(self) + class ILoc: + def __init__(inner, outer): + inner.outer = outer + def __iter_getitem__(inner, item) = + inner.outer.arr$[item] + @property + def iloc(self) = self.ILoc(self) + + # Typing if TYPE_CHECKING or sys.version_info >= (3, 5): # test from typing import *, but that doesn't actually get us