or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

charts-visualization.mdcli.mdcollections-functions.mdconstants.mdconversion.mdcore-objects.mdindex.mdpro-features.mdudfs.mdutilities.md

udfs.mddocs/

0

# User Defined Functions (UDFs)

1

2

Decorators and functions for creating custom Excel functions and subroutines in Python. xlwings UDFs allow you to replace VBA functions with Python implementations that can be called directly from Excel worksheets, supporting both Windows COM server and Office.js implementations.

3

4

## Capabilities

5

6

### Function Decorators

7

8

Core decorators for converting Python functions into Excel User Defined Functions (UDFs) that can be called from Excel worksheets.

9

10

```python { .api }

11

def func(f=None, *, category: str = None, volatile: bool = False,

12

call_in_wizard: bool = True, macro_type: str = None):

13

"""

14

Decorator to create Excel functions from Python functions.

15

16

Args:

17

f (callable, optional): Function to decorate.

18

category (str, optional): Excel function category for organization.

19

volatile (bool): If True, function recalculates on every calculation cycle.

20

call_in_wizard (bool): If True, function appears in Excel Function Wizard.

21

macro_type (str, optional): Macro type for advanced scenarios.

22

23

Returns:

24

callable: Decorated function that can be called from Excel.

25

26

Platform Support:

27

- Windows: COM server integration

28

- Web: Office.js via xlwings PRO

29

"""

30

31

def sub(f=None, *, call_in_wizard: bool = True):

32

"""

33

Decorator to create Excel subroutines from Python functions.

34

Subroutines are called as macros and don't return values to cells.

35

36

Args:

37

f (callable, optional): Function to decorate.

38

call_in_wizard (bool): If True, appears in Excel macro list.

39

40

Returns:

41

callable: Decorated function callable as Excel macro.

42

43

Platform Support:

44

- Windows only (COM server)

45

"""

46

47

def arg(convert=None, *, ndim: int = None, transpose: bool = False):

48

"""

49

Decorator for UDF argument conversion and validation.

50

51

Args:

52

convert: Converter class or conversion options.

53

ndim (int, optional): Force specific number of dimensions.

54

transpose (bool): Transpose input data before processing.

55

56

Returns:

57

callable: Argument converter decorator.

58

"""

59

60

def ret(convert=None, *, transpose: bool = False, expand: str = None):

61

"""

62

Decorator for UDF return value conversion.

63

64

Args:

65

convert: Converter class or conversion options.

66

transpose (bool): Transpose output data.

67

expand (str, optional): Dynamic array expansion ('table', 'down', 'right').

68

69

Returns:

70

callable: Return value converter decorator.

71

"""

72

```

73

74

Usage examples:

75

76

```python

77

import xlwings as xw

78

import numpy as np

79

import pandas as pd

80

81

# Basic UDF function

82

@xw.func

83

def hello(name):

84

"""Simple greeting function callable from Excel."""

85

return f"Hello {name}!"

86

87

# Function with category and options

88

@xw.func(category="Math", volatile=False)

89

def multiply_by_two(x):

90

"""Multiply input by 2."""

91

return x * 2

92

93

# Array function with converters

94

@xw.func

95

@xw.arg('x', np.array, ndim=2)

96

@xw.ret(np.array)

97

def matrix_multiply(x, y):

98

"""Matrix multiplication using NumPy."""

99

return np.dot(x, y)

100

101

# DataFrame function

102

@xw.func

103

@xw.arg('data', pd.DataFrame, index=False, header=True)

104

@xw.ret(pd.DataFrame, expand='table')

105

def process_dataframe(data):

106

"""Process DataFrame and return results."""

107

result = data.copy()

108

result['total'] = result.sum(axis=1)

109

return result

110

111

# Subroutine (macro)

112

@xw.sub

113

def clear_sheet():

114

"""Clear active worksheet content."""

115

xw.sheets.active.clear_contents()

116

117

# Advanced UDF with multiple converters

118

@xw.func

119

@xw.arg('prices', np.array, ndim=1)

120

@xw.arg('volumes', np.array, ndim=1)

121

@xw.ret(expand='down')

122

def calculate_vwap(prices, volumes):

123

"""Calculate Volume Weighted Average Price."""

124

return np.sum(prices * volumes) / np.sum(volumes)

125

```

126

127

### UDF Management Functions

128

129

Functions for managing UDF modules, COM server lifecycle, and dynamic UDF loading.

130

131

```python { .api }

132

def serve(clsid: str = "{506e67c3-55b5-48c3-a035-eed5deea7d6d}"):

133

"""

134

Start the COM server for UDFs on Windows.

135

Must be called to enable UDF functionality.

136

137

Args:

138

clsid (str): COM class ID for the server. Default is xlwings' standard ID.

139

140

Platform: Windows only

141

142

Usage:

143

if __name__ == '__main__':

144

xw.serve()

145

"""

146

147

def import_udfs(module_names: str, xl_workbook):

148

"""

149

Import UDF functions from Python modules.

150

151

Args:

152

module_names (str): Semicolon-separated module names containing UDF functions.

153

xl_workbook: Excel workbook object (internal parameter).

154

155

Note: This function is typically called internally by xlwings.

156

157

Examples:

158

# Import UDFs from multiple modules

159

xw.import_udfs('my_udfs;other_udfs', workbook)

160

"""

161

162

def get_udf_module(module_name: str, xl_workbook):

163

"""

164

Get reference to UDF module for introspection.

165

166

Args:

167

module_name (str): Name of UDF module.

168

xl_workbook: Excel workbook object (internal parameter).

169

170

Returns:

171

dict: Module information including the Python module object.

172

"""

173

```

174

175

### UDF Development Patterns

176

177

Common patterns and best practices for developing robust UDFs:

178

179

```python

180

# Pattern 1: Error handling in UDFs

181

@xw.func

182

def safe_divide(a, b):

183

"""Division with error handling."""

184

try:

185

if b == 0:

186

return "#DIV/0!"

187

return a / b

188

except Exception as e:

189

return f"#ERROR: {str(e)}"

190

191

# Pattern 2: Optional parameters

192

@xw.func

193

def format_currency(amount, currency="USD", decimals=2):

194

"""Format number as currency with optional parameters."""

195

if currency == "USD":

196

symbol = "$"

197

elif currency == "EUR":

198

symbol = "€"

199

else:

200

symbol = currency

201

202

return f"{symbol}{amount:.{decimals}f}"

203

204

# Pattern 3: Range processing

205

@xw.func

206

@xw.arg('data', np.array, ndim=2)

207

@xw.ret(expand='table')

208

def process_range(data, operation="sum"):

209

"""Process 2D range with specified operation."""

210

if operation == "sum":

211

return np.sum(data, axis=1).reshape(-1, 1)

212

elif operation == "mean":

213

return np.mean(data, axis=1).reshape(-1, 1)

214

elif operation == "std":

215

return np.std(data, axis=1).reshape(-1, 1)

216

else:

217

return data

218

219

# Pattern 4: Caching expensive operations

220

_cache = {}

221

222

@xw.func(volatile=False)

223

def expensive_calculation(input_value):

224

"""Cache expensive calculations."""

225

if input_value in _cache:

226

return _cache[input_value]

227

228

# Simulate expensive operation

229

import time

230

time.sleep(0.1)

231

result = input_value ** 2

232

233

_cache[input_value] = result

234

return result

235

236

# Pattern 5: Database integration

237

@xw.func

238

def query_database(sql_query, connection_string):

239

"""Execute SQL query and return results."""

240

import sqlite3

241

242

try:

243

conn = sqlite3.connect(connection_string)

244

cursor = conn.cursor()

245

cursor.execute(sql_query)

246

results = cursor.fetchall()

247

conn.close()

248

return results

249

except Exception as e:

250

return f"#ERROR: {str(e)}"

251

```

252

253

### Advanced UDF Features

254

255

Advanced UDF capabilities including async functions, caller context, and dynamic arrays:

256

257

```python

258

# Async UDF (Windows only)

259

@xw.func

260

async def async_web_request(url):

261

"""Asynchronous web request UDF."""

262

import aiohttp

263

264

async with aiohttp.ClientSession() as session:

265

async with session.get(url) as response:

266

return await response.text()

267

268

# Caller context UDF

269

@xw.func

270

def get_caller_info():

271

"""Get information about the calling cell."""

272

caller = xw.caller() # Special function to get caller context

273

return f"Called from {caller.address}"

274

275

# Dynamic array UDF (Excel 365/2021)

276

@xw.func

277

@xw.ret(expand='table')

278

def generate_multiplication_table(size):

279

"""Generate multiplication table as dynamic array."""

280

import numpy as np

281

282

if not isinstance(size, (int, float)) or size <= 0:

283

return "#ERROR: Size must be positive number"

284

285

size = int(size)

286

table = np.outer(np.arange(1, size + 1), np.arange(1, size + 1))

287

return table

288

289

# Real-time data UDF

290

@xw.func(volatile=True)

291

def get_stock_price(symbol):

292

"""Get real-time stock price (volatile function)."""

293

import requests

294

295

try:

296

# Simulated API call

297

url = f"https://api.example.com/stock/{symbol}"

298

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

299

data = response.json()

300

return data.get('price', '#N/A')

301

except:

302

return '#N/A'

303

```

304

305

### UDF Deployment and Distribution

306

307

Setting up UDF projects for distribution and deployment:

308

309

```python

310

# udfs.py - Main UDF module

311

import xlwings as xw

312

import numpy as np

313

import pandas as pd

314

315

@xw.func

316

def my_function(x):

317

return x * 2

318

319

# Server startup

320

if __name__ == '__main__':

321

# Start COM server for UDFs

322

xw.serve()

323

324

# setup.py for UDF package distribution

325

from setuptools import setup

326

327

setup(

328

name='my-excel-udfs',

329

version='1.0.0',

330

py_modules=['udfs'],

331

install_requires=['xlwings', 'numpy', 'pandas'],

332

entry_points={

333

'console_scripts': [

334

'start-udfs=udfs:main',

335

],

336

},

337

)

338

```

339

340

Excel VBA code for UDF integration:

341

342

```vba

343

' In Excel VBA (ThisWorkbook or Module)

344

Sub ImportPythonUDFs()

345

' Import UDFs from Python module

346

Application.Run "xlwings.import_udfs", "udfs"

347

End Sub

348

349

Sub StartUDFServer()

350

' Start Python UDF server

351

Shell "python -c ""import udfs; udfs.serve()"""

352

End Sub

353

```

354

355

## Platform-Specific Considerations

356

357

### Windows COM Server

358

359

```python

360

# Windows-specific UDF features

361

@xw.func

362

def windows_specific_function():

363

"""Function available only on Windows."""

364

import win32api

365

return win32api.GetComputerName()

366

367

# COM server configuration

368

if __name__ == '__main__':

369

# Register COM server

370

import sys

371

if len(sys.argv) > 1 and sys.argv[1] == '--register':

372

import win32com.server.register

373

win32com.server.register.UseCommandLine()

374

else:

375

xw.serve()

376

```

377

378

### Office.js (PRO)

379

380

```python

381

# Office.js UDFs (PRO version)

382

from xlwings.server import func, arg, ret

383

384

@func

385

@arg('data', convert='dataframe')

386

@ret(expand='table')

387

def web_compatible_function(data):

388

"""Function that works in both desktop and web Excel."""

389

return data.describe()

390

```

391

392

## Types

393

394

```python { .api }

395

# UDF decorator types

396

UDFDecorator = Callable[[Callable], Callable]

397

ArgDecorator = Callable[[Callable], Callable]

398

RetDecorator = Callable[[Callable], Callable]

399

400

# UDF function signatures

401

UDFFunction = Callable[..., Any]

402

SubroutineFunction = Callable[..., None]

403

404

# Server management

405

ServerFunction = Callable[[], None]

406

ImportFunction = Callable[[str], None]

407

GetModuleFunction = Callable[[str], Any]

408

```