1 A table, and a tool for the docs
Build it yourself. Read the tutorial is the other way to read this part.
If you start at this part, make your clone as Get the project says, and then run this command. It puts in your clone the files that this part starts from. A file that the project deleted before this part stays, so delete it if git check lists it:
git rm -rq --cached --ignore-unmatch -- :/ && git restore --source 974d653080b0 --staged --worktree -- :/just setupA list of counts is hard to read at a glance. A table lines up the words and the counts. A small package, tabulate, draws tables, so tally depends on it. The docs come later, and their tool comes now, in a group of its own.
The code before this part. The code after it.
1.1 tabulate, in a range of versions
tally needs tabulate to draw a table. A new version 1.0 of tabulate could change how it draws one, so tally takes
any 0.9 version, and no later one. uv add with a range of versions puts the range in pyproject.toml, and the
exact version in uv.lock:
The author ran uv add "tabulate>=0.9,<0.10" here. It writes pyproject.toml and uv.lock. On a later day, it can write newer versions of packages than the author's, and then git check finds that your files differ. So do not run it. Run this command in its place, from the top of the project. It takes each file as the author has it from the project, and installs exactly the versions that uv.lock names:
git restore --source 418e7fd88a3f -- pyproject.toml uv.lock && uv sync --locked --all-groups --all-packagesuv run python -c "import tabulate; print(tabulate.__version__)"It prints 0.9.0.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check 418e7fd88a3fIf you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source 974d653080b0 --staged -- :/ && git restore --source 418e7fd88a3f --staged --worktree -- :/What changed
pyproject.toml: tabulate, with the range of its versions. 3 lines added, 1 removed. It comes fromuv add "tabulate>=0.9,<0.10", and you take it from the project. The file.- look at
"tabulate>=0.9,<0.10",
- look at
uv.lock: the exact version of tabulate. 13 lines added. It comes fromuv add "tabulate>=0.9,<0.10", and you take it from the project. The file.
The diff that uv add "tabulate>=0.9,<0.10" makes in pyproject.toml, which you take from the project
@@ -4,7 +4,9 @@ version = "0.1.0"
description = "Count the words in a text"
readme = "README.md"
requires-python = ">=3.12"
-dependencies = []
+dependencies = [
+ "tabulate>=0.9,<0.10",
+]
[project.scripts]
tally = "tally:main"1.2 tally --table
tally prints one line for each word, with the count first. A new option, --table, prints the same counts as a
table, with a header. main takes the option out of the arguments, and passes the rows to tabulate.
Apply the patch to src/tally/__init__.py, from the top of the project:
curl -fsSL https://github.com/rahuldave/tally/commit/5051c3ff6e71020d5458c29a2e4a1f358d361b16.diff | git apply --include=src/tally/__init__.pyThe patch adds from tabulate import tabulate under import sys, after a blank line. In main, it adds the flag
as_table, and prints the rows with tabulate when the flag is set.
uv run tally --top 3 --table data/corpus.txtIt prints a header, word and times, a line of dashes, and the three words with their counts in a column on the
right.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check 5051c3ff6e71If you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source 418e7fd88a3f --staged -- :/ && git restore --source 5051c3ff6e71 --staged --worktree -- :/What changed
src/tally/__init__.py: the option--table, which prints the rows with tabulate. 11 lines added, 3 removed. The file.- look at
print(tabulate(rows, headers=["word", "times"]))
- look at
The patch to src/tally/__init__.py
@@ -2,6 +2,8 @@
import sys
+from tabulate import tabulate
+
def count(text: str) -> dict[str, int]:
"How many times each word of `text` appears, ignoring case."
@@ -17,11 +19,17 @@ def top(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
def main() -> None:
- "Count the words of a file, or of the standard input. With --top N, only the N most common."
- args, n = sys.argv[1:], None
+ "Count the words of a file, or of the standard input. Options: --top N, and --table."
+ args = sys.argv[1:]
+ as_table = "--table" in args
+ args, n = [arg for arg in args if arg != "--table"], None
if args[:1] == ["--top"]:
n, args = int(args[1]), args[2:]
text = open(args[0]).read() if args else sys.stdin.read()
counts = count(text)
- for word, times in top(counts, n) if n else sorted(counts.items()):
+ rows = top(counts, n) if n else sorted(counts.items())
+ if as_table:
+ print(tabulate(rows, headers=["word", "times"]))
+ return
+ for word, times in rows:
print(times, word)1.3 mdformat, in the group docs
The docs will be Markdown files. mdformat formats Markdown, and its option --check says whether a file is
formatted. Only the people who write the docs need it, so it goes in a group of its own, docs:
The author ran uv add --group docs mdformat here. It writes pyproject.toml and uv.lock. On a later day, it can write newer versions of packages than the author's, and then git check finds that your files differ. So do not run it. Run this command in its place, from the top of the project. It takes each file as the author has it from the project, and installs exactly the versions that uv.lock names:
git restore --source ec6a8aa7f9a8 -- pyproject.toml uv.lock && uv sync --locked --all-groups --all-packagesuv sync installs the group dev and no other group, unless the project names more. One more line in the table
[tool.uv] of pyproject.toml names both groups, so that just setup installs mdformat too.
The command above took pyproject.toml from the project with this change in it already, so there is nothing to apply.
After uv add, the patch adds a table [tool.uv] at the end of pyproject.toml, with the line default-groups.
uv sync -q && uv run --no-sync mdformat --versionuv sync installs mdformat. Then uv run --no-sync runs it without another sync, and it prints mdformat 1.0.0, or a
newer version.
Check your files against this move. The command prints nothing when they match, and otherwise shows what differs:
git check ec6a8aa7f9a8If you are stuck, this command puts the files of this move in place of yours. A file that you made yourself stays:
git restore --source 5051c3ff6e71 --staged -- :/ && git restore --source ec6a8aa7f9a8 --staged --worktree -- :/What changed
pyproject.toml: the groupdocs, and the groups thatuv syncinstalls. 6 lines added. It comes fromuv add --group docs mdformat, and you take it from the project. The file.- look at
default-groups = ["dev", "docs"]
- look at
uv.lock: the exact versions of mdformat and of the packages it needs. 37 lines added. It comes fromuv add --group docs mdformat, and you take it from the project. The file.
The diff that uv add --group docs mdformat makes in pyproject.toml, which you take from the project
@@ -19,6 +19,12 @@ build-backend = "uv_build"
dev = [
"ruff>=0.17.0",
]
+docs = [
+ "mdformat>=1.0.0",
+]
[tool.ruff]
line-length = 100
+
+[tool.uv]
+default-groups = ["dev", "docs"]