CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-makefun

Small library to dynamically create python functions.

Pending
Overview
Eval results
Files

partial.mddocs/

Partial Function Support

Enhanced partial function implementation with better introspection, documentation generation, and signature handling compared to functools.partial. These utilities provide superior user experience for partial function application with proper metadata preservation.

from typing import Callable, Any, TypeVar

T = TypeVar('T')

Capabilities

Enhanced Partial Function

Improved version of functools.partial with better introspection and automatically generated documentation.

def partial(f: Callable[..., T], *preset_pos_args: Any, **preset_kwargs: Any) -> Callable[..., T]:
    """
    Enhanced functools.partial with better introspection and documentation.
    
    Parameters:
    - f: Callable[..., T], function to partially apply
    - *preset_pos_args: Any, positional arguments to preset/bind
    - **preset_kwargs: Any, keyword arguments to preset/bind
    
    Returns:
    Callable[..., T]: Partial function with enhanced metadata and signature
    
    The returned function has:
    - Proper signature reflecting remaining parameters
    - Auto-generated docstring showing preset values
    - .func attribute pointing to original function (like functools.partial)
    - Better introspection support with inspect.signature()
    
    Raises:
    ValueError: If preset arguments don't match function signature
    """

Partial Function Decorator

Decorator version of partial function for cleaner syntax when creating partial functions.

def with_partial(*preset_pos_args: Any, **preset_kwargs: Any) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """
    Decorator to create partial function with preset arguments.
    
    Parameters:
    - *preset_pos_args: Any, positional arguments to preset
    - **preset_kwargs: Any, keyword arguments to preset
    
    Returns:
    Callable[[Callable[..., T]], Callable[..., T]]: Decorator that creates partial function
    """

Usage Examples

Basic Partial Function Creation

from makefun import partial
from inspect import signature

def multiply(x: int, y: int, z: int = 1) -> int:
    """Multiply three numbers together."""
    return x * y * z

# Create partial with first argument preset
double = partial(multiply, 2)
print(signature(double))  # (y: int, z: int = 1) -> int
print(double(5))          # 10 (2 * 5 * 1)
print(double(3, 4))       # 24 (2 * 3 * 4)

# Create partial with keyword argument preset
multiply_by_10 = partial(multiply, z=10)
print(signature(multiply_by_10))  # (x: int, y: int) -> int
print(multiply_by_10(2, 3))       # 60 (2 * 3 * 10)

Enhanced Documentation

from makefun import partial

def api_request(method: str, url: str, headers: dict = None, timeout: int = 30) -> dict:
    """Make an HTTP API request."""
    return {
        "method": method,
        "url": url, 
        "headers": headers or {},
        "timeout": timeout
    }

# Create partial for GET requests
get_request = partial(api_request, "GET", timeout=60)

# Automatic documentation generation
print(get_request.__doc__)
# <This function is equivalent to 'api_request('GET', url, headers=None, timeout=60)', see original 'api_request' doc below.>
# Make an HTTP API request.

print(get_request.__name__)  # "api_request" (preserved from original)

Multiple Argument Presets

from makefun import partial

def format_message(template: str, name: str, age: int, city: str = "Unknown") -> str:
    """Format a message with user information."""
    return template.format(name=name, age=age, city=city)

# Preset template and city
welcome_message = partial(format_message, "Welcome {name}! You are {age} years old and live in {city}.", city="New York")

print(welcome_message("Alice", 25))  # Welcome Alice! You are 25 years old and live in New York.
print(welcome_message("Bob", 30, "Chicago"))  # Welcome Bob! You are 30 years old and live in Chicago.

Decorator Syntax

from makefun import with_partial

# Define base function
def compute_score(base: int, multiplier: float, bonus: int = 0) -> float:
    """Compute final score with base, multiplier, and bonus."""
    return (base + bonus) * multiplier

# Create specialized versions using decorator
@with_partial(multiplier=1.5)
def boosted_score(base: int, bonus: int = 0) -> float:
    pass  # Implementation replaced by partial

@with_partial(multiplier=2.0, bonus=10)
def premium_score(base: int) -> float:
    pass  # Implementation replaced by partial

print(boosted_score(100))      # 150.0 (100 * 1.5)
print(boosted_score(100, 20))  # 180.0 ((100 + 20) * 1.5)
print(premium_score(100))      # 220.0 ((100 + 10) * 2.0)

Working with Complex Signatures

from makefun import partial
from typing import List, Dict, Optional

def analyze_data(data: List[Dict], method: str, normalize: bool = True, 
                weights: Optional[Dict] = None, *extra_params, **options) -> Dict:
    """Analyze data using specified method with various options."""
    result = {
        "method": method,
        "data_count": len(data),
        "normalized": normalize,
        "weights": weights,
        "extra_params": extra_params,
        "options": options
    }
    return result

# Create partial with method and normalization preset
statistical_analysis = partial(analyze_data, method="statistical", normalize=True)

# Remaining signature handles complex parameters correctly
sample_data = [{"value": 1}, {"value": 2}]
result = statistical_analysis(sample_data, weights={"a": 0.5}, "param1", "param2", 
                             threshold=0.1, debug=True)
print(result)

Generator and Async Function Support

from makefun import partial
import asyncio

def number_generator(start: int, end: int, step: int = 1, prefix: str = "Num"):
    """Generate numbers with optional prefix."""
    for i in range(start, end, step):
        yield f"{prefix}: {i}"

# Create partial generator
even_numbers = partial(number_generator, step=2, prefix="Even")

for num in even_numbers(0, 10):
    print(num)  # Even: 0, Even: 2, Even: 4, etc.

# Async function partial
async def fetch_data(url: str, method: str = "GET", timeout: int = 30) -> dict:
    """Simulate async data fetching."""
    await asyncio.sleep(0.1)
    return {"url": url, "method": method, "timeout": timeout}

# Create partial async function
quick_fetch = partial(fetch_data, timeout=5)
result = asyncio.run(quick_fetch("https://api.example.com", "POST"))
print(result)

Parameter Order and Keyword-Only Arguments

from makefun import partial

def complex_func(a: int, b: str, c: float = 1.0, *, d: bool, e: str = "default") -> dict:
    """Function with positional and keyword-only parameters."""
    return {"a": a, "b": b, "c": c, "d": d, "e": e}

# Preset keyword-only argument
simplified = partial(complex_func, d=True)
result = simplified(1, "hello", 2.5)  # e uses default
print(result)  # {"a": 1, "b": "hello", "c": 2.5, "d": True, "e": "default"}

# Preset positional and keyword arguments
more_simplified = partial(complex_func, 42, c=3.14, d=False, e="custom")
result2 = more_simplified("world")
print(result2)  # {"a": 42, "b": "world", "c": 3.14, "d": False, "e": "custom"}

Error Handling

from makefun import partial

def target_func(x: int, y: str) -> str:
    return f"{x}: {y}"

# Invalid preset arguments
try:
    invalid_partial = partial(target_func, 1, 2, 3)  # Too many positional args
except ValueError as e:
    print(f"Too many arguments: {e}")

try:
    invalid_partial = partial(target_func, nonexistent_param="value")  # Invalid keyword
except ValueError as e:
    print(f"Invalid keyword: {e}")

Comparison with functools.partial

Key advantages over functools.partial:

  1. Better Introspection: Proper inspect.signature() support showing remaining parameters
  2. Enhanced Documentation: Auto-generated docstrings showing preset values
  3. Signature Preservation: Maintains proper type annotations and parameter information
  4. Better Error Messages: Clear validation of preset arguments against function signature
import functools
from makefun import partial
from inspect import signature

def example_func(x: int, y: str, z: float = 1.0) -> str:
    """Example function for comparison."""
    return f"{x}, {y}, {z}"

# functools.partial
stdlib_partial = functools.partial(example_func, 42)
print(signature(stdlib_partial))  # (*args, **kwargs) - not helpful

# makefun.partial  
enhanced_partial = partial(example_func, 42)
print(signature(enhanced_partial))  # (y: str, z: float = 1.0) -> str - precise!

print(stdlib_partial.__doc__)    # partial(func, *args, **keywords) - generic
print(enhanced_partial.__doc__)  # Shows actual preset values and original doc

Integration with Other Makefun Features

Partial functions work seamlessly with other makefun utilities:

from makefun import partial, wraps

def base_function(x: int, y: str, z: float = 1.0) -> str:
    return f"Result: {x}, {y}, {z}"

# Create partial
preset_partial = partial(base_function, z=5.0)

# Wrap the partial with additional functionality
@wraps(preset_partial, prepend_args="verbose: bool = False")  
def enhanced_partial(verbose, x, y):
    if verbose:
        print(f"Calling with x={x}, y={y}")
    result = preset_partial(x, y)
    if verbose:
        print(f"Got: {result}")
    return result

print(enhanced_partial(10, "test"))          # Basic call
print(enhanced_partial(True, 20, "debug"))   # Verbose call

Function Attributes

Partial functions maintain compatibility with functools.partial:

from makefun import partial

def original_func(a, b, c=1):
    return a + b + c

# Create partial
p = partial(original_func, 10, c=5)

# Access original function
print(p.func)           # Points to original_func
print(p.func(1, 2, 3))  # Can call original directly

# Check if it's a partial
import functools
print(isinstance(p, functools.partial))  # False - it's a generated function
print(hasattr(p, 'func'))               # True - maintains .func attribute

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