Refactoring
Jonas Haag
6 years ago
15 | 15 | } |
16 | 16 | |
17 | 17 | def __init__(self, repo_paths, site_name, use_smarthttp): |
18 | self.repos = [FancyRepo(path) for path in repo_paths] | |
19 | self.repo_map = dict((repo.name, repo) for repo in self.repos) | |
18 | """(See `make_app` for parameter descriptions.)""" | |
19 | repo_objs = [FancyRepo(path) for path in repo_paths] | |
20 | self.repos = dict((repo.name, repo) for repo in repo_objs) | |
20 | 21 | self.site_name = site_name |
21 | 22 | self.use_smarthttp = use_smarthttp |
22 | 23 | |
25 | 26 | self.setup_routes() |
26 | 27 | |
27 | 28 | def create_jinja_environment(self): |
28 | """ Called by Flask.__init__ """ | |
29 | """Called by Flask.__init__""" | |
29 | 30 | env = super(Klaus, self).create_jinja_environment() |
30 | 31 | for func in [ |
31 | 32 | 'force_unicode', |
64 | 65 | self.add_url_rule(rule, view_func=getattr(views, endpoint)) |
65 | 66 | |
66 | 67 | |
67 | def make_app(repos, site_name, use_smarthttp=False, htdigest_file=None, | |
68 | def make_app(repo_paths, site_name, use_smarthttp=False, htdigest_file=None, | |
68 | 69 | require_browser_auth=False, disable_push=False, unauthenticated_push=False): |
69 | 70 | """ |
70 | 71 | Returns a WSGI app with all the features (smarthttp, authentication) |
71 | 72 | already patched in. |
72 | 73 | |
73 | :param repos: List of paths of repositories to serve. | |
74 | :param repo_paths: List of paths of repositories to serve. | |
74 | 75 | :param site_name: Name of the Web site (e.g. "John Doe's Git Repositories") |
75 | 76 | :param use_smarthttp: Enable Git Smart HTTP mode, which makes it possible to |
76 | 77 | pull from the served repositories. If `htdigest_file` is set as well, |
95 | 96 | raise ValueError("'htdigest_file' set without 'use_smarthttp' or 'require_browser_auth'") |
96 | 97 | |
97 | 98 | app = Klaus( |
98 | repos, | |
99 | repo_paths, | |
99 | 100 | site_name, |
100 | 101 | use_smarthttp, |
101 | 102 | ) |
104 | 105 | if use_smarthttp: |
105 | 106 | # `path -> Repo` mapping for Dulwich's web support |
106 | 107 | dulwich_backend = dulwich.server.DictBackend( |
107 | dict(('/'+repo.name, repo) for repo in app.repos) | |
108 | dict(('/'+name, repo) for name, repo in app.repos.items()) | |
108 | 109 | ) |
109 | 110 | # Dulwich takes care of all Git related requests/URLs |
110 | 111 | # and passes through everything else to klaus |
146 | 146 | |
147 | 147 | # Skip "no newline at end of file" markers |
148 | 148 | line = next(lineiter) |
149 | if line == "\ No newline at end of file": | |
149 | if line == r"\ No newline at end of file": | |
150 | 150 | lines[-1]['no_newline'] = True |
151 | 151 | line = next(lineiter) |
152 | 152 |
0 | from pygments import highlight | |
1 | from pygments.lexers import get_lexer_for_filename, \ | |
2 | guess_lexer, ClassNotFound, TextLexer | |
3 | from pygments.formatters import HtmlFormatter | |
4 | ||
5 | from klaus import markup | |
6 | ||
7 | ||
8 | class KlausDefaultFormatter(HtmlFormatter): | |
9 | def __init__(self, **kwargs): | |
10 | HtmlFormatter.__init__(self, linenos='table', lineanchors='L', | |
11 | linespans='L', anchorlinenos=True, **kwargs) | |
12 | ||
13 | def _format_lines(self, tokensource): | |
14 | for tag, line in HtmlFormatter._format_lines(self, tokensource): | |
15 | if tag == 1: | |
16 | # sourcecode line | |
17 | line = '<span class=line>%s</span>' % line | |
18 | yield tag, line | |
19 | ||
20 | ||
21 | def pygmentize(code, filename, render_markup): | |
22 | """Render code using Pygments, markup (markdown, rst, ...) using the | |
23 | corresponding renderer, if available. | |
24 | ||
25 | :param code: the program code to highlight, str | |
26 | :param filename: name of the source file the code is taken from, str | |
27 | :param render_markup: whether to render markup if possible, bool | |
28 | """ | |
29 | if render_markup and markup.can_render(filename): | |
30 | return markup.render(filename, code) | |
31 | ||
32 | try: | |
33 | lexer = get_lexer_for_filename(filename, code) | |
34 | except ClassNotFound: | |
35 | try: | |
36 | lexer = guess_lexer(code) | |
37 | except ClassNotFound: | |
38 | lexer = TextLexer() | |
39 | ||
40 | return highlight(code, lexer, KlausFormatter()) |
69 | 69 | """Return a list of ref names that begin with `prefix`, ordered by the |
70 | 70 | time they have been committed to last. |
71 | 71 | """ |
72 | refs = self.refs.as_dict(encode_for_git(prefix)) | |
73 | if exclude: | |
74 | refs.pop(prefix + exclude, None) | |
75 | ||
76 | 72 | def get_commit_time(refname): |
77 | 73 | obj = self[refs[refname]] |
78 | 74 | if isinstance(obj, dulwich.objects.Tag): |
79 | 75 | return obj.tag_time |
80 | 76 | return obj.commit_time |
81 | 77 | |
82 | return [decode_from_git(ref) for ref in | |
83 | sorted(refs.keys(), key=get_commit_time, reverse=True)] | |
78 | refs = self.refs.as_dict(encode_for_git(prefix)) | |
79 | if exclude: | |
80 | refs.pop(prefix + exclude, None) | |
81 | sorted_names = sorted(refs.keys(), key=get_commit_time, reverse=True) | |
82 | return [decode_from_git(ref) for ref in sorted_names] | |
84 | 83 | |
85 | 84 | def get_branch_names(self, exclude=None): |
86 | 85 | """Return a list of branch names of this repo, ordered by the time they |
3 | 3 | from io import BytesIO |
4 | 4 | from contextlib import closing |
5 | 5 | |
6 | from klaus.utils import encode_for_git, decode_from_git | |
6 | from klaus.utils import decode_from_git | |
7 | 7 | |
8 | 8 | |
9 | 9 | class ListBytesIO(object): |
10 | 10 | except ImportError: |
11 | 11 | chardet = None |
12 | 12 | |
13 | from pygments import highlight | |
14 | from pygments.lexers import get_lexer_for_filename, guess_lexer, ClassNotFound, TextLexer | |
15 | from pygments.formatters import HtmlFormatter | |
16 | ||
17 | 13 | from humanize import naturaltime |
18 | ||
19 | from klaus import markup | |
20 | 14 | |
21 | 15 | |
22 | 16 | class SubUri(object): |
51 | 45 | environ['wsgi.url_scheme'] = environ['HTTP_X_SCHEME'] |
52 | 46 | |
53 | 47 | return self.app(environ, start_response) |
54 | ||
55 | ||
56 | class KlausFormatter(HtmlFormatter): | |
57 | def __init__(self): | |
58 | HtmlFormatter.__init__(self, linenos='table', lineanchors='L', | |
59 | linespans='L', anchorlinenos=True) | |
60 | ||
61 | def _format_lines(self, tokensource): | |
62 | for tag, line in HtmlFormatter._format_lines(self, tokensource): | |
63 | if tag == 1: | |
64 | # sourcecode line | |
65 | line = '<span class=line>%s</span>' % line | |
66 | yield tag, line | |
67 | ||
68 | ||
69 | def pygmentize(code, filename=None, render_markup=True): | |
70 | """Render code using Pygments, markup (markdown, rst, ...) using the | |
71 | corresponding renderer, if available. | |
72 | """ | |
73 | if render_markup and markup.can_render(filename): | |
74 | return markup.render(filename, code) | |
75 | ||
76 | try: | |
77 | lexer = get_lexer_for_filename(filename, code) | |
78 | except ClassNotFound: | |
79 | try: | |
80 | lexer = guess_lexer(code) | |
81 | except ClassNotFound: | |
82 | lexer = TextLexer() | |
83 | return highlight(code, lexer, KlausFormatter()) | |
84 | 48 | |
85 | 49 | |
86 | 50 | def timesince(when, now=time.time): |
154 | 118 | |
155 | 119 | |
156 | 120 | def shorten_sha1(sha1): |
157 | if re.match('[a-z\d]{20,40}', sha1): | |
121 | if re.match(r'[a-z\d]{20,40}', sha1): | |
158 | 122 | sha1 = sha1[:7] |
159 | 123 | return sha1 |
160 | 124 |
0 | 0 | import os |
1 | 1 | |
2 | from flask import request, render_template, current_app | |
2 | from flask import request, render_template, current_app, url_for | |
3 | 3 | from flask.views import View |
4 | 4 | |
5 | 5 | from werkzeug.wrappers import Response |
8 | 8 | from dulwich.objects import Blob |
9 | 9 | |
10 | 10 | from klaus import markup, tarutils |
11 | from klaus.utils import parent_directory, subpaths, pygmentize, encode_for_git, \ | |
12 | force_unicode, guess_is_binary, guess_is_image, replace_dupes | |
11 | from klaus.highlighting import pygmentize | |
12 | from klaus.utils import parent_directory, subpaths, force_unicode, guess_is_binary, \ | |
13 | guess_is_image, replace_dupes | |
13 | 14 | |
14 | 15 | |
15 | 16 | def repo_list(): |
20 | 21 | else: |
21 | 22 | sort_key = lambda repo: repo.name |
22 | 23 | reverse = False |
23 | repos = sorted(current_app.repos, key=sort_key, reverse=reverse) | |
24 | repos = sorted(current_app.repos.values(), key=sort_key, reverse=reverse) | |
24 | 25 | return render_template('repo_list.html', repos=repos) |
25 | 26 | |
26 | 27 | |
54 | 55 | |
55 | 56 | def make_template_context(self, repo, rev, path): |
56 | 57 | try: |
57 | repo = current_app.repo_map[repo] | |
58 | repo = current_app.repos[repo] | |
58 | 59 | except KeyError: |
59 | 60 | raise NotFound("No such repository %r" % repo) |
60 | 61 | |
174 | 175 | |
175 | 176 | class BaseFileView(TreeViewMixin, BaseBlobView): |
176 | 177 | """Base for FileView and BlameView.""" |
178 | def render_code(self, render_markup): | |
179 | return pygmentize( | |
180 | force_unicode(self.context['blob_or_tree'].data), | |
181 | self.context['filename'], | |
182 | render_markup, | |
183 | ) | |
184 | ||
177 | 185 | def make_template_context(self, *args): |
178 | 186 | super(BaseFileView, self).make_template_context(*args) |
179 | 187 | self.context.update({ |
206 | 214 | super(FileView, self).make_template_context(*args) |
207 | 215 | if self.context['can_render']: |
208 | 216 | render_markup = 'markup' not in request.args |
209 | rendered_code = pygmentize( | |
210 | force_unicode(self.context['blob_or_tree'].data), | |
211 | self.context['filename'], | |
212 | render_markup | |
213 | ) | |
214 | 217 | self.context.update({ |
215 | 218 | 'is_markup': markup.can_render(self.context['filename']), |
216 | 219 | 'render_markup': render_markup, |
217 | 'rendered_code': rendered_code, | |
220 | 'rendered_code': self.render_code(render_markup), | |
218 | 221 | }) |
219 | 222 | |
220 | 223 | |
224 | 227 | def make_template_context(self, *args): |
225 | 228 | super(BlameView, self).make_template_context(*args) |
226 | 229 | if self.context['can_render']: |
227 | rendered_code = pygmentize( | |
228 | force_unicode(self.context['blob_or_tree'].data), | |
229 | self.context['filename'], | |
230 | render_markup=False, | |
231 | ) | |
232 | 230 | line_commits = self.context['repo'].blame(self.context['commit'], self.context['path']) |
233 | 231 | replace_dupes(line_commits, None) |
234 | 232 | self.context.update({ |
235 | 'rendered_code': rendered_code, | |
233 | 'rendered_code': self.render_code(render_markup=False), | |
236 | 234 | 'line_commits': line_commits, |
237 | 235 | }) |
238 | 236 |