Propagating User Environment Information to IPython Kernels (and more generally)

One of the attractive features of the IPython kernel is the ability to create rich output displays for objects using the _repr_ display methods.

From the docs, I’m not sure how or where the decision is made about which output is rendered in any given display environment, or whether there is communication from the user environment that allows the kernel to modify it’s display behaviour. But there is a sense, for package designers at least, that they can define a range of rich output types and that something sensible will be rendered in the end user environment.

Another part of the kernels environment is the source file, such as a Jupyter notebook, from which the code is loaded. A recent Jupyter Enhancement Proposal – Transmit Cell Metadata – suggests “transmitting cell metadata to kernels”. This seems to provide code and package designers with some sort of awareness of the user environment in at least two possible ways:

  • awareness of cell semantics or presentation semantics used for styling purposes, eg via tags;
  • awareness of other environmental information stored as more general metadata; this might include information written to notebook metadata from extensions regarding presentation state, test cases associated with a cell, and so on.

In terms of the execution environment in a Python kernel, import site;site.getsitepackages() can provide information about the home of packages, and import sys; sys.path provides further path information (are there IPython introspection methods too regarding the current kernel?). Regarding the platform on which a kernel is running, package maintainers can identify the platform using import sys; sys.platform. And I guess there are tools for detecting the presence or otherwise of GPUs? So code can have some awareness of its execution environment.

With the appearance of JupyterLite (JuptyerLab running in a browser against a WASM kernel), and JupyterLab-app (JupyterLab bundled with a Python kernel inside an electron app), as well as Thebe supported execution environments such as Jupyter Book, there are now multiple user environments that can render and execute code from the same source file.

In some cases, the actual user environment may afford or limit particular behaviours in terms of what can be rendered or presented to the user (for example, a console display may not support rich outputs); in other cases, the execution environment may be “bounded” (for example, a locally running kernel can make command line calls to the user’s home environment, a JupyterLite environment can’t).

So I wonder if it would be useful to have a means of communicating information from the (Jupyter) user environment to the kernel about the current user environment, or a simple set of “environment awareness” functions that can attempt to categorise or summarise the environmental context within which code is executing (this might include cell metadata, platform, user environment), at least at the IPython level?

It strikes me that if a user environment were to modify notebook metadata to identify the “current/last used” user environment, if the notebook metadata were also transmitted to the kernel, then the kernel could identify the user environment that way?

Where a rich output has multiple MIME types, the frontend makes the decision about which to display. JupyterLab has a priority (rank) system to choose one over the other.

Currently, the “multiple MIME types in a single output” strategy deals with this, e.g. with SymPy’s rich outputs including text/plain.

It’s not clear to me what the “user environment” is, exactly. For non-JupyterLite scenarios, the kernel environment is the same as what you’re calling the “user environment” except for remote kernels (where it is really the fact that the frontend e.g. JupyterLab presents the idea of a user environment in addition to the kernel environment).

Overall, I’m not quite sure what the problem is that you’re hoping to solve with this proposal. Perhaps you could elaborate on this a little? :slight_smile:

1 Like

I’m not sure what the issue is either!

TLDR (reflecting after writing the response below): code can be executed in all sorts of physical and end user contexts and environments (compare code in jupyterlite/jupyterlab/arm64/gpu with old cross-browser issues of html/css in chrome/IE/firefox/mac/linux/mobile); for me, notebooks+user environment is my output, where notebooks may be authored to behave differently depending on where they are used. So it’s useful to know where they are being used.

What prompted the post is a confluence of observations: I stumbled across the Enhancement proposal (passing metadata to kernel) which seemed to me to be the kernel being made aware of elements of the source code context, and have also recently been trying to come up with a strategy for writing robust notebooks and packages that can work in JupyterLite, where certain workarounds are required (eg micropip vs pip, hacks to read and write and files). I also noticed a forum post yesterday re: getting TLJH running on arm64 devices. And I also suspect that some packages can make use of GPUs in particular ways, if they (know they) are available.

This starts to remind me of old css/js hacks where stylesheets included all manner of cascading rules that broke in different ways in different browsers and designers eventually hacked their way to getting things to sort of work on a cross-browser basis. It seems to me I can try to work out what environment I’m in by trying things and seeing which break and which don’t, but it’d be easier to just run something like jupyter.whereAmI() and look it up. (I also note that recent discussion in these forums re: making code outputs available inline in markdown cells also relates to code influencing its end user/expression environment.)

I’m also working across Jupyter notebooks that are intended to work either as live notebooks, as source documents for Jupyter Books, both “static” (fixed rendered cell outputs) or “live” (eg with code executable via Thebe). To author effectively for an environment means being able to make effective use of that environment.

I am not a developer user of notebooks, using them to create code. I use notebooks as a medium for publishing interactive teaching and learning materials. The Jupyter UI then becomes part of the setting / performance environment of and for the materials. It is not just a tool for creating code or interactive materials, it is also a (performance) space in which a user interacts with pre-created materials. And as such, if the materials are aware of the space, it may be useful for them to manipulate that space in particular ways. For example, it may well be that most of the time, a particular rich type rendering deemed as most appropriate by the user environment might be one I want to override with another behaviour, if I know it is available and supported by the actual or intended user environment. Another example might be manipulating the user environment from code, eg using something like GitHub - jtpio/ipylab: Control JupyterLab from Python Notebooks with Jupyter Widgets 🧪 ☢️ 🐍 . But if I don’t know I’m in that environment, how do I know that is an option available to me?

Generalising across all of these (even if I’m making several category mistakes as I do so!), it seems to me that it might be useful for code to know what context it’s operating in, and that context is potentially quite wide. Whilst it might make sense for the UI to decide how to render things (“environment knows best”), I wonder if at times the code might want to behave differently (from a “code knows best” perspective) if it’s aware of its surroundings.