Forbiddenfruit: Patch Python Built-in Objects
Forbiddenfruit is a Python library designed to extend or modify built-in types at runtime. It achieves this by patching built-in objects that are declared in C through Python's C API. While powerful, it is primarily intended for scenarios like advanced testing or profiling, rather than general production use due to its potential for unexpected side effects. The current version, 0.1.4, was released in January 2021, and the project has an infrequent release cadence.
Warnings
- breaking Forbiddenfruit directly modifies Python's built-in types, which is generally discouraged for production code. This can lead to unpredictable behavior, global state corruption, and difficult-to-debug issues, especially when used in complex or shared environments. It is primarily intended for testing or highly isolated development scenarios.
- gotcha The `reverse()` function only removes attributes previously added by `curse()`. If `curse()` was used to *replace* an existing built-in attribute (e.g., `str.__add__`), `reverse()` will delete the replacement but *not* restore the original built-in attribute, potentially leaving the type without its original functionality.
- breaking Forbiddenfruit is fundamentally dependent on the CPython C API and is therefore incompatible with other Python implementations such as Jython or PyPy. It is tested and designed to work exclusively with CPython.
- gotcha Patching certain dunder (magic) methods, particularly rich comparison methods (e.g., `__le__`) or `__hash__`, can lead to `KeyError` or outright interpreter crashes, especially on specific operating systems (like Windows) or with newer Python versions (e.g., Python 3.13 has reported crashes related to dunder method patching).
- gotcha Due to its low-level manipulation of Python's internals, improper or undocumented use of `forbiddenfruit` can easily lead to Python interpreter segfaults (crashes) or place the interpreter into an inconsistent state where expected operations break unexpectedly.
Install
-
pip install forbiddenfruit
Imports
- curse
from forbiddenfruit import curse
- reverse
from forbiddenfruit import reverse
- cursed
from forbiddenfruit import cursed
Quickstart
from forbiddenfruit import curse, cursed, reverse
def words_of_wisdom(self):
return self * " blah "
def temporary_method(self):
return f"I am a temporary string: {self}"
# --- Using curse() and reverse() ---
# Add a new method to the int class
curse(int, "words_of_wisdom", words_of_wisdom)
assert (2).words_of_wisdom() == " blah blah "
print(f"int(2).words_of_wisdom(): {(2).words_of_wisdom()}")
# Remove the added method
reverse(int, "words_of_wisdom")
try:
(2).words_of_wisdom()
except AttributeError as e:
print(f"Method removed: {e}")
# --- Using cursed() as a context manager ---
print("\n--- Using cursed() as a context manager ---")
with cursed(str, "temp_str_method", temporary_method):
s = "hello"
assert s.temp_str_method() == "I am a temporary string: hello"
print(f"Inside context: {s.temp_str_method()}")
try:
"test".temp_str_method()
except AttributeError as e:
print(f"Outside context, method removed: {e}")
# --- Using cursed() as a decorator ---
print("\n--- Using cursed() as a decorator ---")
@cursed(list, "first_element", lambda self: self[0] if self else None)
def demonstrate_list_patch():
my_list = [1, 2, 3]
assert my_list.first_element() == 1
print(f"Inside decorated function: {my_list.first_element()}")
demonstrate_list_patch()
try:
[4, 5].first_element()
except AttributeError as e:
print(f"Outside decorated function, method removed: {e}")