Skip to content

Commit

Permalink
cqlsh: Add type-based left/right alignment
Browse files Browse the repository at this point in the history
Patch by Arun Ganesh; reviewed by TBD for CASSANDRA-19150
  • Loading branch information
arkn98 committed Mar 31, 2024
1 parent 98ca5f8 commit 5917403
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 27 deletions.
2 changes: 1 addition & 1 deletion pylib/cqlshlib/cqlshmain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ def print_formatted_result(self, formatted_names, formatted_values, with_header,

# print row data
for row in formatted_values:
line = ' | '.join(col.rjust(w, color=self.color) for (col, w) in zip(row, widths))
line = ' | '.join(col.just(w, color=self.color) for (col, w) in zip(row, widths))
self.writeresult(' ' + line)

if tty:
Expand Down
18 changes: 15 additions & 3 deletions pylib/cqlshlib/displaying.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import Enum
from collections import defaultdict

RED = '\033[0;1;31m'
Expand All @@ -27,12 +28,17 @@
ANSI_RESET = '\033[0m'


def colorme(bval, colormap, colorkey):
class Alignment(Enum):
LEFT = 1
RIGHT = 2


def colorme(bval, colormap, colorkey, alignment=None):
if colormap is NO_COLOR_MAP:
return bval
if colormap is None:
colormap = DEFAULT_VALUE_COLORS
return FormattedValue(bval, colormap[colorkey] + bval + colormap['reset'])
return FormattedValue(bval, colormap[colorkey] + bval + colormap['reset'], alignment=alignment)


def get_str(val):
Expand All @@ -43,7 +49,7 @@ def get_str(val):

class FormattedValue:

def __init__(self, strval, coloredval=None, displaywidth=None):
def __init__(self, strval, coloredval=None, displaywidth=None, alignment=Alignment.RIGHT):
self.strval = strval
if coloredval is None:
coloredval = strval
Expand All @@ -53,6 +59,7 @@ def __init__(self, strval, coloredval=None, displaywidth=None):
# displaywidth is useful for display of special unicode characters
# with
self.displaywidth = displaywidth
self.alignment = alignment

def __len__(self):
return len(self.strval)
Expand All @@ -63,6 +70,11 @@ def _pad(self, width, fill=' '):
else:
return ''

def just(self, width, fill=' ', color=False):
if self.alignment == Alignment.LEFT:
return self.ljust(width, fill, color)
return self.rjust(width, fill, color)

def ljust(self, width, fill=' ', color=False):
"""
Similar to self.strval.ljust(width), but takes expected terminal
Expand Down
49 changes: 26 additions & 23 deletions pylib/cqlshlib/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from cassandra.cqltypes import EMPTY
from cassandra.util import datetime_from_timestamp
from .displaying import colorme, get_str, FormattedValue, DEFAULT_VALUE_COLORS, NO_COLOR_MAP
from .displaying import colorme, get_str, FormattedValue, DEFAULT_VALUE_COLORS, NO_COLOR_MAP, Alignment

UNICODE_CONTROLCHARS_RE = re.compile(r'[\x00-\x1f\x7f-\xa0]')
CONTROLCHARS_RE = re.compile(r'[\x00-\x1f\x7f-\xff]')
Expand Down Expand Up @@ -65,7 +65,7 @@ def format_by_type(val, cqltype, encoding, colormap=None, addcolor=False,
if nullval is None:
nullval = default_null_placeholder
if val is None:
return colorme(nullval, colormap, 'error')
return colorme(nullval, colormap, 'error', alignment=Alignment.LEFT)
if addcolor is False:
colormap = empty_colormap
elif colormap is None:
Expand All @@ -80,7 +80,7 @@ def format_by_type(val, cqltype, encoding, colormap=None, addcolor=False,
boolean_styles=boolean_styles)


def color_text(bval, colormap, displaywidth=None):
def color_text(bval, colormap, displaywidth=None, alignment=None):
# note that here, we render natural backslashes as just backslashes,
# in the same color as surrounding text, when using color. When not
# using color, we need to double up the backslashes, so it's not
Expand All @@ -95,7 +95,7 @@ def color_text(bval, colormap, displaywidth=None):
coloredval = colormap['text'] + bits_to_turn_red_re.sub(tbr, bval) + colormap['reset']
if colormap['text']:
displaywidth -= bval.count(r'\\')
return FormattedValue(bval, coloredval, displaywidth)
return FormattedValue(bval, coloredval, displaywidth, alignment)


DEFAULT_NANOTIME_FORMAT = '%H:%M:%S.%N'
Expand Down Expand Up @@ -209,7 +209,7 @@ def format_value_default(val, colormap, **_):
val = str(val)
escapedval = val.replace('\\', '\\\\')
bval = CONTROLCHARS_RE.sub(_show_control_chars, escapedval)
return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap)
return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap, alignment=Alignment.RIGHT)


# Mapping cql type base names ("int", "map", etc) to formatter functions,
Expand Down Expand Up @@ -249,46 +249,46 @@ def __str__(self):
@formatter_for('BlobType')
def format_value_blob(val, colormap, **_):
bval = '0x' + val.hex()
return colorme(bval, colormap, 'blob')
return colorme(bval, colormap, 'blob', alignment=Alignment.LEFT)


formatter_for('bytearray')(format_value_blob)
formatter_for('buffer')(format_value_blob)
formatter_for('blob')(format_value_blob)


def format_python_formatted_type(val, colormap, color, quote=False):
def format_python_formatted_type(val, colormap, color, quote=False, alignment=None):
bval = str(val)
if quote:
bval = "'%s'" % bval
return colorme(bval, colormap, color)
return colorme(bval, colormap, color, alignment)


@formatter_for('Decimal')
def format_value_decimal(val, float_precision, colormap, decimal_sep=None, thousands_sep=None, **_):
if (decimal_sep and decimal_sep != '.') or thousands_sep:
return format_floating_point_type(val, colormap, float_precision, decimal_sep, thousands_sep)
return format_python_formatted_type(val, colormap, 'decimal')
return format_python_formatted_type(val, colormap, 'decimal', alignment=Alignment.RIGHT)


@formatter_for('UUID')
def format_value_uuid(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'uuid')
return format_python_formatted_type(val, colormap, 'uuid', alignment=Alignment.LEFT)


formatter_for('timeuuid')(format_value_uuid)


@formatter_for('inet')
def formatter_value_inet(val, colormap, quote=False, **_):
return format_python_formatted_type(val, colormap, 'inet', quote=quote)
return format_python_formatted_type(val, colormap, 'inet', quote=quote, alignment=Alignment.LEFT)


@formatter_for('bool')
def format_value_boolean(val, colormap, boolean_styles=None, **_):
if boolean_styles:
val = boolean_styles[0] if val else boolean_styles[1]
return format_python_formatted_type(val, colormap, 'boolean')
return format_python_formatted_type(val, colormap, 'boolean', alignment=Alignment.LEFT)


formatter_for('boolean')(format_value_boolean)
Expand Down Expand Up @@ -318,7 +318,7 @@ def format_floating_point_type(val, colormap, float_precision, decimal_sep=None,
if decimal_sep:
bval = bval.replace('.', decimal_sep)

return colorme(bval, colormap, 'float')
return colorme(bval, colormap, 'float', alignment=Alignment.RIGHT)


formatter_for('float')(format_floating_point_type)
Expand All @@ -329,7 +329,7 @@ def format_integer_type(val, colormap, thousands_sep=None, **_):
# base-10 only for now; support others?
bval = format_integer_with_thousands_sep(val, thousands_sep) if thousands_sep else str(val)
bval = str(bval)
return colorme(bval, colormap, 'int')
return colorme(bval, colormap, 'int', alignment=Alignment.RIGHT)


def format_integer_with_thousands_sep(val, thousands_sep=','):
Expand Down Expand Up @@ -357,7 +357,7 @@ def format_value_timestamp(val, colormap, date_time_format, quote=False, **_):

if quote:
bval = "'%s'" % bval
return colorme(bval, colormap, 'timestamp')
return colorme(bval, colormap, 'timestamp', alignment=Alignment.LEFT)


formatter_for('timestamp')(format_value_timestamp)
Expand Down Expand Up @@ -399,17 +399,18 @@ def round_microseconds(val):

@formatter_for('Date')
def format_value_date(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'date')
return format_python_formatted_type(val, colormap, 'date', alignment=Alignment.LEFT)


@formatter_for('Time')
def format_value_time(val, colormap, **_):
return format_python_formatted_type(val, colormap, 'time')
return format_python_formatted_type(val, colormap, 'time', alignment=Alignment.LEFT)


@formatter_for('Duration')
def format_value_duration(val, colormap, **_):
return format_python_formatted_type(duration_as_str(val.months, val.days, val.nanoseconds), colormap, 'duration')
return format_python_formatted_type(duration_as_str(val.months, val.days, val.nanoseconds),
colormap,'duration', alignment=Alignment.RIGHT)


def duration_as_str(months, days, nanoseconds):
Expand Down Expand Up @@ -485,7 +486,9 @@ def format_value_text(val, encoding, colormap, quote=False, **_):
bval = escapedval
if quote:
bval = "'{}'".format(bval)
return bval if colormap is NO_COLOR_MAP else color_text(bval, colormap, wcwidth.wcswidth(bval))
if colormap is NO_COLOR_MAP:
return bval
return color_text(bval, colormap, wcwidth.wcswidth(bval), alignment=Alignment.LEFT)


# name alias
Expand All @@ -510,7 +513,7 @@ def format_simple_collection(val, cqltype, lbracket, rbracket, encoding,
for s in (lbracket, ', ', rbracket)]
coloredval = lb + sep.join(sval.coloredval for sval in subs) + rb
displaywidth = 2 * len(subs) + sum(sval.displaywidth for sval in subs)
return FormattedValue(bval, coloredval, displaywidth)
return FormattedValue(bval, coloredval, displaywidth, alignment=Alignment.RIGHT)


@formatter_for('list')
Expand Down Expand Up @@ -562,7 +565,7 @@ def subformat(v, t):
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
displaywidth = 4 * len(subs) + sum(k.displaywidth + v.displaywidth for (k, v) in subs)
return FormattedValue(bval, coloredval, displaywidth)
return FormattedValue(bval, coloredval, displaywidth, alignment=Alignment.RIGHT)


formatter_for('OrderedDict')(format_value_map)
Expand All @@ -575,7 +578,7 @@ def format_value_utype(val, cqltype, encoding, colormap, date_time_format, float
decimal_sep, thousands_sep, boolean_styles, **_):
def format_field_value(v, t):
if v is None:
return colorme(nullval, colormap, 'error')
return colorme(nullval, colormap, 'error', alignment=Alignment.RIGHT)
return format_value(v, cqltype=t, encoding=encoding, colormap=colormap,
date_time_format=date_time_format, float_precision=float_precision,
nullval=nullval, quote=True, decimal_sep=decimal_sep,
Expand All @@ -596,7 +599,7 @@ def format_field_name(name):
+ comma.join(k.coloredval + colon + v.coloredval for (k, v) in subs) \
+ rb
displaywidth = 4 * len(subs) + sum(k.displaywidth + v.displaywidth for (k, v) in subs)
return FormattedValue(bval, coloredval, displaywidth)
return FormattedValue(bval, coloredval, displaywidth, alignment=Alignment.RIGHT)


NANOS_PER_MICRO = 1000
Expand Down

0 comments on commit 5917403

Please sign in to comment.