# handcalcs: rendering Python calculations as hand-written LaTeX in Jupyter

> handcalcs turns Python calculation code into LaTeX that shows the symbolic formula, the numeric substitution and the result. It is built for engineers who need their arithmetic to be checkable by eye, and it works as a Jupyter cell magic or a function decorator.

**connorferster/handcalcs** — Python library for converting Python calculations into rendered latex.

- Repository: https://github.com/connorferster/handcalcs
- Stars: 5,805 · Forks: 452
- Language: CSS
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/connorferster-handcalcs

## The problem handcalcs solves for engineers who must show their work

A Python script computes a beam deflection and prints 12.4. Nobody can check that number without redoing the algebra. The value is correct or it is not, and the script gives no evidence either way. handcalcs exists to close that gap: it renders the calculation so the symbolic formula appears first, then the same formula with the numbers substituted, then the result. The README describes the goal as formatting a calculation "as though you wrote them by hand", and states that because the numeric substitution is shown, calculations become significantly easier to check and verify by hand.

The audience is narrow and specific. The README addresses engineers directly, and the examples in the repository are engineering problems: a ladder problem, an equation library, a Streamlit example. If your work product is a design calculation that a reviewer, a checker or a regulator will read line by line, the rendered form is the deliverable, not a side effect. If your work product is a model, a plot or a service, handcalcs is the wrong layer to invest in.

## How the rendering pipeline works: cell magic, decorator and the LaTeX it emits

There are two entry points and they share the same translation step. The first is a Jupyter cell magic. Importing handcalcs.render registers both %%render and %%tex, and any cell that starts with %%render is converted. The second is a decorator, @handcalc(), imported from handcalcs.decorator. Everything between the def line and the return statement is treated like the body of a Jupyter cell, and the decorator returns a tuple of (latex_code: str, locals: dict), where locals holds every variable in the function namespace. That tuple is what makes the decorator usable outside Jupyter, for instance in Streamlit, where you pass the latex_code to your own display call.

The translation itself is a source-to-LaTeX conversion. The %%tex magic exposes it directly: given a = 2 / 3 * sqrt(pi), the README shows it producing an aligned block with the fraction, the square root of pi, the square root of 3.142 and the final value 1.182 on the same line. The dependencies in pyproject.toml tell you what does the parsing: pyparsing, more_itertools, and innerscope, the last of which the README credits to @eriknw for making the decorator possible. There is no symbolic math engine in the dependency list, so the substitution you see is the numeric evaluation of the expression, formatted next to its unevaluated form.

The decorator takes arguments that shape the output: override, precision (default 3), left and right strings that wrap the LaTeX, jupyter_display, and record. Setting jupyter_display=True returns only the locals dictionary and displays the LaTeX through IPython.display; the README notes this errors outside a Jupyter context. The record=True flag activates the HandcalcsCallRecorder, added in v1.8.0, which is aimed at iteration: when you compute a table of values in a loop, the table shows results but not the steps, and the recorder lets you display the calculation for one iteration.

## Installing handcalcs and rendering your first calculation

The package installs from PyPI. The README gives a single command, and there is an optional extra for the nbconvert exporters.

```bash
pip install handcalcs
```

The exporters extra exists but is no longer what it was. As of v1.9.0, handcalcs stopped installing the "no input" nbconvert exporters, and the README says this was done to lighten the installation load and keep the package in scope. Those exporters moved to a separate project, nb-hideinputs. Installing the extra still resolves to nb-hideinputs through the optional dependency group in pyproject.toml, but the functionality is no longer maintained inside handcalcs itself.

```bash
pip install "handcalcs[exporters]"
```

Once installed, the shortest path is a Jupyter cell. Import the render module once, which registers both magics, then prefix a cell with %%render.

```python
import handcalcs.render
```

```python
%%render
a = 2
b = 3
c = 2*a + b/3
```

What you should see is the rendered LaTeX in place of the code cell output: the three assignments with their substituted values and the result for c. If you want the LaTeX source rather than the rendered display, %%tex gives you the raw block, which is the form you would paste into a document.

```python
%%tex
a = 2 / 3 * sqrt(pi)
```

For code that is not in a notebook, use the decorator. The function body is written normally and the return value is the LaTeX string plus a dictionary of the local variables.

```python
from handcalcs.decorator import handcalc

@handcalc(precision=3)
def my_calc(a, b):
    c = 2*a + b/3
    return c

latex_code, locals_dict = my_calc(2, 3)
```

To export a notebook as PDF you need a LaTeX environment on your system; the README points to an "Installing Tex" page in the project wiki for that, and the wiki is also where the chaining technique for linking notebooks is described.

## Where handcalcs breaks down or is the wrong tool

The rendering is a source transformation, not a symbolic solver. The substitution shown next to each formula is the numeric value of that expression, so a calculation whose meaning depends on algebraic manipulation (cancelling terms, solving for an unknown, rearranging an inequality) will not be presented that way. You get what the Python evaluated, annotated with the expression text.

The README has a section titled "Gotchas and Disclaimer" and an "Expected Behaviours" section, which is a fair signal that the translation has edges. The dependency list is the other signal: there is no unit library among the runtime dependencies. pint and forallpeople appear only in the dev dependency group in pyproject.toml, so they are used to test handcalcs, not required by it. Whether a quantity object renders with its units intact is therefore not something the README documents, and you should not assume it. If your calculations are dimensioned, verify that path in a throwaway notebook before you build anything on top of it.

Two smaller constraints matter in practice. The decorator's jupyter_display option only works inside Jupyter and errors otherwise, so non-notebook users must handle the LaTeX string themselves. And the PDF path depends on a LaTeX installation that handcalcs does not provide; the README sends you to the wiki rather than shipping a fallback renderer. If your team cannot install a TeX distribution, the notebook rendering still works but the printable artifact does not.

## handcalcs compared with SymPy and forallpeople

SymPy is the obvious comparison and the difference is one of purpose. SymPy is a computer algebra system: you build symbolic expressions, manipulate them, solve them, and it can print LaTeX. handcalcs takes the opposite route. You write ordinary Python that evaluates to numbers, and handcalcs recovers a readable LaTeX form of that code with the numeric substitution shown. SymPy can rearrange an equation for you; handcalcs shows you the equation you already wrote, with the numbers filled in. If your problem is "solve for x", SymPy is the tool. If your problem is "show the reviewer every step of how x was computed", handcalcs is.

forallpeople is a units library and appears in the dev dependency group of pyproject.toml, which suggests the two are used together in testing and in the author's own engineering work. They are not alternatives: forallpeople handles dimensional quantities, handcalcs handles presentation. The pairing is the natural setup for a dimensioned calculation, but note again that the README does not spell out the integration, so the combination is something you confirm rather than something you are promised.

A third point of comparison is the plain f-string or a hand-written Markdown cell. That costs nothing and needs no dependency, and for a calculation you will never re-run it is the honest choice. handcalcs pays off when the calculation is parameterised and re-run, because the rendered output updates with the code and cannot drift out of sync with the numbers the way a manually typed document does.

## Maintenance, licence and what an upgrade costs

The repository is not archived, and the last push was on 2026-09-28. The most recent release listed is v1.11.0 from 2026-01-23, following v1.10.0r1 and v1.10.0 in November 2025. The release titles are informal (v1.11.0 is labelled "I shoulda done this a while ago"), which is consistent with a small, single-maintainer project rather than a release-managed product. The runtime dependency set is small and stable-looking: more_itertools, innerscope >= 0.7.0 and pyparsing. innerscope is the one to watch, since the decorator depends on it and it is a separate project; the README thanks its author for integrating it into handcalcs.

The upgrade cost is mostly in the decorator signature rather than the magic. Optional arguments have been added over time (record in v1.8.0, and the override tags referenced in the README), so code that passes positional arguments to @handcalc() is more exposed than code that uses keyword arguments. The v1.9.0 change that removed the bundled nbconvert exporters is the clearest example of a breaking scope decision: if you relied on those exporters, the migration is to the separate nb-hideinputs package, and the optional extra in pyproject.toml now points there.

Licensing is Apache-2.0, declared in pyproject.toml as the classifier "License :: OSI Approved :: Apache Software License" and shipped as a LICENSE file at the repository root. Apache-2.0 is permissive and includes an explicit patent grant, which matters if you are embedding the library in a commercial engineering toolchain. It is not a copyleft licence, so it does not force you to publish your own calculation code. That is the extent of what the repository states; for anything beyond it, read the LICENSE file itself.

## Conclusion

Adopt handcalcs if your calculations live in Jupyter notebooks or plain Python functions and someone else has to verify the arithmetic by hand, which is the case for most structural and mechanical design work. Skip it if your output is a chart, a simulation trace or a dataframe where nobody reads the intermediate steps; the LaTeX rendering adds nothing there. Before committing, check one thing: whether your unit library survives the round trip, since the project lists pint and forallpeople only in its dev dependency group and the README does not document unit handling. Run a single decorated function with your units attached and read the rendered substitution before you rewrite a real calculation sheet.

## FAQ

### What are hand calcs?

In this project the term describes a calculation presented the way it would be written by hand: the symbolic formula, then the same formula with the numbers substituted, then the result. handcalcs renders Python code in that form as LaTeX, and the README states that showing the numeric substitution makes the calculation significantly easier to check by hand.

### How do you do hand calculations?

With handcalcs you write ordinary Python that evaluates to numbers, either in a Jupyter cell prefixed with %%render or inside a function decorated with @handcalc(), and the library renders the formula, the substitution and the result. The README describes the output as mimicking how one might format a calculation written with a pencil.

### How do I install handcalcs?

The README gives pip install handcalcs. There is also an optional extra, pip install "handcalcs[exporters]", which resolves to the separately maintained nb-hideinputs package since the nbconvert exporters were removed from handcalcs in v1.9.0.

### Can handcalcs be used outside Jupyter?

Yes. The @handcalc() decorator returns a tuple of the LaTeX string and a dictionary of local variables, which the README describes as usable in non-Jupyter environments such as Streamlit. The jupyter_display option is the exception, since it errors outside a Jupyter context.

## Sources

- [connorferster/handcalcs on GitHub](https://github.com/connorferster/handcalcs)
- [Issues](https://github.com/connorferster/handcalcs/issues)
- [License: Apache-2.0](https://github.com/connorferster/handcalcs/blob/main/LICENSE)
- [README](https://github.com/connorferster/handcalcs/blob/main/README.md)
- [Releases](https://github.com/connorferster/handcalcs/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/connorferster-handcalcs
