pl2html 0.6.0__tar.gz → 0.6.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pl2html
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: Add your description here
5
5
  Requires-Dist: polars>=1.42.1
6
6
  Requires-Python: >=3.14
@@ -83,20 +83,22 @@ from pl2html import formats as fmt
83
83
 
84
84
  df = pl.DataFrame(
85
85
  {
86
- "asset_a": [5400600, 2100],
87
- "asset_b": [45100000, -850000],
88
- "growth": [0.1256, -0.024],
89
- "is_active": [True, False],
90
- "status_code": [0, 404]
86
+ 'asset_a': [5400600, 2100],
87
+ 'asset_b': [45100000, -850000],
88
+ 'growth': [0.1256, -0.024],
89
+ 'is_active': [True, False],
90
+ 'status_code': [0, 404],
91
91
  }
92
92
  )
93
93
 
94
94
  # Format multiple financial asset columns compactly in one pass using lists
95
95
  df_f = df.with_columns(
96
- fmt.fmt_integer(["asset_a", "asset_b"], compact=True, compact_system="financial"),
97
- fmt.fmt_percent("growth", decimals=1),
98
- fmt.fmt_tf("is_active", tf_style="check-mark"),
99
- fmt.sub_zero("status_code", zero_text="OK")
96
+ fmt.fmt_integer(
97
+ ['asset_a', 'asset_b'], compact=True, compact_system='financial'
98
+ ),
99
+ fmt.fmt_percent('growth', decimals=1),
100
+ fmt.fmt_tf('is_active', tf_style='check-mark'),
101
+ fmt.sub_zero('status_code', zero_text='OK'),
100
102
  )
101
103
 
102
104
  html = to_html(df_f)
@@ -125,7 +127,7 @@ styles.update(
125
127
  data_color(
126
128
  column='performance_score',
127
129
  palette=['#ff7675', '#fdcb6e', '#00b894'],
128
- auto_contrast=True
130
+ auto_contrast=True,
129
131
  )
130
132
  )
131
133
 
@@ -135,7 +137,7 @@ styles.update(
135
137
  column='utilization',
136
138
  palette=['#000000', '#FFFFFF'],
137
139
  descending=False,
138
- auto_contrast=True
140
+ auto_contrast=True,
139
141
  )
140
142
  )
141
143
 
@@ -74,20 +74,22 @@ from pl2html import formats as fmt
74
74
 
75
75
  df = pl.DataFrame(
76
76
  {
77
- "asset_a": [5400600, 2100],
78
- "asset_b": [45100000, -850000],
79
- "growth": [0.1256, -0.024],
80
- "is_active": [True, False],
81
- "status_code": [0, 404]
77
+ 'asset_a': [5400600, 2100],
78
+ 'asset_b': [45100000, -850000],
79
+ 'growth': [0.1256, -0.024],
80
+ 'is_active': [True, False],
81
+ 'status_code': [0, 404],
82
82
  }
83
83
  )
84
84
 
85
85
  # Format multiple financial asset columns compactly in one pass using lists
86
86
  df_f = df.with_columns(
87
- fmt.fmt_integer(["asset_a", "asset_b"], compact=True, compact_system="financial"),
88
- fmt.fmt_percent("growth", decimals=1),
89
- fmt.fmt_tf("is_active", tf_style="check-mark"),
90
- fmt.sub_zero("status_code", zero_text="OK")
87
+ fmt.fmt_integer(
88
+ ['asset_a', 'asset_b'], compact=True, compact_system='financial'
89
+ ),
90
+ fmt.fmt_percent('growth', decimals=1),
91
+ fmt.fmt_tf('is_active', tf_style='check-mark'),
92
+ fmt.sub_zero('status_code', zero_text='OK'),
91
93
  )
92
94
 
93
95
  html = to_html(df_f)
@@ -116,7 +118,7 @@ styles.update(
116
118
  data_color(
117
119
  column='performance_score',
118
120
  palette=['#ff7675', '#fdcb6e', '#00b894'],
119
- auto_contrast=True
121
+ auto_contrast=True,
120
122
  )
121
123
  )
122
124
 
@@ -126,7 +128,7 @@ styles.update(
126
128
  column='utilization',
127
129
  palette=['#000000', '#FFFFFF'],
128
130
  descending=False,
129
- auto_contrast=True
131
+ auto_contrast=True,
130
132
  )
131
133
  )
132
134
 
@@ -0,0 +1 @@
1
+ __version__ = '0.6.2'
@@ -1,27 +1,39 @@
1
1
  from html import escape as _escape
2
+ from warnings import catch_warnings as _catch_warnings
2
3
 
3
4
  from polars import (
4
5
  DataFrame as _DataFrame,
5
6
  DataType as _DataType,
6
7
  Expr as _Expr,
7
8
  LazyFrame as _LazyFrame,
9
+ Object as _Object,
8
10
  String as _String,
9
11
  col as _col,
10
12
  concat_str as _concat_str,
11
13
  lit as _lit,
12
14
  when as _when,
13
15
  )
16
+ from polars.exceptions import (
17
+ PolarsInefficientMapWarning as _PolarsInefficientMapWarning,
18
+ )
14
19
 
15
20
 
16
- def _escape_expr(expr: _Expr) -> _Expr:
21
+ def _escape_expr(expr: _Expr, is_object: bool) -> _Expr:
17
22
  """
18
23
  Escapes an expression's string representations for secure HTML compatibility.
19
- Using literal=True is critical; otherwise, the '&' character in the replacement
20
- string acts as a regex match expansion macro and corrupts the entities.
21
24
  """
25
+ if is_object:
26
+ with _catch_warnings(
27
+ action='ignore', category=_PolarsInefficientMapWarning
28
+ ):
29
+ expr = expr.map_elements(
30
+ str, return_dtype=_String, skip_nulls=True
31
+ )
32
+ else:
33
+ expr = expr.cast(_String)
34
+
22
35
  return (
23
- expr.cast(_String)
24
- .str.replace_all('&', '&', literal=True)
36
+ expr.str.replace_all('&', '&', literal=True)
25
37
  .str.replace_all('<', '&lt;', literal=True)
26
38
  .str.replace_all('>', '&gt;', literal=True)
27
39
  .str.replace_all('"', '&quot;', literal=True)
@@ -80,23 +92,20 @@ def _build_cell_expr(
80
92
  elif dtype.is_float():
81
93
  fmt_expr = _format_float(col_name)
82
94
  else:
83
- fmt_expr = _escape_expr(_col(col_name))
95
+ fmt_expr = _escape_expr(_col(col_name), dtype == _Object)
84
96
 
85
97
  fmt_expr = fmt_expr.fill_null('')
86
98
 
87
- attr_expr_list = []
88
- if col_name in attrs:
89
- for attr_name, val_expr in attrs[col_name].items():
90
- val_str_expr = val_expr.cast(_String)
91
-
92
- compiled_attr = (
93
- _when(val_str_expr.is_not_null() & (val_str_expr != ''))
94
- .then(_lit(f' {attr_name}="') + val_str_expr + _lit('"'))
95
- .otherwise(_lit(''))
99
+ if col_attrs := attrs.get(col_name):
100
+ attr_expr_list = [
101
+ _when(
102
+ (val_str_expr := val_expr.cast(_String)).is_not_null()
103
+ & (val_str_expr != '')
96
104
  )
97
- attr_expr_list.append(compiled_attr)
98
-
99
- if attr_expr_list:
105
+ .then(_lit(f' {attr_name}="') + val_str_expr + _lit('"'))
106
+ .otherwise(_lit(''))
107
+ for attr_name, val_expr in col_attrs.items()
108
+ ]
100
109
  opening_td = _lit('<td') + _concat_str(attr_expr_list) + _lit('>')
101
110
  else:
102
111
  opening_td = _lit('<td>')
@@ -134,7 +143,7 @@ def _resolve_attributes(
134
143
  if col_name in visible_columns:
135
144
  for attr_name, expr in attr_map.items():
136
145
  alias_key = f'__attr_{col_name}_{attr_name}'
137
- attr_exprs.append(_escape_expr(expr).alias(alias_key))
146
+ attr_exprs.append(_escape_expr(expr, False).alias(alias_key))
138
147
  attr_aliases[(col_name, attr_name)] = alias_key
139
148
 
140
149
  if not attr_exprs:
@@ -155,17 +164,6 @@ def _resolve_attributes(
155
164
  return lf, new_attrs
156
165
 
157
166
 
158
- def _apply_user_formatters(
159
- lf: _LazyFrame, formatters: _Expr | list[_Expr] | None
160
- ) -> _LazyFrame:
161
- """Applies layout transformation modifications/conversions to columns."""
162
- if formatters is None:
163
- return lf
164
- if isinstance(formatters, _Expr):
165
- formatters = [formatters]
166
- return lf.with_columns(formatters)
167
-
168
-
169
167
  def _compile_html(
170
168
  lf: _LazyFrame,
171
169
  visible_columns: list[str],
@@ -258,9 +256,7 @@ def to_html(
258
256
  """
259
257
  lf = df.lazy() if isinstance(df, _DataFrame) else df
260
258
  exclude_columns = exclude_columns or []
261
- attrs = attrs or {}
262
259
 
263
- # Setup the evaluation context data
264
260
  df_eager = lf.collect()
265
261
  lf = df_eager.lazy()
266
262
 
@@ -268,7 +264,12 @@ def to_html(
268
264
  c for c in df_eager.schema.names() if c not in exclude_columns
269
265
  ]
270
266
 
271
- lf, attrs = _resolve_attributes(lf, df_eager, attrs, visible_columns)
272
- lf = _apply_user_formatters(lf, formatters)
267
+ if attrs:
268
+ lf, attrs = _resolve_attributes(lf, df_eager, attrs, visible_columns)
269
+ else:
270
+ attrs = {}
271
+
272
+ if formatters is not None:
273
+ lf = lf.with_columns(formatters)
273
274
 
274
275
  return _compile_html(lf, visible_columns, attrs)
@@ -34,9 +34,24 @@ def _interpolate_segment(
34
34
  local_factor = (norm_val - seg_start) / segment_width
35
35
 
36
36
  # Linear interpolation formula applied directly to Polars Expressions
37
- r_expr = (_lit(r1) + (_lit(r2 - r1) * local_factor)).round(0).cast(_Int32)
38
- g_expr = (_lit(g1) + (_lit(g2 - g1) * local_factor)).round(0).cast(_Int32)
39
- b_expr = (_lit(b1) + (_lit(b2 - b1) * local_factor)).round(0).cast(_Int32)
37
+ r_expr = (
38
+ (_lit(r1) + (_lit(r2 - r1) * local_factor))
39
+ .fill_nan(None)
40
+ .round(0)
41
+ .cast(_Int32)
42
+ )
43
+ g_expr = (
44
+ (_lit(g1) + (_lit(g2 - g1) * local_factor))
45
+ .fill_nan(None)
46
+ .round(0)
47
+ .cast(_Int32)
48
+ )
49
+ b_expr = (
50
+ (_lit(b1) + (_lit(b2 - b1) * local_factor))
51
+ .fill_nan(None)
52
+ .round(0)
53
+ .cast(_Int32)
54
+ )
40
55
 
41
56
  return r_expr, g_expr, b_expr
42
57
 
@@ -4,7 +4,7 @@ build-backend = 'uv_build'
4
4
 
5
5
  [project]
6
6
  name = "pl2html"
7
- version = "0.6.0"
7
+ version = "0.6.2"
8
8
  description = "Add your description here"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -23,44 +23,46 @@ module-name = "pl2html"
23
23
  [tool.ruff]
24
24
  line-length = 79
25
25
  format.quote-style = 'single'
26
- lint.isort.combine-as-imports = true
27
- lint.extend-select = [
26
+
27
+ [tool.ruff.lint]
28
+ extend-select = [
28
29
  'W605', # invalid-escape-sequence
29
30
  'FA', # flake8-future-annotations
30
31
  'I', # isort
31
32
  'UP', # pyupgrade
32
33
  'RUF', # Ruff-specific rules (RUF)
33
34
  ]
34
- lint.ignore = [
35
- 'E721', # Do not compare types, use `isinstance()`
35
+ ignore = [
36
+ 'B023', # function-uses-loop-variable
37
+ 'BLE001', # blind-except
38
+ 'DTZ', # flake8-datetimez
39
+ 'E721', # Do not compare types, use `isinstance()`
40
+ 'PLE2502', # bidirectional-unicode
41
+ 'PLW1510', # subprocess-run-without-check
36
42
  'RUF001', # ambiguous-unicode-character-string
37
43
  'RUF002', # ambiguous-unicode-character-docstring
38
44
  'RUF003', # ambiguous-unicode-character-comment
39
45
  'RUF012', # mutable-class-default
40
46
  'RUF059', # Unpacked variable never used
47
+ 'SIM102', # collapsible-if
48
+ 'SIM103', # Return the condition directly
49
+ 'SIM115', # Use a context manager for opening files
41
50
  ]
42
51
 
43
- [tool.pytest]
44
- addopts = ["--quiet", "--tb=short"]
45
- truncation_limit_lines = '0'
46
- truncation_limit_chars = '0'
52
+ [tool.ruff.lint.isort]
53
+ combine-as-imports = true
47
54
 
48
- [tool.pyright]
49
- typeCheckingMode = 'standard'
50
- reportInvalidStringEscapeSequence = false
51
- reportConstantRedefinition = 'error'
52
- reportDeprecated = 'warning'
53
- reportPropertyTypeMismatch = 'error'
54
- reportTypeCommentUsage = 'warning'
55
- reportUnnecessaryCast = 'warning'
56
- reportUnnecessaryComparison = 'warning'
57
- reportUnnecessaryContains = 'warning'
58
- reportUnnecessaryIsInstance = 'warning'
59
- reportUnnecessaryTypeIgnoreComment = 'warning'
60
- venvPath = "."
61
- venv = ".venv"
55
+ [tool.pytest]
56
+ addopts = ["--quiet", "--tb=short", "--report-chars=N"]
57
+ truncation_limit_lines = 0
58
+ truncation_limit_chars = 0
62
59
 
60
+ [tool.pyrefly]
61
+ infer-with-first-use = false
62
+ min-severity = "info"
63
+ [tool.pyrefly.errors]
64
+ incompatible-comparison = "warn"
63
65
  [dependency-groups]
64
66
  dev = [
65
- "pytest>=9.1.1",
67
+ "pytest>=9.2.0",
66
68
  ]
@@ -1 +0,0 @@
1
- __version__ = '0.6.0'
File without changes