colorama icon indicating copy to clipboard operation
colorama copied to clipboard

Handle multiple calls to init() and deinit() properly

Open thp opened this issue 6 years ago • 6 comments

Looking at this code: https://github.com/tartley/colorama/blob/master/colorama/initialise.py#L23

It looks like the code currently doesn't properly handle calling init() multiple times, I'd expect the init() function to set some internal state to know that it is already inited (maybe with a counter, in case nested init()/deinit() pairs should be supported?). Same with calling deinit(), it doesn't seem to always restore things properly.

Example code:

import colorama
colorama.init()
colorama.init()
colorama.deinit()

(of course, it might not be that obvious, but imagine two libraries want to initialize colorama in some situations)

Maybe it should also support nested init()/deinit() pairs:

import colorama
colorama.init()  # Initialized now
colorama.init()  # Still initialized, maybe increase "refcount"/"initcount"
colorama.deinit()  # Still initialized, because init was called twice
colorama.deinit()  # Actually deinitialize, as the "refcount"/"initcount" will go to zero

Similar logic probably applies to reinit(), not sure.

thp avatar Nov 02 '18 20:11 thp

I'm using colorama 0.3.9 with python 2.7.6 (32-bit) on win10 64-bit, and for some reason I have to call colorama.init() twice in a row to get colors to actually work; if I only call it once, then the color codes get literally displayed rather than the colors actually changing. So that probably needs to be understood and fixed before you go changing the behavior of consecutive calls to colorama.init().

c0d3h4x0r avatar Nov 02 '18 20:11 c0d3h4x0r

To @thp, Nov 2, 2018

  1. So, what is your solution for making deinit() work?
  2. What are 'refinit' and 'initcount'? I couldn't find them anywhere.

AlkisPis avatar Nov 10 '19 08:11 AlkisPis

Something like that (assuming thread safety is not an issue, otherwise protect it with threading.Lock):

# The original init function
def real_init():
    ...
# The original deinit function
def real_deinit():
    ...

_init_counter = 0
def init():
    global _init_counter
    if _init_counter == 0:
        real_init()
    _init_counter += 1

def deinit():
    global _init_counter
    _init_counter -= 1
    assert _init_counter >= 0, 'deinit() called without calling init()'
    if _init_counter == 0:
        real_deinit()

thp avatar Nov 13 '19 06:11 thp

real_init


@thp, thank you. I patched 'initialised.py' as you suggested and run the following test :

print "Start ID",id(sys.stdout) # 20500600
colorama.init()
print "ID after init 1",id(sys.stdout) # 25212144
colorama.init()
print "ID after init 2",id(sys.stdout) # 25212144
colorama.deinit()
print "ID after deinit 1",id(sys.stdout) # = 25212144
try:
  colorama.deinit() # Not accepted
  print "ID after deinit 2",id(sys.stdout)
  colorama.deinit()
  print "ID after deinit 3",id(sys.stdout)
except:
  pass

The differences from the test with the original 'initialise.py', and as you can see, are the following:

  1. A second init() call does not change the stdout ID (from the first call)
  2. Only one deinit() call is allowed (the next one throws en exception).
  3. This single deinit() call does not restore the original stdout ID! It is as if it is ignored.

Note: After posting the above reply, I tried another test: I called init() once only and then deinit(). Guess what: The deinit() call threw an exception!

Anyway, why should we care to use deinit() at all. I never do it and never had a problem. Is there perhaps a hidden problem that it's not good for the system?

AlkisPis avatar Nov 13 '19 10:11 AlkisPis

Possibly a duplication of issue #145.

hlovdal avatar Feb 04 '20 11:02 hlovdal

I did a wrapper on colorama in order to make a customized logger

For some reason calling .init() straight ahead performs worst than calling .deinit() first and then .init()

SamuelJansen avatar Jan 04 '21 14:01 SamuelJansen