mpfaffenberger / mpfaffenberger/code_puppy

Plugin loader: user/builtin name collisions load both copies; user tier lacks namespace isolation (can shadow stdlib via sys.path); __init__.py fallback breaks relative imports

Open
#441 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
814
Forks
278
Avg merge
2d 5h
Merged PRs (30d)
76

Description

File: code_puppy/plugins/__init__.py

Severity: Low-Medium (plugin-loading correctness)

Several correctness gaps in the three-tier plugin loader:

  1. User plugins can shadow builtins silently. A user plugin named e.g. shell_safety or file_permission_handler loads in addition to the builtin of the same name (builtins import via code_puppy.plugins.<name>.register_callbacks, user plugins via <name>.register_callbacks), so both register callbacks - duplicate prompts/handlers. Project-vs-builtin collisions get a warning (_load_project_plugins, "shadows builtin plugin"), and project-vs-user collisions skip the user copy, but builtin-vs-user collisions are neither deduped nor warned. Suggested: pass builtin_names to _load_user_plugins and at least warn (or skip, matching the documented "only one copy registers" policy).

  2. User-plugin module names can collide with arbitrary top-level packages. module_name = f"{plugin_name}.register_callbacks" and sys.modules[module_name] = module means a user plugin directory named json, email, etc. registers json.register_callbacks in sys.modules and - worse - sys.path.insert(0, user_plugins_dir) makes ~/.code_puppy/plugins/json/__init__.py importable as json, shadowing the stdlib for the whole process. The project tier already solved this with the project_plugins. namespace; user plugins should use a user_plugins. namespace the same way (the asymmetry between tiers is itself a there-should-be-one-way violation).

  3. _load_user_plugins's __init__.py fallback doesn't set __path__, so a user plugin loaded via __init__.py cannot import its own submodules (from . import x -> ModuleNotFoundError), while the project tier's _ensure_plugin_package handles this correctly. Same fix: set submodule_search_locations.

  4. load_plugin_callbacks() returns empty lists on second call instead of the cached result, while get_loaded_plugins() returns the real data - surprising API; return dict(_loaded_plugin_names) on repeat calls instead.

Filed by Zen Reviewer B (code-puppy-60635a)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in code_puppy/plugins/init.py, reading load_plugin_callbacks(), _load_user_plugins(), and the project-tier _ensure_plugin_package for comparison. Trace the three plugin-loading paths and verify the done state: builtin/user collisions are handled consistently, user plugins use namespace isolation without stdlib shadowing, package plugins can import submodules, and repeated loads return cached names.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.