lovely-tensors installs a startup hook so you never have to import it
Tensors, for human consumption
At a glance
- What is it?
- A display layer for PyTorch tensors that replaces a wall of numbers with shape, memory, a histogram, statistics and an image. Its most interesting mechanism is a Python startup hook that patches the interpreter, and its README is generated from notebooks.
- Who is it for?
- lovely-tensors earns a place in any notebook where a tensor is printed during debugging, because the information it adds is the information you actually look for. Read two things first.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 177 days ago.
- What is it written in?
- Mainly Jupyter Notebook, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A path file at interpreter startup does the patching
The tree holds three files whose names explain how the package behaves. There is a startup hook path file, a hook module beside it, and a setuptools hooks module. The packaging metadata then overrides setuptools' own install command with a custom class named for installing the library with the hook.
That combination is the reason the library works without you doing anything. Python processes any path file it finds in a site-packages directory at startup, so a hook installed there gets to run before your code does. The library's own documentation still shows the manual route, importing the module and calling a monkey patch function, which remains available and is what you would use if the automatic path is blocked.
For most people the automatic route is what happens, and that is worth being deliberate about. A package that patches a library's representation at interpreter startup changes how every tensor prints in every process in that environment, including ones you did not intend to touch. That is the feature, and it is also the reason to check that it is behaving as expected rather than assuming it.
One line answers the six questions the raw representation cannot
The problem the project starts from is concrete. You dump a tensor to a cell and get back a wall of numbers, and the questions that matter are: what is the shape, what is the size, what are the statistics, are any values non-finite, and is this actually a picture.
The replacement answer is a single line carrying all of it: the shape in brackets, the element count, the memory footprint in mebibytes, a minimum and maximum pair with a unicode histogram drawn between them, and the mean and standard deviation.
The histogram is the part that carries the most information per character. A tensor of 115248 elements collapses to eight glyphs, and those glyphs show you immediately whether the distribution is centred, bimodal, or has a long tail. Reading a mean and a standard deviation tells you nothing about the shape of the data; reading the bar pattern does.
The memory figure is the other quiet win. A shape of three by 196 by 196 looks modest until you see it is 0.4 mebibytes of whatever the tensor contains, which is often the fastest way to find the layer that is unexpectedly large.
Non-finite values get punctuation and a dead gradient gets a name
The library treats two failure conditions as first-class enough to print by name. Non-finite values are flagged inline:
+Inf! -Inf! NaN!That is more than a value, it is a marker that something upstream divided by zero or overflowed, and putting it on the summary line means you see it without scrolling into the values.
The second is an all-zero buffer, which gets a label rather than a set of statistics:
tensor[10, 10] n=100 all_zerosThe gradient section of the documentation shows why this matters. A tensor printed before the backward pass reports its gradient as absent. A non-leaf tensor reports the operation that produced it, which is how you find out you are reading a gradient in the wrong place. After the backward pass the gradient gets its own statistics, and after you zero it, the gradient prints as all zeros instead of a histogram of nothing.
Read together those cases are a small diagnostic vocabulary: no gradient, gradient in an unexpected place, gradient present and populated, gradient present and dead. That is four of the states that cost an hour each when you have to infer them from a raw dump.
Views are chosen per question, and they compose with each other
The summary is the default, and everything else is a view you ask for. Two of them render the tensor as a picture rather than as numbers, one through the tensor's colour view and one through the plotting library. That is the direct answer to the question about whether it is an image.
One view goes deeper, printing statistics for the leading slice and then for each row beneath it. It takes a depth argument, so asking for two levels prints the channel, then the row under it, then each line of that row.
numbers.deeperTwo more views handle the case where you want the old behaviour. One gives you the verbose form with full statistics followed by the raw values, and the other gives you the plain representation and nothing else.
spicy.vspicy.pThe design point is that these are attributes rather than functions. Reading them costs a character, and the documentation shows a slice with an index expression, which still prints values rather than statistics when there are few enough of them. That threshold behaviour is sensible: asking for six values should give you six values.
Named dimensions work, with PyTorch's experimental warning left in place
PyTorch tensors support named dimensions, and the library displays them:
named_numbers = numbers.rename("C", "H","W")The output becomes a shape with labels attached rather than three bare integers, which removes a class of off-by-one bug where an axis order is assumed rather than declared.
What the documentation does not do is hide PyTorch's warning. The rename emits a user warning saying named tensors and their associated APIs are experimental and subject to change, with the advice not to use them for anything important until they are stable. The library prints it and carries on, which is the honest choice: if you rely on named tensors, you should know that the guarantee belongs to PyTorch and can change.
The named view composes with the deeper view as well:
named_numbers.deeper(2)The hierarchy stays labelled at every level, so a channel row and the individual lines inside it both carry their names. Labels survive the drill-down, which is the part that would be easy to get wrong and is clearly handled.
The README is generated, and it carries the author's other links
The first line of the file is a warning that it is autogenerated and should not be edited. The packaging configuration explains how: the project is built with a notebook-first documentation tool configured on the master branch, with a custom sidebar, and the tree holds a directory of notebooks and an index directory that the generated pages are built from.
That has a practical consequence for anyone reading the repository to understand the code. The README is an output. The notebooks are the source, and they contain the narrative of how each display feature came to be, which is where the design reasoning lives.
It also means the repository's front page is not a neutral index. It opens with a link to the author's personal site carrying a referral parameter, and it advertises a family of sibling projects: a NumPy version, a JAX version and a gradient-focused one, plus a community chat.
The NumPy sibling is not a suggestion, it is a requirement. The dependency list names torch with no version constraint at all and the NumPy display layer with a floor. So the PyTorch display is built on top of the NumPy one, which is a reasonable layering choice and also means a change in the lower package can change what your tensors print.
Three installers, one alpha classifier and an unpinned torch
Installation is offered three ways, which is a courtesy to people whose environment tooling differs:
pip install lovely-tensorsmamba install lovely-tensorsconda install -c conda-forge lovely-tensorsThe interpreter floor is 3.8 and the classifiers name Python 3 without listing individual minor versions. The version number itself is not in the packaging file, it is read from an attribute inside the package at build time.
The state of the project: classified as development status alpha, MIT licensed, no published releases, a last push dated 9 April 2026, and only four open issues. Twenty-two forks against nearly fourteen hundred stars suggests a tool that people install and read rather than contribute to, which is what you would expect from a display layer with one clear maintainer.
The one thing to watch on updates is the unpinned torch. Everything else in the dependency list has a floor or an exact pin, and torch has nothing, so a new torch release is picked up silently and the rendering of every tensor in your notebook can change without your code changing.
Editorial conclusion
lovely-tensors earns a place in any notebook where a tensor is printed during debugging, because the information it adds is the information you actually look for. Read two things first. It works by patching the interpreter at startup through a hook file, so if your environment has restrictions on path files, verify it actually took effect rather than assuming it did. And the package is classified as alpha with an unpinned torch dependency, so pin both when you care about reproducing a result.
Frequently asked questions
How do I install lovely-tensors?
Through any of three package managers: pip, mamba, or conda with the conda-forge channel. The interpreter requirement is Python 3.8 or newer, and torch is pulled in as a dependency along with the NumPy display layer at version 0.2.21 or above.
Do I have to call monkey_patch for lovely-tensors to work?
No. The package installs a startup hook that runs at interpreter start, and it also overrides the setuptools install command to place that hook. The manual import and monkey patch call remains available if the automatic route is blocked in your environment.
What does lovely-tensors print that the default PyTorch output does not?
Shape, element count, memory size, minimum and maximum with a unicode histogram between them, mean and standard deviation, flags for non-finite values, and a label for an all-zero buffer. It also offers views that render a tensor as an image and that print statistics slice by slice.
Is lovely-tensors a stable release?
It is classified as development status alpha, has no published releases, and its last push is dated 9 April 2026. It is MIT licensed and the README is autogenerated from notebooks in the repository.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/xl0-lovely-tensors)