Dark magics about variable names in python
Overall
score
90%
Utility functions and classes that build upon varname's core functionality to provide convenient patterns for common use cases. These helpers make it easier to integrate varname capabilities into applications.
Decorator to automatically add __varname__ support to classes and functions, enabling them to access their assigned variable names.
def register(
cls_or_func: type = None,
frame: int = 1,
ignore: IgnoreType = None,
multi_vars: bool = False,
raise_exc: bool = True,
strict: bool = True
) -> Union[Type, Callable]:
"""
A decorator to register __varname__ to a class or function.
Args:
cls_or_func: The class or function to register. When used as @register
without parentheses, this receives the decorated object.
frame: Nth frame used to retrieve the variable name (same as varname)
ignore: Frames to be ignored (same as varname)
multi_vars: Whether allow multiple variables (same as varname)
raise_exc: Whether to raise exception on failure (same as varname)
strict: Whether to only return name for direct assignments (same as varname)
Returns:
The decorated class or function with __varname__ attribute support
Note:
After decoration, instances/calls will have a __varname__ attribute
containing the variable name they were assigned to.
"""from varname.helpers import register
# Class registration
@register
class DataProcessor:
def __init__(self, data):
self.data = data
print(f"Created {self.__varname__} processor")
def process(self):
return f"Processing with {self.__varname__}"
# Usage
main_processor = DataProcessor([1, 2, 3]) # Prints: Created main_processor processor
result = main_processor.process() # Returns: "Processing with main_processor"
# Function registration
@register
def create_connection():
print(f"Creating connection: {create_connection.__varname__}")
return f"Connection_{create_connection.__varname__}"
db_conn = create_connection() # Prints: Creating connection: db_conn
# db_conn == "Connection_db_conn"
# With parameters
@register(frame=2, strict=False)
class FlexibleProcessor:
def __init__(self):
self.name = self.__varname__
def factory():
return FlexibleProcessor()
flexible = factory() # flexible.name == "flexible"A wrapper class that stores both a value and the variable name it was assigned to, useful for debugging and introspection.
class Wrapper:
"""A wrapper with ability to retrieve the variable name."""
def __init__(
self,
value: Any,
frame: int = 1,
ignore: IgnoreType = None,
raise_exc: bool = True,
strict: bool = True
):
"""
Initialize wrapper with value and retrieve variable name.
Args:
value: The value to be wrapped
frame: Nth frame used to retrieve variable name (same as varname)
ignore: Frames to be ignored (same as varname)
raise_exc: Whether to raise exception on failure (same as varname)
strict: Whether to only return name for direct assignments (same as varname)
"""
@property
def name(self) -> str:
"""The variable name to which the instance is assigned."""
@property
def value(self) -> Any:
"""The value this wrapper wraps."""
def __str__(self) -> str:
"""Returns repr(self.value)."""
def __repr__(self) -> str:
"""Returns formatted representation with name and value."""from varname.helpers import Wrapper
# Basic wrapper usage
data_wrapper = Wrapper([1, 2, 3, 4])
print(data_wrapper.name) # "data_wrapper"
print(data_wrapper.value) # [1, 2, 3, 4]
print(data_wrapper) # [1, 2, 3, 4]
print(repr(data_wrapper)) # <Wrapper (data_wrapper): [1, 2, 3, 4]>
# With complex objects
class Config:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
app_config = Wrapper(Config(debug=True, port=8080))
print(f"Config name: {app_config.name}") # Config name: app_config
print(f"Debug mode: {app_config.value.debug}") # Debug mode: True
# Multiple wrappers
user_data = Wrapper({"name": "Alice", "age": 30})
system_data = Wrapper({"version": "1.0", "env": "prod"})
wrappers = [user_data, system_data]
for wrapper in wrappers:
print(f"{wrapper.name}: {wrapper.value}")
# user_data: {'name': 'Alice', 'age': 30}
# system_data: {'version': '1.0', 'env': 'prod'}Print variables with their names automatically retrieved, making debugging output more informative.
def debug(
var,
*more_vars,
prefix: str = "DEBUG: ",
merge: bool = False,
repr: bool = True,
sep: str = "=",
vars_only: bool = False
) -> None:
"""
Print variable names and values for debugging.
Args:
var: The variable to print
*more_vars: Other variables to print
prefix: Prefix to add to each debug line
merge: Whether to merge all variables in one line instead of separate lines
repr: Whether to print values using repr() (True) or str() (False)
sep: Separator between variable name and value
vars_only: Whether to only include variables in output (vs expressions)
Note:
Uses nameof internally to get variable names, so inherits its limitations
in REPL/exec environments.
"""from varname.helpers import debug
# Basic debug printing
x = 42
y = "hello"
debug(x, y)
# DEBUG: x=42
# DEBUG: y='hello'
# Custom formatting
debug(x, y, prefix=">>> ", sep=" -> ", merge=True)
# >>> x -> 42, y -> 'hello'
# With expressions (vars_only=False)
items = [1, 2, 3]
debug(len(items), items[0], vars_only=False)
# DEBUG: len(items)=3
# DEBUG: items[0]=1
# Complex objects without repr
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return f"Person({self.name})"
person = Person("Bob")
debug(person, repr=False)
# DEBUG: person=Person(Bob)Create dictionaries using variable names as keys automatically, similar to JavaScript's object shorthand.
def jsobj(
*args: Any,
vars_only: bool = True,
frame: int = 1,
**kwargs: Any
) -> Dict[str, Any]:
"""
Create a JavaScript-like object (dict) using variable names as keys.
Args:
*args: Positional arguments whose names will become keys
vars_only: Whether to only allow variables as arguments (vs expressions)
frame: Frame adjustment for name retrieval
**kwargs: Keyword arguments to include in the resulting dict
Returns:
Dictionary mapping argument names to their values, plus any kwargs
Note:
Uses nameof internally to get variable names from positional arguments.
Keyword arguments are included as-is.
"""from varname.helpers import jsobj
# Basic usage
username = "alice"
user_id = 12345
active = True
user = jsobj(username, user_id, active)
# user == {'username': 'alice', 'user_id': 12345, 'active': True}
# With keyword arguments
user = jsobj(username, user_id, role="admin", permissions=["read", "write"])
# user == {
# 'username': 'alice',
# 'user_id': 12345,
# 'role': 'admin',
# 'permissions': ['read', 'write']
# }
# Nested objects
server_config = jsobj(
username,
user_id,
database=jsobj(host="localhost", port=5432),
cache=jsobj(enabled=True, ttl=3600)
)
# server_config == {
# 'username': 'alice',
# 'user_id': 12345,
# 'database': {'host': 'localhost', 'port': 5432},
# 'cache': {'enabled': True, 'ttl': 3600}
# }
# With expressions (vars_only=False)
items = [1, 2, 3]
result = jsobj(len(items), items[0], vars_only=False, total=sum(items))
# result == {'len(items)': 3, 'items[0]': 1, 'total': 6}Execute code where the source is visible at runtime, enabling varname functions to work in dynamic execution contexts.
def exec_code(
code: str,
globals: Dict[str, Any] = None,
locals: Dict[str, Any] = None,
/,
sourcefile: PathLike | str = None,
frame: int = 1,
ignore: IgnoreType = None,
**kwargs: Any
) -> None:
"""
Execute code where source code is visible at runtime.
Args:
code: The code string to execute
globals: Global namespace for execution (defaults to caller's globals)
locals: Local namespace for execution (defaults to caller's locals)
sourcefile: Optional file path to write source code to for visibility
frame: Frame adjustment for namespace retrieval
ignore: Frames to ignore when retrieving namespace
**kwargs: Additional arguments passed to exec()
Note:
This function makes varname operations work inside exec'd code by
ensuring the source code is available for AST analysis. Without this,
exec'd code cannot use varname functions effectively.
"""from varname.helpers import exec_code, varname
# Basic code execution with varname support
code = '''
def create_item():
return varname()
my_item = create_item()
print(f"Created: {my_item}") # Will print: Created: my_item
'''
exec_code(code)
# With custom globals/locals
local_vars = {'data': [1, 2, 3]}
global_vars = {'varname': varname}
code = '''
def process():
name = varname()
return f"Processing {name} with {len(data)} items"
result = process()
'''
exec_code(code, globals=global_vars, locals=local_vars)
print(local_vars['result']) # Processing result with 3 items
# With source file for debugging
import tempfile
from pathlib import Path
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
source_file = f.name
code = '''
from varname.helpers import debug
x = 42
y = "test"
debug(x, y)
'''
exec_code(code, sourcefile=source_file)
# DEBUG: x=42
# DEBUG: y='test'
# Clean up
Path(source_file).unlink()from varname.helpers import register
from varname.helpers import Wrapper
@register
class NamedFactory:
@classmethod
def create(cls, *args, **kwargs):
instance = cls(*args, **kwargs)
instance._factory_name = cls.__varname__
return instance
# Automatic name detection in factory
user_factory = NamedFactory.create()
print(user_factory._factory_name) # "user_factory"
# Combined with Wrapper
class SmartFactory:
def __init__(self, config):
self.config = config
def create_component(self):
return Wrapper({"factory": "smart", "config": self.config})
factory = SmartFactory({"debug": True})
component = factory.create_component()
print(f"Component {component.name} created") # Component component createdfrom varname.helpers import jsobj, debug
def load_config():
# Database settings
db_host = "localhost"
db_port = 5432
db_name = "myapp"
# Cache settings
cache_enabled = True
cache_ttl = 3600
# Create config using variable names
config = {
"database": jsobj(db_host, db_port, db_name),
"cache": jsobj(cache_enabled, cache_ttl)
}
# Debug the configuration
debug(config, prefix="CONFIG: ")
return config
config = load_config()
# CONFIG: config={'database': {'db_host': 'localhost', ...}, ...}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