or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dict-operations.mddownload-caching.mdfunction-utilities.mdhashing-imports.mdindex.mdlist-operations.mdpath-operations.mdprogress-timing.mdsystem-integration.mdtext-processing.md

function-utilities.mddocs/

0

# Function and Concurrency Utilities

1

2

Function manipulation, memoization, and enhanced concurrency tools for improving performance and managing function behavior.

3

4

## Capabilities

5

6

### Function Utilities

7

8

Basic function manipulation and utility functions.

9

10

```python { .api }

11

def identity(arg):

12

"""

13

Identity function that returns input unchanged.

14

15

Args:

16

arg: Any input value

17

18

Returns:

19

The same value passed as input

20

21

Note:

22

Useful as default function argument or for functional programming

23

"""

24

25

def inject_method(class_, func, name=None):

26

"""

27

Dynamically inject method into class.

28

29

Args:

30

class_: Target class to modify

31

func: Function to inject as method

32

name (str): Method name (uses func.__name__ if None)

33

34

Returns:

35

The modified class

36

"""

37

38

def compatible(func, args, kwargs):

39

"""

40

Check if function is compatible with given arguments.

41

42

Args:

43

func: Function to check

44

args: Positional arguments

45

kwargs: Keyword arguments

46

47

Returns:

48

bool: True if function can be called with given arguments

49

"""

50

```

51

52

### Memoization

53

54

Decorators for caching function results to improve performance.

55

56

```python { .api }

57

def memoize(func=None, **kwargs):

58

"""

59

Memoize function results for performance optimization.

60

61

Args:

62

func: Function to memoize (when used as @memoize)

63

**kwargs: Memoization options

64

65

Returns:

66

Memoized function

67

68

Usage:

69

@ub.memoize

70

def expensive_function(x):

71

return x ** 2

72

"""

73

74

def memoize_method(func=None, **kwargs):

75

"""

76

Memoize instance method results.

77

78

Args:

79

func: Method to memoize

80

**kwargs: Memoization options

81

82

Returns:

83

Memoized method

84

85

Usage:

86

class MyClass:

87

@ub.memoize_method

88

def expensive_method(self, x):

89

return x ** 2

90

"""

91

92

def memoize_property(func=None, **kwargs):

93

"""

94

Memoize property computation.

95

96

Args:

97

func: Property getter to memoize

98

**kwargs: Memoization options

99

100

Returns:

101

Memoized property

102

103

Usage:

104

class MyClass:

105

@ub.memoize_property

106

def expensive_property(self):

107

return expensive_computation()

108

"""

109

```

110

111

### Concurrency

112

113

Enhanced concurrency tools for parallel execution and job management.

114

115

```python { .api }

116

class Executor:

117

"""

118

Enhanced executor interface wrapping concurrent.futures.

119

Provides simplified API for parallel execution.

120

"""

121

def __init__(self, mode='thread', max_workers=None, **kwargs):

122

"""

123

Args:

124

mode (str): Execution mode ('thread', 'process')

125

max_workers (int): Maximum number of workers

126

**kwargs: Additional executor options

127

"""

128

129

def submit(self, func, *args, **kwargs):

130

"""Submit function for execution"""

131

132

def map(self, func, *iterables, **kwargs):

133

"""Map function over iterables in parallel"""

134

135

def __enter__(self): ...

136

def __exit__(self, exc_type, exc_val, exc_tb): ...

137

138

class JobPool:

139

"""

140

Job pool for managing concurrent tasks with progress tracking.

141

"""

142

def __init__(self, mode='thread', max_workers=None): ...

143

144

def submit(self, func, *args, **kwargs):

145

"""Submit job to pool"""

146

147

def collect(self, show_progress=True):

148

"""Collect all job results"""

149

150

def __enter__(self): ...

151

def __exit__(self, exc_type, exc_val, exc_tb): ...

152

```

153

154

## Usage Examples

155

156

### Basic Function Utilities

157

158

```python

159

import ubelt as ub

160

161

# Identity function

162

result = ub.identity(42)

163

print(result) # 42

164

165

# Useful in functional programming

166

data = [1, 2, 3, 4, 5]

167

filtered = filter(lambda x: x > 3, data)

168

# Use identity when no transformation needed

169

result = list(map(ub.identity, filtered))

170

print(result) # [4, 5]

171

172

# Default function argument

173

def process_data(data, transform=ub.identity):

174

"""Process data with optional transformation"""

175

return [transform(item) for item in data]

176

177

# No transformation

178

result1 = process_data([1, 2, 3])

179

print(result1) # [1, 2, 3]

180

181

# With transformation

182

result2 = process_data([1, 2, 3], transform=lambda x: x * 2)

183

print(result2) # [2, 4, 6]

184

```

185

186

### Dynamic Method Injection

187

188

```python

189

import ubelt as ub

190

191

# Define a class

192

class Calculator:

193

def __init__(self, value=0):

194

self.value = value

195

196

def add(self, x):

197

self.value += x

198

return self

199

200

# Define function to inject

201

def multiply(self, x):

202

self.value *= x

203

return self

204

205

def power(self, exp):

206

self.value = self.value ** exp

207

return self

208

209

# Inject methods dynamically

210

ub.inject_method(Calculator, multiply)

211

ub.inject_method(Calculator, power)

212

213

# Use the enhanced class

214

calc = Calculator(5)

215

result = calc.add(3).multiply(2).power(2)

216

print(calc.value) # ((5 + 3) * 2) ** 2 = 256

217

```

218

219

### Function Compatibility Checking

220

221

```python

222

import ubelt as ub

223

224

def func_with_args(a, b, c=10):

225

return a + b + c

226

227

def func_with_kwargs(x, **kwargs):

228

return x + sum(kwargs.values())

229

230

# Check compatibility

231

args1 = (1, 2)

232

kwargs1 = {'c': 5}

233

compatible1 = ub.compatible(func_with_args, args1, kwargs1)

234

print(f"Compatible with args {args1}, kwargs {kwargs1}: {compatible1}") # True

235

236

args2 = (1,) # Missing required argument 'b'

237

compatible2 = ub.compatible(func_with_args, args2, {})

238

print(f"Compatible with args {args2}: {compatible2}") # False

239

240

# Use for safe function calls

241

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

242

if ub.compatible(func, args, kwargs):

243

return func(*args, **kwargs)

244

else:

245

return None

246

247

result = safe_call(func_with_args, 1, 2, c=3)

248

print(f"Safe call result: {result}") # 6

249

```

250

251

### Memoization

252

253

```python

254

import ubelt as ub

255

import time

256

257

# Basic function memoization

258

@ub.memoize

259

def fibonacci(n):

260

if n <= 1:

261

return n

262

time.sleep(0.01) # Simulate expensive computation

263

return fibonacci(n-1) + fibonacci(n-2)

264

265

# First call is slow

266

start_time = time.time()

267

result1 = fibonacci(10)

268

time1 = time.time() - start_time

269

print(f"First call: {result1} in {time1:.3f}s")

270

271

# Second call is fast (memoized)

272

start_time = time.time()

273

result2 = fibonacci(10)

274

time2 = time.time() - start_time

275

print(f"Memoized call: {result2} in {time2:.6f}s")

276

277

# Method memoization

278

class DataProcessor:

279

def __init__(self, data):

280

self.data = data

281

282

@ub.memoize_method

283

def expensive_analysis(self, threshold):

284

"""Expensive analysis that should be cached"""

285

print(f"Performing analysis with threshold {threshold}")

286

time.sleep(0.1) # Simulate work

287

return sum(x for x in self.data if x > threshold)

288

289

processor = DataProcessor([1, 5, 10, 15, 20, 25])

290

result1 = processor.expensive_analysis(10) # Slow first call

291

print(f"Analysis result: {result1}")

292

293

result2 = processor.expensive_analysis(10) # Fast second call

294

print(f"Cached result: {result2}")

295

```

296

297

### Property Memoization

298

299

```python

300

import ubelt as ub

301

import time

302

303

class ExpensiveCalculation:

304

def __init__(self, data):

305

self.data = data

306

307

@ub.memoize_property

308

def mean(self):

309

"""Expensive mean calculation"""

310

print("Computing mean...")

311

time.sleep(0.1) # Simulate expensive computation

312

return sum(self.data) / len(self.data)

313

314

@ub.memoize_property

315

def variance(self):

316

"""Expensive variance calculation"""

317

print("Computing variance...")

318

time.sleep(0.1)

319

mean_val = self.mean # This will use memoized mean

320

return sum((x - mean_val) ** 2 for x in self.data) / len(self.data)

321

322

calc = ExpensiveCalculation([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

323

print(f"Mean: {calc.mean}") # Computes and caches

324

print(f"Variance: {calc.variance}") # Computes and caches (reuses mean)

325

print(f"Mean again: {calc.mean}") # Uses cache

326

```

327

328

### Parallel Execution

329

330

```python

331

import ubelt as ub

332

import time

333

334

def slow_function(x):

335

"""Simulate slow computation"""

336

time.sleep(0.1)

337

return x ** 2

338

339

# Sequential execution

340

start_time = time.time()

341

results_sequential = [slow_function(i) for i in range(10)]

342

sequential_time = time.time() - start_time

343

print(f"Sequential: {sequential_time:.3f}s")

344

345

# Parallel execution with Executor

346

start_time = time.time()

347

with ub.Executor(mode='thread', max_workers=4) as executor:

348

results_parallel = list(executor.map(slow_function, range(10)))

349

parallel_time = time.time() - start_time

350

print(f"Parallel: {parallel_time:.3f}s")

351

print(f"Speedup: {sequential_time/parallel_time:.2f}x")

352

```

353

354

### Job Pool with Progress

355

356

```python

357

import ubelt as ub

358

import time

359

import random

360

361

def variable_work(task_id):

362

"""Task with variable execution time"""

363

work_time = random.uniform(0.1, 0.5)

364

time.sleep(work_time)

365

return f"Task {task_id} completed in {work_time:.2f}s"

366

367

# Submit jobs to pool

368

with ub.JobPool(mode='thread', max_workers=3) as pool:

369

# Submit multiple jobs

370

for i in range(10):

371

pool.submit(variable_work, i)

372

373

# Collect results with progress bar

374

results = pool.collect(show_progress=True)

375

376

print("Results:")

377

for result in results:

378

print(f" {result}")

379

```

380

381

### Advanced Concurrent Patterns

382

383

```python

384

import ubelt as ub

385

import time

386

import requests

387

from concurrent.futures import as_completed

388

389

def fetch_url(url):

390

"""Fetch URL with error handling"""

391

try:

392

response = requests.get(url, timeout=5)

393

return {'url': url, 'status': response.status_code, 'size': len(response.content)}

394

except Exception as e:

395

return {'url': url, 'error': str(e)}

396

397

# URLs to fetch

398

urls = [

399

'https://httpbin.org/delay/1',

400

'https://httpbin.org/delay/2',

401

'https://httpbin.org/status/200',

402

'https://httpbin.org/status/404',

403

'https://httpbin.org/json'

404

]

405

406

# Fetch URLs in parallel with custom handling

407

with ub.Executor(mode='thread', max_workers=3) as executor:

408

# Submit all tasks

409

future_to_url = {executor.submit(fetch_url, url): url for url in urls}

410

411

# Process results as they complete

412

for future in ub.ProgIter(as_completed(future_to_url), total=len(urls), desc='Fetching'):

413

url = future_to_url[future]

414

try:

415

result = future.result()

416

if 'error' in result:

417

print(f"Error fetching {url}: {result['error']}")

418

else:

419

print(f"Success {url}: {result['status']} ({result['size']} bytes)")

420

except Exception as e:

421

print(f"Exception for {url}: {e}")

422

```

423

424

### Memoization with Custom Cache Control

425

426

```python

427

import ubelt as ub

428

import time

429

430

# Memoization with cache size limit

431

@ub.memoize

432

def expensive_function(x, y):

433

print(f"Computing {x} + {y}")

434

time.sleep(0.1)

435

return x + y

436

437

# Clear cache when needed

438

def reset_cache():

439

expensive_function.cache_clear()

440

441

# Use memoized function

442

result1 = expensive_function(1, 2) # Computes

443

result2 = expensive_function(1, 2) # Uses cache

444

result3 = expensive_function(3, 4) # Computes

445

446

print(f"Cache info: {expensive_function.cache_info()}")

447

448

# Clear and recompute

449

reset_cache()

450

result4 = expensive_function(1, 2) # Computes again

451

```