Skip to main content

Check code snippets in anki or files via docker.

Project description

snippet-checker

Check code snippets in anki or files via docker.

Quickstart

Install:

uv tool install snippet-checker

Requires Docker.

How to check anki

In ~/.snippet-checker/ or $XDG_CONFIG_HOME/snippet-checker/ write snippet-checker.toml:

# Name of your anki profile.
profile = "cosmo"

# Tell the tool how to extract the code and output from your notes.
[[notes]]
note_type = "Code output"

# The field containing the code.
[notes.code_field]
name = "Code"
# Your field may contain markup, as well as the code.
# The pattern should be a Python regex with a group named "target", which matches just the code.
# The pattern below works for fields like '<pre><code class="lang-python">print(1 + 1)</code></pre>'.
# The markup is added back when the tool writes to anki.
pattern = '(?s)^<pre><code class="lang-\w+?">(?P<target>.*)</code></pre>$'

# The field containing the output.
[notes.output_field]
name = "Output"
# As above.
# This pattern works for fields like '<pre><samp>2\n</samp></pre>'.
pattern = "(?s)^<pre><samp>(?P<target>.*)</samp></pre>$"

# Same again for each note type you want to check.

In anki:

  • add a tag to the notes you want to check
    • e.g. check_me
  • add tags snip:image:<image tag> to the notes which have snippets
    • e.g. snip:image:python:3.13
    • sets the image in which the tool runs that note's snippet
    • the image is pulled via docker image pull <image tag>
  • add other tags to customize how the tool treats them
    • snip:no_check_format to skip when checking formatting
    • snip:no_check_output to skip when checking outputs
    • snip:output_verbosity:0 or 1 or 2
    • snip:no_compress to keep double blank lines in code
    • more details on these below

(Anki lets you batch edit tags: select the notes, right click, Notes > Add/Remove Tags.)

Check outputs:

snippet-checker --anki output check_me

Check formatting:

snippet-checker --anki format check_me

Pass --interactive to fix interactively. Pass --fix to auto-fix (back up your collection first).

Checking files

Structure your directory something like

your_dir
├── a_snippet
│   ├── main.py
│   └── output.txt
├── more_snippets
│   ├── extra_files_anywhere_are_ok
│   ├── a_go_snippet
│   │   ├── go.mod
│   │   ├── main.go
│   │   └── output.txt
│   ├── a_javascript_snippet
│   │   ├── main.js
│   │   └── output.txt
│   └── another_python_snippet
│       ├── main.py
│       └── output.txt

Write a snippet_checker.toml file at your_dir's root:

# Set how tracebacks, panics etc. are abbreviated.
output_verbosity = 0  # Or 1 or 2.

# Set image tags.
[images]
js = "node:22"
rb = "ruby:2.7"
py = "python:3.14"
go = "golang:1.23"
rs = "rust:1.93"

To override a setting for a particular snippet, add another snippet_checker.toml alongside it:

check_format = false

[images]
go = "golang:1.21"

Check outputs:

snippet-checker output your_dir

Check formatting:

snippet-checker format your_dir

Pass --interactive to fix interactively. Pass --fix to auto-fix (version control your collection first).

Examples

snippet-checker runs the code as though at the command line (python main.py, node main.js, go build main.go then /main, etc) then constructs timed, normalised outputs.

Hello world

print("hello world")
hello world

Trailing newline included.

Timing

from threading import Thread
from time import sleep


def io_bound():
    sleep(3)
    print("done")


thread1 = Thread(target=io_bound)
thread2 = Thread(target=io_bound)
thread1.start()
thread2.start()
print("here")
here
<~3s>
done
done

Timing matters, so it's included in the output. Gaps are rounded to the nearest second, are only included if at least 1s after rounding, and are included in the form "<~Xs>".

Normalising exceptions

1 / 0

Output verbosity 0:

ZeroDivisionError: division by zero

Output verbosity 1

Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero

Output verbosity 2:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero

Similar for exceptions in other languages.

Normalising memory locations

class C:
    pass


class D:
    pass


c = C()
d = D()
print(c)
print(d)
print(c)
<__main__.C object at 0x100>
<__main__.D object at 0x200>
<__main__.C object at 0x100>

Memory addresses vary from run to run. The tool replaces them by consistent, simpler addresses.

Q&A

Which languages does it support?

Python and Go robustly. JavaScript (Node), Ruby and Rust somewhat, but output normalisation is wip.

How sandboxed?

The snippets run in Docker containers. No mounts or volumes.

What to do when snippet-checker complains?

If you agree, then it's done its job and you can update the snippet or output.

If you disagree, then you have options:

  1. open an issue to adapt snippet-checker to handle your snippet
  2. adapt your snippet to something snippet-checker can handle
  3. tag your snippet so snippet-checker ignores it

Some examples.

snippet-checker can't handle

# assume my_file.txt is "first\nsecond\nthird\n"
with open("my_file.txt") as f:
    for x in f:
        print(x)

but we can adapt the snippet to something it can handle:

with open("my_file.txt", "w") as f:
    f.write("first\nsecond\nthird\n"

with open("my_file.txt") as f:
    for x in f:
        print(x)

It can't handle this either

try:
    x = input()
    # user enters Ctrl-C
except Exception:
    print("exception")
finally:
    print("finally")

and I don't see how to adapt the snippet or the tool. Better just add a tag so snippet-checker ignores it.

Or a formatting example:

print("foo" "bar")

ruff formats this as print("foobar"). But if the point of the question is to show implicit string concatenation, again better to add a tag so snippet-checker ignores it.

What formatters does it use?

A fixed formatter with default configuration for each language: ruff, prettier, gofmt, rubocop, rustfmt. I'm thinking about how to make this customizable.

What's no_compress?

Some formatters like double blank lines, e.g. between class definitions. But space is at a premium in anki notes. So by default when formatting anki double blanks are replaced by single blanks. To keep double blanks add a snip:no_compress tag.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

snippet_checker-0.1.2.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

snippet_checker-0.1.2-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file snippet_checker-0.1.2.tar.gz.

File metadata

  • Download URL: snippet_checker-0.1.2.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for snippet_checker-0.1.2.tar.gz
Algorithm Hash digest
SHA256 2af526edc382f34130d3e1b0bb8857f2fc7148c6c6cbb3352994252f70b423f6
MD5 29c72ca711fdd926779d25f812fd2511
BLAKE2b-256 62680091357d9c528c2db41438cf77b205ea73eaddb101e590bcd9422a84a7e2

See more details on using hashes here.

File details

Details for the file snippet_checker-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: snippet_checker-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.2 {"installer":{"name":"uv","version":"0.10.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for snippet_checker-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0587441d90948f10a08402e1ecb1fb45669ef48486d87a73f695f2c7ff4de9f7
MD5 3ef43568b6af3d3207c2c07bd15989e3
BLAKE2b-256 4ea8ed883d55af1e9a48488b5af002ef43fda77ec1438bfa310bf97244fc03fd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page