##// END OF EJS Templates
added limits to single file diffs...
marcink -
r544:d8778cde default
parent child Browse files
Show More
@@ -1,164 +1,176 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # changeset controller for pylons
3 # changeset controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19 """
19 """
20 Created on April 25, 2010
20 Created on April 25, 2010
21 changeset controller for pylons
21 changeset controller for pylons
22 @author: marcink
22 @author: marcink
23 """
23 """
24 from pylons import tmpl_context as c, url, request, response
24 from pylons import tmpl_context as c, url, request, response
25 from pylons.i18n.translation import _
25 from pylons.i18n.translation import _
26 from pylons.controllers.util import redirect
26 from pylons.controllers.util import redirect
27 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.base import BaseController, render
28 from pylons_app.lib.base import BaseController, render
29 from pylons_app.model.hg_model import HgModel
29 from pylons_app.model.hg_model import HgModel
30 from vcs.exceptions import RepositoryError, ChangesetError
30 from vcs.exceptions import RepositoryError, ChangesetError
31 from vcs.nodes import FileNode
31 from vcs.nodes import FileNode
32 from vcs.utils import diffs as differ
32 from vcs.utils import diffs as differ
33 import logging
33 import logging
34 import traceback
34 import traceback
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38 class ChangesetController(BaseController):
38 class ChangesetController(BaseController):
39
39
40 @LoginRequired()
40 @LoginRequired()
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 'repository.admin')
42 'repository.admin')
43 def __before__(self):
43 def __before__(self):
44 super(ChangesetController, self).__before__()
44 super(ChangesetController, self).__before__()
45
45
46 def index(self, revision):
46 def index(self, revision):
47 hg_model = HgModel()
47 hg_model = HgModel()
48 cut_off_limit = 1024 * 100
48 cut_off_limit = 1024 * 100
49
49
50 def wrap_to_table(str):
50 def wrap_to_table(str):
51
51
52 return '''<table class="code-difftable">
52 return '''<table class="code-difftable">
53 <tr class="line">
53 <tr class="line">
54 <td class="lineno new"></td>
54 <td class="lineno new"></td>
55 <td class="code"><pre>%s</pre></td>
55 <td class="code"><pre>%s</pre></td>
56 </tr>
56 </tr>
57 </table>''' % str
57 </table>''' % str
58
58
59 try:
59 try:
60 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
60 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
61 except RepositoryError:
61 except RepositoryError:
62 log.error(traceback.format_exc())
62 log.error(traceback.format_exc())
63 return redirect(url('hg_home'))
63 return redirect(url('hg_home'))
64 else:
64 else:
65 try:
65 try:
66 c.changeset_old = c.changeset.parents[0]
66 c.changeset_old = c.changeset.parents[0]
67 except IndexError:
67 except IndexError:
68 c.changeset_old = None
68 c.changeset_old = None
69 c.changes = []
69 c.changes = []
70
71 #===================================================================
72 # ADDED FILES
73 #===================================================================
70 c.sum_added = 0
74 c.sum_added = 0
71 for node in c.changeset.added:
75 for node in c.changeset.added:
72
76
73 filenode_old = FileNode(node.path, '')
77 filenode_old = FileNode(node.path, '')
74 if filenode_old.is_binary or node.is_binary:
78 if filenode_old.is_binary or node.is_binary:
75 diff = wrap_to_table(_('binary file'))
79 diff = wrap_to_table(_('binary file'))
76 else:
80 else:
77 c.sum_added += len(node.content)
81 c.sum_added += node.size
78 if c.sum_added < cut_off_limit:
82 if c.sum_added < cut_off_limit:
79 f_udiff = differ.get_udiff(filenode_old, node)
83 f_udiff = differ.get_udiff(filenode_old, node)
80 diff = differ.DiffProcessor(f_udiff).as_html()
84 diff = differ.DiffProcessor(f_udiff).as_html()
81 else:
85 else:
82 diff = wrap_to_table(_('Changeset is to big see raw changeset'))
86 diff = wrap_to_table(_('Changeset is to big and was cut'
87 ' off, see raw changeset instead'))
83
88
84 cs1 = None
89 cs1 = None
85 cs2 = node.last_changeset.short_id
90 cs2 = node.last_changeset.short_id
86 c.changes.append(('added', node, diff, cs1, cs2))
91 c.changes.append(('added', node, diff, cs1, cs2))
87
92
93 #===================================================================
94 # CHANGED FILES
95 #===================================================================
88 c.sum_removed = 0
96 c.sum_removed = 0
89 for node in c.changeset.changed:
97 for node in c.changeset.changed:
90 try:
98 try:
91 filenode_old = c.changeset_old.get_node(node.path)
99 filenode_old = c.changeset_old.get_node(node.path)
92 except ChangesetError:
100 except ChangesetError:
93 filenode_old = FileNode(node.path, '')
101 filenode_old = FileNode(node.path, '')
94
102
95 if filenode_old.is_binary or node.is_binary:
103 if filenode_old.is_binary or node.is_binary:
96 diff = wrap_to_table(_('binary file'))
104 diff = wrap_to_table(_('binary file'))
97 else:
105 else:
98 c.sum_removed += len(node.content)
106 c.sum_removed += node.size
99 if c.sum_removed < cut_off_limit:
107 if c.sum_removed < cut_off_limit:
100 f_udiff = differ.get_udiff(filenode_old, node)
108 f_udiff = differ.get_udiff(filenode_old, node)
101 diff = differ.DiffProcessor(f_udiff).as_html()
109 diff = differ.DiffProcessor(f_udiff).as_html()
102 else:
110 else:
103 diff = wrap_to_table(_('Changeset is to big see raw changeset'))
111 diff = wrap_to_table(_('Changeset is to big and was cut'
112 ' off, see raw changeset instead'))
104
113
105 cs1 = filenode_old.last_changeset.short_id
114 cs1 = filenode_old.last_changeset.short_id
106 cs2 = node.last_changeset.short_id
115 cs2 = node.last_changeset.short_id
107 c.changes.append(('changed', node, diff, cs1, cs2))
116 c.changes.append(('changed', node, diff, cs1, cs2))
108
117
118 #===================================================================
119 # REMOVED FILES
120 #===================================================================
109 for node in c.changeset.removed:
121 for node in c.changeset.removed:
110 c.changes.append(('removed', node, None, None, None))
122 c.changes.append(('removed', node, None, None, None))
111
123
112 return render('changeset/changeset.html')
124 return render('changeset/changeset.html')
113
125
114 def raw_changeset(self, revision):
126 def raw_changeset(self, revision):
115
127
116 hg_model = HgModel()
128 hg_model = HgModel()
117 method = request.GET.get('diff', 'show')
129 method = request.GET.get('diff', 'show')
118 try:
130 try:
119 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
131 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
120 except RepositoryError:
132 except RepositoryError:
121 log.error(traceback.format_exc())
133 log.error(traceback.format_exc())
122 return redirect(url('hg_home'))
134 return redirect(url('hg_home'))
123 else:
135 else:
124 try:
136 try:
125 c.changeset_old = c.changeset.parents[0]
137 c.changeset_old = c.changeset.parents[0]
126 except IndexError:
138 except IndexError:
127 c.changeset_old = None
139 c.changeset_old = None
128 c.changes = []
140 c.changes = []
129
141
130 for node in c.changeset.added:
142 for node in c.changeset.added:
131 filenode_old = FileNode(node.path, '')
143 filenode_old = FileNode(node.path, '')
132 if filenode_old.is_binary or node.is_binary:
144 if filenode_old.is_binary or node.is_binary:
133 diff = _('binary file')
145 diff = _('binary file')
134 else:
146 else:
135 f_udiff = differ.get_udiff(filenode_old, node)
147 f_udiff = differ.get_udiff(filenode_old, node)
136 diff = differ.DiffProcessor(f_udiff).raw_diff()
148 diff = differ.DiffProcessor(f_udiff).raw_diff()
137
149
138 cs1 = None
150 cs1 = None
139 cs2 = node.last_changeset.short_id
151 cs2 = node.last_changeset.short_id
140 c.changes.append(('added', node, diff, cs1, cs2))
152 c.changes.append(('added', node, diff, cs1, cs2))
141
153
142 for node in c.changeset.changed:
154 for node in c.changeset.changed:
143 filenode_old = c.changeset_old.get_node(node.path)
155 filenode_old = c.changeset_old.get_node(node.path)
144 if filenode_old.is_binary or node.is_binary:
156 if filenode_old.is_binary or node.is_binary:
145 diff = _('binary file')
157 diff = _('binary file')
146 else:
158 else:
147 f_udiff = differ.get_udiff(filenode_old, node)
159 f_udiff = differ.get_udiff(filenode_old, node)
148 diff = differ.DiffProcessor(f_udiff).raw_diff()
160 diff = differ.DiffProcessor(f_udiff).raw_diff()
149
161
150 cs1 = filenode_old.last_changeset.short_id
162 cs1 = filenode_old.last_changeset.short_id
151 cs2 = node.last_changeset.short_id
163 cs2 = node.last_changeset.short_id
152 c.changes.append(('changed', node, diff, cs1, cs2))
164 c.changes.append(('changed', node, diff, cs1, cs2))
153
165
154 response.content_type = 'text/plain'
166 response.content_type = 'text/plain'
155 if method == 'download':
167 if method == 'download':
156 response.content_disposition = 'attachment; filename=%s.patch' % revision
168 response.content_disposition = 'attachment; filename=%s.patch' % revision
157 parent = True if len(c.changeset.parents) > 0 else False
169 parent = True if len(c.changeset.parents) > 0 else False
158 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
170 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
159
171
160 c.diffs = ''
172 c.diffs = ''
161 for x in c.changes:
173 for x in c.changes:
162 c.diffs += x[2]
174 c.diffs += x[2]
163
175
164 return render('changeset/raw_changeset.html')
176 return render('changeset/raw_changeset.html')
@@ -1,207 +1,215 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # files controller for pylons
3 # files controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 21, 2010
21 Created on April 21, 2010
22 files controller for pylons
22 files controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial import archival
25 from mercurial import archival
26 from pylons import request, response, session, tmpl_context as c, url
26 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.i18n.translation import _
27 from pylons.controllers.util import redirect
28 from pylons.controllers.util import redirect
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.utils import EmptyChangeset
31 from pylons_app.lib.utils import EmptyChangeset
31 from pylons_app.model.hg_model import HgModel
32 from pylons_app.model.hg_model import HgModel
32 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.nodes import FileNode
34 from vcs.nodes import FileNode
34 from vcs.utils import diffs as differ
35 from vcs.utils import diffs as differ
35 import logging
36 import logging
36 import pylons_app.lib.helpers as h
37 import pylons_app.lib.helpers as h
37 import tempfile
38 import tempfile
38
39
39 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
40
41
41 class FilesController(BaseController):
42 class FilesController(BaseController):
42
43
43 @LoginRequired()
44 @LoginRequired()
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 'repository.admin')
46 'repository.admin')
46 def __before__(self):
47 def __before__(self):
47 super(FilesController, self).__before__()
48 super(FilesController, self).__before__()
48 c.file_size_limit = 250 * 1024 #limit of file size to display
49 c.file_size_limit = 250 * 1024 #limit of file size to display
49
50
50 def index(self, repo_name, revision, f_path):
51 def index(self, repo_name, revision, f_path):
51 hg_model = HgModel()
52 hg_model = HgModel()
52 c.repo = repo = hg_model.get_repo(c.repo_name)
53 c.repo = repo = hg_model.get_repo(c.repo_name)
53 revision = request.POST.get('at_rev', None) or revision
54 revision = request.POST.get('at_rev', None) or revision
54
55
55 def get_next_rev(cur):
56 def get_next_rev(cur):
56 max_rev = len(c.repo.revisions) - 1
57 max_rev = len(c.repo.revisions) - 1
57 r = cur + 1
58 r = cur + 1
58 if r > max_rev:
59 if r > max_rev:
59 r = max_rev
60 r = max_rev
60 return r
61 return r
61
62
62 def get_prev_rev(cur):
63 def get_prev_rev(cur):
63 r = cur - 1
64 r = cur - 1
64 return r
65 return r
65
66
66 c.f_path = f_path
67 c.f_path = f_path
67
68
68
69
69 try:
70 try:
70 cur_rev = repo.get_changeset(revision).revision
71 cur_rev = repo.get_changeset(revision).revision
71 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).short_id
72 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).short_id
72 next_rev = repo.get_changeset(get_next_rev(cur_rev)).short_id
73 next_rev = repo.get_changeset(get_next_rev(cur_rev)).short_id
73
74
74 c.url_prev = url('files_home', repo_name=c.repo_name,
75 c.url_prev = url('files_home', repo_name=c.repo_name,
75 revision=prev_rev, f_path=f_path)
76 revision=prev_rev, f_path=f_path)
76 c.url_next = url('files_home', repo_name=c.repo_name,
77 c.url_next = url('files_home', repo_name=c.repo_name,
77 revision=next_rev, f_path=f_path)
78 revision=next_rev, f_path=f_path)
78
79
79 c.changeset = repo.get_changeset(revision)
80 c.changeset = repo.get_changeset(revision)
80
81
81 c.cur_rev = c.changeset.short_id
82 c.cur_rev = c.changeset.short_id
82 c.rev_nr = c.changeset.revision
83 c.rev_nr = c.changeset.revision
83 c.files_list = c.changeset.get_node(f_path)
84 c.files_list = c.changeset.get_node(f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
85 c.file_history = self._get_history(repo, c.files_list, f_path)
85
86
86 except (RepositoryError, ChangesetError):
87 except (RepositoryError, ChangesetError):
87 c.files_list = None
88 c.files_list = None
88
89
89 return render('files/files.html')
90 return render('files/files.html')
90
91
91 def rawfile(self, repo_name, revision, f_path):
92 def rawfile(self, repo_name, revision, f_path):
92 hg_model = HgModel()
93 hg_model = HgModel()
93 c.repo = hg_model.get_repo(c.repo_name)
94 c.repo = hg_model.get_repo(c.repo_name)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
95 file_node = c.repo.get_changeset(revision).get_node(f_path)
95 response.content_type = file_node.mimetype
96 response.content_type = file_node.mimetype
96 response.content_disposition = 'attachment; filename=%s' \
97 response.content_disposition = 'attachment; filename=%s' \
97 % f_path.split('/')[-1]
98 % f_path.split('/')[-1]
98 return file_node.content
99 return file_node.content
99
100
100 def raw(self, repo_name, revision, f_path):
101 def raw(self, repo_name, revision, f_path):
101 hg_model = HgModel()
102 hg_model = HgModel()
102 c.repo = hg_model.get_repo(c.repo_name)
103 c.repo = hg_model.get_repo(c.repo_name)
103 file_node = c.repo.get_changeset(revision).get_node(f_path)
104 file_node = c.repo.get_changeset(revision).get_node(f_path)
104 response.content_type = 'text/plain'
105 response.content_type = 'text/plain'
105
106
106 return file_node.content
107 return file_node.content
107
108
108 def annotate(self, repo_name, revision, f_path):
109 def annotate(self, repo_name, revision, f_path):
109 hg_model = HgModel()
110 hg_model = HgModel()
110 c.repo = hg_model.get_repo(c.repo_name)
111 c.repo = hg_model.get_repo(c.repo_name)
111 cs = c.repo.get_changeset(revision)
112 cs = c.repo.get_changeset(revision)
112 c.file = cs.get_node(f_path)
113 c.file = cs.get_node(f_path)
113 c.file_msg = cs.get_file_message(f_path)
114 c.file_msg = cs.get_file_message(f_path)
114 c.cur_rev = cs.short_id
115 c.cur_rev = cs.short_id
115 c.rev_nr = cs.revision
116 c.rev_nr = cs.revision
116 c.f_path = f_path
117 c.f_path = f_path
117
118
118 return render('files/files_annotate.html')
119 return render('files/files_annotate.html')
119
120
120 def archivefile(self, repo_name, revision, fileformat):
121 def archivefile(self, repo_name, revision, fileformat):
121 archive_specs = {
122 archive_specs = {
122 '.tar.bz2': ('application/x-tar', 'tbz2'),
123 '.tar.bz2': ('application/x-tar', 'tbz2'),
123 '.tar.gz': ('application/x-tar', 'tgz'),
124 '.tar.gz': ('application/x-tar', 'tgz'),
124 '.zip': ('application/zip', 'zip'),
125 '.zip': ('application/zip', 'zip'),
125 }
126 }
126 if not archive_specs.has_key(fileformat):
127 if not archive_specs.has_key(fileformat):
127 return 'Unknown archive type %s' % fileformat
128 return 'Unknown archive type %s' % fileformat
128
129
129 def read_in_chunks(file_object, chunk_size=1024 * 40):
130 def read_in_chunks(file_object, chunk_size=1024 * 40):
130 """Lazy function (generator) to read a file piece by piece.
131 """Lazy function (generator) to read a file piece by piece.
131 Default chunk size: 40k."""
132 Default chunk size: 40k."""
132 while True:
133 while True:
133 data = file_object.read(chunk_size)
134 data = file_object.read(chunk_size)
134 if not data:
135 if not data:
135 break
136 break
136 yield data
137 yield data
137
138
138 archive = tempfile.TemporaryFile()
139 archive = tempfile.TemporaryFile()
139 repo = HgModel().get_repo(repo_name).repo
140 repo = HgModel().get_repo(repo_name).repo
140 fname = '%s-%s%s' % (repo_name, revision, fileformat)
141 fname = '%s-%s%s' % (repo_name, revision, fileformat)
141 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
142 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
142 prefix='%s-%s' % (repo_name, revision))
143 prefix='%s-%s' % (repo_name, revision))
143 response.content_type = archive_specs[fileformat][0]
144 response.content_type = archive_specs[fileformat][0]
144 response.content_disposition = 'attachment; filename=%s' % fname
145 response.content_disposition = 'attachment; filename=%s' % fname
145 archive.seek(0)
146 archive.seek(0)
146 return read_in_chunks(archive)
147 return read_in_chunks(archive)
147
148
148 def diff(self, repo_name, f_path):
149 def diff(self, repo_name, f_path):
149 hg_model = HgModel()
150 hg_model = HgModel()
150 diff1 = request.GET.get('diff1')
151 diff1 = request.GET.get('diff1')
151 diff2 = request.GET.get('diff2')
152 diff2 = request.GET.get('diff2')
152 c.action = request.GET.get('diff')
153 c.action = request.GET.get('diff')
153 c.no_changes = diff1 == diff2
154 c.no_changes = diff1 == diff2
154 c.f_path = f_path
155 c.f_path = f_path
155 c.repo = hg_model.get_repo(c.repo_name)
156 c.repo = hg_model.get_repo(c.repo_name)
156
157
157 try:
158 try:
158 if diff1 not in ['', None, 'None', '0' * 12]:
159 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
159 c.changeset_1 = c.repo.get_changeset(diff1)
160 c.changeset_1 = c.repo.get_changeset(diff1)
160 node1 = c.changeset_1.get_node(f_path)
161 node1 = c.changeset_1.get_node(f_path)
161 else:
162 else:
162 c.changeset_1 = EmptyChangeset()
163 c.changeset_1 = EmptyChangeset()
163 node1 = FileNode('.', '')
164 node1 = FileNode('.', '', changeset=c.changeset_1)
164 if diff2 not in ['', None, 'None', '0' * 12]:
165
166 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
165 c.changeset_2 = c.repo.get_changeset(diff2)
167 c.changeset_2 = c.repo.get_changeset(diff2)
166 node2 = c.changeset_2.get_node(f_path)
168 node2 = c.changeset_2.get_node(f_path)
167 else:
169 else:
168 c.changeset_2 = EmptyChangeset()
170 c.changeset_2 = EmptyChangeset()
169 node2 = FileNode('.', '')
171 node2 = FileNode('.', '', changeset=c.changeset_2)
170 except RepositoryError:
172 except RepositoryError:
171 return redirect(url('files_home',
173 return redirect(url('files_home',
172 repo_name=c.repo_name, f_path=f_path))
174 repo_name=c.repo_name, f_path=f_path))
173
175
174 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.short_id)
176 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.short_id)
175 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.short_id)
177 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.short_id)
178
176 f_udiff = differ.get_udiff(node1, node2)
179 f_udiff = differ.get_udiff(node1, node2)
177
178 diff = differ.DiffProcessor(f_udiff)
180 diff = differ.DiffProcessor(f_udiff)
179
181
180 if c.action == 'download':
182 if c.action == 'download':
181 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
183 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
182 response.content_type = 'text/plain'
184 response.content_type = 'text/plain'
183 response.content_disposition = 'attachment; filename=%s' \
185 response.content_disposition = 'attachment; filename=%s' \
184 % diff_name
186 % diff_name
185 return diff.raw_diff()
187 return diff.raw_diff()
186
188
187 elif c.action == 'raw':
189 elif c.action == 'raw':
188 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
190 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
189 elif c.action == 'diff':
191 elif c.action == 'diff':
190 c.cur_diff = diff.as_html()
192 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
193 c.cur_diff = _('Diff is to big to display')
194 else:
195 c.cur_diff = diff.as_html()
191 else:
196 else:
192 #default option
197 #default option
193 c.cur_diff = diff.as_html()
198 if node1.size > c.file_size_limit or node2.size > c.file_size_limit:
199 c.cur_diff = _('Diff is to big to display')
200 else:
201 c.cur_diff = diff.as_html()
194
202
195 if not c.cur_diff: c.no_changes = True
203 if not c.cur_diff: c.no_changes = True
196 return render('files/file_diff.html')
204 return render('files/file_diff.html')
197
205
198 def _get_history(self, repo, node, f_path):
206 def _get_history(self, repo, node, f_path):
199 from vcs.nodes import NodeKind
207 from vcs.nodes import NodeKind
200 if not node.kind is NodeKind.FILE:
208 if not node.kind is NodeKind.FILE:
201 return []
209 return []
202 changesets = node.history
210 changesets = node.history
203 hist_l = []
211 hist_l = []
204 for chs in changesets:
212 for chs in changesets:
205 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
213 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
206 hist_l.append((chs.short_id, n_desc,))
214 hist_l.append((chs.short_id, n_desc,))
207 return hist_l
215 return hist_l
@@ -1,476 +1,488 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings, UserLog
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings, UserLog
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import datetime
33 import datetime
34 import os
34 import os
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 def get_repo_slug(request):
39 def get_repo_slug(request):
40 return request.environ['pylons.routes_dict'].get('repo_name')
40 return request.environ['pylons.routes_dict'].get('repo_name')
41
41
42 def is_mercurial(environ):
42 def is_mercurial(environ):
43 """
43 """
44 Returns True if request's target is mercurial server - header
44 Returns True if request's target is mercurial server - header
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 """
46 """
47 http_accept = environ.get('HTTP_ACCEPT')
47 http_accept = environ.get('HTTP_ACCEPT')
48 if http_accept and http_accept.startswith('application/mercurial'):
48 if http_accept and http_accept.startswith('application/mercurial'):
49 return True
49 return True
50 return False
50 return False
51
51
52 def action_logger(user, action, repo, ipaddr, sa=None):
52 def action_logger(user, action, repo, ipaddr, sa=None):
53 """
53 """
54 Action logger for various action made by users
54 Action logger for various action made by users
55 """
55 """
56
56
57 if not sa:
57 if not sa:
58 sa = meta.Session
58 sa = meta.Session
59
59
60 try:
60 try:
61 if hasattr(user, 'user_id'):
61 if hasattr(user, 'user_id'):
62 user_id = user.user_id
62 user_id = user.user_id
63 elif isinstance(user, basestring):
63 elif isinstance(user, basestring):
64 user_id = sa.query(User).filter(User.username == user).one()
64 user_id = sa.query(User).filter(User.username == user).one()
65 else:
65 else:
66 raise Exception('You have to provide user object or username')
66 raise Exception('You have to provide user object or username')
67
67
68 repo_name = repo.lstrip('/')
68 repo_name = repo.lstrip('/')
69 user_log = UserLog()
69 user_log = UserLog()
70 user_log.user_id = user_id
70 user_log.user_id = user_id
71 user_log.action = action
71 user_log.action = action
72 user_log.repository_name = repo_name
72 user_log.repository_name = repo_name
73 user_log.repository = sa.query(Repository)\
73 user_log.repository = sa.query(Repository)\
74 .filter(Repository.repo_name == repo_name).one()
74 .filter(Repository.repo_name == repo_name).one()
75 user_log.action_date = datetime.datetime.now()
75 user_log.action_date = datetime.datetime.now()
76 user_log.user_ip = ipaddr
76 user_log.user_ip = ipaddr
77 sa.add(user_log)
77 sa.add(user_log)
78 sa.commit()
78 sa.commit()
79 log.info('Adding user %s, action %s on %s',
79 log.info('Adding user %s, action %s on %s',
80 user.username, action, repo)
80 user.username, action, repo)
81 except Exception, e:
81 except Exception, e:
82 raise
82 raise
83 sa.rollback()
83 sa.rollback()
84 log.error('could not log user action:%s', str(e))
84 log.error('could not log user action:%s', str(e))
85
85
86 def check_repo_dir(paths):
86 def check_repo_dir(paths):
87 repos_path = paths[0][1].split('/')
87 repos_path = paths[0][1].split('/')
88 if repos_path[-1] in ['*', '**']:
88 if repos_path[-1] in ['*', '**']:
89 repos_path = repos_path[:-1]
89 repos_path = repos_path[:-1]
90 if repos_path[0] != '/':
90 if repos_path[0] != '/':
91 repos_path[0] = '/'
91 repos_path[0] = '/'
92 if not os.path.isdir(os.path.join(*repos_path)):
92 if not os.path.isdir(os.path.join(*repos_path)):
93 raise Exception('Not a valid repository in %s' % paths[0][1])
93 raise Exception('Not a valid repository in %s' % paths[0][1])
94
94
95 def check_repo_fast(repo_name, base_path):
95 def check_repo_fast(repo_name, base_path):
96 if os.path.isdir(os.path.join(base_path, repo_name)):return False
96 if os.path.isdir(os.path.join(base_path, repo_name)):return False
97 return True
97 return True
98
98
99 def check_repo(repo_name, base_path, verify=True):
99 def check_repo(repo_name, base_path, verify=True):
100
100
101 repo_path = os.path.join(base_path, repo_name)
101 repo_path = os.path.join(base_path, repo_name)
102
102
103 try:
103 try:
104 if not check_repo_fast(repo_name, base_path):
104 if not check_repo_fast(repo_name, base_path):
105 return False
105 return False
106 r = hg.repository(ui.ui(), repo_path)
106 r = hg.repository(ui.ui(), repo_path)
107 if verify:
107 if verify:
108 hg.verify(r)
108 hg.verify(r)
109 #here we hnow that repo exists it was verified
109 #here we hnow that repo exists it was verified
110 log.info('%s repo is already created', repo_name)
110 log.info('%s repo is already created', repo_name)
111 return False
111 return False
112 except RepoError:
112 except RepoError:
113 #it means that there is no valid repo there...
113 #it means that there is no valid repo there...
114 log.info('%s repo is free for creation', repo_name)
114 log.info('%s repo is free for creation', repo_name)
115 return True
115 return True
116
116
117 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
117 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
118 while True:
118 while True:
119 ok = raw_input(prompt)
119 ok = raw_input(prompt)
120 if ok in ('y', 'ye', 'yes'): return True
120 if ok in ('y', 'ye', 'yes'): return True
121 if ok in ('n', 'no', 'nop', 'nope'): return False
121 if ok in ('n', 'no', 'nop', 'nope'): return False
122 retries = retries - 1
122 retries = retries - 1
123 if retries < 0: raise IOError
123 if retries < 0: raise IOError
124 print complaint
124 print complaint
125
125
126 @cache_region('super_short_term', 'cached_hg_ui')
126 @cache_region('super_short_term', 'cached_hg_ui')
127 def get_hg_ui_cached():
127 def get_hg_ui_cached():
128 try:
128 try:
129 sa = meta.Session
129 sa = meta.Session
130 ret = sa.query(HgAppUi).all()
130 ret = sa.query(HgAppUi).all()
131 finally:
131 finally:
132 meta.Session.remove()
132 meta.Session.remove()
133 return ret
133 return ret
134
134
135
135
136 def get_hg_settings():
136 def get_hg_settings():
137 try:
137 try:
138 sa = meta.Session
138 sa = meta.Session
139 ret = sa.query(HgAppSettings).all()
139 ret = sa.query(HgAppSettings).all()
140 finally:
140 finally:
141 meta.Session.remove()
141 meta.Session.remove()
142
142
143 if not ret:
143 if not ret:
144 raise Exception('Could not get application settings !')
144 raise Exception('Could not get application settings !')
145 settings = {}
145 settings = {}
146 for each in ret:
146 for each in ret:
147 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
147 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
148
148
149 return settings
149 return settings
150
150
151 def get_hg_ui_settings():
151 def get_hg_ui_settings():
152 try:
152 try:
153 sa = meta.Session
153 sa = meta.Session
154 ret = sa.query(HgAppUi).all()
154 ret = sa.query(HgAppUi).all()
155 finally:
155 finally:
156 meta.Session.remove()
156 meta.Session.remove()
157
157
158 if not ret:
158 if not ret:
159 raise Exception('Could not get application ui settings !')
159 raise Exception('Could not get application ui settings !')
160 settings = {}
160 settings = {}
161 for each in ret:
161 for each in ret:
162 k = each.ui_key
162 k = each.ui_key
163 v = each.ui_value
163 v = each.ui_value
164 if k == '/':
164 if k == '/':
165 k = 'root_path'
165 k = 'root_path'
166
166
167 if k.find('.') != -1:
167 if k.find('.') != -1:
168 k = k.replace('.', '_')
168 k = k.replace('.', '_')
169
169
170 if each.ui_section == 'hooks':
170 if each.ui_section == 'hooks':
171 v = each.ui_active
171 v = each.ui_active
172
172
173 settings[each.ui_section + '_' + k] = v
173 settings[each.ui_section + '_' + k] = v
174
174
175 return settings
175 return settings
176
176
177 #propagated from mercurial documentation
177 #propagated from mercurial documentation
178 ui_sections = ['alias', 'auth',
178 ui_sections = ['alias', 'auth',
179 'decode/encode', 'defaults',
179 'decode/encode', 'defaults',
180 'diff', 'email',
180 'diff', 'email',
181 'extensions', 'format',
181 'extensions', 'format',
182 'merge-patterns', 'merge-tools',
182 'merge-patterns', 'merge-tools',
183 'hooks', 'http_proxy',
183 'hooks', 'http_proxy',
184 'smtp', 'patch',
184 'smtp', 'patch',
185 'paths', 'profiling',
185 'paths', 'profiling',
186 'server', 'trusted',
186 'server', 'trusted',
187 'ui', 'web', ]
187 'ui', 'web', ]
188
188
189 def make_ui(read_from='file', path=None, checkpaths=True):
189 def make_ui(read_from='file', path=None, checkpaths=True):
190 """
190 """
191 A function that will read python rc files or database
191 A function that will read python rc files or database
192 and make an mercurial ui object from read options
192 and make an mercurial ui object from read options
193
193
194 @param path: path to mercurial config file
194 @param path: path to mercurial config file
195 @param checkpaths: check the path
195 @param checkpaths: check the path
196 @param read_from: read from 'file' or 'db'
196 @param read_from: read from 'file' or 'db'
197 """
197 """
198
198
199 baseui = ui.ui()
199 baseui = ui.ui()
200
200
201 if read_from == 'file':
201 if read_from == 'file':
202 if not os.path.isfile(path):
202 if not os.path.isfile(path):
203 log.warning('Unable to read config file %s' % path)
203 log.warning('Unable to read config file %s' % path)
204 return False
204 return False
205 log.debug('reading hgrc from %s', path)
205 log.debug('reading hgrc from %s', path)
206 cfg = config.config()
206 cfg = config.config()
207 cfg.read(path)
207 cfg.read(path)
208 for section in ui_sections:
208 for section in ui_sections:
209 for k, v in cfg.items(section):
209 for k, v in cfg.items(section):
210 baseui.setconfig(section, k, v)
210 baseui.setconfig(section, k, v)
211 log.debug('settings ui from file[%s]%s:%s', section, k, v)
211 log.debug('settings ui from file[%s]%s:%s', section, k, v)
212 if checkpaths:check_repo_dir(cfg.items('paths'))
212 if checkpaths:check_repo_dir(cfg.items('paths'))
213
213
214
214
215 elif read_from == 'db':
215 elif read_from == 'db':
216 hg_ui = get_hg_ui_cached()
216 hg_ui = get_hg_ui_cached()
217 for ui_ in hg_ui:
217 for ui_ in hg_ui:
218 if ui_.ui_active:
218 if ui_.ui_active:
219 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
219 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
220 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
220 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
221
221
222
222
223 return baseui
223 return baseui
224
224
225
225
226 def set_hg_app_config(config):
226 def set_hg_app_config(config):
227 hgsettings = get_hg_settings()
227 hgsettings = get_hg_settings()
228
228
229 for k, v in hgsettings.items():
229 for k, v in hgsettings.items():
230 config[k] = v
230 config[k] = v
231
231
232 def invalidate_cache(name, *args):
232 def invalidate_cache(name, *args):
233 """Invalidates given name cache"""
233 """Invalidates given name cache"""
234
234
235 from beaker.cache import region_invalidate
235 from beaker.cache import region_invalidate
236 log.info('INVALIDATING CACHE FOR %s', name)
236 log.info('INVALIDATING CACHE FOR %s', name)
237
237
238 """propagate our arguments to make sure invalidation works. First
238 """propagate our arguments to make sure invalidation works. First
239 argument has to be the name of cached func name give to cache decorator
239 argument has to be the name of cached func name give to cache decorator
240 without that the invalidation would not work"""
240 without that the invalidation would not work"""
241 tmp = [name]
241 tmp = [name]
242 tmp.extend(args)
242 tmp.extend(args)
243 args = tuple(tmp)
243 args = tuple(tmp)
244
244
245 if name == 'cached_repo_list':
245 if name == 'cached_repo_list':
246 from pylons_app.model.hg_model import _get_repos_cached
246 from pylons_app.model.hg_model import _get_repos_cached
247 region_invalidate(_get_repos_cached, None, *args)
247 region_invalidate(_get_repos_cached, None, *args)
248
248
249 if name == 'full_changelog':
249 if name == 'full_changelog':
250 from pylons_app.model.hg_model import _full_changelog_cached
250 from pylons_app.model.hg_model import _full_changelog_cached
251 region_invalidate(_full_changelog_cached, None, *args)
251 region_invalidate(_full_changelog_cached, None, *args)
252
252
253 class EmptyChangeset(BaseChangeset):
253 class EmptyChangeset(BaseChangeset):
254 """
255 An dummy empty changeset.
256 """
254
257
255 revision = -1
258 revision = -1
256 message = ''
259 message = ''
257 author = ''
260 author = ''
258
261 date = ''
259 @LazyProperty
262 @LazyProperty
260 def raw_id(self):
263 def raw_id(self):
261 """
264 """
262 Returns raw string identifing this changeset, useful for web
265 Returns raw string identifing this changeset, useful for web
263 representation.
266 representation.
264 """
267 """
265 return '0' * 40
268 return '0' * 40
266
269
267 @LazyProperty
270 @LazyProperty
268 def short_id(self):
271 def short_id(self):
269 return self.raw_id[:12]
272 return self.raw_id[:12]
270
273
274 def get_file_changeset(self, path):
275 return self
276
277 def get_file_content(self, path):
278 return u''
279
280 def get_file_size(self, path):
281 return 0
282
271 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
283 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
272 """
284 """
273 maps all found repositories into db
285 maps all found repositories into db
274 """
286 """
275 from pylons_app.model.repo_model import RepoModel
287 from pylons_app.model.repo_model import RepoModel
276
288
277 sa = meta.Session
289 sa = meta.Session
278 user = sa.query(User).filter(User.admin == True).first()
290 user = sa.query(User).filter(User.admin == True).first()
279
291
280 rm = RepoModel()
292 rm = RepoModel()
281
293
282 for name, repo in initial_repo_list.items():
294 for name, repo in initial_repo_list.items():
283 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
295 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
284 log.info('repository %s not found creating default', name)
296 log.info('repository %s not found creating default', name)
285
297
286 form_data = {
298 form_data = {
287 'repo_name':name,
299 'repo_name':name,
288 'description':repo.description if repo.description != 'unknown' else \
300 'description':repo.description if repo.description != 'unknown' else \
289 'auto description for %s' % name,
301 'auto description for %s' % name,
290 'private':False
302 'private':False
291 }
303 }
292 rm.create(form_data, user, just_db=True)
304 rm.create(form_data, user, just_db=True)
293
305
294
306
295 if remove_obsolete:
307 if remove_obsolete:
296 #remove from database those repositories that are not in the filesystem
308 #remove from database those repositories that are not in the filesystem
297 for repo in sa.query(Repository).all():
309 for repo in sa.query(Repository).all():
298 if repo.repo_name not in initial_repo_list.keys():
310 if repo.repo_name not in initial_repo_list.keys():
299 sa.delete(repo)
311 sa.delete(repo)
300 sa.commit()
312 sa.commit()
301
313
302
314
303 meta.Session.remove()
315 meta.Session.remove()
304
316
305 from UserDict import DictMixin
317 from UserDict import DictMixin
306
318
307 class OrderedDict(dict, DictMixin):
319 class OrderedDict(dict, DictMixin):
308
320
309 def __init__(self, *args, **kwds):
321 def __init__(self, *args, **kwds):
310 if len(args) > 1:
322 if len(args) > 1:
311 raise TypeError('expected at most 1 arguments, got %d' % len(args))
323 raise TypeError('expected at most 1 arguments, got %d' % len(args))
312 try:
324 try:
313 self.__end
325 self.__end
314 except AttributeError:
326 except AttributeError:
315 self.clear()
327 self.clear()
316 self.update(*args, **kwds)
328 self.update(*args, **kwds)
317
329
318 def clear(self):
330 def clear(self):
319 self.__end = end = []
331 self.__end = end = []
320 end += [None, end, end] # sentinel node for doubly linked list
332 end += [None, end, end] # sentinel node for doubly linked list
321 self.__map = {} # key --> [key, prev, next]
333 self.__map = {} # key --> [key, prev, next]
322 dict.clear(self)
334 dict.clear(self)
323
335
324 def __setitem__(self, key, value):
336 def __setitem__(self, key, value):
325 if key not in self:
337 if key not in self:
326 end = self.__end
338 end = self.__end
327 curr = end[1]
339 curr = end[1]
328 curr[2] = end[1] = self.__map[key] = [key, curr, end]
340 curr[2] = end[1] = self.__map[key] = [key, curr, end]
329 dict.__setitem__(self, key, value)
341 dict.__setitem__(self, key, value)
330
342
331 def __delitem__(self, key):
343 def __delitem__(self, key):
332 dict.__delitem__(self, key)
344 dict.__delitem__(self, key)
333 key, prev, next = self.__map.pop(key)
345 key, prev, next = self.__map.pop(key)
334 prev[2] = next
346 prev[2] = next
335 next[1] = prev
347 next[1] = prev
336
348
337 def __iter__(self):
349 def __iter__(self):
338 end = self.__end
350 end = self.__end
339 curr = end[2]
351 curr = end[2]
340 while curr is not end:
352 while curr is not end:
341 yield curr[0]
353 yield curr[0]
342 curr = curr[2]
354 curr = curr[2]
343
355
344 def __reversed__(self):
356 def __reversed__(self):
345 end = self.__end
357 end = self.__end
346 curr = end[1]
358 curr = end[1]
347 while curr is not end:
359 while curr is not end:
348 yield curr[0]
360 yield curr[0]
349 curr = curr[1]
361 curr = curr[1]
350
362
351 def popitem(self, last=True):
363 def popitem(self, last=True):
352 if not self:
364 if not self:
353 raise KeyError('dictionary is empty')
365 raise KeyError('dictionary is empty')
354 if last:
366 if last:
355 key = reversed(self).next()
367 key = reversed(self).next()
356 else:
368 else:
357 key = iter(self).next()
369 key = iter(self).next()
358 value = self.pop(key)
370 value = self.pop(key)
359 return key, value
371 return key, value
360
372
361 def __reduce__(self):
373 def __reduce__(self):
362 items = [[k, self[k]] for k in self]
374 items = [[k, self[k]] for k in self]
363 tmp = self.__map, self.__end
375 tmp = self.__map, self.__end
364 del self.__map, self.__end
376 del self.__map, self.__end
365 inst_dict = vars(self).copy()
377 inst_dict = vars(self).copy()
366 self.__map, self.__end = tmp
378 self.__map, self.__end = tmp
367 if inst_dict:
379 if inst_dict:
368 return (self.__class__, (items,), inst_dict)
380 return (self.__class__, (items,), inst_dict)
369 return self.__class__, (items,)
381 return self.__class__, (items,)
370
382
371 def keys(self):
383 def keys(self):
372 return list(self)
384 return list(self)
373
385
374 setdefault = DictMixin.setdefault
386 setdefault = DictMixin.setdefault
375 update = DictMixin.update
387 update = DictMixin.update
376 pop = DictMixin.pop
388 pop = DictMixin.pop
377 values = DictMixin.values
389 values = DictMixin.values
378 items = DictMixin.items
390 items = DictMixin.items
379 iterkeys = DictMixin.iterkeys
391 iterkeys = DictMixin.iterkeys
380 itervalues = DictMixin.itervalues
392 itervalues = DictMixin.itervalues
381 iteritems = DictMixin.iteritems
393 iteritems = DictMixin.iteritems
382
394
383 def __repr__(self):
395 def __repr__(self):
384 if not self:
396 if not self:
385 return '%s()' % (self.__class__.__name__,)
397 return '%s()' % (self.__class__.__name__,)
386 return '%s(%r)' % (self.__class__.__name__, self.items())
398 return '%s(%r)' % (self.__class__.__name__, self.items())
387
399
388 def copy(self):
400 def copy(self):
389 return self.__class__(self)
401 return self.__class__(self)
390
402
391 @classmethod
403 @classmethod
392 def fromkeys(cls, iterable, value=None):
404 def fromkeys(cls, iterable, value=None):
393 d = cls()
405 d = cls()
394 for key in iterable:
406 for key in iterable:
395 d[key] = value
407 d[key] = value
396 return d
408 return d
397
409
398 def __eq__(self, other):
410 def __eq__(self, other):
399 if isinstance(other, OrderedDict):
411 if isinstance(other, OrderedDict):
400 return len(self) == len(other) and self.items() == other.items()
412 return len(self) == len(other) and self.items() == other.items()
401 return dict.__eq__(self, other)
413 return dict.__eq__(self, other)
402
414
403 def __ne__(self, other):
415 def __ne__(self, other):
404 return not self == other
416 return not self == other
405
417
406
418
407 #===============================================================================
419 #===============================================================================
408 # TEST FUNCTIONS
420 # TEST FUNCTIONS
409 #===============================================================================
421 #===============================================================================
410 def create_test_index(repo_location, full_index):
422 def create_test_index(repo_location, full_index):
411 """Makes default test index
423 """Makes default test index
412 @param repo_location:
424 @param repo_location:
413 @param full_index:
425 @param full_index:
414 """
426 """
415 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
427 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
416 from pylons_app.lib.pidlock import DaemonLock, LockHeld
428 from pylons_app.lib.pidlock import DaemonLock, LockHeld
417 from pylons_app.lib.indexers import IDX_LOCATION
429 from pylons_app.lib.indexers import IDX_LOCATION
418 import shutil
430 import shutil
419
431
420 if os.path.exists(IDX_LOCATION):
432 if os.path.exists(IDX_LOCATION):
421 shutil.rmtree(IDX_LOCATION)
433 shutil.rmtree(IDX_LOCATION)
422
434
423 try:
435 try:
424 l = DaemonLock()
436 l = DaemonLock()
425 WhooshIndexingDaemon(repo_location=repo_location)\
437 WhooshIndexingDaemon(repo_location=repo_location)\
426 .run(full_index=full_index)
438 .run(full_index=full_index)
427 l.release()
439 l.release()
428 except LockHeld:
440 except LockHeld:
429 pass
441 pass
430
442
431 def create_test_env(repos_test_path, config):
443 def create_test_env(repos_test_path, config):
432 """Makes a fresh database and
444 """Makes a fresh database and
433 install test repository into tmp dir
445 install test repository into tmp dir
434 """
446 """
435 from pylons_app.lib.db_manage import DbManage
447 from pylons_app.lib.db_manage import DbManage
436 import tarfile
448 import tarfile
437 import shutil
449 import shutil
438 from os.path import dirname as dn, join as jn, abspath
450 from os.path import dirname as dn, join as jn, abspath
439
451
440 log = logging.getLogger('TestEnvCreator')
452 log = logging.getLogger('TestEnvCreator')
441 # create logger
453 # create logger
442 log.setLevel(logging.DEBUG)
454 log.setLevel(logging.DEBUG)
443 log.propagate = True
455 log.propagate = True
444 # create console handler and set level to debug
456 # create console handler and set level to debug
445 ch = logging.StreamHandler()
457 ch = logging.StreamHandler()
446 ch.setLevel(logging.DEBUG)
458 ch.setLevel(logging.DEBUG)
447
459
448 # create formatter
460 # create formatter
449 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
461 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
450
462
451 # add formatter to ch
463 # add formatter to ch
452 ch.setFormatter(formatter)
464 ch.setFormatter(formatter)
453
465
454 # add ch to logger
466 # add ch to logger
455 log.addHandler(ch)
467 log.addHandler(ch)
456
468
457 #PART ONE create db
469 #PART ONE create db
458 log.debug('making test db')
470 log.debug('making test db')
459 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
471 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
460 dbmanage = DbManage(log_sql=True, dbname=dbname, tests=True)
472 dbmanage = DbManage(log_sql=True, dbname=dbname, tests=True)
461 dbmanage.create_tables(override=True)
473 dbmanage.create_tables(override=True)
462 dbmanage.config_prompt(repos_test_path)
474 dbmanage.config_prompt(repos_test_path)
463 dbmanage.create_default_user()
475 dbmanage.create_default_user()
464 dbmanage.admin_prompt()
476 dbmanage.admin_prompt()
465 dbmanage.create_permissions()
477 dbmanage.create_permissions()
466 dbmanage.populate_default_permissions()
478 dbmanage.populate_default_permissions()
467
479
468 #PART TWO make test repo
480 #PART TWO make test repo
469 log.debug('making test vcs repo')
481 log.debug('making test vcs repo')
470 if os.path.isdir('/tmp/vcs_test'):
482 if os.path.isdir('/tmp/vcs_test'):
471 shutil.rmtree('/tmp/vcs_test')
483 shutil.rmtree('/tmp/vcs_test')
472
484
473 cur_dir = dn(dn(abspath(__file__)))
485 cur_dir = dn(dn(abspath(__file__)))
474 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
486 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
475 tar.extractall('/tmp')
487 tar.extractall('/tmp')
476 tar.close()
488 tar.close()
General Comments 0
You need to be logged in to leave comments. Login now