schmonz.com is a Fediverse instance that uses the ActivityPub protocol. In other words, users at this host can communicate with people that use software like Mastodon, Pleroma, Friendica, etc. all around the world.
This server runs the snac software and there is no automatic sign-up process.
Do not extract file helpers from a messy module yet. Snapshot every resolved absolute path before the first edit. Relative opens form a hidden contract across three roots.
Most failed extracts start as a path-root mismatch. The helper looks cleaner after the extract lands. The files then land in a different directory tree.
A typical messy module mixes three path roots. None of them appear in the public function signature.
os.getcwd() follows the process, not the source file. Pytest, systemd, and cron each change that value.
Path(__file__).resolve() follows the module on disk. A later package move silently retargets every relative open.
An env var such as DATA_DIR may override both. Empty, relative, and absolute values all behave differently.
The public function still accepts only a basename. Callers believe the output path stays stable. That belief does not survive a chdir.
The listing below is a teaching example, not production code. It writes one report beside three different roots.
# report_kit.py — messy on purpose
from __future__ import annotations
import json
import os
from pathlib import Path
HERE = Path(__file__).resolve().parent
def write_daily_report(name: str) -> dict[str, str]:
data_dir = os.environ.get("DATA_DIR", "data")
cwd_out = Path("out") / name
here_out = HERE / "out" / name
env_out = Path(data_dir) / name
payload = {"name": name, "pid": os.getpid()}
text = json.dumps(payload, sort_keys=True) + "\n"
cwd_out.parent.mkdir(parents=True, exist_ok=True)
here_out.parent.mkdir(parents=True, exist_ok=True)
env_out.parent.mkdir(parents=True, exist_ok=True)
cwd_out.write_text(text, encoding="utf-8")
here_out.write_text(text, encoding="utf-8")
env_out.write_text(text, encoding="utf-8")
return {
"cwd": str(cwd_out),
"here": str(here_out),
"env": str(env_out),
}
A naive extract wraps the three write_text calls. It often introduces Path.cwd() in one place. One root then silently absorbs the other two.
Return values still look like relative strings. Tests that assert those strings stay green. The bytes move anyway.
Build a ledger before any helper extract. Record caller, raw argument, and resolved absolute path. Hash the sorted ledger. Treat that hash as a characterization oracle.
# path_ledger.py — teaching harness
from __future__ import annotations
import hashlib
import json
import os
import traceback
from pathlib import Path
LEDGER: list[dict[str, str]] =
[]def _caller() -> str:
frames = traceback.extract_stack()
for frame in reversed(frames[:-1]):
if "path_ledger.py" not in frame.filename:
return f"{frame.filename}:{frame.lineno}:{frame.name}"
return "unknown"
def record(kind: str, raw: str, resolved: Path) -> None:
LEDGER.append(
{
"kind": kind,
"caller": _caller(),
"raw": raw,
"cwd": os.getcwd(),
"resolved": str(resolved.resolve()),
}
)
def ledger_hash() -> str:
blob = json.dumps(LEDGER, sort_keys=True, indent=2)
return hashlib.sha256(blob.encode("utf-8")).hexdigest()
def dump(path: Path) -> str:
path.write_text(
json.dumps(LEDGER, indent=2, sort_keys=True) + "\n",
encoding="utf-8",
)
return ledger_hash()
Wrap writes at the test boundary only. Do not patch production code for this measurement. Run the same invocation the module already trusts.
# test_path_ledger.py — characterization, not a unit test
from __future__ import annotations
from pathlib import Path
from unittest.mock import patch
import path_ledger
import report_kit
GOLDEN = Path(__file__).parent / "goldens" / "report_kit_paths.json"
GOLDEN_HASH = Path(__file__).parent / "goldens" / "report_kit_paths.sha256"
def _traced_write_text(self: Path, *args, **kwargs):
path_ledger.record("Path.write_text", str(self), self)
return Path.write_text(self, *args, **kwargs)
def test_write_daily_report_path_ledger(tmp_path, monkeypatch):
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("DATA_DIR", str(tmp_path / "env-data"))
monkeypatch.setattr(report_kit, "HERE", tmp_path / "pkg")
(tmp_path / "pkg").mkdir()
with patch.object(Path, "write_text", _traced_write_text):
report_kit.write_daily_report("daily.json")
digest = path_ledger.dump(tmp_path / "ledger.json")
if not GOLDEN.exists():
GOLDEN.parent.mkdir(parents=True, exist_ok=True)
GOLDEN.write_text(
(tmp_path / "ledger.json").read_text(encoding="utf-8"),
encoding="utf-8",
)
GOLDEN_HASH.write_text(digest + "\n", encoding="utf-8")
raise AssertionError("golden created; rerun to pin")
assert digest == GOLDEN_HASH.read_text(encoding="utf-8").strip()
Label this pin as an oracle, not as coverage. The first run writes goldens on purpose. The second run fails on any resolved-path drift.
Trace mkdir in the same harness when directories matter. A helper can reuse a folder the original code created. That reuse still retargets later writes.
Use the table before any patch is accepted. Each row is a veto, not a preference.
Signal in the ledger
Safe extract?
Required pin
cwd-relative out/name
Not yet
chdir plus expected absolute
__file__-relative out/name
Not yet
frozen HERE
env-relative DATA_DIR/name
Not yet
empty, relative, absolute env
mixed roots in one function
No
split by root, not call shape
only basenames change
Yes
hash still matches
A model often groups the three writes together. They share write_text, so the grouping looks obvious. The ledger groups them by root instead.
Root grouping is the correct split. Call-shape grouping is the usual defect.
Follow these steps in order and skip none.
open, write_text, mkdir, and Path constructor you might move.DATA_DIR, absolute DATA_DIR.Relative goldens will lie after a machine change. Absolute goldens survive a different checkout path. That is the entire point of the pin.
Commands for the first pin:
mkdir -p goldens
python -m pytest test_path_ledger.py -q
# first run creates goldens and fails
python -m pytest test_path_ledger.py -q
# second run must pass before any extract
Commands after a proposed extract:
python -m pytest test_path_ledger.py -q
git diff -- goldens/report_kit_paths.json
# any resolved-path line change is a rejected patch
A smoking-gun diff looks like this fragment:
- "resolved": "/tmp/pytest-of-dev/test0/out/daily.json"
+ "resolved": "/home/ci/project/out/daily.json"
The source diff can still look like a tidy helper. The ledger line is the reject signal.
The smallest change moves one root. It does not introduce a generic writer yet.
def _write_cwd_report(name: str, payload: str) -> str:
target = Path("out") / name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(payload, encoding="utf-8")
return str(target)
Leave __file__ and DATA_DIR writes in the original function. A later extract can take the second root. Each extract must keep the ledger hash unchanged.
Do not normalize paths inside the new helper. Normalization is a behavior change, not cleanup. Record it as a new ledger when you truly need it.
Avoid .resolve() in the extracted helper. resolve() follows symlinks and can rewrite goldens. Prefer the same construction the messy module already used.
open() and Path modules
Some messy modules still call open() directly. Trace that path with a thin wrapper. Keep the same ledger schema for both styles.
import builtins
from pathlib import Path
_real_open = builtins.open
def traced_open(file, mode="r", *args, **kwargs):
raw = str(file)
path_ledger.record("open", raw, Path(file))
return _real_open(file, mode, *args, **kwargs)
Install the wrapper only inside the characterization test. Production code should keep its original open. The oracle lives in the test process.
If both open and Path.write_text appear, keep both kinds. Dropping one kind hides a root. Hidden roots are how extracts ship broken CI writes.
A free coding model can draft the one-root helper. It cannot choose the root. The ledger already chose.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode provides free model access and a free server option. Use the server to run the four pinned invocations. Feed the model the messy function, the ledger JSON, and the decision table. Reject any patch that alters a resolved field.
The model is a diff generator in this workflow. The hash remains the reviewer for every patch. Do not skip that reviewer for a cleaner diff.
Hash changes but the source looks equivalent. A Path constructor started calling .resolve() early. Absolute strings then diverge on symlinks.
Hash stays stable on a laptop and fails in CI. The golden cwd was never isolated. Rerun the pin under tmp_path.
Only two of three writes appear in the ledger. mkdir created a directory the extract later reuses. Trace mkdir as a first-class event.
Env values look absolute in every golden row. The test set an absolute DATA_DIR only. Add the unset and relative cases before review.
Returned relative strings still match after the extract. Callers never saw the absolute target. Assert the ledger, not the return map.
This ledger does not prove functional correctness of the report. File contents can still rot under this pin. Pair it with a payload hash when bytes matter.
It misses networked I/O by design. HTTP and object-store clients need a different oracle. Do not reuse this hash for those calls.
It is weak against symlink farms in deploy trees. resolve() follows links; absolute() does not. Pick one rule and keep it fixed.
Race conditions remain outside the ledger. Two processes can share one cwd. The ledger is per-process and will not serialize them.
Windows drive letters and UNC paths need extra goldens. Do not copy a POSIX hash onto Windows runners. Split those hashes by platform.
Do not use this workflow on a greenfield module. Write explicit path arguments first in new code. There is nothing useful to characterize there.
Do not use it when output locations must change on purpose. Update the golden in the same commit as the move. Do not treat the hash as sacred then.
Do not use it as a substitute for backup policy. Characterization does not recover overwritten files. Keep real backups for destructive jobs.
Skip it for one-off notebooks and scratch CLIs. The process cwd is the product in those tools. A ledger mostly adds noise there.
.resolve() calls on write paths.DATA_DIR cases include unset, relative, and absolute values.If any box is still open, keep the helper on the branch. Ship the path pin first. The extract can wait for a green hash.
Run the ledger on a free server if you already have one. Keep the extract behind that green hash.#python #testing #refactoring #ai #software #coding #development #engineering #inclusive #community
Characterize Path Resolution Before You Move One open()
Mega Bundle 1 by Jason Turner is the featured bundle of ebooks 📚 on Leanpub!
“Note that passing generator expressions into iterable-accepting functions and classes makes something that looks a bit like a custom comprehension.”
Read more 👉 https://pym.dev/custom-comprehensions/
I swear I just saw something today or yesterday about pathlib vs os.path for parameters.
I have forever done something like:
```
from os.path import isfile
def read_file(file_path: str) -> None:
if not isfile(file_path):
print(f"{file_path} is not a file")
return None
...
```
Which is fine, and returning `None` from there all depends on what you want to happen; should it raise an Exception etc..
So instead of `file_path: str` and passing a `pathlib.Path` or similar would be calling a method on the object itself instead of just calling `os.path.isfile` (or similar).
What am I missing here? Am I trying to be too creative about something trivial?
If you are ever doing
try:
...
except ...:
log.failure("...")
anywhere in your Twisted #Python code, you should probably be doing
with mylog.failuresHandled("while doing operation"):
operation()
instead
https://docs.twisted.org/en/stable/api/twisted.logger.Logger.html#failuresHandled
Inspired by multiple years consuming blog posts from @hynek and @glyph, I realize I wish there was more guidance out in the world on when to use threads vs when to use multi-process workers vs when to use a queue-based task system like celery or rq.
In the spirit of the Zen of #python, it feels like there should be a “80% of the time do this” guide to these topics.
RE: https://fosstodon.org/@novelwriter/117218912909489410
I made some pretty significant changes under the hood for this release, which introduces real Qt threading, something that wasn't really used properly before. (Spell checking now runs off the main UI thread.)
The risk here is that the Python garbage collector isn't really designed for running on multiple threads. I now handle it the same way Calibre does.
I've spent many hours working on my own projects with this version, and it has been very stable on my system.
Do we already have a term for people whose ability to reason has seriously deteriorated after they started relying on #AI? If not, I propose slopbrain.
On a totally different topic, am I missing something here or is the maintainer plain wrong? https://github.com/tox-dev/toml-fmt/issues/450#issuecomment-5489314881
Top Links
WinUI OSS Update: Phased Rollout Toward Open Collaboration (Beth Pan) - Rollout complete
TechBash 2026 - Communication Workshop Spotlight (plus don't miss out on Labor Day registration savings) (TechBash Team)
What were the biggest technical shifts in Visual Studio and …
Problem: It was a pain in the ass to install Bram (a Tauri app) on a Windows machine that did not have Python installed. Why needed? The hooks that enforce the workflow were written in Python.
Solution: Port that apparatus to Rust, bake it into the cross-plaform binary. In the Before Time that would make no sense. Now it's a breeze.
Is Rust the language I'd choose for that job? No. Does that matter? No. I am not writing the code, only directing the agents who do it. For them, programming languages are fungible.
GNU Emacs people who write Python using venvs, what's the state of the art for getting linters, LSP servers, and so on started with the right paths/etc set up for a particular project's venv? Is it (still) direnv + the envrc package, or something else (.dir-locals.el with a exec-path setting, maybe)?
Top Links
Repeating the Same AI Prompts? The Agent Skills Handbook Helps You Build Reusable Skills (Suchitha Ramesh)
Copilot Code Reviews for Azure Repos (public preview) (Dan Hellem)
Visual Studio Code 1.136 (Insiders) (Visual Studio Code Team)
.NET Rocks! - Arguing about AI wit…
Top Links
Explore new features available in C# 15 preview (Bill Wagner)
.NET Conf 2026 (Jon Galloway)
Unlocking the Power of AI for Every Developer in Visual Studio with Bring your Own Model (Tanmayee Kamath)
From dotnet run to Foundry Hosted Agent in 3 lines of C# (Bruno Capuano…
Top Links
Today I will… Modernize a .NET application (Matt Soucoup & Pablo Lopes)
The New MCP Roadmap (David Soria Parra & Den Delimarsky)
Microsoft Agent Framework for .NET v1.19.0 Release (Roger Barreto)
Build AI agents without leaving VS Code, join our 3-part Reactor Series (b…
Top Links
WPF Hot Reload in Rider | JetBrains (Michael Hawker)
GitHub: The August 17 outage, and the work ahead (Vlad Fedorov)
terminal-code - VS Code Inside Your Terminal (Rob Pruzan) - Source on GitHub here
The Hanselminutes Podcast - Compile after Class Crossover Episode (Scot…
Build Your Own Coding Agent by J. Owen is on sale on Leanpub! Its suggested price is $34.99; get it for $15.99 with this coupon: https://leanpub.com/build-your-own-coding-agent/c/LeanpubWeeklySale20260818 #ai #python #software_engineering #machine_learning #computer_programming
Dew Drop Weekly Newsletter 497 - Week Ending August 21, 2026
#dewdrop #newsletter #azure #javascript #aspnetcore #css #blazor #cpp #windowsdev #xaml #dotnet #csharp #ai #mcp #python #devops #agile #appdev #IoT #podcasts #m365 #data #sqlserver #terminal #devtools
NEW! Leanpub Book LAUNCH 🚀 Python Concepts Questions & Code: The Total Pythoneering Learning System by Total Pythoneereing
#books #leanpublishing #selfpublishing #Python #PythonInterview #LearnPython #Pythoneering #DevSecOps
The Open Geospatial Python Series by Qiusheng Wu 📚 on Leanpub!
Apprenez Python pour le géospatial, des bases jusqu’à la GeoAI avancée. Développez des workflows concrets pour les SIG, la gestion des données spatiales et l’analyse basée sur l’IA.
Should we remove Flask's interactive debugger? (Being able to execute code at any frame in a traceback.) It's a huge amount of complexity and source of constant invalid security reports. I'm not sure it's been needed for a long time. https://github.com/pallets/werkzeug/issues/3244 #python #flask
UPDATE: we've got a volunteer for the proximate issue, but I'd love to have some more XMPP folks involved generally! Especially to have more than one!
We could really use some help with Twisted's #Python #XMPP and #Jabber implementation. Could you DM if these terms of are of interest to you and if you have any spare capacity to review a little bit of code? It would be much appreciated.
Conformal Prediction and CatBoost by Valery Manokhin 📚 on Leanpub!
Link: https://leanpub.com/b/conformal_prediction_and_catboost
Advanced Python Tips by Rahul Agarwal is free with a Leanpub Reader membership! Or you can buy it for $7.99! https://leanpub.com/advancedpythontips #data_science #software #computer_programming #python
Top Links
Windows App Development CLI v0.6 – create new WinUI applications, sign packages with Azure, and more (Zachary Teutsch) - *Release notes available on *GitHub
Uno Apps Inside of Uno Apps (Steve Bilogan)
How to bring your software delivery workflow into GitHub with agent a…
Alex Martelli coined the term "goose typing" to describe this ability in Python to practice duck typing while also using isinstance and issubclass.
Read more 👉 https://pym.dev/goose-typing/
So if you're wondering how great #RustLang is, let's talk about #mypy. Mypy introduced its own Rust - #Python package, ast-serialize. This package had a deep crate dependency that was broken on PowerPC. The issue was fixed a month ago but everything is still blocked on a deep chain of dependencies being updated and released.
https://github.com/mypyc/ast_serialize/issues/68
EDIT: yes, I know, it's not Rust, it's Cargo. Because obviously Rust without Cargo makes so much sense for that one project using it.
Top Links
.NET 11 Preview 7 is now available! (.NET Team)
Agentic Skills Demystified (Sam Basu)
MAI-Code-1-Flash Highlights Microsoft’s Lower-Cost Approach to Coding AI (Ali Farhat)
Today I will… manage Git Submodules without leaving the IDE (Harshada Hole)
Scott & Mark Learn To.…
A Short Guide to Naming by Tim Ottinger is free with a Leanpub Reader membership! Or you can buy it for $6.50! https://leanpub.com/naming_shortguide #computer_programming #python #javascript #java #web_development #refactoring
Related, I've long wondered why I don't see more #Python projects with .py domains, from the TLD for Paraguy. Perhaps there are restrictions that prevent it, or it's just not as cool as Anguilla?
Top Links
Multi-Language Support for Cross-Platform .NET (Sam Basu)
How to keep thinking (Sean Goedecke)
7 Powerful Ways to Automate PowerPoint Generation in C# (Arun Kumar Chandrakesan)
Implement BFF using Auth0, Angular and ASP.NET Core (Damien Bowden)
ZoomIt for Mac: How AI Po…