Dark magics about variable names in python
Overall
score
90%
Configuration options and comprehensive exception hierarchy for error handling in varname operations. Understanding these components is essential for robust error handling and debugging.
Central configuration object for controlling varname behavior across the application.
class config:
"""Global configurations for varname."""
debug: bool = False
"""Show debug information for frames being ignored.
When True, varname will print detailed information about which frames
are being skipped during the ignore process. Useful for debugging
complex ignore scenarios or when varname is not finding the expected frame.
Example output when debug=True:
VARNAME DEBUG: Ignored frame: <frame at 0x...> (file: decorator.py, func: wrapper)
VARNAME DEBUG: Target frame found: <frame at 0x...> (file: main.py, func: <module>)
"""from varname import config, varname
# Enable debug mode
config.debug = True
def logged(func):
def wrapper(*args, **kwargs):
# With debug=True, will show ignored frames
name = varname(ignore=logged)
print(f"Calling {name}")
return func(*args, **kwargs)
return wrapper
@logged
def process_data():
return "processed"
# This will show debug output about ignored frames
result = process_data()
# Disable debug mode
config.debug = FalseVarname provides a comprehensive exception hierarchy for different types of errors that can occur during variable name retrieval and introspection.
class VarnameException(Exception):
"""Root exception for all varname-related errors.
All other varname exceptions inherit from this base class, allowing
for unified exception handling when needed.
"""class VarnameRetrievingError(VarnameException):
"""Raised when failed to retrieve the variable name.
This is the most common exception raised by varname functions when
they cannot determine the variable name due to:
- AST node retrieval failures
- Unsupported calling contexts
- Frame analysis problems
- Source code unavailability
Common scenarios:
- Using varname in REPL without source code
- Complex call chains that confuse frame analysis
- Missing or corrupted AST information
- Calling from unsupported contexts (e.g., some decorators)
"""class ImproperUseError(VarnameException):
"""Raised when varname functions are used improperly.
This exception indicates incorrect usage patterns that violate
varname's assumptions or requirements:
For varname():
- Multiple target assignment when multi_vars=False
- Non-direct assignment when strict=True (e.g., a = [func()])
- Using in contexts where variable assignment detection is impossible
For will():
- Not followed by attribute access
- Used in contexts where next attribute cannot be determined
For argname():
- Called from functions that cannot be analyzed
- Requesting non-existent arguments
- Invalid argument specifications
"""class QualnameNonUniqueError(VarnameException):
"""Raised when a qualified name refers to multiple objects.
This occurs in the ignore system when a qualified name (module.function)
is used as an ignore element but matches multiple different objects,
making it ambiguous which one should be ignored.
Example:
- import math; from math import sin
- Using "math.sin" as ignore element is ambiguous
"""from varname import (
varname, will, argname, nameof,
VarnameException,
VarnameRetrievingError,
ImproperUseError,
QualnameNonUniqueError
)
# Catching specific varname errors
def safe_varname():
try:
return varname()
except VarnameRetrievingError as e:
print(f"Could not retrieve variable name: {e}")
return "unknown"
except ImproperUseError as e:
print(f"Improper varname usage: {e}")
return "improper"
# Usage in unsupported context
try:
# This will raise VarnameRetrievingError in REPL
name = safe_varname()
except Exception as e:
print(f"Error: {e}")
# Catching all varname exceptions
def robust_operation():
try:
a, b = varname(multi_vars=False) # Will raise ImproperUseError
return a, b
except VarnameException as e:
print(f"Varname operation failed: {type(e).__name__}: {e}")
return None, None
# Example with improper will() usage
class BadWillUsage:
def method(self):
try:
attr = will()
# Not followed by attribute access - will cause issues
return attr
except ImproperUseError as e:
print(f"Will() used improperly: {e}")
return None
obj = BadWillUsage()
result = obj.method() # Direct call, no attribute access
# Example with argname errors
def analyze_function():
try:
# Requesting non-existent argument
name = argname('nonexistent_arg')
return name
except VarnameRetrievingError as e:
print(f"Argument analysis failed: {e}")
return None
def caller():
return analyze_function()
result = caller()Varname includes warnings for situations that are not errors but may indicate potential issues or unexpected behavior.
class VarnameWarning(Warning):
"""Root warning for all varname-related warnings."""
class MultiTargetAssignmentWarning(VarnameWarning):
"""Warning for multiple target assignments like a = b = func().
Issued when varname() detects multiple assignment targets, which can
lead to ambiguous variable name detection. The warning helps identify
potential issues in complex assignment patterns.
Example triggering code:
def create(): return varname()
a = b = create() # Triggers warning
"""
class MaybeDecoratedFunctionWarning(VarnameWarning):
"""Warning when suspicious decorated function used as ignore directly.
Issued when a function that appears to be decorated is used directly
in the ignore parameter instead of using proper ignore handling for
decorated functions.
Example:
@decorator
def func(): pass
varname(ignore=func) # May trigger warning
# Should use: varname(ignore=(func, 1)) for decorated functions
"""
class UsingExecWarning(VarnameWarning):
"""Warning when exec is used to retrieve function name for argname().
Issued when argname() needs to use exec with temporary files to analyze
function calls in environments where source code is not directly available.
This indicates a fallback mechanism is being used.
"""import warnings
from varname import VarnameWarning
# Control varname warnings
warnings.filterwarnings('ignore', category=VarnameWarning) # Suppress all
warnings.filterwarnings('error', category=VarnameWarning) # Make them errors
warnings.filterwarnings('always', category=VarnameWarning) # Always show
# Filter specific warning types
from varname import MultiTargetAssignmentWarning
warnings.filterwarnings('ignore', category=MultiTargetAssignmentWarning)from varname import varname, VarnameRetrievingError
def create_with_fallback(default_name="object"):
"""Create object with varname, falling back to default name."""
try:
name = varname()
except VarnameRetrievingError:
name = default_name
return {
'name': name,
'created_at': '2023-01-01',
'data': []
}
# Works in normal contexts
user_data = create_with_fallback() # name='user_data'
# Works in problematic contexts
items = [create_with_fallback() for _ in range(3)] # name='object' (fallback)from varname import argname, nameof, ImproperUseError, VarnameRetrievingError
def smart_debug_function(*args, **kwargs):
"""Debug function that adapts to available context."""
try:
# Try to get argument names
if args:
arg_names = nameof(*args)
print(f"Arguments: {arg_names}")
except VarnameRetrievingError:
# Fall back to positions
print(f"Arguments: {len(args)} positional args")
except ImproperUseError:
# Handle complex expressions
print(f"Arguments: complex expressions ({len(args)} args)")
try:
# Try to get keyword argument names
if kwargs:
kwarg_info = argname('kwargs')
print(f"Keyword args: {kwarg_info}")
except (VarnameRetrievingError, ImproperUseError):
print(f"Keyword args: {list(kwargs.keys())}")
# Usage examples
x, y = 1, 2
smart_debug_function(x, y, debug=True) # Will try name detection
smart_debug_function(x + 1, y * 2, mode="test") # Will handle expressions gracefullyimport logging
from varname import VarnameException, config
# Set up logging for varname issues
logger = logging.getLogger('varname_ops')
def production_varname_operation():
"""Example of production-ready varname usage."""
try:
# Enable debug only in development
if __debug__:
config.debug = True
name = varname()
logger.debug(f"Retrieved variable name: {name}")
return name
except VarnameException as e:
# Log the specific error type and context
logger.warning(
f"Varname operation failed: {type(e).__name__}: {e}",
exc_info=True
)
# Return sensible default
return "unnamed_variable"
finally:
# Clean up debug mode
config.debug = False
# Usage
try:
result_data = production_varname_operation()
logger.info(f"Created {result_data}")
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)Install with Tessl CLI
npx tessl i tessl/pypi-varnameevals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10