colorful icon indicating copy to clipboard operation
colorful copied to clipboard

Support indexing and slicing

Open rsalmei opened this issue 4 years ago • 16 comments

Hello,

You say in the readme that:

It correctly supports all str() methods including len().

But it seems it does not support basic indexing or slicing:

image

Could you please fix or implement it? I need this kind of functionality to be able to support colors in my progress bar project. I was wondering if I'd implement colorizing and len and indexing and slicing myself, but thankfully found this project of yours, it is missing little to fit my need perfectly. Thanks.

My project is in: https://github.com/rsalmei/alive-progress alive-progress

rsalmei avatar Sep 04 '19 06:09 rsalmei

Good catch! Give me a day or two to find some spare time! If you'd want to implement it by yourself - I'm happy to review PRs :tada:

timofurrer avatar Sep 04 '19 08:09 timofurrer

Great, thanks!

Do you realize it's not that easy to implement? Imagine in my example, a red Hello and an orange World. The char at index 1 should be a red e. And a slice between 4:7 should be a red o, a space and an orange W, and etc. All slices should maintain the styling...

I'm currently busy with my progress bar, but I will contribute in another time, thank you!

rsalmei avatar Sep 04 '19 14:09 rsalmei

Yeah, that's what I figured ... let's see if that's possible some how ;)

timofurrer avatar Sep 04 '19 15:09 timofurrer

I've just released an alpha release v0.6.0a1 which supports the basics of indexing and slicing. There are some limitations though: see the README changes

Could you please verify if that implementation if enough for your project?

timofurrer avatar Sep 04 '19 18:09 timofurrer

Wow, that was fast! Thank you. I'll verify and report back to you!

rsalmei avatar Sep 05 '19 00:09 rsalmei

Hey, it seems to be working real nice! It just broke with unicode characters, but should be easy to fix:

In [30]: colorful.red('♫♬')
Out[30]: <colorful.core.ColorfulString at 0x10e403a10>

In [31]: print(_)
♫♬

In [32]: _[1]
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-32-ac1337140e92> in <module>()
----> 1 _[1]

/Users/rogerio/.pyenv/versions/2.7.15/lib/python2.7/site-packages/colorful/core.pyc in __getitem__(self, item)
    357                 else:
    358                     if start <= current_orig_string_idx < stop:
--> 359                         sliced_styled_string += self.orig_string[current_orig_string_idx]
    360                         step_counter = step - 1
    361

UnicodeDecodeError: 'ascii' codec can't decode byte 0x99 in position 0: ordinal not in range(128)

rsalmei avatar Sep 05 '19 01:09 rsalmei

I got another of the same error with disable:

In [34]: colorful.disable()

In [35]: print(_30)
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-35-16e467c1c773> in <module>()
----> 1 print(_30)

/Users/rogerio/.pyenv/versions/2.7.15/lib/python2.7/site-packages/colorful/core.pyc in __str__(self)
    238         def __str__(self):
    239             if self.colorful_ctx.colormode == terminal.NO_COLORS:
--> 240                 return self.orig_string.encode(DEFAULT_ENCODING)
    241             else:
    242                 return self.styled_string.encode(DEFAULT_ENCODING)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 0: ordinal not in range(128)

ps.: testing with python 2.7 to be more restrictive, it probably works in 3.7.

rsalmei avatar Sep 05 '19 01:09 rsalmei

Are you sure that's a bug in colorful? The string with the unicode char is that really a Python 2 unicode string? Try using the u"" string literal of import unicode_literals from __future__.

image

timofurrer avatar Sep 05 '19 06:09 timofurrer

image

timofurrer avatar Sep 05 '19 06:09 timofurrer

Oh yes, my mistake! I'm sorry for that. It does work for these cases!

Unfortunately it is still missing something, but I'm not sure what. I'm my progress bar, I need to generate some cool lines like these:

In [17]: blank = ' '; gap = 2; block_size = 10; content = 'abc'

In [18]: ''.join(chain.from_iterable(zip(repeat(blank * gap),
    ...:                                 map(lambda c: c * block_size, content))))
Out[18]: '  aaaaaaaaaa  bbbbbbbbbb  cccccccccc'

So, I've generated blocks of each character, with a certain gap between them.

But with a colorful string, I get:

In [19]: content = colorful.red('♫♬')

In [20]: ''.join(chain.from_iterable(zip(repeat(blank * gap),
    ...:                                 map(lambda c: c * block_size, content))))
Out[20]: '  \x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b  [[[[[[[[[[  3333333333  8888888888  ;;;;;;;;;;  2222222222  ;;;;;;;;;;  2222222222  5555555555  5555555555  ;;;;;;;;;;  0000000000  ;;;;;;;;;;  0000000000  mmmmmmmmmm  ♫♫♫♫♫♫♫♫♫♫  ♬♬♬♬♬♬♬♬♬♬  \x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b\x1b  [[[[[[[[[[  3333333333  9999999999  mmmmmmmmmm'

So maybe the __iter__ would have to return colored chars too.

rsalmei avatar Sep 06 '19 05:09 rsalmei

Also, I think I need object.reversed, as I use reversed() in some computations. Look:

In [24]: content = reversed(colorful.red('♫♬'))

In [25]: ''.join(chain.from_iterable(zip(repeat(blank * gap),
    ...:                                 map(lambda c: c * block_size, content))))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-8facd9412722> in <module>
      1 ''.join(chain.from_iterable(zip(repeat(blank * gap),
----> 2                                 map(lambda c: c * block_size, content))))

TypeError: sequence item 1: expected str instance, ColorfulString found

~I don't really get where or why this error was triggered...~ Actually I do! The reverse protocol knows how to handle strings, any other objects has to implement the support via the reversed method...

rsalmei avatar Sep 06 '19 06:09 rsalmei

I'll have a look at the first problem.

Also, I think I need object.reversed, as I use reversed() in some computations.

Reversing the string could be a little harder ... Do you need the coloring there? If not you could just access the original string of the ColorfulString object and reverse that - as a workaround at least.

timofurrer avatar Sep 08 '19 08:09 timofurrer

Hey, yes I do need color in reverse strings... I have a plethora of factory methods to generate the various animations, and if one calls for a different scrolling direction, I need to reverse the original string, including colors. So if we have: red a yellow b reset, I'd expect the reverse to return yellow b red a reset.

Regarding the first issue, I think it's just the iter missing, which should return red a reset, and yellow b reset. Actually, it should respect the len protocol, if the object has len=2, it should return 2 iterations in iter.

Do you think it's doable?

rsalmei avatar Sep 10 '19 01:09 rsalmei

This Issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days

github-actions[bot] avatar Nov 09 '19 02:11 github-actions[bot]

Hello @timofurrer, how are you?

Hey, there's someone interested in helping me with this coloring, maybe he could help you here too! I'm going to link this issue there, thank you for all the help you already did!

rsalmei avatar Jul 18 '20 05:07 rsalmei

As I explained in my issue there:

The problem lies on having to have string-like objects that support: len, indexing, slicing, concatenation and reverse without losing color information!! 😨

rsalmei avatar Jul 18 '20 06:07 rsalmei