guppylang-internals 0.21.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. guppylang_internals/__init__.py +3 -0
  2. guppylang_internals/ast_util.py +350 -0
  3. guppylang_internals/cfg/__init__.py +0 -0
  4. guppylang_internals/cfg/analysis.py +230 -0
  5. guppylang_internals/cfg/bb.py +221 -0
  6. guppylang_internals/cfg/builder.py +606 -0
  7. guppylang_internals/cfg/cfg.py +117 -0
  8. guppylang_internals/checker/__init__.py +0 -0
  9. guppylang_internals/checker/cfg_checker.py +388 -0
  10. guppylang_internals/checker/core.py +550 -0
  11. guppylang_internals/checker/errors/__init__.py +0 -0
  12. guppylang_internals/checker/errors/comptime_errors.py +106 -0
  13. guppylang_internals/checker/errors/generic.py +45 -0
  14. guppylang_internals/checker/errors/linearity.py +300 -0
  15. guppylang_internals/checker/errors/type_errors.py +344 -0
  16. guppylang_internals/checker/errors/wasm.py +34 -0
  17. guppylang_internals/checker/expr_checker.py +1413 -0
  18. guppylang_internals/checker/func_checker.py +269 -0
  19. guppylang_internals/checker/linearity_checker.py +821 -0
  20. guppylang_internals/checker/stmt_checker.py +447 -0
  21. guppylang_internals/compiler/__init__.py +0 -0
  22. guppylang_internals/compiler/cfg_compiler.py +233 -0
  23. guppylang_internals/compiler/core.py +613 -0
  24. guppylang_internals/compiler/expr_compiler.py +989 -0
  25. guppylang_internals/compiler/func_compiler.py +97 -0
  26. guppylang_internals/compiler/hugr_extension.py +224 -0
  27. guppylang_internals/compiler/qtm_platform_extension.py +0 -0
  28. guppylang_internals/compiler/stmt_compiler.py +212 -0
  29. guppylang_internals/decorator.py +246 -0
  30. guppylang_internals/definition/__init__.py +0 -0
  31. guppylang_internals/definition/common.py +214 -0
  32. guppylang_internals/definition/const.py +74 -0
  33. guppylang_internals/definition/custom.py +492 -0
  34. guppylang_internals/definition/declaration.py +171 -0
  35. guppylang_internals/definition/extern.py +89 -0
  36. guppylang_internals/definition/function.py +302 -0
  37. guppylang_internals/definition/overloaded.py +150 -0
  38. guppylang_internals/definition/parameter.py +82 -0
  39. guppylang_internals/definition/pytket_circuits.py +405 -0
  40. guppylang_internals/definition/struct.py +392 -0
  41. guppylang_internals/definition/traced.py +151 -0
  42. guppylang_internals/definition/ty.py +51 -0
  43. guppylang_internals/definition/value.py +115 -0
  44. guppylang_internals/definition/wasm.py +61 -0
  45. guppylang_internals/diagnostic.py +523 -0
  46. guppylang_internals/dummy_decorator.py +76 -0
  47. guppylang_internals/engine.py +295 -0
  48. guppylang_internals/error.py +107 -0
  49. guppylang_internals/experimental.py +92 -0
  50. guppylang_internals/ipython_inspect.py +28 -0
  51. guppylang_internals/nodes.py +427 -0
  52. guppylang_internals/py.typed +0 -0
  53. guppylang_internals/span.py +150 -0
  54. guppylang_internals/std/__init__.py +0 -0
  55. guppylang_internals/std/_internal/__init__.py +0 -0
  56. guppylang_internals/std/_internal/checker.py +573 -0
  57. guppylang_internals/std/_internal/compiler/__init__.py +0 -0
  58. guppylang_internals/std/_internal/compiler/arithmetic.py +136 -0
  59. guppylang_internals/std/_internal/compiler/array.py +569 -0
  60. guppylang_internals/std/_internal/compiler/either.py +131 -0
  61. guppylang_internals/std/_internal/compiler/frozenarray.py +68 -0
  62. guppylang_internals/std/_internal/compiler/futures.py +30 -0
  63. guppylang_internals/std/_internal/compiler/list.py +348 -0
  64. guppylang_internals/std/_internal/compiler/mem.py +13 -0
  65. guppylang_internals/std/_internal/compiler/option.py +78 -0
  66. guppylang_internals/std/_internal/compiler/prelude.py +271 -0
  67. guppylang_internals/std/_internal/compiler/qsystem.py +48 -0
  68. guppylang_internals/std/_internal/compiler/quantum.py +118 -0
  69. guppylang_internals/std/_internal/compiler/tket_bool.py +55 -0
  70. guppylang_internals/std/_internal/compiler/tket_exts.py +59 -0
  71. guppylang_internals/std/_internal/compiler/wasm.py +135 -0
  72. guppylang_internals/std/_internal/compiler.py +0 -0
  73. guppylang_internals/std/_internal/debug.py +95 -0
  74. guppylang_internals/std/_internal/util.py +271 -0
  75. guppylang_internals/tracing/__init__.py +0 -0
  76. guppylang_internals/tracing/builtins_mock.py +62 -0
  77. guppylang_internals/tracing/frozenlist.py +57 -0
  78. guppylang_internals/tracing/function.py +186 -0
  79. guppylang_internals/tracing/object.py +551 -0
  80. guppylang_internals/tracing/state.py +69 -0
  81. guppylang_internals/tracing/unpacking.py +194 -0
  82. guppylang_internals/tracing/util.py +86 -0
  83. guppylang_internals/tys/__init__.py +0 -0
  84. guppylang_internals/tys/arg.py +115 -0
  85. guppylang_internals/tys/builtin.py +382 -0
  86. guppylang_internals/tys/common.py +110 -0
  87. guppylang_internals/tys/const.py +114 -0
  88. guppylang_internals/tys/errors.py +178 -0
  89. guppylang_internals/tys/param.py +251 -0
  90. guppylang_internals/tys/parsing.py +425 -0
  91. guppylang_internals/tys/printing.py +174 -0
  92. guppylang_internals/tys/subst.py +112 -0
  93. guppylang_internals/tys/ty.py +876 -0
  94. guppylang_internals/tys/var.py +49 -0
  95. guppylang_internals-0.21.0.dist-info/METADATA +253 -0
  96. guppylang_internals-0.21.0.dist-info/RECORD +98 -0
  97. guppylang_internals-0.21.0.dist-info/WHEEL +4 -0
  98. guppylang_internals-0.21.0.dist-info/licenses/LICENCE +201 -0
@@ -0,0 +1,76 @@
1
+ from typing import Any, TypeVar
2
+
3
+
4
+ class _DummyGuppy:
5
+ """A dummy class with the same interface as `@guppy` that does nothing when used to
6
+ decorate functions.
7
+
8
+ We use this during sphinx builds as a mock for the decorator, to ensure that Guppy
9
+ functions are recognised as regular functions and included in docs.
10
+ """
11
+
12
+ def __call__(self, f: Any) -> Any:
13
+ return f
14
+
15
+ def comptime(self, f: Any) -> Any:
16
+ return f
17
+
18
+ def extend_type(self, *args: Any, **kwargs: Any) -> Any:
19
+ return lambda cls: cls
20
+
21
+ def type(self, *args: Any, **kwargs: Any) -> Any:
22
+ return lambda cls: cls
23
+
24
+ def struct(self, cls: Any) -> Any:
25
+ return cls
26
+
27
+ def type_var(self, name: str, *args: Any, **kwargs: Any) -> Any:
28
+ return TypeVar(name)
29
+
30
+ def nat_var(self, name: str, *args: Any, **kwargs: Any) -> Any:
31
+ return TypeVar(name)
32
+
33
+ def custom(self, *args: Any, **kwargs: Any) -> Any:
34
+ return lambda f: f
35
+
36
+ def hugr_op(self, *args: Any, **kwargs: Any) -> Any:
37
+ return lambda f: f
38
+
39
+ def declare(self, f: Any) -> Any:
40
+ return f
41
+
42
+ def overload(self, *args: Any, **kwargs: Any) -> Any:
43
+ return lambda f: f
44
+
45
+ def constant(self, *args: Any, **kwargs: Any) -> Any:
46
+ return None
47
+
48
+ def extern(self, *args: Any, **kwargs: Any) -> Any:
49
+ return None
50
+
51
+ def check(self, *args: Any, **kwargs: Any) -> None:
52
+ pass
53
+
54
+ def compile(self, *args: Any, **kwargs: Any) -> Any:
55
+ return None
56
+
57
+ def compile_function(self, *args: Any, **kwargs: Any) -> Any:
58
+ return None
59
+
60
+ def pytket(self, *args: Any, **kwargs: Any) -> Any:
61
+ return lambda f: f
62
+
63
+ def load_pytket(self, *args: Any, **kwargs: Any) -> Any:
64
+ return None
65
+
66
+
67
+ def sphinx_running() -> bool:
68
+ """Checks if guppylang was imported during a sphinx build."""
69
+ # This is the most general solution available at the moment.
70
+ # See: https://github.com/sphinx-doc/sphinx/issues/9805
71
+ try:
72
+ import sphinx # type: ignore[import-untyped, import-not-found, unused-ignore]
73
+
74
+ return hasattr(sphinx, "application")
75
+ except ImportError:
76
+ return False
@@ -0,0 +1,295 @@
1
+ from collections import defaultdict
2
+ from enum import Enum
3
+ from types import FrameType
4
+ from typing import TYPE_CHECKING
5
+
6
+ import hugr.build.function as hf
7
+ import hugr.std.collections.array
8
+ import hugr.std.float
9
+ import hugr.std.int
10
+ import hugr.std.logic
11
+ import hugr.std.prelude
12
+ from hugr import ops
13
+ from hugr.ext import Extension
14
+ from hugr.package import ModulePointer, Package
15
+
16
+ import guppylang_internals
17
+ from guppylang_internals.definition.common import (
18
+ CheckableDef,
19
+ CheckedDef,
20
+ CompiledDef,
21
+ DefId,
22
+ ParsableDef,
23
+ ParsedDef,
24
+ RawDef,
25
+ )
26
+ from guppylang_internals.definition.ty import TypeDef
27
+ from guppylang_internals.definition.value import (
28
+ CompiledCallableDef,
29
+ CompiledHugrNodeDef,
30
+ )
31
+ from guppylang_internals.error import pretty_errors
32
+ from guppylang_internals.span import SourceMap
33
+ from guppylang_internals.tys.builtin import (
34
+ array_type_def,
35
+ bool_type_def,
36
+ callable_type_def,
37
+ float_type_def,
38
+ frozenarray_type_def,
39
+ int_type_def,
40
+ list_type_def,
41
+ nat_type_def,
42
+ none_type_def,
43
+ option_type_def,
44
+ sized_iter_type_def,
45
+ string_type_def,
46
+ tuple_type_def,
47
+ )
48
+
49
+ if TYPE_CHECKING:
50
+ from guppylang_internals.compiler.core import MonoDefId
51
+
52
+ BUILTIN_DEFS_LIST: list[RawDef] = [
53
+ callable_type_def,
54
+ tuple_type_def,
55
+ none_type_def,
56
+ bool_type_def,
57
+ nat_type_def,
58
+ int_type_def,
59
+ float_type_def,
60
+ string_type_def,
61
+ list_type_def,
62
+ array_type_def,
63
+ frozenarray_type_def,
64
+ sized_iter_type_def,
65
+ option_type_def,
66
+ ]
67
+
68
+ BUILTIN_DEFS = {defn.name: defn for defn in BUILTIN_DEFS_LIST}
69
+
70
+
71
+ class CoreMetadataKeys(Enum):
72
+ """Core HUGR metadata keys used by Guppy."""
73
+
74
+ USED_EXTENSIONS = "core.used_extensions"
75
+ GENERATOR = "core.generator"
76
+
77
+
78
+ class DefinitionStore:
79
+ """Storage class holding references to all Guppy definitions created in the current
80
+ interpreter session.
81
+
82
+ See `DEF_STORE` for the singleton instance of this class.
83
+ """
84
+
85
+ raw_defs: dict[DefId, RawDef]
86
+ impls: defaultdict[DefId, dict[str, DefId]]
87
+ frames: dict[DefId, FrameType]
88
+ sources: SourceMap
89
+
90
+ def __init__(self) -> None:
91
+ self.raw_defs = {defn.id: defn for defn in BUILTIN_DEFS_LIST}
92
+ self.impls = defaultdict(dict)
93
+ self.frames = {}
94
+ self.sources = SourceMap()
95
+
96
+ def register_def(self, defn: RawDef, frame: FrameType | None) -> None:
97
+ self.raw_defs[defn.id] = defn
98
+ if frame:
99
+ self.frames[defn.id] = frame
100
+
101
+ def register_impl(self, ty_id: DefId, name: str, impl_id: DefId) -> None:
102
+ self.impls[ty_id][name] = impl_id
103
+ # Update the frame of the definition to the frame of the defining class
104
+ if impl_id in self.frames:
105
+ frame = self.frames[impl_id].f_back
106
+ if frame:
107
+ self.frames[impl_id] = frame
108
+ # For Python 3.12 generic functions and classes, there is an additional
109
+ # inserted frame for the annotation scope. We can detect this frame by
110
+ # looking for the special ".generic_base" variable in the frame locals
111
+ # that is implicitly inserted by CPython. See
112
+ # - https://docs.python.org/3/reference/executionmodel.html#annotation-scopes
113
+ # - https://docs.python.org/3/reference/compound_stmts.html#generic-functions
114
+ # - https://jellezijlstra.github.io/pep695.html
115
+ if ".generic_base" in frame.f_locals:
116
+ frame = frame.f_back
117
+ assert frame is not None
118
+ self.frames[impl_id] = frame
119
+
120
+
121
+ DEF_STORE: DefinitionStore = DefinitionStore()
122
+
123
+
124
+ class CompilationEngine:
125
+ """Main compiler driver handling checking and compiling of definitions.
126
+
127
+ The engine maintains a worklist of definitions that still need to be checked and
128
+ makes sure that all dependencies are compiled.
129
+
130
+ See `ENGINE` for the singleton instance of this class.
131
+ """
132
+
133
+ parsed: dict[DefId, ParsedDef]
134
+ checked: dict[DefId, CheckedDef]
135
+ compiled: dict["MonoDefId", CompiledDef]
136
+ additional_extensions: list[Extension]
137
+
138
+ types_to_check_worklist: dict[DefId, ParsedDef]
139
+ to_check_worklist: dict[DefId, ParsedDef]
140
+
141
+ def reset(self) -> None:
142
+ """Resets the compilation cache."""
143
+ self.parsed = {}
144
+ self.checked = {}
145
+ self.compiled = {}
146
+ self.additional_extensions = []
147
+ self.to_check_worklist = {}
148
+ self.types_to_check_worklist = {}
149
+
150
+ @pretty_errors
151
+ def register_extension(self, extension: Extension) -> None:
152
+ self.additional_extensions.append(extension)
153
+
154
+ @pretty_errors
155
+ def get_parsed(self, id: DefId) -> ParsedDef:
156
+ """Look up the parsed version of a definition by its id.
157
+
158
+ Parses the definition if it hasn't been parsed yet. Also makes sure that the
159
+ definition will be checked and compiled later on.
160
+ """
161
+ from guppylang_internals.checker.core import Globals
162
+
163
+ if id in self.parsed:
164
+ return self.parsed[id]
165
+ defn = DEF_STORE.raw_defs[id]
166
+ if isinstance(defn, ParsableDef):
167
+ defn = defn.parse(Globals(DEF_STORE.frames[defn.id]), DEF_STORE.sources)
168
+ self.parsed[id] = defn
169
+ if isinstance(defn, TypeDef):
170
+ self.types_to_check_worklist[id] = defn
171
+ else:
172
+ self.to_check_worklist[id] = defn
173
+ return defn
174
+
175
+ @pretty_errors
176
+ def get_checked(self, id: DefId) -> CheckedDef:
177
+ """Look up the checked version of a definition by its id.
178
+
179
+ Parses and checks the definition if it hasn't been parsed/checked yet. Also
180
+ makes sure that the definition will be compiled to Hugr later on.
181
+ """
182
+ from guppylang_internals.checker.core import Globals
183
+
184
+ if id in self.checked:
185
+ return self.checked[id]
186
+ defn = self.get_parsed(id)
187
+ if isinstance(defn, CheckableDef):
188
+ defn = defn.check(Globals(DEF_STORE.frames[defn.id]))
189
+ self.checked[id] = defn
190
+
191
+ from guppylang_internals.definition.struct import CheckedStructDef
192
+
193
+ if isinstance(defn, CheckedStructDef):
194
+ for method_def in defn.generated_methods():
195
+ DEF_STORE.register_def(method_def, None)
196
+ DEF_STORE.register_impl(defn.id, method_def.name, method_def.id)
197
+
198
+ return defn
199
+
200
+ @pretty_errors
201
+ def check(self, id: DefId) -> None:
202
+ """Top-level function to kick of checking of a definition.
203
+
204
+ This is the main driver behind `guppy.check()`.
205
+ """
206
+ from guppylang_internals.checker.core import Globals
207
+
208
+ # Clear previous compilation cache.
209
+ # TODO: In order to maintain results from the previous `check` call we would
210
+ # need to store and check if any dependencies have changed.
211
+ self.reset()
212
+
213
+ defn = DEF_STORE.raw_defs[id]
214
+ self.to_check_worklist = {
215
+ defn.id: (
216
+ defn.parse(Globals(DEF_STORE.frames[defn.id]), DEF_STORE.sources)
217
+ if isinstance(defn, ParsableDef)
218
+ else defn
219
+ )
220
+ }
221
+ while self.types_to_check_worklist or self.to_check_worklist:
222
+ # Types need to be checked first. This is because parsing e.g. a function
223
+ # definition requires instantiating the types in its signature which can
224
+ # only be done if the types have already been checked.
225
+ if self.types_to_check_worklist:
226
+ id, _ = self.types_to_check_worklist.popitem()
227
+ else:
228
+ id, _ = self.to_check_worklist.popitem()
229
+ self.checked[id] = self.get_checked(id)
230
+
231
+ @pretty_errors
232
+ def compile(self, id: DefId) -> ModulePointer:
233
+ """Top-level function to kick of Hugr compilation of a definition.
234
+
235
+ This is the function that is invoked by `guppy.compile`.
236
+ """
237
+ self.check(id)
238
+
239
+ # Prepare Hugr for this module
240
+ graph = hf.Module()
241
+ graph.metadata["name"] = "__main__" # entrypoint metadata
242
+
243
+ # Lower definitions to Hugr
244
+ from guppylang_internals.compiler.core import CompilerContext
245
+
246
+ ctx = CompilerContext(graph)
247
+ compiled_def = ctx.compile(self.checked[id])
248
+ self.compiled = ctx.compiled
249
+
250
+ if (
251
+ isinstance(compiled_def, CompiledHugrNodeDef)
252
+ and isinstance(compiled_def, CompiledCallableDef)
253
+ and not isinstance(graph.hugr[compiled_def.hugr_node].op, ops.FuncDecl)
254
+ ):
255
+ # if compiling a region set it as the HUGR entrypoint
256
+ # can be loosened after https://github.com/CQCL/hugr/issues/2501 is fixed
257
+ graph.hugr.entrypoint = compiled_def.hugr_node
258
+
259
+ # TODO: Currently the list of extensions is manually managed by the user.
260
+ # We should compute this dynamically from the imported dependencies instead.
261
+ #
262
+ # The hugr prelude and std_extensions are implicit.
263
+ from guppylang_internals.std._internal.compiler.tket_exts import TKET_EXTENSIONS
264
+
265
+ extensions = [
266
+ *TKET_EXTENSIONS,
267
+ guppylang_internals.compiler.hugr_extension.EXTENSION,
268
+ *self.additional_extensions,
269
+ ]
270
+ # TODO replace with computed extensions after https://github.com/CQCL/guppylang/issues/550
271
+ all_used_extensions = [
272
+ *extensions,
273
+ hugr.std.prelude.PRELUDE_EXTENSION,
274
+ hugr.std.collections.array.EXTENSION,
275
+ hugr.std.float.FLOAT_OPS_EXTENSION,
276
+ hugr.std.float.FLOAT_TYPES_EXTENSION,
277
+ hugr.std.int.INT_OPS_EXTENSION,
278
+ hugr.std.int.INT_TYPES_EXTENSION,
279
+ hugr.std.logic.EXTENSION,
280
+ ]
281
+ graph.hugr.module_root.metadata[CoreMetadataKeys.USED_EXTENSIONS.value] = [
282
+ {
283
+ "name": ext.name,
284
+ "version": str(ext.version),
285
+ }
286
+ for ext in all_used_extensions
287
+ ]
288
+ graph.hugr.module_root.metadata[CoreMetadataKeys.GENERATOR.value] = {
289
+ "name": "guppylang",
290
+ "version": guppylang_internals.__version__,
291
+ }
292
+ return ModulePointer(Package(modules=[graph.hugr], extensions=extensions), 0)
293
+
294
+
295
+ ENGINE: CompilationEngine = CompilationEngine()
@@ -0,0 +1,107 @@
1
+ import functools
2
+ import sys
3
+ from collections.abc import Callable, Iterator
4
+ from contextlib import contextmanager
5
+ from dataclasses import dataclass
6
+ from types import TracebackType
7
+ from typing import TYPE_CHECKING, Any, TypeVar, cast
8
+
9
+ if TYPE_CHECKING:
10
+ from guppylang_internals.diagnostic import Error, Fatal
11
+
12
+
13
+ @dataclass
14
+ class GuppyError(Exception):
15
+ """An error that occurs during compilation."""
16
+
17
+ error: "Error | Fatal"
18
+
19
+
20
+ class GuppyTypeError(GuppyError):
21
+ """Special Guppy exception for type errors."""
22
+
23
+
24
+ class GuppyTypeInferenceError(GuppyError):
25
+ """Special Guppy exception for type inference errors."""
26
+
27
+
28
+ class MissingModuleError(Exception):
29
+ """Special Guppy exception for operations that require a guppy module."""
30
+
31
+
32
+ class GuppyComptimeError(Exception):
33
+ """Exception for type and linearity errors that are caught in a comptime context."""
34
+
35
+
36
+ class InternalGuppyError(Exception):
37
+ """Exception for internal problems during compilation."""
38
+
39
+
40
+ ExceptHook = Callable[[type[BaseException], BaseException, TracebackType | None], Any]
41
+
42
+
43
+ @contextmanager
44
+ def exception_hook(hook: ExceptHook) -> Iterator[None]:
45
+ """Sets a custom `excepthook` for the scope of a 'with' block."""
46
+ try:
47
+ # Check if we're inside a jupyter notebook since it uses its own exception
48
+ # hook. If we're in a regular interpreter, this line will raise a `NameError`
49
+ ipython_shell = get_ipython() # type: ignore[name-defined]
50
+
51
+ def ipython_excepthook(
52
+ shell: Any,
53
+ etype: type[BaseException],
54
+ value: BaseException,
55
+ tb: TracebackType | None,
56
+ tb_offset: Any = None,
57
+ ) -> Any:
58
+ return hook(etype, value, tb)
59
+
60
+ old_hook = ipython_shell.CustomTB
61
+ old_exc_tuple = ipython_shell.custom_exceptions
62
+ ipython_shell.set_custom_exc((Exception,), ipython_excepthook)
63
+ yield
64
+ ipython_shell.set_custom_exc(
65
+ old_exc_tuple, lambda shell, *args, **kwargs: old_hook(*args, **kwargs)
66
+ )
67
+ except NameError:
68
+ pass
69
+ else:
70
+ return
71
+
72
+ # Otherwise, override the regular sys.excepthook
73
+ old_hook = sys.excepthook
74
+ sys.excepthook = hook
75
+ yield
76
+ sys.excepthook = old_hook
77
+
78
+
79
+ FuncT = TypeVar("FuncT", bound=Callable[..., Any])
80
+
81
+
82
+ def pretty_errors(f: FuncT) -> FuncT:
83
+ """Decorator to print custom error banners when a `GuppyError` occurs."""
84
+
85
+ def hook(
86
+ excty: type[BaseException], err: BaseException, traceback: TracebackType | None
87
+ ) -> None:
88
+ """Custom `excepthook` that intercepts `GuppyExceptions` for pretty printing."""
89
+ if isinstance(err, GuppyError):
90
+ from guppylang_internals.diagnostic import DiagnosticsRenderer
91
+ from guppylang_internals.engine import DEF_STORE
92
+
93
+ renderer = DiagnosticsRenderer(DEF_STORE.sources)
94
+ renderer.render_diagnostic(err.error)
95
+ sys.stderr.write("\n".join(renderer.buffer))
96
+ sys.stderr.write("\n\nGuppy compilation failed due to 1 previous error\n")
97
+ return
98
+
99
+ # If it's not a GuppyError, fall back to default hook
100
+ sys.__excepthook__(excty, err, traceback)
101
+
102
+ @functools.wraps(f)
103
+ def pretty_errors_wrapped(*args: Any, **kwargs: Any) -> Any:
104
+ with exception_hook(hook):
105
+ return f(*args, **kwargs)
106
+
107
+ return cast(FuncT, pretty_errors_wrapped)
@@ -0,0 +1,92 @@
1
+ from ast import expr
2
+ from dataclasses import dataclass
3
+ from types import TracebackType
4
+ from typing import ClassVar
5
+
6
+ from guppylang_internals.ast_util import AstNode
7
+ from guppylang_internals.checker.errors.generic import UnsupportedError
8
+ from guppylang_internals.diagnostic import Error, Help
9
+ from guppylang_internals.error import GuppyError
10
+
11
+ EXPERIMENTAL_FEATURES_ENABLED = False
12
+
13
+
14
+ class enable_experimental_features:
15
+ """Enables experimental Guppy features.
16
+
17
+ Can be used as a context manager to enable experimental features in a `with` block.
18
+ """
19
+
20
+ def __init__(self) -> None:
21
+ global EXPERIMENTAL_FEATURES_ENABLED
22
+ self.original = EXPERIMENTAL_FEATURES_ENABLED
23
+ EXPERIMENTAL_FEATURES_ENABLED = True
24
+
25
+ def __enter__(self) -> None:
26
+ pass
27
+
28
+ def __exit__(
29
+ self,
30
+ exc_type: type[BaseException] | None,
31
+ exc_val: BaseException | None,
32
+ exc_tb: TracebackType | None,
33
+ ) -> None:
34
+ global EXPERIMENTAL_FEATURES_ENABLED
35
+ EXPERIMENTAL_FEATURES_ENABLED = self.original
36
+
37
+
38
+ class disable_experimental_features:
39
+ """Disables experimental Guppy features.
40
+
41
+ Can be used as a context manager to enable experimental features in a `with` block.
42
+ """
43
+
44
+ def __init__(self) -> None:
45
+ global EXPERIMENTAL_FEATURES_ENABLED
46
+ self.original = EXPERIMENTAL_FEATURES_ENABLED
47
+ EXPERIMENTAL_FEATURES_ENABLED = False
48
+
49
+ def __enter__(self) -> None:
50
+ pass
51
+
52
+ def __exit__(
53
+ self,
54
+ exc_type: type[BaseException] | None,
55
+ exc_val: BaseException | None,
56
+ exc_tb: TracebackType | None,
57
+ ) -> None:
58
+ global EXPERIMENTAL_FEATURES_ENABLED
59
+ EXPERIMENTAL_FEATURES_ENABLED = self.original
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class ExperimentalFeatureError(Error):
64
+ title: ClassVar[str] = "Experimental feature"
65
+ span_label: ClassVar[str] = "{things} are an experimental feature"
66
+ things: str
67
+
68
+ @dataclass(frozen=True)
69
+ class Suggestion(Help):
70
+ message: ClassVar[str] = (
71
+ "Experimental features are currently disabled. You can enable them by "
72
+ "calling `guppylang.enable_experimental_features()`, however note that "
73
+ "these features are unstable and might break in the future."
74
+ )
75
+
76
+ def __post_init__(self) -> None:
77
+ self.add_sub_diagnostic(ExperimentalFeatureError.Suggestion(None))
78
+
79
+
80
+ def check_function_tensors_enabled(node: expr | None = None) -> None:
81
+ if not EXPERIMENTAL_FEATURES_ENABLED:
82
+ raise GuppyError(ExperimentalFeatureError(node, "Function tensors"))
83
+
84
+
85
+ def check_lists_enabled(loc: AstNode | None = None) -> None:
86
+ if not EXPERIMENTAL_FEATURES_ENABLED:
87
+ raise GuppyError(ExperimentalFeatureError(loc, "Lists"))
88
+
89
+
90
+ def check_capturing_closures_enabled(loc: AstNode | None = None) -> None:
91
+ if not EXPERIMENTAL_FEATURES_ENABLED:
92
+ raise GuppyError(UnsupportedError(loc, "Capturing closures"))
@@ -0,0 +1,28 @@
1
+ """Tools for inspecting source code when running in IPython."""
2
+
3
+
4
+ def is_running_ipython() -> bool:
5
+ """Checks if we are currently running in IPython"""
6
+ try:
7
+ return get_ipython() is not None # type: ignore[name-defined]
8
+ except NameError:
9
+ return False
10
+
11
+
12
+ def normalize_ipython_dummy_files(filename: str) -> str:
13
+ """Turns dummy file names generated for IPython cells into readable names like
14
+ "<In[42]>".
15
+
16
+ In vanilla IPython, cells have filenames like "<ipython-input-3-3e9b5833de21>" and
17
+ in Jupyter, cells have filenames like "/var/{...}/ipykernel_82076/61218616.py".
18
+ """
19
+ try:
20
+ if shell := get_ipython(): # type: ignore[name-defined]
21
+ res = shell.compile.format_code_name(filename)
22
+ if res is None:
23
+ return filename
24
+ return f"<{res[1]}>"
25
+ else:
26
+ return filename
27
+ except NameError:
28
+ return filename