From 0720d90e3938e8ae43a9eb5e0e52ce02cc0a4200 Mon Sep 17 00:00:00 2001 From: Gabriel Falcao Date: Fri, 22 Feb 2013 14:48:13 -0500 Subject: [PATCH] you can now specify the output when using `couleur.Shell` --- README.md | 104 ++++++++++--------- couleur/__init__.py | 16 +-- requirements.pip | 6 +- test_couleur.py | 238 +++++++++++++++++++++++--------------------- 4 files changed, 197 insertions(+), 167 deletions(-) diff --git a/README.md b/README.md index 85311c1..6c8c5c9 100644 --- a/README.md +++ b/README.md @@ -22,61 +22,71 @@ unix terminal #### further - >>> import sys, couleur +```python +import sys, couleur - >>> couleur.proxy(sys.stdout).enable() +couleur.proxy(sys.stdout).enable() +print "#{bold}#{blue}#{on:black}This is#{normal} a test" +couleur.proxy(sys.stdout).ignore() - >>> print "#{bold}#{red}#{on:yellow}This is#{normal} a test" - u'\x1b[1;31;43mThis is\x1b[39m a test' - - >>> couleur.proxy(sys.stdout).ignore() - - >>> print "#{bold}#{red}#{on:yellow}This is#{normal} a test" - u'This is a test' - - >>> couleur.proxy(sys.stdout).disable() +print "#{green}#{on:black}This is#{normal} a test" +couleur.proxy(sys.stdout).disable() +``` ### dynamic methods couleur has a syntax sugar that is semantically nice: - import couleur - sh = couleur.Shell(indent=4) +```python +import couleur +sh = couleur.Shell(indent=4) - sh.bold_black_on_white('Nice highlight') - # prints '\033[47m\033[1m\033[30mNice highlight\033[0m' +sh.bold_black_on_white('Nice highlight') +# prints '\033[47m\033[1m\033[30mNice highlight\033[0m' - sh.indent() - # will increase a internal indentation factor in couleur.Shell instance +sh.indent() +# will increase a internal indentation factor in couleur.Shell instance - sh.green('Just green') - # prints indented as well ' \033[32mJust Green\033[0m' +sh.green('Just green') +# prints indented as well ' \033[32mJust Green\033[0m' - sh.dedent() - # will decrease that indentation factor (above) +sh.dedent() +# will decrease that indentation factor (above) - # syntax sugar - sh.green_and_nornal_and_blue('this will be printed in green| and |this in blue') - # see: '\033[32mthis will be printed in green\033[0m and \033[34mthis in blue\033[0m' +# syntax sugar +sh.green_and_normal_and_blue('this will be printed in green| and |this in blue') +# see: '\033[32mthis will be printed in green\033[0m and \033[34mthis in blue\033[0m' +``` couleur can overwrite output, so that you can make things like printing progress bars, show percentage and so on: - #!/usr/bin/env python - # -*- coding: utf-8 -*- +```python +import time +import couleur - import time - import couleur +shell = couleur.Shell(linebreak=True, bold=True) - shell = couleur.Shell(linebreak=True, bold=True) +for num in range(101): + if num == 0: + print - for num in range(101): - if num == 0: - print + shell.yellow_and_red("Downloading file: |%d%%" % num, replace=True) + time.sleep(0.01) - shell.yellow_and_red("Downloading file: |%d%%" % num, replace=True) - time.sleep(0.05) +shell.white_and_green("Downloading file: |DONE!", replace=True) +``` + +#### Writing to other streams + +Simply pass the output as first argument of the `Shell` +```python +import couleur + +with open('output.log', 'w') as output: + shell = couleur.Shell(output, linebreak=True, bold=True) + shell.white_and_green("done with | Some task") +``` - shell.white_and_green("Downloading file: |DONE!", replace=True) ### furthermore @@ -107,13 +117,12 @@ Available colors: Example chaining modifiers: - #!/usr/bin/env python - # -*- coding: utf-8 -*- +```python +import couleur - import couleur - - shell = couleur.Shell(linebreak=True) - shell.bold_italic_underline_yellow_on_black_and_italic_black_on_white("WOO| HOO") +shell = couleur.Shell(linebreak=True) +shell.bold_italic_underline_green_on_black_and_italic_black_on_white("WOO| HOO") +``` ## free software @@ -124,10 +133,13 @@ To contribute back with this project, all you need to do is write code, and test You will need to install [nose](http://somethingaboutorange.com/mrl/projects/nose/0.11.3/ "a pretty way for testing in python"). And run: - user@machine:~/Projects$ git clone git://github.com/gabrielfalcao/couleur.git - user@machine:~/Projects$ cd couleur - user@machine:~/Projects$ pip install -r requirements.pip - user@machine:~/Projects/couleur$ make + +```shell +user@machine:~/Projects$ git clone git://github.com/gabrielfalcao/couleur.git +user@machine:~/Projects$ cd couleur +user@machine:~/Projects$ pip install -r requirements.pip +user@machine:~/Projects/couleur$ make +``` ## nomenclature @@ -135,6 +147,6 @@ And run: ## Licensing - Copyright (c) 2010 Gabriel Falcão + Copyright (c) 2010-2013 Gabriel Falcão Licensed under Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0.html diff --git a/couleur/__init__.py b/couleur/__init__.py index 2e72f8a..3e6cca6 100644 --- a/couleur/__init__.py +++ b/couleur/__init__.py @@ -216,7 +216,7 @@ _sep2 = u'_and_' class Shell(object): - def __init__(self, indent=2, linebreak=False, bold=False, + def __init__(self, output=None, indent=2, linebreak=False, bold=False, disabled=not SUPPORTS_ANSI): self._indentation_factor = indent self._indent = 0 @@ -224,6 +224,8 @@ class Shell(object): self._bold = bold self._in_format = False self._disabled = disabled + self.output = output or sys.stdout + if disabled: self._backcolors = empty() self._forecolors = empty() @@ -279,8 +281,8 @@ class Shell(object): def dec(z, replace=False): pre = unicode(replace and self._modifiers.up or u'') - sys.stdout.write(pre) - sys.stdout.write(string % unicode(z.decode('utf-8'))) + self.output.write(pre) + self.output.write(string % unicode(z.decode('utf-8'))) return dec @@ -295,19 +297,19 @@ class Shell(object): string = string.replace(ur'\|', unique) parts = string.split(ur"|") if replace: - sys.stdout.write(self._modifiers.up) + self.output.write(self._modifiers.up) if self._indent: - sys.stdout.write(u' ' * self._indent) + self.output.write(u' ' * self._indent) if self._bold: - sys.stdout.write(self._modifiers.bold) + self.output.write(self._modifiers.bold) for part, output in zip(parts, printers): output(part.replace(unique, ur"|")) if self._linebreak: - sys.stdout.write(u"\n") + self.output.write(u"\n") self._in_format = False diff --git a/requirements.pip b/requirements.pip index 2828450..7fb6aac 100644 --- a/requirements.pip +++ b/requirements.pip @@ -1,4 +1,4 @@ distribute==0.6.27 -nose==1.1.2 -sure==1.0.6 -wsgiref==0.1.2 +nose==1.2.1 +steadymark==0.4.5 +sure==1.1.7 diff --git a/test_couleur.py b/test_couleur.py index 78c0cad..789d518 100644 --- a/test_couleur.py +++ b/test_couleur.py @@ -16,154 +16,170 @@ # along with this program. If not, see . import sys from StringIO import StringIO -from nose.tools import with_setup, assert_equals - -from couleur import ansify +from sure import scenario, action_for, expect from couleur import Shell -def prepare_stdout(): - if isinstance(sys.stdout, StringIO): - del sys.stdout - std = StringIO() - sys.stdout = std +def prepare_output_stream(context): + context.output = StringIO() -def assert_stdout(expected): - string = sys.stdout.getvalue() - assert_equals(string, expected) + @action_for(context, provides=['check_output']) + def check_output(expected): + context.output.getvalue().should.equal(expected) -def test_ansify(): - "couleur.ansify wraps ansi code for proper pythonic output" - assert_equals(ansify(0), '\033[0m') + @action_for(context, provides=['check_output']) + def make_shell(*args, **kw): + context.sh = Shell(context.output, *args, **kw) -@with_setup(prepare_stdout) -def test_output_black_foreground(): + +def test_default_output(): + "Shell.output should default to sys.stdout" + sh = Shell() + expect(sh.output).to.equal(sys.stdout) + + +@scenario(prepare_output_stream) +def test_output_black_foreground(spec): "Test output: black foreground" - sh = Shell() - sh.black("Hello Black!") - assert_stdout('\033[30mHello Black!\033[0m') + spec.make_shell() + spec.sh.black("Hello Black!") + spec.check_output('\033[30mHello Black!\033[0m') -@with_setup(prepare_stdout) -def test_output_black_foreground_on_white_background(): + +@scenario(prepare_output_stream) +def test_output_black_foreground_on_white_background(spec): "Test output: black foreground on white background" - sh = Shell() - sh.black_on_white("Hello Black!") - assert_stdout('\033[47m\033[30mHello Black!\033[0m') + spec.make_shell() + spec.sh.black_on_white("Hello Black!") + spec.check_output('\033[47m\033[30mHello Black!\033[0m') -@with_setup(prepare_stdout) -def test_output_green_foreground(): + +@scenario(prepare_output_stream) +def test_output_green_foreground(spec): "Test output: green foreground" - sh = Shell() - sh.green("Hello World!") - assert_stdout('\033[32mHello World!\033[0m') + spec.make_shell() + spec.sh.green("Hello World!") + spec.check_output('\033[32mHello World!\033[0m') -@with_setup(prepare_stdout) -def test_mixed_output(): + +@scenario(prepare_output_stream) +def test_mixed_output(spec): "green_and_red_and_white is a valid call" - sh = Shell() - sh.green_and_red_and_white("Hello |World |for you!") - assert_stdout( + spec.make_shell() + spec.sh.green_and_red_and_white("Hello |World |for you!") + spec.check_output( '\033[32mHello \033[0m\033[31mWorld \033[0m\033[37mfor you!\033[0m' ) -@with_setup(prepare_stdout) -def test_mixed_output_with_escaped_separator(): + +@scenario(prepare_output_stream) +def test_mixed_output_with_escaped_separator(spec): "green_and_red_on_yellow works is a valid call" - sh = Shell() - sh.green_and_red_on_yellow("Hello |World \|for you!") - assert_stdout( + spec.make_shell() + spec.sh.green_and_red_on_yellow("Hello |World \|for you!") + spec.check_output( '\033[32mHello \033[0m\033[43m\033[31mWorld |for you!\033[0m' ) -@with_setup(prepare_stdout) -def test_mixed_output_with_backgrounds(): + +@scenario(prepare_output_stream) +def test_mixed_output_with_backgrounds(spec): "green_on_magenta_and_red_and_white_on_blue is a valid call" - sh = Shell() - sh.green_on_magenta_and_red_and_white_on_blue("Hello |World |for you!") - assert_stdout('\033[45m\033[32mHello \033[0m\033[31mWorld \033[0m\033[44m\033[37mfor you!\033[0m' + spec.make_shell() + spec.sh.green_on_magenta_and_red_and_white_on_blue("Hello |World |for you!") + spec.check_output('\033[45m\033[32mHello \033[0m\033[31mWorld \033[0m\033[44m\033[37mfor you!\033[0m' ) -@with_setup(prepare_stdout) -def test_indent(): + +@scenario(prepare_output_stream) +def test_indent(spec): "indentation" - sh = Shell(indent=4, linebreak=True) - sh.normal_on_blue("Hello") - sh.indent() - sh.normal_on_red("World") - assert_stdout('\033[44m\033[39mHello\033[0m\n \033[41m\033[39mWorld\033[0m\n') + spec.make_shell(indent=4, linebreak=True) + spec.sh.normal_on_blue("Hello") + spec.sh.indent() + spec.sh.normal_on_red("World") + spec.check_output('\033[44m\033[39mHello\033[0m\n \033[41m\033[39mWorld\033[0m\n') -@with_setup(prepare_stdout) -def test_dedent(): + +@scenario(prepare_output_stream) +def test_dedent(spec): "de-indentation" - sh = Shell(indent=4, linebreak=True) - sh.indent() - sh.normal_on_blue("Hello") - sh.dedent() - sh.normal_on_red("World") - assert_stdout(' \033[44m\033[39mHello\033[0m\n\033[41m\033[39mWorld\033[0m\n') + spec.make_shell(indent=4, linebreak=True) + spec.sh.indent() + spec.sh.normal_on_blue("Hello") + spec.sh.dedent() + spec.sh.normal_on_red("World") + spec.check_output(' \033[44m\033[39mHello\033[0m\n\033[41m\033[39mWorld\033[0m\n') -@with_setup(prepare_stdout) -def test_bold(): + +@scenario(prepare_output_stream) +def test_bold(spec): "bold text" - sh = Shell(bold=True) - sh.normal_on_blue("Hello") - sh.normal_on_red("World") - assert_stdout('\033[1m\033[44m\033[39mHello\033[0m\033[1m\033[41m\033[39mWorld\033[0m') + spec.make_shell(bold=True) + spec.sh.normal_on_blue("Hello") + spec.sh.normal_on_red("World") + spec.check_output('\033[1m\033[44m\033[39mHello\033[0m\033[1m\033[41m\033[39mWorld\033[0m') -@with_setup(prepare_stdout) -def test_bold_inline(): + +@scenario(prepare_output_stream) +def test_bold_inline(spec): "bold text with inline call" - sh = Shell() - sh.bold_normal_on_blue("Hello") - sh.bold_normal_on_red("World") - assert_stdout('\033[44m\033[1m\033[39mHello\033[0m\033[41m\033[1m\033[39mWorld\033[0m') + spec.make_shell() + spec.sh.bold_normal_on_blue("Hello") + spec.sh.bold_normal_on_red("World") + spec.check_output('\033[44m\033[1m\033[39mHello\033[0m\033[41m\033[1m\033[39mWorld\033[0m') -@with_setup(prepare_stdout) -def test_update_shell(): + +@scenario(prepare_output_stream) +def test_update_shell(spec): "updating the shell, replacing the last output" - sh = Shell(indent=6) - sh.yellow("Yellow") - sh.indent() - sh.red("Red", True) - assert_stdout('\033[33mYellow\033[0m\r\033[A \033[31mRed\033[0m') + spec.make_shell(indent=6) + spec.sh.yellow("Yellow") + spec.sh.indent() + spec.sh.red("Red", True) + spec.check_output('\033[33mYellow\033[0m\r\033[A \033[31mRed\033[0m') -@with_setup(prepare_stdout) -def test_update_shell_mixed_with_linebreak(): + +@scenario(prepare_output_stream) +def test_update_shell_mixed_with_linebreak(spec): "updating the shell with mixed output and linebreak enabled" - sh = Shell(linebreak=True) - sh.yellow("Yellow") - sh.yellow_and_normal_and_red("Yellow| and |Red", True) - sh.green("Green") - assert_stdout('\033[33mYellow\033[0m\n\r\033[A\033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\n\033[32mGreen\033[0m\n') + spec.make_shell(linebreak=True) + spec.sh.yellow("Yellow") + spec.sh.yellow_and_normal_and_red("Yellow| and |Red", True) + spec.sh.green("Green") + spec.check_output('\033[33mYellow\033[0m\n\r\033[A\033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\n\033[32mGreen\033[0m\n') -@with_setup(prepare_stdout) -def test_update_shell_mixed_with_indentation(): + +@scenario(prepare_output_stream) +def test_update_shell_mixed_with_indentation(spec): "updating the shell with mixed output and indentation" - sh = Shell(linebreak=True) - sh.yellow("Yellow") - sh.indent() - sh.yellow_and_normal_and_red("Yellow| and |Red", True) - sh.dedent() - sh.green("Green") - assert_stdout('\033[33mYellow\033[0m\n\r\033[A \033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\n\033[32mGreen\033[0m\n') + spec.make_shell(linebreak=True) + spec.sh.yellow("Yellow") + spec.sh.indent() + spec.sh.yellow_and_normal_and_red("Yellow| and |Red", True) + spec.sh.dedent() + spec.sh.green("Green") + spec.check_output('\033[33mYellow\033[0m\n\r\033[A \033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\n\033[32mGreen\033[0m\n') -@with_setup(prepare_stdout) -def test_update_shell_mixed_with_bold(): + +@scenario(prepare_output_stream) +def test_update_shell_mixed_with_bold(spec): "updating the shell with mixed output and bold enabled" - sh = Shell(bold=True) - sh.yellow("Yellow") - sh.yellow_and_normal_and_red("Yellow| and |Red", True) - sh.green("Green") - assert_stdout('\033[1m\033[33mYellow\033[0m\r\033[A\033[1m\033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\033[1m\033[32mGreen\033[0m') + spec.make_shell(bold=True) + spec.sh.yellow("Yellow") + spec.sh.yellow_and_normal_and_red("Yellow| and |Red", True) + spec.sh.green("Green") + spec.check_output('\033[1m\033[33mYellow\033[0m\r\033[A\033[1m\033[33mYellow\033[0m\033[39m and \033[0m\033[31mRed\033[0m\033[1m\033[32mGreen\033[0m') -@with_setup(prepare_stdout) -def test_dont_print_colors_if_set_as_disabled(): + +@scenario(prepare_output_stream) +def test_dont_print_colors_if_set_as_disabled(spec): "disable colors" - sh = Shell(disabled=True, linebreak=True, bold=True) - sh.yellow("Yellow") - sh.indent() - sh.indent() - sh.yellow_and_red_on_yellow("Yellow| and Red", True) - sh.dedent() - sh.green("Green") - assert_stdout('Yellow\n\r\033[A Yellow and Red\n Green\n') + spec.make_shell(disabled=True, linebreak=True, bold=True) + spec.sh.yellow("Yellow") + spec.sh.indent() + spec.sh.indent() + spec.sh.yellow_and_red_on_yellow("Yellow| and Red", True) + spec.sh.dedent() + spec.sh.green("Green") + spec.check_output('Yellow\n\r\033[A Yellow and Red\n Green\n')