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.
- xllify/XLLIFY_DIST_VERSION +2 -0
- xllify/__init__.py +101 -0
- xllify/__main__.py +343 -0
- xllify/diagnostics.py +78 -0
- xllify/funcinfo.py +375 -0
- xllify/install.py +251 -0
- xllify/py.typed +1 -0
- xllify/rpc_server.py +639 -0
- xllify/rtd_client.py +576 -0
- xllify-0.8.9.dist-info/METADATA +407 -0
- xllify-0.8.9.dist-info/RECORD +15 -0
- xllify-0.8.9.dist-info/WHEEL +5 -0
- xllify-0.8.9.dist-info/entry_points.txt +5 -0
- xllify-0.8.9.dist-info/licenses/LICENSE +21 -0
- xllify-0.8.9.dist-info/top_level.txt +1 -0
|
@@ -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,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
|