Skip to content

REF: avoid circular imports in formats #55467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pandas/core/indexes/multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@
lexsort_indexer,
)

from pandas.io.formats.printing import pprint_thing
from pandas.io.formats.printing import (
get_adjustment,
pprint_thing,
)

if TYPE_CHECKING:
from pandas import (
Expand Down Expand Up @@ -1437,8 +1440,6 @@ def format(
)

if adjoin:
from pandas.io.formats.format import get_adjustment

adj = get_adjustment()
return adj.adjoin(space, *result_levels).split("\n")
else:
Expand Down
6 changes: 4 additions & 2 deletions pandas/io/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
is_integer,
is_list_like,
)
from pandas.core.dtypes.generic import ABCMultiIndex

from pandas.core.indexes.api import MultiIndex
from pandas.core.shared_docs import _shared_docs

_VALID_URLS = set(uses_relative + uses_netloc + uses_params)
Expand All @@ -91,6 +91,8 @@
WriteBuffer,
)

from pandas import MultiIndex


@dataclasses.dataclass
class IOArgs:
Expand Down Expand Up @@ -1228,7 +1230,7 @@ def is_potential_multi_index(

return bool(
len(columns)
and not isinstance(columns, MultiIndex)
and not isinstance(columns, ABCMultiIndex)
and all(isinstance(c, tuple) for c in columns if c not in list(index_col))
)

Expand Down
73 changes: 4 additions & 69 deletions pandas/io/formats/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from collections.abc import (
Generator,
Hashable,
Iterable,
Mapping,
Sequence,
)
Expand All @@ -26,7 +25,6 @@
Final,
cast,
)
from unicodedata import east_asian_width

import numpy as np

Expand Down Expand Up @@ -226,7 +224,7 @@ def __init__(
float_format = get_option("display.float_format")
self.float_format = float_format
self.dtype = dtype
self.adj = get_adjustment()
self.adj = printing.get_adjustment()

self._chk_truncate()

Expand Down Expand Up @@ -347,69 +345,6 @@ def to_string(self) -> str:
return str("".join(result))


class _TextAdjustment:
def __init__(self) -> None:
self.encoding = get_option("display.encoding")

def len(self, text: str) -> int:
return len(text)

def justify(self, texts: Any, max_len: int, mode: str = "right") -> list[str]:
return printing.justify(texts, max_len, mode=mode)

def adjoin(self, space: int, *lists, **kwargs) -> str:
return printing.adjoin(
space, *lists, strlen=self.len, justfunc=self.justify, **kwargs
)


class _EastAsianTextAdjustment(_TextAdjustment):
def __init__(self) -> None:
super().__init__()
if get_option("display.unicode.ambiguous_as_wide"):
self.ambiguous_width = 2
else:
self.ambiguous_width = 1

# Definition of East Asian Width
# https://unicode.org/reports/tr11/
# Ambiguous width can be changed by option
self._EAW_MAP = {"Na": 1, "N": 1, "W": 2, "F": 2, "H": 1}

def len(self, text: str) -> int:
"""
Calculate display width considering unicode East Asian Width
"""
if not isinstance(text, str):
return len(text)

return sum(
self._EAW_MAP.get(east_asian_width(c), self.ambiguous_width) for c in text
)

def justify(
self, texts: Iterable[str], max_len: int, mode: str = "right"
) -> list[str]:
# re-calculate padding space per str considering East Asian Width
def _get_pad(t):
return max_len - self.len(t) + len(t)

if mode == "left":
return [x.ljust(_get_pad(x)) for x in texts]
elif mode == "center":
return [x.center(_get_pad(x)) for x in texts]
else:
return [x.rjust(_get_pad(x)) for x in texts]


def get_adjustment() -> _TextAdjustment:
use_east_asian_width = get_option("display.unicode.east_asian_width")
if use_east_asian_width:
return _EastAsianTextAdjustment()
else:
return _TextAdjustment()


def get_dataframe_repr_params() -> dict[str, Any]:
"""Get the parameters used to repr(dataFrame) calls using DataFrame.to_string.

Expand Down Expand Up @@ -529,7 +464,7 @@ def __init__(

self.tr_frame = self.frame
self.truncate()
self.adj = get_adjustment()
self.adj = printing.get_adjustment()

def get_strcols(self) -> list[list[str]]:
"""
Expand Down Expand Up @@ -1789,13 +1724,13 @@ def _make_fixed_width(
strings: list[str],
justify: str = "right",
minimum: int | None = None,
adj: _TextAdjustment | None = None,
adj: printing._TextAdjustment | None = None,
) -> list[str]:
if len(strings) == 0 or justify == "all":
return strings

if adj is None:
adjustment = get_adjustment()
adjustment = printing.get_adjustment()
else:
adjustment = adj

Expand Down
79 changes: 74 additions & 5 deletions pandas/io/formats/printing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
TypeVar,
Union,
)
from unicodedata import east_asian_width

from pandas._config import get_option

from pandas.core.dtypes.inference import is_sequence

from pandas.io.formats.console import get_console_size

EscapeChars = Union[Mapping[str, str], Iterable[str]]
_KT = TypeVar("_KT")
_VT = TypeVar("_VT")
Expand All @@ -42,7 +45,7 @@ def adjoin(space: int, *lists: list[str], **kwargs) -> str:
function used to justify str. Needed for unicode handling.
"""
strlen = kwargs.pop("strlen", len)
justfunc = kwargs.pop("justfunc", justify)
justfunc = kwargs.pop("justfunc", _adj_justify)

newLists = []
lengths = [max(map(strlen, x)) + space for x in lists[:-1]]
Expand All @@ -57,7 +60,7 @@ def adjoin(space: int, *lists: list[str], **kwargs) -> str:
return "\n".join("".join(lines) for lines in toJoin)


def justify(texts: Iterable[str], max_len: int, mode: str = "right") -> list[str]:
def _adj_justify(texts: Iterable[str], max_len: int, mode: str = "right") -> list[str]:
"""
Perform ljust, center, rjust against string or list-like
"""
Expand Down Expand Up @@ -314,9 +317,6 @@ def format_object_summary(
-------
summary string
"""
from pandas.io.formats.console import get_console_size
from pandas.io.formats.format import get_adjustment

display_width, _ = get_console_size()
if display_width is None:
display_width = get_option("display.width") or 80
Expand Down Expand Up @@ -501,3 +501,72 @@ class PrettyDict(dict[_KT, _VT]):

def __repr__(self) -> str:
return pprint_thing(self)


class _TextAdjustment:
def __init__(self) -> None:
self.encoding = get_option("display.encoding")

def len(self, text: str) -> int:
return len(text)

def justify(self, texts: Any, max_len: int, mode: str = "right") -> list[str]:
"""
Perform ljust, center, rjust against string or list-like
"""
if mode == "left":
return [x.ljust(max_len) for x in texts]
elif mode == "center":
return [x.center(max_len) for x in texts]
else:
return [x.rjust(max_len) for x in texts]

def adjoin(self, space: int, *lists, **kwargs) -> str:
return adjoin(space, *lists, strlen=self.len, justfunc=self.justify, **kwargs)


class _EastAsianTextAdjustment(_TextAdjustment):
def __init__(self) -> None:
super().__init__()
if get_option("display.unicode.ambiguous_as_wide"):
self.ambiguous_width = 2
else:
self.ambiguous_width = 1

# Definition of East Asian Width
# https://unicode.org/reports/tr11/
# Ambiguous width can be changed by option
self._EAW_MAP = {"Na": 1, "N": 1, "W": 2, "F": 2, "H": 1}

def len(self, text: str) -> int:
"""
Calculate display width considering unicode East Asian Width
"""
if not isinstance(text, str):
return len(text)

return sum(
self._EAW_MAP.get(east_asian_width(c), self.ambiguous_width) for c in text
)

def justify(
self, texts: Iterable[str], max_len: int, mode: str = "right"
) -> list[str]:
# re-calculate padding space per str considering East Asian Width
def _get_pad(t):
return max_len - self.len(t) + len(t)

if mode == "left":
return [x.ljust(_get_pad(x)) for x in texts]
elif mode == "center":
return [x.center(_get_pad(x)) for x in texts]
else:
return [x.rjust(_get_pad(x)) for x in texts]


def get_adjustment() -> _TextAdjustment:
use_east_asian_width = get_option("display.unicode.east_asian_width")
if use_east_asian_width:
return _EastAsianTextAdjustment()
else:
return _TextAdjustment()
2 changes: 1 addition & 1 deletion pandas/tests/io/formats/test_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def test_repr_truncation(self):
r = repr(df)
r = r[r.find("\n") + 1 :]

adj = fmt.get_adjustment()
adj = printing.get_adjustment()

for line, value in zip(r.split("\n"), df["B"]):
if adj.len(value) + 1 > max_len:
Expand Down
11 changes: 5 additions & 6 deletions pandas/tests/io/formats/test_printing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import pandas as pd

from pandas.io.formats import printing
import pandas.io.formats.format as fmt


def test_adjoin():
Expand Down Expand Up @@ -48,7 +47,7 @@ def test_adjoin_unicode(self):
adjoined = printing.adjoin(2, *data)
assert adjoined == expected

adj = fmt._EastAsianTextAdjustment()
adj = printing._EastAsianTextAdjustment()

expected = """あ dd ggg
b ええ hhh
Expand All @@ -73,7 +72,7 @@ def test_adjoin_unicode(self):
assert adj.len(cols[2]) == 26

def test_justify(self):
adj = fmt._EastAsianTextAdjustment()
adj = printing._EastAsianTextAdjustment()

def just(x, *args, **kwargs):
# wrapper to test single str
Expand All @@ -95,7 +94,7 @@ def just(x, *args, **kwargs):
assert just("パンダ", 10, mode="right") == " パンダ"

def test_east_asian_len(self):
adj = fmt._EastAsianTextAdjustment()
adj = printing._EastAsianTextAdjustment()

assert adj.len("abc") == 3
assert adj.len("abc") == 3
Expand All @@ -106,11 +105,11 @@ def test_east_asian_len(self):
assert adj.len("パンダpanda") == 10

def test_ambiguous_width(self):
adj = fmt._EastAsianTextAdjustment()
adj = printing._EastAsianTextAdjustment()
assert adj.len("¡¡ab") == 4

with cf.option_context("display.unicode.ambiguous_as_wide", True):
adj = fmt._EastAsianTextAdjustment()
adj = printing._EastAsianTextAdjustment()
assert adj.len("¡¡ab") == 6

data = [["あ", "b", "c"], ["dd", "ええ", "ff"], ["ggg", "¡¡ab", "いいい"]]
Expand Down