primopydocuments and notebooks
A document is text to read beside the editor: a worksheet, a chapter of notes, a Jupyter notebook, a short course. It opens on a tab of its own, and its code examples run right where they are written, in the same Python as your own code. This page is about reading one, writing one, and getting one out again. The handbook covers the rest of the editor.
This page is a document itself. Open it in the editor and every example on it can be run with Run here, which is the quickest way to see what the sections below describe.
If a teacher sent you a document, section 1 is all you need. If you teach, sections 2 to 4 say how a notebook, a Quarto or R Markdown file, or a Markdown file of your own becomes a document, and how to hand it out.
Contents#
1Reading a document#
How one arrives#
- A link. A link whose address ends in
#md=…or#mdz=…carries the whole document, pictures and data files included. A link ending in#mdu=/path/notes.mdnames a Markdown file on the primopy site, which the editor fetches; that is how the lessons and this page open. Nothing is uploaded either way: the document is in the link, or on the site. - A file. Drag a
.mdfile onto the editor, or pick one with Open…. A Jupyter notebook (.ipynb), a Quarto file (.qmd) or an R Markdown file (.Rmd) opens the same way; see section 2. - A folder. Drag a folder onto the editor. The
.mdfiles at its top are the chapters, in the order of their names, with numbers counted as numbers, so2-loops.mdcomes before10-classes.md. AREADME.mdis a chapter only when it is the only Markdown file there; beside others it is a note to whoever keeps the folder. Pictures the Markdown shows come with it. Everything else in the folder, such as themain.pyan exercise asks you to write, goes among your files as usual. - A zip. A zip of such a folder opens as the folder would, and so does a repository downloaded from GitHub as a ZIP.
The status line under the editor says what was opened, and whether your files were touched.
The document tab#
The document takes the place of the editor, and its tab stands first in the row of file tabs, with the document's title on it: its first # heading; without one, its first heading of any level, or else the file's name. Click a file tab to go to your code, and the document's tab to come back to it. The tab stays for as long as the document is open.
Each # heading starts a chapter, and one chapter is on screen at a time. ☰ at the top left shows or hides the contents: every chapter, and the sections of the one you are reading. Under each chapter, two buttons go to the chapter before and the one after, each with its title. On a narrow screen the contents lie over the text and close again once you pick something.
Running the examples#
Every Python example in a document that can run has buttons in its title bar.
- Run here runs the example where it is. What it prints goes to the Output tab under the editor: lines for the console print below what is already there, each line shown after
>>>as at the prompt, while a program clears the tab first, as Run does. A plot goes to Plots, a drawing to Draw. While it runs, the button turns into Stop. - Console, on lines meant for the console, puts them at the console prompt instead, where Enter runs one at a time.
- Open, on a program, opens it in the editor, where you can record it and step through it. Your own files go away for the moment, and Undo brings them back.
- Copy copies the code.
There are two kinds of example. Lines for the console are the cells of a notebook: they run in the console's names, so what one defines, the next can use, and you can look at all of it at the prompt. Here are two:
prices = [4.50, 2.20, 3.80]total = sum(prices)
print(f"Total: {total:.2f}")Total: 10.50The box under the second, captioned output, is what that cell printed when the document was written. It stays as it is: when you run the cell, its output goes to the Output tab, not under the cell.
A program is a whole program, main.py and any files that come with it. Run here runs it as Run would run your own, among your files, with the example's files laid over them, and leaves your tabs as they are. Afterwards its variables are the console's, as after a Run.
Cells catch up#
Press Run here on the second cell above before running the first, and it still works. A cell first runs every cell above it that has not run yet, in the order they stand, and then itself. In the Output tab a thin line sets off what the cells above printed from what the one you pressed printed; hold the pointer on a line to see which is which.
This goes back to the start of the document, to a program example, or to a cell marked fresh by its author, whichever comes first; a program example or a fresh cell there runs too, if it has not run yet. A cell that has run wears a small dot beside its buttons; one that ended in an error wears a red one. When a cell above ends in an error, the cells below it do not run, and the status line says which one it was. The dots go when the console's names go: after your own Run, after a program example, or after %reset at the console, every cell counts as not run again.
Changing an example#
Click into an example and you can change its code, there in the document. Ctrl/Cmd+Enter runs it. In console lines, Enter on a finished line runs that line, as at the prompt; in a program, Shift+Enter runs the line the caret is on, at the console and in your project's folder, where the example's own extra files are not. Escape leaves the code. A changed example says edited, and Reset in its title bar puts the document's code back. The change lasts until you reload, close the document, or open another; to keep it, use the pencil, below.
The document's own files#
A document can carry files for its code: the helpers.py a notebook imports, the data/scores.csv its cells read. When the document opens, these become tabs among your files, all in one step that Undo takes back. Your code and the document's cells find them under the same names. A tab of the same name that you already have stays as it is, and the status line says so. A file over 400 KB is too big for a tab.
Edit, Done and ×#
The pencil on the document's tab opens the document's Markdown for editing, at the chapter you were reading. Change anything; the changes you made in examples are already in it. The tick, or Ctrl/Cmd+Enter, shows the document again with your changes. While you edit, the tab also offers Reset once there is something to go back to, which puts the document back as it came, after asking, and Update outputs, described in section 4.
× on the tab closes the document. Nothing is asked: Undo brings it back, at the chapter you were on. Undo also brings back a document that a link replaced, when you open one document over another.
Reloading and sharing#
A document stays open across a reload, at the chapter you were reading, with your changes. One that is too big for that is shown all the same, and the status line says it will not be kept for your next visit.
While a document is open, Save & share → Link to all files + document (Link to file + document when you have one file) copies a link that carries your files and the document together. Whoever opens it gets your files in the tabs and the document beside them, open at the chapter you were reading.
2Notebooks, R Markdown and Quarto#
Jupyter notebooks#
A Python notebook (.ipynb) opens as a document. Its markdown cells are the text, as they are. Each code cell becomes lines for the console, so the cells share their names, as they do in Jupyter. What the notebook saved under a cell is shown under it as saved output: text in an output box, a plot as a picture.
A cell's tags change how it is shown, in the words Jupyter Book and nbconvert use:
| Tag | What it does here |
|---|---|
remove-cell | The cell runs when a cell below catches up, but is not shown and has no output. If it fails, it is shown with its code |
remove-input | Only the cell's Run here is shown, not its code |
remove-output | The saved output is left out |
raises-exception | The cell ends in an error on purpose; the cells below still run |
skip-execution | The code is shown and never run |
fresh | Catching up from below stops at this cell |
Several notebooks, picked or dropped together, or a folder of them, make one document with a chapter each, in the order of their names. Each chapter is headed by the notebook's own first # heading, or its file name, and its other # headings become sections. Each notebook's cells catch up only within that notebook, as each had its own kernel in Jupyter. A folder can also hold .md chapter files beside the notebooks; they all go in, in one order.
The other files in the folder, the modules the notebooks import and the data they read, come with the document and become tabs among your files when it opens. A notebook in another language is not run here; for R, see section 5.
Quarto and R Markdown#
A .qmd or .Rmd file opens as a document too. Each Python chunk, written ```{python} (or {python3}, or quarto-live's {pyodide}), becomes lines for the console, and the chunks share their names, as they do when Quarto renders the file. The chunk options are read from the #| lines at the top of a chunk, or from the chunk's header, as in {python echo=FALSE}, and from the defaults in the front matter's execute: or knitr: opts_chunk::
| Option | What it does here |
|---|---|
include: false | Runs when a cell below catches up, but is not shown. If it fails, it is shown with its code |
echo: false | Only Run here is shown, not the code |
eval: false | The code is shown and never run |
error: true | An error in the chunk does not stop the cells below |
A knitr::opts_chunk$set(echo = FALSE) line in an R chunk changes the defaults from the next chunk on, as it does when knitting, when every value in it is written out. So is an option: echo=solution, a value that comes from a variable, is not guessed at, and the default stands. Chunk labels go.
A chunk in another language, such as {r} or {bash}, is shown as code and not run. A plain ```python fence without braces is shown and not run, as Quarto leaves it. Inline code, such as `{python} x`, stays as it is written. child=, file=, code= and ref.label= are not followed. A callout's ::: lines and a heading's {#id .class} go, and what they held stays.
The front matter's title: becomes the document's title when the text has no # heading of its own. Its params: become a hidden first cell that runs before any other, built from their default values:
params = {
"region": "north",
"year": 2024
}A value Python cannot be given the way Quarto would read it, such as an R expression or a date, is left out rather than guessed at. In a .qmd file, yes, no, on and off are the strings they look like, as Quarto reads them, and only true and false are True and False; in an .Rmd file all of them are True or False, as R Markdown reads them.
In a folder, index.qmd comes first. A README.qmd is the folder's note. An X.md beside an X.qmd is the rendered copy, and so a file rather than a chapter. With a Quarto or R Markdown chapter, the rest of the folder comes along, as with notebooks, since its code reads it.
3Writing a document#
A document is a Markdown file: headings, paragraphs, lists, tables, links, pictures and fenced code. What primopy adds is a few words on the line that opens a fence. They are the same words the handbook's pages are built from, so one Markdown file makes a page on the site and a document in the editor.
Lines for the console#
A fence marked console holds lines to type at the prompt. Each line, or each block such as a for loop, runs as if typed, and an expression shows its value, so words on its own line shows the list:
```python console fresh
words = ["tea", "bread", "apples"]
```That is the fence of this cell:
words = ["tea", "bread", "apples"]words.sort()
wordsThe word fresh makes the first cell a starting point: running the second cell catches up as far as the first and no further, so nothing from section 1 runs again. Use it where a new part of a document starts afresh. It bounds what catches up; it does not clear any names. A transcript, lines beginning with >>>, is lines for the console too, without being marked.
A program, with its files#
A fence marked python, with no console, is a program, main.py. A fence right under it that names a file with file= is another tab of the same example, a module or a data file:
```python
with open("scores.txt") as f:
scores = [int(line) for line in f]
print("Best:", max(scores))
```
```text file=scores.txt lines=4
72
88
```Here is a full one. lines=4 keeps the data file's box about four lines tall; the rest scrolls inside it.
with open("scores.txt") as f:
scores = [int(line) for line in f]
print("Best:", max(scores))
print("Average:", sum(scores) / len(scores))72
88
95
61
80
78Output:
Best: 95
Average: 79.0A program that asks with input() gets its answers from a stdin fence under it, one answer per line. Without one, it asks in the Output tab.
name = input("Your name? ")
print("Hello,", name)AdaOutput:
Your name? Ada
Hello, AdaMore words for a program's fence:
file=tools/helpers.pynames the tab; a fence that names one joins the example above it.pip=numpy,matplotlibnames packages to install when the example is run or opened.src=data.csvfills the fence from a file beside the Markdown, which is how a file that is not text, such as a spreadsheet, comes along:text file=book.xlsx src=book.xlsx` with the fence left empty. On a page of the site this names a file beside the page's Markdown; in a link the file travels in the link.- A
quizfence under an example carries questions for the tutor, with a Start quiz button; the handbook's section on quizzes says how to write one.
Words for code that is not run as it stands#
These are the words Quarto's chunk options become, and you can write them yourself:
| On the fence line | What the reader gets |
|---|---|
python console hidden | Nothing on screen. The lines run when a cell below catches up, and appear with their code only if they fail. Good for imports and setup |
python console nocode | Only Run here, with the caption code hidden |
python norun | The code, with no button that runs it, and catching up passes it by |
python console error | Lines that end in an error on purpose. Catching up counts them as run and goes on |
A norun fence, for code that must not run here:
while True:
print("for ever")An error cell, which catching up goes past:
int("seven")And a nocode cell, which shows only its button:
Saved output#
What a cell printed can be kept under it, as a notebook keeps it: its text in a fence marked text output, and each plot or drawing as a picture named out-N.png on a line of its own, where N counts the document's cells that run, program examples included, from the top. Here the cell is the document's third:
```python console
print(6 * 7)
```
```text output
42
```
Only what stands directly under a cell in exactly this form, with blank lines between, counts as its output. A plain text fence of your own, or a picture of another name, is left alone. You need not write saved output by hand: Update outputs writes it, see the next section.
Headings, links and pictures#
Each # heading starts a chapter; each ## heading is a section, listed under its chapter in the contents. ### and below are not listed. Every heading has an anchor, its text in lower case with spaces as - and punctuation left out, as on GitHub: ## 3. Writing a document is #3-writing-a-document. A link to it, [Writing](#3-writing-a-document), goes to it in whichever chapter it stands. A link that opens a document at a heading adds &ch= and the anchor: #mdu=/handbook/documents.md&ch=saved-output.
- A link to another
.mdfile beside a document of the site, such as[the drawing reference](./draw.md), opens that document on the tab; on a page of the site it goes to the page built from it: the drawing reference. Any other page of the site opens in a new browser tab. - In a folder of chapters, a link to another chapter file,
[next](02-loops.md#while), goes to that chapter. - A link to a web page opens in a new tab, with the name of its site written after it, so you can see where it goes.
- A picture shows when it travels with the document, or is a file of the site beside a document of the site. A picture on another server is not fetched, since fetching it would tell that server who is reading.
- In the document tab,
$x^2$is a formula, and$$…$$on lines of their own a formula set apart. The pages of the site do not render them. - HTML in the Markdown is shown as text, not as markup.
4Update outputs, and getting a document out#
Update outputs#
Open the document with the pencil on its tab, and Update outputs appears beside it. Pressed, it shows the document again with your changes, as the tick would, and writes the saved output of every cell afresh:
- It clears the console's names, as Run does.
- It runs every cell from the top, in order: console lines, hidden cells and program examples alike. Each one's output appears in the tabs below as it runs, and while it runs, the button reads Stop.
- Under each cell it writes what that cell printed, as a
text outputfence, and its plots, displays and drawings asout-N.pngpictures, in place of what was saved there before.
A program example gets a text output fence too. An Output: line with a plain text fence of your own under a program stays as it is, so in a document you mean to update, leave it out, or the output is there twice. A hidden cell runs, but gets no output written, and anything saved under it goes. A transcript keeps the output it was written with. A norun fence is no cell at all. Very long output is cut after 2000 lines or 100,000 characters, with a line saying so.
When a cell ends in an error, the update stops there and the status line names the cell; the outputs of the cells above it are written all the same. A cell marked error is the exception: its error is its output, and the update goes on. Stop stops the update too, and keeps what the cells before it wrote. The document is then kept as an edit is: Reset brings back the one before, and a reload or a link carries the new outputs, pictures included.
Download as .md and as .ipynb#
While a document is open, the Save & share menu has two more entries:
- Download document as .md saves a ZIP holding a folder with the Markdown and every file the document carries. Drag that ZIP onto the editor and the same document opens again. Files the Markdown does not name, such as a notebook's modules, are listed in a
link.jsonbeside it, so they come back too. - Download document as .ipynb saves a Jupyter notebook: the text as markdown cells, each cell a code cell with its tags, and its saved output as the cell's outputs. Opened in primopy again, it reads back as the same document.
Both take the document as it stands, with your changes and the saved outputs.
Making the link yourself#
The quickest way is the editor itself: open the notebook, file or folder as a document, then Save & share → Link to all files + document. That link carries the files in your tabs as well, so take out the ones your students should not get before you copy it.
A teacher who has primopy_link.py, the script primopy's own pages are built with, can make the link at a shell instead, without a browser:
python3 primopy_link.py --doc notes.md
python3 primopy_link.py --doc course/
python3 primopy_link.py --doc lesson.ipynb
python3 primopy_link.py --doc lesson.qmd
python3 primopy_link.py --doc course.zip
python3 primopy_link.py --doc notes.md --attach helpers.py --attach data/--doc takes one Markdown, notebook, Quarto or R Markdown file, or a folder or zip of them, and reads it exactly as a drop on the editor would; a ZIP from Download document as .md reads back as the document it was. --attach adds a file or a folder that the Markdown does not name but its code needs; a link.json beside the Markdown can list them instead, as {"attach": ["helpers.py", "data/"]}. --chapter opens the link at a heading, by its anchor. --base says where the editor lives, if not at https://primopy.com/ide/. The tool prints the link.
A link holds at most 400,000 characters, unpacks to at most 4 MB, and carries at most 100 files. A link past those limits is an error, said at the shell, rather than a link that opens short. A file over 400 KB in a folder of notebooks is left out, as a drop leaves it out, and the tool says so. For anything larger, put the Markdown on a site and link to it, or hand out the ZIP.
5Code in the other language#
primopy runs Python. Its sister, Rprimo, is the same editor for R, in the browser the same way. Drop an R notebook, a .qmd or .Rmd file whose chunks are all R, .R files, or a folder or zip of those on primopy, and nothing is added. A dialog says what it is and offers it to Rprimo instead:
- Open in Rprimo opens it there in a new browser tab, with a link that carries it. Nothing here changes.
- Copy link copies that link, to send or to open later.
- Cancel leaves everything as it was.
When it is too big for a link, the dialog offers Download ZIP instead: drop the ZIP on Rprimo to open it there. Where R and Python come together, as in a .qmd with chunks of both, the drop opens here as usual, and the R chunks are shown as code. Rprimo does the same the other way round, offering Python to primopy.