Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"Bash(pip:*)",
"Bash(log \")",
"Bash(test:*)",
"Bash(done)",
"WebFetch(domain:github.com)",
"WebFetch(domain:raw.githubusercontent.com)",
"WebFetch(domain:coconut.readthedocs.io)",
Expand Down
4 changes: 4 additions & 0 deletions _coconut/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ numpy = _numpy
npt = _npt # Fake, like typing
zip_longest = _zip_longest

import importlib as _importlib
_coconut_lazy_module = _importlib.import_module

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this showing up in the diff? Isn't this already in develop?


numpy_modules: _t.Any = ...
xarray_modules: _t.Any = ...
pandas_modules: _t.Any = ...
Expand Down Expand Up @@ -147,6 +150,7 @@ any = _builtins.any
bool = _builtins.bool
bytes = _builtins.bytes
dict = _builtins.dict
dir = _builtins.dir
enumerate = _builtins.enumerate
filter = _builtins.filter
float = _builtins.float
Expand Down
2 changes: 1 addition & 1 deletion coconut/__coconut__.pyi
Original file line number Diff line number Diff line change
@@ -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_complex_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_arr_concat_op, _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, _coconut_if_op, _coconut_CoconutWarning
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_complex_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_arr_concat_op, _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, _coconut_if_op, _coconut_CoconutWarning, _coconut_lazy_module
49 changes: 37 additions & 12 deletions coconut/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,21 @@ def strip_raw_and_b(string):
return raw, has_b, string


def import_stmt(imp_from, imp, imp_as, raw=False):
def import_stmt(imp_from, imp, imp_as, raw=False, lazy=False):
"""Generate an import statement."""
if lazy:
bind_name = imp_as if imp_as is not None else imp.split(".", 1)[0]
if imp_from is not None:
return '{name} = _coconut_lazy_module("{module}").{attr}'.format(
name=bind_name,
module=imp_from,
attr=imp,
)
else:
return '{name} = _coconut_lazy_module("{module}")'.format(
name=bind_name,
module=imp,
)
if not raw and imp != "*":
module_path = (imp if imp_from is None else imp_from).split(".", 1)
existing_imp = import_existing.get(module_path[0])
Expand Down Expand Up @@ -4005,7 +4018,7 @@ def anon_namedtuple_handle(self, original, loc, tokens):

return self.make_namedtuple_call(None, names, types, of_args=items)

def single_import(self, loc, path, imp_as, type_ignore=False):
def single_import(self, loc, path, imp_as, type_ignore=False, lazy=False):
"""Generate import statements from a fully qualified import and the name to bind it to."""
out = []

Expand All @@ -4025,7 +4038,7 @@ def single_import(self, loc, path, imp_as, type_ignore=False):

if imp_as is not None and "." in imp_as:
import_as_var = self.get_temp_var("import", loc)
out.append(import_stmt(imp_from, imp, import_as_var))
out.append(import_stmt(imp_from, imp, import_as_var, lazy=lazy))
fake_mods = imp_as.split(".")
for i in range(1, len(fake_mods)):
mod_name = ".".join(fake_mods[:i])
Expand All @@ -4040,15 +4053,15 @@ def single_import(self, loc, path, imp_as, type_ignore=False):
]
out.append(".".join(fake_mods) + " = " + import_as_var)
else:
out.append(import_stmt(imp_from, imp, imp_as))
out.append(import_stmt(imp_from, imp, imp_as, lazy=lazy))

if type_ignore:
for i, line in enumerate(out):
out[i] = line + self.type_ignore_comment()

return out

def universal_import(self, loc, imports, imp_from=None):
def universal_import(self, loc, imports, imp_from=None, lazy=False):
"""Generate code for a universal import of imports from imp_from.
imports = [[imp1], [imp2, as], ...]"""
importmap = [] # [((imp | old_imp, imp, version_check), imp_as), ...]
Expand Down Expand Up @@ -4095,7 +4108,7 @@ def universal_import(self, loc, imports, imp_from=None):
stmts = []
for paths, imp_as, type_ignore in importmap:
if len(paths) == 1:
more_stmts = self.single_import(loc, paths[0], imp_as)
more_stmts = self.single_import(loc, paths[0], imp_as, lazy=lazy)
stmts.extend(more_stmts)
else:
old_imp, new_imp, version_check = paths
Expand All @@ -4116,29 +4129,38 @@ def universal_import(self, loc, imports, imp_from=None):
""").format(
store_var=self.get_temp_var("sys", loc),
version_check=version_check,
new_imp="\n".join(self.single_import(loc, new_imp, imp_as)),
new_imp="\n".join(self.single_import(loc, new_imp, imp_as, lazy=lazy)),
# should only type: ignore the old import
old_imp="\n".join(self.single_import(loc, old_imp, imp_as, type_ignore=type_ignore)),
old_imp="\n".join(self.single_import(loc, old_imp, imp_as, type_ignore=type_ignore, lazy=lazy)),
type_ignore=self.type_ignore_comment(),
),
)
return "\n".join(stmts)

def import_handle(self, original, loc, tokens):
"""Universalizes imports."""
# First token is always either "lazy" or "" (from Optional default)
lazy = tokens[0] == "lazy"
tokens = tokens[1:]

if len(tokens) == 1:
imp_from, imports = None, tokens[0]
elif len(tokens) == 2:
imp_from, imports = tokens
if imp_from == "__future__":
self.strict_err_or_warn("unnecessary from __future__ import (Coconut does these automatically)", original, loc, noqa_able=True)
return ""
else:
raise CoconutInternalException("invalid import tokens", tokens)

if imp_from == "__future__":
if lazy:
raise self.make_err(CoconutSyntaxError, "lazy imports not allowed for __future__", original, loc)
self.strict_err_or_warn("unnecessary from __future__ import (Coconut does these automatically)", original, loc, noqa_able=True)
return ""
imports = list(imports)
imported_names, star_import = get_imported_names(imports)
self.star_import = self.star_import or star_import
if star_import:
if lazy:
raise self.make_err(CoconutSyntaxError, "lazy imports not allowed for star imports", original, loc)
self.strict_warn("found * import; these disable Coconut's undefined name detection", original, loc)
if imp_from == "*" or (imp_from is None and star_import):
if not (len(imports) == 1 and imports[0] == "*"):
Expand All @@ -4147,7 +4169,10 @@ def import_handle(self, original, loc, tokens):
return special_starred_import_handle(imp_all=bool(imp_from))
for imp_name in imported_names:
self.name_info[imp_name]["imported"].add(loc)
return self.universal_import(loc, imports, imp_from=imp_from)
if lazy:
return self.universal_import(loc, imports, imp_from=imp_from, lazy=True)
else:
return self.universal_import(loc, imports, imp_from=imp_from)

def complex_raise_stmt_handle(self, loc, tokens):
"""Process Python 3 raise from statement."""
Expand Down
2 changes: 1 addition & 1 deletion coconut/compiler/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -2010,7 +2010,7 @@ class Grammar(object):
- keyword("import").suppress() - from_import_names
)
import_stmt = Forward()
import_stmt_ref = from_import | basic_import
import_stmt_ref = Optional(keyword("lazy"), default="") + (from_import | basic_import)

augassign_stmt = Forward()
augassign_rhs = (
Expand Down
2 changes: 1 addition & 1 deletion coconut/compiler/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ 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_complex_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_arr_concat_op, _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, _coconut_if_op, _coconut_CoconutWarning".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_complex_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_arr_concat_op, _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, _coconut_if_op, _coconut_CoconutWarning, _coconut_lazy_module".format(**format_dict),
import_typing=pycondition(
(3, 5),
if_ge='''
Expand Down
2 changes: 1 addition & 1 deletion coconut/compiler/templates/header.py_template
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class _coconut{object}:{COMMENT.EVERYTHING_HERE_MUST_BE_COPIED_TO_STUB_FILE}
reiterables = abc.Sequence, abc.Mapping, abc.Set
fmappables = list, tuple, dict, set, frozenset, bytes, bytearray
abc.Sequence.register(collections.deque)
Ellipsis, NotImplemented, NotImplementedError, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, RuntimeError, all, any, bool, bytes, callable, chr, classmethod, complex, dict, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, globals, map, min, max, next, object, ord, property, range, reversed, set, setattr, slice, str, sum, super, tuple, type, vars, zip, repr, print{comma_bytearray} = Ellipsis, NotImplemented, NotImplementedError, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, RuntimeError, all, any, bool, bytes, callable, chr, classmethod, complex, dict, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, globals, map, {lstatic}min{rstatic}, {lstatic}max{rstatic}, next, object, ord, property, range, reversed, set, setattr, slice, str, sum, {lstatic}super{rstatic}, tuple, type, vars, zip, {lstatic}repr{rstatic}, {lstatic}print{rstatic}{comma_bytearray}
Ellipsis, NotImplemented, NotImplementedError, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, RuntimeError, all, any, bool, bytes, callable, chr, classmethod, complex, dict, dir, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, globals, map, min, max, next, object, ord, property, range, reversed, set, setattr, slice, str, sum, super, tuple, type, vars, zip, repr, print{comma_bytearray} = Ellipsis, NotImplemented, NotImplementedError, Exception, AttributeError, ImportError, IndexError, KeyError, NameError, TypeError, ValueError, StopIteration, RuntimeError, all, any, bool, bytes, callable, chr, classmethod, complex, dict, dir, enumerate, filter, float, frozenset, getattr, hasattr, hash, id, int, isinstance, issubclass, iter, len, list, locals, globals, map, {lstatic}min{rstatic}, {lstatic}max{rstatic}, next, object, ord, property, range, reversed, set, setattr, slice, str, sum, {lstatic}super{rstatic}, tuple, type, vars, zip, {lstatic}repr{rstatic}, {lstatic}print{rstatic}{comma_bytearray}
def _coconut_handle_cls_kwargs(**kwargs):
"""Some code taken from six under the terms of its MIT license."""
metaclass = kwargs.pop("metaclass", None)
Expand Down
21 changes: 21 additions & 0 deletions coconut/tests/src/cocotest/agnostic/suite.coco
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,27 @@ forward 2""") == 900
assert process_map(list .. .$[:2] .. loop, ([1], [2]))$[:2] |> tuple == ([1, 1], [2, 2])
assert all(r == 4 for r in process_map(call$(?, [7,1,4,5]), [maxdiff1, maxdiff2, maxdiff3]))

# lazy import tests (PEP 810)
lazy import json
assert json.dumps([1, 2, 3]) == "[1, 2, 3]"
lazy import json as j
assert j.loads("[1]") == [1]
lazy from json import dumps
assert dumps({"a": 1}) == '{"a": 1}'
lazy from json import loads as ld
assert ld("[2]") == [2]
lazy from json import dumps as d, loads as l
assert l(d([3])) == [3]
lazy import collections
assert "OrderedDict" in dir(collections)
lazy from collections import OrderedDict
assert OrderedDict([("a", 1)]) |> dict == {"a": 1}
# test remapped imports (queue -> Queue on py2)
lazy import queue
assert hasattr(queue, "Queue")
lazy from queue import Queue
assert Queue is not None

# must come at end
assert fibs_calls[0] == 1
assert lols[0] == 5
Expand Down
Loading