Path: blob/main/py-polars/tests/unit/meta/test_config.py
8424 views
from __future__ import annotations12import os3from pathlib import Path4from textwrap import dedent5from typing import TYPE_CHECKING, Any67import pytest89import polars as pl10import polars._plr as plr11from polars._utils.unstable import issue_unstable_warning12from polars.config import _POLARS_CFG_ENV_VARS1314if TYPE_CHECKING:15from collections.abc import Iterator161718@pytest.fixture(autouse=True)19def _environ() -> Iterator[None]:20"""Fixture to restore the environment after/during tests."""21with pl.Config(restore_defaults=True):22yield232425def test_ascii_tables() -> None:26df = pl.DataFrame(27{28"a": [1, 2],29"b": [4, 5],30"c": [[list(range(1, 26))], [list(range(1, 76))]],31}32)3334ascii_table_repr = (35"shape: (2, 3)\n"36"+-----+-----+------------------+\n"37"| a | b | c |\n"38"| --- | --- | --- |\n"39"| i64 | i64 | list[list[i64]] |\n"40"+==============================+\n"41"| 1 | 4 | [[1, 2, ... 25]] |\n"42"| 2 | 5 | [[1, 2, ... 75]] |\n"43"+-----+-----+------------------+"44)45# note: expect to render ascii only within the given scope46with pl.Config(set_ascii_tables=True):47assert repr(df) == ascii_table_repr4849# confirm back to utf8 default after scope-exit50assert (51repr(df) == "shape: (2, 3)\n"52"┌─────┬─────┬─────────────────┐\n"53"│ a ┆ b ┆ c │\n"54"│ --- ┆ --- ┆ --- │\n"55"│ i64 ┆ i64 ┆ list[list[i64]] │\n"56"╞═════╪═════╪═════════════════╡\n"57"│ 1 ┆ 4 ┆ [[1, 2, … 25]] │\n"58"│ 2 ┆ 5 ┆ [[1, 2, … 75]] │\n"59"└─────┴─────┴─────────────────┘"60)6162@pl.Config(set_ascii_tables=True)63def ascii_table() -> str:64return repr(df)6566assert ascii_table() == ascii_table_repr676869def test_hide_header_elements() -> None:70df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})7172pl.Config.set_tbl_hide_column_data_types(True)73assert (74str(df) == "shape: (3, 3)\n"75"┌───┬───┬───┐\n"76"│ a ┆ b ┆ c │\n"77"╞═══╪═══╪═══╡\n"78"│ 1 ┆ 4 ┆ 7 │\n"79"│ 2 ┆ 5 ┆ 8 │\n"80"│ 3 ┆ 6 ┆ 9 │\n"81"└───┴───┴───┘"82)8384pl.Config.set_tbl_hide_column_data_types(False).set_tbl_hide_column_names(True)85assert (86str(df) == "shape: (3, 3)\n"87"┌─────┬─────┬─────┐\n"88"│ i64 ┆ i64 ┆ i64 │\n"89"╞═════╪═════╪═════╡\n"90"│ 1 ┆ 4 ┆ 7 │\n"91"│ 2 ┆ 5 ┆ 8 │\n"92"│ 3 ┆ 6 ┆ 9 │\n"93"└─────┴─────┴─────┘"94)959697def test_set_tbl_cols() -> None:98df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})99100pl.Config.set_tbl_cols(1)101assert str(df).split("\n")[2] == "│ a ┆ … │"102pl.Config.set_tbl_cols(2)103assert str(df).split("\n")[2] == "│ a ┆ … ┆ c │"104pl.Config.set_tbl_cols(3)105assert str(df).split("\n")[2] == "│ a ┆ b ┆ c │"106107df = pl.DataFrame(108{"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9], "d": [10, 11, 12]}109)110pl.Config.set_tbl_cols(2)111assert str(df).split("\n")[2] == "│ a ┆ … ┆ d │"112pl.Config.set_tbl_cols(3)113assert str(df).split("\n")[2] == "│ a ┆ b ┆ … ┆ d │"114pl.Config.set_tbl_cols(-1)115assert str(df).split("\n")[2] == "│ a ┆ b ┆ c ┆ d │"116117118def test_set_tbl_rows() -> None:119df = pl.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8], "c": [9, 10, 11, 12]})120ser = pl.Series("ser", [1, 2, 3, 4, 5])121122pl.Config.set_tbl_rows(0)123assert (124str(df) == "shape: (4, 3)\n"125"┌─────┬─────┬─────┐\n"126"│ a ┆ b ┆ c │\n"127"│ --- ┆ --- ┆ --- │\n"128"│ i64 ┆ i64 ┆ i64 │\n"129"╞═════╪═════╪═════╡\n"130"│ … ┆ … ┆ … │\n"131"└─────┴─────┴─────┘"132)133assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t…\n]"134135pl.Config.set_tbl_rows(1)136assert (137str(df) == "shape: (4, 3)\n"138"┌─────┬─────┬─────┐\n"139"│ a ┆ b ┆ c │\n"140"│ --- ┆ --- ┆ --- │\n"141"│ i64 ┆ i64 ┆ i64 │\n"142"╞═════╪═════╪═════╡\n"143"│ 1 ┆ 5 ┆ 9 │\n"144"│ … ┆ … ┆ … │\n"145"└─────┴─────┴─────┘"146)147assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t…\n]"148149pl.Config.set_tbl_rows(2)150assert (151str(df) == "shape: (4, 3)\n"152"┌─────┬─────┬─────┐\n"153"│ a ┆ b ┆ c │\n"154"│ --- ┆ --- ┆ --- │\n"155"│ i64 ┆ i64 ┆ i64 │\n"156"╞═════╪═════╪═════╡\n"157"│ 1 ┆ 5 ┆ 9 │\n"158"│ … ┆ … ┆ … │\n"159"│ 4 ┆ 8 ┆ 12 │\n"160"└─────┴─────┴─────┘"161)162assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t…\n\t5\n]"163164pl.Config.set_tbl_rows(3)165assert (166str(df) == "shape: (4, 3)\n"167"┌─────┬─────┬─────┐\n"168"│ a ┆ b ┆ c │\n"169"│ --- ┆ --- ┆ --- │\n"170"│ i64 ┆ i64 ┆ i64 │\n"171"╞═════╪═════╪═════╡\n"172"│ 1 ┆ 5 ┆ 9 │\n"173"│ 2 ┆ 6 ┆ 10 │\n"174"│ … ┆ … ┆ … │\n"175"│ 4 ┆ 8 ┆ 12 │\n"176"└─────┴─────┴─────┘"177)178assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t…\n\t5\n]"179180pl.Config.set_tbl_rows(4)181assert (182str(df) == "shape: (4, 3)\n"183"┌─────┬─────┬─────┐\n"184"│ a ┆ b ┆ c │\n"185"│ --- ┆ --- ┆ --- │\n"186"│ i64 ┆ i64 ┆ i64 │\n"187"╞═════╪═════╪═════╡\n"188"│ 1 ┆ 5 ┆ 9 │\n"189"│ 2 ┆ 6 ┆ 10 │\n"190"│ 3 ┆ 7 ┆ 11 │\n"191"│ 4 ┆ 8 ┆ 12 │\n"192"└─────┴─────┴─────┘"193)194assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t…\n\t4\n\t5\n]"195196df = pl.DataFrame(197{198"a": [1, 2, 3, 4, 5],199"b": [6, 7, 8, 9, 10],200"c": [11, 12, 13, 14, 15],201}202)203204pl.Config.set_tbl_rows(3)205assert (206str(df) == "shape: (5, 3)\n"207"┌─────┬─────┬─────┐\n"208"│ a ┆ b ┆ c │\n"209"│ --- ┆ --- ┆ --- │\n"210"│ i64 ┆ i64 ┆ i64 │\n"211"╞═════╪═════╪═════╡\n"212"│ 1 ┆ 6 ┆ 11 │\n"213"│ 2 ┆ 7 ┆ 12 │\n"214"│ … ┆ … ┆ … │\n"215"│ 5 ┆ 10 ┆ 15 │\n"216"└─────┴─────┴─────┘"217)218219pl.Config.set_tbl_rows(-1)220assert str(ser) == "shape: (5,)\nSeries: 'ser' [i64]\n[\n\t1\n\t2\n\t3\n\t4\n\t5\n]"221222pl.Config.set_tbl_hide_dtype_separator(True)223assert (224str(df) == "shape: (5, 3)\n"225"┌─────┬─────┬─────┐\n"226"│ a ┆ b ┆ c │\n"227"│ i64 ┆ i64 ┆ i64 │\n"228"╞═════╪═════╪═════╡\n"229"│ 1 ┆ 6 ┆ 11 │\n"230"│ 2 ┆ 7 ┆ 12 │\n"231"│ 3 ┆ 8 ┆ 13 │\n"232"│ 4 ┆ 9 ┆ 14 │\n"233"│ 5 ┆ 10 ┆ 15 │\n"234"└─────┴─────┴─────┘"235)236237238def test_set_tbl_formats() -> None:239df = pl.DataFrame(240{241"foo": [1, 2, 3],242"bar": [6.0, 7.0, 8.0],243"ham": ["a", "b", "c"],244}245)246pl.Config().set_tbl_formatting("ASCII_MARKDOWN")247assert str(df) == (248"shape: (3, 3)\n"249"| foo | bar | ham |\n"250"| --- | --- | --- |\n"251"| i64 | f64 | str |\n"252"|-----|-----|-----|\n"253"| 1 | 6.0 | a |\n"254"| 2 | 7.0 | b |\n"255"| 3 | 8.0 | c |"256)257258pl.Config().set_tbl_formatting("ASCII_BORDERS_ONLY_CONDENSED")259with pl.Config(tbl_hide_dtype_separator=True):260assert str(df) == (261"shape: (3, 3)\n"262"+-----------------+\n"263"| foo bar ham |\n"264"| i64 f64 str |\n"265"+=================+\n"266"| 1 6.0 a |\n"267"| 2 7.0 b |\n"268"| 3 8.0 c |\n"269"+-----------------+"270)271272# temporarily scope "nothing" style, with no data types273with pl.Config(274tbl_formatting="NOTHING",275tbl_hide_column_data_types=True,276):277assert str(df) == (278"shape: (3, 3)\n"279" foo bar ham \n"280" 1 6.0 a \n"281" 2 7.0 b \n"282" 3 8.0 c "283)284285# after scope, expect previous style286assert str(df) == (287"shape: (3, 3)\n"288"+-----------------+\n"289"| foo bar ham |\n"290"| --- --- --- |\n"291"| i64 f64 str |\n"292"+=================+\n"293"| 1 6.0 a |\n"294"| 2 7.0 b |\n"295"| 3 8.0 c |\n"296"+-----------------+"297)298299# invalid style300with pytest.raises(ValueError, match="invalid table format name: 'NOPE'"):301pl.Config().set_tbl_formatting("NOPE") # type: ignore[arg-type]302303304def test_set_tbl_width_chars() -> None:305df = pl.DataFrame(306{307"a really long col": [1, 2, 3],308"b": ["", "this is a string value that will be truncated", None],309"this is 10": [4, 5, 6],310}311)312assert max(len(line) for line in str(df).split("\n")) == 68313314pl.Config.set_tbl_width_chars(60)315assert max(len(line) for line in str(df).split("\n")) == 60316317# force minimal table size (will hard-wrap everything; "don't try this at home" :p)318pl.Config.set_tbl_width_chars(0)319assert max(len(line) for line in str(df).split("\n")) == 19320321# this check helps to check that column width bucketing322# is exact; no extraneous character allocation323df = pl.DataFrame(324{325"A": [1, 2, 3, 4, 5],326"fruits": ["banana", "banana", "apple", "apple", "banana"],327"B": [5, 4, 3, 2, 1],328"cars": ["beetle", "audi", "beetle", "beetle", "beetle"],329},330schema_overrides={"A": pl.Int64, "B": pl.Int64},331).select(pl.all(), pl.all().name.suffix("_suffix!"))332333with pl.Config(tbl_width_chars=87):334assert max(len(line) for line in str(df).split("\n")) == 87335336# check that -1 is interpreted as no limit337df = pl.DataFrame({str(i): ["a" * 25] for i in range(5)})338for tbl_width_chars, expected_width in [339(None, 100),340(-1, 141),341]:342with pl.Config(tbl_width_chars=tbl_width_chars):343assert max(len(line) for line in str(df).split("\n")) == expected_width344345346def test_shape_below_table_and_inlined_dtype() -> None:347df = pl.DataFrame({"a": [1, 2], "b": [3, 4], "c": [5, 6]})348349pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(True)350pl.Config.set_tbl_formatting("UTF8_FULL", rounded_corners=True)351assert (352str(df) == ""353"╭─────────┬─────────┬─────────╮\n"354"│ a (i64) ┆ b (i64) ┆ c (i64) │\n"355"╞═════════╪═════════╪═════════╡\n"356"│ 1 ┆ 3 ┆ 5 │\n"357"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"358"│ 2 ┆ 4 ┆ 6 │\n"359"╰─────────┴─────────┴─────────╯\n"360"shape: (2, 3)"361)362363pl.Config.set_tbl_dataframe_shape_below(False)364assert (365str(df) == "shape: (2, 3)\n"366"╭─────────┬─────────┬─────────╮\n"367"│ a (i64) ┆ b (i64) ┆ c (i64) │\n"368"╞═════════╪═════════╪═════════╡\n"369"│ 1 ┆ 3 ┆ 5 │\n"370"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"371"│ 2 ┆ 4 ┆ 6 │\n"372"╰─────────┴─────────┴─────────╯"373)374(375pl.Config.set_tbl_formatting(None, rounded_corners=False)376.set_tbl_column_data_type_inline(False)377.set_tbl_cell_alignment("RIGHT")378)379assert (380str(df) == "shape: (2, 3)\n"381"┌─────┬─────┬─────┐\n"382"│ a ┆ b ┆ c │\n"383"│ --- ┆ --- ┆ --- │\n"384"│ i64 ┆ i64 ┆ i64 │\n"385"╞═════╪═════╪═════╡\n"386"│ 1 ┆ 3 ┆ 5 │\n"387"│ 2 ┆ 4 ┆ 6 │\n"388"└─────┴─────┴─────┘"389)390with pytest.raises(ValueError):391pl.Config.set_tbl_cell_alignment("INVALID") # type: ignore[arg-type]392393394def test_shape_format_for_big_numbers() -> None:395df = pl.DataFrame({"a": range(1, 1001), "b": range(1001, 1001 + 1000)})396397pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(True)398pl.Config.set_tbl_formatting("UTF8_FULL", rounded_corners=True)399assert (400str(df) == ""401"╭─────────┬─────────╮\n"402"│ a (i64) ┆ b (i64) │\n"403"╞═════════╪═════════╡\n"404"│ 1 ┆ 1001 │\n"405"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"406"│ 2 ┆ 1002 │\n"407"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"408"│ 3 ┆ 1003 │\n"409"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"410"│ 4 ┆ 1004 │\n"411"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"412"│ 5 ┆ 1005 │\n"413"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"414"│ … ┆ … │\n"415"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"416"│ 996 ┆ 1996 │\n"417"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"418"│ 997 ┆ 1997 │\n"419"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"420"│ 998 ┆ 1998 │\n"421"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"422"│ 999 ┆ 1999 │\n"423"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"424"│ 1000 ┆ 2000 │\n"425"╰─────────┴─────────╯\n"426"shape: (1_000, 2)"427)428429pl.Config.set_tbl_column_data_type_inline(True).set_tbl_dataframe_shape_below(False)430assert (431str(df) == "shape: (1_000, 2)\n"432"╭─────────┬─────────╮\n"433"│ a (i64) ┆ b (i64) │\n"434"╞═════════╪═════════╡\n"435"│ 1 ┆ 1001 │\n"436"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"437"│ 2 ┆ 1002 │\n"438"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"439"│ 3 ┆ 1003 │\n"440"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"441"│ 4 ┆ 1004 │\n"442"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"443"│ 5 ┆ 1005 │\n"444"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"445"│ … ┆ … │\n"446"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"447"│ 996 ┆ 1996 │\n"448"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"449"│ 997 ┆ 1997 │\n"450"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"451"│ 998 ┆ 1998 │\n"452"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"453"│ 999 ┆ 1999 │\n"454"├╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌┤\n"455"│ 1000 ┆ 2000 │\n"456"╰─────────┴─────────╯"457)458459pl.Config.set_tbl_rows(0)460ser = pl.Series("ser", range(1000))461assert str(ser) == "shape: (1_000,)\nSeries: 'ser' [i64]\n[\n\t…\n]"462463pl.Config.set_tbl_rows(1)464pl.Config.set_tbl_cols(1)465df = pl.DataFrame({str(col_num): 1 for col_num in range(1000)})466467assert (468str(df) == "shape: (1, 1_000)\n"469"╭─────────┬───╮\n"470"│ 0 (i64) ┆ … │\n"471"╞═════════╪═══╡\n"472"│ 1 ┆ … │\n"473"╰─────────┴───╯"474)475476pl.Config.set_tbl_formatting("ASCII_FULL_CONDENSED")477assert (478str(df) == "shape: (1, 1_000)\n"479"+---------+-----+\n"480"| 0 (i64) | ... |\n"481"+===============+\n"482"| 1 | ... |\n"483"+---------+-----+"484)485486487def test_numeric_right_alignment() -> None:488pl.Config.set_tbl_cell_numeric_alignment("RIGHT")489490df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]})491assert (492str(df) == "shape: (3, 3)\n"493"┌─────┬─────┬─────┐\n"494"│ a ┆ b ┆ c │\n"495"│ --- ┆ --- ┆ --- │\n"496"│ i64 ┆ i64 ┆ i64 │\n"497"╞═════╪═════╪═════╡\n"498"│ 1 ┆ 4 ┆ 7 │\n"499"│ 2 ┆ 5 ┆ 8 │\n"500"│ 3 ┆ 6 ┆ 9 │\n"501"└─────┴─────┴─────┘"502)503504df = pl.DataFrame(505{"a": [1.1, 2.22, 3.333], "b": [4.0, 5.0, 6.0], "c": [7.0, 8.0, 9.0]}506)507with pl.Config():508pl.Config.set_fmt_float("full")509assert (510str(df) == "shape: (3, 3)\n"511"┌───────┬─────┬─────┐\n"512"│ a ┆ b ┆ c │\n"513"│ --- ┆ --- ┆ --- │\n"514"│ f64 ┆ f64 ┆ f64 │\n"515"╞═══════╪═════╪═════╡\n"516"│ 1.1 ┆ 4 ┆ 7 │\n"517"│ 2.22 ┆ 5 ┆ 8 │\n"518"│ 3.333 ┆ 6 ┆ 9 │\n"519"└───────┴─────┴─────┘"520)521522with pl.Config(fmt_float="mixed"):523assert (524str(df) == "shape: (3, 3)\n"525"┌───────┬─────┬─────┐\n"526"│ a ┆ b ┆ c │\n"527"│ --- ┆ --- ┆ --- │\n"528"│ f64 ┆ f64 ┆ f64 │\n"529"╞═══════╪═════╪═════╡\n"530"│ 1.1 ┆ 4.0 ┆ 7.0 │\n"531"│ 2.22 ┆ 5.0 ┆ 8.0 │\n"532"│ 3.333 ┆ 6.0 ┆ 9.0 │\n"533"└───────┴─────┴─────┘"534)535536with pl.Config(float_precision=6):537assert str(df) == (538"shape: (3, 3)\n"539"┌──────────┬──────────┬──────────┐\n"540"│ a ┆ b ┆ c │\n"541"│ --- ┆ --- ┆ --- │\n"542"│ f64 ┆ f64 ┆ f64 │\n"543"╞══════════╪══════════╪══════════╡\n"544"│ 1.100000 ┆ 4.000000 ┆ 7.000000 │\n"545"│ 2.220000 ┆ 5.000000 ┆ 8.000000 │\n"546"│ 3.333000 ┆ 6.000000 ┆ 9.000000 │\n"547"└──────────┴──────────┴──────────┘"548)549with pl.Config(float_precision=None):550assert (551str(df) == "shape: (3, 3)\n"552"┌───────┬─────┬─────┐\n"553"│ a ┆ b ┆ c │\n"554"│ --- ┆ --- ┆ --- │\n"555"│ f64 ┆ f64 ┆ f64 │\n"556"╞═══════╪═════╪═════╡\n"557"│ 1.1 ┆ 4.0 ┆ 7.0 │\n"558"│ 2.22 ┆ 5.0 ┆ 8.0 │\n"559"│ 3.333 ┆ 6.0 ┆ 9.0 │\n"560"└───────┴─────┴─────┘"561)562563df = pl.DataFrame(564{"a": [1.1, 22.2, 3.33], "b": [444.0, 55.5, 6.6], "c": [77.7, 8888.0, 9.9999]}565)566with pl.Config(fmt_float="full", float_precision=1):567assert (568str(df) == "shape: (3, 3)\n"569"┌──────┬───────┬────────┐\n"570"│ a ┆ b ┆ c │\n"571"│ --- ┆ --- ┆ --- │\n"572"│ f64 ┆ f64 ┆ f64 │\n"573"╞══════╪═══════╪════════╡\n"574"│ 1.1 ┆ 444.0 ┆ 77.7 │\n"575"│ 22.2 ┆ 55.5 ┆ 8888.0 │\n"576"│ 3.3 ┆ 6.6 ┆ 10.0 │\n"577"└──────┴───────┴────────┘"578)579580df = pl.DataFrame(581{582"a": [1100000000000000000.1, 22200000000000000.2, 33330000000000000.33333],583"b": [40000000000000000000.0, 5, 600000000000000000.0],584"c": [700000.0, 80000000000000000.0, 900],585}586)587with pl.Config(float_precision=2):588assert (589str(df) == "shape: (3, 3)\n"590"┌─────────┬─────────┬───────────┐\n"591"│ a ┆ b ┆ c │\n"592"│ --- ┆ --- ┆ --- │\n"593"│ f64 ┆ f64 ┆ f64 │\n"594"╞═════════╪═════════╪═══════════╡\n"595"│ 1.10e18 ┆ 4.00e19 ┆ 700000.00 │\n"596"│ 2.22e16 ┆ 5.00 ┆ 8.00e16 │\n"597"│ 3.33e16 ┆ 6.00e17 ┆ 900.00 │\n"598"└─────────┴─────────┴───────────┘"599)600601602@pytest.mark.write_disk603def test_config_load_save(tmp_path: Path) -> None:604for file in (605None,606tmp_path / "polars.config",607str(tmp_path / "polars.config"),608):609# set some config options...610pl.Config.set_tbl_cols(12)611pl.Config.set_verbose(True)612pl.Config.set_fmt_float("full")613pl.Config.set_float_precision(6)614pl.Config.set_thousands_separator(",")615assert os.environ.get("POLARS_VERBOSE") == "1"616617if file is None:618cfg = pl.Config.save()619assert isinstance(cfg, str)620else:621assert pl.Config.save_to_file(file) is None622623assert "POLARS_VERBOSE" in pl.Config.state(if_set=True)624625# ...modify the same options...626pl.Config.set_tbl_cols(10)627pl.Config.set_verbose(False)628pl.Config.set_fmt_float("mixed")629pl.Config.set_float_precision(2)630pl.Config.set_thousands_separator(None)631assert os.environ.get("POLARS_VERBOSE") == "0"632633# ...load back from config file/string...634if file is None:635pl.Config.load(cfg)636else:637with pytest.raises(ValueError, match="invalid Config file"):638pl.Config.load_from_file(cfg)639640if isinstance(file, Path):641with pytest.raises(TypeError, match="the JSON object must be str"):642pl.Config.load(file) # type: ignore[arg-type]643else:644with pytest.raises(ValueError, match="invalid Config string"):645pl.Config.load(file)646647pl.Config.load_from_file(file)648649# ...and confirm the saved options were set.650assert os.environ.get("POLARS_FMT_MAX_COLS") == "12"651assert os.environ.get("POLARS_VERBOSE") == "1"652assert plr.get_float_fmt() == "full"653assert plr.get_float_precision() == 6654655# restore all default options (unsets from env)656pl.Config.restore_defaults()657for e in ("POLARS_FMT_MAX_COLS", "POLARS_VERBOSE"):658assert e not in pl.Config.state(if_set=True)659assert e in pl.Config.state()660661assert os.environ.get("POLARS_FMT_MAX_COLS") is None662assert os.environ.get("POLARS_VERBOSE") is None663assert plr.get_float_fmt() == "mixed"664assert plr.get_float_precision() is None665666# ref: #11094667with pl.Config(668streaming_chunk_size=100,669tbl_cols=2000,670tbl_formatting="UTF8_NO_BORDERS",671tbl_hide_column_data_types=True,672tbl_hide_dtype_separator=True,673tbl_rows=2000,674tbl_width_chars=2000,675verbose=True,676):677assert isinstance(repr(pl.DataFrame({"xyz": [0]})), str)678679680def test_config_load_save_context() -> None:681# Store the default configuration state.682default_state = pl.Config.save()683684# Establish some non-default settings.685pl.Config.set_tbl_formatting("ASCII_MARKDOWN")686pl.Config.set_verbose(True)687688# Load the default config, validate load & context manager behaviour.689with pl.Config.load(default_state):690assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None691assert os.environ.get("POLARS_VERBOSE") is None692693# Ensure earlier state was restored.694assert os.environ["POLARS_FMT_TABLE_FORMATTING"] == "ASCII_MARKDOWN"695assert os.environ["POLARS_VERBOSE"]696697# Restore defaults for other tests.698pl.Config.restore_defaults()699700701def test_config_instances() -> None:702# establish two config instances that defer setting their options703cfg_markdown = pl.Config(704tbl_formatting="MARKDOWN",705apply_on_context_enter=True,706)707cfg_compact = pl.Config(708tbl_rows=4,709tbl_cols=4,710tbl_column_data_type_inline=True,711apply_on_context_enter=True,712)713714# check instance (in)equality715assert cfg_markdown != cfg_compact716assert cfg_markdown == pl.Config(717tbl_formatting="MARKDOWN", apply_on_context_enter=True718)719720# confirm that the options have not been applied yet721assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None722723# confirm that the deferred options are applied when the instance context724# is entered into, and that they can be re-used without leaking state725@cfg_markdown726def fn1() -> str | None:727return os.environ.get("POLARS_FMT_TABLE_FORMATTING")728729assert fn1() == "MARKDOWN"730assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None731732with cfg_markdown: # can re-use instance as decorator and context733assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") == "MARKDOWN"734assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None735736@cfg_markdown737def fn2() -> str | None:738return os.environ.get("POLARS_FMT_TABLE_FORMATTING")739740assert fn2() == "MARKDOWN"741assert os.environ.get("POLARS_FMT_TABLE_FORMATTING") is None742743df = pl.DataFrame({f"c{idx}": [idx] * 10 for idx in range(10)})744745@cfg_compact746def fn3(df: pl.DataFrame) -> str:747return repr(df)748749# reuse config instance and confirm state does not leak between invocations750for _ in range(3):751assert (752fn3(df)753== dedent("""754shape: (10, 10)755┌──────────┬──────────┬───┬──────────┬──────────┐756│ c0 (i64) ┆ c1 (i64) ┆ … ┆ c8 (i64) ┆ c9 (i64) │757╞══════════╪══════════╪═══╪══════════╪══════════╡758│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │759│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │760│ … ┆ … ┆ … ┆ … ┆ … │761│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │762│ 0 ┆ 1 ┆ … ┆ 8 ┆ 9 │763└──────────┴──────────┴───┴──────────┴──────────┘""").lstrip()764)765766assert (767repr(df)768== dedent("""769shape: (10, 10)770┌─────┬─────┬─────┬─────┬───┬─────┬─────┬─────┬─────┐771│ c0 ┆ c1 ┆ c2 ┆ c3 ┆ … ┆ c6 ┆ c7 ┆ c8 ┆ c9 │772│ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │773│ i64 ┆ i64 ┆ i64 ┆ i64 ┆ ┆ i64 ┆ i64 ┆ i64 ┆ i64 │774╞═════╪═════╪═════╪═════╪═══╪═════╪═════╪═════╪═════╡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│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │784│ 0 ┆ 1 ┆ 2 ┆ 3 ┆ … ┆ 6 ┆ 7 ┆ 8 ┆ 9 │785└─────┴─────┴─────┴─────┴───┴─────┴─────┴─────┴─────┘""").lstrip()786)787788789def test_config_scope() -> None:790pl.Config.set_verbose(False)791pl.Config.set_tbl_cols(8)792793initial_state = pl.Config.state()794795with pl.Config() as cfg:796(797cfg.set_tbl_formatting(rounded_corners=True)798.set_verbose(True)799.set_tbl_hide_dtype_separator(True)800.set_ascii_tables()801)802new_state_entries = set(803{804"POLARS_FMT_MAX_COLS": "8",805"POLARS_FMT_TABLE_FORMATTING": "ASCII_FULL_CONDENSED",806"POLARS_FMT_TABLE_HIDE_COLUMN_SEPARATOR": "1",807"POLARS_FMT_TABLE_ROUNDED_CORNERS": "1",808"POLARS_VERBOSE": "1",809}.items()810)811assert set(initial_state.items()) != new_state_entries812assert new_state_entries.issubset(set(cfg.state().items()))813814# expect scope-exit to restore original state815assert pl.Config.state() == initial_state816817818def test_config_raise_error_if_not_exist() -> None:819with pytest.raises(AttributeError), pl.Config(i_do_not_exist=True): # type: ignore[call-arg]820pass821822823def test_config_state_env_only() -> None:824with pl.Config() as cfg:825cfg.set_verbose(False)826cfg.set_fmt_float("full")827828state_all = cfg.state(env_only=False)829state_env_only = cfg.state(env_only=True)830assert len(state_env_only) < len(state_all)831assert "set_fmt_float" in state_all832assert "set_fmt_float" not in state_env_only833834835def test_set_streaming_chunk_size() -> None:836with pl.Config() as cfg:837cfg.set_streaming_chunk_size(8)838assert os.environ.get("POLARS_STREAMING_CHUNK_SIZE") == "8"839840with pytest.raises(ValueError), pl.Config() as cfg:841cfg.set_streaming_chunk_size(0)842843844def test_set_fmt_str_lengths_invalid_length() -> None:845with pl.Config() as cfg:846with pytest.raises(ValueError):847cfg.set_fmt_str_lengths(0)848with pytest.raises(ValueError):849cfg.set_fmt_str_lengths(-2)850851852def test_truncated_rows_cols_values_ascii() -> None:853df = pl.DataFrame({f"c{n}": list(range(-n, 100 - n)) for n in range(10)})854855pl.Config.set_tbl_formatting("UTF8_BORDERS_ONLY", rounded_corners=True)856assert (857str(df) == "shape: (100, 10)\n"858"╭───────────────────────────────────────────────────╮\n"859"│ c0 c1 c2 c3 … c6 c7 c8 c9 │\n"860"│ --- --- --- --- --- --- --- --- │\n"861"│ i64 i64 i64 i64 i64 i64 i64 i64 │\n"862"╞═══════════════════════════════════════════════════╡\n"863"│ 0 -1 -2 -3 … -6 -7 -8 -9 │\n"864"│ 1 0 -1 -2 … -5 -6 -7 -8 │\n"865"│ 2 1 0 -1 … -4 -5 -6 -7 │\n"866"│ 3 2 1 0 … -3 -4 -5 -6 │\n"867"│ 4 3 2 1 … -2 -3 -4 -5 │\n"868"│ … … … … … … … … … │\n"869"│ 95 94 93 92 … 89 88 87 86 │\n"870"│ 96 95 94 93 … 90 89 88 87 │\n"871"│ 97 96 95 94 … 91 90 89 88 │\n"872"│ 98 97 96 95 … 92 91 90 89 │\n"873"│ 99 98 97 96 … 93 92 91 90 │\n"874"╰───────────────────────────────────────────────────╯"875)876with pl.Config(tbl_formatting="ASCII_FULL_CONDENSED"):877assert (878str(df) == "shape: (100, 10)\n"879"+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n"880"| c0 | c1 | c2 | c3 | ... | c6 | c7 | c8 | c9 |\n"881"| --- | --- | --- | --- | | --- | --- | --- | --- |\n"882"| i64 | i64 | i64 | i64 | | i64 | i64 | i64 | i64 |\n"883"+=====================================================+\n"884"| 0 | -1 | -2 | -3 | ... | -6 | -7 | -8 | -9 |\n"885"| 1 | 0 | -1 | -2 | ... | -5 | -6 | -7 | -8 |\n"886"| 2 | 1 | 0 | -1 | ... | -4 | -5 | -6 | -7 |\n"887"| 3 | 2 | 1 | 0 | ... | -3 | -4 | -5 | -6 |\n"888"| 4 | 3 | 2 | 1 | ... | -2 | -3 | -4 | -5 |\n"889"| ... | ... | ... | ... | ... | ... | ... | ... | ... |\n"890"| 95 | 94 | 93 | 92 | ... | 89 | 88 | 87 | 86 |\n"891"| 96 | 95 | 94 | 93 | ... | 90 | 89 | 88 | 87 |\n"892"| 97 | 96 | 95 | 94 | ... | 91 | 90 | 89 | 88 |\n"893"| 98 | 97 | 96 | 95 | ... | 92 | 91 | 90 | 89 |\n"894"| 99 | 98 | 97 | 96 | ... | 93 | 92 | 91 | 90 |\n"895"+-----+-----+-----+-----+-----+-----+-----+-----+-----+"896)897898with pl.Config(tbl_formatting="MARKDOWN"):899df = pl.DataFrame({"b": [b"0tigohij1prisdfj1gs2io3fbjg0pfihodjgsnfbbmfgnd8j"]})900assert (901str(df)902== dedent("""903shape: (1, 1)904| b |905| --- |906| binary |907|---------------------------------|908| b"0tigohij1prisdfj1gs2io3fbjg0… |""").lstrip()909)910911with pl.Config(tbl_formatting="ASCII_MARKDOWN"):912df = pl.DataFrame({"b": [b"0tigohij1prisdfj1gs2io3fbjg0pfihodjgsnfbbmfgnd8j"]})913assert (914str(df)915== dedent("""916shape: (1, 1)917| b |918| --- |919| binary |920|-----------------------------------|921| b"0tigohij1prisdfj1gs2io3fbjg0... |""").lstrip()922)923924925def test_warn_unstable(recwarn: pytest.WarningsRecorder) -> None:926issue_unstable_warning()927assert len(recwarn) == 0928929pl.Config().warn_unstable(True)930931issue_unstable_warning()932assert len(recwarn) == 1933934pl.Config().warn_unstable(False)935936issue_unstable_warning()937assert len(recwarn) == 1938939940@pytest.mark.parametrize(941("environment_variable", "config_setting", "value", "expected"),942[943("POLARS_ENGINE_AFFINITY", "set_engine_affinity", "gpu", "gpu"),944("POLARS_FMT_MAX_COLS", "set_tbl_cols", 12, "12"),945("POLARS_FMT_MAX_ROWS", "set_tbl_rows", 3, "3"),946("POLARS_FMT_STR_LEN", "set_fmt_str_lengths", 42, "42"),947("POLARS_FMT_TABLE_CELL_ALIGNMENT", "set_tbl_cell_alignment", "RIGHT", "RIGHT"),948(949"POLARS_FMT_TABLE_CELL_NUMERIC_ALIGNMENT",950"set_tbl_cell_numeric_alignment",951"RIGHT",952"RIGHT",953),954("POLARS_FMT_TABLE_HIDE_COLUMN_NAMES", "set_tbl_hide_column_names", True, "1"),955(956"POLARS_FMT_TABLE_DATAFRAME_SHAPE_BELOW",957"set_tbl_dataframe_shape_below",958True,959"1",960),961(962"POLARS_FMT_TABLE_FORMATTING",963"set_ascii_tables",964True,965"ASCII_FULL_CONDENSED",966),967(968"POLARS_FMT_TABLE_FORMATTING",969"set_tbl_formatting",970"ASCII_MARKDOWN",971"ASCII_MARKDOWN",972),973(974"POLARS_FMT_TABLE_HIDE_COLUMN_DATA_TYPES",975"set_tbl_hide_column_data_types",976True,977"1",978),979(980"POLARS_FMT_TABLE_HIDE_COLUMN_SEPARATOR",981"set_tbl_hide_dtype_separator",982True,983"1",984),985(986"POLARS_FMT_TABLE_HIDE_DATAFRAME_SHAPE_INFORMATION",987"set_tbl_hide_dataframe_shape",988True,989"1",990),991(992"POLARS_FMT_TABLE_INLINE_COLUMN_DATA_TYPE",993"set_tbl_column_data_type_inline",994True,995"1",996),997("POLARS_STREAMING_CHUNK_SIZE", "set_streaming_chunk_size", 100, "100"),998("POLARS_TABLE_WIDTH", "set_tbl_width_chars", 80, "80"),999("POLARS_VERBOSE", "set_verbose", True, "1"),1000("POLARS_WARN_UNSTABLE", "warn_unstable", True, "1"),1001],1002)1003def test_unset_config_env_vars(1004environment_variable: str, config_setting: str, value: Any, expected: str1005) -> None:1006assert environment_variable in _POLARS_CFG_ENV_VARS10071008with pl.Config(**{config_setting: value}):1009assert os.environ[environment_variable] == expected10101011with pl.Config(**{config_setting: None}): # type: ignore[arg-type]1012assert environment_variable not in os.environ101310141015