or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bases.mdexceptions.mdindex.mdinference.mdmanager.mdnodes.mdparsing.md

inference.mddocs/

0

# Inference System

1

2

Advanced static inference capabilities that determine types and values of expressions. The inference system is one of astroid's key features, enabling static analysis tools to understand what Python code will do at runtime.

3

4

## Capabilities

5

6

### Inference Context

7

8

Manages state and caching during inference operations.

9

10

```python { .api }

11

class InferenceContext:

12

"""Context for managing inference state."""

13

14

nodes_inferred: int

15

"""Number of nodes inferred in this context."""

16

17

callcontext: CallContext | None

18

"""Call context for function arguments."""

19

20

boundnode: NodeNG | None

21

"""Node that this context is bound to."""

22

23

lookupname: str | None

24

"""Name being looked up."""

25

26

def __init__(self, nodes_inferred: int = 0) -> None:

27

"""Initialize inference context."""

28

29

def clone(self) -> InferenceContext:

30

"""Create a copy of this context."""

31

32

def is_empty(self) -> bool:

33

"""Check if context has no bound node."""

34

35

def push(self, node: NodeNG) -> InferenceContext:

36

"""Push a new node onto the context stack."""

37

38

def restore_path(self) -> None:

39

"""Restore the inference path."""

40

```

41

42

### Call Context

43

44

Specialized context for function and method calls.

45

46

```python { .api }

47

class CallContext:

48

"""Context for function/method calls."""

49

50

args: list[NodeNG]

51

"""Positional arguments."""

52

53

keywords: list[Keyword]

54

"""Keyword arguments."""

55

56

callee: NodeNG | None

57

"""The callable being invoked."""

58

59

def __init__(self, args: list[NodeNG], keywords: list[Keyword] | None = None, callee: NodeNG | None = None) -> None:

60

"""Initialize call context."""

61

62

def infer_argument(self, name: str, context: InferenceContext | None = None) -> Iterator[InferenceResult]:

63

"""Infer the value of a named argument."""

64

```

65

66

### Inference Functions

67

68

Core functions for performing inference on various node types.

69

70

```python { .api }

71

def infer_call_result(call_node: Call, context: InferenceContext | None = None) -> Iterator[InferenceResult]:

72

"""

73

Infer the result of a function/method call.

74

75

Parameters:

76

- call_node: Call node to infer

77

- context: Inference context

78

79

Yields:

80

Possible return values of the call

81

82

Raises:

83

InferenceError: When call cannot be inferred

84

"""

85

86

def infer_attribute(attr_node: Attribute, context: InferenceContext | None = None) -> Iterator[InferenceResult]:

87

"""

88

Infer attribute access results.

89

90

Parameters:

91

- attr_node: Attribute node to infer

92

- context: Inference context

93

94

Yields:

95

Possible attribute values

96

"""

97

98

def infer_subscript(subscript_node: Subscript, context: InferenceContext | None = None) -> Iterator[InferenceResult]:

99

"""

100

Infer subscription results.

101

102

Parameters:

103

- subscript_node: Subscript node to infer

104

- context: Inference context

105

106

Yields:

107

Possible subscripted values

108

"""

109

```

110

111

### Inference Tips

112

113

System for registering custom inference behavior for specific functions or classes.

114

115

```python { .api }

116

def inference_tip(func: Callable) -> Callable:

117

"""

118

Decorator to register inference tips.

119

120

Parameters:

121

- func: Function returning inference results

122

123

Returns:

124

Decorated function that can be used as inference tip

125

"""

126

127

def _inference_tip_cached(func: Callable) -> Callable:

128

"""

129

Cached version of inference tip decorator.

130

131

Parameters:

132

- func: Function to cache inference results for

133

134

Returns:

135

Cached inference function

136

"""

137

```

138

139

### Special Inference Objects

140

141

Objects representing special inference states and results.

142

143

```python { .api }

144

class Uninferable:

145

"""Represents values that cannot be inferred."""

146

147

def __bool__(self) -> bool:

148

"""Always returns False."""

149

150

def __repr__(self) -> str:

151

"""String representation."""

152

153

Uninferable: type[Uninferable]

154

"""Singleton representing uninferable values."""

155

156

def safe_infer(node: NodeNG, context: InferenceContext | None = None) -> NodeNG | None:

157

"""

158

Safe inference that returns None instead of raising exceptions.

159

160

Parameters:

161

- node: Node to infer

162

- context: Inference context

163

164

Returns:

165

Inferred node or None if inference fails

166

"""

167

```

168

169

## Usage Examples

170

171

### Basic Inference

172

173

```python

174

import astroid

175

176

code = '''

177

x = 42

178

y = "hello"

179

z = [1, 2, 3]

180

result = x + len(y)

181

'''

182

183

module = astroid.parse(code)

184

185

# Infer variable values

186

for node in module.nodes_of_class(astroid.Name):

187

if node.name == 'result':

188

for inferred in node.infer():

189

if hasattr(inferred, 'value'):

190

print(f"Result value: {inferred.value}") # 47

191

```

192

193

### Function Call Inference

194

195

```python

196

import astroid

197

198

code = '''

199

def add(a, b):

200

return a + b

201

202

def multiply(x, y):

203

return x * y

204

205

result1 = add(10, 20)

206

result2 = multiply(3, 4)

207

'''

208

209

module = astroid.parse(code)

210

211

# Find function calls and infer results

212

for call in module.nodes_of_class(astroid.Call):

213

try:

214

for inferred in call.infer():

215

if hasattr(inferred, 'value'):

216

print(f"Call result: {inferred.value}")

217

except astroid.InferenceError:

218

print("Cannot infer call result")

219

```

220

221

### Attribute Inference

222

223

```python

224

import astroid

225

226

code = '''

227

class MyClass:

228

def __init__(self):

229

self.value = 42

230

231

def get_value(self):

232

return self.value

233

234

obj = MyClass()

235

attr_value = obj.value

236

method_result = obj.get_value()

237

'''

238

239

module = astroid.parse(code)

240

241

# Infer attribute access

242

for attr in module.nodes_of_class(astroid.Attribute):

243

try:

244

for inferred in attr.infer():

245

print(f"Attribute {attr.attrname}: {type(inferred).__name__}")

246

except astroid.InferenceError:

247

print(f"Cannot infer attribute {attr.attrname}")

248

```

249

250

### Context-Aware Inference

251

252

```python

253

import astroid

254

255

code = '''

256

def func(param):

257

if isinstance(param, str):

258

return param.upper()

259

elif isinstance(param, int):

260

return param * 2

261

return None

262

263

result = func("hello")

264

'''

265

266

module = astroid.parse(code)

267

268

# Create inference context

269

context = astroid.InferenceContext()

270

271

# Find the function call

272

for call in module.nodes_of_class(astroid.Call):

273

if isinstance(call.func, astroid.Name) and call.func.name == 'func':

274

try:

275

for inferred in call.infer(context):

276

print(f"Function result: {inferred}")

277

except astroid.InferenceError as e:

278

print(f"Inference failed: {e}")

279

```

280

281

### Custom Inference Tips

282

283

```python

284

import astroid

285

286

# Register custom inference for a function

287

@astroid.inference_tip

288

def infer_custom_function(node, context=None):

289

"""Custom inference for special functions."""

290

if (isinstance(node, astroid.Call) and

291

isinstance(node.func, astroid.Name) and

292

node.func.name == 'special_func'):

293

# Return custom inference result

294

return iter([astroid.Const(value="custom_result")])

295

raise astroid.InferenceError("Not a special function")

296

297

# Apply to manager

298

astroid.MANAGER.register_transform(astroid.Call, infer_custom_function)

299

```

300

301

### Working with Uninferable

302

303

```python

304

import astroid

305

306

code = '''

307

import random

308

x = random.choice([1, 2, 3]) # Can't be statically determined

309

y = x + 10

310

'''

311

312

module = astroid.parse(code)

313

314

for name in module.nodes_of_class(astroid.Name):

315

if name.name in ('x', 'y'):

316

inferred = astroid.safe_infer(name)

317

if inferred is None:

318

print(f"{name.name} is uninferable")

319

elif inferred is astroid.Uninferable:

320

print(f"{name.name} explicitly uninferable")

321

else:

322

print(f"{name.name} inferred as: {inferred}")

323

```

324

325

## Advanced Inference

326

327

### Method Resolution

328

329

```python

330

import astroid

331

332

code = '''

333

class Base:

334

def method(self):

335

return "base"

336

337

class Derived(Base):

338

def method(self):

339

return "derived"

340

341

obj = Derived()

342

result = obj.method()

343

'''

344

345

module = astroid.parse(code)

346

347

# Find method calls and infer through MRO

348

for call in module.nodes_of_class(astroid.Call):

349

if isinstance(call.func, astroid.Attribute):

350

try:

351

for inferred in call.infer():

352

print(f"Method result: {inferred}")

353

except astroid.InferenceError:

354

print("Method call inference failed")

355

```

356

357

### Inference Limitations

358

359

The inference system has limitations:

360

361

- Dynamic attribute creation

362

- Runtime-dependent values

363

- Complex control flow

364

- Metaclass behavior

365

- Import system intricacies

366

367

```python

368

import astroid

369

370

# These cases may not infer correctly

371

problematic_code = '''

372

# Dynamic attributes

373

class Dynamic:

374

pass

375

376

obj = Dynamic()

377

setattr(obj, 'attr', 42) # Can't infer obj.attr

378

379

# Runtime values

380

import random

381

value = random.randint(1, 100) # Uninferable

382

383

# Complex control flow

384

def complex_func(x):

385

for i in range(x):

386

if some_condition():

387

return compute_value(i)

388

return None

389

'''

390

```

391

392

## Error Handling

393

394

Inference operations can raise various exceptions:

395

396

- **InferenceError**: Base inference failure

397

- **NameInferenceError**: Name lookup failure

398

- **AttributeInferenceError**: Attribute access failure

399

- **UseInferenceDefault**: Fallback to default behavior

400

401

Always handle these exceptions or use `safe_infer()` for robust code.