diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +build diff --git a/docs/build.sh b/docs/build.sh index 0cedc7167..f3fc90baa 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -5,3 +5,4 @@ mkdir build touch build/.nojekyll # Disable jekyll to keep files starting with underscores uv run --group docs sphinx-build -b html ./api-docs ./build/api-docs +uv run --group docs sphinx-build -b html ./library-docs ./build/library-docs diff --git a/docs/library-docs/__init__.py b/docs/library-docs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/library-docs/conf.py b/docs/library-docs/conf.py new file mode 100644 index 000000000..c1e9bb4c6 --- /dev/null +++ b/docs/library-docs/conf.py @@ -0,0 +1,30 @@ +# Configuration file for the Sphinx documentation builder. +# See https://www.sphinx-doc.org/en/master/usage/configuration.html + +project = "Guppy Libraries" +copyright = "2026, Quantinuum" +author = "Quantinuum" + +extensions = [ + "sphinx.ext.napoleon", + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx.ext.intersphinx", + "sphinx_copybutton", + "myst_parser", +] + +html_theme = "furo" + +html_title = "Guppy library docs" + +html_theme_options = {} + +html_static_path = [] + +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), +} diff --git a/docs/library-docs/index.md b/docs/library-docs/index.md new file mode 100644 index 000000000..52a205edd --- /dev/null +++ b/docs/library-docs/index.md @@ -0,0 +1,16 @@ +# Guppy Libraries Documentation + +An introductory tale into how Guppy libraries (are supposed to) work, how to write them, and how to use them. + +```{note} +This docs collection concerns Guppy libraries usage for independently compiling a set of functions to a Hugr package, +which can be independently distributed and e.g. cached for optimization purposes. If you are just looking to open +source your Guppy code, the standard packaging mechanisms of Python suffice, and you can ignore this page. +``` + +```{toctree} +:maxdepth: 2 + +overview +proposals +``` diff --git a/docs/library-docs/overview.md b/docs/library-docs/overview.md new file mode 100644 index 000000000..0b829a647 --- /dev/null +++ b/docs/library-docs/overview.md @@ -0,0 +1,184 @@ +# Overview + +## Defining a library + +A Guppy library is, at its simplest, a collection of functions that are compiled together into a Hugr package. +In contrast to the usual executable Hugr packages (like those produced from compiling single entrypoint functions), such +library Hugr packages feature (usually multiple) *public* functions, possibly with arguments and non-``None`` return +types. + +A developer may define several functions beyond this collection, for example for internally sharing functionality +between the public functions. Furthermore, developers may want to create different libraries / Hugr packages from a +single codebase, requiring to export different public functions in each such library. +In Guppy, a library is defined by calling the ``guppy.library(...)`` function, passing the functions to be exported as +public. +Any functions reachable via those functions are still included in the Hugr package, but with private visibility. + +For example, a library may be defined as follows: + +```python +from guppylang import guppy + + +@guppy +def my_func() -> None: + pass + + +@guppy +def another_func(x: int) -> None: + pass + + +@guppy +def a_third_func(x: int) -> int: + return x + 1 + + +# Creates the library object, but does not compile it +library = guppy.library( + my_func, + another_func, + a_third_func, +) +``` + +For private functions, the name their Hugr nodes receive is not important. +However, public functions need to have a stable (and unique) name for the Hugr to be valid and useful. +Guppy includes a default name inference mechanism based on the fully qualified name of the function (including any +parent types and the module path), but it is also possible to explicitly specify the name (e.g. to avoid conflicts, make +a backward-compatible change, or to provide a more user-friendly name) using the `link_name` argument of the `@guppy` +decorator: + +```python +from guppylang import guppy + + +@guppy(link_name="my.custom.link.name") +def my_func() -> None: + pass +``` + +The following are not yet supported: + +- Exporting generic functions of any kind (see the [proposal](proposals/generic.md)) +- Automatically collecting functions to be included in a library (see the [proposal](proposals/collection.md)) + +## Compiling a library and creating stubs + +Once a library object is created, it can be compiled into a Hugr package, which can be distributed and used +independently of the source code: + +```python +from guppylang import guppy +from hugr.package import Package + +library = guppy.library(...) # As above + +# Hugr package contains any specified functions as public, and otherwise reachable functions as private +hugr_package: Package = library.compile() +with open("library.hugr", "wb") as f: + f.write(hugr_package.to_bytes()) +``` + +However, this Hugr package does not expose an interface that can be programmed against by consumers of the library *in +Guppy*. For this, the library author must define *stubs* for all exposed functions, utilising `@guppy.declare(...)` +decorators. + +For the example above, this would look like: + +```python +from guppylang import guppy + + +@guppy.declare +def my_func() -> None: ... + + +@guppy.declare +def another_func(x: int) -> None: ... + + +@guppy.declare +def a_third_func(x: int) -> int: ... +``` + +Creation of these stubs is a manual process; it is currently not possible to automatically generate stubs for a +library or validate that definitions faithfully implement their corresponding stubs (see the +[proposal](proposals/stubs.md) for both). Furthermore, it is currently not possible to define the stubs, and +subsequently reference the stubs in the library function definitions for easier consistency checks (see the +[proposal](proposals/impls.md) for that). + +Finally, these stubs (importable, thus in `*.py` files instead of `*.pyi` as usual) should be distributed using regular +Python packaging mechanisms, so that users of the library can install and program against them. This distribution may or +may not contain the Hugr package as well. + +## Using a library + +Let's call the library in the example above `super_guppy`. +That is, the library author has published a distribution containing the stubs above, to be imported `from super_guppy`. +A consumer of the library has installed that distribution using their favourite package manager (either from an index, +or by downloading the stub repository). + +The consumer may now program against the API as follows: + +```python +from guppylang import guppy +from guppylang.std.builtins import result +from super_guppy import my_func, a_third_func + + +@guppy +def consumer_func() -> None: + my_func() + result("library_call", a_third_func(5)) + + +@guppy +def main() -> None: + consumer_func() +``` + +In this case, the consumer aims to create an executable Hugr package (e.g. by calling `main.compile()` and creating a +package with a single, argument-less entrypoint). However, the created Hugr package is incomplete: It lacks the function +bodies of the library functions, and thus cannot be executed. + +As of https://github.com/Quantinuum/hugr/commit/329c243fffff0d6c4437664a012361619a0a425e, `hugr-py` provides the means +to link one or more library Hugr packages into the consumer Hugr package. This may look like: + +```python +from hugr.package import Package + +package_a: Package = ... +package_b: Package = ... +package_c: Package = ... + +package_d = package_a.link(package_b, package_c) + +# Now package_d contains a single module with all the functions +# from package_a, package_b, and package_c +``` + +Whenever two or more modules are linked together, the resulting module will keep the *single* non-module entrypoint +across all modules, if it exists. When more than one module has a non-module entrypoint (i.e. more than one module is +executable), an error is raised. + +There are no guarantees about the order of modules being linked together, or whether that is pairwise or all at once. +The user can exert a certain level of control by gradually linking packages together, using multiple calls to `link`. +Furthermore, future versions of `hugr-py` may provide even more control with extensions / alternatives to `link`. + +For convenience, the library Hugr package may be provided to the consumer program executor, so that it can be +automatically linked in before compiling to binary. For example, using selene, this may look like: + +```python +main.emulator(n_qubits=0, libs=[lib_package]).with_shots(100).run() +``` + +Currently, Hugr packages have to be manually downloaded / imported from whatever distribution mechanism the library +author chose. In the future, library authors may opt to distribute Hugr packages in Python wheels as well, and have +consuming code auto-collect these from the Python environment (see the [proposal](proposals/discovery.md) for this). + +```{note} +A consumer of `super_guppy` may as well be another library author. Dependency of a library should be specified as usual +in Python requirements by depending on the header distributions (or through other common mechanisms). +``` diff --git a/docs/library-docs/proposals.md b/docs/library-docs/proposals.md new file mode 100644 index 000000000..87ab697c0 --- /dev/null +++ b/docs/library-docs/proposals.md @@ -0,0 +1,9 @@ +# Proposals + +A collection of proposals for QOL features related to Guppy libraries. + +```{toctree} +:glob: + +proposals/* +``` diff --git a/docs/library-docs/proposals/collection.md b/docs/library-docs/proposals/collection.md new file mode 100644 index 000000000..5d8c90dcd --- /dev/null +++ b/docs/library-docs/proposals/collection.md @@ -0,0 +1,82 @@ +# (Proposal) More ways to define a library / auto-collection + +Currently, functions must be manually fed into `guppy.library(...)` to be included in the library. This proposal +concerns additional ways to define (members of) a library, and automatically collect functions to be included in it. + +A potential addition would be a `@guppy(export=...)` argument, that registers the function to be included in *the* +library in some global Guppy store (like the `DEF_STORE`). A function could then be included simply by adding that +argument, e.g.: + +```python +from guppylang import guppy + + +@guppy(export=True) +def included() -> None: + ... + + +@guppy # or @guppy(export=False) for explicit exclusion +def not_included() -> None: + ... +``` + +This is similar to a `public` / `private` modifier, and indirectly determines the visibility of the function in the Hugr +package. It may be added to `@guppy.struct` and `@guppy.enum` as well, to include the functions on these constructs in +the library as public as well. + +A call to `guppy.library` could then be simplified to an explicit opt-in to auto-collection: + +```python +from guppylang import guppy + +# ... the functions above ... + +# Includes `included`, but not `not_included` as a public member. +library = guppy.library(auto_collect=True) +``` + +## Optional: Keys + +In addition to supporting `True | False | None` for the `export` argument, it may be useful to support string keys as +well, to allow for more fine-grained control over the produced packages in which the function is included (concerning +both the visibility in the produced Hugr package and the stubs that are generated, e.g. +through [the stub proposal](stubs.md)). + +For example, one may want to include certain (core) functions in all versions of the library, but specify certain sets +of functions to be included (akin to `extras` from Python packages): + +```python +from guppylang import guppy + + +@guppy(export=True) +def included_in_all_libs() -> None: + ... + + +@guppy(export='key1') +def included_when_key_1() -> None: + ... + + +@guppy(export='key2') +def included_when_key_2() -> None: + ... + + +@guppy(export='key3') +def included_when_key_3() -> None: + ... +``` + +Auto-collection may then take a range of keys to include: + +```python +from guppylang import guppy + +# ... the functions above ... + +# Includes all functions with export=True, export='key1', and export='key2', but not export='key3' +library = guppy.library(auto_collect=['key1', 'key2']) +``` diff --git a/docs/library-docs/proposals/discovery.md b/docs/library-docs/proposals/discovery.md new file mode 100644 index 000000000..713265414 --- /dev/null +++ b/docs/library-docs/proposals/discovery.md @@ -0,0 +1,79 @@ +# (Proposal) Automatic discovery of Hugr packages in installed distributions + +This proposal concerns how Hugr packages are distributed and discovered. +At a high level, Hugr packages should be distributed as part of Python wheels, and discovered by Guppy at runtime +without the need for manual configuration or other custom user code. + +The proposal is to leverage the +Python [entry points mechanism](https://packaging.python.org/en/latest/specifications/entry-points/) for this (as one of +the common drivers behind +[plugin discovery](https://packaging.python.org/en/latest/guides/creating-and-discovering-plugins/#using-package-metadata) +in Python). Library authors can define an entry point in the group `guppylang.hugr` in their `pyproject.toml`, and +specify a loader function that returns a list of paths to Hugr packages to be loaded. + +For example, assuming the library was compiled to a `library.hugr` binary file, a library author may include the +following in a top-level `loader.py` inside their distribution: + +```python +from pathlib import Path + + +def load_hugrs() -> list[Path]: + return [Path(__file__).parent / "library.hugr"] +``` + +Additionally, they would include the `library.hugr` file, and the following configuration in the `pyproject.toml` of +their distribution (e.g. `super-guppy-hugr`): + +```toml +[project] +name = "super-guppy-hugr" +version = "0.1.0" +description = "HUGR distribution for {self.metadata.name}" + +[project.entry-points."guppylang.hugr"] +hugr_loader = "loader:load_hugrs" +``` + +```{note} +Hugr packages may also be included in stub distributions, and discovered in the same way by including the loader and +the entry point configuration. They do not have to be separate distributions. +``` + +`guppylang` can now auto-discover Hugr packages by looking for entry points in the `guppylang.hugr` group, and calling +the corresponding loader. An example implementation of this discovery mechanism may look like the following: + +```python +from pathlib import Path +from importlib.metadata import entry_points + + +def discover_hugr_packages() -> list[Path]: + eps = entry_points(group='guppylang.hugr') + hugr_packages = [] + for ep in eps: + # Can use `ep.dist.name` to get the distribution name, if needed for logging, debugging, or filtering. + loader_func = ep.load() + hugr_packages.extend(loader_func()) + + return hugr_packages +``` + +Finally, consumers of the library would simply install the `super-guppy-hugr` distribution, and call the discovery +function before executing their Hugr code, to ensure that the library Hugr package is linked in: + +```python +from guppylang.library import discover_hugr_packages + +main = ... # Define some consuming entry point + +main.emulator(n_qubits=..., libraries=discover_hugr_packages()).with_shots(100).run() +``` + +Optionally, omission of `libraries` may lead to internal calls to automatic discovery, and other means of configuring +the used Hugr packages may be provided as well (e.g. environment variables, or a configuration file). + +## Open questions + +- How does a user sort out which Hugr packages to use with multiple Hugr packages for the same stubs installed? + It is unclear how this case can be discovered (e.g. for a nice error message if they do not choose). diff --git a/docs/library-docs/proposals/generic.md b/docs/library-docs/proposals/generic.md new file mode 100644 index 000000000..aa2eb92b0 --- /dev/null +++ b/docs/library-docs/proposals/generic.md @@ -0,0 +1,27 @@ +# (Proposal) Exporting generic functions + +- Need to restrict / make finite the monomorphisations to be included in the library +- For private functions, this is easy: Just include all the variants that are used +- For public functions, usage is not yet clear (since they may be called arbitrarily) +- Thus, developer needs to specify which monomorphisations to *at least* include in the library, for example via + +```python +from guppylang import guppy + +T = guppy.type_var("T") + + +# Choice 1, includes at least the monomorphisations for int and float, and any privately required ones +@guppy.instances([{T: int}, {T: float}, ...]) # may be unpacked, as QOL for manual specification +def my_generic_func(x: T) -> T: ... + + +# Choice 2, different syntax, same effect +@guppy(instances=[{T: int}, {T: float}, ...]) # may be unpacked, as QOL for manual specification +def my_generic_func(x: T) -> T: ... +``` + +## Optional QOL features + +- Allow to easily specify a range of types for each type variable, and form their product + - This could perhaps be done via `itertools.product` or similar, but this has to be investigated diff --git a/docs/library-docs/proposals/impls.md b/docs/library-docs/proposals/impls.md new file mode 100644 index 000000000..130292d3e --- /dev/null +++ b/docs/library-docs/proposals/impls.md @@ -0,0 +1,17 @@ +# (Proposal) Easily referencing headers to implement + +A new syntax, that allows function definitions to easily reference the declarations they are providing an implementation +for. +Signatures are validated similar to [the stub validation](./stubs.md). +The declarations link name and other keyword args should be largely copied onto the definition. +The main use case is not running risk of desyncing the link name when doing something like this. + +```python +from guppylang import guppy +from some_lib import some_declaration + + +@guppy.impl(some_declaration) # or @guppy.implements(...) +def my_implementation(...) -> ...: + ... +``` diff --git a/docs/library-docs/proposals/stubs.md b/docs/library-docs/proposals/stubs.md new file mode 100644 index 000000000..a2f39e392 --- /dev/null +++ b/docs/library-docs/proposals/stubs.md @@ -0,0 +1,56 @@ +# (Proposal) Generating stubs automatically + +One of the more arduous tasks of creating a library is writing the stubs for it. +This proposal aims to provide tooling to simplify this process and partially automate it. +There are existing stub generators for Python (like [ +`stubgen` from `mypy`](https://mypy.readthedocs.io/en/stable/stubgen.html), tooling from [ +`pyright`](https://github.com/microsoft/pyright/blob/main/docs/type-stubs.md#generating-type-stubs-from-command-line), +and more). +These stub generators are often meant to provide a start for writing stubs for a given chunk of Python, but often +require manual labor to clean, fix, and clarify generated code. While the aim of this proposal is not to necessarily +fully automate the process, there are additional considerations which, if not automated, can cause great slowdowns for +library developer workflows. + +General difficulties around stub generation, that the above generators have (more or less successfully) resolved, +include: + +- Types mentioned in signatures may come from imports (which you want to preserve, since importing from their + implementing module may reveal implementation details), or be defined locally, in which case you need to include them + in the stubs as well. +- Pythonic public re-exports should be preserved / made available in the stubs +- When depending on types provided from imports (i.e. defined in other files), these types need to be included in the + stubs for that other file, even if they may be unused there + +Special difficulties for the Guppy ecosystem and compile time Python include: + +- Instead of preserving the `@guppy` decorator (or whatever custom decorator was used), we have to use `@guppy.declare` + in the stubs +- The link name should always be fixed in the stubs, to avoid changes in the default link name inference mechanism + breaking the stubs (and thus the library interface) without any observable change in the source code of the library +- Structs and enums need to be included in the stubs for typing reasons, but the interaction with nominal typing is + unclear +- Guppy functions may be generated by Python "factory" functions, that may provide some types / arguments at compile + time, especially for generic numeric parameters. Thus, we need a very careful mix of resolving types, but recalling + the original source and definition sites if they cannot be fully resolved (e.g. boiled to a single number). + +## Validation + +When writing stubs (especially those generated automatically), it is important to be able to validate that a stub +faithfully represents the definition it corresponds to (or rather, that the definition can be substituted for the stub +without violating the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle)). +Checking may be optionally (i.e. via a flag) strengthened to require equality rather than a subtyping relationship, to +effectively check that: + +1. A definition can replace the stub without violating LSP, *and* +2. The stub is the most specific signature that can be used for the definition + +This can be extended to take two arbitrary function signatures (with accompanying flags, i.e. from +`@guppy(unitary=True)`), and subsequently check LSP on them. + +Such a validation mechanism would be useful for the following use cases: + +1. Verifying (automatically, and fit for use in CI) that a definition did not observably break against the stub it + corresponds to (at least with the typed contract, written contracts cannot be checked). +2. Verifying (mainly for our test cases) that the automatically generated stubs work as intended, and one did not + discover a new edge case where they do not. +3. Probably more. diff --git a/pyproject.toml b/pyproject.toml index 82b0b46af..5b1a8f14a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,10 @@ [dependency-groups] -docs = ["furo>=2024.8.6", "sphinx >=7.2.6,<9"] +docs = [ + "furo>=2024.8.6", + "myst-parser>=4.0.1", + "sphinx >=7.2.6,<9", + "sphinx-copybutton>=0.5.2", +] dev = [ { include-group = "lint" }, { include-group = "test" }, diff --git a/uv.lock b/uv.lock index 4175adc65..b8069518b 100644 --- a/uv.lock +++ b/uv.lock @@ -34,7 +34,9 @@ dev = [ ] docs = [ { name = "furo", specifier = ">=2024.8.6" }, + { name = "myst-parser", specifier = ">=4.0.1" }, { name = "sphinx", specifier = ">=7.2.6,<9" }, + { name = "sphinx-copybutton", specifier = ">=0.5.2" }, ] examples = [ { name = "matplotlib", specifier = ">=3.9.2" }, @@ -1566,6 +1568,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/6d/bd967a5d1738665445e8bed1dd89f61c56eceb6a128b2f56674fef0504d6/lief-0.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:c2514f8d5c9061af83f737d2d67c3d36b55453fa74a3911bb9631ca86efcbb9c", size = 3637355, upload-time = "2026-01-24T16:02:33.18Z" }, ] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version >= '3.11' and python_full_version < '3.14'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -1738,6 +1771,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] +[[package]] +name = "mdit-py-plugins" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "miette-py" source = { editable = "miette-py" } @@ -1818,6 +1873,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "myst-parser" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "jinja2", marker = "python_full_version < '3.11'" }, + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "mdit-py-plugins", marker = "python_full_version < '3.11'" }, + { name = "pyyaml", marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, +] + +[[package]] +name = "myst-parser" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version >= '3.11' and python_full_version < '3.14'", +] +dependencies = [ + { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "jinja2", marker = "python_full_version >= '3.11'" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "mdit-py-plugins", marker = "python_full_version >= '3.11'" }, + { name = "pyyaml", marker = "python_full_version >= '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl", hash = "sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211", size = 84533, upload-time = "2026-01-15T09:08:16.788Z" }, +] + [[package]] name = "nbclient" version = "0.5.13" @@ -3464,6 +3560,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/dd/018ce05c532a22007ac58d4f45232514cd9d6dd0ee1dc374e309db830983/sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b", size = 22496, upload-time = "2023-07-08T18:40:52.659Z" }, ] +[[package]] +name = "sphinx-copybutton" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, +] + [[package]] name = "sphinxcontrib-applehelp" version = "2.0.0"