dataframe-textual 0.1.0__py3-none-any.whl → 1.1.0__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.

Potentially problematic release.


This version of dataframe-textual might be problematic. Click here for more details.

@@ -1,204 +0,0 @@
1
- """Common utilities and constants for dataframe_viewer."""
2
-
3
- import re
4
- from dataclasses import dataclass
5
- from typing import Any
6
-
7
- import polars as pl
8
- from rich.text import Text
9
-
10
- # Boolean string mappings
11
- BOOLS = {
12
- "true": True,
13
- "t": True,
14
- "yes": True,
15
- "y": True,
16
- "1": True,
17
- "false": False,
18
- "f": False,
19
- "no": False,
20
- "n": False,
21
- "0": False,
22
- }
23
-
24
- # itype is used by Input widget for input validation
25
- # fmt: off
26
- STYLES = {
27
- "Int64": {"style": "cyan", "justify": "right", "itype": "integer", "convert": int},
28
- "Float64": {"style": "magenta", "justify": "right", "itype": "number", "convert": float},
29
- "String": {"style": "green", "justify": "left", "itype": "text", "convert": str},
30
- "Boolean": {"style": "blue", "justify": "center", "itype": "text", "convert": lambda x: BOOLS[x.lower()]},
31
- "Date": {"style": "blue", "justify": "center", "itype": "text", "convert": str},
32
- "Datetime": {"style": "blue", "justify": "center", "itype": "text", "convert": str},
33
- }
34
- # fmt: on
35
-
36
-
37
- @dataclass
38
- class DtypeConfig:
39
- style: str
40
- justify: str
41
- itype: str
42
- convert: Any
43
-
44
- def __init__(self, dtype: pl.DataType):
45
- dc = STYLES.get(
46
- str(dtype), {"style": "", "justify": "", "itype": "text", "convert": str}
47
- )
48
- self.style = dc["style"]
49
- self.justify = dc["justify"]
50
- self.itype = dc["itype"]
51
- self.convert = dc["convert"]
52
-
53
-
54
- # Subscript digits mapping for sort indicators
55
- SUBSCRIPT_DIGITS = {
56
- 0: "₀",
57
- 1: "₁",
58
- 2: "₂",
59
- 3: "₃",
60
- 4: "₄",
61
- 5: "₅",
62
- 6: "₆",
63
- 7: "₇",
64
- 8: "₈",
65
- 9: "₉",
66
- }
67
-
68
- # Cursor types ("none" removed)
69
- CURSOR_TYPES = ["row", "column", "cell"]
70
-
71
- # Pagination settings
72
- INITIAL_BATCH_SIZE = 100 # Load this many rows initially
73
- BATCH_SIZE = 50 # Load this many rows when scrolling
74
-
75
-
76
- def _format_row(vals, dtypes, apply_justify=True) -> list[Text]:
77
- """Format a single row with proper styling and justification.
78
-
79
- Args:
80
- vals: The list of values in the row.
81
- dtypes: The list of data types corresponding to each value.
82
- apply_justify: Whether to apply justification styling. Defaults to True.
83
- """
84
- formatted_row = []
85
-
86
- for val, dtype in zip(vals, dtypes, strict=True):
87
- dc = DtypeConfig(dtype)
88
-
89
- # Format the value
90
- if val is None:
91
- text_val = "-"
92
- elif str(dtype).startswith("Float"):
93
- text_val = f"{val:.4g}"
94
- else:
95
- text_val = str(val)
96
-
97
- formatted_row.append(
98
- Text(
99
- text_val,
100
- style=dc.style,
101
- justify=dc.justify if apply_justify else "",
102
- )
103
- )
104
-
105
- return formatted_row
106
-
107
-
108
- def _rindex(lst: list, value) -> int:
109
- """Return the last index of value in a list. Return -1 if not found."""
110
- for i, item in enumerate(reversed(lst)):
111
- if item == value:
112
- return len(lst) - 1 - i
113
- return -1
114
-
115
-
116
- def _next(lst: list[Any], current, offset=1) -> Any:
117
- """Return the next item in the list after the current item, cycling if needed."""
118
- if current not in lst:
119
- raise ValueError("Current item not in list")
120
- current_index = lst.index(current)
121
- next_index = (current_index + offset) % len(lst)
122
- return lst[next_index]
123
-
124
-
125
- def parse_filter_expression(
126
- expression: str, df: pl.DataFrame, current_col_idx: int
127
- ) -> str:
128
- """Parse and convert a filter expression to Polars syntax.
129
-
130
- Supports:
131
- - $_ - Current selected column
132
- - $1, $2, etc. - Column by 1-based index
133
- - $col_name - Column by name
134
- - Comparison operators: ==, !=, <, >, <=, >=
135
- - Logical operators: &&, ||
136
- - String literals: 'text', "text"
137
- - Numeric literals: integers and floats
138
-
139
- Examples:
140
- - "$_ > 50" -> "pl.col('current_col') > 50"
141
- - "$1 > 50" -> "pl.col('col0') > 50"
142
- - "$name == 'Alex'" -> "pl.col('name') == 'Alex'"
143
- - "$1 > 3 && $name == 'Alex'" -> "(pl.col('col0') > 3) & (pl.col('name') == 'Alex')"
144
- - "$age < $salary" -> "pl.col('age') < pl.col('salary')"
145
-
146
- Args:
147
- expression: The filter expression as a string.
148
- df: The DataFrame to validate column references.
149
- current_col_idx: The index of the currently selected column (0-based). Used for $_ reference.
150
-
151
- Returns:
152
- A Python expression string that can be eval'd with Polars symbols.
153
-
154
- Raises:
155
- ValueError: If the expression contains invalid column references.
156
- SyntaxError: If the expression has invalid syntax.
157
- """
158
- # Tokenize the expression
159
- # Pattern matches: $_, $index, $identifier, strings, operators, numbers, etc.
160
- token_pattern = r'\$_|\$\d+|\$\w+|\'[^\']*\'|"[^"]*"|&&|\|\||<=|>=|!=|==|[+\-*/%<>=()]|\d+\.?\d*|\w+|.'
161
-
162
- tokens = re.findall(token_pattern, expression)
163
-
164
- if not tokens:
165
- raise ValueError("Expression is empty")
166
-
167
- # Convert tokens to Polars expression syntax
168
- converted_tokens = []
169
- for token in tokens:
170
- if token.startswith("$"):
171
- # Column reference
172
- col_ref = token[1:]
173
-
174
- # Special case: $_ refers to the current selected column
175
- if col_ref == "_":
176
- col_name = df.columns[current_col_idx]
177
- # Check if it's a numeric index
178
- elif col_ref.isdigit():
179
- col_idx = int(col_ref) - 1 # Convert to 0-based index
180
- if col_idx < 0 or col_idx >= len(df.columns):
181
- raise ValueError(f"Column index out of range: ${col_ref}")
182
- col_name = df.columns[col_idx]
183
- else:
184
- # It's a column name
185
- if col_ref not in df.columns:
186
- raise ValueError(f"Column not found: ${col_ref}")
187
- col_name = col_ref
188
-
189
- converted_tokens.append(f"pl.col('{col_name}')")
190
-
191
- elif token in ("&&", "||"):
192
- # Convert logical operators and wrap surrounding expressions in parentheses
193
- if token == "&&":
194
- converted_tokens.append(") & (")
195
- else:
196
- converted_tokens.append(") | (")
197
-
198
- else:
199
- # Keep as-is (operators, numbers, strings, parentheses)
200
- converted_tokens.append(token)
201
-
202
- # Join tokens with space to ensure proper separation
203
- result = "(" + " ".join(converted_tokens) + ")"
204
- return result