Skip to main content

IPython magic to patch modules before you import them

Project description

patchimport-magic

Build Status PyPI - Version PyPI - Downloads PyPI - License

IPython magic to patch modules before you import them.

patchimport-magic provides a cell magic (%%patchimport) that allows you to apply quick, in-memory patches to any installed Python module directly from a Jupyter Notebook or IPython session. This is incredibly useful for rapid debugging, performance profiling, experimenting with library internals without forking, or testing a potential fix on the fly.


🤔 Why?

Have you ever wanted to:

  • Quickly A/B test a function's performance with %%timeit?
  • Add a print statement inside a third-party library function to see what's going on?
  • Test a one-line fix for a bug without cloning and reinstalling the entire package?
  • Experiment with a function's behavior by temporarily changing its source code?

patchimport-magic lets you do all of this and more, right from your notebook.


🚀 Installation

Install the package from PyPI:

pip install patchimport-magic

✍️ Usage

First, load the extension in your IPython or Jupyter environment.

%load_ext patchimport

The magic command has two main forms:

  1. Inserting Code: %%patchimport <module_name> <start_line>
  2. Replacing Code: %%patchimport <module_name> <start_line> <end_line>

After running the magic cell, you must import the module in a new cell for the changes to take effect.

You can also do %unpatchimport <module_name> to revert your patch.

Example 1: Replacing Code

Let's modify the behavior of random.choice(). We can patch it to always return the first element of a sequence by replacing its original implementation.

# %%
%load_ext patchimport
!grep -n -A10 -e 'def choice(' /usr/lib/python3.9/random.py
# We find out the return we want to replace is in line 346 and has 8 spaces indentation
# %%
%%patchimport random 346 347
        return seq[0] # Always return the first element!

# %%
import random

my_list = ['apple', 'banana', 'cherry']
print(f"Patched random.choice: {random.choice(my_list)}")

Example 3: A/B Performance Testing with %%timeit

This is where patchimport-magic really shines. Imagine you have written a utility module and suspect one of its functions is slow. You can quickly test an alternative implementation without changing the file.

Scenario: Let's say you have this file, my_utility_module.py:

# my_utility_module.py
def find_common_elements(list1, list2):
    """Finds common elements using a slow, nested loop approach."""
    common = []
    for item in list1:
        if item in list2: # O(n) lookup for lists is slow!
            common.append(item)
    return common

Now, in your notebook, first time the original, slow version.

# In your Notebook:

# 1. Create some data
import my_utility_module

data = list(range(1000))
sample_to_find = list(range(500, 1500))

# 2. Time the original implementation
print("Timing original (list-based) implementation:")
%timeit my_utility_module.find_common_elements(data, sample_to_find)

You hypothesize that converting list2 to a set for O(1) lookups will be much faster. Let's patch it and find out!

# 3. Patch the function with a faster implementation
# We will replace the body of the function (lines 3 to 7)
# Note: The end line in the magic (8) is exclusive.
%%patchimport my_utility_module 3 8
    # A much faster implementation using a set for O(1) lookups.
    set2 = set(list2)
    return [item for item in list1 if item in set2]

# 4. Re-import and time the new version
import my_utility_module # This re-import loads the patched version

print("\nTiming patched (set-based) implementation:")
%timeit my_utility_module.find_common_elements(data, sample_to_find)

Expected Output:

You will see a dramatic performance improvement, proving your hypothesis in seconds.

Timing original (list-based) implementation:
11.5 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Patch applied from line 3 to 8:
...

REMEMBER TO DO `import my_utility_module` OR `from my_utility_module import ...` AFTER THIS MAGIC CALL!

Timing patched (set-based) implementation:
33.1 µs ± 0.2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

This workflow allows for incredibly fast, iterative performance tuning without ever leaving your notebook.


⚙️ How It Works

This magic uses Python's importlib library. It locates the source file for the specified module, reads its content, and applies the patch from your cell to the source code string in memory. Then, it compiles this new, modified source code and replaces the original module object in sys.modules. When you call import in the next cell, you get the patched version.


⚠️ Limitations

  • In-Memory Only: Patches are not saved to disk and last only for the current kernel session.
  • Indentation: You must provide the correct indentation for your patch code. There is no auto-indentation.
  • ?? Operator: Using ?? in IPython/Jupyter to view a patched object's source may show the original, unpatched code.
  • Development Use: This tool is designed for interactive debugging and experimentation, not for use in production code.

🤝 Contributing

Contributions are welcome! Whether it's reporting a bug, suggesting an enhancement, or submitting a pull request, your help is appreciated.

Development Setup

  1. Fork and clone the repository.
  2. It is recommended to create a virtual environment:
    python -m venv .venv
    source .venv/bin/activate
    
  3. Install the project in editable mode along with its development dependencies:
    pip install -r requirements_dev.txt
    

Running Tests & Linters

This project uses tox to automate linting and testing across multiple Python versions. It ensures code quality and compatibility.

To run the full suite of checks, simply execute:

tox

Bug Reports & Feature Requests

Please use the GitHub Issues to report bugs or request new features. When reporting a bug, please include:

  • Your operating system and Python version.
  • A minimal, reproducible example demonstrating the issue.
  • The expected behavior and what actually happened.

Pull Requests

  1. Create a new branch for your feature or bugfix.
  2. Make your changes and add or update tests as appropriate.
  3. Ensure all tests and lint checks pass by running tox.
  4. Submit a pull request with a clear description of your changes.

📄 License

This project is licensed under the BSD-3-Clause License. See the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

patchimport_magic-0.2.0.tar.gz (9.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

patchimport_magic-0.2.0-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file patchimport_magic-0.2.0.tar.gz.

File metadata

  • Download URL: patchimport_magic-0.2.0.tar.gz
  • Upload date:
  • Size: 9.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for patchimport_magic-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7eb02c5c12b1675d5967122857d470fe4d472463011e0dc0e8d868f376e62a04
MD5 d019a3312eef402806be66a34a7fce39
BLAKE2b-256 a46e363f51920c8dfe7f9f575f9a776345b9b11b8a051d6aca7e68ccf04e1c88

See more details on using hashes here.

Provenance

The following attestation bundles were made for patchimport_magic-0.2.0.tar.gz:

Publisher: pypi.yml on dev-random-sas/patchimport-magic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file patchimport_magic-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for patchimport_magic-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 942b4c81ac34343eaff8c831c5714399e8292d83f339008ab0e8dd61ce8ebba4
MD5 37cb3882e4af60c03c3d8c2534dd3c38
BLAKE2b-256 54e5694ddd5784bfc4b0dfed1967cc29d5bcf61cab52648f7f6863f985b9863c

See more details on using hashes here.

Provenance

The following attestation bundles were made for patchimport_magic-0.2.0-py3-none-any.whl:

Publisher: pypi.yml on dev-random-sas/patchimport-magic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page