seejx 0.3.0__tar.gz

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.
seejx-0.3.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Lei Zhang (original "seej" project)
4
+ Copyright (c) 2025 seejx contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include LICENSE
2
+ include NOTICE
3
+ include README.md
seejx-0.3.0/NOTICE ADDED
@@ -0,0 +1,28 @@
1
+ seejx
2
+ =====
3
+
4
+ This project is a fork of the "seej" package:
5
+
6
+ seej — Interactive JSONL browser with rich rendering
7
+ Copyright (c) 2025 Lei Zhang
8
+ Author-email: Jiaxi Yang <jason.yang98@foxmail.com>
9
+ License: MIT
10
+ https://pypi.org/project/seej/
11
+
12
+ seejx redistributes and modifies that MIT-licensed code. The original MIT
13
+ license and copyright notice are retained in the LICENSE file.
14
+
15
+ Modifications in seejx
16
+ ----------------------
17
+ - Replaced the per-record "clear screen + reprint" navigation with a built-in
18
+ full-screen scroll viewer (alternate screen buffer):
19
+ * trackpad / mouse-wheel (SGR mouse reporting), arrow keys, j/k line scroll
20
+ * PageUp/PageDown / Space / b page scroll; g / G jump to top / bottom
21
+ * left / right (or n / p / Enter) to move between records
22
+ * the whole record is held in memory, so the top/bottom is always reachable
23
+ regardless of terminal scrollback size
24
+ - wcwidth-aware line wrapping so CJK (double-width) text wraps correctly.
25
+ - A soft, colored status bar (replacing the reverse-video footer).
26
+ - Added `wcwidth` as a dependency.
27
+ - Console entry point renamed to `seejx`; plugin entry-point group is
28
+ `seejx.plugins`.
seejx-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: seejx
3
+ Version: 0.3.0
4
+ Summary: Interactive JSONL browser with rich rendering and full-screen scrolling (fork of seej)
5
+ Author-email: "busi.wp" <busi.wp@alibaba-inc.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Lei Zhang (original "seej" project)
9
+ Copyright (c) 2025 seejx contributors
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://pypi.org/project/seejx/
30
+ Keywords: jsonl,browser,cli,rich,viewer,tui,pager
31
+ Classifier: Development Status :: 4 - Beta
32
+ Classifier: Environment :: Console
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: POSIX
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Topic :: Utilities
38
+ Requires-Python: >=3.8
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE
41
+ License-File: NOTICE
42
+ Requires-Dist: rich>=10.0.0
43
+ Requires-Dist: readchar>=3.0.0
44
+ Requires-Dist: tqdm>=4.0.0
45
+ Requires-Dist: wcwidth>=0.2.5
46
+ Provides-Extra: dev
47
+ Requires-Dist: pytest>=6.0; extra == "dev"
48
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
49
+ Requires-Dist: black>=21.0; extra == "dev"
50
+ Requires-Dist: build>=1.0; extra == "dev"
51
+ Requires-Dist: twine>=4.0; extra == "dev"
52
+ Dynamic: license-file
53
+
54
+ # seejx
55
+
56
+ **seejx** is an interactive terminal JSONL (JSON Lines) browser with rich
57
+ rendering and a built-in **full-screen scroll viewer**. It steps through
58
+ `.jsonl` files record by record, with pluggable per-field renderers for complex
59
+ data (chat messages, tables, nested structures).
60
+
61
+ It is a fork of [`seej`](https://pypi.org/project/seej/) (MIT, © 2025 Lei Zhang)
62
+ that replaces the old "clear-and-reprint" navigation with a proper scrollable
63
+ pager — so long records scroll smoothly and you can always reach the top.
64
+
65
+ ## Features
66
+
67
+ - **Full-screen scrolling** — trackpad / mouse wheel / arrow keys scroll within
68
+ a record; the whole record is kept in memory, so top and bottom are always
69
+ reachable (independent of terminal scrollback size).
70
+ - **Rich output** — formatting via the [`rich`](https://github.com/Textualize/rich) library.
71
+ - **CJK-aware wrapping** — double-width characters wrap correctly (via `wcwidth`).
72
+ - **Per-field renderers** — map fields to renderers (messages, tables, nested structures).
73
+ - **Filtering** — keep rows matching a safe Python expression.
74
+ - **Plugins** — extend rendering via Python entry points (`seejx.plugins`).
75
+ - **View modes** — toggle between rich and plain-text rendering.
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ pip install seejx
81
+ ```
82
+
83
+ ## Quick start
84
+
85
+ ```bash
86
+ # Basic usage
87
+ seejx path/to/file.jsonl
88
+
89
+ # Start at a given record index
90
+ seejx path/to/file.jsonl -I 10
91
+
92
+ # Plain text instead of rich rendering
93
+ seejx path/to/file.jsonl --raw
94
+ ```
95
+
96
+ ## Keys (in the viewer)
97
+
98
+ | Key | Action |
99
+ | --- | --- |
100
+ | Two-finger swipe / mouse wheel / ↑ ↓ / `j` `k` | Scroll line by line |
101
+ | PageUp / PageDown / Space / `b` | Scroll by page |
102
+ | `g` / `G` | Jump to top / bottom |
103
+ | → / ← (or `n` / `p` / Enter) | Next / previous record |
104
+ | `t` | Toggle rich / raw rendering |
105
+ | `q` (or Ctrl-C) | Quit |
106
+
107
+ > Tip: while the viewer is open it captures the trackpad for scrolling. To
108
+ > select text the normal way, hold **Option/Fn** while dragging.
109
+
110
+ ## License
111
+
112
+ MIT. See [LICENSE](LICENSE) and [NOTICE](NOTICE) (fork attribution).
seejx-0.3.0/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # seejx
2
+
3
+ **seejx** is an interactive terminal JSONL (JSON Lines) browser with rich
4
+ rendering and a built-in **full-screen scroll viewer**. It steps through
5
+ `.jsonl` files record by record, with pluggable per-field renderers for complex
6
+ data (chat messages, tables, nested structures).
7
+
8
+ It is a fork of [`seej`](https://pypi.org/project/seej/) (MIT, © 2025 Lei Zhang)
9
+ that replaces the old "clear-and-reprint" navigation with a proper scrollable
10
+ pager — so long records scroll smoothly and you can always reach the top.
11
+
12
+ ## Features
13
+
14
+ - **Full-screen scrolling** — trackpad / mouse wheel / arrow keys scroll within
15
+ a record; the whole record is kept in memory, so top and bottom are always
16
+ reachable (independent of terminal scrollback size).
17
+ - **Rich output** — formatting via the [`rich`](https://github.com/Textualize/rich) library.
18
+ - **CJK-aware wrapping** — double-width characters wrap correctly (via `wcwidth`).
19
+ - **Per-field renderers** — map fields to renderers (messages, tables, nested structures).
20
+ - **Filtering** — keep rows matching a safe Python expression.
21
+ - **Plugins** — extend rendering via Python entry points (`seejx.plugins`).
22
+ - **View modes** — toggle between rich and plain-text rendering.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install seejx
28
+ ```
29
+
30
+ ## Quick start
31
+
32
+ ```bash
33
+ # Basic usage
34
+ seejx path/to/file.jsonl
35
+
36
+ # Start at a given record index
37
+ seejx path/to/file.jsonl -I 10
38
+
39
+ # Plain text instead of rich rendering
40
+ seejx path/to/file.jsonl --raw
41
+ ```
42
+
43
+ ## Keys (in the viewer)
44
+
45
+ | Key | Action |
46
+ | --- | --- |
47
+ | Two-finger swipe / mouse wheel / ↑ ↓ / `j` `k` | Scroll line by line |
48
+ | PageUp / PageDown / Space / `b` | Scroll by page |
49
+ | `g` / `G` | Jump to top / bottom |
50
+ | → / ← (or `n` / `p` / Enter) | Next / previous record |
51
+ | `t` | Toggle rich / raw rendering |
52
+ | `q` (or Ctrl-C) | Quit |
53
+
54
+ > Tip: while the viewer is open it captures the trackpad for scrolling. To
55
+ > select text the normal way, hold **Option/Fn** while dragging.
56
+
57
+ ## License
58
+
59
+ MIT. See [LICENSE](LICENSE) and [NOTICE](NOTICE) (fork attribution).
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "seejx"
7
+ version = "0.3.0"
8
+ description = "Interactive JSONL browser with rich rendering and full-screen scrolling (fork of seej)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "busi.wp", email = "busi.wp@alibaba-inc.com" },
14
+ ]
15
+ keywords = ["jsonl", "browser", "cli", "rich", "viewer", "tui", "pager"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: POSIX",
22
+ "Programming Language :: Python :: 3",
23
+ "Topic :: Utilities",
24
+ ]
25
+ dependencies = [
26
+ "rich>=10.0.0",
27
+ "readchar>=3.0.0",
28
+ "tqdm>=4.0.0",
29
+ "wcwidth>=0.2.5",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest>=6.0",
35
+ "pytest-cov>=4.0",
36
+ "black>=21.0",
37
+ "build>=1.0",
38
+ "twine>=4.0",
39
+ ]
40
+
41
+ [project.scripts]
42
+ seejx = "seejx.see_jsonl:main"
43
+
44
+ [project.urls]
45
+ Homepage = "https://pypi.org/project/seejx/"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
seejx-0.3.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ """seejx - Interactive JSONL browser with rich rendering and full-screen scrolling.
2
+
3
+ Fork of `seej` (MIT, © 2025 Lei Zhang) with an in-app full-screen scroll
4
+ viewer: trackpad / mouse-wheel / arrow-key scrolling, wcwidth-aware wrapping,
5
+ and a soft status bar. See NOTICE for details.
6
+ """
7
+
8
+ __version__ = "0.3.0"
9
+
10
+ from .see_jsonl import main
11
+ from .renderers import register_renderer, get_renderer
12
+ from .conditions import SafeExampleProxy, build_condition_function
13
+
14
+ __all__ = [
15
+ "main",
16
+ "register_renderer",
17
+ "get_renderer",
18
+ "SafeExampleProxy",
19
+ "build_condition_function",
20
+ ]
@@ -0,0 +1,178 @@
1
+ """Condition parsing and evaluation for filtering examples."""
2
+
3
+ import ast
4
+ import operator
5
+ from typing import Any, Callable, Optional
6
+ from rich.console import Console
7
+
8
+ console = Console()
9
+
10
+
11
+ class SafeExampleProxy:
12
+ """安全的样本代理,支持 ex['key'] 和 ex.key 两种语法"""
13
+
14
+ def __init__(self, data: dict):
15
+ self._data = data
16
+
17
+ def __getitem__(self, key):
18
+ """支持 ex['key'] 语法"""
19
+ return self._data.get(key)
20
+
21
+ def __getattr__(self, key):
22
+ """支持 ex.key 语法"""
23
+ if key.startswith("_"):
24
+ raise AttributeError(f"Cannot access private attribute: {key}")
25
+ return self._data.get(key)
26
+
27
+ def __contains__(self, key):
28
+ """支持 'key' in ex"""
29
+ return key in self._data
30
+
31
+ def get(self, key, default=None):
32
+ """支持 ex.get('key', default)"""
33
+ return self._data.get(key, default)
34
+
35
+
36
+ def create_safe_namespace():
37
+ """创建安全的执行命名空间"""
38
+ # 只允许安全的内置函数和操作符
39
+ safe_builtins = {
40
+ "len": len,
41
+ "str": str,
42
+ "int": int,
43
+ "float": float,
44
+ "bool": bool,
45
+ "list": list,
46
+ "dict": dict,
47
+ "tuple": tuple,
48
+ "set": set,
49
+ "abs": abs,
50
+ "min": min,
51
+ "max": max,
52
+ "sum": sum,
53
+ "any": any,
54
+ "all": all,
55
+ "isinstance": isinstance,
56
+ "True": True,
57
+ "False": False,
58
+ "None": None,
59
+ }
60
+ return safe_builtins
61
+
62
+
63
+ def validate_condition_syntax(condition: str) -> tuple[bool, Optional[str]]:
64
+ """
65
+ 验证条件表达式的语法安全性
66
+
67
+ Returns:
68
+ (is_valid, error_message)
69
+ """
70
+ # 检查是否包含 'ex'
71
+ if "ex" not in condition:
72
+ return False, "Condition must reference 'ex' (e.g., ex['field'] == 'value')"
73
+
74
+ # 尝试解析 AST
75
+ try:
76
+ tree = ast.parse(condition, mode="eval")
77
+ except SyntaxError as e:
78
+ return False, f"Syntax error: {e}"
79
+
80
+ # 检查危险操作
81
+ dangerous_nodes = (
82
+ ast.Import,
83
+ ast.ImportFrom, # 禁止导入
84
+ ast.Call, # 暂时禁止所有函数调用(可以放宽)
85
+ )
86
+
87
+ for node in ast.walk(tree):
88
+ # 允许安全的函数调用
89
+ if isinstance(node, ast.Call):
90
+ if isinstance(node.func, ast.Name):
91
+ func_name = node.func.id
92
+ if func_name not in create_safe_namespace():
93
+ return False, f"Function '{func_name}' is not allowed"
94
+
95
+ # 禁止其他危险操作
96
+ if isinstance(node, (ast.Import, ast.ImportFrom)):
97
+ return False, "Import statements are not allowed"
98
+
99
+ return True, None
100
+
101
+
102
+ def build_condition_function(conditions: list[str]) -> Optional[Callable]:
103
+ """
104
+ 构建条件过滤函数
105
+
106
+ Args:
107
+ conditions: 条件表达式列表,例如 ["ex['type'] == 'chat'", "len(ex['messages']) > 5"]
108
+
109
+ Returns:
110
+ 过滤函数或 None
111
+ """
112
+ if not conditions:
113
+ return None
114
+
115
+ console.print("[bold]Building filter conditions:[/bold]")
116
+
117
+ # 验证并处理每个条件
118
+ validated_conditions = []
119
+ for idx, cond in enumerate(conditions, 1):
120
+ cond = cond.strip()
121
+ if not cond:
122
+ continue
123
+
124
+ is_valid, error = validate_condition_syntax(cond)
125
+ if not is_valid:
126
+ console.print(f"[red]✗[/red] Condition {idx}: {cond}")
127
+ console.print(f" [red]Error: {error}[/red]")
128
+ continue
129
+
130
+ validated_conditions.append(cond)
131
+ console.print(f"[green]✓[/green] Condition {idx}: [cyan]{cond}[/cyan]")
132
+
133
+ if not validated_conditions:
134
+ console.print("[yellow]Warning: No valid conditions, showing all examples[/yellow]")
135
+ return None
136
+
137
+ # 组合所有条件
138
+ combined_expr = " and ".join(f"({c})" for c in validated_conditions)
139
+ console.print(f"\n[bold]Combined:[/bold] [dim]{combined_expr}[/dim]\n")
140
+
141
+ # 编译表达式
142
+ try:
143
+ compiled_code = compile(combined_expr, "<condition>", "eval")
144
+ except Exception as e:
145
+ console.print(f"[red]Failed to compile conditions: {e}[/red]")
146
+ return None
147
+
148
+ # 创建安全的命名空间
149
+ safe_namespace = create_safe_namespace()
150
+
151
+ def condition_filter(example: dict) -> bool:
152
+ """实际的过滤函数"""
153
+ try:
154
+ # 创建代理对象
155
+ ex = SafeExampleProxy(example)
156
+ # 在安全命名空间中执行
157
+ namespace = {**safe_namespace, "ex": ex}
158
+ result = eval(compiled_code, {"__builtins__": {}}, namespace)
159
+ return bool(result)
160
+ except KeyError as e:
161
+ # 字段不存在时返回 False
162
+ return False
163
+ except Exception as e:
164
+ # 其他错误:打印警告但不中断
165
+ # console.print(f"[yellow]Warning: Condition evaluation error: {e}[/yellow]")
166
+ return False
167
+
168
+ try:
169
+ console.print(f"\n[red][bold]>>>Press any key to see jsonl!<<<\n[/bold][/red]")
170
+ input()
171
+ except KeyboardInterrupt:
172
+ console.print(f"\n[bold]Keyboard Interruptted[/bold]\n")
173
+ exit(0)
174
+
175
+ return condition_filter
176
+
177
+
178
+ # ============ 向后兼容的旧版解析(可选保留) ============
@@ -0,0 +1,82 @@
1
+ """Configuration parsing for seejx."""
2
+
3
+ from typing import Dict, Callable
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+ from .renderers.base import get_renderer, list_renderers, get_all_renderer_names
7
+
8
+ console = Console()
9
+
10
+
11
+ def parse_render_mappings(render_args) -> Dict[str, Callable]:
12
+ """解析渲染映射参数"""
13
+ field_mapper = {}
14
+ if not render_args:
15
+ return field_mapper
16
+
17
+ for mapping in render_args:
18
+ if "=" not in mapping:
19
+ console.print(f"[yellow]Warning: Invalid mapping format '{mapping}', expected 'renderer=field1,field2'[/yellow]")
20
+ continue
21
+
22
+ renderer_name, fields_str = mapping.split("=", 1)
23
+ renderer = get_renderer(renderer_name.strip())
24
+
25
+ if renderer is None:
26
+ console.print(f"[yellow]Warning: Unknown renderer '{renderer_name}'[/yellow]")
27
+
28
+ # 尝试给出建议
29
+ all_names = get_all_renderer_names()
30
+ suggestions = [name for name in all_names if renderer_name.lower() in name.lower()]
31
+ if suggestions:
32
+ console.print(f"[dim]Did you mean: {', '.join(suggestions[:3])}?[/dim]")
33
+ console.print(f"[dim]Use --list-renderers to see all available options[/dim]")
34
+ continue
35
+
36
+ for field in fields_str.split(","):
37
+ field = field.strip()
38
+ if field:
39
+ field_mapper[field] = renderer
40
+ console.print(f"[green]✓[/green] Mapped '{field}' → {renderer_name}")
41
+
42
+ return field_mapper
43
+
44
+
45
+ def print_available_renderers():
46
+ """打印所有可用的渲染器"""
47
+ renderers = list_renderers()
48
+
49
+ if not renderers:
50
+ console.print("[yellow]No renderers available[/yellow]")
51
+ return
52
+
53
+ table = Table(title="Available Renderers", show_header=True, header_style="bold magenta")
54
+ table.add_column("Renderer", style="cyan", no_wrap=True)
55
+ table.add_column("Source", style="yellow", width=12)
56
+ table.add_column("Aliases", style="dim")
57
+ table.add_column("Description", style="green")
58
+
59
+ # 按来源分组排序
60
+ builtin_renderers = []
61
+ plugin_renderers = []
62
+
63
+ for name, info in renderers.items():
64
+ if info.source == "builtin":
65
+ builtin_renderers.append((name, info))
66
+ else:
67
+ plugin_renderers.append((name, info))
68
+
69
+ # 先显示内置,再显示插件
70
+ for name, info in sorted(builtin_renderers) + sorted(plugin_renderers):
71
+ source = info.source
72
+ if info.plugin_name:
73
+ source = f"📦 {info.plugin_name}"
74
+
75
+ aliases = ", ".join(info.aliases) if info.aliases else "-"
76
+ desc = info.description or "-"
77
+
78
+ table.add_row(name, source, aliases, desc)
79
+
80
+ console.print(table)
81
+ console.print("\n[bold]Usage:[/bold] seejx data.jsonl -r [cyan]<renderer>[/cyan]=[green]<field1>[/green],[green]<field2>[/green]...")
82
+ console.print("[bold]Example:[/bold] seejx data.jsonl -r messages=chat,conversation")