...

You can now use the .🐍 Extension in Python


Now that we understand how Python’s import system works, let’s build our own Finder and Loader to intercept imports and modify module behavior!

🕵️ Creating a Finder

A Finder is responsible for locating modules. To create one, we define a class with a find_spec method:

  • Returns a ModuleSpec object if it finds the module.
  • Returns None if it doesn’t find it, it will allow Python to move to the next import hook.

Let’s implement a custom finder that intercepts imports of a specific module:

import importlib.abc
import importlib.util
from importlib.machinery import PathFinder

class CustomFinder(importlib.abc.MetaPathFinder):
def __init__(self, intercepted_module) -> None:
self.intercepted_module = intercepted_module
self.finder = PathFinder()
super().__init__()

def find_spec(self, fullname, path, target=None):
if not fullname.startswith(self.intercepted_module):
return None # Move to the next finder

print(f"Intercepting import: {fullname}")
spec = self.finder.find_spec(fullname, path, target)
if spec is None:
return None
spec.loader = CustomLoader()
return spec

Now, any attempt to import a module matching intercepted_module will be intercepted by our custom Finder.

⚡Creating a Loader

A Loader is responsible for executing the module code. Our Finder referenced a CustomLoader , so let’s create that next:

To implement a Loader, we need:

  • create_module(self, spec) : Instantiates a new module object.
  • exec_module(self, module) : Executes the module’s code.

Here’s our custom loader in action:

class CustomLoader(importlib.abc.Loader):
def create_module(self, spec):
return None # Use default module creation

def exec_module(self, module):
print(f"Executing module: {module.__name__}")

# Read the module's source code
with open(module.__spec__.origin, "r") as f:
source_code = f.read()

# Modify the source code (add logging)
modified_code = 'print("Custom Hook: Module Loaded!")\n' + source_code

# Execute the modified code within the module's namespace
exec(modified_code, module.__dict__)

Now, when a module is imported, our loader:

  1. Reads its source code.
  2. Modifies it (in this case, adding a log message).
  3. Executes the modified code.

🛠️Installing the Custom Hook

We have our Finder and Loader, but Python won’t use them until we register them in sys.meta_path :

Image by author
import sys

# Register the custom finder to intercept 'json' imports
sys.meta_path.insert(0, CustomFinder("json"))

# Import the target module (should trigger the hook)
import json

🏆Output

When you import json , here’s what happens:

Intercepting import: json
Executing module: json
Custom Hook: Module Loaded!
Intercepting import: json.decoder
Executing module: json.decoder
Custom Hook: Module Loaded!
Intercepting import: json.scanner
Executing module: json.scanner
Custom Hook: Module Loaded!
Intercepting import: json.encoder
Executing module: json.encoder
Custom Hook: Module Loaded!

Since json has submodules ( decoder , scanner , encoder ), our Finder intercepts all of them too !

Our CustomFinder hooks into the import system, intercepting modules before the default importers get a chance.

Source link

#Extension #Python