zettcode-sdk 0.3.4__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.
zettcode/table.py ADDED
@@ -0,0 +1,95 @@
1
+ """Plain-text layout for the command line: columns measured in terminal cells, not in characters.
2
+
3
+ A command that prints a table has two ways to get it wrong. ``len`` counts a Chinese character as one cell when a
4
+ terminal gives it two, so a column of mixed names is crooked; and one row of many columns is wider than the terminal
5
+ it is printed to. :func:`table` measures with :func:`display_width`, and :func:`block` lays one field per line, which
6
+ is what a record with ten fields needs. Both are stdlib only, so any command may import them.
7
+
8
+ A cell's text is shown, not interpreted: a newline, a tab, an escape or a bidi override in a title becomes a space,
9
+ so a value can never break the layout or turn the rest of the line around; :func:`pad` does the same. Width is counted
10
+ per code point, with combining marks, joiners, variation selectors and skin-tone modifiers taking none; a joined emoji
11
+ is measured as its parts, which is wider than the one glyph a terminal may draw, and that is the limit of what this
12
+ can know without a terminal.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import unicodedata
18
+ from collections.abc import Iterable, Sequence
19
+
20
+ __all__ = ["block", "display_width", "pad", "table"]
21
+
22
+ #: Categories a character keeps to itself: an accent on the letter before it, a joining mark, a direction mark.
23
+ _ZERO_WIDTH = ("Mn", "Me", "Cf")
24
+
25
+ #: Format characters that reorder or hide what follows: a right-to-left override turns the rest of the line around.
26
+ _BIDI = frozenset("\u061c\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069")
27
+
28
+
29
+ def display_width(text: str) -> int:
30
+ """Return how many terminal cells ``text`` takes.
31
+
32
+ An East Asian wide or fullwidth character takes two cells; a combining mark, a joiner, a variation selector, a
33
+ skin-tone modifier or another format character (``Cf``) takes none; everything else takes one.
34
+ """
35
+ cells = 0
36
+ for character in text:
37
+ if unicodedata.category(character) in _ZERO_WIDTH or _modifier(character):
38
+ continue
39
+ cells += 2 if unicodedata.east_asian_width(character) in ("W", "F") else 1
40
+ return cells
41
+
42
+
43
+ def _modifier(character: str) -> bool:
44
+ """Return whether ``character`` only changes the glyph before it: a variation selector or a skin tone."""
45
+ return "\ufe0e" <= character <= "\ufe0f" or "\U0001f3fb" <= character <= "\U0001f3ff"
46
+
47
+
48
+ def _shown(text: str) -> str:
49
+ """Return ``text`` with the characters a terminal would act on rather than show replaced by spaces."""
50
+ return "".join(
51
+ " " if unicodedata.category(character) in ("Cc", "Cs", "Zl", "Zp") or character in _BIDI else character
52
+ for character in text
53
+ )
54
+
55
+
56
+ def pad(text: str, width: int) -> str:
57
+ """Return ``text`` followed by the spaces that make it ``width`` cells wide; a wider one is left alone.
58
+
59
+ Control characters are shown as spaces here too, so a caller that pads with this cannot break its own line.
60
+ """
61
+ shown = _shown(text)
62
+ return shown + " " * max(0, width - display_width(shown))
63
+
64
+
65
+ def table(rows: Sequence[Sequence[str]], *, indent: str = "", separator: str = " ") -> list[str]:
66
+ """Return ``rows`` as aligned lines, the first row being the headings.
67
+
68
+ Each column is as wide as its widest cell in terminal cells, so a name in Chinese lines up with one in Latin.
69
+
70
+ Raises:
71
+ ValueError: A row holds a different number of cells from the first, so there is no column to put it in.
72
+ """
73
+ if not rows:
74
+ return []
75
+ width = len(rows[0])
76
+ if any(len(row) != width for row in rows):
77
+ raise ValueError(f"every row of a table needs {width} cells")
78
+ shown = [[_shown(cell) for cell in row] for row in rows]
79
+ widths = [max(display_width(row[index]) for row in shown) for index in range(width)]
80
+ return [
81
+ (indent + separator.join(pad(cell, widths[index]) for index, cell in enumerate(row))).rstrip() for row in shown
82
+ ]
83
+
84
+
85
+ def block(fields: Iterable[tuple[str, str]], *, indent: str = " ", separator: str = " ") -> list[str]:
86
+ """Return one line per ``(label, value)``, the labels padded to the widest of them.
87
+
88
+ This is the layout for a record: a table with one row per field and no heading, so a value may hold a path, a
89
+ sentence or a title without widening every other row.
90
+ """
91
+ pairs = [(_shown(label), _shown(value)) for label, value in fields]
92
+ if not pairs:
93
+ return []
94
+ width = max(display_width(label) for label, _ in pairs)
95
+ return [f"{indent}{pad(label, width)}{separator}{value}".rstrip() for label, value in pairs]
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.5
2
+ Name: zettcode-sdk
3
+ Version: 0.3.4
4
+ Summary: The documented, stable Python surface over ZettCode: sessions, runs, models, daemons
5
+ Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
6
+ Author: Chang-LeHung
7
+ License-Expression: MIT
8
+ Keywords: agent,sdk,zettcode
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: httpx<1,>=0.27
19
+ Requires-Dist: zett-agent<0.3,>=0.2.7
20
+ Requires-Dist: zettcode-broker
21
+ Requires-Dist: zettcode-engine
@@ -0,0 +1,30 @@
1
+ zettcode/doctor.py,sha256=7wNHm_or83TSL3cV1YjoJUWXb-syTPGLirL0ueb9PTw,6976
2
+ zettcode/mentions.py,sha256=ObKgfoNsVF26atYlZXyBBuLkE_oewtatrxduLQyRaPQ,13730
3
+ zettcode/table.py,sha256=VL1miiuBkq7sk8Rr2G_aOpV1IsJjlg62QwoAkrrXDCY,4557
4
+ zettcode/daemon/__init__.py,sha256=bepYUKyEnsis1e-JJwSw6slnXs3AsqTiz7g1iaSHcyo,943
5
+ zettcode/daemon/__main__.py,sha256=jaKXXQMUGDPC0rmJ2_e3E7s_u7xfW4yT9B5y_r-cV7k,263
6
+ zettcode/daemon/cli.py,sha256=xGcp5RH33kVBLoGMW9LsyEZP_TQmvpYzn42n3WA9MtA,28124
7
+ zettcode/daemon/companions.py,sha256=udz_cd_A4oqxl9BbqBq5JeZz19e9Kb3DhpgGPYB94_0,2763
8
+ zettcode/daemon/process.py,sha256=y4fmOZ7y6cOvEJenE5uzTn7HNJlOLLAYG3gPGZlYbRU,25571
9
+ zettcode/daemon/run.py,sha256=UdW_dwYSRzwB9gAzKqhBHuFP6nmrrYnXgAjghtT6aNk,33800
10
+ zettcode/daemon/state.py,sha256=abgM5NLQMTQRpVbooY8hh0vK_CNN7wvNF-6NP8rS-IE,15974
11
+ zettcode/sdk/__init__.py,sha256=PZiUsPvm1tkYNh9ycVe3t7zZHM3NhV4D8d7OWv5CNmY,6465
12
+ zettcode/sdk/builtins.py,sha256=SzsjBptA8Wiqog6vxC9vt3D7BN9hKzHsfbDqZrZi3W8,25121
13
+ zettcode/sdk/client.py,sha256=ammzc0Knl2GVx3TjYVTJS0c2OoHQTmfS3PWh0DkY0uw,78639
14
+ zettcode/sdk/commands.py,sha256=6iHuJDPxh0Uc1AlrYyRZEr681ZKdXVcIs355xmSZedA,494
15
+ zettcode/sdk/daemons.py,sha256=pT8s6T7RoyNaVqaAtFPyA1gt1HqkbBKS5AO3zuckdFc,10468
16
+ zettcode/sdk/errors.py,sha256=gFCjSwEohoxiI2WE4WL7edbCXMRn93tGPociRLAv9vU,4939
17
+ zettcode/sdk/events.py,sha256=2pavVQvwM_UuiXV6YUmJy7fuRO0ta9tL8sDer0MZ06M,14051
18
+ zettcode/sdk/handlers.py,sha256=NeU2ZA_G38kk0rPhQJKzSD3BgaJ4ix9-EVsskZM9wRI,5439
19
+ zettcode/sdk/messenger.py,sha256=FqHjN4Pzuh2bD_4fef0_eJMynyVF2AZH7TZZPKpbWbg,13416
20
+ zettcode/sdk/models.py,sha256=4qtlp3hpGEut5ncqWkD9qMkEwF7Ka3LU9Hjumc1cXaU,6900
21
+ zettcode/sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ zettcode/sdk/session.py,sha256=USdy5zbtFdtkUyYCoNO-wX0kxsRaNypMSyv8mVHL-oQ,51394
23
+ zettcode/sdk/translation.py,sha256=HFZYHrNoUu70Uzdi0V9hAp9Zou5MNKAN8hz1zOUFmMw,16063
24
+ zettcode/sdk/types.py,sha256=TPc7JM8D0g8WZdk0Zt3aGjZGp7TsBqqZtyzwBjW_ioM,33904
25
+ zettcode/sdk/extensions/__init__.py,sha256=mwyP4MwOCD6dRAY57YHVXFGCZXL4xhB0c_FCX__DFQc,433
26
+ zettcode/sdk/extensions/mcp.py,sha256=CtLlhJ439W5k7JlhKrrpicYijdDTCuuo0-_JUIy7y6E,14171
27
+ zettcode/sdk/extensions/messaging.py,sha256=lsvb55CuyH7ZH1YOIeGkisxgjZTxWd7hvd0tAWuiDOY,16216
28
+ zettcode_sdk-0.3.4.dist-info/METADATA,sha256=2LGzTgLlRnhBt0ZhHomHEt90MHgnkNBgs301Hji44Ng,834
29
+ zettcode_sdk-0.3.4.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
30
+ zettcode_sdk-0.3.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any