llusen for Python
Read and change the project open in llusen from Python: tables, formulas, filters, summary values, group tables, charts and notes; and export them as CSV, Excel or OpenDocument files.
import llusen
with llusen.connect() as project:
orders = project.table("orders")
orders.add_column("total", "price * qty")
orders.set_format("total", 'currency("£", 2)')
print(orders.summary())
frame = orders.to_pandas() # with pandas installed
Setting up
-
Python 3.14 or later. The package uses only the standard library; pandas is optional, for
to_pandasand for giving DataFrames. -
Install from PyPI:
pip install llusen, orpip install "llusen[pandas]"with pandas. From this folder:pip install .. -
In llusen, choose Tools > API Enabled. llusen then listens on this machine only, and writes its port and a token to
api.jsonin its state folder:- Linux:
~/.local/state/llusen(or$XDG_STATE_HOME/llusen); - macOS:
~/Library/Application Support/llusen; - Windows:
%LOCALAPPDATA%\llusen.
llusen.connect()reads that file and gives the token. API Enabled is remembered, and the token is new each time it is turned on. - Linux:
-
Which llusen: each project opens in a llusen of its own, with its own API, turned on in its own window.
connect()reaches the llusen used last (the one whose window last had the keyboard), asapi.jsonnames it. To reach the llusen that has a given project open, name the project's file:with llusen.connect(project="~/reports/sales.llu") as project: ...
If no llusen has it open, or the one that has has its API off,
NotRunningsays which.llusen.find_project(path)gives that llusen's port and token without connecting. (Each llusen lists what it has open ininstancesin the state folder.)
How it works
- Changes go into the project as if made in llusen. Each is one step that Ctrl+Z takes back in that table, and the status bar says what the script did. A table a script makes opens in a tab.
- Rows are counted in the order they were loaded or added, from 0, whatever the user's sort and filters show. Reading gives every row.
- Reading waits until formulas are worked out, so values are never half done. llusen gives up on a call after five minutes of waiting.
- A call llusen refuses raises
LlusenError, with llusen's own message. For a formula,error.atis the (start, end) of the problem in it. - If llusen isn't running, or API Enabled is off,
connectraisesNotRunning(aConnectionError).
Exporting
llusen writes the file, so the path is on the machine llusen runs on. A
relative path, or one from ~, is made whole from the script's folder
first. A file already there is replaced, through a temporary file, so a
failed write leaves it as it was. Rows go in the order llusen shows them
(sorted, every row, whatever the filters). llusen writes on a thread of
its own and answers once the file is complete; the status bar says so.
The path's extension decides the kind (.csv, .xlsx, .ods), unless
format says; a path with no extension gets the format's.
Values
| llusen | Python | pandas |
|---|---|---|
| int | int |
int64 (Int64 if any are missing) |
| float | float |
float64 |
| bool | bool |
bool (boolean if any are missing) |
| text | str |
object |
| date | datetime.date |
datetime64 |
| datetime | datetime.datetime (UTC, aware, if the column is UTC) |
datetime64 |
| time | datetime.time |
object (datetime.time) |
| duration | datetime.timedelta |
timedelta64 |
| null | None |
missing |
a fault such as #DIV0 |
llusen.Fault("#DIV0") |
missing |
When giving values, NaN, NaT and pd.NA are null; numpy's numbers are
numbers; a naive datetime is sent as it is, and an aware one in UTC;
pandas' Timedelta and numpy's timedelta64 are durations (to the
microsecond), and a time of day must have no time zone. A
new column's type is worked out from its values (whole numbers make
int, any decimal makes float), unless named in types.
Reference
llusen.connect(port=None, token=None, info_file=None, timeout=360, project=None) gives a Project: with project, the llusen that has
that project file open. Use it in a with block, or call close(). One
connection is for one thread at a time.
Project
describe(): every table as llusen describes it (columns, types, formulas, formats, filters, sort, summary values, groupings, charts, notes).table_names(),table(name),chart(name).new_table(name, data, types=None):datais a DataFrame, or a dict of column names to lists of values.new_group_table(name, grouping), e.g.'table("orders").group(r => ({ Item: r.item }))'.new_chart(name, table, x, ys, kind=None):ysis a column or a list of them;kindis"bar","line"or"doughnut"(a doughnut takes one column of values).export(path, format=None, tables=None): every table (or those named) to an Excel (.xlsx) or OpenDocument (.ods) workbook, a sheet each, with summary values at the top and column formats; charts are left out. Gives{"path", "rows", "sheets"}once written.call(method, **params): any API method, as llusen documents it.
Table
read(columns=None, start=0, count=None):{name: [values]}, read in pieces of 100,000 rows.to_pandas(columns=None).columns,len(table),summary()(values by name),describe().add_column(name, formula=None, values=None, type=None),set_formula,remove_formula,rename_column,delete_columns(*names),set_format(column, format=None).append_rows(data)gives the first new row;set_cells(column, values, start=0), all or none.change_case(column, case)puts every cell of a text column in"upper","lower","capitalize"(Hello world) or"title"(Hello World) case, and gives how many changed. The formula functions of the same names do it in a new column instead.- Live rows:
with table.locked(): table.insert_rows(data), orlock(),insert_rows(data)andunlock(). While a script has a table locked, rows go in at its end, many times a second if need be, with no undo steps; llusen lifts any sort and filters, and no one else can change the table (the user watches it fill, scrolled to the end).insert_rowsis answered at once, with the row its first will be at, and its rows go in as soon as the table, and the tables reading it, have worked out the last ones.unlockwaits until all are in. The lock goes if the script's connection closes.is_lockedsays whether a script has it. lock(clear=False, columns=None):columnsare the rows' columns and types, as{"price": "float", "side": "text"}. Withclear=True, the table is emptied for a new run and its plain columns made exactly those, in that order (formula columns stay, after them), with no undo step; it gives what changed. Without, they are checked against the table's, and a difference refuses the lock.locked()takes the same.set_filter(column, formula=None)gives the rows shown;valueis the column's cell, as in"value > 5".clear_filters().reapply_filters()runs them again over rows added or changed since they ran, and gives the rows shown.set_sort(column, direction="ascending")sorts the rows shown by a column,"ascending"or"descending";clear_sort()puts them back as they were. Rows read are stored rows, whatever the sort.add_summary(formula, name=None, format=None)gives its name;set_summary_formula,set_summary_format,delete_summary.set_grouping(grouping)for a group table.export(path, format=None): as CSV, or as a workbook of this table if the path ends in.xlsxor.ods(orformatsays so).rename(name),delete().
Chart
notes(),add_note(text, anchors=())gives its id. Each anchor is a(series, x)pair, wherexis a value of the column along the bottom: a date, a time, a duration, a number, or text as llusen shows it. A series of""puts the note on every series at that place. A note's anchors all share one series, or are all"".set_note(id, text=None, anchors=None)changes a note's text, its anchors (()for the chart as a whole), or both;delete_note(id).title(), andset_title(title=None):Noneor""is the title made from its columns.set_settings(kind=None, **settings): the kind ("bar","line","doughnut") and the settings menu, assmooth,line_fill,line_from_zero,horizontal,bar_from_zeroandpie(eachTrueorFalse); what is left out stays. Gives whether it changed.hidden_series(), andset_hidden_series(series)to hide those series from the key, as a click on it does, showing the rest. At least one series stays shown.rename(name),delete().
The protocol
Each message is one line of JSON-RPC 2.0 on a TCP connection to
127.0.0.1, with authenticate {token} first. See the module notes in
llusen-core/src/api.rs for every method and its params, if you want to
call llusen from another language.
Tests
python -m unittest discover -s tests runs the tests against a stand-in
for llusen. examples/orders.py is a tour against the real thing.
Metadata
Release files for llusen 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| llusen-0.1.4.tar.gz | 25.9 kB | Without provenance | Details for llusen-0.1.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| llusen-0.1.4-py3-none-any.whl | Python 3 | none | any | Details for llusen-0.1.4-py3-none-any.whl |
Total release size: 44.1 kB
Release files / llusen-0.1.4.tar.gz
| Download URL | llusen-0.1.4.tar.gz |
|---|---|
| Size | 25.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04f8cddb604eb4212830809799b436d1955b1a33378863886e5751d5dd548183
|
|
BLAKE2b-256 checksum How to use checksums |
9d6a85d46b32de06091dd9be2da8086edc1952a36a3d475ecc11308623fca0b4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.9
|
Release files / llusen-0.1.4-py3-none-any.whl
| Download URL | llusen-0.1.4-py3-none-any.whl |
|---|---|
| Size | 18.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2b068da54b2cc8da83c984f49e937e3f404a65c17cfc5bb59e7d77ce98b00a3d
|
|
BLAKE2b-256 checksum How to use checksums |
c1d3f3af28c4a3b60ce4d0cfbb319244a368fe1898f8ba9cdb007c062dd50fb5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.9
|
Security for llusen 0.1.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.