xllify 0.8.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,407 @@
1
+ Metadata-Version: 2.4
2
+ Name: xllify
3
+ Version: 0.8.9
4
+ Summary: Python SDK for creating Excel XLL add-ins with xllify
5
+ Author: Alex Reid
6
+ License: MIT
7
+ Project-URL: Homepage, https://xllify.com
8
+ Project-URL: Documentation, https://xllify.com
9
+ Project-URL: Repository, https://github.com/xllifycom/xllify-python
10
+ Project-URL: Issues, https://github.com/xllifycom/xllify-python/issues
11
+ Keywords: excel,xll,udf,add-in,finance
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: pyzmq>=25.0
24
+ Requires-Dist: requests>=2.25.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
28
+ Requires-Dist: black>=23.0; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # xllify Python SDK
32
+
33
+ A Python SDK for creating high-performance Excel add-ins with xllify. Write Python functions and call them from Excel with automatic type conversion, error handling, and real-time updates.
34
+
35
+ ## Installation
36
+
37
+ You it is strongly recommended that you create a virtual environment and activate it.
38
+
39
+ ```bash
40
+ pip install virtualenv # if you don't have it
41
+ virtualenv venv
42
+ source venv/bin/activate # mac OR
43
+ source venv\Scripts\activate # win
44
+ ```
45
+
46
+ Then install xllify.
47
+
48
+ ```bash
49
+ pip install xllify
50
+ xllify-install
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. Create your Python functions
56
+
57
+ ```python
58
+ # my_functions.py
59
+ import xllify
60
+
61
+ @xllify.fn("xllipy.Hello")
62
+ def hello(name: str = "World") -> str:
63
+ return f"Hello, {name}!"
64
+
65
+ @xllify.fn("xllipy.Add", category="Math")
66
+ def add(a: float, b: float) -> float:
67
+ return a + b
68
+ ```
69
+
70
+ ### 2. Build
71
+
72
+ ```bash
73
+ xllify MyAddin.xll my_functions.py
74
+ ```
75
+
76
+ ### 3. Use in Excel
77
+
78
+ Functions are immediately available after you open the .xll in Excel.
79
+
80
+ ```
81
+ =xllipy.Hello("World") -> "Hello, World!"
82
+ =xllipy.Add(5, 10) -> 15
83
+ ```
84
+
85
+ Functions execute asynchronously - Excel shows #N/A while processing, then updates automatically when complete.
86
+
87
+ ## Workflow
88
+
89
+ ### Build and deployment
90
+
91
+ For production, Python files are embedded directly into your XLL:
92
+
93
+ ```bash
94
+ xllify build MyAddin.xll main.py --requirements requirements.txt
95
+ ```
96
+
97
+ When the XLL loads in Excel, Python files are extracted to `%LOCALAPPDATA%\xllify\MyAddin\python\` and the Python process starts automatically.
98
+
99
+ ### Distribution
100
+
101
+ Just distribute the XLL file. Everything else (Python files, dependencies) is embedded and will be extracted automatically on first load.
102
+
103
+ ## Features
104
+
105
+ - **Async by default** - Functions run asynchronously so Excel never freezes
106
+ - **Auto-reload** - Hot reload functions during development without restarting Excel
107
+ - **Matrix support** - Return 2D arrays and pandas DataFrames directly to Excel
108
+ - **Type-safe** - Full type hints with `CellValue`, `Matrix`, and `ExcelValue`
109
+ - **Simple API** - Just add the `@xllify.fn()` decorator to your existing functions
110
+
111
+ ## Best practices
112
+
113
+ Our recommended approach is for you to implement your business logic in plain Python modules and call them from short functions wrapped with `@xllify.fn`. This keeps your core logic testable, reusable, and independent of Excel.
114
+
115
+ ```python
116
+ # my_logic.py - Pure Python, no xllify dependencies
117
+ def calculate_option_price(spot, strike, time, rate, volatility):
118
+ """Black-Scholes call option pricing - testable business logic"""
119
+ # ... implementation ...
120
+ return price
121
+
122
+ # excel_functions.py - Thin xllify wrapper
123
+ import xllify
124
+ from my_logic import calculate_option_price
125
+
126
+ @xllify.fn("xllipy.BSCall", category="Finance")
127
+ def bs_call(s: float, k: float, t: float, r: float, sigma: float) -> float:
128
+ """Excel wrapper for Black-Scholes calculation"""
129
+ return calculate_option_price(s, k, t, r, sigma)
130
+ ```
131
+
132
+ Benefits:
133
+ - **Testable**: Run `pytest` on `my_logic.py` without Excel or xllify
134
+ - **Reusable**: Use the same logic in web apps, CLIs, or other contexts without needing to import xllify
135
+ - **Maintainable**: Separate concerns between business logic and Excel integration
136
+ - **Debuggable**: Test and debug core logic independently
137
+
138
+ ## Advanced usage
139
+
140
+ ### Batching configuration
141
+
142
+ By default, xllify batches RTD updates for better performance (batch_size=500, batch_timeout_ms=50). You can customize this:
143
+
144
+ ```python
145
+ import xllify
146
+
147
+ # Configure batching before registering functions
148
+ xllify.configure_batching(
149
+ enabled=True,
150
+ batch_size=1000, # Batch up to 1000 updates together
151
+ batch_timeout_ms=100 # Wait up to 100ms before flushing
152
+ )
153
+
154
+ @xllify.fn("xllipy.Hello")
155
+ def hello(name: str) -> str:
156
+ return f"Hello, {name}!"
157
+ ```
158
+
159
+ **When to adjust batching:**
160
+ - **High-volume scenarios**: Increase `batch_size` (1000+) and `batch_timeout_ms` (100+) for better throughput when handling many concurrent calculations
161
+ - **Low-latency requirements**: Decrease `batch_timeout_ms` (10-20ms) or disable batching entirely for faster individual responses
162
+ - **Balanced performance**: Use defaults (batch_size=500, batch_timeout_ms=50)
163
+
164
+ **Disable batching:**
165
+ ```python
166
+ xllify.configure_batching(enabled=False) # Send updates immediately
167
+ ```
168
+
169
+ ### Parameter metadata
170
+
171
+ Provide detailed parameter information for better documentation:
172
+
173
+ ```python
174
+ from xllify import fn, Parameter
175
+
176
+ @fn(
177
+ "xllipy.Calculate",
178
+ description="Perform calculation with optional delay",
179
+ category="Math",
180
+ parameters=[
181
+ Parameter("value", type="number", description="Value to process"),
182
+ Parameter("delay", type="number", description="Delay in seconds (optional)")
183
+ ],
184
+ return_type="number"
185
+ )
186
+ def calculate(value: float, delay: float = 1.0) -> float:
187
+ """Process a value with optional delay"""
188
+ import time
189
+ if delay > 0:
190
+ time.sleep(delay)
191
+ return value * 2
192
+ ```
193
+
194
+ ### Working with arrays and matrices
195
+
196
+ Excel ranges are passed as 2D lists. You can also **return** matrices to Excel:
197
+
198
+ ```python
199
+ from xllify import Matrix
200
+
201
+ # Input: Accept ranges as 2D lists
202
+ @xllify.fn("xllipy.SumArray", description="Sum all numbers in a range")
203
+ def sum_array(numbers: list) -> float:
204
+ """Sum a 2D array from Excel range"""
205
+ total = 0.0
206
+ for row in numbers:
207
+ for cell in row:
208
+ if isinstance(cell, (int, float)):
209
+ total += cell
210
+ return total
211
+
212
+ # Output: Return matrices to Excel
213
+ @xllify.fn("xllipy.GetData")
214
+ def get_data() -> Matrix:
215
+ """Return a 2D array to Excel"""
216
+ return [
217
+ [1.0, True, "hello"],
218
+ [2.0, False, None], # None displays as empty cell
219
+ [3.0, None, "world"]
220
+ ]
221
+ ```
222
+
223
+ In Excel:
224
+ ```
225
+ =xllipy.SumArray(A1:C10) -> Sum of all numbers
226
+ =xllipy.GetData() -> Spills 3x3 array into cells
227
+ ```
228
+
229
+ ### Pandas DataFrames
230
+
231
+ Return DataFrames directly - automatically converted to Excel ranges with headers:
232
+
233
+ ```python
234
+ import pandas as pd
235
+
236
+ @xllify.fn("xllipy.GetDataFrame")
237
+ def get_dataframe() -> pd.DataFrame:
238
+ """Return pandas DataFrame to Excel"""
239
+ return pd.DataFrame({
240
+ 'Name': ['Alice', 'Bob', 'Charlie'],
241
+ 'Age': [25, 30, 35],
242
+ 'Score': [95.5, 87.3, 92.1]
243
+ })
244
+ ```
245
+
246
+ In Excel, `=xllipy.GetDataFrame()` spills as:
247
+ ```
248
+ Name Age Score
249
+ Alice 25 95.5
250
+ Bob 30 87.3
251
+ Charlie 35 92.1
252
+ ```
253
+
254
+ ### Type mapping
255
+
256
+ | Excel Type | Python Type |
257
+ |------------|-------------|
258
+ | Number | `float` |
259
+ | String | `str` |
260
+ | Boolean | `bool` |
261
+ | Range | `List[List]` (2D array) |
262
+ | Empty | `None` |
263
+
264
+ ## API reference
265
+
266
+ ### Type definitions
267
+
268
+ ```python
269
+ from xllify import CellValue, Matrix, ExcelValue
270
+
271
+ CellValue = Union[float, bool, str, None]
272
+ Matrix = List[List[CellValue]]
273
+ ExcelValue = Union[CellValue, Matrix, Any]
274
+ ```
275
+
276
+ - **CellValue**: A single Excel cell value (number, boolean, string, or None for empty cells)
277
+ - **Matrix**: A 2D array of cell values
278
+ - **ExcelValue**: Any value that can be returned to Excel (scalar, matrix, or pandas DataFrame)
279
+
280
+ ### Decorators
281
+
282
+ #### `@xllify.fn(name, description="", category="", parameters=None, return_type="")`
283
+
284
+ Register a Python function as an Excel function.
285
+
286
+ **Arguments:**
287
+ - `name` (str): Excel function name (e.g., "xllipy.MyFunc")
288
+ - `description` (str, optional): Function description (defaults to docstring)
289
+ - `category` (str, optional): Excel function category
290
+ - `parameters` (List[Parameter], optional): Parameter metadata
291
+ - `return_type` (str, optional): Return type override
292
+
293
+ **Example:**
294
+ ```python
295
+ @xllify.fn("xllipy.Add", description="Add numbers", category="Math")
296
+ def add(a: float, b: float) -> float:
297
+ return a + b
298
+ ```
299
+
300
+ ### CLI commands
301
+
302
+ ```bash
303
+ # Install the xllify tool and xllify-lua
304
+ xllify-install
305
+
306
+ # Clear async function cache
307
+ xllify-clear-cache
308
+ ```
309
+
310
+ ## Error handling
311
+
312
+ Python exceptions are caught and returned to Excel as error strings:
313
+
314
+ ```python
315
+ @xllify.fn("xllipy.Divide")
316
+ def divide(a, b):
317
+ if b == 0:
318
+ raise ValueError("Division by zero")
319
+ return a / b
320
+ ```
321
+
322
+ In Excel:
323
+ ```
324
+ =xllipy.Divide(10, 0) -> #ERROR: Division by zero
325
+ ```
326
+
327
+ ## More examples
328
+
329
+ ### Slow operations (async)
330
+
331
+ Functions run asynchronously - Excel never freezes:
332
+
333
+ ```python
334
+ @xllify.fn("xllipy.SlowCalc")
335
+ def slow_calc(seconds: float) -> str:
336
+ import time
337
+ time.sleep(seconds)
338
+ return f"Done after {seconds}s"
339
+ ```
340
+
341
+ Excel shows #N/A while waiting, then updates automatically.
342
+ > It is worth pointing out that this WILL however block your Python process. A simple workaround is to run multiple processes of the same Python script. `asyncio` support is planned.
343
+
344
+ ### Black-Scholes option pricing
345
+
346
+ ```python
347
+ from math import log, sqrt, exp, erf
348
+
349
+ @xllify.fn("xllipy.BSCall", category="Finance")
350
+ def black_scholes_call(s: float, k: float, t: float, r: float, sigma: float) -> float:
351
+ """Black-Scholes call option price"""
352
+ if t <= 0:
353
+ return max(s - k, 0)
354
+
355
+ d1 = (log(s / k) + (r + 0.5 * sigma ** 2) * t) / (sigma * sqrt(t))
356
+ d2 = d1 - sigma * sqrt(t)
357
+
358
+ def norm_cdf(x):
359
+ return 0.5 * (1 + erf(x / sqrt(2)))
360
+
361
+ return s * norm_cdf(d1) - k * exp(-r * t) * norm_cdf(d2)
362
+ ```
363
+
364
+ Usage: `=xllipy.BSCall(100, 95, 0.25, 0.05, 0.2)`
365
+
366
+ ### HTTP requests
367
+
368
+ ```python
369
+ @xllify.fn("xllipy.FetchPrice")
370
+ def fetch_price(symbol: str) -> float:
371
+ import requests
372
+ resp = requests.get(f"https://api.example.com/price/{symbol}")
373
+ return resp.json()["price"]
374
+ ```
375
+
376
+ ### System info
377
+
378
+ ```python
379
+ @xllify.fn("xllipy.GetInfo")
380
+ def get_info() -> str:
381
+ import sys
382
+ import platform
383
+ return f"Python {sys.version.split()[0]} on {platform.system()}"
384
+ ```
385
+
386
+ ## Development
387
+
388
+ ### Running tests
389
+
390
+ ```bash
391
+ pip install -e ".[dev]"
392
+ pytest tests/ -v
393
+ ```
394
+
395
+ ### Code formatting
396
+
397
+ ```bash
398
+ black xllify/ tests/ examples/
399
+ ```
400
+
401
+ ## License
402
+
403
+ MIT
404
+
405
+ ## Support
406
+
407
+ - Issues: https://github.com/xllifycom/xllify-python/issues
@@ -0,0 +1,15 @@
1
+ xllify/XLLIFY_DIST_VERSION,sha256=1SFdV7EyDWuUVWVw3KFzS2imQ1exZck0frXl3AAV2Sg,7
2
+ xllify/__init__.py,sha256=A80rrO9InK4wszp1A-Qdfopc0E40E_bN6Rv_U31Ohs4,2928
3
+ xllify/__main__.py,sha256=x7pZHqCCGnHa6a5j9bgBq9AEqcp31e9BbyqR7oX772w,10711
4
+ xllify/diagnostics.py,sha256=JSCmZEHUSEUgsm6Z-eeXwnG4tdskDVKIdIhoGlkIb0A,2459
5
+ xllify/funcinfo.py,sha256=zZsJgA3VDiNFQLqsGkCRUb0liIcz4PXXHJE7nEbFBnI,14120
6
+ xllify/install.py,sha256=_zJ1C2AE4qXoQI2MVDkdVJ4YEn_CoYd320f5BIvNsDc,8214
7
+ xllify/py.typed,sha256=uQAMa-HCv2CTSGvNbRA28pPjZcEZIZhX97AJaNajfGw,72
8
+ xllify/rpc_server.py,sha256=kNkKz6GC-iS9IW01lVPmlSki83qGFSnWQYe1HUzPPOw,24646
9
+ xllify/rtd_client.py,sha256=od0ZluXU2d5nPVn9zDg8wGn-ZgMfJ4TlPwk0JDa5L8M,20650
10
+ xllify-0.8.9.dist-info/licenses/LICENSE,sha256=BM-hal6apcxEHTtn9YUywwuIQzBv9lxaVlXuJUKQJIs,1105
11
+ xllify-0.8.9.dist-info/METADATA,sha256=2ybn88HIqmnKX_bjEv9kY2QzNSgqFoxfTpu4DDqXofw,11369
12
+ xllify-0.8.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ xllify-0.8.9.dist-info/entry_points.txt,sha256=h3UTb7Wi2RvNAJ0vgEyIYCCTU9Z_CI_KuMirJxQDVdE,182
14
+ xllify-0.8.9.dist-info/top_level.txt,sha256=sjZHYyyqJSOJWSSeMgByqNhqZW0qgPqqSgGn6gxNrbs,7
15
+ xllify-0.8.9.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ xllify-clear-cache = xllify.__main__:clear_cache_main
3
+ xllify-funcinfo = xllify.funcinfo:main
4
+ xllify-install = xllify.install:main
5
+ xllify-rpc = xllify.__main__:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alex Reid / Lexvica Limited
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ xllify