Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
939dc42
Add an `--index-url` flag for the `piplite` CLI
agriyakhetarpal Feb 13, 2025
bed093c
Add `pipliteInstallDefaultOptions` to schema, types
agriyakhetarpal Feb 13, 2025
5d45a40
Fix a typo
agriyakhetarpal Feb 13, 2025
83757c0
Add effective index URLs to pass to piplite
agriyakhetarpal Feb 13, 2025
a9b539d
Add `--index-url`, `-i`, to CLI flags
agriyakhetarpal Feb 13, 2025
95b4700
Handle index URLs with requirements files
agriyakhetarpal Feb 13, 2025
32e0f51
Fix JS prettier lint errors
agriyakhetarpal Feb 13, 2025
8014816
Now fix Python linter errors
agriyakhetarpal Feb 13, 2025
48d1970
Fix typo
agriyakhetarpal Feb 13, 2025
7207454
Log what index URL is being used if verbose
agriyakhetarpal Feb 13, 2025
ffe3907
Fix, allow adding index URL inside a requirements file
agriyakhetarpal Feb 13, 2025
1c8e574
Mark CLI alias for index_urls in docstring
agriyakhetarpal Feb 14, 2025
e7d3818
Hopefully fix Python linter
agriyakhetarpal Feb 14, 2025
a5e9565
Handle tuple unpacking better
agriyakhetarpal Feb 14, 2025
c04d84f
Try to fix index URLs in requirements file
agriyakhetarpal Feb 14, 2025
23852ca
Rename `indexUrls` to `index_urls`
agriyakhetarpal Feb 14, 2025
b3f7808
Single source of truth for installation defaults
agriyakhetarpal Feb 14, 2025
d0fd31a
Fix Python formatting
agriyakhetarpal Feb 14, 2025
a9a62b3
Revert "Fix Python formatting"
agriyakhetarpal Feb 14, 2025
2d7aed6
Revert "Single source of truth for installation defaults"
agriyakhetarpal Feb 14, 2025
a1bcf66
Reapply "Single source of truth for installation defaults"
agriyakhetarpal Feb 14, 2025
a8cf844
Reapply "Fix Python formatting"
agriyakhetarpal Feb 14, 2025
d2192fe
Fix boolean capitalisation b/w JS/TS and Python
agriyakhetarpal Feb 14, 2025
4a7116c
Add a TS fix
agriyakhetarpal Feb 14, 2025
443c206
Fix index URLs and requirements files again
agriyakhetarpal Feb 14, 2025
e4e7a30
Some more fixes for install order precedence
agriyakhetarpal Feb 14, 2025
fec4e1b
More fixes
agriyakhetarpal Feb 14, 2025
98576dc
Simplify handling yet again
agriyakhetarpal Feb 15, 2025
d694c00
Fix URL handling that can lead to silent failures
agriyakhetarpal Feb 15, 2025
3fc2381
Temporarily remove NumPy, add SPNW index URL
agriyakhetarpal Feb 15, 2025
d131c93
Merge main and resolve lots of conflicts
agriyakhetarpal Feb 22, 2026
f9fdb3c
restore string or array for `pipliteInstallDefaultOptions`
agriyakhetarpal Feb 22, 2026
610c066
Consolidate everything into `_PIPLITE_DEFAULT_INSTALL_ARGS`
agriyakhetarpal Feb 22, 2026
5dc6c2c
More work
agriyakhetarpal Feb 22, 2026
89d6339
Oops add missing `pipliteInstallDefaultOptions`
agriyakhetarpal Feb 22, 2026
eaca48b
Lint
agriyakhetarpal Feb 22, 2026
253a3bc
Remove SPNW index URL added to example
agriyakhetarpal Feb 23, 2026
0cf4a57
Add a test to validate `index_urls`
agriyakhetarpal Feb 23, 2026
7ab3309
Make `index_urls` a list
agriyakhetarpal Feb 23, 2026
92566ff
Rename `collected_index_urls` ➡️ `index_urls`
agriyakhetarpal Feb 23, 2026
cb0aa87
Parse the index URLs in a better manner
agriyakhetarpal Feb 23, 2026
d9c96e8
Lint
agriyakhetarpal Feb 23, 2026
f55adea
Simplify more
agriyakhetarpal Feb 23, 2026
a3fff15
Bring back `_PIPLITE_URLS` and `_PIPLITE_DISABLE_PYPI` constants
agriyakhetarpal Feb 23, 2026
f15c661
resolve `pipliteInstallDefaultOptions.index_urls` against `baseUrl`
agriyakhetarpal Feb 23, 2026
24e1da8
Add `pipliteIndexUrls`
agriyakhetarpal Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions jupyterlite_pyodide_kernel/tests/test_piplite.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,69 @@ def a_lite_config_file(request, an_empty_lite_dir):
return an_empty_lite_dir / request.param


@pytest.mark.parametrize(
"index_urls",
[
"https://example.com/simple",
["https://example.com/simple", "https://pypi.org/simple"],
],
ids=["string", "list"],
)
def test_validate_piplite_install_default_options_valid(
script_runner, a_lite_config_file, index_urls
):
"""valid index_urls (string or list) in pipliteInstallDefaultOptions passes check"""
lite_dir = a_lite_config_file.parent
output = lite_dir / "_output"

build = script_runner.run(["jupyter", "lite", "build"], cwd=str(lite_dir))
assert build.success
shutil.copy2(output / a_lite_config_file.name, a_lite_config_file)

whole_file = config_data = json.loads(a_lite_config_file.read_text(**UTF8))
if a_lite_config_file.name == JUPYTERLITE_IPYNB:
config_data = whole_file["metadata"][JUPYTERLITE_METADATA]

config_data[JUPYTER_CONFIG_DATA].setdefault(LITE_PLUGIN_SETTINGS, {}).setdefault(
PYODIDE_KERNEL_PLUGIN_ID, {}
)["pipliteInstallDefaultOptions"] = {"index_urls": index_urls}

a_lite_config_file.write_text(json.dumps(whole_file, **JSON_FMT), **UTF8)
rebuild = script_runner.run(["jupyter", "lite", "build"], cwd=str(lite_dir))
assert rebuild.success

check = script_runner.run(["jupyter", "lite", "check"], cwd=str(lite_dir))
assert check.success, f"check failed for index_urls={index_urls!r}"


def test_validate_piplite_install_default_options_invalid(
script_runner, a_lite_config_file
):
"""a non-string, non-list index_urls in pipliteInstallDefaultOptions fails check"""
lite_dir = a_lite_config_file.parent
output = lite_dir / "_output"

build = script_runner.run(["jupyter", "lite", "build"], cwd=str(lite_dir))
assert build.success
shutil.copy2(output / a_lite_config_file.name, a_lite_config_file)

whole_file = config_data = json.loads(a_lite_config_file.read_text(**UTF8))
if a_lite_config_file.name == JUPYTERLITE_IPYNB:
config_data = whole_file["metadata"][JUPYTERLITE_METADATA]

config_data[JUPYTER_CONFIG_DATA].setdefault(LITE_PLUGIN_SETTINGS, {}).setdefault(
PYODIDE_KERNEL_PLUGIN_ID, {}
)["pipliteInstallDefaultOptions"] = {"index_urls": 42}

invalid_config = json.dumps(whole_file, **JSON_FMT)
a_lite_config_file.write_text(invalid_config, **UTF8)
rebuild = script_runner.run(["jupyter", "lite", "build"], cwd=str(lite_dir))
assert rebuild.success

recheck = script_runner.run(["jupyter", "lite", "check"], cwd=str(lite_dir))
assert not recheck.success, invalid_config


def test_validate_config(script_runner, a_lite_config_file):
lite_dir = a_lite_config_file.parent
output = lite_dir / "_output"
Expand Down
23 changes: 23 additions & 0 deletions packages/pyodide-kernel-extension/schema/kernel.v0.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@
"default": [],
"format": "uri"
},
"pipliteInstallDefaultOptions": {
"type": "object",
"description": "Default options to pass to piplite.install",
"default": {},
"properties": {
"index_urls": {
"description": "Base URL(s) of extra indices to use, passed as `index_urls` to micropip.install. Accepts a single URL string or a list of URL strings.",
"oneOf": [
{
"type": "string",
"format": "uri"
},
{
"type": "array",
"items": {
"type": "string",
"format": "uri"
}
}
]
}
}
},
"loadPyodideOptions": {
"type": "object",
"description": "additional options to provide to `loadPyodide`, see https://pyodide.org/en/stable/usage/api/js-api.html#globalThis.loadPyodide",
Expand Down
2 changes: 2 additions & 0 deletions packages/pyodide-kernel-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const kernel: JupyterFrontEndPlugin<void> = {
const pipliteUrls = rawPipUrls.map((pipUrl: string) => URLExt.parse(pipUrl).href);
const disablePyPIFallback = !!config.disablePyPIFallback;
const loadPyodideOptions = config.loadPyodideOptions || {};
const pipliteInstallDefaultOptions = config.pipliteInstallDefaultOptions || {};

for (const [key, value] of Object.entries(loadPyodideOptions)) {
if (key.endsWith('URL') && typeof value === 'string') {
Expand Down Expand Up @@ -116,6 +117,7 @@ const kernel: JupyterFrontEndPlugin<void> = {
mountDrive,
loadPyodideOptions,
contentsManager,
pipliteInstallDefaultOptions,
browsingContextId: serviceWorkerManager?.browsingContextId,
logger,
});
Expand Down
70 changes: 56 additions & 14 deletions packages/pyodide-kernel/py/piplite/piplite/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ async def install(
deps: bool = True, # --no-deps
credentials: str | None = None, # no CLI alias
pre: bool = False, # --pre
index_urls: list[str] | str | None = None, # no CLI alias
index_urls: list[str] | str | None = None, # -i and --index-url
*,
constraints: list[str] | None = None, # --constraints
reinstall: bool = False, # no CLI alias
Expand All @@ -37,6 +37,11 @@ async def install(

REQ_FILE_SPEC = r"^(?P<flag>-r|--requirements)\s*=?\s*(?P<path_ref>.+)$"

# Matches --index-url or -i directives (with optional = separator and optional quotes)
INDEX_URL_SPEC = (
r'^(--index-url|-i)\s*=?\s*(?:"([^"]*)"|\047([^\047]*)\047|([^\s]*))\s*$'
Copy link
Contributor

Choose a reason for hiding this comment

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

similar to the above about uri-reference, but also, trying to write "real" URL matches in regexen is one of those things that will almost certainly break in some case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense. I can shlex it if that's easier, though maybe I should check what pip is doing the same as well.

We need to handle cases like --index-url URL, --index-url=URL, -i URL, -i=URL, but I don't know if I am missing any.

Copy link
Member Author

Choose a reason for hiding this comment

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

See cb0aa87

Copy link
Contributor

Choose a reason for hiding this comment

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

sure, parsing the left side of the thing, which we control, is fine, but trying to parse the value containing :// is better handled by an actual URL parser... if even needed. pyodide even defines some of its own conventions like emfs: which would probably work with this, if done correctly, but might need deeper configuration: relative URLs, for example, might need to have the base URL appended in the upstream config-utils.js, as a worker may not get something accurate.

Copy link
Member Author

@agriyakhetarpal agriyakhetarpal Feb 23, 2026

Choose a reason for hiding this comment

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

Yes, if someone puts a relative path like -i ../my-index/simple in a requirements file is a different aspect. This belongs in config-utils.js upstream I suppose (it should recurse into nested objects such as pipliteInstallDefaultOptions to resolve index_urls), but we can bridge that in index.ts right now using the same new URL(value, baseUrl).href pattern I see we have for loadPyodideOptions. Edit: see f15c661

But yeah, requirements-file -i values are a known limitation since they're parsed in the worker without context of base URLs, so I am not sure what we can do about that...

Copy link
Contributor

Choose a reason for hiding this comment

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

If we used jsStyleKeys, a config-based key ending in Url or Urls will "just work":

https://github.com/jupyterlite/jupyterlite/blob/v0.7.2/app/config-utils.js#L228

Not sure how this would work with runtime-provided -i values.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we used jsStyleKeys, a config-based key ending in Url or Urls will "just work":

jupyterlite/jupyterlite@v0.7.2/app/config-utils.js#L228

Thanks. We will need to go back to pipliteIndexUrls in that case, but then we should also move it out of pipliteInstallDefaultOptions and make it a top-level plugin config key?

Not sure how this would work with runtime-provided -i values.

The answer is already in piplite.py. _install will use index_urls from the CLI if given, and otherwise falls back to _PIPLITE_DEFAULT_INSTALL_ARGS.get("index_urls") from pipliteIndexUrls/pipliteInstallDefaultOptions.index_urls/whatever.

Copy link
Contributor

Choose a reason for hiding this comment

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

go back to pipliteIndexUrls

Sure, that's one option. That config value is used in the wild (though sometimes the very wild, as in private repos referencing private values). Wherever it eventually lands, at least one release should have both.

The magic _ values are underscored because they are not intended to be public, but we all know how that goes.

index_urls from the CLI if given

Right, but if they are relative, resolving them accurately might need, e.g. _JUPYTERLITE_REAL_BASE_URL or something: maybe they can "just" be emfs:// links. It's a little difficult to reason about these changes purely from the code perspective, and might require bringing a more exhaustive set of demonstrative, interactive tests over, such as the python-packages.ipynb notebook into this repo so these kind of changes are directly reviewable in a representative setting (e.g. RTD).

Copy link
Member Author

Choose a reason for hiding this comment

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

go back to pipliteIndexUrls

Sure, that's one option. That config value is used in the wild (though sometimes the very wild, as in private repos referencing private values). Wherever it eventually lands, at least one release should have both.

The magic _ values are underscored because they are not intended to be public, but we all know how that goes.

I can make this change in a while – I'll make pipliteIndexUrls take precedence over pipliteInstallDefaultOptions.index_urls though and merge them in worker.ts. I didn't get what you meant by it being used in the wild though, since pipliteIndexUrls doesn't exist right now?

index_urls from the CLI if given

Right, but if they are relative, resolving them accurately might need, e.g. _JUPYTERLITE_REAL_BASE_URL or something: maybe they can "just" be emfs:// links. It's a little difficult to reason about these changes purely from the code perspective, and might require bringing a more exhaustive set of demonstrative, interactive tests over, such as the python-packages.ipynb notebook into this repo so these kind of changes are directly reviewable in a representative setting (e.g. RTD).

Thanks for being thorough on this. I'd still lean toward saying that hopefully no one is (going to be) writing -i ./my-local-index in a requirements file inside a JupyterLite notebook. Could we punt that to a future PR, i.e., as a known limitation for now? I think the more realistic use case (for me and various others) would be for them to always be absolute (PyPI, Anaconda.org, any other CORS-enabled hosted index, and so on).

I can add that notebook to this PR, yes, though the changes right now are mostly testable on RTD and work well. Let me try out a few scenarios with various paths, relative links, and the like, and comment here.

)

__all__ = ["get_transformed_code"]


Expand Down Expand Up @@ -95,6 +100,13 @@ def _get_parser() -> ArgumentParser:
action="store_true",
help="whether pre-release packages should be considered",
)
parser.add_argument(
"--index-url",
"-i",
type=str,
default=None,
help="base URL of the package index to use for lookup",
)
parser.add_argument(
"--force-reinstall",
action="store_true",
Expand Down Expand Up @@ -138,8 +150,7 @@ async def get_action_kwargs(argv: list[str]) -> tuple[str | None, dict[str, Any]
except (Exception, SystemExit):
return None, {}

kwargs = {}

kwargs: dict[str, Any] = {}
action = args.action

if action == "install":
Expand All @@ -154,33 +165,54 @@ async def get_action_kwargs(argv: list[str]) -> tuple[str | None, dict[str, Any]
if args.verbose:
kwargs["keep_going"] = True

if args.index_url:
kwargs["index_urls"] = args.index_url

if args.force_reinstall:
kwargs["reinstall"] = True

collected_index_urls: list[str] = []
for req_file in args.requirements or []:
async for spec in _specs_from_requirements_file(Path(req_file)):
async for spec in _specs_from_requirements_file(
Path(req_file), collected_index_urls=collected_index_urls
):
kwargs["requirements"] += [spec]

for const_file in args.constraints or []:
async for spec in _specs_from_requirements_file(Path(const_file)):
kwargs["constraints"] += [spec]
kwargs.setdefault("constraints", []).append(spec)

# Apply index URLs from requirements files only if --index-url was not
# already given on the command line.
if collected_index_urls and "index_urls" not in kwargs:
kwargs["index_urls"] = collected_index_urls

return action, kwargs


async def _specs_from_requirements_file(spec_path: Path) -> AsyncIterator[str]:
async def _specs_from_requirements_file(
spec_path: Path,
*,
collected_index_urls: list[str] | None = None,
) -> AsyncIterator[str]:
"""Extract package specs from a ``requirements.txt``-style file."""
if not spec_path.exists():
warn(f"piplite could not find requirements file {spec_path}")
return

for line_no, line in enumerate(spec_path.read_text(encoding="utf").splitlines()):
async for spec in _specs_from_requirements_line(spec_path, line_no + 1, line):
for line_no, line in enumerate(spec_path.read_text(encoding="utf-8").splitlines()):
async for spec in _specs_from_requirements_line(
spec_path, line_no + 1, line, collected_index_urls=collected_index_urls
):
yield spec


async def _specs_from_requirements_line(
spec_path: Path, line_no: int, line: str
spec_path: Path,
line_no: int,
line: str,
*,
collected_index_urls: list[str] | None = None,
) -> AsyncIterator[str]:
"""Get package specs from a line of a ``requirements.txt``-style file.

Expand All @@ -195,13 +227,23 @@ async def _specs_from_requirements_line(
if file_match:
ref = file_match.groupdict()["path_ref"]
ref_path = Path(ref if ref.startswith("/") else spec_path.parent / ref)
async for sub_spec in _specs_from_requirements_file(ref_path):
async for sub_spec in _specs_from_requirements_file(
ref_path, collected_index_urls=collected_index_urls
):
yield sub_spec
return

index_url_match = re.match(INDEX_URL_SPEC, raw)
if index_url_match:
# Extract the URL from whichever capture group matched (quoted or bare)
# and collect it so the caller can forward it to piplite.install.
url = next((g for g in index_url_match.groups()[1:] if g is not None), None)
if url and collected_index_urls is not None:
collected_index_urls.append(url)
return
elif raw.startswith("-"):
warn(f"{spec_path}:{line_no}: unrecognized spec: {raw}")
return
else:
spec = raw

if spec:
yield spec
if raw:
yield raw
38 changes: 28 additions & 10 deletions packages/pyodide-kernel/py/piplite/piplite/piplite.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,26 @@
logger = logging.getLogger(__name__)


#: a list of Warehouse-like API endpoints or derived multi-package all.json
_PIPLITE_URLS: list[str] = []
Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, dunno about just dropping these; might be a cas that needs to be deprecated, but supported through this major version. micropip API changed give us enough surprises without adding our own breaking changes.

Copy link
Member Author

@agriyakhetarpal agriyakhetarpal Feb 23, 2026

Choose a reason for hiding this comment

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

Sure, can restore both as aliases. _PIPLITE_URLS points to the same list object so mutations still work, but for _PIPLITE_DISABLE_PYPI we can only give a snapshot of the initial value.

We can deprecate them properly in a follow-up later!

Edit: see a3fff15


#: a cache of available packages
_PIPLITE_INDICES: dict[str, dict[str, Any]] = {}

#: don't fall back to pypi.org if a package is not found in _PIPLITE_URLS
_PIPLITE_DISABLE_PYPI = False

#: a well-known file name respected by the rest of the build chain
ALL_JSON = "/all.json"

#: This is the runtime configuration set from the JS side via worker.ts, which acts as
#: a single source of truth for all site-level piplite settings.
#:
#: Keys written by worker.ts on every kernel start:
#: piplite_urls – list of local all.json warehouse index URLs
#: disable_pypi – whether to block fallback to pypi.org
#:
#: Keys that are optionally written by worker.ts from pipliteInstallDefaultOptions:
#: index_urls – default index URL(s) that get forwarded to micropip.install
_PIPLITE_DEFAULT_INSTALL_ARGS: dict[str, Any] = {
"piplite_urls": [], # a list of Warehouse-like API endpoints or derived multi-package all.json
"disable_pypi": False, # don't fall back to pypi.org if package not found in _PIPLITE_URLS
}


class PiplitePyPIDisabled(ValueError):
"""An error for when PyPI is disabled at the site level, but a download was
Expand Down Expand Up @@ -91,7 +99,7 @@ async def _query_package(
fetch_kwargs: dict[str, Any] | None = None,
) -> ProjectInfo:
"""Fetch the warehouse API metadata for a specific ``pkgname``."""
for piplite_url in _PIPLITE_URLS:
for piplite_url in _PIPLITE_DEFAULT_INSTALL_ARGS.get("piplite_urls", []):
if not piplite_url.split("?")[0].split("#")[0].endswith(ALL_JSON):
logger.warning("Non-all.json piplite URL not supported %s", piplite_url)
continue
Expand All @@ -105,7 +113,7 @@ async def _query_package(
if pypi_json_from_index:
return pypi_json_from_index

if _PIPLITE_DISABLE_PYPI:
if _PIPLITE_DEFAULT_INSTALL_ARGS.get("disable_pypi", False):
raise PiplitePyPIDisabled(
f"{name} could not be installed: PyPI fallback is disabled"
)
Expand All @@ -130,7 +138,17 @@ async def _install(
constraints: list[str] | None = None,
reinstall: bool = False,
):
"""Invoke micropip.install with a patch to get data from local indexes"""
"""Invoke micropip.install with a patch to get data from local indexes.

If ``index_urls`` is not explicitly provided and a default has been
configured via ``_PIPLITE_DEFAULT_INSTALL_ARGS`` (e.g. from
``pipliteInstallDefaultOptions`` in ``jupyter-lite.json``), the default
is used.
"""
effective_index_urls = index_urls
if effective_index_urls is None:
effective_index_urls = _PIPLITE_DEFAULT_INSTALL_ARGS.get("index_urls")

with patch("micropip.package_index.query_package", _query_package):
return await micropip.install(
requirements=requirements,
Expand All @@ -139,7 +157,7 @@ async def _install(
credentials=credentials,
pre=pre,
constraints=constraints,
index_urls=index_urls,
index_urls=effective_index_urls,
verbose=verbose,
reinstall=reinstall,
)
Expand Down
6 changes: 6 additions & 0 deletions packages/pyodide-kernel/src/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export class PyodideKernel extends BaseKernel implements IKernel {
location: this.location,
mountDrive: options.mountDrive,
loadPyodideOptions: options.loadPyodideOptions || {},
pipliteInstallDefaultOptions: options.pipliteInstallDefaultOptions,
browsingContextId: options.browsingContextId,
kernelId: this.id,
};
Expand Down Expand Up @@ -432,6 +433,11 @@ export namespace PyodideKernel {
*/
mountDrive: boolean;

/**
* Default options to pass to piplite.install
*/
pipliteInstallDefaultOptions?: IPyodideWorkerKernel.IPipliteInstallOptions;

/**
* additional options to provide to `loadPyodide`
* @see https://pyodide.org/en/stable/usage/api/js-api.html#globalThis.loadPyodide
Expand Down
24 changes: 23 additions & 1 deletion packages/pyodide-kernel/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,26 @@ export interface ICoincidentPyodideWorkerKernel extends IPyodideWorkerKernel {
export type IRemotePyodideWorkerKernel = IPyodideWorkerKernel;

/**
* An namespace for Pyodide workers.
* A namespace for Pyodide workers.
*/
export namespace IPyodideWorkerKernel {
/**
* Options for piplite installation.
*/
export interface IPipliteInstallOptions {
/**
* Base URL(s) of extra indices to use, forwarded to micropip.install as
* ``index_urls``. Accepts either a single URL string or a list of URL
* strings.
*/
index_urls?: string | string[];

/**
* Any additional piplite install options.
*/
[key: string]: any;
}

/**
* Initialization options for a worker.
*/
Expand Down Expand Up @@ -125,6 +142,11 @@ export namespace IPyodideWorkerKernel {
*/
mountDrive: boolean;

/**
* Default options to pass to piplite.install (e.g. index_urls).
*/
pipliteInstallDefaultOptions?: IPipliteInstallOptions;

/**
* A unique ID to identify the origin of this request.
* This should be provided by `IServiceWorkerManager` and is used to
Expand Down
Loading