CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-varname

Dark magics about variable names in python

Overall
score

90%

Overview
Eval results
Files

advanced-functions.mddocs/

Advanced Functions

Advanced varname functions for attribute detection and argument inspection. These functions provide more specialized capabilities for complex runtime introspection scenarios.

Capabilities

Attribute Access Detection

Detects the attribute name that will be accessed immediately after a function call returns, enabling dynamic attribute resolution patterns.

def will(frame: int = 1, raise_exc: bool = True) -> Union[str, None]:
    """
    Detect the attribute name right immediately after a function call.

    Args:
        frame: At which frame this function is called. frame=1 means 
               the immediate upper frame. frame=0 means the current frame.
        raise_exc: Raise exception if failed to detect the ast node.
                  If False, return None when detection fails.

    Returns:
        The attribute name right after the function call as string.
        None if detection fails and raise_exc=False.

    Raises:
        VarnameRetrievingError: When unable to retrieve ast node and raise_exc=True
        ImproperUseError: When will() is not used immediately before an attribute access
        
    Note:
        Must be used in the context where the returned value will have an 
        attribute accessed immediately, like: obj.method().some_attr
    """

Usage Examples

from varname import will

# Basic attribute detection
class DynamicObject:
    def get_data(self):
        attr_name = will()
        return f"You requested: {attr_name}"

obj = DynamicObject()
result = obj.get_data().username  # result == "You requested: username"

# With error handling
class SafeObject:
    def query(self):
        attr_name = will(raise_exc=False)
        if attr_name:
            return f"Accessing: {attr_name}"
        return "Direct call"

safe_obj = SafeObject()
result1 = safe_obj.query().data    # result1 == "Accessing: data"
result2 = safe_obj.query()         # result2 == "Direct call"

# Dynamic method routing
class Router:
    def route(self):
        endpoint = will()
        return f"Routing to: {endpoint}"
    
    def __getattr__(self, name):
        return f"Handler for {name}"

router = Router()
handler = router.route().api_endpoint  # handler == "Routing to: api_endpoint"

Function Argument Inspection

Retrieves the names/sources of arguments passed to a function, enabling introspection of how functions are called and with what variable names.

def argname(
    arg: str,
    *more_args: str,
    func: Optional[Callable] = None,
    dispatch: Optional[Type] = None,
    frame: int = 1,
    ignore: Optional[IgnoreType] = None,
    vars_only: bool = True
) -> Union[ArgSourceType, Tuple[ArgSourceType, ...]]:
    """
    Get the names/sources of arguments passed to a function.

    Args:
        arg: Name of the argument to retrieve name/source of. Use special names:
             - '*args' for positional arguments tuple
             - 'kwargs' for keyword arguments dict
             - '**kwargs' for keyword arguments dict (same as 'kwargs')
        *more_args: Names of other arguments to retrieve names/sources of
        func: Target function to inspect arguments for. If None, automatically
              detects the function that called argname().
        dispatch: Type for the dispatched function in single-dispatch scenarios.
                 Only used when auto-detecting function fails.
        frame: Frame where target function is called. frame=1 means the immediate
               upper frame where the target function is called.
        ignore: Intermediate calls to be ignored to reach the target frame.
               Similar to varname's ignore parameter.
        vars_only: Whether to require arguments to be variables only or allow
                  any expressions. If False, returns source expressions.

    Returns:
        Source/name of argument if single argument requested.
        Tuple of sources/names if multiple arguments requested.
        For '*args': tuple of positional argument names
        For 'kwargs'/'**kwargs': dict mapping parameter names to argument names

    Raises:
        VarnameRetrievingError: When unable to retrieve function call or arguments
        ImproperUseError: When argname is not called from inside a function that
                         can be analyzed or when specified arguments don't exist

    Note:
        Works by analyzing the AST of the function call. In environments where
        source code is not available (REPL, exec), uses exec with temporary
        files to enable source analysis.
    """

Usage Examples

from varname import argname

# Basic argument name retrieval
def process_data(data, options=None):
    data_name = argname('data')
    opts_name = argname('options') 
    print(f"Processing {data_name} with {opts_name}")
    return f"Processed {data_name}"

dataset = [1, 2, 3]
config = {'mode': 'fast'}
result = process_data(dataset, options=config)
# Prints: Processing dataset with config
# Returns: "Processed dataset"

# Multiple arguments
def analyze(*args, **kwargs):
    arg_names = argname('*args', 'kwargs')
    print(f"Args: {arg_names[0]}")
    print(f"Kwargs: {arg_names[1]}")

x, y = 10, 20
analyze(x, y, method='linear', debug=True)
# Prints: Args: ('x', 'y')
# Prints: Kwargs: {'method': 'method', 'debug': 'debug'}

# With function specification
def wrapper(func, *args, **kwargs):
    # Get argument names for the wrapped function
    arg_sources = argname('*args', func=func, frame=2)
    print(f"Calling {func.__name__} with: {arg_sources}")
    return func(*args, **kwargs)

def compute(a, b):
    return a + b

p, q = 5, 3
result = wrapper(compute, p, q)
# Prints: Calling compute with: ('p', 'q')

# With ignore parameter for decorators
def logged(func):
    def decorator(*args, **kwargs):
        # Skip the decorator frame
        arg_names = argname('*args', ignore=logged)
        print(f"Logged call with args: {arg_names}")
        return func(*args, **kwargs)
    return decorator

@logged
def calculate(value1, value2):
    return value1 * value2

num1, num2 = 4, 7
result = calculate(num1, num2)
# Prints: Logged call with args: ('num1', 'num2')

Expression vs Variable Names

The vars_only parameter controls whether to return just variable names or full expressions:

from varname import argname

def analyze_call(data):
    var_name = argname('data', vars_only=True)   # Variable name only
    full_expr = argname('data', vars_only=False)  # Full expression
    return var_name, full_expr

# With simple variable
items = [1, 2, 3]
var_result, expr_result = analyze_call(items)
# var_result == 'items', expr_result == 'items'

# With attribute access
class Container:
    def __init__(self):
        self.data = [4, 5, 6]

container = Container()
var_result, expr_result = analyze_call(container.data)
# var_result == 'data', expr_result == 'container.data'

# With method call
def get_items():
    return [7, 8, 9]

var_result, expr_result = analyze_call(get_items())
# var_result raises VarnameRetrievingError (not a variable)
# expr_result == 'get_items()'

Advanced Use Cases

Dynamic API Discovery

from varname import will, argname

class APIBuilder:
    def __init__(self):
        self.endpoints = {}
        
    def endpoint(self):
        # Detect what endpoint is being accessed
        name = will()
        return EndpointBuilder(name, self)

class EndpointBuilder:
    def __init__(self, name, api):
        self.name = name
        self.api = api
        
    def handler(self, func):
        # Get the function and arguments info
        func_name = argname('func')
        self.api.endpoints[self.name] = {
            'handler': func,
            'source_name': func_name
        }
        return func

# Usage
api = APIBuilder()

def user_handler():
    return "User data"

api.endpoint().users.handler(user_handler)
# Automatically registers 'users' endpoint with user_handler

Smart Debugging with Context

from varname import argname, will

def smart_assert(condition, message="Assertion failed"):
    if not condition:
        # Get the condition expression
        cond_expr = argname('condition', vars_only=False)
        raise AssertionError(f"{message}: {cond_expr}")

def conditional_processor():
    next_action = will(raise_exc=False)
    if next_action:
        return f"Will perform: {next_action}"
    return "Direct processing"

# Usage
x = 5
smart_assert(x > 10)  # AssertionError: Assertion failed: x > 10

processor = conditional_processor()
result = processor.validate  # result == "Will perform: validate"

Ignore System Classes

Advanced frame filtering system for precise control over which frames are skipped during variable name retrieval.

Core Ignore Classes

class IgnoreList:
    """A list of ignore elements for frame filtering."""
    
    @classmethod
    def create(cls, ignore: IgnoreType) -> "IgnoreList":
        """Create ignore list from ignore specification."""
        
    def get_frame(self, frame: int) -> FrameType:
        """Get the target frame after applying ignore rules."""

class IgnoreElem:
    """Abstract base class for ignore elements."""
    
    def match(self, frame: FrameType) -> bool:
        """Check if frame matches this ignore element."""

Ignore Element Types

# Module-based ignoring
class IgnoreModule(IgnoreElem):
    """Ignore calls from a module or its submodules."""

class IgnoreFilename(IgnoreElem):
    """Ignore calls from a module by matching filename."""
    
class IgnoreDirname(IgnoreElem):
    """Ignore calls from modules inside a directory."""
    
class IgnoreStdlib(IgnoreElem):
    """Ignore standard library calls."""

# Function-based ignoring  
class IgnoreFunction(IgnoreElem):
    """Ignore a non-decorated function."""
    
class IgnoreDecorated(IgnoreElem):
    """Ignore a decorated function with decorator count."""

# Qualified name ignoring
class IgnoreModuleQualname(IgnoreElem):
    """Ignore by module and qualified name."""
    
class IgnoreFilenameQualname(IgnoreElem):
    """Ignore by filename and qualified name."""
    
class IgnoreOnlyQualname(IgnoreElem):
    """Ignore by qualified name only."""

Usage Examples

from varname import varname
from varname.ignore import IgnoreModule, IgnoreFunction

# Custom ignore list for complex scenarios
def complex_wrapper():
    # Ignore specific modules and functions
    ignore_list = [
        IgnoreModule("decorator_library"),
        IgnoreFunction(some_wrapper_function),
        ("mymodule", "MyClass.method")  # Module + qualname
    ]
    return varname(ignore=ignore_list)

# The ignore system automatically handles standard patterns
result = complex_wrapper()

Note: These classes are primarily used internally by varname's ignore system. Most users should use the simpler ignore specifications supported by the main functions (modules, functions, tuples) rather than constructing these classes directly.

Install with Tessl CLI

npx tessl i tessl/pypi-varname

docs

advanced-functions.md

config-exceptions.md

core-functions.md

helper-functions.md

index.md

tile.json