CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-makefun

Small library to dynamically create python functions.

Pending
Overview
Eval results
Files

compilation.mddocs/

Compilation Utilities

Function compilation utilities for masking implementation details from debuggers while preserving source code access for development purposes. These utilities provide a way to create "compiled" versions of functions that hide their implementation from debugging tools while maintaining functionality.

from typing import Union, Callable, Iterable

Capabilities

Function Compilation Decorator

Decorator that compiles functions to mask their implementation from debuggers while preserving source code access.

def compile_fun(recurse: Union[bool, Callable] = True, 
               except_names: Iterable[str] = ()) -> Union[Callable, Callable[[Callable], Callable]]:
    """
    Decorator to compile functions for debugging convenience.
    
    Compiles any existing function so that users can't debug through it,
    which can be handy to mask some code from users for convenience.
    The source code is preserved in the __source__ attribute.
    
    Parameters:
    - recurse: Union[bool, Callable], default True
        If True, recursively compile referenced functions in closure.
        If Callable, apply compilation immediately (no-args decorator usage).
    - except_names: Iterable[str], default ()
        Function names to exclude from recursive compilation
        
    Returns:
    Union[Callable, Callable[[Callable], Callable]]: 
        - If called with parentheses: returns decorator function
        - If called without parentheses: returns compiled function directly
        
    Raises:
    UnsupportedForCompilation: If target is not a function
    UndefinedSymbolError: If function references undefined symbols
    SourceUnavailable: If function source code cannot be retrieved
    
    Note:
    - Compilation does not improve performance (per Python design)
    - Primary use is hiding implementation details during debugging
    - Source code remains available via __source__ attribute
    - Recursively compiles closure functions by default
    """

Exception Classes

class UndefinedSymbolError(NameError):
    """
    Raised by compile_fun when function requires symbols not yet defined.
    
    This typically occurs when compilation is applied before all required
    symbols (variables, functions, classes) have been defined in the scope.
    """

class UnsupportedForCompilation(TypeError):
    """
    Raised by compile_fun when the decorated target is not supported.
    
    Only function objects can be compiled. Other types (classes, modules,
    built-ins, etc.) are not supported.
    """

class SourceUnavailable(OSError):
    """
    Raised by compile_fun when function source code is not available.
    
    This occurs when inspect.getsource() cannot retrieve the source code,
    typically for built-in functions, C extensions, or functions defined
    in interactive sessions without source preservation.
    """

Usage Examples

Basic Function Compilation

from makefun import compile_fun

@compile_fun
def secret_algorithm(x: int, y: int) -> int:
    """Perform secret calculation."""
    # Complex implementation that we want to hide
    intermediate = x * 2 + y
    result = intermediate ** 2 - x
    return result % 1000

# Function works normally
print(secret_algorithm(5, 3))  # Result: 121

# Source code is preserved for reference
print(secret_algorithm.__source__)
# Shows the original function definition

# But debugger cannot step into the implementation
# (implementation is compiled bytecode, not original source)

Compilation with Parameters

from makefun import compile_fun

# Compile with recursive compilation disabled
@compile_fun(recurse=False)
def simple_function(x: int) -> int:
    """Simple function without recursive compilation."""
    return x * 2

# Compile with exception list
def helper_function(x):
    return x + 1

@compile_fun(recurse=True, except_names=["helper_function"])
def main_function(x: int) -> int:
    """Main function that uses helper."""
    return helper_function(x) * 2

print(main_function(5))  # 12
# main_function is compiled, but helper_function is not

No-Arguments Decorator Usage

from makefun import compile_fun

# Direct application without parentheses
@compile_fun
def immediate_compilation(data: list) -> int:
    """Function compiled immediately."""
    return sum(data) if data else 0

print(immediate_compilation([1, 2, 3, 4]))  # 10

Hiding Complex Logic

from makefun import compile_fun

@compile_fun
def proprietary_calculation(values: list, config: dict) -> dict:
    """Proprietary algorithm for data processing."""
    # Complex proprietary logic that should be hidden
    weights = config.get("weights", [1.0] * len(values))
    
    processed = []
    for i, val in enumerate(values):
        weight = weights[i] if i < len(weights) else 1.0
        # Complex transformation
        transformed = (val * weight + config.get("bias", 0)) ** config.get("power", 1)
        processed.append(transformed)
    
    return {
        "processed_values": processed,
        "total": sum(processed),
        "average": sum(processed) / len(processed) if processed else 0,
        "config_used": config
    }

# Function works normally but implementation is hidden from debugger
config = {"weights": [1.5, 2.0, 1.0], "bias": 10, "power": 1.2}
result = proprietary_calculation([100, 200, 150], config)
print(f"Total: {result['total']:.2f}")

Recursive Compilation Example

from makefun import compile_fun

def helper1(x):
    return x * 2

def helper2(x):
    return x + 10

@compile_fun(recurse=True)
def main_with_helpers(x: int) -> int:
    """Function that uses helper functions."""
    # Both helper functions will be compiled recursively
    intermediate = helper1(x)
    return helper2(intermediate)

print(main_with_helpers(5))  # 20 (5*2 + 10)

# All functions in the closure are compiled
# main_with_helpers, helper1, and helper2 are all masked from debugger

Selective Compilation

from makefun import compile_fun

def public_utility(x):
    """This should remain debuggable."""
    return x ** 2

def private_helper(x):
    """This should be hidden."""
    return x * 3.14159

@compile_fun(recurse=True, except_names=["public_utility"])
def mixed_function(x: float) -> float:
    """Function with mixed compilation needs."""
    # public_utility remains debuggable
    # private_helper gets compiled and hidden
    squared = public_utility(x)
    return private_helper(squared)

print(mixed_function(2.0))  # ~12.57
# public_utility can be debugged, private_helper cannot

Error Handling

from makefun import compile_fun, UnsupportedForCompilation, UndefinedSymbolError, SourceUnavailable

# Trying to compile non-function
try:
    compiled_class = compile_fun(str)  # str is not a function
except UnsupportedForCompilation as e:
    print(f"Cannot compile non-function: {e}")

# Undefined symbol error
def function_with_undefined_ref():
    return undefined_variable  # This variable doesn't exist

try:
    compiled_func = compile_fun(function_with_undefined_ref)
except UndefinedSymbolError as e:
    print(f"Undefined symbol: {e}")

# Built-in function source unavailable
try:
    compiled_builtin = compile_fun(len)  # Built-in function
except SourceUnavailable as e:
    print(f"Source not available: {e}")

Integration with Other Makefun Features

from makefun import compile_fun, create_function, with_signature

def secret_implementation(name, age, location):
    """Secret implementation logic."""
    # Complex logic we want to hide
    score = len(name) * age + hash(location) % 1000
    return f"Score for {name}: {score}"

# First create function with specific signature
public_func = create_function("calculate_score(person_name: str, person_age: int, city: str = 'Unknown')", 
                             secret_implementation)

# Then compile to hide implementation
@compile_fun
def compiled_calculator(person_name: str, person_age: int, city: str = 'Unknown') -> str:
    return public_func(person_name, person_age, city)

print(compiled_calculator("Alice", 25, "New York"))
# Function works but implementation is hidden from debugger

Library Development Pattern

from makefun import compile_fun

class DataProcessor:
    """Public API class with hidden implementations."""
    
    @compile_fun
    def _internal_algorithm(self, data: list) -> list:
        """Internal algorithm that should be hidden."""
        # Proprietary processing logic
        return [x * 2 + 1 for x in data if x > 0]
    
    @compile_fun  
    def _optimization_step(self, data: list) -> list:
        """Internal optimization that should be hidden."""
        # Complex optimization logic
        return sorted(data, reverse=True)[:10]
    
    def process(self, data: list) -> list:
        """Public API method - not compiled."""
        # This method remains debuggable for users
        if not data:
            return []
        
        # Internal methods are compiled and hidden
        processed = self._internal_algorithm(data)
        optimized = self._optimization_step(processed)
        return optimized

# Usage
processor = DataProcessor()
result = processor.process([1, -2, 3, 4, -5, 6])
print(result)

# Users can debug process() method but not the internal algorithms

Development vs Production Usage

The compilation feature is particularly useful for distinguishing between development and production environments:

from makefun import compile_fun
import os

# Conditional compilation based on environment
PRODUCTION = os.getenv("ENVIRONMENT") == "production"

def apply_compilation(func):
    """Apply compilation only in production."""
    if PRODUCTION:
        return compile_fun(func)
    return func

@apply_compilation
def sensitive_algorithm(data: dict) -> dict:
    """Algorithm that should be hidden in production."""
    # Development: debuggable
    # Production: compiled and hidden
    return {"processed": True, "data_size": len(data)}

Performance Considerations

Important: Function compilation does NOT improve performance. According to Python's design principles, compiled bytecode runs at the same speed as interpreted code. The primary purpose is to hide implementation details during debugging sessions.

from makefun import compile_fun
import time

def regular_function(n: int) -> int:
    """Regular function for performance comparison."""
    return sum(i * i for i in range(n))

@compile_fun
def compiled_function(n: int) -> int:
    """Compiled function for performance comparison."""
    return sum(i * i for i in range(n))

# Performance is identical
n = 10000

start = time.time()
result1 = regular_function(n)
time1 = time.time() - start

start = time.time()
result2 = compiled_function(n)
time2 = time.time() - start

print(f"Regular: {time1:.6f}s, Compiled: {time2:.6f}s")
print(f"Results equal: {result1 == result2}")
# Performance difference is negligible

Install with Tessl CLI

npx tessl i tessl/pypi-makefun

docs

compilation.md

decorators.md

function-creation.md

index.md

partial.md

signature-utils.md

wrapping.md

tile.json