Skip to content
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

use colorama to support ANSI color codes on Windows #296

Merged
merged 7 commits into from
Nov 11, 2018
Merged

Conversation

cfbao
Copy link
Contributor

@cfbao cfbao commented Oct 25, 2018

close #295

@cfbao
Copy link
Contributor Author

cfbao commented Oct 27, 2018

This adds an optional dependency on colorama on Windows for colored stdout reports.

Copy link
Owner

@thp thp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good change!

Should we add this to setup.py, so that it's also installed automatically on Windows? (note that I'm using .extend(), just in case we have more Windows-specific dependencies in the future, for a single item, .append() would of course also work)

After the line:

m['install_requires'] = ['minidb', 'PyYAML', 'requests', 'keyring', 'pycodestyle', 'appdirs', 'lxml']

Add this:

if sys.platform == 'win32':
    m['install_requires'].extend(['colorama'])

@cfbao
Copy link
Contributor Author

cfbao commented Nov 1, 2018

Thanks! Windows dependencies added in setup.py.

Copy link
Owner

@thp thp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to look at the other code, so I didn't see this in the first review -- colorama.init() should at the moment probably only be called once.

self._has_color = sys.stdout.isatty() and self.config.get('color', False)
if sys.platform == 'win32' and self._has_color:
import colorama
colorama.init()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code in https://github.com/tartley/colorama/blob/master/colorama/initialise.py#L23, it looks like the colorama module doesn't support calling init() multiple times (well, at least it will probably re-wrap the streams or something, and "uniniting" might no longer be possible).

I've filed tartley/colorama#205 in colorama, maybe it can/should be fixed there (too?).

For now (and to support old installations of the colorama module), maybe add a global/static member _colorama_inited and only import and call colorama.init() once.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a static member _colorama_inited as requested. But I'm afraid it's a bit futile given this flaw in colorama.

I noticed that, even without this patch, it's possible to get proper colored stdout report on Windows by just having a browser job, if colorama is already installed. This is because, BrowserJob imports requests_html which imports pyppeteer which imports tqdm which imports colorama (if available) and calls init().

I really don't know how to properly handle this situation, or if it's even meaningful to handle it, when any package in urlwatch's dependency tree can potentially call colorama.init(), or user may write hooks.py that calls it.

Earlier related issues: tartley/colorama#145, tartley/colorama#128, tartley/colorama#182

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super dirty idea: We call colorama.init() outside of the reporter, somewhere in urlwatch's main function (before any other code like imports or hooks gets run) and then we monkey-patch the module to have a no-op init function?

colorama.init()

def dummy_init(*args, **kwargs):
    ...

colorama.init = dummy_init

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... dirty it is.
This works for the current situation. However, it wouldn't work if imported modules do something like from colorama import init.

Copy link
Contributor Author

@cfbao cfbao Nov 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... of course it works for from colorama import init. What was I thinking...

But then what do we do about colorama.deinit? What if external code called init (nothing happens) then called deinit (color gone), after that StdoutReporter starts working and we have no color and a mess of ANSI codes! (Windows only of course)
Or... what if someone uses urlwatch as a library, then the main function has no effect... Aggghhh!

How about we don't call init and don't change sys.stdout, and just directly use colorama.AnsiToWin32 inside of StdoutReporter.submit (like one example in colorama's README)?
This way, other modules can do whatever they like, we just ensure that StdoutReporter.submit has the right color, and no side effect on sys.stdout.

cfbao added 2 commits November 4, 2018 01:22
Directly use `colorama.AnsiToWin32` in `StdoutReporter`
with a custom `print` function. Avoid issues with
`colorama.init`. See tartley/colorama#205.
@thp
Copy link
Owner

thp commented Nov 11, 2018

Ready to be merged once the merge conflict (due to merging of the enum34 change) is resolved.

@thp thp merged commit fde3bb6 into thp:master Nov 11, 2018
@cfbao cfbao deleted the win-color branch November 11, 2018 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Stdout colors not working on Windows
2 participants