diff options
-rw-r--r-- | CHANGES | 4 | ||||
-rw-r--r-- | pygments/__init__.py | 2 | ||||
-rw-r--r-- | pygments/cmdline.py | 84 | ||||
-rw-r--r-- | pygments/formatters/html.py | 12 | ||||
-rw-r--r-- | pygments/formatters/latex.py | 23 | ||||
-rw-r--r-- | pygments/lexers/textfmts.py | 2 | ||||
-rw-r--r-- | pygments/sphinxext.py | 4 | ||||
-rwxr-xr-x | setup.py | 2 | ||||
-rw-r--r-- | tests/test_cmdline.py | 217 | ||||
-rw-r--r-- | tests/test_html_formatter.py | 27 | ||||
-rw-r--r-- | tests/test_util.py | 68 |
11 files changed, 302 insertions, 143 deletions
@@ -2,9 +2,9 @@ Pygments changelog ================== Issue numbers refer to the tracker at -<http://bitbucket.org/birkenfeld/pygments-main/issues>, +<https://bitbucket.org/birkenfeld/pygments-main/issues>, pull request numbers to the requests at -<http://bitbucket.org/birkenfeld/pygments-main/pull-requests/merged>. +<https://bitbucket.org/birkenfeld/pygments-main/pull-requests/merged>. Version 2.0.1 diff --git a/pygments/__init__.py b/pygments/__init__.py index dc377369..c1ea2af4 100644 --- a/pygments/__init__.py +++ b/pygments/__init__.py @@ -26,7 +26,7 @@ :license: BSD, see LICENSE for details. """ -__version__ = '2.0.1' +__version__ = '2.1a0' __docformat__ = 'restructuredtext' __all__ = ['lex', 'format', 'highlight'] diff --git a/pygments/cmdline.py b/pygments/cmdline.py index 0f629ece..9d6742a3 100644 --- a/pygments/cmdline.py +++ b/pygments/cmdline.py @@ -19,7 +19,7 @@ from pygments import __version__, highlight from pygments.util import ClassNotFound, OptionError, docstring_headline, \ guess_decode, guess_decode_from_terminal, terminal_encoding from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \ - get_lexer_for_filename, find_lexer_class, TextLexer + get_lexer_for_filename, find_lexer_class_for_filename, TextLexer from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter from pygments.formatters import get_all_formatters, get_formatter_by_name, \ get_formatter_for_filename, find_formatter_class, \ @@ -30,7 +30,7 @@ from pygments.styles import get_all_styles, get_style_by_name USAGE = """\ Usage: %s [-l <lexer> | -g] [-F <filter>[:<options>]] [-f <formatter>] - [-O <options>] [-P <option=value>] [-s] [-o <outfile>] [<infile>] + [-O <options>] [-P <option=value>] [-s] [-v] [-o <outfile>] [<infile>] %s -S <style> -f <formatter> [-a <arg>] [-O <options>] [-P <option=value>] %s -L [<which> ...] @@ -90,6 +90,9 @@ waiting to process the entire file. This only works for stdin, and is intended for streaming input such as you get from 'tail -f'. Example usage: "tail -f sql.log | pygmentize -s -l sql" +The -v option prints a detailed traceback on unhandled exceptions, +which is useful for debugging and bug reports. + The -h option prints this help. The -V option prints the package version. """ @@ -100,7 +103,7 @@ def _parse_options(o_strs): if not o_strs: return opts for o_str in o_strs: - if not o_str: + if not o_str.strip(): continue o_args = o_str.split(',') for o_arg in o_args: @@ -132,7 +135,7 @@ def _parse_filters(f_strs): def _print_help(what, name): try: if what == 'lexer': - cls = find_lexer_class(name) + cls = get_lexer_by_name(name) print("Help on the %s lexer:" % cls.name) print(dedent(cls.__doc__)) elif what == 'formatter': @@ -143,8 +146,10 @@ def _print_help(what, name): cls = find_filter_class(name) print("Help on the %s filter:" % name) print(dedent(cls.__doc__)) - except AttributeError: + return 0 + except (AttributeError, ValueError): print("%s not found!" % what, file=sys.stderr) + return 1 def _print_list(what): @@ -247,8 +252,7 @@ def main_inner(popts, args, usage): print(usage, file=sys.stderr) return 2 - _print_help(what, name) - return 0 + return _print_help(what, name) # parse -O options parsed_opts = _parse_options(O_opts) @@ -271,13 +275,9 @@ def main_inner(popts, args, usage): # handle ``pygmentize -N`` infn = opts.pop('-N', None) if infn is not None: - try: - lexer = get_lexer_for_filename(infn, **parsed_opts) - except ClassNotFound as err: - lexer = TextLexer() - except OptionError as err: - print('Error:', err, file=sys.stderr) - return 1 + lexer = find_lexer_class_for_filename(infn) + if lexer is None: + lexer = TextLexer print(lexer.aliases[0]) return 0 @@ -301,12 +301,7 @@ def main_inner(popts, args, usage): print(err, file=sys.stderr) return 1 - arg = a_opt or '' - try: - print(fmter.get_style_defs(arg)) - except Exception as err: - print('Error:', err, file=sys.stderr) - return 1 + print(fmter.get_style_defs(a_opt or '')) return 0 # if no -S is given, -a is not allowed @@ -341,7 +336,7 @@ def main_inner(popts, args, usage): if '-s' in opts: print('Error: -s option not usable when input file specified', file=sys.stderr) - return 1 + return 2 infn = args[0] try: @@ -387,6 +382,20 @@ def main_inner(popts, args, usage): except ClassNotFound: lexer = TextLexer(**parsed_opts) + else: # -s option needs a lexer with -l + if not lexer: + print('Error: when using -s a lexer has to be selected with -l', + file=sys.stderr) + return 2 + + # process filters + for fname, fopts in F_opts: + try: + lexer.add_filter(fname, **fopts) + except ClassNotFound as err: + print('Error:', err, file=sys.stderr) + return 1 + # select formatter outfn = opts.pop('-o', None) fmter = opts.pop('-f', None) @@ -429,7 +438,7 @@ def main_inner(popts, args, usage): # provide coloring under Windows, if possible if not outfn and sys.platform in ('win32', 'cygwin') and \ - fmter.name in ('Terminal', 'Terminal256'): + fmter.name in ('Terminal', 'Terminal256'): # pragma: no cover # unfortunately colorama doesn't support binary streams on Py3 if sys.version_info > (3,): from pygments.util import UnclosingTextIOWrapper @@ -452,24 +461,12 @@ def main_inner(popts, args, usage): right = escapeinside[1] lexer = LatexEmbeddedLexer(left, right, lexer) - # process filters - for fname, fopts in F_opts: - try: - lexer.add_filter(fname, **fopts) - except ClassNotFound as err: - print('Error:', err, file=sys.stderr) - return 1 - # ... and do it! if '-s' not in opts: # process whole input as per normal... highlight(code, lexer, fmter, outfile) return 0 else: - if not lexer: - print('Error: when using -s a lexer has to be selected with -l', - file=sys.stderr) - return 1 # line by line processing of stdin (eg: for 'tail -f')... try: while 1: @@ -485,7 +482,8 @@ def main_inner(popts, args, usage): highlight(line, lexer, fmter, outfile) if hasattr(outfile, 'flush'): outfile.flush() - except KeyboardInterrupt: + return 0 + except KeyboardInterrupt: # pragma: no cover return 0 @@ -496,7 +494,7 @@ def main(args=sys.argv): usage = USAGE % ((args[0],) * 6) try: - popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:hVHgs") + popts, args = getopt.getopt(args[1:], "l:f:F:o:O:P:LS:a:N:vhVHgs") except getopt.GetoptError: print(usage, file=sys.stderr) return 2 @@ -504,6 +502,18 @@ def main(args=sys.argv): try: return main_inner(popts, args, usage) except Exception: + if '-v' in dict(popts): + print(file=sys.stderr) + print('*' * 65, file=sys.stderr) + print('An unhandled exception occurred while highlighting.', + file=sys.stderr) + print('Please report the whole traceback to the issue tracker at', + file=sys.stderr) + print('<https://bitbucket.org/birkenfeld/pygments-main/issues>.', + file=sys.stderr) + print('*' * 65, file=sys.stderr) + print(file=sys.stderr) + raise import traceback info = traceback.format_exception(*sys.exc_info()) msg = info[-1].strip() @@ -513,4 +523,6 @@ def main(args=sys.argv): print(file=sys.stderr) print('*** Error while highlighting:', file=sys.stderr) print(msg, file=sys.stderr) + print('*** If this is a bug you want to report, please rerun with -v.', + file=sys.stderr) return 1 diff --git a/pygments/formatters/html.py b/pygments/formatters/html.py index 2c6de4ca..f15edc7e 100644 --- a/pygments/formatters/html.py +++ b/pygments/formatters/html.py @@ -36,21 +36,11 @@ _escape_html_table = { ord("'"): u''', } + def escape_html(text, table=_escape_html_table): """Escape &, <, > as well as single and double quotes for HTML.""" return text.translate(table) -def get_random_id(): - """Return a random id for javascript fields.""" - from random import random - from time import time - try: - from hashlib import sha1 as sha - except ImportError: - import sha - sha = sha.new - return sha('%s|%s' % (random(), time())).hexdigest() - def _get_ttype_class(ttype): fname = STANDARD_TYPES.get(ttype) diff --git a/pygments/formatters/latex.py b/pygments/formatters/latex.py index 7a4eeca8..bc8b07e1 100644 --- a/pygments/formatters/latex.py +++ b/pygments/formatters/latex.py @@ -360,7 +360,7 @@ class LatexFormatter(Formatter): start += value[i] value = value[len(start):] - start = escape_tex(start, self.commandprefix) + start = escape_tex(start, cp) # ... but do not escape inside comment. value = start + value @@ -370,26 +370,26 @@ class LatexFormatter(Formatter): in_math = False for i, part in enumerate(parts): if not in_math: - parts[i] = escape_tex(part, self.commandprefix) + parts[i] = escape_tex(part, cp) in_math = not in_math value = '$'.join(parts) elif self.escapeinside: text = value value = '' - while len(text) > 0: + while text: a, sep1, text = text.partition(self.left) - if len(sep1) > 0: + if sep1: b, sep2, text = text.partition(self.right) - if len(sep2) > 0: - value += escape_tex(a, self.commandprefix) + b + if sep2: + value += escape_tex(a, cp) + b else: - value += escape_tex(a + sep1 + b, self.commandprefix) + value += escape_tex(a + sep1 + b, cp) else: - value = value + escape_tex(a, self.commandprefix) + value += escape_tex(a, cp) else: - value = escape_tex(value, self.commandprefix) + value = escape_tex(value, cp) elif ttype not in Token.Escape: - value = escape_tex(value, self.commandprefix) + value = escape_tex(value, cp) styles = [] while ttype is not Token: try: @@ -423,8 +423,7 @@ class LatexFormatter(Formatter): class LatexEmbeddedLexer(Lexer): - r""" - + """ This lexer takes one lexer as argument, the lexer for the language being formatted, and the left and right delimiters for escaped text. diff --git a/pygments/lexers/textfmts.py b/pygments/lexers/textfmts.py index 189d334a..b37421a2 100644 --- a/pygments/lexers/textfmts.py +++ b/pygments/lexers/textfmts.py @@ -102,7 +102,7 @@ class GettextLexer(RegexLexer): (r'^(")([A-Za-z-]+:)(.*")$', bygroups(String, Name.Property, String)), (r'^".*"$', String), - (r'^(msgid|msgid_plural|msgstr)(\s+)(".*")$', + (r'^(msgid|msgid_plural|msgstr|msgctxt)(\s+)(".*")$', bygroups(Name.Variable, Text, String)), (r'^(msgstr\[)(\d)(\])(\s+)(".*")$', bygroups(Name.Variable, Number.Integer, Name.Variable, Text, String)), diff --git a/pygments/sphinxext.py b/pygments/sphinxext.py index 82bc6543..85a434ad 100644 --- a/pygments/sphinxext.py +++ b/pygments/sphinxext.py @@ -133,8 +133,8 @@ class PygmentsDoc(Directive): if isinstance(docstring, bytes): docstring = docstring.decode('utf8') heading = cls.__name__ - out.append(FMTERDOC % (heading, ', '.join(data[1]) or 'None', - ', '.join(data[2]).replace('*', '\\*') or 'None', + out.append(FMTERDOC % (heading, ', '.join(data[2]) or 'None', + ', '.join(data[3]).replace('*', '\\*') or 'None', docstring)) return ''.join(out) @@ -54,7 +54,7 @@ else: setup( name = 'Pygments', - version = '2.0.1', + version = '2.1a0', url = 'http://pygments.org/', license = 'BSD License', author = 'Georg Brandl', diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py index 64c4245e..da6b2bec 100644 --- a/tests/test_cmdline.py +++ b/tests/test_cmdline.py @@ -11,32 +11,44 @@ from __future__ import print_function import io import os +import re import sys import tempfile import unittest -from pygments import highlight, cmdline -from pygments.util import StringIO, BytesIO - import support +from pygments import cmdline, highlight +from pygments.util import BytesIO, StringIO + TESTFILE, TESTDIR = support.location(__file__) +TESTCODE = '''\ +def func(args): + pass +''' -def run_cmdline(*args): +def run_cmdline(*args, **kwds): + saved_stdin = sys.stdin saved_stdout = sys.stdout saved_stderr = sys.stderr if sys.version_info > (3,): + stdin_buffer = BytesIO() stdout_buffer = BytesIO() stderr_buffer = BytesIO() + new_stdin = sys.stdin = io.TextIOWrapper(stdin_buffer, 'utf-8') new_stdout = sys.stdout = io.TextIOWrapper(stdout_buffer, 'utf-8') new_stderr = sys.stderr = io.TextIOWrapper(stderr_buffer, 'utf-8') else: + stdin_buffer = new_stdin = sys.stdin = StringIO() stdout_buffer = new_stdout = sys.stdout = StringIO() stderr_buffer = new_stderr = sys.stderr = StringIO() + new_stdin.write(kwds.get('stdin', '')) + new_stdin.seek(0, 0) try: - ret = cmdline.main(["pygmentize"] + list(args)) + ret = cmdline.main(['pygmentize'] + list(args)) finally: + sys.stdin = saved_stdin sys.stdout = saved_stdout sys.stderr = saved_stderr new_stdout.flush() @@ -48,54 +60,18 @@ def run_cmdline(*args): class CmdLineTest(unittest.TestCase): - def test_L_opt(self): - c, o, e = run_cmdline("-L") - self.assertEqual(c, 0) - self.assertTrue("Lexers" in o and "Formatters" in o and - "Filters" in o and "Styles" in o) - c, o, e = run_cmdline("-L", "lexer") - self.assertEqual(c, 0) - self.assertTrue("Lexers" in o and "Formatters" not in o) - c, o, e = run_cmdline("-L", "lexers") - self.assertEqual(c, 0) - - def test_O_opt(self): - filename = TESTFILE - c, o, e = run_cmdline("-Ofull=1,linenos=true,foo=bar", - "-fhtml", filename) - self.assertEqual(c, 0) - self.assertTrue("<html" in o) - self.assertTrue('class="linenos"' in o) + def check_success(self, *cmdline, **kwds): + code, out, err = run_cmdline(*cmdline, **kwds) + self.assertEqual(code, 0) + self.assertEqual(err, '') + return out - def test_P_opt(self): - filename = TESTFILE - c, o, e = run_cmdline("-Pfull", "-Ptitle=foo, bar=baz=,", - "-fhtml", filename) - self.assertEqual(c, 0) - self.assertTrue("<title>foo, bar=baz=,</title>" in o) - - def test_F_opt(self): - filename = TESTFILE - c, o, e = run_cmdline("-Fhighlight:tokentype=Name.Blubb," - "names=TESTFILE filename", - "-fhtml", filename) - self.assertEqual(c, 0) - self.assertTrue('<span class="n-Blubb' in o) - - def test_H_opt(self): - c, o, e = run_cmdline("-H", "formatter", "html") - self.assertEqual(c, 0) - self.assertTrue('HTML' in o) - - def test_S_opt(self): - c, o, e = run_cmdline("-S", "default", "-f", "html", "-O", "linenos=1") - self.assertEqual(c, 0) - - def test_invalid_opts(self): - for opts in [("-L", "-lpy"), ("-L", "-fhtml"), ("-L", "-Ox"), - ("-a",), ("-Sst", "-lpy"), ("-H",), - ("-H", "formatter")]: - self.assertTrue(run_cmdline(*opts)[0] == 2) + def check_failure(self, *cmdline, **kwds): + expected_code = kwds.pop('code', 1) + code, out, err = run_cmdline(*cmdline, **kwds) + self.assertEqual(code, expected_code) + self.assertEqual(out, '') + return err def test_normal(self): # test that cmdline gives the same output as library api @@ -107,11 +83,20 @@ class CmdLineTest(unittest.TestCase): output = highlight(code, PythonLexer(), HtmlFormatter()) - c, o, e = run_cmdline("-lpython", "-fhtml", filename) - + o = self.check_success('-lpython', '-fhtml', filename) self.assertEqual(o, output) - self.assertEqual(e, "") - self.assertEqual(c, 0) + + def test_stdin(self): + o = self.check_success('-lpython', '-fhtml', stdin=TESTCODE) + o = re.sub('<[^>]*>', '', o) + # rstrip is necessary since HTML inserts a \n after the last </div> + self.assertEqual(o.rstrip(), TESTCODE.rstrip()) + + # guess if no lexer given + o = self.check_success('-fhtml', stdin=TESTCODE) + o = re.sub('<[^>]*>', '', o) + # rstrip is necessary since HTML inserts a \n after the last </div> + self.assertEqual(o.rstrip(), TESTCODE.rstrip()) def test_outfile(self): # test that output file works with and without encoding @@ -121,15 +106,100 @@ class CmdLineTest(unittest.TestCase): ['-flatex', '-o', name, TESTFILE], ['-fhtml', '-o', name, '-O', 'encoding=utf-8', TESTFILE]]: try: - self.assertEqual(run_cmdline(*opts)[0], 0) + self.check_success(*opts) finally: os.unlink(name) - def check_failure(self, *cmdline): - c, o, e = run_cmdline(*cmdline) - self.assertEqual(c, 1) - self.assertEqual(o, '') - return e + def test_stream_opt(self): + o = self.check_success('-lpython', '-s', '-fterminal', stdin=TESTCODE) + o = re.sub(r'\x1b\[.*?m', '', o) + self.assertEqual(o.replace('\r\n', '\n'), TESTCODE) + + def test_h_opt(self): + o = self.check_success('-h') + self.assertTrue('Usage:' in o) + + def test_L_opt(self): + o = self.check_success('-L') + self.assertTrue('Lexers' in o and 'Formatters' in o and + 'Filters' in o and 'Styles' in o) + o = self.check_success('-L', 'lexer') + self.assertTrue('Lexers' in o and 'Formatters' not in o) + self.check_success('-L', 'lexers') + + def test_O_opt(self): + filename = TESTFILE + o = self.check_success('-Ofull=1,linenos=true,foo=bar', + '-fhtml', filename) + self.assertTrue('<html' in o) + self.assertTrue('class="linenos"' in o) + + # "foobar" is invalid for a bool option + e = self.check_failure('-Ostripnl=foobar', TESTFILE) + self.assertTrue('Error: Invalid value' in e) + e = self.check_failure('-Ostripnl=foobar', '-lpy') + self.assertTrue('Error: Invalid value' in e) + + def test_P_opt(self): + filename = TESTFILE + o = self.check_success('-Pfull', '-Ptitle=foo, bar=baz=,', + '-fhtml', filename) + self.assertTrue('<title>foo, bar=baz=,</title>' in o) + + def test_F_opt(self): + filename = TESTFILE + o = self.check_success('-Fhighlight:tokentype=Name.Blubb,' + 'names=TESTFILE filename', + '-fhtml', filename) + self.assertTrue('<span class="n-Blubb' in o) + + def test_H_opt(self): + o = self.check_success('-H', 'formatter', 'html') + self.assertTrue('HTML' in o) + o = self.check_success('-H', 'lexer', 'python') + self.assertTrue('Python' in o) + o = self.check_success('-H', 'filter', 'raiseonerror') + self.assertTrue('raiseonerror', o) + e = self.check_failure('-H', 'lexer', 'foobar') + self.assertTrue('not found' in e) + + def test_S_opt(self): + o = self.check_success('-S', 'default', '-f', 'html', '-O', 'linenos=1') + lines = o.splitlines() + for line in lines: + # every line is for a token class + parts = line.split() + self.assertTrue(parts[0].startswith('.')) + self.assertTrue(parts[1] == '{') + if parts[0] != '.hll': + self.assertTrue(parts[-4] == '}') + self.assertTrue(parts[-3] == '/*') + self.assertTrue(parts[-1] == '*/') + self.check_failure('-S', 'default', '-f', 'foobar') + + def test_N_opt(self): + o = self.check_success('-N', 'test.py') + self.assertEqual('python', o.strip()) + o = self.check_success('-N', 'test.unknown') + self.assertEqual('text', o.strip()) + + def test_invalid_opts(self): + for opts in [ + ('-X',), + ('-L', '-lpy'), + ('-L', '-fhtml'), + ('-L', '-Ox'), + ('-S', 'default', '-l', 'py', '-f', 'html'), + ('-S', 'default'), + ('-a', 'arg'), + ('-H',), + (TESTFILE, TESTFILE), + ('-H', 'formatter'), + ('-H', 'foo', 'bar'), + ('-s',), + ('-s', TESTFILE), + ]: + self.check_failure(*opts, code=2) def test_errors(self): # input file not found @@ -145,6 +215,10 @@ class CmdLineTest(unittest.TestCase): e = self.check_failure('-lpython', '-ffoo', TESTFILE) self.assertTrue('Error: no formatter found for name' in e) + # formatter for outfile not found + e = self.check_failure('-ofoo.foo', TESTFILE) + self.assertTrue('Error: no formatter found for file name' in e) + # output file not writable e = self.check_failure('-o', os.path.join('nonexistent', 'dir', 'out.html'), '-lpython', TESTFILE) @@ -156,10 +230,23 @@ class CmdLineTest(unittest.TestCase): self.assertTrue('Error: filter \'foo\' not found' in e) def test_exception(self): - # unexpected exception while highlighting - cmdline.highlight = None # override callable + cmdline.highlight = None # override callable to provoke TypeError try: + # unexpected exception while highlighting e = self.check_failure('-lpython', TESTFILE) + self.assertTrue('*** Error while highlighting:' in e) + self.assertTrue('TypeError' in e) + + # same with -v: should reraise the exception + try: + self.check_failure('-lpython', '-v', TESTFILE) + except Exception: + pass + else: + self.fail('exception not reraised') finally: cmdline.highlight = highlight - self.assertTrue('*** Error while highlighting:' in e) + + def test_parse_opts(self): + self.assertEqual(cmdline._parse_options([' ', 'keyonly,key = value ']), + {'keyonly': True, 'key': 'value'}) diff --git a/tests/test_html_formatter.py b/tests/test_html_formatter.py index 5b1b2576..92a0415b 100644 --- a/tests/test_html_formatter.py +++ b/tests/test_html_formatter.py @@ -68,15 +68,32 @@ class HtmlFormatterTest(unittest.TestCase): pass def test_all_options(self): - for optdict in [dict(nowrap=True), - dict(linenos=True), - dict(linenos=True, full=True), - dict(linenos=True, full=True, noclasses=True)]: - + def check(optdict): outfile = StringIO() fmt = HtmlFormatter(**optdict) fmt.format(tokensource, outfile) + for optdict in [ + dict(nowrap=True), + dict(linenos=True, full=True), + dict(linenos=True, linespans='L'), + dict(hl_lines=[1, 5, 10, 'xxx']), + dict(hl_lines=[1, 5, 10], noclasses=True), + ]: + check(optdict) + + for linenos in [False, 'table', 'inline']: + for noclasses in [False, True]: + for linenospecial in [0, 5]: + for anchorlinenos in [False, True]: + optdict = dict( + linenos=linenos, + noclasses=noclasses, + linenospecial=linenospecial, + anchorlinenos=anchorlinenos, + ) + check(optdict) + def test_linenos(self): optdict = dict(linenos=True) outfile = StringIO() diff --git a/tests/test_util.py b/tests/test_util.py index e480b503..695fb7d2 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -10,12 +10,12 @@ import re import unittest -from pygments import util +from pygments import util, console class FakeLexer(object): def analyse(text): - return float(text) + return text analyse = util.make_analysator(analyse) @@ -40,6 +40,10 @@ class UtilTest(unittest.TestCase): equals(util.get_list_opt({}, 'a', '1 2'), ['1', '2']) raises(util.OptionError, util.get_list_opt, {}, 'a', 1) + equals(util.get_choice_opt({}, 'a', ['foo', 'bar'], 'bar'), 'bar') + equals(util.get_choice_opt({}, 'a', ['foo', 'bar'], 'Bar', True), 'bar') + raises(util.OptionError, util.get_choice_opt, {}, 'a', + ['foo', 'bar'], 'baz') def test_docstring_headline(self): def f1(): @@ -55,9 +59,12 @@ class UtilTest(unittest.TestCase): other text """ + def f3(): + pass - self.assertEqual(util.docstring_headline(f1), "docstring headline") - self.assertEqual(util.docstring_headline(f2), "docstring headline") + self.assertEqual(util.docstring_headline(f1), 'docstring headline') + self.assertEqual(util.docstring_headline(f2), 'docstring headline') + self.assertEqual(util.docstring_headline(f3), '') def test_analysator_returns_float(self): # If an analysator wrapped by make_analysator returns a floating point @@ -88,10 +95,10 @@ class UtilTest(unittest.TestCase): def test_analysator_type_error(self): # When converting the analysator's return value to a float a # TypeError may occur. If that happens 0.0 is returned instead. - self.assertEqual(FakeLexer.analyse(None), 0.0) + self.assertEqual(FakeLexer.analyse('xxx'), 0.0) def test_shebang_matches(self): - self.assertTrue(util.shebang_matches('#!/usr/bin/env python', r'python(2\.\d)?')) + self.assertTrue(util.shebang_matches('#!/usr/bin/env python\n', r'python(2\.\d)?')) self.assertTrue(util.shebang_matches('#!/usr/bin/python2.4', r'python(2\.\d)?')) self.assertTrue(util.shebang_matches('#!/usr/bin/startsomethingwith python', r'python(2\.\d)?')) @@ -106,7 +113,7 @@ class UtilTest(unittest.TestCase): def test_doctype_matches(self): self.assertTrue(util.doctype_matches( - '<!DOCTYPE html PUBLIC "a"> <html>', 'html.*')) + '<!DOCTYPE html> <html>', 'html.*')) self.assertFalse(util.doctype_matches( '<?xml ?> <DOCTYPE html PUBLIC "a"> <html>', 'html.*')) self.assertTrue(util.html_doctype_matches( @@ -157,3 +164,50 @@ class UtilTest(unittest.TestCase): # keeps first x = util.duplicates_removed(('a', 'b', 'a')) self.assertEqual(['a', 'b'], x) + + def test_guess_decode(self): + # UTF-8 should be decoded as UTF-8 + s = util.guess_decode(u'\xff'.encode('utf-8')) + self.assertEqual(s, (u'\xff', 'utf-8')) + + # otherwise, it could be latin1 or the locale encoding... + import locale + s = util.guess_decode(b'\xff') + self.assertTrue(s[1] in ('latin1', locale.getpreferredencoding())) + + def test_guess_decode_from_terminal(self): + class Term: + encoding = 'utf-7' + + s = util.guess_decode_from_terminal(u'\xff'.encode('utf-7'), Term) + self.assertEqual(s, (u'\xff', 'utf-7')) + + s = util.guess_decode_from_terminal(u'\xff'.encode('utf-8'), Term) + self.assertEqual(s, (u'\xff', 'utf-8')) + + def test_add_metaclass(self): + class Meta(type): + pass + + @util.add_metaclass(Meta) + class Cls: + pass + + self.assertEqual(type(Cls), Meta) + + +class ConsoleTest(unittest.TestCase): + + def test_ansiformat(self): + f = console.ansiformat + c = console.codes + all_attrs = f('+*_blue_*+', 'text') + self.assertTrue(c['blue'] in all_attrs and c['blink'] in all_attrs + and c['bold'] in all_attrs and c['underline'] in all_attrs + and c['reset'] in all_attrs) + self.assertRaises(KeyError, f, '*mauve*', 'text') + + def test_functions(self): + self.assertEqual(console.reset_color(), console.codes['reset']) + self.assertEqual(console.colorize('blue', 'text'), + console.codes['blue'] + 'text' + console.codes['reset']) |