3-day current streak·56-day longest streak
<!-- You can manually process this file with cog: $ python -m pip install -r requirements.pip $ python -m cogapp -rP README.md On GitHub, it's generated by an action: https://github.com/nedbat/nedbat/blob/main/.github/workflows/build.yml…
<!--
You can manually process this file with cog:
$ python -m pip install -r requirements.pip
$ python -m cogapp -rP README.md
On GitHub, it's generated by an action:
https://github.com/nedbat/nedbat/blob/main/.github/workflows/build.yml
-->
<!-- [[[cog
import base64
import datetime
import os
import sys
import time
from urllib.parse import quote, urlencode
import requests
def requests_get_json(url):
"""Get JSON data from a URL, with retries."""
headers = {}
token = None
if "github.com" in url:
token = os.environ.get("GITHUB_TOKEN", "")
if token:
headers["Authorization"] = f"Bearer {token}"
for _ in range(3):
sys.stderr.write(f"Fetching {url}\n")
resp = requests.get(url, headers=headers)
if resp.status_code == 200:
break
print(f"{resp.status_code} from {url}:", file=sys.stderr)
print(resp.text, file=sys.stderr)
time.sleep(1)
else:
raise Exception(f"Couldn't get data from {url}")
return resp.json()
def rounded_nice(n):
"""Make a good human-readable summary of a number: 1734 -> "1.7k"."""
n = int(n)
ndigits = len(str(n))
if ndigits <= 3:
return str(n)
elif 3 < ndigits <= 4:
return f"{round(n/1000, 1):.1f}k"
elif 4 < ndigits <= 6:
return f"{round(n/1000):d}k"
elif 6 < ndigits <= 7:
return f"{round(n/1_000_000, 1):.1f}M"
elif 7 < ndigits <= 9:
return f"{round(n/1_000_000):d}M"
def shields_url(
url=None,
label=None,
message=None,
color=None,
label_color=None,
logo=None,
logo_color=None,
):
"""Flexible building of a shields.io URL with optional components."""
params = {"style": "flat"}
if url is None:
url = "".join([
"/badge/",
quote(label or ""),
"-",
quote(message),
"-",
color,
])
else:
if label:
params["label"] = label
url = "https://img.shields.io" + url
if label_color:
params["labelColor"] = label_color
if logo:
params["logo"] = logo
if logo_color:
params["logoColor"] = logo_color
return url + "?" + urlencode(params)
def md_image(image_url, text, link, title=None, attrs=None):
"""Build the Markdown for an image.
image_url: the URL for the image.
text: used for the alt text and the title if title is missing.
link: the URL destination when clicking on the image.
title: the title text to use.
attrs: HTML attributes (switches to HTML syntax)
"""
if title is None:
title = text
assert "]" not in text
assert '"' not in title
if attrs:
img_attrs = " ".join(f'{k}="{v}"' for k, v in attrs.items())
return f'[]({link})'
else:
return f'[]({link})'
def badge(text=None, link=None, title=None, kwargs):
"""Build the Markdown for a shields.io badge."""
return md_image(image_url=shields_url(kwargs), text=text, link=link, title=title)
def badge_mastodon(server, handle):
"""A badge for a Mastodon account."""
# https://github.com/badges/shields/issues/4492
# https://docs.joinmastodon.org/methods/accounts/#lookup
url = f"https://{server}/api/v1/accounts/lookup?acct={handle}"
followers = requests_get_json(url)["followers_count"]
return badge(
label=f"@{handle}", message=rounded_nice(followers),
logo="mastodon", color="96a3b0", label_color="450657", logo_color="white",
text=f"Follow @{handle} on Mastodon ({followers})", link=f"https://{server}/@{handle}",
)
def badge_bluesky(handle):
"""A badge for a Bluesky account."""
url = f"https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor={handle}"
followers = requests_get_json(url)["followersCount"]
return badge(
label=f"Bluesky", message=rounded_nice(followers),
logo="icloud", label_color="3686f7", color="96a3b0", logo_color="white",
text=f"Follow {handle} on Bluesky ({followers})", link=f"https://bsky.app/profile/{handle}",
)
def badge_stackoverflow(userid):
"""A badge for a Stackoverflow account."""
data = requests_get_json(f"https://api.stackexchange.com/2.3/users/{userid}?order=desc&sort=reputation&site=stackoverflow")["items"][0]
rep_points = rounded_nice(data["reputation"])
gold = rounded_nice(data["badge_counts"]["gold"])
silver = rounded_nice(data["badge_counts"]["silver"])
bronze = rounded_nice(data["badge_counts"]["bronze"])
sp = "\N{THIN SPACE}"
return badge(
logo="stackoverflow", logo_color=None, label_color="333333", color="e6873e",
message=(
f"{rep_points} "
+ f"\N{LARGE YELLOW CIRCLE}{sp}{gold} "
+ f"\N{MEDIUM WHITE CIRCLE}{sp}{silver} "
+ f"\N{LARGE BROWN CIRCLE}{sp}{bronze}"
),
text="Stack Overflow reputation", link=data["link"],
)
def data_url(image_file):
"""Read an image file and return a self-contained data URL."""
assert image_file.endswith((".png", ".jpg"))
with open(image_file, "rb") as imgf:
b64 = base64.b64encode(imgf.read()).decode("ascii")
return f"data:image/png;base64,{b64}"
]]] -->
<!-- [[[end]]] -->
<!--
##
## BADGES
##
-->
<!-- [[[cog
print(badge(
logo=data_url("pencil.png"), logo_color="white", label_color="eeeeee", message="Blog etc", color="888888",
text="Read my blog", link="https://nedbatchelder.com",
))
print(badge_mastodon("hachyderm.io", "nedbat"))
print(badge_bluesky("nedbat.com"))
print(badge(
logo="meetup", logo_color="red", label_color="eeeeee", message="Boston Python", color="4d7954",
text="Join us at Boston Python", link="https://about.bostonpython.com",
))
print(badge(
logo="discord", logo_color="white", label_color="7289da", message="Discord", color="ffe97c",
text="Python Discord", link="https://discord.gg/python",
))
print(badge(
logo="GitHub", label="\N{HEAVY BLACK HEART}", message="Sponsor me", color="brightgreen",
text="Sponsor me on GitHub", link="https://github.com/sponsors/nedbat",
))
print(badge_stackoverflow(userid=14343))
print(badge(
logo="python", logo_color="FFE873", label_color="306998", message="PyPI", color="4B8BBE",
text="My PyPI packages", link="https://pypi.org/user/nedbatchelder",
))
]]] -->
[](https://nedbatchelder.com)
[")](https://hachyderm.io/@nedbat)
[")](https://bsky.app/profile/nedbat.com)
[](https://about.bostonpython.com)
[](https://discord.gg/python)
[](https://github.com/sponsors/nedbat)
[](https://stackoverflow.com/users/14343/ned-batchelder)
[](https://pypi.org/user/nedbatchelder)
<!-- [[[end]]] -->
<!--
##
## CAUSES
##
-->
<!-- [[[cog
attrs = {"height": 75, "style": "border: 1px solid #888"}
print(md_image("https://nedbatchelder.com/pix/nokings.jpg", "No Kings", "https://nokings.org", attrs=attrs))
print(" " * 4)
print(md_image("https://nedbatchelder.com/pix/us-flag.png", "Optimistic despite current events", "https://nedbatchelder.com/blog/202411/my_politics.html", attrs=attrs))
print(" " * 4)
print(md_image("https://nedbatchelder.com/pix/ukraine.png", "Support Ukraine", "https://stand-with-ukraine.pp.ua/#support-ukraine", attrs=attrs))
print(" " * 4)
print(md_image("https://nedbatchelder.com/pix/progressprideflag.png", "Pride", "https://nedbatchelder.com/blog/201207/my_mom_got_married.html", attrs=attrs))
print(" " * 4)
print(md_image("https://nedbatchelder.com/pix/blm.jpg", "Black lives matter", "https://nedbatchelder.com/blog/202006/black_lives_matter.html", attrs=attrs))
]]] -->
[](https://nokings.org)
    
[](https://nedbatchelder.com/blog/202411/my_politics.html)
    
[](https://stand-with-ukraine.pp.ua/#support-ukraine)
    
[](https://nedbatchelder.com/blog/201207/my_mom_got_married.html)
    
[](https://nedbatchelder.com/blog/202006/black_lives_matter.html)
<!-- [[[end]]] -->
<!--
##
## ME
##
-->
I'm Ned Batchelder, a Python software developer and community organizer.
- My personal site is [nedbatchelder.com][nedbat].
- I'm an organizer of [Boston Python][bp].
- I'm a member of the [Python Docs Editorial Board][pdeb].
- Mastodon: [@[email protected]][mastodon].
- Discord: nedbat in the [Python Discord][discord].
- Libera IRC: nedbat in [#python][libera].
<!-- [[[cog
blogdata = requests_get_json("https://nedbatchelder.com/summary.json")
def write_blog_post(entry, twoline=False):
when = datetime.datetime.strptime(entry['when_iso'], "%Y%m%d")
print(f"- [{entry['title']}]({entry['url']}), {when:%-d %b}", end="")
if twoline:
print(f"\n{entry['description_text']} *([read..]({entry['url']}))*")
else:
print()
]]] -->
<!-- [[[end]]] -->
My latest [blog][blog] posts:
<!-- [[[cog
N_ENTRIES = 4
entries = blogdata["entries"][:N_ENTRIES]
for entry in entries:
write_blog_post(entry, twoline=True)
print("- and [many more][blog]..")
]]] -->
- A place of certainty, 14 Jul
- Dodecahedron with stars, 18 Jun
- Cultural factoids of the day: 64, 16 Jun
- Franklin’s parents grave, 2 Jun
- and [many more][blog]..
<!--
##
## PYPI PACKAGES
##
-->
<!-- [[[cog
pkgs = [
# (pypi name, human name, github repo, (mastserver, masthandle)),
("coverage", "Coverage.py", "coveragepy/coveragepy", ("hachyderm.io", "coveragepy")),
("cogapp", "Cog", "nedbat/cog"),
("scriv", "Scriv", "nedbat/scriv"),
("linklint", "Linklint", "nedbat/linklint"),
("dinghy", "Dinghy", "nedbat/dinghy"),
("watchgha", "WatchGHA", "nedbat/watchgha"),
]
def write_package(pkg, human, repo, mastinfo=None):
description = requests_get_json(f"https://api.github.com/repos/{repo}")["description"]
main_line = f"{human}: {description}"
pypi_badge = badge(
url=f"/pypi/v/{pkg}?style=flat",
text="PyPI",
link=f"https://pypi.org/project/{pkg}",
title=f"The {pkg} PyPI page",
)
github_badge = badge(
url=f"/github/last-commit/{repo}?logo=github&style=flat",
text="GitHub last commit",
link=f"https://github.com/{repo}/commits",
title=f"Recent {human.lower()} commits",
)
pypi_downloads_badge = badge(
url=f"/pypi/dm/{pkg}?style=flat",
text="PyPI - Downloads",
…
-
dinghy ★ PINNED
A GitHub activity digest tool
Python ★ 199 11d agoExplain → -
watchgha ★ PINNED
Live display of current GitHub action runs
Python ★ 422 5mo agoExplain → -
scriv ★ PINNED
Changelog management tool
Python ★ 304 9d agoExplain → -
cog ★ PINNED
Small bits of Python computation for static files
Python ★ 406 1d agoExplain → -
byterun
A Python implementation of a Python bytecode runner
Python ★ 1.4k 2y agoExplain → -
pkgsample
A simple example of how to structure a Python project
Python ★ 138 9mo agoExplain → -
truchet
Playing with Truchet tiles
Jupyter Notebook ★ 58 2mo agoExplain → -
gefilte
Gefilte Fish GMail filter creator
Python ★ 54 4y agoExplain → -
pydoctor
A diagnostic program to show the Python environment
Python ★ 33 11mo agoExplain → -
dot
Personal dotfiles
Shell ★ 29 1y agoExplain → -
pytest-gallery
A sampler of tests showing different ways to construct tests for pytest
Python ★ 23 5y agoExplain → -
choosy
A Python teaching tool
Python ★ 20 14y agoExplain → -
cupid
No description.
Python ★ 20 5y agoExplain → -
aptus
Mandelbrot fractal viewer
Python ★ 19 10d agoExplain → -
zellij
A toy for making geometric art, inspired by Islamic Zellij.
Python ★ 18 27d agoExplain → -
pylintdb
Put pylint violations into sqlite
Python ★ 18 7y agoExplain → -
flourish
Harmonograph toy
Python ★ 14 2y agoExplain → -
gpxmapper
No description.
Python ★ 12 4y agoExplain → -
edtext
A string-like object with ed-like addressing
Python ★ 11 5mo agoExplain → -
unittest-mixins
Helpful unittest mixin classes
Python ★ 10 2y agoExplain → -
adventofcode2017
No description.
Python ★ 10 8y agoExplain → -
nedbatcom
nedbatchelder.com
HTML ★ 9 15h agoExplain → -
adventofcode2018
No description.
Python ★ 9 7y agoExplain → -
odds
Odds & Ends
Python ★ 8 5mo agoExplain → -
adventofcode2020
No description.
Python ★ 8 5y agoExplain → -
adventofcode2019
No description.
Python ★ 8 4y agoExplain → -
unipain
PyCon presentation: Pragmatic Unicode, or, How Do I Stop the Pain?
HTML ★ 8 9y agoExplain → -
iter
PyCon presentation about iteration
HTML ★ 7 6y agoExplain → -
nedbat
nedbat's profile
Shell ★ 6 5h agoExplain → -
pyhurry
No description.
Python ★ 6 9y agoExplain → -
adventofcode2021
No description.
Python ★ 6 4y agoExplain → -
song-basket
Simple Spotify app to collect songs into a basket playlist.
Python ★ 6 4y agoExplain → -
linklint
A Sphinx extension to reduce excessive linking
Python ★ 5 3d agoExplain → -
adventofcode2022
adventofcode 2022
Python ★ 5 3y agoExplain → -
coverage_pytest_plugin
No description.
Python ★ 5 6y agoExplain → -
adventofcode2016
No description.
Python ★ 5 7y agoExplain → -
adventofcode2015
No description.
Python ★ 5 10y agoExplain → -
toomuchregex
A lightning talk
JavaScript ★ 4 1y agoExplain → -
typing_app ▣
No description.
JavaScript ★ 4 8y agoExplain → -
injectx ▣
No description.
Python ★ 4 11y agoExplain → -
templite
No description.
Python ★ 4 7y agoExplain → -
pgeom
No description.
Jupyter Notebook ★ 4 7y agoExplain → -
native-matrix
No description.
★ 4 4y agoExplain → -
adventures_prz
A presentation
JavaScript ★ 4 7y agoExplain → -
test0
PyCon presentation: Getting Started Testing
HTML ★ 4 4y agoExplain → -
dinghy_sample
An example of publishing Dinghy digests
HTML ★ 3 10d agoExplain → -
dotfiles
Dot files managed by chezmoi
Vim Script ★ 3 17d agoExplain → -
wikicrawl
No description.
Python ★ 3 4y agoExplain → -
blogtools
No description.
Python ★ 3 6mo agoExplain → -
github-to-sqlite ⑂
Save data from GitHub to a SQLite database
Python ★ 3 2y agoExplain → -
blowyournose
No description.
Python ★ 3 9y agoExplain → -
tabtest
No description.
Python ★ 3 9y agoExplain → -
stilted
No description.
Python ★ 3 3y agoExplain → -
commitstats
No description.
Jupyter Notebook ★ 3 2y agoExplain → -
hello-github-actions
No description.
★ 3 7y agoExplain → -
explainer
No description.
★ 3 9y agoExplain → -
version_dummy
Absolute minimal versioned Python package.
Shell ★ 3 10y agoExplain → -
jreport
Utility for making console reports from JSON APIs
Python ★ 3 12y agoExplain → -
natsworld
No description.
Python ★ 3 10y agoExplain → -
point_match
A presentation.
HTML ★ 3 7y agoExplain → -
bigo
No description.
HTML ★ 3 5y agoExplain → -
sarai
No description.
HTML ★ 2 6d agoExplain → -
human_spider
No description.
Python ★ 2 2mo agoExplain → -
blank_prz
A blank presentation, using my own crappy toolchain
JavaScript ★ 2 4mo agoExplain → -
python-coverage-comment-action ⑂
Publish diff coverage report as PR comment, and create a coverage badge to display on the Readme for Python projects
★ 2 2y agoExplain → -
toxghabug
No description.
★ 2 5y agoExplain → -
branch-tests
No description.
★ 2 5y agoExplain → -
xunit_tools
Janky tools for doing things with xunit.xml files
Python ★ 2 10y agoExplain → -
django_issue_25793
Demo for https://code.djangoproject.com/ticket/25793
Python ★ 2 10y agoExplain → -
madlib
No description.
Python ★ 2 4y agoExplain → -
prznames
No description.
HTML ★ 2 5y agoExplain → -
nedbat_dinghy
No description.
HTML ★ 1 1h agoExplain → -
devguide ⑂
The Python developer's guide
Python ★ 1 1mo agoExplain → -
silence
A lightning talk
HTML ★ 1 1mo agoExplain → -
fluidity
No description.
Jupyter Notebook ★ 1 8mo agoExplain → -
docs.openedx.org ⑂
Open edX Official Documentation
JavaScript ★ 1 10mo agoExplain → -
badge-samples
No description.
★ 1 1y agoExplain → -
jslex
JavaScript lexer in pure Python
Python ★ 1 1y agoExplain → -
smartypants.py ⑂
Translate plain ASCII punctuation characters into “smart” typographic punctuation HTML entities.
Python ★ 1 4y agoExplain → -
ghapi ⑂
A delightful and complete interface to GitHub's amazing API
★ 1 4y agoExplain → -
tox ⑂
Command line driven CI frontend and development task automation tool.
Python ★ 1 2y agoExplain → -
adventofcode2023
No description.
Python ★ 1 2y agoExplain → -
operator ⑂
(forked for coverage benchmarking) Pure Python framework for writing Juju charms
Python ★ 1 2y agoExplain → -
cyclorama
No description.
Python ★ 1 3y agoExplain → -
pytest-cov ⑂
Coverage plugin for pytest.
Python ★ 1 6y agoExplain → -
irc.vim ⑂
syntax file for irc logs
Vim script ★ 1 8y agoExplain → -
vim-interestingwords ⑂
vim-interestingwords allows you to highlight and navigate through (multiple) different words in a buffer
VimL ★ 1 9y agoExplain → -
cpython ⑂
The Python programming language
Python ★ 0 7d agoExplain → -
pyenv ⑂
Simple Python version management
Shell ★ 0 21d agoExplain → -
przgoodies
Small presentations
HTML ★ 0 3mo agoExplain → -
sphinx ⑂
The Sphinx documentation generator
★ 0 4mo agoExplain → -
pylint-pytest ⑂
A Pylint plugin to suppress pytest-related false positives.
★ 0 5mo agoExplain → -
adventofcode2025
No description.
Python ★ 0 7mo agoExplain → -
sentry-python ⑂
A fork for debugging coverage.py #2082
★ 0 8mo agoExplain → -
free-threaded-compatibility ⑂
A central repository to keep track of the status of work on and support for free-threaded CPython (see PEP 703), with a focus on the scientific and ML/AI ecosystem
★ 0 8mo agoExplain → -
sphinxext-rediraffe ⑂
Sphinx extension to redirect files
★ 0 9mo agoExplain → -
zizmor ⑂
Static analysis for GitHub Actions
★ 0 10mo agoExplain → -
open-edx-proposals ⑂
Proposals for Open edX architecture, best practices and processes
Python ★ 0 10mo agoExplain → -
jonesforth ⑂
Mirror of JONESFORTH
Assembly ★ 0 10mo agoExplain → -
dockerfiles
No description.
Dockerfile ★ 0 1y agoExplain → -
packaging.python.org ⑂
Python Packaging User Guide
Python ★ 0 1y agoExplain → -
match_case_prz
A quick presentation
JavaScript ★ 0 1y agoExplain → -
badfile
No description.
★ 0 1y agoExplain → -
pythondotorg ⑂
Source code for python.org
★ 0 1y agoExplain → -
safe-path-tests
No description.
Python ★ 0 1y agoExplain → -
ox_profile ⑂
A simple python statistical profiler which can be used in flask or stand alone projects
★ 0 1y agoExplain → -
roman-numerals ⑂
Manipulate roman numerals
★ 0 1y agoExplain → -
linkchecker ⑂
check links in web documents or full websites
★ 0 1y agoExplain → -
lcov ⑂
LCOV
★ 0 1y agoExplain → -
pydantic ⑂
Data validation using Python type hints
★ 0 2y agoExplain → -
memray ⑂
Memray is a memory profiler for Python
★ 0 2y agoExplain → -
nox ⑂
Flexible test automation for Python
★ 0 2y agoExplain → -
exopublish ▣
No description.
★ 0 2y agoExplain → -
sphinx-inline-tabs ⑂
Add inline tabbed content to your Sphinx documentation. (maintained, though extremely stable as of Jan 2022)
★ 0 2y agoExplain → -
pydata-sphinx-theme ⑂
A clean, three-column Sphinx theme with Bootstrap for the PyData community
★ 0 2y agoExplain → -
rocket-pack-andrew
No description.
Python ★ 0 2y agoExplain → -
tox-gh ⑂
Github Action support for tox 4 and later
★ 0 3y agoExplain → -
github-action-push-to-another-repository ⑂
github Action to push files into another Github repository
★ 0 4y agoExplain → -
signtest
No description.
★ 0 3y agoExplain → -
pycairo ⑂
Python bindings for cairo
★ 0 3y agoExplain → -
playground
Just for random junky playing around
★ 0 4y agoExplain → -
isstemp
No description.
★ 0 4y agoExplain → -
StandWithUkraine ⑂
StandWithUkraine support materials
★ 0 4y agoExplain → -
webhook-testing
No description.
★ 0 2y agoExplain → -
graphql-learn
No description.
HTML ★ 0 4y agoExplain → -
python-docs-theme ⑂
Sphinx theme for Python documentation
★ 0 4y agoExplain → -
composite-gha-upgrade
No description.
Shell ★ 0 4y agoExplain → -
commitlint ⑂
📓 Lint commit messages
★ 0 4y agoExplain → -
cibuildwheel ⑂
🎡 Build Python wheels for all the platforms on CI with minimal configuration.
★ 0 5y agoExplain → -
sphinx-rst-builder ⑂
A Sphinx builder for rST (reStructuredText) files
Python ★ 0 5y agoExplain → -
vim-unstack ⑂
Vim plugin for parsing stack traces and opening the files
Vim script ★ 0 5y agoExplain → -
tryit
No description.
★ 0 5y agoExplain → -
labels
No description.
★ 0 6y agoExplain → -
relplay
How do releases work?
★ 0 6y agoExplain → -
rst_to_md ⑂
Markdown writer for Docutils
Python ★ 0 7y agoExplain → -
rst2ctags ⑂
A simple script to help create ctags-compatible tag files for the sections within a reStructuredText document.
Python ★ 0 7y agoExplain → -
bitbucket-issue-migration ⑂
A small script for migrating repo issues from Bitbucket to GitHub
Python ★ 0 8y agoExplain → -
isect_segments-bentley_ottmann ⑂
BentleyOttmann sweep-line implementation (for finding all intersections in a set of line segments)
Python ★ 0 9y agoExplain → -
vim-space ⑂
text objects for the whitespace
VimL ★ 0 9y agoExplain →
No repos match these filters.