def expand_macro(self, formatter, name, content): add_stylesheet(formatter.req, 'lineno/css/lineno.css') i = 1 self._anchor = 'a1' while self._anchor in formatter._anchors: self._anchor = 'a' + str(i) i += 1 formatter._anchors[self._anchor] = True mt = 'txt' match = WikiParser._processor_re.match(content) if match: try: #Trac 0.12+ mt = match.group(2) content = content[match.end(2) + 1:] except IndexError: #Trac 0.11 mt = match.group(1) content = content[match.end(1) + 1:] mimeview = Mimeview(formatter.env) mimetype = mimeview.get_mimetype(mt) or mimeview.get_mimetype('txt') return mimeview.render(formatter.context, mimetype, content, annotations=['codeblock-lineno'])
def render(self, req): """Render the data.""" context = Context.from_request(req) mimeview = Mimeview(self.env) return mimeview.render(context, self.mimetype, self.data, annotations=['lineno'])
def test_plain_text_content(self): """Render simple plain text content.""" mimeview = Mimeview(self.env) req = MockRequest(self.env) context = RenderingContext(Resource('wiki', 'readme.txt')) context.req = req content = io.BytesIO("""\ Some text. """) rendered = mimeview.render(context, 'text/plain', content) self.assertEqual('<div class="code"><pre>Some text.\n</pre></div>', str(rendered))
def expand_macro(self, formatter, name, content): add_stylesheet(formatter.req, 'lineno/css/lineno.css') i = 1 self._anchor = 'a1' while self._anchor in formatter._anchors: self._anchor = 'a' + str(i) i += 1 formatter._anchors[self._anchor] = True mt = 'txt' match = WikiParser._processor_re.match(content) if match: try: #Trac 0.12+ mt = match.group(2) content = content[match.end(2)+1:] except IndexError: #Trac 0.11 mt = match.group(1) content = content[match.end(1)+1:] mimeview = Mimeview(formatter.env) mimetype = mimeview.get_mimetype(mt) or mimeview.get_mimetype('txt') return mimeview.render(formatter.context, mimetype, content, annotations=['codeblock-lineno'])
def process_request(self, req): req.perm.require('WIKI_VIEW') action = req.args.get('action', 'view') version = req.args.get('version') root = self.config.get('docs', 'root', '') base = req.args.get('path') if base[-1] == '/': base = base[:-1] path = root + base title = base or 'Docs' if action != 'view': title += ' (%s)' % action data = {} data['title'] = title repos = self.env.get_repository(authname=req.authname) node = repos.get_node(path, None) if node.isdir: page = DirPage(self.env, node, root, base) else: page = FilePage(self.env, node, root, base) data['editable'] = not node.isdir if req.method == 'POST': if action == 'edit': if page.node.isdir: raise TracError("Cannot edit a directory") latest_version = page.version if req.args.has_key('cancel'): req.redirect(req.href.docs(page.base)) elif int(version) != latest_version: action = 'collision' self._render_editor(req, page, data) elif req.args.has_key('preview'): action = 'preview' self._render_editor(req, page, data, preview=True) else: self._do_save(req, page) elif action == 'edit': self._render_editor(req, page, data) else: req.perm.assert_permission('WIKI_VIEW') if node.isdir: data['entries'] = page.get_entries(req) if page.index is not None: data['index'] = page.index.get_html(req) else: mimeview = Mimeview(self.env) text = [] text.append('=' * (len(page.name) + 6)) text.append(' %s' % page.name) text.append('=' * (len(page.name) + 6)) text = '\n'.join(text) mimetype = 'text/x-rst; charset=utf8' result = mimeview.render(req, mimetype, text) data['index'] = result else: mime_type, chunk = page.get_raw() # When possible, send with non-text mimetype # Perhaps we should embed images...? if not mime_type.startswith('text') or \ mime_type.startswith('text/html'): req.send_response(200) req.send_header('Content-Type', mime_type) req.end_headers() req.write(chunk) return format = req.args.get('format') if format: Mimeview(self.env).send_converted(req, 'text/x-rst', chunk, format, page.name) for conversion in Mimeview(self.env).get_supported_conversions('text/x-rst'): conversion_href = req.href.docs(page.base, format=conversion[0]) add_link(req, 'alternate', conversion_href, conversion[1], conversion[3]) # Render the content into HTML data['content'] = page.get_html(req) data['action'] = action data['current_href'] = req.href.docs(page.base) data['log_href'] = req.href.log(page.path) # Include trac wiki stylesheet add_stylesheet(req, 'common/css/wiki.css') # Include trac docs stylesheet add_stylesheet(req, 'docs/common.css') # Include docutils stylesheet add_stylesheet(req, 'docs/docutils.css') # Include google-code-prettify add_stylesheet(req, 'docs/prettify.css') add_script(req, 'docs/prettify.min.js') # Include context navigation links history = [('root', req.href.docs())] t = '' for s in base[1:].split('/'): if not s: continue t += '/' + s history.append((s, req.href.docs(t))) for h in reversed(history): add_ctxtnav(req, h[0], h[1]) add_ctxtnav(req, 'Revision Log', req.href.log(path)) return 'docs.html', data, None
class PygmentsRendererTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=[Chrome, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = Mock(base_path='', chrome={}, args={}, abs_href=Href('/'), href=Href('/'), session={}, perm=None, authname=None, tz=None) self.context = web_context(self.req) pygments_html = open(os.path.join(os.path.split(__file__)[0], 'pygments.html')) self.pygments_html = Stream(list(HTMLParser(pygments_html, encoding='utf-8'))) def _expected(self, expected_id): return self.pygments_html.select( '//div[@id="%s"]/*|//div[@id="%s"]/text())' % (expected_id, expected_id)) def _test(self, expected_id, result): expected = str(self._expected(expected_id)) result = str(result) #print "\nE: " + repr(expected) #print "\nR: " + repr(result) expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEqual(exp, res) self.assertEqual(len(expected), len(result)) def test_python_hello(self): """ Simple Python highlighting with Pygments (direct) """ result = self.pygments.render(self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello', result) def test_python_hello_mimeview(self): """ Simple Python highlighting with Pygments (through Mimeview.render) """ result = Mimeview(self.env).render(self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello_mimeview', result) def test_newline_content(self): """ The behavior of Pygments changed post-Pygments 0.11.1, and now contains all four newlines. In Pygments 0.11.1 and prior, it only has three since stripnl defaults to True. See http://trac.edgewall.org/ticket/7705. """ from pkg_resources import parse_version, get_distribution result = self.pygments.render(self.context, 'text/x-python', '\n\n\n\n') self.assertTrue(result) t = "".join([r[1] for r in result if r[0] is TEXT]) if parse_version(pygments.__version__) > parse_version('0.11.1') \ or pygments.__version__ == '0.11.1' and 'dev' in \ get_distribution('Pygments').version: self.assertEqual("\n\n\n\n", t) else: self.assertEqual("\n\n\n", t) def test_empty_content(self): """ A '\n' token is generated for an empty file, so we have to bypass pygments when rendering empty files. """ result = self.pygments.render(self.context, 'text/x-python', '') self.assertIsNone(result) def test_extra_mimetypes(self): """ The text/x-ini mimetype is normally not known by Trac, but Pygments supports it. """ mimeview = Mimeview(self.env) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.ini')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.cfg')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.text/x-ini'))
class PatchRendererTestCase(unittest.TestCase): def setUp(self): env = EnvironmentStub(enable=[Chrome, PatchRenderer]) req = Mock(base_path='', chrome={}, args={}, session={}, abs_href=Href('/'), href=Href('/'), locale='', perm=MockPerm(), authname=None, tz=None) self.context = Context.from_request(req) self.patch = Mimeview(env).renderers[0] patch_html = open( os.path.join(os.path.split(__file__)[0], 'patch.html')) self.patch_html = Stream(list(HTMLParser(patch_html))) def _expected(self, expected_id): return self.patch_html.select('//div[@id="%s"]/div' % expected_id) def _test(self, expected_id, result): expected = str(self._expected(expected_id)) result = str(XML(result)) expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEquals(exp, res) self.assertEquals(len(expected), len(result)) def test_simple(self): """ Simple patch rendering """ result = self.patch.render( self.context, None, """ --- README.orig 2006-10-27 14:42:04.062500000 +0200 +++ README 2006-10-27 14:42:28.125000000 +0200 @@ -1,5 +1,5 @@ ---- -base -base -base +be +the base +base modified . """) self.assertTrue(result) self._test('simple', result) def test_no_newline_in_base(self): """ Simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render( self.context, None, """ --- nonewline 2006-10-27 08:36:48.453125000 +0200 +++ newline 2006-10-27 08:36:57.187500000 +0200 @@ -1 +1 @@ -ONELINE \ No newline at end of file +ONELINE """) self.assertTrue(result) self._test('no_newline_in_base', result) def test_no_newline_in_changed(self): """ Another simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render( self.context, None, """ --- newline 2006-10-27 08:36:57.187500000 +0200 +++ nonewline 2006-10-27 08:36:48.453125000 +0200 @@ -1 +1 @@ -ONELINE +ONELINE \ No newline at end of file """) self.assertTrue(result) self._test('no_newline_in_changed', result) def test_diff_to_hdf_expandtabs(self): """Regression test related to #4557""" changes = self.patch._diff_to_hdf([ '--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-aa\tb', '+aaxb' ], 8) self.assertEquals('aa<del> </del>b', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEquals( 'aa<ins>x</ins>b', str(changes[0]['diffs'][0][0]['changed']['lines'][0])) def test_diff_to_hdf_leading_ws(self): """Regression test related to #5795""" changes = self.patch._diff_to_hdf( ['--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-*a', '+ *a'], 8) self.assertEquals('<del></del>*a', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEquals( '<ins> </ins>*a', str(changes[0]['diffs'][0][0]['changed']['lines'][0]))
def format_wiki_text(text): from trac.mimeview.api import Mimeview mimeview = Mimeview(self.env) mimetype = 'text/x-trac-wiki' return mimeview.render(req, mimetype, text)
class PatchRendererTestCase(unittest.TestCase): def setUp(self): env = EnvironmentStub(enable=[Chrome, PatchRenderer]) req = Mock(base_path='', chrome={'static_hash': None}, args={}, session={}, abs_href=Href('/'), href=Href('/'), locale='', perm=MockPerm(), authname=None, tz=None) self.context = web_context(req) self.patch = Mimeview(env).renderers[0] patch_html = open(os.path.join(os.path.split(__file__)[0], 'patch.html')) self.patch_html = Stream(list(HTMLParser(patch_html, encoding='utf-8'))) def _expected(self, expected_id): return self.patch_html.select('//div[@id="%s"]/div' % expected_id) def _test(self, expected_id, result): expected = self._expected(expected_id).render(encoding='utf-8') result = XML(result.render(encoding='utf-8')).render(encoding='utf-8') expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEquals(exp, res) self.assertEquals(len(expected), len(result)) def test_simple(self): """ Simple patch rendering """ result = self.patch.render(self.context, None, """ --- README.orig 2006-10-27 14:42:04.062500000 +0200 +++ README 2006-10-27 14:42:28.125000000 +0200 @@ -1,5 +1,5 @@ ---- -base -base -base +be +the base +base modified . """) self.assertTrue(result) self._test('simple', result) def test_no_newline_in_base(self): """ Simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render(self.context, None, """ --- nonewline 2006-10-27 08:36:48.453125000 +0200 +++ newline 2006-10-27 08:36:57.187500000 +0200 @@ -1 +1 @@ -ONELINE \ No newline at end of file +ONELINE """) self.assertTrue(result) self._test('no_newline_in_base', result) def test_no_newline_in_changed(self): """ Another simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render(self.context, None, """ --- newline 2006-10-27 08:36:57.187500000 +0200 +++ nonewline 2006-10-27 08:36:48.453125000 +0200 @@ -1 +1 @@ -ONELINE +ONELINE \ No newline at end of file """) self.assertTrue(result) self._test('no_newline_in_changed', result) def test_diff_to_hdf_expandtabs(self): """Regression test related to #4557""" changes = self.patch._diff_to_hdf( ['--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-aa\tb', '+aaxb'], 8) self.assertEquals('aa<del> </del>b', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEquals('aa<ins>x</ins>b', str(changes[0]['diffs'][0][0]['changed']['lines'][0])) def test_diff_to_hdf_leading_ws(self): """Regression test related to #5795""" changes = self.patch._diff_to_hdf( ['--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-*a', '+ *a'], 8) self.assertEquals('<del></del>*a', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEquals('<ins> </ins>*a', str(changes[0]['diffs'][0][0]['changed']['lines'][0]))
def expand_macro(self, formatter, name, content): self.log.info('Begin expand_macro for req: ' + repr(content)) largs, kwargs = parse_args(content) if len(largs) == 0: raise TracError("File name to include is required parameter!") orig_file_name = file_name = largs[0] global multirepos if not multirepos: repos = self.env.get_repository(formatter.req.authname) else: if (orig_file_name[0] == '/'): orig_file_name = orig_file_name[1:] splitpath = file_name.split('/') if (file_name[0] == '/'): reponame = splitpath[1] else: reponame = splitpath[0] repos = self.env.get_repository(reponame) if (repos): l = len(reponame) if (file_name[0] == '/'): file_name = file_name[1:] file_name = file_name[l:] else: repos = self.env.get_repository() rev = kwargs.get('rev', None) if kwargs.has_key('header'): header = kwargs.get('header') # user specified header else: href = '../browser/%s%s' % (orig_file_name, make_rev_str(rev)) header = tag.a(file_name, href=href) if not header: header = u'\xa0' # default value from trac.mimeview.api.py # TODO - 'content' is default from mimeview.api.py, but it picks # up text-align: center, which probably isn't the best thing if # we are adding a file name in the header. There isn't an obvious # replacement in the delivered CSS to pick over this for now though header_class = kwargs.get('header_class', 'content') src = repos.get_node(file_name, rev).get_content().read() context = formatter.context # put these into context object so annotator sees them context.file_name = file_name context.rev = rev context.startline = 1 # we generally include line numbers in the output, unless it has been # explicitly requested otherwise. 0, no, false, none will suppress line_numbers = kwargs.get('line_numbers', None) if line_numbers is None: line_numbers = True else: try: line_numbers = int(line_numbers) except: negatory = ('no', 'false', 'none') line_numbers = str(line_numbers).lower() not in negatory # lines added up front to "trick" renderer when rendering partial render_prepend = [] start, end = kwargs.get('start', None), kwargs.get('end', None) if start or end: src, start, end = self._handle_partial(src, start, end) context.startline = start if start > 2 and file_name.endswith('.php'): render_prepend = [ '#!/usr/bin/php -f', '<?' ] if render_prepend: src = '\n'.join(render_prepend) + '\n' + src # ensure accurate start number after this gets stripped context.startline = start - len(render_prepend) mimetype = kwargs.get('mimetype', None) url = None # render method doesn't seem to use this mv = Mimeview(self.env) annotations = line_numbers and ['givenlineno'] or None src = mv.render(formatter.context, mimetype, src, file_name, url, annotations) if line_numbers: # handle the case where only one line of code was included # and we get back an XHTML string if not hasattr(src, 'generate'): from genshi.input import XML src = XML(src) # the _render_source method will always set the CSS class # of the annotator to it's name; there isn't an easy way # to override that. We could create our own CSS class for # givenlineno that mimics lineno, but it's cleaner to just # tweak the output here by running the genshi stream from # src through a transformer that will make the change xpath1 = 'thead/tr/th[@class="givenlineno"]' xpath2 = 'thead/tr/th[2]' # last() not supported by Genshi? xpath3 = 'thead/tr/th[2]/text()' # TODO - does genshi require a QName here? Seems to work w/o it src = src.generate() | Transformer(xpath1).attr('class', 'lineno') \ | Transformer(xpath2).attr('class', header_class) \ | Transformer(xpath3).replace(header) if render_prepend: # TODO - is there a better of stripping lines here? for i in xrange(len(render_prepend)): src = src | Transformer('tbody/tr[1]').remove() return src
class PygmentsRendererTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub( enable=[Chrome, LineNumberAnnotator, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = Mock(base_path='', chrome={}, args={}, abs_href=Href('/'), href=Href('/'), session={}, perm=None, authname=None, tz=None) self.context = web_context(self.req) pygments_html = open( os.path.join(os.path.split(__file__)[0], 'pygments.html')) self.pygments_html = Stream( list(HTMLParser(pygments_html, encoding='utf-8'))) def _expected(self, expected_id): return self.pygments_html.select( '//div[@id="%s"]/*|//div[@id="%s"]/text())' % (expected_id, expected_id)) def _test(self, expected_id, result): expected = unicode(self._expected(expected_id)) result = unicode(result) #print("\nE: " + repr(expected)) #print("\nR: " + repr(result)) expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEqual(exp, res) self.assertEqual(len(expected), len(result)) def test_python_hello(self): """ Simple Python highlighting with Pygments (direct) """ result = self.pygments.render( self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello', result) def test_python_hello_mimeview(self): """ Simple Python highlighting with Pygments (through Mimeview.render) """ result = Mimeview(self.env).render( self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello_mimeview', result) def test_python_with_lineno(self): result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) self._test('python_with_lineno_1', result) result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=3 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) self._test('python_with_lineno_2', result) def test_python_with_lineno_and_markups(self): """Python highlighting with Pygments and lineno annotator """ result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=3 id=b marks=4-5 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) self._test('python_with_lineno_and_markups', result) def test_python_with_invalid_arguments(self): result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=-10 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) self._test('python_with_invalid_arguments_1', result) result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=a id=d marks=a-b print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) self._test('python_with_invalid_arguments_2', result) def test_pygments_lexer_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html( self.env, self.context, """ {{{#!php if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_options', result) def test_pygments_lexer_arguments(self): result = format_to_html( self.env, self.context, """ {{{#!php startinline=True funcnamehighlighting=False if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_arguments', result) def test_pygments_lexer_arguments_override_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html( self.env, self.context, """ {{{#!php funcnamehighlighting=True if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_arguments_override_options', result) def test_newline_content(self): """ The behavior of Pygments changed post-Pygments 0.11.1, and now contains all four newlines. In Pygments 0.11.1 and prior, it only has three since stripnl defaults to True. See http://trac.edgewall.org/ticket/7705. """ from pkg_resources import parse_version, get_distribution result = self.pygments.render(self.context, 'text/x-python', '\n\n\n\n') self.assertTrue(result) t = "".join([r[1] for r in result if r[0] is TEXT]) if parse_version(pygments.__version__) > parse_version('0.11.1') \ or pygments.__version__ == '0.11.1' and 'dev' in \ get_distribution('Pygments').version: self.assertEqual("\n\n\n\n", t) else: self.assertEqual("\n\n\n", t) def test_empty_content(self): """ A '\n' token is generated for an empty file, so we have to bypass pygments when rendering empty files. """ result = self.pygments.render(self.context, 'text/x-python', '') self.assertIsNone(result) def test_extra_mimetypes(self): """ The text/x-ini mimetype is normally not known by Trac, but Pygments supports it. """ mimeview = Mimeview(self.env) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.ini')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.cfg')) self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.text/x-ini'))
class PygmentsRendererTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=[Chrome, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = Mock(base_path='', chrome={}, args={}, abs_href=Href('/'), href=Href('/'), session={}, perm=None, authname=None, tz=None) self.context = web_context(self.req) pygments_html = open(os.path.join(os.path.split(__file__)[0], 'pygments.html')) self.pygments_html = Stream(list(HTMLParser(pygments_html, encoding='utf-8'))) def _expected(self, expected_id): return self.pygments_html.select( '//div[@id="%s"]/*|//div[@id="%s"]/text())' % (expected_id, expected_id)) def _test(self, expected_id, result): expected = str(self._expected(expected_id)) result = str(result) #print "\nE: " + repr(expected) #print "\nR: " + repr(result) expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEquals(exp, res) self.assertEquals(len(expected), len(result)) def test_python_hello(self): """ Simple Python highlighting with Pygments (direct) """ result = self.pygments.render(self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello', result) def test_python_hello_mimeview(self): """ Simple Python highlighting with Pygments (through Mimeview.render) """ result = Mimeview(self.env).render(self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) self._test('python_hello_mimeview', result) def test_newline_content(self): """ The behavior of Pygments changed post-Pygments 0.11.1, and now contains all four newlines. In Pygments 0.11.1 and prior, it only has three since stripnl defaults to True. See http://trac.edgewall.org/ticket/7705. """ from pkg_resources import parse_version, get_distribution result = self.pygments.render(self.context, 'text/x-python', '\n\n\n\n') self.assertTrue(result) t = "".join([r[1] for r in result if r[0] is TEXT]) if parse_version(pygments.__version__) > parse_version('0.11.1') \ or pygments.__version__ == '0.11.1' and 'dev' in \ get_distribution('Pygments').version: self.assertEqual("\n\n\n\n", t) else: self.assertEqual("\n\n\n", t) def test_empty_content(self): """ A '\n' token is generated for an empty file, so we have to bypass pygments when rendering empty files. """ result = self.pygments.render(self.context, 'text/x-python', '') self.assertEqual(None, result)
class PygmentsRendererTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub( enable=[Chrome, LineNumberAnnotator, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = MockRequest(self.env) self.context = web_context(self.req) pygments_html = open( os.path.join(os.path.split(__file__)[0], 'pygments.html')) self.pygments_html = Stream( list(HTMLParser(pygments_html, encoding='utf-8'))) def _expected(self, expected_id): return self.pygments_html.select( '//div[@id="%s"]/*|//div[@id="%s"]/text())' % (expected_id, expected_id)) def _test(self, expected_id, result): expected = unicode(self._expected(expected_id)) result = unicode(result) #print("\nE: " + repr(expected)) #print("\nR: " + repr(result)) expected, result = expected.splitlines(), result.splitlines() for exp, res in zip(expected, result): self.assertEqual(exp, res) self.assertEqual(len(expected), len(result)) def test_python_hello(self): """ Simple Python highlighting with Pygments (direct) """ result = self.pygments.render( self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_hello', result) else: self._test('python_hello_pygments_2.1plus', result) def test_python_hello_mimeview(self): """ Simple Python highlighting with Pygments (through Mimeview.render) """ result = Mimeview(self.env).render( self.context, 'text/x-python', """ def hello(): return "Hello World!" """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_hello_mimeview', result) else: self._test('python_hello_mimeview_pygments_2.1plus', result) def test_python_with_lineno(self): result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_1', result) else: self._test('python_with_lineno_1_pygments_2.1plus', result) result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=3 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_2', result) else: self._test('python_with_lineno_2_pygments_2.1plus', result) def test_python_with_lineno_and_markups(self): """Python highlighting with Pygments and lineno annotator """ result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=3 id=b marks=4-5 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_and_markups', result) else: self._test('python_with_lineno_and_markups_pygments_2.1plus', result) def test_python_with_invalid_arguments(self): result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=-10 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_invalid_arguments_1', result) else: self._test('python_with_invalid_arguments_1_pygments_2.1plus', result) result = format_to_html( self.env, self.context, """\ {{{#!text/x-python lineno=a id=d marks=a-b print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_invalid_arguments_2', result) else: self._test('python_with_invalid_arguments_2_pygments_2.1plus', result) def test_pygments_lexer_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html( self.env, self.context, """ {{{#!php if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_options', result) def test_pygments_lexer_arguments(self): result = format_to_html( self.env, self.context, """ {{{#!php startinline=True funcnamehighlighting=False if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_arguments', result) def test_pygments_lexer_arguments_override_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html( self.env, self.context, """ {{{#!php funcnamehighlighting=True if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """) self.assertTrue(result) self._test('pygments_lexer_arguments_override_options', result) def test_newline_content(self): """Regression test for newline-stripping behavior in Pygments. http://trac.edgewall.org/ticket/7705 """ result = self.pygments.render(self.context, 'text/x-python', '\n\n\n\n') self.assertTrue(result) t = "".join(r[1] for r in result if r[0] is TEXT) self.assertEqual("\n\n\n\n", t) def test_empty_content(self): """ A '\n' token is generated for an empty file, so we have to bypass pygments when rendering empty files. """ result = self.pygments.render(self.context, 'text/x-python', '') self.assertIsNone(result) def test_extra_mimetypes(self): """ The text/x-ini mimetype is normally not known by Trac, but Pygments supports it. """ mimeview = Mimeview(self.env) self.assertIn(mimeview.get_mimetype('file.ini'), ('text/x-ini; charset=utf-8', 'text/inf; charset=utf-8')) # Pygment 2.1+ self.assertIn(mimeview.get_mimetype('file.cfg'), ('text/x-ini; charset=utf-8', 'text/inf; charset=utf-8')) # Pygment 2.1+ self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.text/x-ini'))
class FilePage(Page): def __init__(self, env, node, root, base): super(FilePage, self).__init__(env, node, root, base) self.mimeview = Mimeview(self.env) self.exists = (node is not None) self.mime_type = None self.chunk = None def get_html(self, req): """ Get the raw content from the repository and convert to html. """ mime_type, chunk = self.get_raw() if not mime_type.startswith('text'): raise TracError("Invalid mime-type: %s" % mime_type) # Hack to support images, we change the path from relative # the document being requested to absolute. # 1: Ignore http and ftp urls to allow images to be fetched # 2: Assume URLS beginning with "/" are relative to top-level # 3: Assume URLS that do not include "http/ftp" are relative to # current path. def fixup(m): text = m.group(1) if text.startswith('http:') or text.startswith('ftp:'): return m.group(0) if text.startswith('/'): text = text[1:] dir = self.dir if dir.endswith('/'): dir = dir[:-1] return '.. image:: %s/%s' % (req.href.docs(dir), text) chunk = re.sub('\.\. image\:\:\s*(\S+)', fixup, chunk, re.MULTILINE) # Assume all wiki pages are ReStructuredText documents result = self.mimeview.render(req, mime_type, chunk) if not isinstance(result, (str, unicode)): result = unicode(result) # Hack to pretty-print source code (assumes all literal-blocks # contain source code). result = result.replace('<pre class="literal-block">', '<pre class="literal-block prettyprint">') if 'prettyprint' in result: # FIXME: Add as an event listener instead? result += """ <script type="text/javascript"> var origOnLoad = window.onload; function onLoad() { if (origOnLoad) { if(typeof(origOnLoad) == "string") { eval(origOnLoad); } else { origOnLoad(); } } prettyPrint(); } window.onload = onLoad; </script> """ return Markup(result) def get_raw(self): """ Load the raw content from the repository. """ if self.mime_type is not None: return self.mime_type, self.chunk node = self.node content_length = node.get_content_length() if content_length > (1024 * 1024): raise TracError("Docs is too large: %d" % content_length) content = node.get_content() chunk = content.read(content_length) mime_type = node.content_type # Guess the mime-type when possible to be text/plain. if not mime_type or mime_type == 'application/octet-stream': mime_type = self.mimeview.get_mimetype(node.name, chunk) or \ mime_type or 'text/plain' if mime_type.startswith('text/plain'): mime_type = 'text/x-rst; charset=utf8' self.mime_type = mime_type self.chunk = chunk return mime_type, chunk def save(self, req, text, comment): """ Save the specified text into this document. """ if not isinstance(self.node.repos, SubversionRepository): raise TracError("The '%s' repository is not supported" % type(self.node.repos)) from svn import core as _core from svn import fs as _fs from svn import repos as _repos repos = self.node.repos.repos #.repos revnum = self.node._requested_rev author = req.authname message = 'Edited %s' % self.base[1:] if comment: message += ' (%s)' % comment pool = _core.Pool() fs_txn = _repos.fs_begin_txn_for_commit(repos, revnum, author, message, pool) fs_root = _fs.svn_fs_txn_root(fs_txn, pool) if hasattr(self.node, '_scoped_svn_path'): fs_path = self.node._scoped_svn_path else: fs_path = self.node._scoped_path_utf8 stream = _fs.svn_fs_apply_text(fs_root, fs_path, None, pool) _core.svn_stream_write(stream, text) _core.svn_stream_close(stream) return _repos.fs_commit_txn(repos, fs_txn, pool)
class PygmentsRendererTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=[Chrome, LineNumberAnnotator, PygmentsRenderer]) self.pygments = Mimeview(self.env).renderers[0] self.req = MockRequest(self.env) self.context = web_context(self.req) self.pygments_html = {} testcase = [] html_file = os.path.join(os.path.dirname(__file__), 'pygments.data') with open(html_file, 'rb') as f: for line in f.readlines(): if line.startswith('#'): self.pygments_html[line[1:].strip()] = testcase = [] else: testcase.append(unicode(line.rstrip(), 'utf-8')) def _expected(self, expected_id): return self.pygments_html[expected_id] def _test(self, expected_id, result): expected = self._expected(expected_id) result = unicode(result).splitlines() # from pprint import pformat # print("\nE: " + expected_id + "\n" + pformat(expected)) # print("\nR: " + expected_id + "\n" + pformat(result)) self.maxDiff = None def split(s): sp = re.split('(>)', s) return [a + b for (a, b) in zip(sp[0::2], sp[1::2])] for exp, res in zip(expected, result): self.assertEqual(split(exp), split(res)) self.assertEqual(len(expected), len(result)) def test_python_hello(self): """ Simple Python highlighting with Pygments (direct) """ result = self.pygments.render(self.context, 'text/x-python', textwrap.dedent("""\ def hello(): return "Hello World!" """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_hello', result) else: self._test('python_hello_pygments_2.1plus', result) def test_python_hello_mimeview(self): """ Simple Python highlighting with Pygments (through Mimeview.render) """ result = Mimeview(self.env).render(self.context, 'text/x-python', textwrap.dedent(""" def hello(): return "Hello World!" """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_hello_mimeview', result) else: self._test('python_hello_mimeview_pygments_2.1plus', result) def test_python_with_lineno(self): result = format_to_html(self.env, self.context, textwrap.dedent("""\ {{{#!text/x-python lineno print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_1', result) else: self._test('python_with_lineno_1_pygments_2.1plus', result) result = format_to_html(self.env, self.context, textwrap.dedent("""\ {{{#!text/x-python lineno=3 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_2', result) else: self._test('python_with_lineno_2_pygments_2.1plus', result) def test_python_with_lineno_and_markups(self): """Python highlighting with Pygments and lineno annotator """ result = format_to_html(self.env, self.context, textwrap.dedent("""\ {{{#!text/x-python lineno=3 id=b marks=4-5 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_lineno_and_markups', result) else: self._test('python_with_lineno_and_markups_pygments_2.1plus', result) def test_python_with_invalid_arguments(self): result = format_to_html(self.env, self.context, textwrap.dedent("""\ {{{#!text/x-python lineno=-10 print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_invalid_arguments_1', result) else: self._test('python_with_invalid_arguments_1_pygments_2.1plus', result) result = format_to_html(self.env, self.context, textwrap.dedent("""\ {{{#!text/x-python lineno=a id=d marks=a-b print 'this is a python sample' a = b+3 z = "this is a string" print 'this is the end of the python sample' }}} """)) self.assertTrue(result) if pygments_version < parse_version('2.1'): self._test('python_with_invalid_arguments_2', result) else: self._test('python_with_invalid_arguments_2_pygments_2.1plus', result) def test_pygments_lexer_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html(self.env, self.context, textwrap.dedent(""" {{{#!php if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """)) self.assertTrue(result) self._test('pygments_lexer_options', result) def test_pygments_lexer_arguments(self): result = format_to_html(self.env, self.context, textwrap.dedent(""" {{{#!php startinline=True funcnamehighlighting=False if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """)) self.assertTrue(result) self._test('pygments_lexer_arguments', result) def test_pygments_lexer_arguments_override_options(self): self.env.config.set('pygments-lexer', 'php.startinline', True) self.env.config.set('pygments-lexer', 'php.funcnamehighlighting', False) result = format_to_html(self.env, self.context, textwrap.dedent(""" {{{#!php funcnamehighlighting=True if (class_exists('MyClass')) { $myclass = new MyClass(); } }}} """)) self.assertTrue(result) self._test('pygments_lexer_arguments_override_options', result) def test_newline_content(self): """Regression test for newline-stripping behavior in Pygments. http://trac.edgewall.org/ticket/7705 """ result = self.pygments.render(self.context, 'text/x-python', '\n\n\n\n') self.assertTrue(result) t = result self.assertEqual("\n\n\n\n", t) def test_empty_content(self): """ A '\n' token is generated for an empty file, so we have to bypass pygments when rendering empty files. """ result = self.pygments.render(self.context, 'text/x-python', '') self.assertIsNone(result) def test_extra_mimetypes(self): """ The text/x-ini mimetype is normally not known by Trac, but Pygments supports it. """ mimeview = Mimeview(self.env) self.assertIn(mimeview.get_mimetype('file.ini'), ('text/x-ini; charset=utf-8', 'text/inf; charset=utf-8')) # Pygment 2.1+ self.assertIn(mimeview.get_mimetype('file.cfg'), ('text/x-ini; charset=utf-8', 'text/inf; charset=utf-8')) # Pygment 2.1+ self.assertEqual('text/x-ini; charset=utf-8', mimeview.get_mimetype('file.text/x-ini'))
class PatchRendererTestCase(unittest.TestCase): def setUp(self): env = EnvironmentStub(enable=[Chrome, PatchRenderer]) req = MockRequest(env) self.context = web_context(req) self.patch = Mimeview(env).renderers[0] html_file = os.path.join(os.path.dirname(__file__), 'patch.data') self.patch_html = {} testcase = [] with open(html_file, 'rb') as f: for line in f.readlines(): if line.startswith('#'): self.patch_html[line[1:].strip()] = testcase = [] else: testcase.append(line.rstrip()) def _expected(self, expected_id): return self.patch_html[expected_id] def _test(self, expected_id, result): expected = self._expected(expected_id) result = result.splitlines() self.maxDiff = None for exp, res in zip(expected, result): self.assertEqual(exp, res) self.assertEqual(len(expected), len(result)) def test_simple(self): """ Simple patch rendering """ result = self.patch.render(self.context, None, """ --- README.orig 2006-10-27 14:42:04.062500000 +0200 +++ README 2006-10-27 14:42:28.125000000 +0200 @@ -1,5 +1,5 @@ ---- -base -base -base +be +the base +base modified . """) self.assertTrue(result) self._test('simple', result) def test_no_newline_in_base(self): """ Simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render(self.context, None, """ --- nonewline 2006-10-27 08:36:48.453125000 +0200 +++ newline 2006-10-27 08:36:57.187500000 +0200 @@ -1 +1 @@ -ONELINE \ No newline at end of file +ONELINE """) self.assertTrue(result) self._test('no_newline_in_base', result) def test_no_newline_in_changed(self): """ Another simple regression test for #4027 ("No newline at end of file") """ result = self.patch.render(self.context, None, """ --- newline 2006-10-27 08:36:57.187500000 +0200 +++ nonewline 2006-10-27 08:36:48.453125000 +0200 @@ -1 +1 @@ -ONELINE +ONELINE \ No newline at end of file """) self.assertTrue(result) self._test('no_newline_in_changed', result) def test_diff_to_hdf_expandtabs(self): """Regression test related to #4557""" changes = self.patch._diff_to_hdf( ['--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-aa\tb', '+aaxb'], 8) self.assertEqual('aa<del> </del>b', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEqual('aa<ins>x</ins>b', str(changes[0]['diffs'][0][0]['changed']['lines'][0])) def test_diff_to_hdf_leading_ws(self): """Regression test related to #5795""" changes = self.patch._diff_to_hdf( ['--- hello.c 1', '+++ hello.c 2', '@@ -1 +1 @@', '-*a', '+ *a'], 8) self.assertEqual('<del></del>*a', str(changes[0]['diffs'][0][0]['base']['lines'][0])) self.assertEqual('<ins> </ins>*a', str(changes[0]['diffs'][0][0]['changed']['lines'][0])) def test_range_information_with_no_lines(self): result = self.patch.render(self.context, None, """ Index: filename.txt =================================================================== --- filename.txt +++ filename.txt @@ -14,7 +14,7 @@ """) self.assertTrue(result) self._test('range_information_with_no_lines', result)
def expand_macro(self, formatter, name, content): self.log.info('Begin expand_macro for req: ' + repr(content)) largs, kwargs = parse_args(content) if len(largs) == 0: raise TracError("File name to include is required parameter!") orig_file_name = file_name = largs[0] global multirepos if not multirepos: repos = self.env.get_repository(formatter.req.authname) else: if (orig_file_name[0] == '/'): orig_file_name = orig_file_name[1:] splitpath = file_name.split('/') if (file_name[0] == '/'): reponame = splitpath[1] else: reponame = splitpath[0] repos = self.env.get_repository(reponame) if (repos): l = len(reponame) if (file_name[0] == '/'): file_name = file_name[1:] file_name = file_name[l:] else: repos = self.env.get_repository() rev = kwargs.get('rev', None) if kwargs.has_key('header'): header = kwargs.get('header') # user specified header else: href = '../browser/%s%s' % (orig_file_name, make_rev_str(rev)) header = tag.a(file_name, href=href) if not header: header = u'\xa0' # default value from trac.mimeview.api.py # TODO - 'content' is default from mimeview.api.py, but it picks # up text-align: center, which probably isn't the best thing if # we are adding a file name in the header. There isn't an obvious # replacement in the delivered CSS to pick over this for now though header_class = kwargs.get('header_class', 'content') src = repos.get_node(file_name, rev).get_content().read() context = formatter.context # put these into context object so annotator sees them context.file_name = file_name context.rev = rev context.startline = 1 # we generally include line numbers in the output, unless it has been # explicitly requested otherwise. 0, no, false, none will suppress line_numbers = kwargs.get('line_numbers', None) if line_numbers is None: line_numbers = True else: try: line_numbers = int(line_numbers) except: negatory = ('no', 'false', 'none') line_numbers = str(line_numbers).lower() not in negatory # lines added up front to "trick" renderer when rendering partial render_prepend = [] start, end = kwargs.get('start', None), kwargs.get('end', None) if start or end: src, start, end = self._handle_partial(src, start, end) context.startline = start if start > 2 and file_name.endswith('.php'): render_prepend = ['#!/usr/bin/php -f', '<?'] if render_prepend: src = '\n'.join(render_prepend) + '\n' + src # ensure accurate start number after this gets stripped context.startline = start - len(render_prepend) mimetype = kwargs.get('mimetype', None) url = None # render method doesn't seem to use this mv = Mimeview(self.env) annotations = line_numbers and ['givenlineno'] or None src = mv.render(formatter.context, mimetype, src, file_name, url, annotations) if line_numbers: # handle the case where only one line of code was included # and we get back an XHTML string if not hasattr(src, 'generate'): from genshi.input import XML src = XML(src) # the _render_source method will always set the CSS class # of the annotator to it's name; there isn't an easy way # to override that. We could create our own CSS class for # givenlineno that mimics lineno, but it's cleaner to just # tweak the output here by running the genshi stream from # src through a transformer that will make the change xpath1 = 'thead/tr/th[@class="givenlineno"]' xpath2 = 'thead/tr/th[2]' # last() not supported by Genshi? xpath3 = 'thead/tr/th[2]/text()' # TODO - does genshi require a QName here? Seems to work w/o it src = src.generate() | Transformer(xpath1).attr('class', 'lineno') \ | Transformer(xpath2).attr('class', header_class) \ | Transformer(xpath3).replace(header) if render_prepend: # TODO - is there a better of stripping lines here? for i in xrange(len(render_prepend)): src = src | Transformer('tbody/tr[1]').remove() return src