
Research
Malicious fezbox npm Package Steals Browser Passwords from Cookies via Innovative QR Code Steganographic Technique
A malicious package uses a QR code as steganography in an innovative technique.
A powerful and flexible Python plugin system that enables dynamic loading, hot-reloading, and management of plugins in your applications. Build extensible software with ease!
Install PlugFlow using pip:
pip install plugflow
Or install from source:
git clone https://github.com/keklick1337/plugflow.git
cd plugflow
pip install -e .
from plugflow import PluginManager
# Create plugin manager with plugins directory
manager = PluginManager(plugins_paths=["plugins/"])
# Load all plugins from configured paths
manager.load_all()
# Process a command through plugins
result = manager.handle_message("/hello world")
print(result) # Output from plugin that handles "hello" command
# Send events to plugins
manager.dispatch_event("user_login", {"user_id": 123})
Create a file plugins/greeter.py
:
from plugflow import BasePlugin
class GreeterPlugin(BasePlugin):
name = "greeter"
version = "1.0.0"
priority = 10 # Higher priority loads first
def on_load(self, manager):
"""Called when plugin is loaded"""
print(f"Greeter plugin v{self.version} loaded!")
def handle_command(self, command: str, args: str):
"""Handle commands"""
if command == "hello":
return f"Hello, {args}!"
elif command == "goodbye":
return f"Goodbye, {args}!"
return None # Command not handled
def on_event(self, event: str, data, manager):
"""Handle events from other plugins"""
if event == "user_login":
print(f"User {data['user_id']} logged in!")
Create a directory plugins/advanced_greeter/
:
plugins/advanced_greeter/
├── __init__.py
├── handlers.py
└── utils.py
plugins/advanced_greeter/__init__.py
:
from plugflow import BasePlugin
from .handlers import CommandHandler
from .utils import format_message
class AdvancedGreeterPlugin(BasePlugin):
name = "advanced_greeter"
version = "2.0.0"
def __init__(self):
super().__init__()
self.handler = CommandHandler()
def handle_command(self, command: str, args: str):
if command in ["greet", "welcome"]:
return self.handler.handle_greeting(command, args)
return None
The main class that manages plugin lifecycle and coordination:
from plugflow import PluginManager
manager = PluginManager(
hot_reload=True, # Enable hot reload
context={"app": my_app} # Share context with plugins
)
# Load plugins
manager.load_from_path(Path("path/to/plugin.py"))
manager.load_all() # Load all plugins from configured paths
# Manage plugins
manager.unload_plugin("plugin_name")
manager.reload_plugin("plugin_name")
# Plugin communication
result = manager.handle_message("/command args") # Handle chat-style messages
manager.dispatch_event("event_name", data)
# Get plugin information
plugins = manager.list_plugins()
plugin = manager.get("plugin_name")
Base class for all plugins:
from plugflow import BasePlugin
from typing import Optional, Any
class MyPlugin(BasePlugin):
name = "my_plugin" # Required: unique plugin name
version = "1.0.0" # Required: plugin version
priority = 10 # Optional: loading priority (higher first)
dependencies = ["other"] # Optional: plugin dependencies
def on_load(self, manager) -> None:
"""Called when plugin is loaded"""
pass
def on_unload(self, manager) -> None:
"""Called when plugin is unloaded"""
pass
def handle_command(self, command: str, args: str) -> Optional[str]:
"""Handle commands - return result or None if not handled"""
return None
def filter_message(self, text: str) -> Optional[str]:
"""Filter/modify messages - return modified text or None"""
return None
def on_event(self, event: str, data: Any, manager) -> None:
"""Handle events from other plugins"""
pass
Enable automatic plugin reloading during development:
manager = PluginManager(
plugins_paths=["plugins/"],
hot_reload=True # Enable hot reload for development
)
manager.load_all()
# Now edit your plugins - changes will be detected automatically!
Share application state and resources with plugins:
# Create manager with shared context
context = {
"database": db_connection,
"config": app_config,
"logger": logger
}
manager = PluginManager(context=context)
# Plugins can access context
class DatabasePlugin(BasePlugin):
def on_load(self, manager):
db = self.context.get("database")
logger = self.context.get("logger")
logger.info("Database plugin connected!")
Plugins can communicate through events:
# Plugin A sends event
class PublisherPlugin(BasePlugin):
def handle_command(self, command, args):
if command == "notify":
# Dispatch event to all plugins
self.manager.dispatch_event("notification", {
"message": args,
"timestamp": time.time()
})
# Plugin B receives event
class SubscriberPlugin(BasePlugin):
def on_event(self, event, data, manager):
if event == "notification":
print(f"Received: {data['message']}")
Specify plugin loading order with dependencies:
class DatabasePlugin(BasePlugin):
name = "database"
priority = 100 # Load first
class UserPlugin(BasePlugin):
name = "user_manager"
dependencies = ["database"] # Load after database
def on_load(self, manager):
# Database plugin is guaranteed to be loaded
db_plugin = manager.get_plugin("database")
Plugins are isolated - errors don't crash your application:
class FaultyPlugin(BasePlugin):
def handle_command(self, command, args):
if command == "crash":
raise Exception("Plugin error!")
return None
# Manager handles plugin errors gracefully
try:
result = manager.handle_command("crash", "")
except Exception as e:
print(f"Plugin error handled: {e}")
# Application continues running
PlugFlow includes comprehensive examples in the examples/
directory:
examples/cli_tool/
)Professional command-line utility with plugin-based commands:
cd examples/cli_tool
python cli.py help # Show all commands
python cli.py hash md5 "test" # Cryptographic operations
python cli.py tree . 2 # File system utilities
Features: Type-safe plugins, clean output, debug mode, comprehensive help
examples/tg_stub/
)Production-ready bot simulation with advanced features:
cd examples/tg_stub
python bot.py # Clean production mode
python bot.py --debug # Development mode with logs
Features: Command handling, message filtering, dynamic help, hot reload
examples/tk_app/
)Sophisticated tkinter application with plugin integration:
cd examples/tk_app
python app.py
Features: Menu integration, real-time logging, file operations, event system
examples/web_server/
)Flask-based web application with plugin routes:
cd examples/web_server
python server.py
Features: Dynamic routing, middleware, API endpoints, template system
from plugflow import BasePlugin
from typing import Optional
class MyPlugin(BasePlugin):
# Plugin metadata
name = "my_plugin"
version = "1.0.0"
description = "My awesome plugin"
author = "Your Name"
def on_load(self, manager):
"""Initialize plugin resources"""
self.data = {}
print(f"{self.name} loaded!")
def on_unload(self, manager):
"""Clean up plugin resources"""
self.data.clear()
print(f"{self.name} unloaded!")
def handle_command(self, command: str, args: str) -> Optional[str]:
"""Process commands"""
commands = {
"status": self._status,
"set": self._set_data,
"get": self._get_data
}
if command in commands:
return commands[command](args)
return None
def _status(self, args: str) -> str:
return f"Plugin {self.name} v{self.version} - {len(self.data)} items"
def _set_data(self, args: str) -> str:
key, value = args.split("=", 1)
self.data[key] = value
return f"Set {key} = {value}"
def _get_data(self, args: str) -> str:
return self.data.get(args, "Key not found")
on_unload
for proper cleanupimport unittest
from plugflow import PluginManager
from my_plugin import MyPlugin
class TestMyPlugin(unittest.TestCase):
def setUp(self):
self.manager = PluginManager()
# Manually add plugin for testing
from plugflow.manager import PluginRecord
self.plugin = MyPlugin()
record = PluginRecord(self.plugin, Path("test"), None)
self.manager._records[self.plugin.name] = record
def test_command_handling(self):
result = self.manager.handle_message("/status")
self.assertTrue(any("Plugin my_plugin" in r for r in result))
def test_data_operations(self):
self.manager.handle_message("/set key=value")
result = self.manager.handle_message("/get key")
self.assertTrue(any("value" in r for r in result))
load_from_path(path: Path) -> None
: Load plugins from a file or directoryload_all() -> None
: Load all plugins from configured pathsunload_plugin(name: str) -> bool
: Unload a plugin by namereload_plugin(name: str) -> bool
: Reload a plugin by namehandle_message(text: str) -> List[str]
: Process message through plugins (supports /commands and filters)dispatch_event(event: str, data: Any = None) -> List[Any]
: Send event to all pluginsbroadcast(method: str, *args, **kwargs) -> List[Any]
: Call method on all plugins that have itlist_plugins() -> List[str]
: Get list of loaded plugin namesget(name: str) -> Optional[BasePlugin]
: Get plugin instance by namestop() -> None
: Stop hot reload watchershot_reload: bool
: Enable/disable hot reloadcontext: Dict[str, Any]
: Shared context dictionaryname: str
: Unique plugin identifierversion: str
: Plugin versionpriority: int
: Loading priority (default: 0)dependencies: List[str]
: Required pluginsdescription: str
: Plugin descriptionauthor: str
: Plugin authoron_load(manager: PluginManager) -> None
: Initialization hookon_unload(manager: PluginManager) -> None
: Cleanup hookhandle_command(command: str, args: str) -> Optional[str]
: Command handlerfilter_message(text: str) -> Optional[str]
: Message filteron_event(event: str, data: Any, manager: PluginManager) -> None
: Event handlerPlugin Not Loading
# Check plugin file syntax
manager.load_from_path(Path("plugin.py"))
# Enable debug logging
import logging
logging.basicConfig(level=logging.DEBUG)
Import Errors
# Ensure plugin directory is in Python path
import sys
sys.path.append("plugins")
Hot Reload Not Working
# Ensure hot_reload is enabled
manager = PluginManager(hot_reload=True)
# Check file permissions and watching capability
Enable verbose logging:
import logging
logging.basicConfig(level=logging.DEBUG)
manager = PluginManager(hot_reload=True)
# Now you'll see detailed plugin loading information
We welcome contributions!
git clone https://github.com/keklick1337/plugflow.git
cd plugflow
pip install -e ".[dev]"
pre-commit install
pytest tests/
This project is licensed under the MIT License - see the LICENSE file for details.
Built with love for the Python community
PlugFlow makes it easy to create extensible applications. Start building your plugin ecosystem today!
FAQs
A powerful Python plugin system with dynamic loading and hot-reload capabilities.
We found that plugflow demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.
Application Security
/Research
/Security News
Socket detected multiple compromised CrowdStrike npm packages, continuing the "Shai-Hulud" supply chain attack that has now impacted nearly 500 packages.