Green Selftest, Then an ImportError Caused by a File Name
A CLI tool named keyword.py passed its own selftest yet broke import collections. sys.path order and a transitive import turned out to be the trap.
TL;DR
Naming a local file keyword.py shadows Python's stdlib keyword module because the script directory sits first in sys.path. The trap stays hidden until collections transitively imports keyword and crashes with a confusing ImportError. Fix: avoid stdlib names, run a quick import check, and use Python 3.11's -P flag as backup.
Green Selftest, Red Command Right After
I had a CLI tool file in my tools directory named keyword.py. Running its selftest worked fine, all green. The script itself ran without complaints. Seconds later I tried another command in the same folder that imports collections, and the screen went red:
ImportError: cannot import name 'iskeyword' from 'keyword' (consider renaming
'/path/tools/keyword.py' since it has the same name as the standard library module
named 'keyword' and prevents importing that standard library module)
I was confused. Why did import keyword inside that very file succeed while the import from another module exploded? My first guess was that my own code was broken. The answer turned out to be Python's module search order, and the hint had been sitting in the error message I ignored while panicking.
The sys.path Order and Transitive Imports
Python looks up modules through sys.path, and its first entry is the directory of the script being run, or the current working directory when there is no script file [1]. So when I ran python3 tools/keyword.py, the tools directory automatically became the top search priority.
Then why did import keyword directly inside that file not explode? After re-testing, a direct import did load my local file, and Python startup does not preload the keyword module either. The problem only surfaced through a transitive path. The collections module in CPython 3.13 has one key line in its __init__.py:
from keyword import iskeyword as _iskeyword
The moment collections gets imported, it looks for a module named keyword. Because the tools directory sits first in sys.path, what it found was my local file, not the stdlib module. That file has no iskeyword function, so Python gave up with an ImportError. This is what makes the trap sneaky: the script looks healthy, the selftest is green, and the bomb waits for the first import of collections.
The good news is that recent Python versions already help. The error message explicitly suggests renaming the file that shadows a stdlib module. But while your eyes are bleeding from a long traceback, that hint is easy to skim past.
The -P Flag, Naming Habits, and the Kedro Case
My main fix now is simple: never give a .py file the same name as a stdlib module. Short common names like keyword, json, email, types, code, or time are all taken by built-in modules. Add a specific suffix that matches what the tool does. I also picked up a new habit: run python3 -c "import yourname" ten seconds before settling on a new file name. That check costs far less than chasing a misleading error for hours.
Since Python 3.11 there is also an extra shield: the -P flag or the PYTHONSAFEPATH environment variable, which stops Python from prepending the script directory or cwd to sys.path [2]. With the flag set, the real stdlib module loads even when a local file shares its name. Handy for CI/CD and production containers that need strict isolation, though as someone writing CLI tools daily I still treat it as the second layer after naming hygiene.
This trap has taken down a major framework too. In June 2026, issue #5607 on the Kedro repository reported that kedro new --name=email created a project successfully, but every kedro run crashed with a ModuleNotFoundError pointing nowhere near the real cause [4]. The user's email package shadowed the stdlib email module. Kedro closed it with project-name validation in release 1.5.0, so names colliding with built-in modules are now rejected up front. Even a framework team slipped past the radar once.
The lesson I keep from this: the most expensive failures often come from the most trivial decisions, like picking a file name. Failure patterns like this stay invisible in isolated testing and only appear in combinations nobody expected. That is why the ten-second name check became a habit for me, not a lesson forgotten by tomorrow morning. The same pattern applies widely, for instance when my article cron switched engines, checking names and load paths is always the first thing I do.
- Python Tutorial: Modules, sys.path (docs.python.org) [1]
- Python Setup and Usage: command line, the -P option and PYTHONSAFEPATH (docs.python.org) [2]
- CPython 3.13 source: collections/__init__.py, the from keyword import iskeyword line (github.com) [3]
- Kedro issue #5607: project name shadowing a stdlib module, fixed in 1.5.0 (github.com) [4]