From 2e3cf8ae524e6bfdd77c89fede88ff6ac79e706a Mon Sep 17 00:00:00 2001
From: Philip Georgi <110174000+philg314@users.noreply.github.com>
Date: Tue, 23 Aug 2022 16:42:42 +0200
Subject: [PATCH 1/2] Add types

---
 _quickjs.pyi        | 60 +++++++++++++++++++++++++++++++++++++++++++++
 module.c            |  2 +-
 pyproject.toml      |  2 +-
 quickjs/__init__.py | 23 ++++++++---------
 quickjs/py.typed    |  0
 setup.py            | 12 ++++++---
 6 files changed, 82 insertions(+), 17 deletions(-)
 create mode 100644 _quickjs.pyi
 create mode 100644 quickjs/py.typed

diff --git a/_quickjs.pyi b/_quickjs.pyi
new file mode 100644
index 0000000..2755189
--- /dev/null
+++ b/_quickjs.pyi
@@ -0,0 +1,60 @@
+from typing import Any, Callable, Literal, TypedDict
+
+
+def test() -> Literal[1]: ...
+
+JSValue = None | bool | int | float | str | Object
+
+class Object():
+    def json(self) -> str: ...
+    def __call__(self, *args: Any) -> Any: ...
+
+class MemoryDict(TypedDict):
+    malloc_size: int
+    malloc_limit: int
+    memory_used_size: int
+    malloc_count: int
+    memory_used_count: int
+    atom_count: int
+    atom_size: int
+    str_count: int
+    str_size: int
+    obj_count: int
+    obj_size: int
+    prop_count: int
+    prop_size: int
+    shape_count: int
+    shape_size: int
+    js_func_count: int
+    js_func_size: int
+    js_func_code_size: int
+    js_func_pc2line_count: int
+    js_func_pc2line_size: int
+    c_func_count: int
+    array_count: int
+    fast_array_count: int
+    fast_array_elements: int
+    binary_object_count: int
+    binary_object_size: int
+
+class Context:
+    def eval(self, code: str) -> JSValue: ...
+    def module(self, code: str) -> JSValue: ...
+    def execute_pending_job(self) -> bool: ...
+    def parse_json(self, data: str) -> JSValue: ...
+    def get(self, name: str) -> JSValue: ...
+    def set(self, name: str, item: JSValue) -> None: ...
+    def set_memory_limit(self, limit: int) -> None: ...
+    def set_time_limit(self, limit: int) -> None: ...
+    def set_max_stack_size(self, limit: int) -> None: ...
+    def memory(self) -> MemoryDict: ...
+    def gc(self) -> None: ...
+    def add_callable(self, name: str, callable: Callable[..., Any]) -> None: ...
+    @property
+    def globalThis(self) -> Object: ...
+
+class JSException(Exception):
+    pass
+
+class StackOverflow(Exception):
+    pass
diff --git a/module.c b/module.c
index fba11c3..2a46433 100644
--- a/module.c
+++ b/module.c
@@ -642,7 +642,7 @@ static PyObject *runtime_set_max_stack_size(RuntimeData *self, PyObject *args) {
 
 // _quickjs.Context.memory
 //
-// Sets the CPU time limit of the context. This will be used in an interrupt handler.
+// Returns the memory usage as a dict.
 static PyObject *runtime_memory(RuntimeData *self) {
 	PyObject *dict = PyDict_New();
 	if (dict == NULL) {
diff --git a/pyproject.toml b/pyproject.toml
index 61bed85..8791ba3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ description = "Python wrapper around https://bellard.org/quickjs/"
 authors = ["Petter Strandmark"]
 
 [tool.poetry.dependencies]
-python = ">=3.6"
+python = ">=3.7"
 
 [tool.poetry.dev-dependencies]
 yapf = "*"
diff --git a/quickjs/__init__.py b/quickjs/__init__.py
index 4285999..1106441 100644
--- a/quickjs/__init__.py
+++ b/quickjs/__init__.py
@@ -1,7 +1,7 @@
 import concurrent.futures
 import json
 import threading
-from typing import Tuple, Callable
+from typing import Any, Tuple, Callable
 
 import _quickjs
 
@@ -21,8 +21,8 @@ class Function:
     # same runtime, even if it is not at the same time. So we run everything on the same thread in
     # order to prevent this.
     _threadpool = concurrent.futures.ThreadPoolExecutor(max_workers=1)
-    
-    def __init__(self, name: str, code: str, *, own_executor=False) -> None:
+
+    def __init__(self, name: str, code: str, *, own_executor: bool = False) -> None:
         """
         Arguments:
             name: The name of the function in the provided code that will be executed.
@@ -39,21 +39,21 @@ def __init__(self, name: str, code: str, *, own_executor=False) -> None:
         concurrent.futures.wait([future])
         self._context, self._f = future.result()
 
-    def __call__(self, *args, run_gc=True):
+    def __call__(self, *args: Any, run_gc: bool = True) -> Any:
         with self._lock:
             future = self._threadpool.submit(self._call, *args, run_gc=run_gc)
             concurrent.futures.wait([future])
             return future.result()
 
-    def set_memory_limit(self, limit):
+    def set_memory_limit(self, limit: int):
         with self._lock:
             return self._context.set_memory_limit(limit)
 
-    def set_time_limit(self, limit):
+    def set_time_limit(self, limit: int):
         with self._lock:
             return self._context.set_time_limit(limit)
 
-    def set_max_stack_size(self, limit):
+    def set_max_stack_size(self, limit: int):
         with self._lock:
             return self._context.set_max_stack_size(limit)
 
@@ -61,7 +61,7 @@ def memory(self):
         with self._lock:
             return self._context.memory()
 
-    def add_callable(self, global_name: str, callable: Callable) -> None:
+    def add_callable(self, global_name: str, callable: Callable[..., Any]) -> None:
         with self._lock:
             self._context.add_callable(global_name, callable)
 
@@ -86,10 +86,11 @@ def _compile(self, name: str, code: str) -> Tuple[Context, Object]:
         context = Context()
         context.eval(code)
         f = context.get(name)
+        assert isinstance(f, Object)
         return context, f
 
-    def _call(self, *args, run_gc=True):
-        def convert_arg(arg):
+    def _call(self, *args: Any, run_gc: bool = True):
+        def convert_arg(arg: Any):
             if isinstance(arg, (type(None), str, bool, float, int)):
                 return arg
             else:
@@ -99,7 +100,7 @@ def convert_arg(arg):
         try:
             result = self._f(*[convert_arg(a) for a in args])
             if isinstance(result, Object):
-                result = json.loads(result.json())            
+                result = json.loads(result.json())
             return result
         finally:
             if run_gc:
diff --git a/quickjs/py.typed b/quickjs/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/setup.py b/setup.py
index 4aedcbf..9c4941c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,3 @@
-import glob
 import sys
 from typing import List
 
@@ -16,13 +15,13 @@
     #    system PATH when compiling.
     # 3. The code below will moneky-patch distutils to work.
     import distutils.cygwinccompiler
-    distutils.cygwinccompiler.get_msvcr = lambda: [] 
+    distutils.cygwinccompiler.get_msvcr = lambda: []  # type: ignore
     # Make sure that pthreads is linked statically, otherwise we run into problems
     # on computers where it is not installed.
     extra_link_args = ["-static"]
 
 
-def get_c_sources(include_headers=False):
+def get_c_sources(include_headers: bool = False):
     sources = [
         "module.c",
         "upstream-quickjs/cutils.c",
@@ -77,4 +76,9 @@ def get_c_sources(include_headers=False):
       description='Wrapping the quickjs C library.',
       long_description=long_description,
       packages=["quickjs"],
-      ext_modules=[_quickjs])
+      ext_modules=[_quickjs],
+      package_data={
+          "": ["_quickjs.pyi"],
+          "quickjs": ["py.typed"]
+      },
+      include_package_data=True,)

From 50ffc9c50ea2964751df63a37dcc2ded8613a024 Mon Sep 17 00:00:00 2001
From: Philip Georgi <110174000+philg314@users.noreply.github.com>
Date: Thu, 25 Aug 2022 23:53:51 +0200
Subject: [PATCH 2/2] Remove typed memory dict

---
 _quickjs.pyi | 32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)

diff --git a/_quickjs.pyi b/_quickjs.pyi
index 2755189..17b3cc9 100644
--- a/_quickjs.pyi
+++ b/_quickjs.pyi
@@ -1,4 +1,4 @@
-from typing import Any, Callable, Literal, TypedDict
+from typing import Any, Callable, Literal
 
 
 def test() -> Literal[1]: ...
@@ -9,34 +9,6 @@ class Object():
     def json(self) -> str: ...
     def __call__(self, *args: Any) -> Any: ...
 
-class MemoryDict(TypedDict):
-    malloc_size: int
-    malloc_limit: int
-    memory_used_size: int
-    malloc_count: int
-    memory_used_count: int
-    atom_count: int
-    atom_size: int
-    str_count: int
-    str_size: int
-    obj_count: int
-    obj_size: int
-    prop_count: int
-    prop_size: int
-    shape_count: int
-    shape_size: int
-    js_func_count: int
-    js_func_size: int
-    js_func_code_size: int
-    js_func_pc2line_count: int
-    js_func_pc2line_size: int
-    c_func_count: int
-    array_count: int
-    fast_array_count: int
-    fast_array_elements: int
-    binary_object_count: int
-    binary_object_size: int
-
 class Context:
     def eval(self, code: str) -> JSValue: ...
     def module(self, code: str) -> JSValue: ...
@@ -47,7 +19,7 @@ class Context:
     def set_memory_limit(self, limit: int) -> None: ...
     def set_time_limit(self, limit: int) -> None: ...
     def set_max_stack_size(self, limit: int) -> None: ...
-    def memory(self) -> MemoryDict: ...
+    def memory(self) -> dict[str, int]: ...
     def gc(self) -> None: ...
     def add_callable(self, name: str, callable: Callable[..., Any]) -> None: ...
     @property