A minimal Python package showing how to bootstrap and auto-discover local plugin modules at runtime using a two-phase binding approach
- Zero-boilerplate plugins — just drop a
<plugin_name>.pynext to your script. - Bootstrapped imports — automatically binds plugin without path hacks.
- Configurable filename — pick any
<plugin_name>.py. - Pure stdlib — no extra dependencies.
The bootstrapper pattern solves these pain‑points by:
- Giving you a single drop‑in file (
<plugin_name>.py) that overrides behaviour—no need to touch the upstream package. - Avoiding path juggling by locating the plugin relative to the script you actually run, not the working directory.
- Preventing circular‑import errors via an early, provisional binding.
- Staying dependency‑free—everything is done with the Python standard library.
- Needing a simple plugin capability in your Python project.
- Small tools or projects where simplicity is key.
- Educational, prototyping, or single-user tools.
- Scenarios where you want users to customize behavior by simply editing or replacing a file.
| Scenario | Benefit |
|---|---|
| Custom math/physics kernels in a simulation repo | Collaborators can add their own formulas without editing core code |
| Data‑processing pipelines (ETL) | Drop a new transform.py to adjust business logic per deployment |
| Classroom assignments | Instructors ship a locked framework; students implement only solution.py |
| CLI tools with pluggable commands | Users add commands merely by shipping an extra file |
| Rapid prototyping | Swap algorithms by switching which plugin file sits next to the script |
If you ever find yourself telling users “clone the repo, then modify some_module.py,” consider adopting this lightweight bootstrapper instead.
Clone it first using your preferred method:
# HTTPS
git clone https://github.com/aslan-ng/python-bootstrapper.git
# SSH
git clone git@github.com:aslan-ng/python-bootstrapper.git
# GitHub CLI
gh repo clone aslan-ng/python-bootstrapperThen, install in editable mode:
cd python-bootstrapper
pip install -e .Without defining this initial part, the code will face circular import error during runtime. This works as the default so that from bootstrapper import Math never fails—even while plugins are still loading.
from .main import Math as _BasePackage
Math = _BasePackageLater, it calls:
Math = _discover_local(plugin_name="customize")which:
- Finds the directory of the running script.
- Builds the filename
<plugin_name>.py. - If present, imports that file as a module.
- Scans for any subclass of _BasePackage.
- Returns that subclass—or falls back to _BasePackage.
Typical approaches to extensibility often require users to:
- Fork the library and edit core code.
- Change the package name (bootstrapper) to match your own module.
- Change package files and the base class name (Math) in
main.pyto your own. - In
__init__.py, changeplugin_namein_discover_localfunction. - Write
<plugin_name>.pynext to your script, subclassing your base class. - Start using your customized package! In your entry-point, simply import your base class. The bootstrapper will swap in your subclass automatically.
After configuring your project based on "Adapting to Your Own Project”, the usage will look like this:
# project/plugin.py
import MyClass
class MyPluginName(MyClass):
def my_method(my_inputs):
# Override the default method
...# project/file.py
import MyClass
MyClass.my_method(my_inputs)
...For more usage examples, refer to the examples folder in the project repository.
This is a minimalistic, intentionally simple approach to enable plugin capability by local file discovery. The bootstrapper expects a single plugin file (<plugin_name>.py) placed next to your script. All plugin classes inside the file must inherit from the base class, and they will be merged automatically into a single class at runtime. No error reporting is provided if multiple classes define conflicting methods: the last class in the inheritance chain takes precedence silently. Finally, It does not support more advanced plugin lifecycle management (e.g., enabling/disabling plugins dynamically, multiple isolated plugins, or plugin priorities).
Distributed under the MIT License. See LICENSE.txt for more information.
