Blackhole927
Blackhole927

Reputation: 19

How to add extra text to a value mid-print

I am modifying the print function like:

cg = "\u001b[32;1m"
stop = "\u001b[0m"

_print = print
def print(*args, **kw):
  #color the word "CommandGeek" green
  args = (arg.replace('CommandGeek', f'{cg}CommandGeek{stop}') if isinstance(arg, str) else arg for arg in args)

  _print(*args, **kw)

What I want to do is add some code like so:

cg = "\u001b[32;1m"
stop = "\u001b[0m"

_print = print
def print(*args, **kw):
   #color the word "CommandGeek" green
   args = (arg.replace('CommandGeek', f'{cg}CommandGeek{stop}') if isinstance(arg, str) else arg for arg in args)
   
   #Color entire the message green if "CommandGeek:" is in it.
   if "CommandGeek:" in args:
      args = cg + args + stop

  _print(*args, **kw)

But this doesn't work, and it just prints nothing. I also tried to just put in the if statement without the modification to args, and it still did nothing. What is happening here, and how do I change the value that will be printed if "CommandGeek:" is in the message?

Upvotes: 0

Views: 207

Answers (2)

hc_dev
hc_dev

Reputation: 9377

Without advanced constructs like generator, list-comprehension or similar.

Understanding the asterisk (unpacking) and tuples

First we need to understand that *args is a parameter named args defined as varargs (multiple parameters as tuple). See What does ** (double star/asterisk) and * (star/asterisk) do for parameters?

See also this answer which explains it very well:

Using *args in the function signature packs all of the positional arguments into a single tuple args. If you then say *args in another function call within your the function body, it spreads it back into multiple arguments, [..]

Why it is used here?

This is used in your function definition def print(*args, **kw): to mimic the same signature (parameters) as the built-in print function, originally in Python. With the re-definition _print = print before this can be seen as interceptor.

The effect will be a print function that automatically highlights words or output using ANSI-colors (ANSI-escape code) when certain keyword (here "CommandGeek" found).

Your task

Implement the comments (tasks) inside the function body:

   #color the word "CommandGeek" green
       # locate the word
       # surround it with ANSI-color code

   #Color entire the message green if "CommandGeek:" is in it.
       # locate the word
       # surround the entire message with ANSI-color code

Your input (the message(s) containing words or commands) is given by parameter *args which we know is to pack all positional arguments into a tuple (varargs). The tuple is named args.

GREEN = "\u001b[32;1m"
RESET = "\u001b[0m"
WORD = "CommandGeek"

# a testable function with debug-prints
def color(*args):
    print(len(args))
    colored = []
    for a in args:
        s = str(a)
        if s.__contains__(WORD):
            print("Found it in: " + s)
            colored.append(s.replace(WORD, f"{GREEN}{WORD}{RESET}"))
        else:
            colored.append(a)
    return colored

colored = color('Hello', 'World', 'CommandGeek')
print(colored)  # prints the list or tuple (not colored)
print(*colored)  # prints all with color if found (because unpacked)

Prints (bold was originally green in console):

3

Found it in: CommandGeek

Hello World CommandGeek

Now we have one word colored. Next let's color the entire message (= all args) if at least one contains the word.

GREEN = "\u001b[32;1m"
RESET = "\u001b[0m"
WORD = "CommandGeek"

def contains_word(*args):
    for a in args:
        s = str(a)
        if s.__contains__(WORD):
            return True
    return False

tuple = ('Hello', 'World', 'CommandGeek')  # define a tuple
should_color = contains_word(*tuple)  # unpack a tuple as multiple args (varargs)
if should_color:
    print(GREEN, *tuple, RESET)  # prints all elements in color (unpacked)
else:
    print(*tuple)  # prints the tuple (unpacked, not colored)

Prints (bold was originally green in console):

** Hello World CommandGeek**


Update: complete solution (fixed leading space)

GREEN = "\u001b[32;1m"
RESET = "\u001b[0m"
KEYWORD = "CommandGeek"


def color_word(*args):
    colored = []
    for a in args:
        if isinstance(a, str):
            colored.append(a.replace(KEYWORD, f"{GREEN}{KEYWORD}{RESET}"))
        else:
            colored.append(a)
    return colored

print(*color_word('Hello', 1, 'World', True, '_CommandGeek_'))


def contains_word(*args):
    for a in args:
        if str(a).__contains__("CommandGeek"):
            return True
    return False

def color_message(*args):
    if contains_word(*args):  # unpack a tuple as multiple args (varargs)
        # surround tuples with color-code and reset-code
        colored = (f"{GREEN}{args[0]}",) + args[1:]  # prepend to first element (new colored tuple)
        colored = colored[0:-1] + (f"{colored[-1]}{RESET}",)  # and append on last element (replace colored tuple)
        return colored
    return args

print(*color_message('Hello', 1, 'World', True, '_CommandGeek_'))
print(*color_message('Hello: CommandGeek'))

Prints on a console supporting ANSI-colors:

screenshot of console output


Further reading

Upvotes: 1

Random Davis
Random Davis

Reputation: 6857

When you do the replacement, you're setting args to a generator expression, since you're using generator expression syntax. args needs to be a tuple. All you have to do is add the word tuple to the front of the statement and it will work just fine:

args = tuple(arg.replace('CommandGeek', f'{cg}CommandGeek{stop}') if isinstance(arg, str) else arg for arg in args)

To test this I did:

cg = "_Testing_"
stop = "_123_"

_print = print
def print(*args, **kw):
   #color the word "CommandGeek" green
   args = tuple(arg.replace('CommandGeek', f'{cg}CommandGeek{stop}') if isinstance(arg, str) else arg for arg in args)
   
   #Color entire the message green if "CommandGeek:" is in it.
   if "CommandGeek:" in args:
      args = cg + args + stop
   
   _print(*args, **kw)
  
print('Test CommandGeek Test','CommandGeek','BlahBlah')

Output:

Test _Testing_CommandGeek_123_ Test _Testing_CommandGeek_123_ BlahBlah

Upvotes: 0

Related Questions