diff --git a/.readthedocs.yml b/.readthedocs.yml index 04f9f5fa8..c10334185 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -18,4 +18,4 @@ build: os: ubuntu-20.04 tools: python: "3.11" - nodejs: "14" + nodejs: "20" diff --git a/docs/project/release-notes/v0.17.0.md b/docs/project/release-notes/v0.17.0.md index ea906262d..ac59edd06 100644 --- a/docs/project/release-notes/v0.17.0.md +++ b/docs/project/release-notes/v0.17.0.md @@ -21,7 +21,7 @@ issues and that future releases will have fewer breaking changes. We added full support for asyncio, including a new Python event loop that schedules tasks on the browser event loop, support for top level await in {any}`pyodide.runPythonAsync`, and implementations of `await` for {any}`JsProxy ` -and {js:class}`PyProxy`, so that it is possible to await a Python awaitable from +and `PyProxy`, so that it is possible to await a Python awaitable from JavaScript and a JavaScript thenable from Python. This allows seamless interoperability: @@ -99,7 +99,7 @@ We added several new conversion APIs to give more explicit control over the foreign function interface. In particular, the goal was to make it easier for users to avoid leaking memory. -For the basic use cases, we have {js:meth}`PyProxy.toJs` and `JsProxy.to_py` +For the basic use cases, we have `PyProxy.toJs` and `JsProxy.to_py` which respectively convert Python objects to JavaScript objects and JavaScript objects to Python objects. We also added also "wrong-way" conversion functions {any}`pyodide.to_js ` and {any}`pyodide.toPy` which are particularly helpful for @@ -110,7 +110,7 @@ The promise handler methods `JsProxy.then`, `JsProxy.catch`, and `JsProxy.finally_` were particularly hard to use without leaking memory so they have been updated with internal magic to automatically manage the memory. -For more advanced use cases where control over the life cycle of a {js:class}`PyProxy` is +For more advanced use cases where control over the life cycle of a `PyProxy` is needed, there are {any}`create_proxy` and {any}`create_once_callable`. (Added in PRs {pr}`1186`, {pr}`1244`, {pr}`1344`, {pr}`1436`) @@ -150,7 +150,7 @@ The buffer translation code in previous versions was less flexible, leaked memor and had serious bugs including use after free ({issue}`749`) and buffer overflow errors. We completely reworked these: buffers are now proxied like most other objects. -In simple use cases they can be converted with a copy using {js:meth}`PyProxy.toJs` +In simple use cases they can be converted with a copy using `PyProxy.toJs` and `JsProxy.to_py`. We added new APIs `PyProxy.getBuffer`, `JsProxy.assign`, and `JsProxy.assign_to` which give more fine-grained control, though they are not yet as ergonomic as they could be. diff --git a/docs/requirements-doc.txt b/docs/requirements-doc.txt index 85b0a6ff4..6dd39ae14 100644 --- a/docs/requirements-doc.txt +++ b/docs/requirements-doc.txt @@ -8,7 +8,6 @@ sphinx_book_theme>=0.4.0rc1 # A dependency of the above theme, which had some breaking changes in 0.13.2 pydata_sphinx_theme < 0.13.2 sphinx-issues -sphinx-js>=3.2.1 sphinx-click sphinx-autodoc-typehints>=1.21.7 sphinx-design>=0.3.0 @@ -19,3 +18,4 @@ pydantic micropip==0.2.2 jinja2>=3.0 ruamel.yaml +sphinx-js @ git+https://github.com/pyodide/sphinx-js-fork@3c02e364d3444c9c75ca68419d71237b61f3f5d8 diff --git a/docs/sphinx_pyodide/sphinx_pyodide/__init__.py b/docs/sphinx_pyodide/sphinx_pyodide/__init__.py index 8291e391a..a59316be3 100644 --- a/docs/sphinx_pyodide/sphinx_pyodide/__init__.py +++ b/docs/sphinx_pyodide/sphinx_pyodide/__init__.py @@ -2,6 +2,9 @@ from .jsdoc import ( PyodideAnalyzer, get_jsdoc_content_directive, get_jsdoc_summary_directive, + ts_post_convert, + ts_should_destructure_arg, + ts_xref_formatter, ) from .lexers import HtmlPyodideLexer, PyodideLexer from .mdn_xrefs import add_mdn_xrefs @@ -12,36 +15,6 @@ def wrap_analyzer(app): app._sphinxjs_analyzer = PyodideAnalyzer(app._sphinxjs_analyzer) -def patch_templates(): - """Patch in a different jinja2 loader so we can override templates with our - own versions. - """ - from pathlib import Path - - from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PackageLoader - from sphinx_js.analyzer_utils import dotted_path - from sphinx_js.renderers import JsRenderer - - loader = ChoiceLoader( - [ - FileSystemLoader(Path(__file__).parent / "templates"), - PackageLoader("sphinx_js", "templates"), - ] - ) - env = Environment(loader=loader) - - def patched_rst_method(self, partial_path, obj, use_short_name=False): - """Return rendered RST about an entity with the given name and IR - object.""" - dotted_name = partial_path[-1] if use_short_name else dotted_path(partial_path) - - # Render to RST using Jinja: - template = env.get_template(self._template) - return template.render(**self._template_vars(dotted_name, obj)) - - JsRenderer.rst = patched_rst_method - - def fix_pyodide_ffi_path(): """ The `pyodide.ffi` stuff is defined in `_pyodide._core_docs`. We don't want @@ -80,7 +53,6 @@ def remove_property_prefix(): def setup(app): - patch_templates() fix_pyodide_ffi_path() remove_property_prefix() app.add_lexer("pyodide", PyodideLexer) @@ -91,3 +63,7 @@ def setup(app): app.add_directive("js-doc-content", get_jsdoc_content_directive(app)) app.add_directive("pyodide-package-list", get_packages_summary_directive(app)) app.connect("builder-inited", add_mdn_xrefs) + app.config.ts_post_convert = ts_post_convert + app.config.ts_should_destructure_arg = ts_should_destructure_arg + app.config.ts_type_xref_formatter = ts_xref_formatter + app.config.ts_type_bold = True diff --git a/docs/sphinx_pyodide/sphinx_pyodide/jsdoc.py b/docs/sphinx_pyodide/sphinx_pyodide/jsdoc.py index 604a7f08f..ef7641153 100644 --- a/docs/sphinx_pyodide/sphinx_pyodide/jsdoc.py +++ b/docs/sphinx_pyodide/sphinx_pyodide/jsdoc.py @@ -1,20 +1,17 @@ -import re from collections import OrderedDict from typing import Any -import docutils.parsers.rst.directives as directives from docutils import nodes from docutils.parsers.rst import Directive from docutils.parsers.rst import Parser as RstParser from docutils.statemachine import StringList from docutils.utils import new_document from sphinx import addnodes -from sphinx.domains.javascript import JavaScriptDomain, JSCallable from sphinx.ext.autosummary import autosummary_table, extract_summary from sphinx.util import rst from sphinx.util.docutils import switch_source_input -from sphinx_js.ir import Class, Function, Interface, Pathname -from sphinx_js.parsers import PathVisitor, path_and_formal_params +from sphinx_js import ir, typedoc +from sphinx_js.ir import Class, Function, Interface from sphinx_js.renderers import ( AutoAttributeRenderer, AutoClassRenderer, @@ -22,412 +19,118 @@ from sphinx_js.renderers import ( JsRenderer, ) from sphinx_js.typedoc import Analyzer as TsAnalyzer -from sphinx_js.typedoc import make_path_segments +from sphinx_js.typedoc import Base, Callable, Converter, ReflectionType -_orig_convert_node = TsAnalyzer._convert_node -_orig_constructor_and_members = TsAnalyzer._constructor_and_members -_orig_top_level_properties = TsAnalyzer._top_level_properties -_orig_convert_all_nodes = TsAnalyzer._convert_all_nodes - - -def _constructor_and_members(self, cls): - result = _orig_constructor_and_members(self, cls) - for tag in cls.get("comment", {}).get("tags", []): - if tag["tag"] == "hideconstructor": - return (None, result[1]) - return result - - -TsAnalyzer._constructor_and_members = _constructor_and_members - -commentdict = {} - -FFI_FIELDS: set[str] = set() - - -def _convert_all_nodes(self, root): - for node in root.get("children", []): - if node["name"] == "ffi": - FFI_FIELDS.update(x["name"] for x in node["children"]) - FFI_FIELDS.remove("ffi") - break - return _orig_convert_all_nodes(self, root) - - -TsAnalyzer._convert_all_nodes = _convert_all_nodes - - -def _top_level_properties(self, node): - if "comment" not in node: - sig = {} - if "getSignature" in node: - sig = node["getSignature"][0] - elif "signatures" in node: - sig = node["signatures"][0] - node["comment"] = sig.get("comment", {}) - path = str(Pathname(make_path_segments(node, self._base_dir))) - commentdict[path] = node.get("comment") or {} - result = _orig_top_level_properties(self, node) - return result - - -def get_tag(doclet, tag): - tags = commentdict[str(doclet.path)].get("tags", []) - for t in tags: - if t["tag"] == tag: - return True, t["text"] - return False, None +# Custom tags are a great way of conveniently passing information from the +# source code to this file. No custom tags will be seen by this code unless they +# are registered in src/js/tsdoc.json +# +# Modifier tags act like a flag, block tags have content. def has_tag(doclet, tag): - return get_tag(doclet, tag)[0] - - -TsAnalyzer._top_level_properties = _top_level_properties - -orig_JsRenderer_rst_ = JsRenderer.rst - - -def JsRenderer_rst(self, partial_path, obj, use_short_name=False): - match get_tag(obj, "deprecated"): - case (True, text): - # This is definitely not unreachable... - if not text.strip(): # type: ignore[unreachable] - obj.deprecated = True - else: - obj.deprecated = text - if has_tag(obj, "hidetype"): - obj.type = "" - return orig_JsRenderer_rst_(self, partial_path, obj, use_short_name) - - -for cls in [AutoAttributeRenderer, AutoFunctionRenderer, AutoClassRenderer]: - cls.rst = JsRenderer_rst - - -def destructure_param(param: dict[str, Any]) -> list[dict[str, Any]]: - """We want to document a destructured argument as if it were several - separate arguments. This finds complex inline object types in the arguments - list of a function and "destructures" them into separately documented arguments. - - E.g., a function - - /** - * @param options - */ - function f({x , y } : { - /** The x value */ - x : number, - /** The y value */ - y : string - }){ ... } - - should be documented like: - - options.x (number) The x value - options.y (number) The y value + """Detects whether the doclet comes from a node that has the given modifier + tag. """ - decl = param["type"]["declaration"] - result = [] - for child in decl["children"]: - child = dict(child) - if "type" not in child: - if "signatures" in child: - try: - child["comment"] = child["signatures"][0]["comment"] - except KeyError: - # TODO: handle no comment case - pass - child["type"] = { - "type": "reflection", - "declaration": dict(child), - } - else: - raise AssertionError("Didn't expect to get here...") - child["name"] = param["name"] + "." + child["name"] - result.append(child) - return result + return ("@" + tag) in doclet.modifier_tags -def fix_up_inline_object_signature(self: TsAnalyzer, node: dict[str, Any]) -> None: - """Calls get_destructured_children on inline object types""" - kind = node.get("kindString") - if kind not in ["Call signature", "Constructor signature"]: - return - params = node.get("parameters", []) - new_params = [] - for param in params: - if "@ignore" in param.get("comment", {}).get("shortText", ""): - if not param.get("flags", {}).get("isOptional"): - print("sphinx-pyodide warning: Hiding mandatory argument!") - continue - param_type = param["type"] - if ( - param_type["type"] != "reflection" - or "children" not in param_type["declaration"] - ): - new_params.append(param) - else: - new_params.extend(destructure_param(param)) - node["parameters"] = new_params +def member_properties(self): + return dict( + is_abstract=self.flags.isAbstract, + is_optional=self.flags.isOptional, + is_static=self.flags.isStatic, + is_private=self.flags.isPrivate or self.flags.isExternal, + ) -def _convert_node(self: TsAnalyzer, node: dict[str, Any]) -> Any: - """Monkey patch for TsAnalyzer._convert_node. - - Fixes two crashes and separates documentation for destructured object - arguments into a series of separate argument entries. - """ - kind = node.get("kindString") - # if a class has no documented constructor, don't crash - if kind in ["Function", "Constructor", "Method"] and not node.get("sources"): - return None, [] - # This fixes a crash, not really sure what it means. - node["extendedTypes"] = [t for t in node.get("extendedTypes", []) if "id" in t] - # See docstring for destructure_param - fix_up_inline_object_signature(self, node) - converted, more_todo = _orig_convert_node(self, node) - if not converted: - return converted, more_todo - converted.is_private = node.get("flags", {}).get("isPrivate", False) - if kind in ["Call signature", "Constructor signature"]: - tags = node.get("comment", {}).get("tags", []) - converted.examples = [tag["text"] for tag in tags if tag["tag"] == "example"] - return converted, more_todo +Base.member_properties = member_properties -TsAnalyzer._convert_node = _convert_node - -from os.path import relpath +def ts_should_destructure_arg(sig, param): + return param.name == "options" -def _containing_deppath(self, node): - """Return the path pointing to the module containing the given node. - The path is absolute or relative to `root_for_relative_js_paths`. - Raises ValueError if one isn't found. +def ts_post_convert(converter, node, doclet): + doclet.exported_from = None + doclet.name = doclet.name.replace("Symbol․Symbol․", "Symbol․") - """ - from pathlib import Path + if has_tag(doclet, "hidetype"): + doclet.type = "" + if isinstance(node, typedoc.Callable): + node.signatures[0].type = "" - filename = node["sources"][0]["fileName"].replace(".gen", "") - deppath = next(Path(self._base_dir).glob("**/" + filename), None) - if deppath: - return relpath(deppath, self._base_dir) - return "" + if isinstance(doclet, ir.Class) and has_tag(doclet, "hideconstructor"): + doclet.constructor = None + + if node.name == "setStdin": + fix_set_stdin(converter, node, doclet) + + if node.name == "mountNativeFS": + fix_native_fs(converter, node, doclet) -TsAnalyzer._containing_deppath = _containing_deppath +def fix_set_stdin(converter, node, doclet): + assert isinstance(node, Callable) + options = node.signatures[0].parameters[0] + assert isinstance(options.type, ReflectionType) + for param in options.type.declaration.children: + if param.name == "stdin": + break + target = converter.index[param.type.target] + for docparam in doclet.params: + if docparam.name == "stdin": + break + docparam.type = target.type.render_name(converter) -def _add_type_role(self, name): +def fix_native_fs(converter, node, doclet): + assert isinstance(node, Callable) + ty = node.signatures[0].type + target = converter.index[ty.typeArguments[0].target] + ty.typeArguments[0] = target.type + doclet.returns[0].type = ty.render_name(converter) + + +orig_convert_all_nodes = Converter.convert_all_nodes + + +def locate_ffi_fields(root): + for node in root.children: + if node.name == "js/ffi": + break + for child in node.children: + if child.name == "ffi": + break + fields = child.type.declaration.children + FFI_FIELDS.update(x.name for x in fields) + + +# locate the ffi fields +FFI_FIELDS: set[str] = set() + + +def convert_all_nodes(self, root): + locate_ffi_fields(root) + return orig_convert_all_nodes(self, root) + + +Converter.convert_all_nodes = convert_all_nodes + + +def ts_xref_formatter(self, xref): from sphinx_pyodide.mdn_xrefs import JSDATA + name = xref.name + if name == "PyodideInterface": + return ":ref:`PyodideInterface `" if name in JSDATA: - return f":js:data:`{name}`" - if name in FFI_FIELDS: - return f":js:class:`~pyodide.ffi.{name}`" - return f":js:class:`{name}`" - - -def object_literal_type_name(self, decl): - """This renders the names of object literal types. - - They have zero or more "children" and zero or one "indexSignatures". - For example: - - { - [key: string]: string, - name : string, - id : string - } - - has children "name" and "id" and an indexSignature "[key: string]: string" - """ - children = [] - if "indexSignature" in decl: - index_sig = decl["indexSignature"] - assert len(index_sig["parameters"]) == 1 - key = index_sig["parameters"][0] - keyname = key["name"] - keytype = self._type_name(key["type"]) - valuetype = self._type_name(index_sig["type"]) - children.append(rf"\ **[{keyname}:** {keytype}\ **]:** {valuetype}") - if "children" in decl: - for child in decl["children"]: - maybe_optional = "" - if child["flags"].get("isOptional"): - maybe_optional = "?" - if child["kindString"] == "Method": - child_type_name = self.function_type_name(child) - else: - child_type_name = self._type_name(child["type"]) - children.append( - r"\ **" + child["name"] + maybe_optional + ":** " + child_type_name - ) - - return r"\ **{**\ " + r"\ **,** ".join(children) + r"\ **}**\ " - - -def function_type_name(self, decl): - decl_sig = None - if "signatures" in decl: - decl_sig = decl["signatures"][0] - elif decl["kindString"] == "Call signature": - decl_sig = decl - assert decl_sig - params = [ - rf'\ **{ty["name"]}:** {self._type_name(ty["type"])}' - for ty in decl_sig.get("parameters", []) - ] - params_str = r"\ **,** ".join(params) - ret_str = self._type_name(decl_sig["type"]) - return rf"\ **(**\ {params_str}\ **) =>** {ret_str}" - - -def reflection_type_name(self, type): - """Fill in the type names for type_of_type == "reflection" - - This is left as a TODO in sphinx-js. - - There are a couple of options: if - - decl["kindString"] == "Type Literal" - - then this is a literal object type. At least we assume it's a literal object - type, maybe there are other ways for that to happen. - - Otherwise, we assume that it's a function type, which we want to format - like: - - (a : string, b : number) => string - """ - decl = type["declaration"] - if decl["kindString"] == "Type literal" and "signatures" not in decl: - return self.object_literal_type_name(decl) - return self.function_type_name(decl) - - -def _type_name_root(self, type): - type_of_type = type.get("type") - - if type_of_type == "reference" and type.get("id"): - node = self._index[type["id"]] - name = node["name"] - if node.get("flags", {}).get("isPrivate") and "type" in node: - return self._type_name(node["type"]) - return self._add_type_role(name) - if type_of_type == "unknown": - if re.match(r"-?\d*(\.\d+)?", type["name"]): # It's a number. - # TypeDoc apparently sticks numeric constants' values into - # the type name. String constants? Nope. Function ones? Nope. - return "number" - return self._add_type_role(type["name"]) - if type_of_type in ["intrinsic", "reference"]: - return self._add_type_role(type["name"]) - if type_of_type == "stringLiteral": - return '"' + type["value"] + '"' - if type_of_type == "array": - return self._type_name(type["elementType"]) + r"\ **[]**" - if type_of_type == "tuple" and type.get("elements"): - types = [self._type_name(t) for t in type["elements"]] - return r"\ **[**\ " + r"\ **,** ".join(types) + r"\ **]** " - if type_of_type == "union": - return r" **|** ".join(self._type_name(t) for t in type["types"]) - if type_of_type == "intersection": - return " **&** ".join(self._type_name(t) for t in type["types"]) - if type_of_type == "typeOperator": - return type["operator"] + " " + self._type_name(type["target"]) - # e.g. "keyof T" - if type_of_type == "typeParameter": - name = type["name"] - constraint = type.get("constraint") - if constraint is not None: - name += " extends " + self._type_name(constraint) - # e.g. K += extends + keyof T - return name - if type_of_type == "reflection": - return self.reflection_type_name(type) - if type_of_type == "named-tuple-member": - name = type["name"] - type = self._type_name(type["element"]) - return rf"\ **{name}:** {type}" - if type_of_type == "predicate": - return ( - f":js:data:`boolean` (typeguard for {self._type_name(type['targetType'])})" - ) - if type_of_type == "literal" and type["value"] is None: - return ":js:data:`null`" - if type_of_type == "query": - return f"``typeof {type['queryType']['name']}``" - return "" - - -def _type_name(self, type): - """Return a string description of a type. - - :arg type: A TypeDoc-emitted type node - - """ - name = self._type_name_root(type) - - type_args = type.get("typeArguments") - if type_args: - arg_names = ", ".join(self._type_name(arg) for arg in type_args) - name += rf"\ **<**\ {arg_names}\ **>** " - return name - - -for obj in [ - _add_type_role, - object_literal_type_name, - reflection_type_name, - _type_name_root, - _type_name, - function_type_name, -]: - setattr(TsAnalyzer, obj.__name__, obj) - - -def _param_type_formatter(param): - """Generate types for function parameters specified in field.""" - if not param.type: - return None - heads = ["type", param.name] - tail = param.type - return heads, tail - - -def _return_formatter(return_): - """Derive heads and tail from ``@returns`` blocks.""" - tail = ("%s -- " % return_.type) if return_.type else "" - tail += return_.description - return ["returns"], tail - - -import sphinx_js.renderers - -for obj in [_param_type_formatter, _return_formatter]: # type:ignore[assignment] - setattr(sphinx_js.renderers, obj.__name__, obj) - - -class JSFuncMaybeAsync(JSCallable): - option_spec = { - **JSCallable.option_spec, - "async": directives.flag, - } - - def get_display_prefix( - self, - ): - if "async" in self.options: - return [ - addnodes.desc_sig_keyword("async", "async"), - addnodes.desc_sig_space(), - ] - return [] - - -JavaScriptDomain.directives["function"] = JSFuncMaybeAsync + result = f":js:data:`{name}`" + elif name in FFI_FIELDS: + result = f":js:class:`~pyodide.ffi.{name}`" + else: + result = f":js:class:`{name}`" + return result def flatten_suffix_tree(tree): @@ -478,13 +181,6 @@ class PyodideAnalyzer: def __getattr__(self, key): return getattr(self.inner, key) - def longname_to_path(self, name): - """Convert the longname field produced by jsdoc to a path appropriate to use - with _sphinxjs_analyzer.get_object. Based on: - https://github.com/mozilla/sphinx-js/blob/3.1/sphinx_js/jsdoc.py#L181 - """ - return PathVisitor().visit(path_and_formal_params["path"].parse(name)) - def set_doclet_is_private(self, key, doclet): if getattr(doclet, "is_private", False): return @@ -493,6 +189,10 @@ class PyodideAnalyzer: key = [x for x in key if "/" not in x] filename = key[0] toplevelname = key[1] + if doclet.name == "PyodideAPI": + doclet.is_private = True + return + if key[-1].startswith("$"): doclet.is_private = True return @@ -536,14 +236,10 @@ class PyodideAnalyzer: if doclet.is_private: continue + key = [x for x in key if "/" not in x] filename = key[0] toplevelname = key[1] doclet.name = doclet.name.rpartition(".")[2] - if doclet.name.startswith("["): - # a symbol. - # \u2024 looks like a period but is not a period. - # This isn't ideal, but otherwise the coloring is weird. - doclet.name = "[Symbol\u2024" + doclet.name[1:] if filename == "pyodide.": items["globalThis"].append(doclet) @@ -594,21 +290,15 @@ class PyodideAnalyzer: for key, value in items.items(): for obj in sorted(value, key=attrgetter("name")): - _, kind = get_tag(obj, "dockind") + kind = obj.block_tags.get("dockind", [None])[0] if kind: - obj.kind = kind + obj.kind = kind[0].text elif isinstance(obj, Class): obj.kind = "class" elif isinstance(obj, Function): obj.kind = "function" else: obj.kind = "attribute" - - obj.async_ = False - if isinstance(obj, Function): - obj.async_ = obj.returns and obj.returns[0].type.startswith( - ":js:class:`Promise`" - ) self.js_docs[key][obj.kind].append(obj) @@ -635,19 +325,8 @@ def get_jsdoc_content_directive(app): cls = AutoAttributeRenderer renderer = cls(self, app, arguments=["dummy"], options={"members": ["*"]}) rst = renderer.rst([obj.name], obj, use_short_name=False) - if obj.async_: - rst = self.add_async_option_to_rst(rst) return rst - def add_async_option_to_rst(self, rst: str) -> str: - rst_lines = rst.split("\n") - try: - index = next(i for i, ln in enumerate(rst_lines) if ln.startswith("..")) - except StopIteration: - index = len(rst_lines) - 1 - rst_lines.insert(index + 1, " :async:") - return "\n".join(rst_lines) - def get_rst_for_group(self, objects): return [self.get_rst(obj) for obj in objects] @@ -739,10 +418,16 @@ def get_jsdoc_summary_directive(app): """ sig = self.get_sig(obj) display_name = obj.name - prefix = "**async** " if obj.async_ else "" - summary = self.extract_summary(obj.description) + prefix = "**async** " if getattr(obj, "is_async", False) else "" + qualifier = "any" + if obj.name == "ffi": + qualifier = "js:mod" + + summary = self.extract_summary( + JsRenderer.render_description(None, obj.description) + ) link_name = pkgname + "." + display_name - return (prefix, display_name, sig, summary, link_name) + return (prefix, qualifier, display_name, sig, summary, link_name) def get_summary_table(self, pkgname, group): """Get the data for a summary tget_summary_tableable. Return value @@ -791,9 +476,8 @@ def get_jsdoc_summary_directive(app): row.append(nodes.entry("", node)) body.append(row) - for prefix, name, sig, summary, real_name in items: + for prefix, qualifier, name, sig, summary, real_name in items: # The body of this loop is changed from copied code. - qualifier = "any" sig = rst.escape(sig) if sig: sig = f"**{sig}**" @@ -824,7 +508,7 @@ def get_jsdoc_summary_directive(app): name = name.removeprefix("~") _, obj, *_ = self.import_by_name(name, prefixes=prefixes) prefix = "**async** " if iscoroutinefunction(obj) else "" - new_items.append((prefix, *item)) + new_items.append((prefix, "any", *item)) return new_items Autosummary.get_items = get_items diff --git a/docs/sphinx_pyodide/sphinx_pyodide/mdn_xrefs.py b/docs/sphinx_pyodide/sphinx_pyodide/mdn_xrefs.py index 35779d60e..85950d353 100644 --- a/docs/sphinx_pyodide/sphinx_pyodide/mdn_xrefs.py +++ b/docs/sphinx_pyodide/sphinx_pyodide/mdn_xrefs.py @@ -13,11 +13,14 @@ DATA = { "Reflect.ownKeys": "$global/", "Array.from": "$global/", "Atomics.wait": "$global/", + "getDirectory": "API/StorageManager/", + "showDirectoryPicker": "API/Window/", }, "js:class": { "Array": "$global/", "NodeList": "API/", "HTMLCollection": "API/", + "HTMLCanvasElement": "API/", "Generator": "$global/", "AsyncGenerator": "$global/", "Date": "$global/", @@ -56,6 +59,15 @@ DATA = { "Function.bind": "$global/", "Function.call": "$global/", "Array.join": "$global/", + "Array.copyWithin": "$global/", + "Array.fill": "$global/", + "Array.pop": "$global/", + "Array.push": "$global/", + "Array.reverse": "$global/", + "Array.shift": "$global/", + "Array.sort": "$global/", + "Array.splice": "$global/", + "Array.unshift": "$global/", "Array.slice": "$global/", "Array.lastIndexOf": "$global/", "Array.indexOf": "$global/", diff --git a/docs/sphinx_pyodide/sphinx_pyodide/templates/attribute.rst b/docs/sphinx_pyodide/sphinx_pyodide/templates/attribute.rst deleted file mode 100644 index 065c88dae..000000000 --- a/docs/sphinx_pyodide/sphinx_pyodide/templates/attribute.rst +++ /dev/null @@ -1,21 +0,0 @@ -{% import 'common.rst' as common %} - -.. js:attribute:: {{ name }}{{ '?' if is_optional else '' }} - - {{ common.deprecated(deprecated)|indent(3) }} - - {% if type -%} - .. rst-class:: js attribute type - - type: {{ type|indent(3) }} - {%- endif %} - - {% if description -%} - {{ description|indent(3) }} - {%- endif %} - - {{ common.examples(examples)|indent(3) }} - - {{ content|indent(3) }} - - {{ common.see_also(see_also)|indent(3) }} diff --git a/docs/sphinx_pyodide/tests/test_directives.py b/docs/sphinx_pyodide/tests/test_directives.py index cd885200f..c410abbf8 100644 --- a/docs/sphinx_pyodide/tests/test_directives.py +++ b/docs/sphinx_pyodide/tests/test_directives.py @@ -13,6 +13,7 @@ if not hasattr(inspect, "getargspec"): from sphinx_js.suffix_tree import SuffixTree from sphinx_js.typedoc import Analyzer as TsAnalyzer +from sphinx_js.typedoc import Project test_directory = Path(__file__).resolve().parent sys.path.append(str(test_directory.parent)) @@ -25,18 +26,28 @@ src_dir = test_directory.parents[2] / "src" # typedoc src/js/*.ts --tsconfig src/js/tsconfig.json --json docs/sphinx_pyodide/tests/tsdoc_dump.json # gzip docs/sphinx_pyodide/tests/tsdoc_dump.json # rm src/js/pyproxy.gen.ts -with gzip.open(test_directory / "tsdoc_dump.json.gz") as fh: - jsdoc_json = json.load(fh) -settings_json = json.loads((test_directory / "app_settings.json").read_text()) + from sphinx_pyodide.jsdoc import ( PyodideAnalyzer, flatten_suffix_tree, get_jsdoc_content_directive, get_jsdoc_summary_directive, + ts_post_convert, + ts_should_destructure_arg, + ts_xref_formatter, ) -inner_analyzer = TsAnalyzer(jsdoc_json, str(src_dir)) +with gzip.open(test_directory / "tsdoc_dump.json.gz") as fh: + jsdoc_json = Project.parse_raw(fh.read()) +settings_json = json.loads((test_directory / "app_settings.json").read_text()) + +inner_analyzer = TsAnalyzer( + jsdoc_json, + str(src_dir), + post_convert=ts_post_convert, + should_destructure_arg=ts_should_destructure_arg, +) settings = OptionParser().get_default_values() settings.update(settings_json, OptionParser()) @@ -60,9 +71,14 @@ def test_flatten_suffix_tree(): assert d == r +class dummy_config: + ts_type_xref_formatter = ts_xref_formatter + + class dummy_app: _sphinxjs_analyzer = pyodide_analyzer document = document + config = dummy_config class dummy_state: @@ -83,7 +99,7 @@ def test_pyodide_analyzer(): "registerJsModule", "runPython", "runPythonAsync", - "setDefaultStdout", + "setDebug", "setInterruptBuffer", "setStderr", "setStdin", @@ -97,6 +113,8 @@ def test_pyodide_analyzer(): "ERRNO_CODES", "FS", "PATH", + "canvas", + "ffi", "version", "globals", "loadedPackages", @@ -171,11 +189,12 @@ def test_summary(): functions = jsdoc_summary.get_summary_table( "pyodide", dummy_app._sphinxjs_analyzer.js_docs["pyodide"]["function"] ) - globals = {t[1]: t for t in globals} - attributes = {t[1]: t for t in attributes} - functions = {t[1]: t for t in functions} + globals = {t[2]: t for t in globals} + attributes = {t[2]: t for t in attributes} + functions = {t[2]: t for t in functions} assert globals["loadPyodide"] == ( "**async** ", + "any", "loadPyodide", "(options)", "Load the main Pyodide wasm module and initialize it.", @@ -184,13 +203,15 @@ def test_summary(): assert attributes["pyodide_py"] == ( "", + "any", "pyodide_py", "", - "An alias to the Python :py:mod:`pyodide` package.", + "An alias to the Python :ref:`pyodide ` package.", "pyodide.pyodide_py", ) assert attributes["version"] == ( "", + "any", "version", "", "The Pyodide version.", @@ -198,6 +219,7 @@ def test_summary(): ) assert attributes["loadedPackages"] == ( "", + "any", "loadedPackages", "", "The list of packages that Pyodide has loaded.", @@ -206,149 +228,7 @@ def test_summary(): assert functions["loadPackagesFromImports"][:-2] == ( "**async** ", + "any", "loadPackagesFromImports", "(code, options)", ) - - -def test_type_name(): - tn = inner_analyzer._type_name - assert tn({"name": "void", "type": "intrinsic"}) == ":js:data:`void`" - assert tn({"value": None, "type": "literal"}) == ":js:data:`null`" - assert ( - tn( - { - "name": "Promise", - "type": "reference", - "typeArguments": [{"name": "string", "type": "intrinsic"}], - } - ).strip() - == r":js:class:`Promise`\ **<**\ :js:data:`string`\ **>**" - ) - - assert ( - tn( - { - "asserts": False, - "name": "jsobj", - "targetType": {"name": "PyProxy", "type": "reference"}, - "type": "predicate", - } - ).strip() - == ":js:data:`boolean` (typeguard for :js:class:`PyProxy`)" - ) - - assert ( - tn( - { - "declaration": { - "kindString": "Method", - "name": "messageCallback", - "signatures": [ - { - "kindString": "Call signature", - "name": "messageCallback", - "parameters": [ - { - "flags": {}, - "kindString": "Parameter", - "name": "message", - "type": {"name": "string", "type": "intrinsic"}, - } - ], - "type": {"name": "void", "type": "intrinsic"}, - } - ], - }, - "type": "reflection", - } - ).strip() - == r"\ **(**\ \ **message:** :js:data:`string`\ **) =>** :js:data:`void`" - ) - - assert ( - tn( - { - "name": "Iterable", - "type": "reference", - "typeArguments": [ - { - "elements": [ - { - "element": {"name": "string", "type": "intrinsic"}, - "isOptional": False, - "name": "key", - "type": "named-tuple-member", - }, - { - "element": {"name": "any", "type": "intrinsic"}, - "isOptional": False, - "name": "value", - "type": "named-tuple-member", - }, - ], - "type": "tuple", - } - ], - } - ).strip() - == r":js:data:`Iterable`\ **<**\ \ **[**\ \ **key:** :js:data:`string`\ **,** \ **value:** :js:data:`any`\ **]** \ **>**" - ) - - assert ( - tn( - { - "declaration": { - "flags": {}, - "indexSignature": { - "flags": {}, - "kindString": "Index signature", - "parameters": [ - { - "flags": {}, - "name": "key", - "type": {"name": "string", "type": "intrinsic"}, - } - ], - "type": {"name": "string", "type": "intrinsic"}, - }, - "kindString": "Type literal", - }, - "type": "reflection", - } - ).strip() - == r"""\ **{**\ \ **[key:** :js:data:`string`\ **]:** :js:data:`string`\ **}**\ """.strip() - ) - - assert ( - tn( - { - "declaration": { - "children": [ - { - "flags": {}, - "kindString": "Property", - "name": "cache", - "type": {"name": "PyProxyCache", "type": "reference"}, - }, - { - "flags": {"isOptional": True}, - "kindString": "Property", - "name": "destroyed_msg", - "type": {"name": "string", "type": "intrinsic"}, - }, - { - "flags": {}, - "kindString": "Property", - "name": "ptr", - "type": {"name": "number", "type": "intrinsic"}, - }, - ], - "flags": {}, - "kindString": "Type literal", - }, - "type": "reflection", - } - ).strip() - == r"""\ **{**\ \ **cache:** :js:class:`PyProxyCache`\ **,** \ **destroyed_msg?:** :js:data:`string`\ **,** \ **ptr:** :js:data:`number`\ **}**\ """.strip() - ) diff --git a/docs/sphinx_pyodide/tests/tsdoc_dump.json.gz b/docs/sphinx_pyodide/tests/tsdoc_dump.json.gz index 538e09664..55d96e50e 100644 Binary files a/docs/sphinx_pyodide/tests/tsdoc_dump.json.gz and b/docs/sphinx_pyodide/tests/tsdoc_dump.json.gz differ diff --git a/docs/usage/api/js-api.md b/docs/usage/api/js-api.md index 536728c26..9cc994ffa 100644 --- a/docs/usage/api/js-api.md +++ b/docs/usage/api/js-api.md @@ -43,7 +43,7 @@ import type { PyProxy } from "pyodide/ffi"; This provides APIs to set a canvas for rendering graphics. For example, you need to set a canvas if you want to use the SDL library. See -:ref:`using-sdl` for more information. +{ref}`using-sdl` for more information. ```{eval-rst} .. js-doc-summary:: pyodide.canvas diff --git a/docs/usage/type-conversions.md b/docs/usage/type-conversions.md index f4b5c10b9..6f7e094ab 100644 --- a/docs/usage/type-conversions.md +++ b/docs/usage/type-conversions.md @@ -571,12 +571,12 @@ An example of a case where you would not want to use the {js:func}`~pyodide.ffi.PyProxy.toJs` method is when the buffer is bitmapped image data. If for instance you have a 3d buffer shaped 1920 x 1080 x 4, then {js:func}`~pyodide.ffi.PyProxy.toJs` will be extremely slow. In this case you -could use {js:func}`~pyodide.ffi.PyProxy.getBuffer`. On the other hand, if you +could use {js:func}`~pyodide.ffi.PyBuffer.getBuffer`. On the other hand, if you have a 3d buffer shaped 1920 x 4 x 1080, the performance of {js:func}`~pyodide.ffi.PyProxy.toJs` will most likely be satisfactory. Typically, the innermost dimension won't matter for performance. -The {js:func}`~pyodide.ffi.PyProxy.getBuffer` method can be used to retrieve a reference to +The {js:func}`~pyodide.ffi.PyBuffer.getBuffer` method can be used to retrieve a reference to a JavaScript typed array that points to the data backing the Python object, combined with other metadata about the buffer format. The metadata is suitable for use with a JavaScript ndarray library if one is present. For instance, if diff --git a/pyodide-build/pyodide_build/cli/build_recipes.py b/pyodide-build/pyodide_build/cli/build_recipes.py index d42e62c7f..e1383b7d7 100644 --- a/pyodide-build/pyodide_build/cli/build_recipes.py +++ b/pyodide-build/pyodide_build/cli/build_recipes.py @@ -10,12 +10,12 @@ from ..logger import logger def recipe( packages: list[str] = typer.Argument( - ..., help="Packages to build, or * for all packages in recipe directory" + ..., help="Packages to build, or ``*`` for all packages in recipe directory" ), recipe_dir: str = typer.Option( None, help="The directory containing the recipe of packages. " - "If not specified, the default is `./packages`", + "If not specified, the default is ``./packages``", ), no_deps: bool = typer.Option( False, help="If true, do not build dependencies of the specified packages. " @@ -28,13 +28,13 @@ def recipe( install_dir: str = typer.Option( None, help="Path to install built packages and pyodide-lock.json. " - "If not specified, the default is `./dist`.", + "If not specified, the default is ``./dist``.", ), metadata_files: bool = typer.Option( False, help="If true, extract the METADATA file from the built wheels " - "to a matching *.whl.metadata file. " - "If false, no *.whl.metadata file is produced.", + "to a matching ``*.whl.metadata`` file. " + "If false, no ``*.whl.metadata`` file is produced.", ), cflags: str = typer.Option( None, help="Extra compiling flags. Default: SIDE_MODULE_CFLAGS" diff --git a/pyodide-build/pyodide_build/cli/skeleton.py b/pyodide-build/pyodide_build/cli/skeleton.py index 2772f69d1..1696fe690 100644 --- a/pyodide-build/pyodide_build/cli/skeleton.py +++ b/pyodide-build/pyodide_build/cli/skeleton.py @@ -44,7 +44,7 @@ def new_recipe_pypi( recipe_dir: str = typer.Option( None, help="The directory containing the recipe of packages." - "If not specified, the default is `/packages`.", + "If not specified, the default is ``/packages``.", ), ) -> None: """ diff --git a/src/core/error_handling.ts b/src/core/error_handling.ts index b67a279bb..df4c54b60 100644 --- a/src/core/error_handling.ts +++ b/src/core/error_handling.ts @@ -280,7 +280,7 @@ Module.handle_js_error = function (e: any) { /** * A JavaScript error caused by a Python exception. * - * In order to reduce the risk of large memory leaks, the :py:exc:`PythonError` + * In order to reduce the risk of large memory leaks, the :js:class:`PythonError` * contains no reference to the Python exception that caused it. You can find * the actual Python exception that caused this error as * :py:data:`sys.last_value`. diff --git a/src/core/pyproxy.ts b/src/core/pyproxy.ts index d6d7415b2..0f73c7829 100644 --- a/src/core/pyproxy.ts +++ b/src/core/pyproxy.ts @@ -192,7 +192,6 @@ const pyproxyAttrsSymbol = Symbol("pyproxy.attrs"); * Function so that PyProxy objects can be callable. In that case we MUST expose * certain properties inherited from Function, but we do our best to remove as * many as possible. - * @private */ function pyproxy_new( ptr: number, @@ -344,7 +343,6 @@ let pyproxyClassMap = new Map(); * pyproxy_getflags. Multiple PyProxies with the same set of feature flags * will share the same prototype, so the memory footprint of each individual * PyProxy is minimal. - * @private */ Module.getPyProxyClass = function (flags: number) { const FLAG_TYPE_PAIRS: [number, any][] = [ @@ -556,14 +554,13 @@ export class PyProxy { } /** - * @private * @hideconstructor */ constructor() { throw new TypeError("PyProxy is not a constructor"); } - /** @private */ + /** @hidden */ get [Symbol.toStringTag]() { return "PyProxy"; } @@ -1033,7 +1030,6 @@ export class PyContainsMethods { * Quote from: * https://hacks.mozilla.org/2015/07/es6-in-depth-generators-continued/ * - * @private */ function* iter_helper(iterptr: number, token: {}): Generator { try { @@ -1122,7 +1118,6 @@ export class PyIterableMethods { * Quote from: * https://hacks.mozilla.org/2015/07/es6-in-depth-generators-continued/ * - * @private */ async function* aiter_helper(iterptr: number, token: {}): AsyncGenerator { try { @@ -1573,6 +1568,7 @@ function defaultCompareFunc(a: any, b: any): number { // Missing: // flatMap, flat, export class PySequenceMethods { + /** @hidden */ get [Symbol.isConcatSpreadable]() { return true; } @@ -1645,10 +1641,11 @@ export class PySequenceMethods { * return value is added as a single element in the new array. * @param thisArg A value to use as ``this`` when executing ``callbackFn``. */ - map( - callbackfn: (elt: any, index: number, array: any) => void, + map( + callbackfn: (elt: any, index: number, array: any) => U, thisArg?: any, - ) { + ): U[] { + // @ts-ignore return Array.prototype.map.call(this, callbackfn, thisArg); } /** @@ -1842,22 +1839,23 @@ export interface PyMutableSequence extends PyMutableSequenceMethods {} export class PyMutableSequenceMethods { /** - * The :js:meth:`Array.reverse` method reverses a ``MutableSequence`` in + * The :js:meth:`Array.reverse` method reverses a :js:class:`PyMutableSequence` in * place. - * @returns A reference to the same ``MutableSequence`` + * @returns A reference to the same :js:class:`PyMutableSequence` */ - reverse() { + reverse(): PyMutableSequence { // @ts-ignore this.$reverse(); + // @ts-ignore return this; } /** * The :js:meth:`Array.sort` method sorts the elements of a - * ``MutableSequence`` in place. + * :js:class:`PyMutableSequence` in place. * @param compareFn A function that defines the sort order. - * @returns A reference to the same ``MutableSequence`` + * @returns A reference to the same :js:class:`PyMutableSequence` */ - sort(compareFn?: (a: any, b: any) => number) { + sort(compareFn?: (a: any, b: any) => number): PyMutableSequence { // Copy the behavior of sort described here: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#creating_displaying_and_sorting_an_array // Yes JS sort is weird. @@ -1897,17 +1895,18 @@ export class PyMutableSequenceMethods { cmp_to_key.destroy(); functools.destroy(); } + // @ts-ignore return this; } /** * The :js:meth:`Array.splice` method changes the contents of a - * ``MutableSequence`` by removing or replacing existing elements and/or + * :js:class:`PyMutableSequence` by removing or replacing existing elements and/or * adding new elements in place. * @param start Zero-based index at which to start changing the - * ``MutableSequence``. + * :js:class:`PyMutableSequence`. * @param deleteCount An integer indicating the number of elements in the - * ``MutableSequence`` to remove from ``start``. - * @param items The elements to add to the ``MutableSequence``, beginning from + * :js:class:`PyMutableSequence` to remove from ``start``. + * @param items The elements to add to the :js:class:`PyMutableSequence`, beginning from * ``start``. * @returns An array containing the deleted elements. */ @@ -1920,8 +1919,8 @@ export class PyMutableSequenceMethods { } /** * The :js:meth:`Array.push` method adds the specified elements to the end of - * a ``MutableSequence``. - * @param elts The element(s) to add to the end of the ``MutableSequence``. + * a :js:class:`PyMutableSequence`. + * @param elts The element(s) to add to the end of the :js:class:`PyMutableSequence`. * @returns The new length property of the object upon which the method was * called. */ @@ -1935,27 +1934,27 @@ export class PyMutableSequenceMethods { } /** * The :js:meth:`Array.pop` method removes the last element from a - * ``MutableSequence``. - * @returns The removed element from the ``MutableSequence``; undefined if the - * ``MutableSequence`` is empty. + * :js:class:`PyMutableSequence`. + * @returns The removed element from the :js:class:`PyMutableSequence`; undefined if the + * :js:class:`PyMutableSequence` is empty. */ pop() { return python_pop(this, false); } /** * The :js:meth:`Array.shift` method removes the first element from a - * ``MutableSequence``. - * @returns The removed element from the ``MutableSequence``; undefined if the - * ``MutableSequence`` is empty. + * :js:class:`PyMutableSequence`. + * @returns The removed element from the :js:class:`PyMutableSequence`; undefined if the + * :js:class:`PyMutableSequence` is empty. */ shift() { return python_pop(this, true); } /** * The :js:meth:`Array.unshift` method adds the specified elements to the - * beginning of a ``MutableSequence``. - * @param elts The elements to add to the front of the ``MutableSequence``. - * @returns The new length of the ``MutableSequence``. + * beginning of a :js:class:`PyMutableSequence`. + * @param elts The elements to add to the front of the :js:class:`PyMutableSequence`. + * @returns The new length of the :js:class:`PyMutableSequence`. */ unshift(...elts: any[]) { elts.forEach((elt, idx) => { @@ -1967,12 +1966,12 @@ export class PyMutableSequenceMethods { } /** * The :js:meth:`Array.copyWithin` method shallow copies part of a - * ``MutableSequence`` to another location in the same ``MutableSequence`` + * :js:class:`PyMutableSequence` to another location in the same :js:class:`PyMutableSequence` * without modifying its length. * @param target Zero-based index at which to copy the sequence to. * @param start Zero-based index at which to start copying elements from. * @param end Zero-based index at which to end copying elements from. - * @returns The modified ``MutableSequence``. + * @returns The modified :js:class:`PyMutableSequence`. */ copyWithin(target: number, start?: number, end?: number): any; copyWithin(...args: number[]): any { @@ -2339,7 +2338,6 @@ export type PyProxyAwaitable = PyAwaitable; /** * The Promise / JavaScript awaitable API. - * @private */ export class PyAwaitableMethods { $$: any; @@ -2444,7 +2442,7 @@ export class PyAwaitableMethods { /** * A :js:class:`~pyodide.ffi.PyProxy` whose proxied Python object is - * :std:term:`callable` (i.e., has an :py:meth:`~operator.__call__` method). + * :std:term:`callable` (i.e., has an :py:meth:`~object.__call__` method). */ export class PyCallable extends PyProxy { /** @private */ @@ -2958,10 +2956,8 @@ export class PyBufferView { */ f_contiguous: boolean; - /** @private */ _released: boolean; - /** @private */ _view_ptr: number; /** @private */ diff --git a/src/js/api.ts b/src/js/api.ts index 43c094c2e..8c384b170 100644 --- a/src/js/api.ts +++ b/src/js/api.ts @@ -470,8 +470,8 @@ export class PyodideAPI { * @param path The absolute path in the Emscripten file system to mount the * native directory. If the directory does not exist, it will be created. If it * does exist, it must be empty. - * @param fileSystemHandle A handle returned by ``navigator.storage.getDirectory()`` - * or ``window.showDirectoryPicker()``. + * @param fileSystemHandle A handle returned by :js:func:`navigator.storage.getDirectory() ` + * or :js:func:`window.showDirectoryPicker() `. */ static async mountNativeFS( path: string, diff --git a/src/js/package-lock.json b/src/js/package-lock.json index f1778bb42..155855bb9 100644 --- a/src/js/package-lock.json +++ b/src/js/package-lock.json @@ -17,7 +17,7 @@ "@types/emscripten": "^1.39.5", "@types/expect": "^24.3.0", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.25", + "@types/node": "^17.0.45", "@types/ws": "^8.5.3", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -32,7 +32,7 @@ "prettier": "^2.2.1", "ts-mocha": "^9.0.2", "tsd": "^0.24.1", - "typedoc": "^0.22.15", + "typedoc": "^0.25.1", "typescript": "^4.6.4" } }, @@ -1262,6 +1262,12 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3559,9 +3565,9 @@ } }, "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5229,14 +5235,15 @@ } }, "node_modules/shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", "dev": true, "dependencies": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "node_modules/side-channel": { @@ -5764,25 +5771,24 @@ } }, "node_modules/typedoc": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz", - "integrity": "sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", "dev": true, "dependencies": { - "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^4.0.16", - "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 12.10.0" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.0.x || 4.1.x || 4.2.x || 4.3.x || 4.4.x || 4.5.x || 4.6.x || 4.7.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -5794,35 +5800,19 @@ "balanced-match": "^1.0.0" } }, - "node_modules/typedoc/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/typedoc/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/typescript": { @@ -5932,9 +5922,9 @@ "dev": true }, "node_modules/vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "node_modules/which": { @@ -7031,6 +7021,12 @@ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, + "ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -8704,9 +8700,9 @@ "dev": true }, "marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true }, "media-typer": { @@ -9930,14 +9926,15 @@ "dev": true }, "shiki": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz", - "integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==", + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", "dev": true, "requires": { - "jsonc-parser": "^3.0.0", - "vscode-oniguruma": "^1.6.1", - "vscode-textmate": "5.2.0" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, "side-channel": { @@ -10342,16 +10339,15 @@ } }, "typedoc": { - "version": "0.22.18", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.22.18.tgz", - "integrity": "sha512-NK9RlLhRUGMvc6Rw5USEYgT4DVAUFk7IF7Q6MYfpJ88KnTZP7EneEa4RcP+tX1auAcz7QT1Iy0bUSZBYYHdoyA==", + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.1.tgz", + "integrity": "sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==", "dev": true, "requires": { - "glob": "^8.0.3", "lunr": "^2.3.9", - "marked": "^4.0.16", - "minimatch": "^5.1.0", - "shiki": "^0.10.1" + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.1" }, "dependencies": { "brace-expansion": { @@ -10363,23 +10359,10 @@ "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - } - }, "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -10456,9 +10439,9 @@ "dev": true }, "vscode-textmate": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz", - "integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, "which": { diff --git a/src/js/package.json b/src/js/package.json index 1134a4262..73feb86b4 100644 --- a/src/js/package.json +++ b/src/js/package.json @@ -20,7 +20,7 @@ "@types/emscripten": "^1.39.5", "@types/expect": "^24.3.0", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.25", + "@types/node": "^17.0.45", "@types/ws": "^8.5.3", "chai": "^4.3.6", "chai-as-promised": "^7.1.1", @@ -35,7 +35,7 @@ "prettier": "^2.2.1", "ts-mocha": "^9.0.2", "tsd": "^0.24.1", - "typedoc": "^0.22.15", + "typedoc": "^0.25.1", "typescript": "^4.6.4" }, "main": "pyodide.js", diff --git a/src/js/tsdoc.json b/src/js/tsdoc.json new file mode 100644 index 000000000..c27eed450 --- /dev/null +++ b/src/js/tsdoc.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://developer.microsoft.com/en-us/json-schemas/tsdoc/v0/tsdoc.schema.json", + "extends": ["typedoc/tsdoc.json"], + "noStandardTags": false, + "tagDefinitions": [ + { + "tagName": "@alias", + "syntaxKind": "modifier" + }, + { + "tagName": "@hidetype", + "syntaxKind": "modifier" + }, + { + "tagName": "@hideconstructor", + "syntaxKind": "modifier" + }, + { + "tagName": "@dockind", + "syntaxKind": "block" + } + ] +} diff --git a/src/py/_pyodide/_core_docs.py b/src/py/_pyodide/_core_docs.py index 3c42e3f6d..6961c3eba 100644 --- a/src/py/_pyodide/_core_docs.py +++ b/src/py/_pyodide/_core_docs.py @@ -252,7 +252,7 @@ class JsProxy(metaclass=_JsProxyMetaClass): -------- Here are a couple examples of converter functions. In addition to the - normal conversions, convert :js:class:`Date`` to :py:class:`~datetime.datetime`: + normal conversions, convert :js:class:`Date` to :py:class:`~datetime.datetime`: .. code-block:: python