or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-serialization.mddiagnostic-tools.mdindex.mdpickler-classes.mdsession-management.mdsource-analysis.mdtemp-operations.mdtype-registry.md

source-analysis.mddocs/

0

# Source Code Analysis

1

2

dill provides powerful tools for extracting, analyzing, and manipulating source code from Python objects, enabling introspection, code generation, and dynamic analysis capabilities.

3

4

## Source Code Extraction

5

6

### Primary Source Functions

7

8

```python { .api }

9

def getsource(object, alias='', lstrip=False, enclosing=False, force=False, builtin=False):

10

"""

11

Get source code for an object.

12

13

Extracts the source code of functions, classes, methods, and other

14

code objects, providing string representation of the original code.

15

The source code for interactively-defined objects are extracted from

16

the interpreter's history.

17

18

Parameters:

19

- object: object to get source for (module, class, method, function, traceback, frame, or code object)

20

- alias: str, alias name for the object (adds line of code that renames the object)

21

- lstrip: bool, ensure there is no indentation in the first line of code

22

- enclosing: bool, include enclosing code and dependencies

23

- force: bool, catch (TypeError,IOError) and try to use import hooks

24

- builtin: bool, force an import for any builtins

25

26

Returns:

27

str: source code as single string

28

29

Raises:

30

- IOError: when source code cannot be retrieved

31

- TypeError: for objects where source code is unavailable (e.g. builtins)

32

"""

33

34

def getsourcelines(object, lstrip=False, enclosing=False):

35

"""

36

Get source lines for an object.

37

38

Returns the source code as a list of lines along with the starting

39

line number in the original file.

40

41

Parameters:

42

- object: object to get source lines for

43

- lstrip: bool, left-strip whitespace from source lines

44

- enclosing: bool, include enclosing scope

45

46

Returns:

47

tuple: (list of source lines, starting line number)

48

49

Raises:

50

- OSError: when source cannot be found

51

"""

52

53

def findsource(object):

54

"""

55

Find source code for an object.

56

57

Locate the source code for an object by examining the file system,

58

module imports, and other available sources.

59

60

Parameters:

61

- object: object to find source for

62

63

Returns:

64

tuple: (list of all source lines, starting line number)

65

66

Raises:

67

- OSError: when source cannot be located

68

"""

69

```

70

71

### Source Code Utilities

72

73

```python { .api }

74

def getblocks(object, lstrip=False, enclosing=False, locate=False):

75

"""

76

Get code blocks for an object.

77

78

Extracts logical code blocks (functions, classes, methods) associated

79

with an object, providing structured access to code components.

80

81

Parameters:

82

- object: object to get code blocks for

83

- lstrip: bool, left-strip whitespace from blocks

84

- enclosing: bool, include enclosing blocks

85

- locate: bool, include location information

86

87

Returns:

88

list: list of code blocks with metadata

89

"""

90

91

def dumpsource(object, alias='', new=False, enclose=True):

92

"""

93

Dump source code with additional metadata.

94

95

Creates a comprehensive source dump including dependencies,

96

imports, and context information needed for code reconstruction.

97

98

Parameters:

99

- object: object to dump source for

100

- alias: str, alias name for the object

101

- new: bool, create new-style source dump format

102

- enclose: bool, enclose source with additional context

103

104

Returns:

105

str: comprehensive source dump

106

"""

107

```

108

109

## Import Analysis

110

111

### Import Statement Generation

112

113

```python { .api }

114

def getimport(obj, alias='', verify=True, builtin=False, enclosing=False):

115

"""

116

Get the likely import string for the given object.

117

118

Generates the appropriate import statement needed to access an object,

119

including module path resolution and alias handling.

120

121

Parameters:

122

- obj: object to inspect and generate import for

123

- alias: str, alias name to use (renames the object on import)

124

- verify: bool, test the import string before returning it

125

- builtin: bool, force an import for builtins where possible

126

- enclosing: bool, get the import for the outermost enclosing callable

127

128

Returns:

129

str: import statement as string

130

"""

131

132

def getimportable(obj, alias='', byname=True, explicit=False):

133

"""

134

Get importable representation of an object.

135

136

Creates a representation that can be used to recreate the object

137

through import statements and attribute access.

138

139

Parameters:

140

- obj: object to make importable

141

- alias: str, alias name to use

142

- byname: bool, prefer name-based imports over direct references

143

- explicit: bool, use explicit import paths

144

145

Returns:

146

str: importable representation

147

"""

148

149

def likely_import(obj, passive=False, explicit=False):

150

"""

151

Get likely import statement for an object.

152

153

Attempts to determine the most likely import statement that would

154

provide access to the given object.

155

156

Parameters:

157

- obj: object to analyze

158

- passive: bool, use passive analysis without side effects

159

- explicit: bool, prefer explicit import statements

160

161

Returns:

162

str: likely import statement

163

"""

164

```

165

166

### Object Identification

167

168

```python { .api }

169

def getname(obj, force=False, fqn=False):

170

"""

171

Get the name of an object.

172

173

Attempts to determine the canonical name of an object by examining

174

its attributes, module context, and definition location.

175

176

Parameters:

177

- obj: object to get name for

178

- force: bool, force name extraction even if not directly available

179

- fqn: bool, return fully qualified name including module path

180

181

Returns:

182

str: object name or None if name cannot be determined

183

"""

184

185

def isfrommain(obj):

186

"""

187

Check if object is from __main__ module.

188

189

Determines whether an object originates from the main module,

190

which affects how it should be serialized and imported.

191

192

Parameters:

193

- obj: object to check

194

195

Returns:

196

bool: True if object is from __main__ module

197

"""

198

199

def isdynamic(obj):

200

"""

201

Check if object is dynamically created.

202

203

Determines whether an object was created dynamically at runtime

204

rather than being defined in source code.

205

206

Parameters:

207

- obj: object to check

208

209

Returns:

210

bool: True if object is dynamically created

211

"""

212

```

213

214

## Usage Examples

215

216

### Basic Source Extraction

217

218

```python

219

import dill.source as source

220

221

# Define a function

222

def example_function(x, y=10):

223

"""Example function with default parameter."""

224

result = x + y

225

return result * 2

226

227

# Get source code

228

source_code = source.getsource(example_function)

229

print(source_code)

230

# Output: Full function definition as string

231

232

# Get source lines with line numbers

233

lines, start_line = source.getsourcelines(example_function)

234

print(f"Function starts at line {start_line}")

235

for i, line in enumerate(lines):

236

print(f"{start_line + i}: {line.rstrip()}")

237

```

238

239

### Advanced Source Analysis

240

241

```python

242

import dill.source as source

243

244

class ExampleClass:

245

"""Example class for source analysis."""

246

247

def __init__(self, value):

248

self.value = value

249

250

def method(self, multiplier=2):

251

return self.value * multiplier

252

253

@staticmethod

254

def static_method(x):

255

return x ** 2

256

257

# Analyze class source

258

class_source = source.getsource(ExampleClass)

259

print("Class source:")

260

print(class_source)

261

262

# Get method source separately

263

method_source = source.getsource(ExampleClass.method)

264

print("\nMethod source:")

265

print(method_source)

266

267

# Get source blocks

268

blocks = source.getblocks(ExampleClass)

269

print(f"\nFound {len(blocks)} code blocks in class")

270

```

271

272

### Import Generation

273

274

```python

275

import dill.source as source

276

import json

277

from collections import defaultdict

278

279

# Generate import statements for various objects

280

objects_to_import = [

281

json.dumps,

282

defaultdict,

283

ExampleClass,

284

example_function

285

]

286

287

for obj in objects_to_import:

288

try:

289

import_stmt = source.getimport(obj)

290

name = source.getname(obj) or str(obj)

291

print(f"{name}: {import_stmt}")

292

except Exception as e:

293

print(f"Could not generate import for {obj}: {e}")

294

295

# Generate imports with aliases

296

alias_import = source.getimport(json.dumps, alias='json_serialize')

297

print(f"With alias: {alias_import}")

298

```

299

300

### Dynamic Code Analysis

301

302

```python

303

import dill.source as source

304

305

# Create dynamic function

306

exec('''

307

def dynamic_function(x):

308

return x * 3 + 1

309

''')

310

311

# Analyze dynamic objects

312

if source.isdynamic(dynamic_function):

313

print("Function was created dynamically")

314

315

if source.isfrommain(dynamic_function):

316

print("Function is from __main__ module")

317

318

# Try to get source of dynamic function

319

try:

320

dynamic_source = source.getsource(dynamic_function)

321

print("Dynamic function source:")

322

print(dynamic_source)

323

except OSError:

324

print("Cannot get source for dynamic function")

325

```

326

327

### Source Code Modification

328

329

```python

330

import dill.source as source

331

332

def modify_function_source(func, modifications):

333

"""Modify function source code."""

334

try:

335

# Get original source

336

original_source = source.getsource(func)

337

338

# Apply modifications

339

modified_source = original_source

340

for old, new in modifications.items():

341

modified_source = modified_source.replace(old, new)

342

343

# Create new function from modified source

344

namespace = {}

345

exec(modified_source, namespace)

346

347

# Find the function in the namespace

348

func_name = source.getname(func)

349

if func_name in namespace:

350

return namespace[func_name]

351

352

return None

353

354

except Exception as e:

355

print(f"Failed to modify function: {e}")

356

return None

357

358

# Example usage

359

def original_add(a, b):

360

return a + b

361

362

# Modify to multiply instead

363

modifications = {'a + b': 'a * b'}

364

modified_func = modify_function_source(original_add, modifications)

365

366

if modified_func:

367

print(f"Original: {original_add(3, 4)}") # 7

368

print(f"Modified: {modified_func(3, 4)}") # 12

369

```

370

371

## Advanced Features

372

373

### Source Code Archival

374

375

```python

376

import dill.source as source

377

import json

378

379

def archive_source_code(objects, filename):

380

"""Archive source code for multiple objects."""

381

archive = {}

382

383

for name, obj in objects.items():

384

try:

385

obj_info = {

386

'source': source.getsource(obj),

387

'import': source.getimport(obj, verify=False),

388

'name': source.getname(obj),

389

'is_dynamic': source.isdynamic(obj),

390

'is_from_main': source.isfrommain(obj)

391

}

392

archive[name] = obj_info

393

except Exception as e:

394

archive[name] = {'error': str(e)}

395

396

with open(filename, 'w') as f:

397

json.dump(archive, f, indent=2)

398

399

return archive

400

401

# Archive important functions and classes

402

objects_to_archive = {

403

'example_function': example_function,

404

'ExampleClass': ExampleClass,

405

'json_dumps': json.dumps

406

}

407

408

archive = archive_source_code(objects_to_archive, 'source_archive.json')

409

print(f"Archived {len(archive)} objects")

410

```

411

412

### Code Dependency Analysis

413

414

```python

415

import dill.source as source

416

import dill.detect as detect

417

import ast

418

419

def analyze_dependencies(func):

420

"""Analyze function dependencies."""

421

try:

422

# Get source code

423

source_code = source.getsource(func)

424

425

# Parse AST

426

tree = ast.parse(source_code)

427

428

# Find imports and name references

429

dependencies = {

430

'imports': [],

431

'globals': [],

432

'builtins': []

433

}

434

435

for node in ast.walk(tree):

436

if isinstance(node, ast.Import):

437

for alias in node.names:

438

dependencies['imports'].append(alias.name)

439

elif isinstance(node, ast.ImportFrom):

440

module = node.module or ''

441

for alias in node.names:

442

dependencies['imports'].append(f"{module}.{alias.name}")

443

444

# Get global variables referenced

445

global_vars = detect.referredglobals(func, recurse=True)

446

dependencies['globals'] = list(global_vars.keys())

447

448

return dependencies

449

450

except Exception as e:

451

return {'error': str(e)}

452

453

# Analyze function dependencies

454

deps = analyze_dependencies(example_function)

455

print("Function dependencies:")

456

for dep_type, items in deps.items():

457

if items:

458

print(f" {dep_type}: {items}")

459

```

460

461

### Interactive Source Browser

462

463

```python

464

import dill.source as source

465

466

class SourceBrowser:

467

"""Interactive source code browser."""

468

469

def __init__(self):

470

self.history = []

471

472

def browse(self, obj):

473

"""Browse source code for an object."""

474

try:

475

obj_name = source.getname(obj) or str(obj)

476

self.history.append(obj_name)

477

478

print(f"\n{'='*60}")

479

print(f"Source for {obj_name}")

480

print(f"{'='*60}")

481

482

# Show basic info

483

print(f"Type: {type(obj).__name__}")

484

print(f"From main: {source.isfrommain(obj)}")

485

print(f"Dynamic: {source.isdynamic(obj)}")

486

487

# Show import statement

488

try:

489

import_stmt = source.getimport(obj, verify=False)

490

print(f"Import: {import_stmt}")

491

except:

492

print("Import: Not available")

493

494

print(f"\nSource code:")

495

print("-" * 40)

496

497

# Show source

498

source_code = source.getsource(obj)

499

print(source_code)

500

501

except Exception as e:

502

print(f"Error browsing {obj}: {e}")

503

504

def show_history(self):

505

"""Show browsing history."""

506

print("\nBrowsing history:")

507

for i, item in enumerate(self.history):

508

print(f" {i+1}. {item}")

509

510

# Usage

511

browser = SourceBrowser()

512

browser.browse(example_function)

513

browser.browse(ExampleClass)

514

browser.show_history()

515

```

516

517

## Integration with Serialization

518

519

### Source-Aware Pickling

520

521

```python

522

import dill

523

import dill.source as source

524

525

def pickle_with_source(obj, filename):

526

"""Pickle object along with its source code."""

527

# Create enhanced object with source

528

enhanced_obj = {

529

'object': obj,

530

'metadata': {

531

'source': None,

532

'import': None,

533

'name': source.getname(obj),

534

'type': type(obj).__name__

535

}

536

}

537

538

# Try to get source and import info

539

try:

540

enhanced_obj['metadata']['source'] = source.getsource(obj)

541

except:

542

pass

543

544

try:

545

enhanced_obj['metadata']['import'] = source.getimport(obj, verify=False)

546

except:

547

pass

548

549

# Pickle enhanced object

550

with open(filename, 'wb') as f:

551

dill.dump(enhanced_obj, f)

552

553

def unpickle_with_source(filename):

554

"""Unpickle object and display source information."""

555

with open(filename, 'rb') as f:

556

enhanced_obj = dill.load(f)

557

558

obj = enhanced_obj['object']

559

metadata = enhanced_obj['metadata']

560

561

print(f"Loaded {metadata['type']}: {metadata['name']}")

562

563

if metadata['import']:

564

print(f"Import: {metadata['import']}")

565

566

if metadata['source']:

567

print("Source code:")

568

print(metadata['source'])

569

570

return obj

571

572

# Usage

573

pickle_with_source(example_function, 'function_with_source.pkl')

574

restored_func = unpickle_with_source('function_with_source.pkl')

575

```