Core Commands
Core Commands
The toolchain in this book is small on purpose: uv run, uv python, python -c, python -m, ruff, pytest. Learn these six and you can run, format, lint, and test every later chapter.
Mental model
uv is the front door. uv run … means “in this project, with this Python, run that command.” You rarely type a bare python.
uv run python file.py— run a script.uv run python -c "…"— run one statement, no file.uv run python -m name— run a module by name (json.tool, oropen_shiftforopen_shift.py).uv python install|pin|list— manage interpreters.uv run ruff format|check— format and lint.uv run pytest— run tests.
ruff and pytest are ordinary packages. Add them once as dev dependencies, then uv run finds them.
Worked examples
Case 1: uv run and uv python
Save as open_shift.py.
# open_shift.py
def main():
print("shift open")
if __name__ == "__main__":
main()Run:
uv run python open_shift.pyOutput:
shift open
Useful uv python commands (output varies by machine):
uv python install 3.14
uv python pin 3.14
uv python list
uv run python --versionThe last one should report Python 3.14.x. That is the check that matters.
Case 2: python -c (no file)
One-off prints and arithmetic do not need a file.
uv run python -c 'print("shift open")'Output:
shift open
Keep -c for probes. Put anything you will run twice in a .py file.
Case 3: python -m runs a module by name
The same open_shift.py can be run as a module. The name is the filename without .py.
uv run python -m open_shiftOutput:
shift open
-m is also how you run tools that shipped as modules. Save as ticket_dump.py:
# ticket_dump.py
import json
def main():
print(json.dumps({"id": 7, "table": 12}))
if __name__ == "__main__":
main()Pretty-print its JSON with the standard library’s json.tool:
uv run python ticket_dump.py | uv run python -m json.toolOutput:
{
"id": 7,
"table": 12
}
Case 4: ruff format and ruff check
Add the linter once (this also creates or updates pyproject.toml / uv.lock):
uv add --dev ruffSave as messy.py — working code, ugly spacing:
# messy.py
def main( ):
tables=8
print( f"tables {tables}" )
if __name__=="__main__":
main()Format it in place:
uv run ruff format messy.pyOutput:
1 file reformatted
messy.py on disk is now:
# messy.py
def main():
tables = 8
print(f"tables {tables}")
if __name__ == "__main__":
main()Save as unused.py for a lint finding:
# unused.py
import json
def main():
tables = 8
print(tables)
if __name__ == "__main__":
main()uv run ruff check unused.pyOutput:
F401 [*] `json` imported but unused
--> unused.py:2:8
|
1 | # unused.py
2 | import json
| ^^^^
help: Remove unused import: `json`
Found 1 error.
[*] 1 fixable with the `--fix` option.
F401 is “imported but unused.” Delete the import, or run uv run ruff check --fix unused.py. The next chapter treats format and lint as policy.
Case 5: pytest on a tiny test
Save as total.py:
# total.py
def line_total(qty, price):
return qty * price
def main():
print(line_total(3, 4))
if __name__ == "__main__":
main()Save as test_total.py in the same folder:
# test_total.py
from total import line_total
def test_line_total():
assert line_total(3, 4) == 12uv add --dev pytest
uv run pytest test_total.py -qOutput (the time on the last line will vary):
. [100%]
1 passed in 0.01s
A test file is a complete program pytest loads. Functions named test_* are collected. assert is the check. You do not add if __name__ == "__main__" to the test file.
The trap
Installing ruff and pytest “globally” with a random pip, then wondering why uv run cannot see them. uv run uses this project’s environment. Add tools with uv add --dev.
The other trap is living in python -c and never saving a file. -c has no traceback you can open, no test, and no format.
The boring rule
- Run scripts with
uv run python file.py. - Probe with
uv run python -c '…'only. - Use
uv run python -m namefor modules and stdlib tools. - Pin 3.14 with
uv python pin 3.14. - Format and lint with
uv run ruff formatanduv run ruff check. - Test with
uv run pytest. - Add
ruffandpytestas dev dependencies, not as runtime dependencies of the desk.
Try this
- Change
open_shift.pyto print the table count. Run it both asuv run python open_shift.pyand asuv run python -m open_shift. - In
test_total.py, addtest_zero_qtythat assertsline_total(0, 4) == 0. Run pytest again. - Run
uv run ruff check unused.py --fix, thenuv run ruff check unused.pyand confirm it is clean.