or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-functions.mdconfig-exceptions.mdcore-functions.mdhelper-functions.mdindex.md

advanced-functions.mddocs/

0

# Advanced Functions

1

2

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

3

4

## Capabilities

5

6

### Attribute Access Detection

7

8

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

9

10

```python { .api }

11

def will(frame: int = 1, raise_exc: bool = True) -> Union[str, None]:

12

"""

13

Detect the attribute name right immediately after a function call.

14

15

Args:

16

frame: At which frame this function is called. frame=1 means

17

the immediate upper frame. frame=0 means the current frame.

18

raise_exc: Raise exception if failed to detect the ast node.

19

If False, return None when detection fails.

20

21

Returns:

22

The attribute name right after the function call as string.

23

None if detection fails and raise_exc=False.

24

25

Raises:

26

VarnameRetrievingError: When unable to retrieve ast node and raise_exc=True

27

ImproperUseError: When will() is not used immediately before an attribute access

28

29

Note:

30

Must be used in the context where the returned value will have an

31

attribute accessed immediately, like: obj.method().some_attr

32

"""

33

```

34

35

#### Usage Examples

36

37

```python

38

from varname import will

39

40

# Basic attribute detection

41

class DynamicObject:

42

def get_data(self):

43

attr_name = will()

44

return f"You requested: {attr_name}"

45

46

obj = DynamicObject()

47

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

48

49

# With error handling

50

class SafeObject:

51

def query(self):

52

attr_name = will(raise_exc=False)

53

if attr_name:

54

return f"Accessing: {attr_name}"

55

return "Direct call"

56

57

safe_obj = SafeObject()

58

result1 = safe_obj.query().data # result1 == "Accessing: data"

59

result2 = safe_obj.query() # result2 == "Direct call"

60

61

# Dynamic method routing

62

class Router:

63

def route(self):

64

endpoint = will()

65

return f"Routing to: {endpoint}"

66

67

def __getattr__(self, name):

68

return f"Handler for {name}"

69

70

router = Router()

71

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

72

```

73

74

### Function Argument Inspection

75

76

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

77

78

```python { .api }

79

def argname(

80

arg: str,

81

*more_args: str,

82

func: Optional[Callable] = None,

83

dispatch: Optional[Type] = None,

84

frame: int = 1,

85

ignore: Optional[IgnoreType] = None,

86

vars_only: bool = True

87

) -> Union[ArgSourceType, Tuple[ArgSourceType, ...]]:

88

"""

89

Get the names/sources of arguments passed to a function.

90

91

Args:

92

arg: Name of the argument to retrieve name/source of. Use special names:

93

- '*args' for positional arguments tuple

94

- 'kwargs' for keyword arguments dict

95

- '**kwargs' for keyword arguments dict (same as 'kwargs')

96

*more_args: Names of other arguments to retrieve names/sources of

97

func: Target function to inspect arguments for. If None, automatically

98

detects the function that called argname().

99

dispatch: Type for the dispatched function in single-dispatch scenarios.

100

Only used when auto-detecting function fails.

101

frame: Frame where target function is called. frame=1 means the immediate

102

upper frame where the target function is called.

103

ignore: Intermediate calls to be ignored to reach the target frame.

104

Similar to varname's ignore parameter.

105

vars_only: Whether to require arguments to be variables only or allow

106

any expressions. If False, returns source expressions.

107

108

Returns:

109

Source/name of argument if single argument requested.

110

Tuple of sources/names if multiple arguments requested.

111

For '*args': tuple of positional argument names

112

For 'kwargs'/'**kwargs': dict mapping parameter names to argument names

113

114

Raises:

115

VarnameRetrievingError: When unable to retrieve function call or arguments

116

ImproperUseError: When argname is not called from inside a function that

117

can be analyzed or when specified arguments don't exist

118

119

Note:

120

Works by analyzing the AST of the function call. In environments where

121

source code is not available (REPL, exec), uses exec with temporary

122

files to enable source analysis.

123

"""

124

```

125

126

#### Usage Examples

127

128

```python

129

from varname import argname

130

131

# Basic argument name retrieval

132

def process_data(data, options=None):

133

data_name = argname('data')

134

opts_name = argname('options')

135

print(f"Processing {data_name} with {opts_name}")

136

return f"Processed {data_name}"

137

138

dataset = [1, 2, 3]

139

config = {'mode': 'fast'}

140

result = process_data(dataset, options=config)

141

# Prints: Processing dataset with config

142

# Returns: "Processed dataset"

143

144

# Multiple arguments

145

def analyze(*args, **kwargs):

146

arg_names = argname('*args', 'kwargs')

147

print(f"Args: {arg_names[0]}")

148

print(f"Kwargs: {arg_names[1]}")

149

150

x, y = 10, 20

151

analyze(x, y, method='linear', debug=True)

152

# Prints: Args: ('x', 'y')

153

# Prints: Kwargs: {'method': 'method', 'debug': 'debug'}

154

155

# With function specification

156

def wrapper(func, *args, **kwargs):

157

# Get argument names for the wrapped function

158

arg_sources = argname('*args', func=func, frame=2)

159

print(f"Calling {func.__name__} with: {arg_sources}")

160

return func(*args, **kwargs)

161

162

def compute(a, b):

163

return a + b

164

165

p, q = 5, 3

166

result = wrapper(compute, p, q)

167

# Prints: Calling compute with: ('p', 'q')

168

169

# With ignore parameter for decorators

170

def logged(func):

171

def decorator(*args, **kwargs):

172

# Skip the decorator frame

173

arg_names = argname('*args', ignore=logged)

174

print(f"Logged call with args: {arg_names}")

175

return func(*args, **kwargs)

176

return decorator

177

178

@logged

179

def calculate(value1, value2):

180

return value1 * value2

181

182

num1, num2 = 4, 7

183

result = calculate(num1, num2)

184

# Prints: Logged call with args: ('num1', 'num2')

185

```

186

187

### Expression vs Variable Names

188

189

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

190

191

```python

192

from varname import argname

193

194

def analyze_call(data):

195

var_name = argname('data', vars_only=True) # Variable name only

196

full_expr = argname('data', vars_only=False) # Full expression

197

return var_name, full_expr

198

199

# With simple variable

200

items = [1, 2, 3]

201

var_result, expr_result = analyze_call(items)

202

# var_result == 'items', expr_result == 'items'

203

204

# With attribute access

205

class Container:

206

def __init__(self):

207

self.data = [4, 5, 6]

208

209

container = Container()

210

var_result, expr_result = analyze_call(container.data)

211

# var_result == 'data', expr_result == 'container.data'

212

213

# With method call

214

def get_items():

215

return [7, 8, 9]

216

217

var_result, expr_result = analyze_call(get_items())

218

# var_result raises VarnameRetrievingError (not a variable)

219

# expr_result == 'get_items()'

220

```

221

222

## Advanced Use Cases

223

224

### Dynamic API Discovery

225

226

```python

227

from varname import will, argname

228

229

class APIBuilder:

230

def __init__(self):

231

self.endpoints = {}

232

233

def endpoint(self):

234

# Detect what endpoint is being accessed

235

name = will()

236

return EndpointBuilder(name, self)

237

238

class EndpointBuilder:

239

def __init__(self, name, api):

240

self.name = name

241

self.api = api

242

243

def handler(self, func):

244

# Get the function and arguments info

245

func_name = argname('func')

246

self.api.endpoints[self.name] = {

247

'handler': func,

248

'source_name': func_name

249

}

250

return func

251

252

# Usage

253

api = APIBuilder()

254

255

def user_handler():

256

return "User data"

257

258

api.endpoint().users.handler(user_handler)

259

# Automatically registers 'users' endpoint with user_handler

260

```

261

262

### Smart Debugging with Context

263

264

```python

265

from varname import argname, will

266

267

def smart_assert(condition, message="Assertion failed"):

268

if not condition:

269

# Get the condition expression

270

cond_expr = argname('condition', vars_only=False)

271

raise AssertionError(f"{message}: {cond_expr}")

272

273

def conditional_processor():

274

next_action = will(raise_exc=False)

275

if next_action:

276

return f"Will perform: {next_action}"

277

return "Direct processing"

278

279

# Usage

280

x = 5

281

smart_assert(x > 10) # AssertionError: Assertion failed: x > 10

282

283

processor = conditional_processor()

284

result = processor.validate # result == "Will perform: validate"

285

```

286

287

## Ignore System Classes

288

289

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

290

291

### Core Ignore Classes

292

293

```python { .api }

294

class IgnoreList:

295

"""A list of ignore elements for frame filtering."""

296

297

@classmethod

298

def create(cls, ignore: IgnoreType) -> "IgnoreList":

299

"""Create ignore list from ignore specification."""

300

301

def get_frame(self, frame: int) -> FrameType:

302

"""Get the target frame after applying ignore rules."""

303

304

class IgnoreElem:

305

"""Abstract base class for ignore elements."""

306

307

def match(self, frame: FrameType) -> bool:

308

"""Check if frame matches this ignore element."""

309

```

310

311

### Ignore Element Types

312

313

```python { .api }

314

# Module-based ignoring

315

class IgnoreModule(IgnoreElem):

316

"""Ignore calls from a module or its submodules."""

317

318

class IgnoreFilename(IgnoreElem):

319

"""Ignore calls from a module by matching filename."""

320

321

class IgnoreDirname(IgnoreElem):

322

"""Ignore calls from modules inside a directory."""

323

324

class IgnoreStdlib(IgnoreElem):

325

"""Ignore standard library calls."""

326

327

# Function-based ignoring

328

class IgnoreFunction(IgnoreElem):

329

"""Ignore a non-decorated function."""

330

331

class IgnoreDecorated(IgnoreElem):

332

"""Ignore a decorated function with decorator count."""

333

334

# Qualified name ignoring

335

class IgnoreModuleQualname(IgnoreElem):

336

"""Ignore by module and qualified name."""

337

338

class IgnoreFilenameQualname(IgnoreElem):

339

"""Ignore by filename and qualified name."""

340

341

class IgnoreOnlyQualname(IgnoreElem):

342

"""Ignore by qualified name only."""

343

```

344

345

#### Usage Examples

346

347

```python

348

from varname import varname

349

from varname.ignore import IgnoreModule, IgnoreFunction

350

351

# Custom ignore list for complex scenarios

352

def complex_wrapper():

353

# Ignore specific modules and functions

354

ignore_list = [

355

IgnoreModule("decorator_library"),

356

IgnoreFunction(some_wrapper_function),

357

("mymodule", "MyClass.method") # Module + qualname

358

]

359

return varname(ignore=ignore_list)

360

361

# The ignore system automatically handles standard patterns

362

result = complex_wrapper()

363

```

364

365

**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.