Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pola-rs
GitHub Repository: pola-rs/polars
Path: blob/main/py-polars/tests/unit/test_config.py
6939 views
1
from __future__ import annotations
2
3
import os
4
from pathlib import Path
5
from textwrap import dedent
6
from typing import TYPE_CHECKING, Any
7
8
import pytest
9
10
import polars as pl
11
import polars._plr as plr
12
from polars._utils.unstable import issue_unstable_warning
13
from polars.config import _POLARS_CFG_ENV_VARS
14
15
if TYPE_CHECKING:
16
from collections.abc import Iterator
17
18
19
@pytest.fixture(autouse=True)
20
def _environ() -> Iterator[None]:
21
"""Fixture to restore the environment after/during tests."""
22
with pl.Config(restore_defaults=True):
23
yield
24
25
26
def test_ascii_tables() -> None:
27
df = pl.DataFrame(
28
{
29
"a": [1, 2],
30
"b": [4, 5],
31
"c": [[list(range(1, 26))], [list(range(1, 76))]],
32
}
33
)
34
35
ascii_table_repr = (
36
"shape: (2, 3)\n"
37
"+-----+-----+------------------+\n"
38
"| a | b | c |\n"
39
"| --- | --- | --- |\n"
40
"| i64 | i64 | list[list[i64]] |\n"
41
"+==============================+\n"
42
"| 1 | 4 | [[1, 2, ... 25]] |\n"
43
"| 2 | 5 | [[1, 2, ... 75]] |\n"
44
"+-----+-----+------------------+"
45
)
46
# note: expect to render ascii only within the given scope
47
with pl.Config(set_ascii_tables=True):
48
assert repr(df) == ascii_table_repr
49
50
# confirm back to utf8 default after scope-exit
51
assert (
52
repr(df) == "shape: (2, 3)\n"
53
"┌─────┬─────┬─────────────────┐\n"
54
"│ a ┆ b ┆ c │\n"
55
"│ --- ┆ --- ┆ --- │\n"
56
"│ i64 ┆ i64 ┆ list[list[i64]] │\n"
57
"╞═════╪═════╪═════════════════╡\n"
58
"│ 1 ┆ 4 ┆ [[1, 2, … 25]] │\n"
59
"│ 2 ┆ 5 ┆ [[1, 2, … 75]] │\n"
60
"└─────┴─────┴─────────────────┘"
61
)
62
63
@pl.Config(set_ascii_tables=True)
64
def ascii_table() -> str:
65
return repr(df)
66
67
assert ascii_table() == ascii_table_repr
68
69
70
def test_hide_header_elements() -> None:
71
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
72
73
pl.Config.set_tbl_hide_column_data_types(True)
74
assert (
75
str(df) == "shape: (3, 3)\n"
76
"┌───┬───┬───┐\n"
77
"│ a ┆ b ┆ c │\n"
78
"╞═══╪═══╪═══╡\n"
79
"│ 1 ┆ 4 ┆ 7 │\n"
80
"│ 2 ┆ 5 ┆ 8 │\n"
81
"│ 3 ┆ 6 ┆ 9 │\n"
82
"└───┴───┴───┘"
83
)
84
85
pl.Config.set_tbl_hide_column_data_types(False).set_tbl_hide_column_names(True)
86
assert (
87
str(df) == "shape: (3, 3)\n"
88
"┌─────┬─────┬─────┐\n"
89
"│ i64 ┆ i64 ┆ i64 │\n"
90
"╞═════╪═════╪═════╡\n"
91
"│ 1 ┆ 4 ┆ 7 │\n"
92
"│ 2 ┆ 5 ┆ 8 │\n"
93
"│ 3 ┆ 6 ┆ 9 │\n"
94
"└─────┴─────┴─────┘"
95
)
96
97
98
def test_set_tbl_cols() -> None:
99
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
100
101
pl.Config.set_tbl_cols(1)
102
assert str(df).split("\n")[2] == "│ a ┆ … │"
103
pl.Config.set_tbl_cols(2)
104
assert str(df).split("\n")[2] == "│ a ┆ … ┆ c │"
105
pl.Config.set_tbl_cols(3)
106
assert str(df).split("\n")[2] == "│ a ┆ b ┆ c │"
107
108
df = pl.DataFrame(
109
{"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}
110
)
111
pl.Config.set_tbl_cols(2)
112
assert str(df).split("\n")[2] == "│ a ┆ … ┆ d │"
113
pl.Config.set_tbl_cols(3)
114
assert str(df).split("\n")[2] == "│ a ┆ b ┆ … ┆ d │"
115
pl.Config.set_tbl_cols(-1)
116
assert str(df).split("\n")[2] == "│ a ┆ b ┆ c ┆ d │"
117
118
119
def test_set_tbl_rows() -> None:
120
df = pl.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "c": [9, 10, 11, 12]})
121
ser = pl.Series("ser", [1, 2, 3, 4, 5])
122
123
pl.Config.set_tbl_rows(0)
124
assert (
125
str(df) == "shape: (4, 3)\n"
126
"┌─────┬─────┬─────┐\n"
127
"│ a ┆ b ┆ c │\n"
128
"│ --- ┆ --- ┆ --- │\n"
129
"│ i64 ┆ i64 ┆ i64 │\n"
130
"╞═════╪═════╪═════╡\n"
131
"│ … ┆ … ┆ … │\n"
132
"└─────┴─────┴─────┘"
133
)
134
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t…\n]"
135
136
pl.Config.set_tbl_rows(1)
137
assert (
138
str(df) == "shape: (4, 3)\n"
139
"┌─────┬─────┬─────┐\n"
140
"│ a ┆ b ┆ c │\n"
141
"│ --- ┆ --- ┆ --- │\n"
142
"│ i64 ┆ i64 ┆ i64 │\n"
143
"╞═════╪═════╪═════╡\n"
144
"│ 1 ┆ 5 ┆ 9 │\n"
145
"│ … ┆ … ┆ … │\n"
146
"└─────┴─────┴─────┘"
147
)
148
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t…\n]"
149
150
pl.Config.set_tbl_rows(2)
151
assert (
152
str(df) == "shape: (4, 3)\n"
153
"┌─────┬─────┬─────┐\n"
154
"│ a ┆ b ┆ c │\n"
155
"│ --- ┆ --- ┆ --- │\n"
156
"│ i64 ┆ i64 ┆ i64 │\n"
157
"╞═════╪═════╪═════╡\n"
158
"│ 1 ┆ 5 ┆ 9 │\n"
159
"│ … ┆ … ┆ … │\n"
160
"│ 4 ┆ 8 ┆ 12 │\n"
161
"└─────┴─────┴─────┘"
162
)
163
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t…\n\t5\n]"
164
165
pl.Config.set_tbl_rows(3)
166
assert (
167
str(df) == "shape: (4, 3)\n"
168
"┌─────┬─────┬─────┐\n"
169
"│ a ┆ b ┆ c │\n"
170
"│ --- ┆ --- ┆ --- │\n"
171
"│ i64 ┆ i64 ┆ i64 │\n"
172
"╞═════╪═════╪═════╡\n"
173
"│ 1 ┆ 5 ┆ 9 │\n"
174
"│ 2 ┆ 6 ┆ 10 │\n"
175
"│ … ┆ … ┆ … │\n"
176
"│ 4 ┆ 8 ┆ 12 │\n"
177
"└─────┴─────┴─────┘"
178
)
179
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t…\n\t5\n]"
180
181
pl.Config.set_tbl_rows(4)
182
assert (
183
str(df) == "shape: (4, 3)\n"
184
"┌─────┬─────┬─────┐\n"
185
"│ a ┆ b ┆ c │\n"
186
"│ --- ┆ --- ┆ --- │\n"
187
"│ i64 ┆ i64 ┆ i64 │\n"
188
"╞═════╪═════╪═════╡\n"
189
"│ 1 ┆ 5 ┆ 9 │\n"
190
"│ 2 ┆ 6 ┆ 10 │\n"
191
"│ 3 ┆ 7 ┆ 11 │\n"
192
"│ 4 ┆ 8 ┆ 12 │\n"
193
"└─────┴─────┴─────┘"
194
)
195
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t…\n\t4\n\t5\n]"
196
197
df = pl.DataFrame(
198
{
199
"a": [1, 2, 3, 4, 5],
200
"b": [6, 7, 8, 9, 10],
201
"c": [11, 12, 13, 14, 15],
202
}
203
)
204
205
pl.Config.set_tbl_rows(3)
206
assert (
207
str(df) == "shape: (5, 3)\n"
208
"┌─────┬─────┬─────┐\n"
209
"│ a ┆ b ┆ c │\n"
210
"│ --- ┆ --- ┆ --- │\n"
211
"│ i64 ┆ i64 ┆ i64 │\n"
212
"╞═════╪═════╪═════╡\n"
213
"│ 1 ┆ 6 ┆ 11 │\n"
214
"│ 2 ┆ 7 ┆ 12 │\n"
215
"│ … ┆ … ┆ … │\n"
216
"│ 5 ┆ 10 ┆ 15 │\n"
217
"└─────┴─────┴─────┘"
218
)
219
220
pl.Config.set_tbl_rows(-1)
221
assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t3\n\t4\n\t5\n]"
222
223
pl.Config.set_tbl_hide_dtype_separator(True)
224
assert (
225
str(df) == "shape: (5, 3)\n"
226
"┌─────┬─────┬─────┐\n"
227
"│ a ┆ b ┆ c │\n"
228
"│ i64 ┆ i64 ┆ i64 │\n"
229
"╞═════╪═════╪═════╡\n"
230
"│ 1 ┆ 6 ┆ 11 │\n"
231
"│ 2 ┆ 7 ┆ 12 │\n"
232
"│ 3 ┆ 8 ┆ 13 │\n"
233
"│ 4 ┆ 9 ┆ 14 │\n"
234
"│ 5 ┆ 10 ┆ 15 │\n"
235
"└─────┴─────┴─────┘"
236
)
237
238
239
def test_set_tbl_formats() -> None:
240
df = pl.DataFrame(
241
{
242
"foo": [1, 2, 3],
243
"bar": [6.0, 7.0, 8.0],
244
"ham": ["a", "b", "c"],
245
}
246
)
247
pl.Config().set_tbl_formatting("ASCII_MARKDOWN")
248
assert str(df) == (
249
"shape: (3, 3)\n"
250
"| foo | bar | ham |\n"
251
"| --- | --- | --- |\n"
252
"| i64 | f64 | str |\n"
253
"|-----|-----|-----|\n"
254
"| 1 | 6.0 | a |\n"
255
"| 2 | 7.0 | b |\n"
256
"| 3 | 8.0 | c |"
257
)
258
259
pl.Config().set_tbl_formatting("ASCII_BORDERS_ONLY_CONDENSED")
260
with pl.Config(tbl_hide_dtype_separator=True):
261
assert str(df) == (
262
"shape: (3, 3)\n"
263
"+-----------------+\n"
264
"| foo bar ham |\n"
265
"| i64 f64 str |\n"
266
"+=================+\n"
267
"| 1 6.0 a |\n"
268
"| 2 7.0 b |\n"
269
"| 3 8.0 c |\n"
270
"+-----------------+"
271
)
272
273
# temporarily scope "nothing" style, with no data types
274
with pl.Config(
275
tbl_formatting="NOTHING",
276
tbl_hide_column_data_types=True,
277
):
278
assert str(df) == (
279
"shape: (3, 3)\n"
280
" foo bar ham \n"
281
" 1 6.0 a \n"
282
" 2 7.0 b \n"
283
" 3 8.0 c "
284
)
285
286
# after scope, expect previous style
287
assert str(df) == (
288
"shape: (3, 3)\n"
289
"+-----------------+\n"
290
"| foo bar ham |\n"
291
"| --- --- --- |\n"
292
"| i64 f64 str |\n"
293
"+=================+\n"
294
"| 1 6.0 a |\n"
295
"| 2 7.0 b |\n"
296
"| 3 8.0 c |\n"
297
"+-----------------+"
298
)
299
300
# invalid style
301
with pytest.raises(ValueError, match="invalid table format name: 'NOPE'"):
302
pl.Config().set_tbl_formatting("NOPE") # type: ignore[arg-type]
303
304
305
def test_set_tbl_width_chars() -> None:
306
df = pl.DataFrame(
307
{
308
"a really long col": [1, 2, 3],
309
"b": ["", "this is a string value that will be truncated", None],
310
"this is 10": [4, 5, 6],
311
}
312
)
313
assert max(len(line) for line in str(df).split("\n")) == 68
314
315
pl.Config.set_tbl_width_chars(60)
316
assert max(len(line) for line in str(df).split("\n")) == 60
317
318
# force minimal table size (will hard-wrap everything; "don't try this at home" :p)
319
pl.Config.set_tbl_width_chars(0)
320
assert max(len(line) for line in str(df).split("\n")) == 19
321
322
# this check helps to check that column width bucketing
323
# is exact; no extraneous character allocation
324
df = pl.DataFrame(
325
{
326
"A": [1, 2, 3, 4, 5],
327
"fruits": ["banana", "banana", "apple", "apple", "banana"],
328
"B": [5, 4, 3, 2, 1],
329
"cars": ["beetle", "audi", "beetle", "beetle", "beetle"],
330
},
331
schema_overrides={"A": pl.Int64, "B": pl.Int64},
332
).select(pl.all(), pl.all().name.suffix("_suffix!"))
333
334
with pl.Config(tbl_width_chars=87):
335
assert max(len(line) for line in str(df).split("\n")) == 87
336
337
# check that -1 is interpreted as no limit
338
df = pl.DataFrame({str(i): ["a" * 25] for i in range(5)})
339
for tbl_width_chars, expected_width in [
340
(None, 100),
341
(-1, 141),
342
]:
343
with pl.Config(tbl_width_chars=tbl_width_chars):
344
assert max(len(line) for line in str(df).split("\n")) == expected_width
345
346
347
def test_shape_below_table_and_inlined_dtype() -> None:
348
df = pl.DataFrame({"a": [1, 2], "b": [3, 4], "c": [5, 6]})
349
350
pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(True)
351
pl.Config.set_tbl_formatting("UTF8_FULL", rounded_corners=True)
352
assert (
353
str(df) == ""
354
"╭─────────┬─────────┬─────────╮\n"
355
"│ a (i64) ┆ b (i64) ┆ c (i64) │\n"
356
"╞═════════╪═════════╪═════════╡\n"
357
"│ 1 ┆ 3 ┆ 5 │\n"
358
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
359
"│ 2 ┆ 4 ┆ 6 │\n"
360
"╰─────────┴─────────┴─────────╯\n"
361
"shape: (2, 3)"
362
)
363
364
pl.Config.set_tbl_dataframe_shape_below(False)
365
assert (
366
str(df) == "shape: (2, 3)\n"
367
"╭─────────┬─────────┬─────────╮\n"
368
"│ a (i64) ┆ b (i64) ┆ c (i64) │\n"
369
"╞═════════╪═════════╪═════════╡\n"
370
"│ 1 ┆ 3 ┆ 5 │\n"
371
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
372
"│ 2 ┆ 4 ┆ 6 │\n"
373
"╰─────────┴─────────┴─────────╯"
374
)
375
(
376
pl.Config.set_tbl_formatting(None, rounded_corners=False)
377
.set_tbl_column_data_type_inline(False)
378
.set_tbl_cell_alignment("RIGHT")
379
)
380
assert (
381
str(df) == "shape: (2, 3)\n"
382
"┌─────┬─────┬─────┐\n"
383
"│ a ┆ b ┆ c │\n"
384
"│ --- ┆ --- ┆ --- │\n"
385
"│ i64 ┆ i64 ┆ i64 │\n"
386
"╞═════╪═════╪═════╡\n"
387
"│ 1 ┆ 3 ┆ 5 │\n"
388
"│ 2 ┆ 4 ┆ 6 │\n"
389
"└─────┴─────┴─────┘"
390
)
391
with pytest.raises(ValueError):
392
pl.Config.set_tbl_cell_alignment("INVALID") # type: ignore[arg-type]
393
394
395
def test_shape_format_for_big_numbers() -> None:
396
df = pl.DataFrame({"a": range(1, 1001), "b": range(1001, 1001 + 1000)})
397
398
pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(True)
399
pl.Config.set_tbl_formatting("UTF8_FULL", rounded_corners=True)
400
assert (
401
str(df) == ""
402
"╭─────────┬─────────╮\n"
403
"│ a (i64) ┆ b (i64) │\n"
404
"╞═════════╪═════════╡\n"
405
"│ 1 ┆ 1001 │\n"
406
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
407
"│ 2 ┆ 1002 │\n"
408
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
409
"│ 3 ┆ 1003 │\n"
410
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
411
"│ 4 ┆ 1004 │\n"
412
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
413
"│ 5 ┆ 1005 │\n"
414
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
415
"│ … ┆ … │\n"
416
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
417
"│ 996 ┆ 1996 │\n"
418
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
419
"│ 997 ┆ 1997 │\n"
420
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
421
"│ 998 ┆ 1998 │\n"
422
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
423
"│ 999 ┆ 1999 │\n"
424
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
425
"│ 1000 ┆ 2000 │\n"
426
"╰─────────┴─────────╯\n"
427
"shape: (1_000, 2)"
428
)
429
430
pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(False)
431
assert (
432
str(df) == "shape: (1_000, 2)\n"
433
"╭─────────┬─────────╮\n"
434
"│ a (i64) ┆ b (i64) │\n"
435
"╞═════════╪═════════╡\n"
436
"│ 1 ┆ 1001 │\n"
437
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
438
"│ 2 ┆ 1002 │\n"
439
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
440
"│ 3 ┆ 1003 │\n"
441
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
442
"│ 4 ┆ 1004 │\n"
443
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
444
"│ 5 ┆ 1005 │\n"
445
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
446
"│ … ┆ … │\n"
447
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
448
"│ 996 ┆ 1996 │\n"
449
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
450
"│ 997 ┆ 1997 │\n"
451
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
452
"│ 998 ┆ 1998 │\n"
453
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
454
"│ 999 ┆ 1999 │\n"
455
"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"
456
"│ 1000 ┆ 2000 │\n"
457
"╰─────────┴─────────╯"
458
)
459
460
pl.Config.set_tbl_rows(0)
461
ser = pl.Series("ser", range(1000))
462
assert str(ser) == "shape: (1_000,)\nSeries: 'ser' [i64]\n[\n\t…\n]"
463
464
pl.Config.set_tbl_rows(1)
465
pl.Config.set_tbl_cols(1)
466
df = pl.DataFrame({str(col_num): 1 for col_num in range(1000)})
467
468
assert (
469
str(df) == "shape: (1, 1_000)\n"
470
"╭─────────┬───╮\n"
471
"│ 0 (i64) ┆ … │\n"
472
"╞═════════╪═══╡\n"
473
"│ 1 ┆ … │\n"
474
"╰─────────┴───╯"
475
)
476
477
pl.Config.set_tbl_formatting("ASCII_FULL_CONDENSED")
478
assert (
479
str(df) == "shape: (1, 1_000)\n"
480
"+---------+-----+\n"
481
"| 0 (i64) | ... |\n"
482
"+===============+\n"
483
"| 1 | ... |\n"
484
"+---------+-----+"
485
)
486
487
488
def test_numeric_right_alignment() -> None:
489
pl.Config.set_tbl_cell_numeric_alignment("RIGHT")
490
491
df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})
492
assert (
493
str(df) == "shape: (3, 3)\n"
494
"┌─────┬─────┬─────┐\n"
495
"│ a ┆ b ┆ c │\n"
496
"│ --- ┆ --- ┆ --- │\n"
497
"│ i64 ┆ i64 ┆ i64 │\n"
498
"╞═════╪═════╪═════╡\n"
499
"│ 1 ┆ 4 ┆ 7 │\n"
500
"│ 2 ┆ 5 ┆ 8 │\n"
501
"│ 3 ┆ 6 ┆ 9 │\n"
502
"└─────┴─────┴─────┘"
503
)
504
505
df = pl.DataFrame(
506
{"a": [1.1, 2.22, 3.333], "b": [4.0, 5.0, 6.0], "c": [7.0, 8.0, 9.0]}
507
)
508
with pl.Config():
509
pl.Config.set_fmt_float("full")
510
assert (
511
str(df) == "shape: (3, 3)\n"
512
"┌───────┬─────┬─────┐\n"
513
"│ a ┆ b ┆ c │\n"
514
"│ --- ┆ --- ┆ --- │\n"
515
"│ f64 ┆ f64 ┆ f64 │\n"
516
"╞═══════╪═════╪═════╡\n"
517
"│ 1.1 ┆ 4 ┆ 7 │\n"
518
"│ 2.22 ┆ 5 ┆ 8 │\n"
519
"│ 3.333 ┆ 6 ┆ 9 │\n"
520
"└───────┴─────┴─────┘"
521
)
522
523
with pl.Config(fmt_float="mixed"):
524
assert (
525
str(df) == "shape: (3, 3)\n"
526
"┌───────┬─────┬─────┐\n"
527
"│ a ┆ b ┆ c │\n"
528
"│ --- ┆ --- ┆ --- │\n"
529
"│ f64 ┆ f64 ┆ f64 │\n"
530
"╞═══════╪═════╪═════╡\n"
531
"│ 1.1 ┆ 4.0 ┆ 7.0 │\n"
532
"│ 2.22 ┆ 5.0 ┆ 8.0 │\n"
533
"│ 3.333 ┆ 6.0 ┆ 9.0 │\n"
534
"└───────┴─────┴─────┘"
535
)
536
537
with pl.Config(float_precision=6):
538
assert str(df) == (
539
"shape: (3, 3)\n"
540
"┌──────────┬──────────┬──────────┐\n"
541
"│ a ┆ b ┆ c │\n"
542
"│ --- ┆ --- ┆ --- │\n"
543
"│ f64 ┆ f64 ┆ f64 │\n"
544
"╞══════════╪══════════╪══════════╡\n"
545
"│ 1.100000 ┆ 4.000000 ┆ 7.000000 │\n"
546
"│ 2.220000 ┆ 5.000000 ┆ 8.000000 │\n"
547
"│ 3.333000 ┆ 6.000000 ┆ 9.000000 │\n"
548
"└──────────┴──────────┴──────────┘"
549
)
550
with pl.Config(float_precision=None):
551
assert (
552
str(df) == "shape: (3, 3)\n"
553
"┌───────┬─────┬─────┐\n"
554
"│ a ┆ b ┆ c │\n"
555
"│ --- ┆ --- ┆ --- │\n"
556
"│ f64 ┆ f64 ┆ f64 │\n"
557
"╞═══════╪═════╪═════╡\n"
558
"│ 1.1 ┆ 4.0 ┆ 7.0 │\n"
559
"│ 2.22 ┆ 5.0 ┆ 8.0 │\n"
560
"│ 3.333 ┆ 6.0 ┆ 9.0 │\n"
561
"└───────┴─────┴─────┘"
562
)
563
564
df = pl.DataFrame(
565
{"a": [1.1, 22.2, 3.33], "b": [444.0, 55.5, 6.6], "c": [77.7, 8888.0, 9.9999]}
566
)
567
with pl.Config(fmt_float="full", float_precision=1):
568
assert (
569
str(df) == "shape: (3, 3)\n"
570
"┌──────┬───────┬────────┐\n"
571
"│ a ┆ b ┆ c │\n"
572
"│ --- ┆ --- ┆ --- │\n"
573
"│ f64 ┆ f64 ┆ f64 │\n"
574
"╞══════╪═══════╪════════╡\n"
575
"│ 1.1 ┆ 444.0 ┆ 77.7 │\n"
576
"│ 22.2 ┆ 55.5 ┆ 8888.0 │\n"
577
"│ 3.3 ┆ 6.6 ┆ 10.0 │\n"
578
"└──────┴───────┴────────┘"
579
)
580
581
df = pl.DataFrame(
582
{
583
"a": [1100000000000000000.1, 22200000000000000.2, 33330000000000000.33333],
584
"b": [40000000000000000000.0, 5, 600000000000000000.0],
585
"c": [700000.0, 80000000000000000.0, 900],
586
}
587
)
588
with pl.Config(float_precision=2):
589
assert (
590
str(df) == "shape: (3, 3)\n"
591
"┌─────────┬─────────┬───────────┐\n"
592
"│ a ┆ b ┆ c │\n"
593
"│ --- ┆ --- ┆ --- │\n"
594
"│ f64 ┆ f64 ┆ f64 │\n"
595
"╞═════════╪═════════╪═══════════╡\n"
596
"│ 1.10e18 ┆ 4.00e19 ┆ 700000.00 │\n"
597
"│ 2.22e16 ┆ 5.00 ┆ 8.00e16 │\n"
598
"│ 3.33e16 ┆ 6.00e17 ┆ 900.00 │\n"
599
"└─────────┴─────────┴───────────┘"
600
)
601
602
603
@pytest.mark.write_disk
604
def test_config_load_save(tmp_path: Path) -> None:
605
for file in (
606
None,
607
tmp_path / "polars.config",
608
str(tmp_path / "polars.config"),
609
):
610
# set some config options...
611
pl.Config.set_tbl_cols(12)
612
pl.Config.set_verbose(True)
613
pl.Config.set_fmt_float("full")
614
pl.Config.set_float_precision(6)
615
pl.Config.set_thousands_separator(",")
616
assert os.environ.get("POLARS_VERBOSE") == "1"
617
618
if file is None:
619
cfg = pl.Config.save()
620
assert isinstance(cfg, str)
621
else:
622
assert pl.Config.save_to_file(file) is None
623
624
assert "POLARS_VERBOSE" in pl.Config.state(if_set=True)
625
626
# ...modify the same options...
627
pl.Config.set_tbl_cols(10)
628
pl.Config.set_verbose(False)
629
pl.Config.set_fmt_float("mixed")
630
pl.Config.set_float_precision(2)
631
pl.Config.set_thousands_separator(None)
632
assert os.environ.get("POLARS_VERBOSE") == "0"
633
634
# ...load back from config file/string...
635
if file is None:
636
pl.Config.load(cfg)
637
else:
638
with pytest.raises(ValueError, match="invalid Config file"):
639
pl.Config.load_from_file(cfg)
640
641
if isinstance(file, Path):
642
with pytest.raises(TypeError, match="the JSON object must be str"):
643
pl.Config.load(file) # type: ignore[arg-type]
644
else:
645
with pytest.raises(ValueError, match="invalid Config string"):
646
pl.Config.load(file)
647
648
pl.Config.load_from_file(file)
649
650
# ...and confirm the saved options were set.
651
assert os.environ.get("POLARS_FMT_MAX_COLS") == "12"
652
assert os.environ.get("POLARS_VERBOSE") == "1"
653
assert plr.get_float_fmt() == "full"
654
assert plr.get_float_precision() == 6
655
656
# restore all default options (unsets from env)
657
pl.Config.restore_defaults()
658
for e in ("POLARS_FMT_MAX_COLS", "POLARS_VERBOSE"):
659
assert e not in pl.Config.state(if_set=True)
660
assert e in pl.Config.state()
661
662
assert os.environ.get("POLARS_FMT_MAX_COLS") is None
663
assert os.environ.get("POLARS_VERBOSE") is None
664
assert plr.get_float_fmt() == "mixed"
665
assert plr.get_float_precision() is None
666
667
# ref: #11094
668
with pl.Config(
669
streaming_chunk_size=100,
670
tbl_cols=2000,
671
tbl_formatting="UTF8_NO_BORDERS",
672
tbl_hide_column_data_types=True,
673
tbl_hide_dtype_separator=True,
674
tbl_rows=2000,
675
tbl_width_chars=2000,
676
verbose=True,
677
):
678
assert isinstance(repr(pl.DataFrame({"xyz": [0]})), str)
679
680
681
def test_config_load_save_context() -> None:
682
# store the default configuration state
683
default_state = pl.Config.save()
684
685
# establish some non-default settings
686
pl.Config.set_tbl_formatting("ASCII_MARKDOWN")
687
pl.Config.set_verbose(True)
688
689
# load the default config, validate load & context manager behaviour
690
with pl.Config.load(default_state):
691
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None
692
assert os.environ.get("POLARS_VERBOSE") is None
693
694
# ensure earlier state was restored
695
assert os.environ["POLARS_FMT_TABLE_FORMATTING"] == "ASCII_MARKDOWN"
696
assert os.environ["POLARS_VERBOSE"]
697
698
699
def test_config_instances() -> None:
700
# establish two config instances that defer setting their options
701
cfg_markdown = pl.Config(
702
tbl_formatting="MARKDOWN",
703
apply_on_context_enter=True,
704
)
705
cfg_compact = pl.Config(
706
tbl_rows=4,
707
tbl_cols=4,
708
tbl_column_data_type_inline=True,
709
apply_on_context_enter=True,
710
)
711
712
# check instance (in)equality
713
assert cfg_markdown != cfg_compact
714
assert cfg_markdown == pl.Config(
715
tbl_formatting="MARKDOWN", apply_on_context_enter=True
716
)
717
718
# confirm that the options have not been applied yet
719
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None
720
721
# confirm that the deferred options are applied when the instance context
722
# is entered into, and that they can be re-used without leaking state
723
@cfg_markdown
724
def fn1() -> str | None:
725
return os.environ.get("POLARS_FMT_TABLE_FORMATTING")
726
727
assert fn1() == "MARKDOWN"
728
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None
729
730
with cfg_markdown: # can re-use instance as decorator and context
731
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") == "MARKDOWN"
732
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None
733
734
@cfg_markdown
735
def fn2() -> str | None:
736
return os.environ.get("POLARS_FMT_TABLE_FORMATTING")
737
738
assert fn2() == "MARKDOWN"
739
assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None
740
741
df = pl.DataFrame({f"c{idx}": [idx] * 10 for idx in range(10)})
742
743
@cfg_compact
744
def fn3(df: pl.DataFrame) -> str:
745
return repr(df)
746
747
# reuse config instance and confirm state does not leak between invocations
748
for _ in range(3):
749
assert (
750
fn3(df)
751
== dedent("""
752
shape: (10, 10)
753
┌──────────┬──────────┬───┬──────────┬──────────┐
754
│ c0 (i64) ┆ c1 (i64) ┆ … ┆ c8 (i64) ┆ c9 (i64) │
755
╞══════════╪══════════╪═══╪══════════╪══════════╡
756
│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │
757
│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │
758
│ … ┆ … ┆ … ┆ … ┆ … │
759
│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │
760
│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │
761
└──────────┴──────────┴───┴──────────┴──────────┘""").lstrip()
762
)
763
764
assert (
765
repr(df)
766
== dedent("""
767
shape: (10, 10)
768
┌─────┬─────┬─────┬─────┬───┬─────┬─────┬─────┬─────┐
769
│ c0 ┆ c1 ┆ c2 ┆ c3 ┆ … ┆ c6 ┆ c7 ┆ c8 ┆ c9 │
770
│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │
771
│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ ┆ i64 ┆ i64 ┆ i64 ┆ i64 │
772
╞═════╪═════╪═════╪═════╪═══╪═════╪═════╪═════╪═════╡
773
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
774
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
775
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
776
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
777
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
778
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
779
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
780
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
781
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
782
│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │
783
└─────┴─────┴─────┴─────┴───┴─────┴─────┴─────┴─────┘""").lstrip()
784
)
785
786
787
def test_config_scope() -> None:
788
pl.Config.set_verbose(False)
789
pl.Config.set_tbl_cols(8)
790
791
initial_state = pl.Config.state()
792
793
with pl.Config() as cfg:
794
(
795
cfg.set_tbl_formatting(rounded_corners=True)
796
.set_verbose(True)
797
.set_tbl_hide_dtype_separator(True)
798
.set_ascii_tables()
799
)
800
new_state_entries = set(
801
{
802
"POLARS_FMT_MAX_COLS": "8",
803
"POLARS_FMT_TABLE_FORMATTING": "ASCII_FULL_CONDENSED",
804
"POLARS_FMT_TABLE_HIDE_COLUMN_SEPARATOR": "1",
805
"POLARS_FMT_TABLE_ROUNDED_CORNERS": "1",
806
"POLARS_VERBOSE": "1",
807
}.items()
808
)
809
assert set(initial_state.items()) != new_state_entries
810
assert new_state_entries.issubset(set(cfg.state().items()))
811
812
# expect scope-exit to restore original state
813
assert pl.Config.state() == initial_state
814
815
816
def test_config_raise_error_if_not_exist() -> None:
817
with pytest.raises(AttributeError), pl.Config(i_do_not_exist=True): # type: ignore[call-arg]
818
pass
819
820
821
def test_config_state_env_only() -> None:
822
pl.Config.set_verbose(False)
823
pl.Config.set_fmt_float("full")
824
825
state_all = pl.Config.state(env_only=False)
826
state_env_only = pl.Config.state(env_only=True)
827
assert len(state_env_only) < len(state_all)
828
assert "set_fmt_float" in state_all
829
assert "set_fmt_float" not in state_env_only
830
831
832
def test_set_streaming_chunk_size() -> None:
833
with pl.Config() as cfg:
834
cfg.set_streaming_chunk_size(8)
835
assert os.environ.get("POLARS_STREAMING_CHUNK_SIZE") == "8"
836
837
with pytest.raises(ValueError), pl.Config() as cfg:
838
cfg.set_streaming_chunk_size(0)
839
840
841
def test_set_fmt_str_lengths_invalid_length() -> None:
842
with pl.Config() as cfg:
843
with pytest.raises(ValueError):
844
cfg.set_fmt_str_lengths(0)
845
with pytest.raises(ValueError):
846
cfg.set_fmt_str_lengths(-2)
847
848
849
def test_truncated_rows_cols_values_ascii() -> None:
850
df = pl.DataFrame({f"c{n}": list(range(-n, 100 - n)) for n in range(10)})
851
852
pl.Config.set_tbl_formatting("UTF8_BORDERS_ONLY", rounded_corners=True)
853
assert (
854
str(df) == "shape: (100, 10)\n"
855
"╭───────────────────────────────────────────────────╮\n"
856
"│ c0 c1 c2 c3 … c6 c7 c8 c9 │\n"
857
"│ --- --- --- --- --- --- --- --- │\n"
858
"│ i64 i64 i64 i64 i64 i64 i64 i64 │\n"
859
"╞═══════════════════════════════════════════════════╡\n"
860
"│ 0 -1 -2 -3 … -6 -7 -8 -9 │\n"
861
"│ 1 0 -1 -2 … -5 -6 -7 -8 │\n"
862
"│ 2 1 0 -1 … -4 -5 -6 -7 │\n"
863
"│ 3 2 1 0 … -3 -4 -5 -6 │\n"
864
"│ 4 3 2 1 … -2 -3 -4 -5 │\n"
865
"│ … … … … … … … … … │\n"
866
"│ 95 94 93 92 … 89 88 87 86 │\n"
867
"│ 96 95 94 93 … 90 89 88 87 │\n"
868
"│ 97 96 95 94 … 91 90 89 88 │\n"
869
"│ 98 97 96 95 … 92 91 90 89 │\n"
870
"│ 99 98 97 96 … 93 92 91 90 │\n"
871
"╰───────────────────────────────────────────────────╯"
872
)
873
with pl.Config(tbl_formatting="ASCII_FULL_CONDENSED"):
874
assert (
875
str(df) == "shape: (100, 10)\n"
876
"+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n"
877
"| c0 | c1 | c2 | c3 | ... | c6 | c7 | c8 | c9 |\n"
878
"| --- | --- | --- | --- | | --- | --- | --- | --- |\n"
879
"| i64 | i64 | i64 | i64 | | i64 | i64 | i64 | i64 |\n"
880
"+=====================================================+\n"
881
"| 0 | -1 | -2 | -3 | ... | -6 | -7 | -8 | -9 |\n"
882
"| 1 | 0 | -1 | -2 | ... | -5 | -6 | -7 | -8 |\n"
883
"| 2 | 1 | 0 | -1 | ... | -4 | -5 | -6 | -7 |\n"
884
"| 3 | 2 | 1 | 0 | ... | -3 | -4 | -5 | -6 |\n"
885
"| 4 | 3 | 2 | 1 | ... | -2 | -3 | -4 | -5 |\n"
886
"| ... | ... | ... | ... | ... | ... | ... | ... | ... |\n"
887
"| 95 | 94 | 93 | 92 | ... | 89 | 88 | 87 | 86 |\n"
888
"| 96 | 95 | 94 | 93 | ... | 90 | 89 | 88 | 87 |\n"
889
"| 97 | 96 | 95 | 94 | ... | 91 | 90 | 89 | 88 |\n"
890
"| 98 | 97 | 96 | 95 | ... | 92 | 91 | 90 | 89 |\n"
891
"| 99 | 98 | 97 | 96 | ... | 93 | 92 | 91 | 90 |\n"
892
"+-----+-----+-----+-----+-----+-----+-----+-----+-----+"
893
)
894
895
with pl.Config(tbl_formatting="MARKDOWN"):
896
df = pl.DataFrame({"b": [b"0tigohij1prisdfj1gs2io3fbjg0pfihodjgsnfbbmfgnd8j"]})
897
assert (
898
str(df)
899
== dedent("""
900
shape: (1, 1)
901
| b |
902
| --- |
903
| binary |
904
|---------------------------------|
905
| b"0tigohij1prisdfj1gs2io3fbjg0… |""").lstrip()
906
)
907
908
with pl.Config(tbl_formatting="ASCII_MARKDOWN"):
909
df = pl.DataFrame({"b": [b"0tigohij1prisdfj1gs2io3fbjg0pfihodjgsnfbbmfgnd8j"]})
910
assert (
911
str(df)
912
== dedent("""
913
shape: (1, 1)
914
| b |
915
| --- |
916
| binary |
917
|-----------------------------------|
918
| b"0tigohij1prisdfj1gs2io3fbjg0... |""").lstrip()
919
)
920
921
922
def test_warn_unstable(recwarn: pytest.WarningsRecorder) -> None:
923
issue_unstable_warning()
924
assert len(recwarn) == 0
925
926
pl.Config().warn_unstable(True)
927
928
issue_unstable_warning()
929
assert len(recwarn) == 1
930
931
pl.Config().warn_unstable(False)
932
933
issue_unstable_warning()
934
assert len(recwarn) == 1
935
936
937
@pytest.mark.parametrize(
938
("environment_variable", "config_setting", "value", "expected"),
939
[
940
("POLARS_ENGINE_AFFINITY", "set_engine_affinity", "gpu", "gpu"),
941
("POLARS_FMT_MAX_COLS", "set_tbl_cols", 12, "12"),
942
("POLARS_FMT_MAX_ROWS", "set_tbl_rows", 3, "3"),
943
("POLARS_FMT_STR_LEN", "set_fmt_str_lengths", 42, "42"),
944
("POLARS_FMT_TABLE_CELL_ALIGNMENT", "set_tbl_cell_alignment", "RIGHT", "RIGHT"),
945
(
946
"POLARS_FMT_TABLE_CELL_NUMERIC_ALIGNMENT",
947
"set_tbl_cell_numeric_alignment",
948
"RIGHT",
949
"RIGHT",
950
),
951
("POLARS_FMT_TABLE_HIDE_COLUMN_NAMES", "set_tbl_hide_column_names", True, "1"),
952
(
953
"POLARS_FMT_TABLE_DATAFRAME_SHAPE_BELOW",
954
"set_tbl_dataframe_shape_below",
955
True,
956
"1",
957
),
958
(
959
"POLARS_FMT_TABLE_FORMATTING",
960
"set_ascii_tables",
961
True,
962
"ASCII_FULL_CONDENSED",
963
),
964
(
965
"POLARS_FMT_TABLE_FORMATTING",
966
"set_tbl_formatting",
967
"ASCII_MARKDOWN",
968
"ASCII_MARKDOWN",
969
),
970
(
971
"POLARS_FMT_TABLE_HIDE_COLUMN_DATA_TYPES",
972
"set_tbl_hide_column_data_types",
973
True,
974
"1",
975
),
976
(
977
"POLARS_FMT_TABLE_HIDE_COLUMN_SEPARATOR",
978
"set_tbl_hide_dtype_separator",
979
True,
980
"1",
981
),
982
(
983
"POLARS_FMT_TABLE_HIDE_DATAFRAME_SHAPE_INFORMATION",
984
"set_tbl_hide_dataframe_shape",
985
True,
986
"1",
987
),
988
(
989
"POLARS_FMT_TABLE_INLINE_COLUMN_DATA_TYPE",
990
"set_tbl_column_data_type_inline",
991
True,
992
"1",
993
),
994
("POLARS_STREAMING_CHUNK_SIZE", "set_streaming_chunk_size", 100, "100"),
995
("POLARS_TABLE_WIDTH", "set_tbl_width_chars", 80, "80"),
996
("POLARS_VERBOSE", "set_verbose", True, "1"),
997
("POLARS_WARN_UNSTABLE", "warn_unstable", True, "1"),
998
],
999
)
1000
def test_unset_config_env_vars(
1001
environment_variable: str, config_setting: str, value: Any, expected: str
1002
) -> None:
1003
assert environment_variable in _POLARS_CFG_ENV_VARS
1004
1005
with pl.Config(**{config_setting: value}):
1006
assert os.environ[environment_variable] == expected
1007
1008
with pl.Config(**{config_setting: None}): # type: ignore[arg-type]
1009
assert environment_variable not in os.environ
1010
1011