Mercurial > kallithea
changeset 8015:d2319cb2ba9b
Merge stable
author | Mads Kiilerich <mads@kiilerich.com> |
---|---|
date | Thu, 19 Dec 2019 20:50:33 +0100 |
parents | e8e9f33e9ff6 (diff) 01dbd21d206c (current diff) |
children | b84f495d82ce |
files | |
diffstat | 54 files changed, 240 insertions(+), 537 deletions(-) [+] |
line wrap: on
line diff
--- a/kallithea/__init__.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/__init__.py Thu Dec 19 20:50:33 2019 +0100 @@ -31,7 +31,7 @@ import sys -VERSION = (0, 5, 0) +VERSION = (0, 5, 99) BACKENDS = { 'hg': 'Mercurial repository', 'git': 'Git repository',
--- a/kallithea/bin/kallithea_api.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/bin/kallithea_api.py Thu Dec 19 20:50:33 2019 +0100 @@ -101,7 +101,7 @@ parser.error('Please specify method name') try: - margs = dict(map(lambda s: s.split(':', 1), other)) + margs = dict(s.split(':', 1) for s in other) except ValueError: sys.stderr.write('Error parsing arguments \n') sys.exit()
--- a/kallithea/bin/kallithea_cli_base.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/bin/kallithea_cli_base.py Thu Dec 19 20:50:33 2019 +0100 @@ -12,8 +12,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import cStringIO import functools +import io import logging.config import os import re @@ -44,7 +44,7 @@ return m.group(0) with open(ini_file_name) as f: - return re.sub(r'^\[([^:]+):(.*)]', repl, f.read(), flags=re.MULTILINE) + return re.sub(r'^\[([^:]+):(.*)]', repl, f.read().decode(), flags=re.MULTILINE) # This placeholder is the main entry point for the kallithea-cli command @@ -71,8 +71,8 @@ def runtime_wrapper(config_file, *args, **kwargs): path_to_ini_file = os.path.realpath(config_file) kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file) - config_bytes = read_config(path_to_ini_file, strip_section_prefix=annotated.__name__) - logging.config.fileConfig(cStringIO.StringIO(config_bytes)) + config_string = read_config(path_to_ini_file, strip_section_prefix=annotated.__name__) + logging.config.fileConfig(io.StringIO(config_string)) if config_file_initialize_app: kallithea.config.middleware.make_app_without_logging(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf) kallithea.lib.utils.setup_cache_regions(kallithea.CONFIG)
--- a/kallithea/config/app_cfg.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/config/app_cfg.py Thu Dec 19 20:50:33 2019 +0100 @@ -34,7 +34,7 @@ import kallithea.lib.locale import kallithea.model.base -from kallithea.lib.auth import set_available_permissions +import kallithea.model.meta from kallithea.lib.middleware.https_fixup import HttpsFixup from kallithea.lib.middleware.permanent_repo_url import PermanentRepoUrl from kallithea.lib.middleware.simplegit import SimpleGit @@ -162,7 +162,6 @@ load_rcextensions(root_path=config['here']) - set_available_permissions(config) repos_path = make_ui().configitems('paths')[0][1] config['base_path'] = repos_path set_app_settings(config) @@ -183,6 +182,8 @@ check_git_version() + kallithea.model.meta.Session.remove() + hooks.register('configure_new_app', setup_configuration)
--- a/kallithea/config/routing.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/config/routing.py Thu Dec 19 20:50:33 2019 +0100 @@ -86,7 +86,7 @@ #========================================================================== # MAIN PAGE - rmap.connect('home', '/', controller='home', action='index') + rmap.connect('home', '/', controller='home') rmap.connect('about', '/about', controller='home', action='about') rmap.redirect('/favicon.ico', '/images/favicon.ico') rmap.connect('repo_switcher_data', '/_repos', controller='home', @@ -106,7 +106,7 @@ m.connect("repos", "/repos", action="create", conditions=dict(method=["POST"])) m.connect("repos", "/repos", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("new_repo", "/create_repository", action="create_repository", conditions=dict(method=["GET"])) m.connect("update_repo", "/repos/{repo_name:.*?}", @@ -121,7 +121,7 @@ m.connect("repos_groups", "/repo_groups", action="create", conditions=dict(method=["POST"])) m.connect("repos_groups", "/repo_groups", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("new_repos_group", "/repo_groups/new", action="new", conditions=dict(method=["GET"])) m.connect("update_repos_group", "/repo_groups/{group_name:.*?}", @@ -161,9 +161,9 @@ m.connect("new_user", "/users/new", action="create", conditions=dict(method=["POST"])) m.connect("users", "/users", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("formatted_users", "/users.{format}", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("new_user", "/users/new", action="new", conditions=dict(method=["GET"])) m.connect("update_user", "/users/{id}", @@ -216,7 +216,7 @@ m.connect("users_groups", "/user_groups", action="create", conditions=dict(method=["POST"])) m.connect("users_groups", "/user_groups", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("new_users_group", "/user_groups/new", action="new", conditions=dict(method=["GET"])) m.connect("update_users_group", "/user_groups/{id}", @@ -263,8 +263,7 @@ # ADMIN DEFAULTS ROUTES with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='admin/defaults') as m: - m.connect('defaults', '/defaults', - action="index") + m.connect('defaults', '/defaults') m.connect('defaults_update', 'defaults/{id}/update', action="update", conditions=dict(method=["POST"])) @@ -370,7 +369,7 @@ m.connect("gists", "/gists", action="create", conditions=dict(method=["POST"])) m.connect("gists", "/gists", - action="index", conditions=dict(method=["GET"])) + conditions=dict(method=["GET"])) m.connect("new_gist", "/gists/new", action="new", conditions=dict(method=["GET"])) @@ -396,7 +395,7 @@ # ADMIN MAIN PAGES with rmap.submapper(path_prefix=ADMIN_PREFIX, controller='admin/admin') as m: - m.connect('admin_home', '', action='index') + m.connect('admin_home', '') m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9. _-]*}', action='add_repo') #========================================================================== @@ -408,7 +407,7 @@ # USER JOURNAL rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, - controller='journal', action='index') + controller='journal') rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX, controller='journal', action='journal_rss') rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX, @@ -602,7 +601,7 @@ rmap.connect('compare_home', '/{repo_name:.*?}/compare', - controller='compare', action='index', + controller='compare', conditions=dict(function=check_repo)) rmap.connect('compare_url', @@ -616,7 +615,7 @@ rmap.connect('pullrequest_home', '/{repo_name:.*?}/pull-request/new', controller='pullrequests', - action='index', conditions=dict(function=check_repo, + conditions=dict(function=check_repo, method=["GET"])) rmap.connect('pullrequest_repo_info', @@ -674,7 +673,7 @@ controller='changelog', conditions=dict(function=check_repo)) rmap.connect('changelog_file_home', '/{repo_name:.*?}/changelog/{revision}/{f_path:.*}', - controller='changelog', f_path=None, + controller='changelog', conditions=dict(function=check_repo)) rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}', @@ -719,8 +718,8 @@ rmap.connect('files_annotate_home', '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}', - controller='files', action='index', revision='tip', - f_path='', annotate=True, conditions=dict(function=check_repo)) + controller='files', revision='tip', + f_path='', annotate='1', conditions=dict(function=check_repo)) rmap.connect('files_edit_home', '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
--- a/kallithea/controllers/admin/admin.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/admin/admin.py Thu Dec 19 20:50:33 2019 +0100 @@ -36,7 +36,6 @@ from whoosh.qparser.dateparse import DateParserPlugin from whoosh.qparser.default import QueryParser -from kallithea.config.routing import url from kallithea.lib.auth import HasPermissionAnyDecorator, LoginRequired from kallithea.lib.base import BaseController, render from kallithea.lib.indexers import JOURNAL_SCHEMA @@ -139,10 +138,8 @@ p = safe_int(request.GET.get('page'), 1) - def url_generator(**kw): - return url.current(filter=c.search_term, **kw) - - c.users_log = Page(users_log, page=p, items_per_page=10, url=url_generator) + c.users_log = Page(users_log, page=p, items_per_page=10, + filter=c.search_term) if request.environ.get('HTTP_X_PARTIAL_XHR'): return render('admin/admin_log.html')
--- a/kallithea/controllers/admin/gists.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/admin/gists.py Thu Dec 19 20:50:33 2019 +0100 @@ -71,6 +71,11 @@ not_default_user = not request.authuser.is_default_user c.show_private = request.GET.get('private') and not_default_user c.show_public = request.GET.get('public') and not_default_user + url_params = {} + if c.show_public: + url_params['public'] = 1 + elif c.show_private: + url_params['private'] = 1 gists = Gist().query() \ .filter_by(is_expired=False) \ @@ -97,7 +102,8 @@ c.gists = gists p = safe_int(request.GET.get('page'), 1) - c.gists_pager = Page(c.gists, page=p, items_per_page=10) + c.gists_pager = Page(c.gists, page=p, items_per_page=10, + **url_params) return render('admin/gists/index.html') @LoginRequired()
--- a/kallithea/controllers/admin/repo_groups.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/admin/repo_groups.py Thu Dec 19 20:50:33 2019 +0100 @@ -25,7 +25,6 @@ :license: GPLv3, see LICENSE.md for more details. """ -import itertools import logging import traceback @@ -42,7 +41,7 @@ from kallithea.lib import helpers as h from kallithea.lib.auth import HasPermissionAny, HasRepoGroupPermissionLevel, HasRepoGroupPermissionLevelDecorator, LoginRequired from kallithea.lib.base import BaseController, render -from kallithea.lib.utils2 import safe_int +from kallithea.lib.utils2 import safe_int, safe_unicode from kallithea.model.db import RepoGroup, Repository from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm from kallithea.model.meta import Session @@ -93,10 +92,8 @@ return data def _revoke_perms_on_yourself(self, form_result): - _up = filter(lambda u: request.authuser.username == u[0], - form_result['perms_updates']) - _new = filter(lambda u: request.authuser.username == u[0], - form_result['perms_new']) + _up = [u for u in form_result['perms_updates'] if request.authuser.username == u[0]] + _new = [u for u in form_result['perms_new'] if request.authuser.username == u[0]] if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin': return True return False @@ -120,9 +117,7 @@ ) for repo_gr in group_iter: - children_groups = map(h.safe_unicode, - itertools.chain((g.name for g in repo_gr.parents), - (x.name for x in [repo_gr]))) + children_groups = [safe_unicode(g.name) for g in repo_gr.parents] + [safe_unicode(repo_gr.name)] repo_count = repo_gr.repositories.count() repo_groups_data.append({ "raw_name": repo_gr.group_name,
--- a/kallithea/controllers/admin/user_groups.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/admin/user_groups.py Thu Dec 19 20:50:33 2019 +0100 @@ -32,7 +32,7 @@ from formencode import htmlfill from sqlalchemy.orm import joinedload from sqlalchemy.sql.expression import func -from tg import app_globals, config, request +from tg import app_globals, request from tg import tmpl_context as c from tg.i18n import ugettext as _ from webob.exc import HTTPFound, HTTPInternalServerError @@ -61,7 +61,6 @@ @LoginRequired(allow_default_user=True) def _before(self, *args, **kwargs): super(UserGroupsController, self)._before(*args, **kwargs) - c.available_permissions = config['available_permissions'] def __load_data(self, user_group_id): c.group_members_obj = sorted((x.user for x in c.user_group.members),
--- a/kallithea/controllers/admin/users.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/admin/users.py Thu Dec 19 20:50:33 2019 +0100 @@ -31,7 +31,7 @@ import formencode from formencode import htmlfill from sqlalchemy.sql.expression import func -from tg import app_globals, config, request +from tg import app_globals, request from tg import tmpl_context as c from tg.i18n import ugettext as _ from webob.exc import HTTPFound, HTTPNotFound @@ -63,7 +63,6 @@ @HasPermissionAnyDecorator('hg.admin') def _before(self, *args, **kwargs): super(UsersController, self)._before(*args, **kwargs) - c.available_permissions = config['available_permissions'] def index(self, format='html'): c.users_list = User.query().order_by(User.username) \
--- a/kallithea/controllers/api/__init__.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/api/__init__.py Thu Dec 19 20:50:33 2019 +0100 @@ -168,8 +168,8 @@ # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] - defaults = map(type, argspec[3] or []) - default_empty = types.NotImplementedType + defaults = [type(arg) for arg in argspec[3] or []] + default_empty = type(NotImplemented) # kw arguments required by this method func_kwargs = dict(itertools.izip_longest(reversed(arglist), reversed(defaults),
--- a/kallithea/controllers/changelog.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/changelog.py Thu Dec 19 20:50:33 2019 +0100 @@ -38,7 +38,7 @@ from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired from kallithea.lib.base import BaseRepoController, render from kallithea.lib.graphmod import graph_data -from kallithea.lib.page import RepoPage +from kallithea.lib.page import Page from kallithea.lib.utils2 import safe_int, safe_str from kallithea.lib.vcs.exceptions import ChangesetDoesNotExistError, ChangesetError, EmptyRepositoryError, NodeDoesNotExistError, RepositoryError @@ -113,14 +113,13 @@ except RepositoryError as e: h.flash(safe_str(e), category='warning') raise HTTPFound(location=h.url('changelog_home', repo_name=repo_name)) - collection = list(reversed(collection)) else: collection = c.db_repo_scm_instance.get_changesets(start=0, end=revision, - branch_name=branch_name) + branch_name=branch_name, reverse=True) c.total_cs = len(collection) - c.cs_pagination = RepoPage(collection, page=p, item_count=c.total_cs, - items_per_page=c.size, branch=branch_name,) + c.cs_pagination = Page(collection, page=p, item_count=c.total_cs, items_per_page=c.size, + branch=branch_name) page_revisions = [x.raw_id for x in c.cs_pagination] c.cs_comments = c.db_repo.get_comments(page_revisions)
--- a/kallithea/controllers/changeset.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/changeset.py Thu Dec 19 20:50:33 2019 +0100 @@ -25,6 +25,7 @@ :license: GPLv3, see LICENSE.md for more details. """ +import binascii import logging import traceback from collections import OrderedDict, defaultdict @@ -65,7 +66,7 @@ def get_ignore_ws(fid, GET): ig_ws_global = GET.get('ignorews') - ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid)) + ig_ws = [k for k in GET.getall(fid) if k.startswith('WS')] if ig_ws: try: return int(ig_ws[0].split(':')[-1]) @@ -108,9 +109,9 @@ def get_line_ctx(fid, GET): ln_ctx_global = GET.get('context') if fid: - ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid)) + ln_ctx = [k for k in GET.getall(fid) if k.startswith('C')] else: - _ln_ctx = filter(lambda k: k.startswith('C'), GET) + _ln_ctx = [k for k in GET if k.startswith('C')] ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global if ln_ctx: ln_ctx = [ln_ctx] @@ -395,6 +396,8 @@ c.changeset = c.cs_ranges[0] c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id for x in c.changeset.parents]) + c.changeset_graft_source_hash = c.changeset.extra.get(b'source', b'') + c.changeset_transplant_source_hash = binascii.hexlify(c.changeset.extra.get(b'transplant_source', b'')) if method == 'download': response.content_type = 'text/plain' response.content_disposition = 'attachment; filename=%s.diff' \
--- a/kallithea/controllers/feed.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/feed.py Thu Dec 19 20:50:33 2019 +0100 @@ -100,19 +100,19 @@ desc_msg.append('\n\n') desc_msg.append(raw_diff) desc_msg.append('</pre>') - return map(safe_unicode, desc_msg) + return [safe_unicode(chunk) for chunk in desc_msg] - def atom(self, repo_name): - """Produce an atom-1.0 feed via feedgenerator module""" + def _feed(self, repo_name, kind, feed_factory): + """Produce a simple feed""" @cache_region('long_term', '_get_feed_from_cache') def _get_feed_from_cache(*_cache_keys): # parameters are not really used - only as caching key - feed = Atom1Feed( + feed = feed_factory( title=_('%s %s feed') % (c.site_name, repo_name), link=h.canonical_url('summary_home', repo_name=repo_name), description=_('Changes on %s repository') % repo_name, language=language, - ttl=ttl + ttl=ttl, # rss only ) rss_items_per_page = safe_int(CONFIG.get('rss_items_per_page', 20)) @@ -128,34 +128,12 @@ response.content_type = feed.mime_type return feed.writeString('utf-8') - kind = 'ATOM' return _get_feed_from_cache(repo_name, kind, c.db_repo.changeset_cache.get('raw_id')) + def atom(self, repo_name): + """Produce a simple atom-1.0 feed""" + return self._feed(repo_name, 'ATOM', Atom1Feed) + def rss(self, repo_name): """Produce an rss2 feed via feedgenerator module""" - - @cache_region('long_term', '_get_feed_from_cache') - def _get_feed_from_cache(*_cache_keys): # parameters are not really used - only as caching key - feed = Rss201rev2Feed( - title=_('%s %s feed') % (c.site_name, repo_name), - link=h.canonical_url('summary_home', repo_name=repo_name), - description=_('Changes on %s repository') % repo_name, - language=language, - ttl=ttl - ) - - rss_items_per_page = safe_int(CONFIG.get('rss_items_per_page', 20)) - for cs in reversed(list(c.db_repo_scm_instance[-rss_items_per_page:])): - feed.add_item(title=self._get_title(cs), - link=h.canonical_url('changeset_home', repo_name=repo_name, - revision=cs.raw_id), - author_name=cs.author, - description=''.join(self.__get_desc(cs)), - pubdate=cs.date, - ) - - response.content_type = feed.mime_type - return feed.writeString('utf-8') - - kind = 'RSS' - return _get_feed_from_cache(repo_name, kind, c.db_repo.changeset_cache.get('raw_id')) + return self._feed(repo_name, 'RSS', Rss201rev2Feed)
--- a/kallithea/controllers/home.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/home.py Thu Dec 19 20:50:33 2019 +0100 @@ -37,7 +37,6 @@ from kallithea.lib import helpers as h from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired from kallithea.lib.base import BaseController, jsonify, render -from kallithea.lib.utils import conditional_cache from kallithea.model.db import RepoGroup, Repository, User, UserGroup from kallithea.model.repo import RepoModel from kallithea.model.scm import UserGroupList @@ -67,9 +66,7 @@ @LoginRequired(allow_default_user=True) @jsonify def repo_switcher_data(self): - # wrapper for conditional cache - def _c(): - log.debug('generating switcher repo/groups list') + if request.is_xhr: all_repos = Repository.query(sorted=True).all() repo_iter = self.scm_model.get_repos(all_repos) all_groups = RepoGroup.query(sorted=True).all() @@ -96,17 +93,16 @@ ], }] + for res_dict in res: + for child in (res_dict['children']): + child['obj'].pop('_changeset_cache', None) # bytes cannot be encoded in json ... but this value isn't relevant on client side at all ... + data = { 'more': False, 'results': res, } return data - if request.is_xhr: - condition = False - compute = conditional_cache('short_term', 'cache_desc', - condition=condition, func=_c) - return compute() else: raise HTTPBadRequest()
--- a/kallithea/controllers/journal.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/journal.py Thu Dec 19 20:50:33 2019 +0100 @@ -39,7 +39,6 @@ from webob.exc import HTTPBadRequest import kallithea.lib.helpers as h -from kallithea.config.routing import url from kallithea.controllers.admin.admin import _journal_filter from kallithea.lib.auth import LoginRequired from kallithea.lib.base import BaseController, render @@ -105,21 +104,16 @@ return journal - def _atom_feed(self, repos, public=True): + def _feed(self, repos, feed_factory, link, desc): journal = self._get_journal_data(repos) - if public: - _link = h.canonical_url('public_journal_atom') - _desc = '%s %s %s' % (c.site_name, _('Public Journal'), - 'atom feed') - else: - _link = h.canonical_url('journal_atom') - _desc = '%s %s %s' % (c.site_name, _('Journal'), 'atom feed') - feed = Atom1Feed(title=_desc, - link=_link, - description=_desc, - language=language, - ttl=ttl) + feed = feed_factory( + title=desc, + link=link, + description=desc, + language=language, + ttl=ttl, + ) for entry in journal[:feed_nr]: user = entry.user @@ -131,7 +125,6 @@ action, action_extra, ico = h.action_parser(entry, feed=True) title = "%s - %s %s" % (user.short_contact, action(), entry.repository.repo_name) - desc = action_extra() _url = None if entry.repository is not None: _url = h.canonical_url('changelog_home', @@ -142,52 +135,32 @@ link=_url or h.canonical_url(''), author_email=user.email, author_name=user.full_contact, - description=desc) + description=action_extra()) response.content_type = feed.mime_type return feed.writeString('utf-8') - def _rss_feed(self, repos, public=True): - journal = self._get_journal_data(repos) + def _atom_feed(self, repos, public=True): if public: - _link = h.canonical_url('public_journal_atom') - _desc = '%s %s %s' % (c.site_name, _('Public Journal'), + link = h.canonical_url('public_journal_atom') + desc = '%s %s %s' % (c.site_name, _('Public Journal'), + 'atom feed') + else: + link = h.canonical_url('journal_atom') + desc = '%s %s %s' % (c.site_name, _('Journal'), 'atom feed') + + return self._feed(repos, Atom1Feed, link, desc) + + def _rss_feed(self, repos, public=True): + if public: + link = h.canonical_url('public_journal_atom') + desc = '%s %s %s' % (c.site_name, _('Public Journal'), 'rss feed') else: - _link = h.canonical_url('journal_atom') - _desc = '%s %s %s' % (c.site_name, _('Journal'), 'rss feed') - - feed = Rss201rev2Feed(title=_desc, - link=_link, - description=_desc, - language=language, - ttl=ttl) + link = h.canonical_url('journal_atom') + desc = '%s %s %s' % (c.site_name, _('Journal'), 'rss feed') - for entry in journal[:feed_nr]: - user = entry.user - if user is None: - # fix deleted users - user = AttributeDict({'short_contact': entry.username, - 'email': '', - 'full_contact': ''}) - action, action_extra, ico = h.action_parser(entry, feed=True) - title = "%s - %s %s" % (user.short_contact, action(), - entry.repository.repo_name) - desc = action_extra() - _url = None - if entry.repository is not None: - _url = h.canonical_url('changelog_home', - repo_name=entry.repository.repo_name) - - feed.add_item(title=title, - pubdate=entry.action_date, - link=_url or h.canonical_url(''), - author_email=user.email, - author_name=user.full_contact, - description=desc) - - response.content_type = feed.mime_type - return feed.writeString('utf-8') + return self._feed(repos, Rss201rev2Feed, link, desc) @LoginRequired() def index(self): @@ -201,10 +174,8 @@ journal = self._get_journal_data(c.following) - def url_generator(**kw): - return url.current(filter=c.search_term, **kw) - - c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator) + c.journal_pager = Page(journal, page=p, items_per_page=20, + filter=c.search_term) c.journal_day_aggregate = self._get_daily_aggregate(c.journal_pager) if request.environ.get('HTTP_X_PARTIAL_XHR'):
--- a/kallithea/controllers/pullrequests.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/pullrequests.py Thu Dec 19 20:50:33 2019 +0100 @@ -201,6 +201,11 @@ def show_all(self, repo_name): c.from_ = request.GET.get('from_') or '' c.closed = request.GET.get('closed') or '' + url_params = {} + if c.from_: + url_params['from_'] = 1 + if c.closed: + url_params['closed'] = 1 p = safe_int(request.GET.get('page'), 1) q = PullRequest.query(include_closed=c.closed, sorted=True) @@ -210,7 +215,7 @@ q = q.filter_by(other_repo=c.db_repo) c.pull_requests = q.all() - c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100) + c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=100, **url_params) return render('/pullrequests/pullrequest_show_all.html')
--- a/kallithea/controllers/search.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/search.py Thu Dec 19 20:50:33 2019 +0100 @@ -27,12 +27,10 @@ import logging import traceback -import urllib from tg import config, request from tg import tmpl_context as c from tg.i18n import ugettext as _ -from webhelpers2.html.tools import update_params from whoosh.index import EmptyIndexError, exists_in, open_dir from whoosh.qparser import QueryParser, QueryParserError from whoosh.query import Phrase, Prefix @@ -119,9 +117,6 @@ res_ln, results.runtime ) - def url_generator(**kw): - q = urllib.quote(safe_str(c.cur_query)) - return update_params("?q=%s&type=%s" % (q, safe_str(c.cur_type)), **kw) repo_location = RepoModel().repos_path c.formated_results = Page( WhooshResultWrapper(search_type, searcher, matcher, @@ -129,7 +124,8 @@ page=p, item_count=res_ln, items_per_page=10, - url=url_generator + type=safe_str(c.cur_type), + q=safe_str(c.cur_query), ) except QueryParserError:
--- a/kallithea/controllers/summary.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/controllers/summary.py Thu Dec 19 20:50:33 2019 +0100 @@ -38,14 +38,15 @@ from tg.i18n import ugettext as _ from webob.exc import HTTPBadRequest +import kallithea.lib.helpers as h from kallithea.config.conf import ALL_EXTS, ALL_READMES, LANGUAGES_EXTENSIONS_MAP from kallithea.lib.auth import HasRepoPermissionLevelDecorator, LoginRequired from kallithea.lib.base import BaseRepoController, jsonify, render from kallithea.lib.celerylib.tasks import get_commits_stats from kallithea.lib.compat import json from kallithea.lib.markup_renderer import MarkupRenderer -from kallithea.lib.page import RepoPage -from kallithea.lib.utils2 import safe_int +from kallithea.lib.page import Page +from kallithea.lib.utils2 import safe_int, safe_str from kallithea.lib.vcs.backends.base import EmptyChangeset from kallithea.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, NodeDoesNotExistError from kallithea.lib.vcs.nodes import FileNode @@ -104,8 +105,12 @@ def index(self, repo_name): p = safe_int(request.GET.get('page'), 1) size = safe_int(request.GET.get('size'), 10) - collection = c.db_repo_scm_instance - c.cs_pagination = RepoPage(collection, page=p, items_per_page=size) + try: + collection = c.db_repo_scm_instance.get_changesets(reverse=True) + except EmptyRepositoryError as e: + h.flash(safe_str(e), category='warning') + collection = [] + c.cs_pagination = Page(collection, page=p, items_per_page=size) page_revisions = [x.raw_id for x in list(c.cs_pagination)] c.cs_comments = c.db_repo.get_comments(page_revisions) c.cs_statuses = c.db_repo.statuses(page_revisions)
--- a/kallithea/lib/annotate.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/annotate.py Thu Dec 19 20:50:33 2019 +0100 @@ -25,8 +25,6 @@ :license: GPLv3, see LICENSE.md for more details. """ -import StringIO - from pygments import highlight from pygments.formatters import HtmlFormatter @@ -111,12 +109,12 @@ return ''.join((changeset.id, '\n')) def _wrap_tablelinenos(self, inner): - dummyoutfile = StringIO.StringIO() + inner_lines = [] lncount = 0 for t, line in inner: if t: lncount += 1 - dummyoutfile.write(line) + inner_lines.append(line) fl = self.linenostart mw = len(str(lncount + fl - 1)) @@ -176,7 +174,7 @@ '<tr><td class="linenos"><div class="linenodiv"><pre>' + ls + '</pre></div></td>' + '<td class="code">') - yield 0, dummyoutfile.getvalue() + yield 0, ''.join(inner_lines) yield 0, '</td></tr></table>' ''' @@ -204,5 +202,5 @@ ''.join(headers_row) + ''.join(body_row_start) ) - yield 0, dummyoutfile.getvalue() + yield 0, ''.join(inner_lines) yield 0, '</td></tr></table>'
--- a/kallithea/lib/app_globals.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/app_globals.py Thu Dec 19 20:50:33 2019 +0100 @@ -39,9 +39,7 @@ """One instance of Globals is created during application initialization and is available during requests via the 'app_globals' variable - """ - self.available_permissions = None # propagated after init_model @property def cache(self):
--- a/kallithea/lib/auth.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/auth.py Thu Dec 19 20:50:33 2019 +0100 @@ -594,24 +594,6 @@ return _set or set(['0.0.0.0/0', '::/0']) -def set_available_permissions(config): - """ - This function will propagate globals with all available defined - permission given in db. We don't want to check each time from db for new - permissions since adding a new permission also requires application restart - ie. to decorate new views with the newly created permission - - :param config: current config instance - - """ - log.info('getting information about all available permissions') - try: - all_perms = Session().query(Permission).all() - config['available_permissions'] = [x.permission_name for x in all_perms] - finally: - Session.remove() - - #============================================================================== # CHECK DECORATORS #==============================================================================
--- a/kallithea/lib/base.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/base.py Thu Dec 19 20:50:33 2019 +0100 @@ -30,7 +30,6 @@ import datetime import logging -import time import traceback import warnings @@ -300,7 +299,6 @@ return _get_ip_addr(environ) def __call__(self, environ, start_response): - start = time.time() try: # try parsing a request for this VCS - if it fails, call the wrapped app parsed_request = self.parse_request(environ) @@ -343,10 +341,6 @@ except webob.exc.HTTPException as e: return e(environ, start_response) - finally: - log_ = logging.getLogger('kallithea.' + self.__class__.__name__) - log_.debug('Request time: %.3fs', time.time() - start) - meta.Session.remove() class BaseController(TGController): @@ -634,7 +628,7 @@ warnings.warn(msg, Warning, 2) log.warning(msg) log.debug("Returning JSON wrapped action output") - return json.dumps(data, encoding='utf-8') + return json.dumps(data) @decorator.decorator def IfSshEnabled(func, *args, **kwargs):
--- a/kallithea/lib/caching_query.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/caching_query.py Thu Dec 19 20:50:33 2019 +0100 @@ -1,3 +1,5 @@ +# apparently based on https://github.com/sqlalchemy/sqlalchemy/blob/rel_0_7/examples/beaker_caching/caching_query.py + """caching_query.py Represent persistence structures which allow the usage of @@ -137,8 +139,7 @@ if cache_key is None: # cache key - the value arguments from this query's parameters. args = [safe_str(x) for x in _params_from_query(query)] - args.extend(filter(lambda k: k not in ['None', None, u'None'], - [str(query._limit), str(query._offset)])) + args.extend([k for k in [str(query._limit), str(query._offset)] if k not in ['None', None, u'None']]) cache_key = " ".join(args)
--- a/kallithea/lib/celerylib/tasks.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/celerylib/tasks.py Thu Dec 19 20:50:33 2019 +0100 @@ -26,9 +26,9 @@ :license: GPLv3, see LICENSE.md for more details. """ +import email.utils import logging import os -import rfc822 import traceback from collections import OrderedDict from operator import itemgetter @@ -282,7 +282,7 @@ # extract the e-mail address. envelope_addr = author_email(envelope_from) headers['From'] = '"%s" <%s>' % ( - rfc822.quote('%s (no-reply)' % author.full_name_or_username), + email.utils.quote('%s (no-reply)' % author.full_name_or_username), envelope_addr) user = email_config.get('smtp_username')
--- a/kallithea/lib/diffs.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/diffs.py Thu Dec 19 20:50:33 2019 +0100 @@ -216,8 +216,7 @@ stats = (0, 0) if not html_diff: - submodules = filter(lambda o: isinstance(o, SubModuleNode), - [filenode_new, filenode_old]) + submodules = [o for o in [filenode_new, filenode_old] if isinstance(o, SubModuleNode)] if submodules: html_diff = wrap_to_table(h.escape('Submodule %r' % submodules[0])) else: @@ -235,8 +234,7 @@ """ # make sure we pass in default context context = context or 3 - submodules = filter(lambda o: isinstance(o, SubModuleNode), - [filenode_new, filenode_old]) + submodules = [o for o in [filenode_new, filenode_old] if isinstance(o, SubModuleNode)] if submodules: return ''
--- a/kallithea/lib/helpers.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/helpers.py Thu Dec 19 20:50:33 2019 +0100 @@ -22,7 +22,6 @@ import logging import random import re -import StringIO import textwrap import urlparse @@ -246,12 +245,12 @@ yield i, t def _wrap_tablelinenos(self, inner): - dummyoutfile = StringIO.StringIO() + inner_lines = [] lncount = 0 for t, line in inner: if t: lncount += 1 - dummyoutfile.write(line) + inner_lines.append(line) fl = self.linenostart mw = len(str(lncount + fl - 1)) @@ -304,7 +303,7 @@ '<tr><td class="linenos"><div class="linenodiv">' '<pre>' + ls + '</pre></div></td>' '<td id="hlcode" class="code">') - yield 0, dummyoutfile.getvalue() + yield 0, ''.join(inner_lines) yield 0, '</td></tr></table>' @@ -380,7 +379,7 @@ h %= 1 HSV_tuple = [h, 0.95, 0.95] RGB_tuple = hsv_to_rgb(*HSV_tuple) - yield map(lambda x: str(int(x * 256)), RGB_tuple) + yield [str(int(x * 256)) for x in RGB_tuple] cgenerator = gen_color() @@ -677,7 +676,7 @@ return _op, _name revs = [] - if len(filter(lambda v: v != '', revs_ids)) > 0: + if len([v for v in revs_ids if v != '']) > 0: repo = None for rev in revs_ids[:revs_top_limit]: _op, _name = _get_op(rev)
--- a/kallithea/lib/indexers/daemon.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/indexers/daemon.py Thu Dec 19 20:50:33 2019 +0100 @@ -78,7 +78,7 @@ # filter repo list if repo_list: # Fix non-ascii repo names to unicode - repo_list = map(safe_unicode, repo_list) + repo_list = set(safe_unicode(repo_name) for repo_name in repo_list) self.filtered_repo_paths = {} for repo_name, repo in self.repo_paths.items(): if repo_name in repo_list:
--- a/kallithea/lib/middleware/pygrack.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/middleware/pygrack.py Thu Dec 19 20:50:33 2019 +0100 @@ -186,7 +186,7 @@ _path = self._get_fixedpath(req.path_info) if _path.startswith('info/refs'): app = self.inforefs - elif [a for a in self.valid_accepts if a in req.accept]: + elif req.accept.acceptable_offers(self.valid_accepts): app = self.backend try: resp = app(req, environ)
--- a/kallithea/lib/page.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/page.py Thu Dec 19 20:50:33 2019 +0100 @@ -15,11 +15,11 @@ Custom paging classes """ import logging -import math -import re -from webhelpers2.html import HTML, literal -from webhelpers.paginate import Page as _Page +import paginate +import paginate_sqlalchemy +import sqlalchemy.orm +from webhelpers2.html import literal from kallithea.config.routing import url @@ -27,229 +27,36 @@ log = logging.getLogger(__name__) -class Page(_Page): - """ - Custom pager emitting Bootstrap paginators - """ - - def __init__(self, *args, **kwargs): - kwargs.setdefault('url', url.current) - _Page.__init__(self, *args, **kwargs) - - def _get_pos(self, cur_page, max_page, items): - edge = (items / 2) + 1 - if (cur_page <= edge): - radius = max(items / 2, items - cur_page) - elif (max_page - cur_page) < edge: - radius = (items - 1) - (max_page - cur_page) - else: - radius = items / 2 - - left = max(1, (cur_page - (radius))) - right = min(max_page, cur_page + (radius)) - return left, cur_page, right - - def _range(self, regexp_match): - """ - Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8'). - - Arguments: - - regexp_match - A "re" (regular expressions) match object containing the - radius of linked pages around the current page in - regexp_match.group(1) as a string - - This function is supposed to be called as a callable in - re.sub. - - """ - radius = int(regexp_match.group(1)) - - # Compute the first and last page number within the radius - # e.g. '1 .. 5 6 [7] 8 9 .. 12' - # -> leftmost_page = 5 - # -> rightmost_page = 9 - leftmost_page, _cur, rightmost_page = self._get_pos(self.page, - self.last_page, - (radius * 2) + 1) - nav_items = [] - - # Create a link to the first page (unless we are on the first page - # or there would be no need to insert '..' spacers) - if self.page != self.first_page and self.first_page < leftmost_page: - nav_items.append(HTML.li(self._pagerlink(self.first_page, self.first_page))) +class Page(paginate.Page): - # Insert dots if there are pages between the first page - # and the currently displayed page range - if leftmost_page - self.first_page > 1: - # Wrap in a SPAN tag if nolink_attr is set - text_ = '..' - if self.dotdot_attr: - text_ = HTML.span(c=text_, **self.dotdot_attr) - nav_items.append(HTML.li(text_)) - - for thispage in xrange(leftmost_page, rightmost_page + 1): - # Highlight the current page number and do not use a link - text_ = str(thispage) - if thispage == self.page: - # Wrap in a SPAN tag if nolink_attr is set - if self.curpage_attr: - text_ = HTML.li(HTML.span(c=text_), **self.curpage_attr) - nav_items.append(text_) - # Otherwise create just a link to that page - else: - nav_items.append(HTML.li(self._pagerlink(thispage, text_))) - - # Insert dots if there are pages between the displayed - # page numbers and the end of the page range - if self.last_page - rightmost_page > 1: - text_ = '..' - # Wrap in a SPAN tag if nolink_attr is set - if self.dotdot_attr: - text_ = HTML.span(c=text_, **self.dotdot_attr) - nav_items.append(HTML.li(text_)) - - # Create a link to the very last page (unless we are on the last - # page or there would be no need to insert '..' spacers) - if self.page != self.last_page and rightmost_page < self.last_page: - nav_items.append(HTML.li(self._pagerlink(self.last_page, self.last_page))) - - #_page_link = url.current() - #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) - #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1)))) - return self.separator.join(nav_items) - - def pager(self, format='<ul class="pagination">$link_previous ~2~ $link_next</ul>', page_param='page', partial_param='partial', - show_if_single_page=False, separator=' ', onclick=None, - symbol_first='<<', symbol_last='>>', - symbol_previous='<', symbol_next='>', - link_attr=None, - curpage_attr=None, - dotdot_attr=None, **kwargs - ): - self.curpage_attr = curpage_attr or {'class': 'active'} - self.separator = separator - self.pager_kwargs = kwargs - self.page_param = page_param - self.partial_param = partial_param - self.onclick = onclick - self.link_attr = link_attr or {'class': 'pager_link', 'rel': 'prerender'} - self.dotdot_attr = dotdot_attr or {'class': 'pager_dotdot'} + def __init__(self, collection, + page=1, items_per_page=20, item_count=None, + **kwargs): + if isinstance(collection, sqlalchemy.orm.query.Query): + collection = paginate_sqlalchemy.SqlalchemyOrmWrapper(collection) + paginate.Page.__init__(self, collection, page=page, items_per_page=items_per_page, item_count=item_count, + url_maker=lambda page: url.current(page=page, **kwargs)) - # Don't show navigator if there is no more than one page - if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page): - return '' - - from string import Template - # Replace ~...~ in token format by range of pages - result = re.sub(r'~(\d+)~', self._range, format) - - # Interpolate '%' variables - result = Template(result).safe_substitute({ - 'first_page': self.first_page, - 'last_page': self.last_page, - 'page': self.page, - 'page_count': self.page_count, - 'items_per_page': self.items_per_page, - 'first_item': self.first_item, - 'last_item': self.last_item, - 'item_count': self.item_count, - 'link_first': self.page > self.first_page and - self._pagerlink(self.first_page, symbol_first) or '', - 'link_last': self.page < self.last_page and - self._pagerlink(self.last_page, symbol_last) or '', - 'link_previous': HTML.li(self.previous_page and - self._pagerlink(self.previous_page, symbol_previous) - or HTML.a(symbol_previous)), - 'link_next': HTML.li(self.next_page and - self._pagerlink(self.next_page, symbol_next) - or HTML.a(symbol_next)), - }) - - return literal(result) - - -class RepoPage(Page): - - def __init__(self, collection, page=1, items_per_page=20, - item_count=None, **kwargs): - - """Create a "RepoPage" instance. special pager for paging - repository - """ - # TODO: call baseclass __init__ - self._url_generator = kwargs.pop('url', url.current) - - # Safe the kwargs class-wide so they can be used in the pager() method - self.kwargs = kwargs - - # Save a reference to the collection - self.original_collection = collection - - self.collection = collection + def pager(self): + return literal( + paginate.Page.pager(self, + format='<ul class="pagination">$link_previous\n~4~$link_next</ul>', + link_attr={'class': 'pager_link'}, + dotdot_attr={'class': 'pager_dotdot'}, + separator='\n', + )) - # The self.page is the number of the current page. - # The first page has the number 1! - try: - self.page = int(page) # make it int() if we get it as a string - except (ValueError, TypeError): - log.error("Invalid page value: %r", page) - self.page = 1 - - self.items_per_page = items_per_page - - # Unless the user tells us how many items the collections has - # we calculate that ourselves. - if item_count is not None: - self.item_count = item_count - else: - self.item_count = len(self.collection) - - # Compute the number of the first and last available page - if self.item_count > 0: - self.first_page = 1 - self.page_count = int(math.ceil(float(self.item_count) / - self.items_per_page)) - self.last_page = self.first_page + self.page_count - 1 - - # Make sure that the requested page number is the range of - # valid pages - if self.page > self.last_page: - self.page = self.last_page - elif self.page < self.first_page: - self.page = self.first_page + @staticmethod + def default_link_tag(item): + # based on the base class implementation, but wrapping results in <li>, and with different handling of current_page + text = item['value'] + if item['type'] == 'current_page': # we need active on the li and can thus not use curpage_attr + return '''<li class="active"><span>%s</span></li>''' % text - # Note: the number of items on this page can be less than - # items_per_page if the last page is not full - self.first_item = max(0, (self.item_count) - (self.page * - items_per_page)) - self.last_item = ((self.item_count - 1) - items_per_page * - (self.page - 1)) - - self.items = list(self.collection[self.first_item:self.last_item + 1]) - - # Links to previous and next page - if self.page > self.first_page: - self.previous_page = self.page - 1 - else: - self.previous_page = None - - if self.page < self.last_page: - self.next_page = self.page + 1 - else: - self.next_page = None - - # No items available + if not item['href'] or item['type'] == 'span': + if item['attrs']: + text = paginate.make_html_tag('span', **item['attrs']) + text + '</span>' else: - self.first_page = None - self.page_count = 0 - self.last_page = None - self.first_item = None - self.last_item = None - self.previous_page = None - self.next_page = None - self.items = [] - - # This is a subclass of the 'list' type. Initialise the list now. - list.__init__(self, reversed(self.items)) + target_url = item['href'] + text = paginate.make_html_tag('a', text=text, href=target_url, **item['attrs']) + return '''<li>%s</li>''' % text
--- a/kallithea/lib/pygmentsutils.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/pygmentsutils.py Thu Dec 19 20:50:33 2019 +0100 @@ -26,7 +26,6 @@ """ from collections import defaultdict -from itertools import ifilter from pygments import lexers @@ -59,15 +58,11 @@ """ Get list of known indexable filenames from pygment lexer internals """ - filenames = [] - - def likely_filename(s): - return s.find('*') == -1 and s.find('[') == -1 - for lx, t in sorted(lexers.LEXERS.items()): - for f in ifilter(likely_filename, t[-2]): - filenames.append(f) + for f in t[-2]: + if '*' not in f and '[' not in f: + filenames.append(f) return filenames
--- a/kallithea/lib/rcmail/response.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/rcmail/response.py Thu Dec 19 20:50:33 2019 +0100 @@ -422,7 +422,7 @@ return "" encoder = Charset(DEFAULT_ENCODING) - if type(value) == list: + if isinstance(value, list): return separator.join(properly_encode_header( v, encoder, not_email) for v in value) else:
--- a/kallithea/lib/utils.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/utils.py Thu Dec 19 20:50:33 2019 +0100 @@ -322,7 +322,7 @@ 'ui', 'web', ] -def make_ui(repo_path=None, clear_session=True): +def make_ui(repo_path=None): """ Create an Mercurial 'ui' object based on database Ui settings, possibly augmenting with content from a hgrc file. @@ -342,8 +342,6 @@ ui_.ui_key, ui_val) baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key), ui_val) - if clear_session: - meta.Session.remove() # force set push_ssl requirement to False, Kallithea handles that baseui.setconfig('web', 'push_ssl', False) @@ -377,12 +375,10 @@ :param config: """ - try: - hgsettings = Setting.get_app_settings() - for k, v in hgsettings.items(): - config[k] = v - finally: - meta.Session.remove() + hgsettings = Setting.get_app_settings() + + for k, v in hgsettings.items(): + config[k] = v def set_vcs_config(config):
--- a/kallithea/lib/utils2.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/utils2.py Thu Dec 19 20:50:33 2019 +0100 @@ -394,7 +394,7 @@ else: host, port = uri[:cred_pos], uri[cred_pos + 1:] - return filter(None, [proto, host, port]) + return [_f for _f in [proto, host, port] if _f] def credentials_filter(uri):
--- a/kallithea/lib/vcs/backends/git/inmemory.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/backends/git/inmemory.py Thu Dec 19 20:50:33 2019 +0100 @@ -47,7 +47,7 @@ for node in self.added + self.changed: # Compute subdirs if needed dirpath, nodename = posixpath.split(node.path) - dirnames = map(safe_str, dirpath and dirpath.split('/') or []) + dirnames = safe_str(dirpath).split('/') if dirpath else [] parent = commit_tree ancestors = [('', parent)]
--- a/kallithea/lib/vcs/backends/git/repository.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/backends/git/repository.py Thu Dec 19 20:50:33 2019 +0100 @@ -572,7 +572,8 @@ revs = revs[start_pos:end_pos] if reverse: - revs = reversed(revs) + revs.reverse() + return CollectionGenerator(self, revs) def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False,
--- a/kallithea/lib/vcs/backends/hg/changeset.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/backends/hg/changeset.py Thu Dec 19 20:50:33 2019 +0100 @@ -27,7 +27,7 @@ @LazyProperty def tags(self): - return map(safe_unicode, self._ctx.tags()) + return [safe_unicode(tag) for tag in self._ctx.tags()] @LazyProperty def branch(self): @@ -95,7 +95,7 @@ @LazyProperty def bookmarks(self): - return map(safe_unicode, self._ctx.bookmarks()) + return [safe_unicode(bookmark) for bookmark in self._ctx.bookmarks()] @LazyProperty def message(self): @@ -246,9 +246,9 @@ """ fctx = self._get_filectx(path) if 'x' in fctx.flags(): - return 0100755 + return 0o100755 else: - return 0100644 + return 0o100644 def get_file_content(self, path): """
--- a/kallithea/lib/vcs/backends/hg/repository.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/backends/hg/repository.py Thu Dec 19 20:50:33 2019 +0100 @@ -553,7 +553,7 @@ # would be to get rid of this function entirely and use revsets revs = list(revisions)[start_pos:end_pos] if reverse: - revs = reversed(revs) + revs.reverse() return CollectionGenerator(self, revs)
--- a/kallithea/lib/vcs/nodes.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/nodes.py Thu Dec 19 20:50:33 2019 +0100 @@ -120,10 +120,6 @@ return None @LazyProperty - def unicode_path(self): - return safe_unicode(self.path) - - @LazyProperty def name(self): """ Returns name of the node so if its path @@ -259,7 +255,7 @@ super(FileNode, self).__init__(path, kind=NodeKind.FILE) self.changeset = changeset self._content = content - self._mode = mode or 0100644 + self._mode = mode or 0o100644 @LazyProperty def mode(self):
--- a/kallithea/lib/vcs/utils/annotate.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/lib/vcs/utils/annotate.py Thu Dec 19 20:50:33 2019 +0100 @@ -1,5 +1,3 @@ -import StringIO - from pygments import highlight from pygments.formatters import HtmlFormatter @@ -83,12 +81,12 @@ return ''.join((changeset.id, '\n')) def _wrap_tablelinenos(self, inner): - dummyoutfile = StringIO.StringIO() + inner_lines = [] lncount = 0 for t, line in inner: if t: lncount += 1 - dummyoutfile.write(line) + inner_lines.append(line) fl = self.linenostart mw = len(str(lncount + fl - 1)) @@ -147,7 +145,7 @@ '<tr><td class="linenos"><div class="linenodiv"><pre>' + ls + '</pre></div></td>' + '<td class="code">') - yield 0, dummyoutfile.getvalue() + yield 0, ''.join(inner_lines) yield 0, '</td></tr></table>' ''' @@ -175,5 +173,5 @@ ''.join(headers_row) + ''.join(body_row_start) ) - yield 0, dummyoutfile.getvalue() + yield 0, ''.join(inner_lines) yield 0, '</td></tr></table>'
--- a/kallithea/model/db.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/model/db.py Thu Dec 19 20:50:33 2019 +0100 @@ -205,7 +205,7 @@ @validates('_app_settings_value') def validate_settings_value(self, key, val): - assert type(val) == unicode + assert isinstance(val, unicode) return val @hybrid_property @@ -1163,7 +1163,7 @@ # names in the database, but that eventually needs to be converted # into a valid system path p += self.repo_name.split(Repository.url_sep()) - return os.path.join(*map(safe_unicode, p)) + return os.path.join(*(safe_unicode(d) for d in p)) @property def cache_keys(self): @@ -1190,7 +1190,7 @@ Creates an db based ui object for this repository """ from kallithea.lib.utils import make_ui - return make_ui(clear_session=False) + return make_ui() @classmethod def is_valid(cls, repo_name): @@ -2511,8 +2511,7 @@ def scm_instance(self): from kallithea.lib.vcs import get_repo base_path = self.base_path() - return get_repo(os.path.join(*map(safe_str, - [base_path, self.gist_access_id]))) + return get_repo(os.path.join(safe_str(base_path), safe_str(self.gist_access_id))) class UserSshKeys(Base, BaseDbModel):
--- a/kallithea/model/permission.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/model/permission.py Thu Dec 19 20:50:33 2019 +0100 @@ -73,8 +73,7 @@ return '.'.join(perm_name.split('.')[:1]) perms = UserToPerm.query().filter(UserToPerm.user == user).all() - defined_perms_groups = map(_get_group, - (x.permission.permission_name for x in perms)) + defined_perms_groups = set(_get_group(x.permission.permission_name) for x in perms) log.debug('GOT ALREADY DEFINED:%s', perms) DEFAULT_PERMS = Permission.DEFAULT_USER_PERMISSIONS
--- a/kallithea/model/repo.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/model/repo.py Thu Dec 19 20:50:33 2019 +0100 @@ -128,7 +128,7 @@ tmpl = template.get_def(tmpl) kwargs.update(dict(_=_, h=h, c=c, request=request)) - return tmpl.render(*args, **kwargs) + return tmpl.render_unicode(*args, **kwargs) def get_repos_as_dict(self, repos_list, repo_groups_list=None, admin=False, @@ -290,7 +290,7 @@ # clone_uri is modified - if given a value, check it is valid if clone_uri != '': # will raise exception on error - is_valid_repo_uri(cur_repo.repo_type, clone_uri, make_ui(clear_session=False)) + is_valid_repo_uri(cur_repo.repo_type, clone_uri, make_ui()) cur_repo.clone_uri = clone_uri if 'repo_name' in kwargs: @@ -306,8 +306,7 @@ repo=cur_repo, user='default', perm=EMPTY_PERM ) # handle extra fields - for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), - kwargs): + for field in [k for k in kwargs if k.startswith(RepositoryField.PREFIX)]: k = RepositoryField.un_prefix_key(field) ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo) if ex_field: @@ -360,7 +359,7 @@ new_repo.private = private if clone_uri: # will raise exception on error - is_valid_repo_uri(repo_type, clone_uri, make_ui(clear_session=False)) + is_valid_repo_uri(repo_type, clone_uri, make_ui()) new_repo.clone_uri = clone_uri new_repo.landing_rev = landing_rev @@ -644,7 +643,7 @@ else: _paths = [self.repos_path, new_parent_path, repo_name] # we need to make it str for mercurial - repo_path = os.path.join(*map(lambda x: safe_str(x), _paths)) + repo_path = os.path.join(*(safe_str(x) for x in _paths)) # check if this path is not a repository if is_valid_repo(repo_path, self.repos_path): @@ -661,7 +660,7 @@ backend = get_backend(repo_type) if repo_type == 'hg': - baseui = make_ui(clear_session=False) + baseui = make_ui() # patch and reset hooks section of UI config to not run any # hooks on creating remote repo for k, v in baseui.configitems('hooks'):
--- a/kallithea/model/scm.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/model/scm.py Thu Dec 19 20:50:33 2019 +0100 @@ -25,7 +25,6 @@ :license: GPLv3, see LICENSE.md for more details. """ -import cStringIO import logging import os import posixpath @@ -485,12 +484,8 @@ # in any other case this will throw exceptions and deny commit if isinstance(content, (basestring,)): content = safe_str(content) - elif isinstance(content, (file, cStringIO.OutputType,)): + else: content = content.read() - else: - raise Exception('Content is of unrecognized type %s' % ( - type(content) - )) processed_nodes.append((f_path, content)) message = safe_unicode(message) @@ -755,7 +750,7 @@ with open(_hook_file, 'wb') as f: tmpl = tmpl.replace('_TMPL_', kallithea.__version__) f.write(tmpl) - os.chmod(_hook_file, 0755) + os.chmod(_hook_file, 0o755) except IOError as e: log.error('error writing %s: %s', _hook_file, e) else:
--- a/kallithea/model/validators.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/model/validators.py Thu Dec 19 20:50:33 2019 +0100 @@ -412,7 +412,7 @@ if url and url != value.get('clone_uri_hidden'): try: - is_valid_repo_uri(repo_type, url, make_ui(clear_session=False)) + is_valid_repo_uri(repo_type, url, make_ui()) except Exception: log.exception('URL validation failed') msg = self.message('clone_uri', state) @@ -782,7 +782,7 @@ def _convert_to_python(self, value, state): # filter empty values - return filter(lambda s: s not in [None, ''], value) + return [s for s in value if s not in [None, '']] def _validate_python(self, value, state): from kallithea.lib import auth_modules
--- a/kallithea/templates/admin/gists/index.html Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/templates/admin/gists/index.html Thu Dec 19 20:50:33 2019 +0100 @@ -61,7 +61,7 @@ <div class="text-muted">${gist.gist_description}</div> </div> % endfor - ${c.gists_pager.pager(**request.GET.mixed())} + ${c.gists_pager.pager()} %else: <div>${_('There are no gists yet')}</div> %endif
--- a/kallithea/templates/changeset/changeset.html Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/templates/changeset/changeset.html Thu Dec 19 20:50:33 2019 +0100 @@ -90,16 +90,14 @@ <span><b>${h.person(c.changeset.author,'full_name_and_username')}</b> - ${h.age(c.changeset.date,True)} ${h.fmt_date(c.changeset.date)}</span><br/> <span>${h.email_or_none(c.changeset.author)}</span><br/> </div> - <% rev = c.changeset.extra.get('source') %> - %if rev: + %if c.changeset_graft_source_hash: <div> - ${_('Grafted from:')} ${h.link_to(h.short_id(rev),h.url('changeset_home',repo_name=c.repo_name,revision=rev), class_="changeset_hash")} + ${_('Grafted from:')} ${h.link_to(h.short_id(c.changeset_graft_source_hash),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset_graft_source_hash), class_="changeset_hash")} </div> %endif - <% rev = c.changeset.extra.get('transplant_source', '').encode('hex') %> - %if rev: + %if c.changeset_transplant_source_hash: <div> - ${_('Transplanted from:')} ${h.link_to(h.short_id(rev),h.url('changeset_home',repo_name=c.repo_name,revision=rev), class_="changeset_hash")} + ${_('Transplanted from:')} ${h.link_to(h.short_id(c.changeset_transplant_source_hash),h.url('changeset_home',repo_name=c.repo_name,revision=c.changeset_transplant_source_hash), class_="changeset_hash")} </div> %endif
--- a/kallithea/templates/pullrequests/pullrequest_data.html Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/templates/pullrequests/pullrequest_data.html Thu Dec 19 20:50:33 2019 +0100 @@ -80,7 +80,7 @@ </div> %if hasattr(pullrequests, 'pager'): - ${pullrequests.pager(**request.GET.mixed())} + ${pullrequests.pager()} %endif </%def>
--- a/kallithea/tests/functional/test_files.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/tests/functional/test_files.py Thu Dec 19 20:50:33 2019 +0100 @@ -136,7 +136,7 @@ repo_name=HG_REPO, revision='tip', f_path='vcs/nodes.py', - annotate=True)) + annotate='1')) response.mustcontain("""r356:25213a5fbb04""") @@ -146,7 +146,7 @@ repo_name=GIT_REPO, revision='master', f_path='vcs/nodes.py', - annotate=True)) + annotate='1')) response.mustcontain("""r345:c994f0de03b2""") def test_file_annotation_history(self): @@ -155,7 +155,7 @@ repo_name=HG_REPO, revision='tip', f_path='vcs/nodes.py', - annotate=True), + annotate='1'), extra_environ={'HTTP_X_PARTIAL_XHR': '1'}) assert response.body == HG_NODE_HISTORY @@ -177,7 +177,7 @@ repo_name=HG_REPO, revision='tip', f_path='vcs/nodes.py', - annotate=True)) + annotate='1')) response.mustcontain('Marcin Kuzminski') response.mustcontain('Lukasz Balcerzak') @@ -187,7 +187,7 @@ repo_name=GIT_REPO, revision='master', f_path='vcs/nodes.py', - annotate=True)) + annotate='1')) response.mustcontain('Marcin Kuzminski') response.mustcontain('Lukasz Balcerzak')
--- a/kallithea/tests/vcs/test_archives.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/tests/vcs/test_archives.py Thu Dec 19 20:50:33 2019 +0100 @@ -1,6 +1,6 @@ import datetime +import io import os -import StringIO import tarfile import tempfile import zipfile @@ -37,9 +37,8 @@ for x in xrange(5): node_path = '%d/file_%d.txt' % (x, x) - decompressed = StringIO.StringIO() - decompressed.write(out.read('repo/' + node_path)) - assert decompressed.getvalue() == self.tip.get_node(node_path).content + decompressed = out.read('repo/' + node_path) + assert decompressed == self.tip.get_node(node_path).content def test_archive_tgz(self): path = tempfile.mkstemp(dir=TESTS_TMP_PATH, prefix='test_archive_tgz-')[1] @@ -71,7 +70,7 @@ tmppath = tempfile.mkstemp(dir=TESTS_TMP_PATH, prefix='test_archive_default_stream-')[1] with open(tmppath, 'wb') as stream: self.tip.fill_archive(stream=stream) - mystream = StringIO.StringIO() + mystream = io.BytesIO() self.tip.fill_archive(stream=mystream) mystream.seek(0) with open(tmppath, 'rb') as f:
--- a/kallithea/tests/vcs/test_git.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/tests/vcs/test_git.py Thu Dec 19 20:50:33 2019 +0100 @@ -590,17 +590,17 @@ def test_commit_message_is_unicode(self): for cs in self.repo: - assert type(cs.message) == unicode + assert isinstance(cs.message, unicode) def test_changeset_author_is_unicode(self): for cs in self.repo: - assert type(cs.author) == unicode + assert isinstance(cs.author, unicode) def test_repo_files_content_is_unicode(self): changeset = self.repo.get_changeset() for node in changeset.get_node('/'): if node.is_file(): - assert type(node.content) == unicode + assert isinstance(node.content, unicode) def test_wrong_path(self): # There is 'setup.py' in the root dir but not there: @@ -657,7 +657,7 @@ 'added': [ FileNode('foobar/static/js/admin/base.js', content='base'), FileNode('foobar/static/admin', content='admin', - mode=0120000), # this is a link + mode=0o120000), # this is a link FileNode('foo', content='foo'), ], },
--- a/kallithea/tests/vcs/test_hg.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/tests/vcs/test_hg.py Thu Dec 19 20:50:33 2019 +0100 @@ -444,20 +444,20 @@ # added: 20 # removed: 1 changed = set(['.hgignore' - , 'README.rst' , 'docs/conf.py' , 'docs/index.rst' , 'setup.py' - , 'tests/test_hg.py' , 'tests/test_nodes.py' , 'vcs/__init__.py' - , 'vcs/backends/__init__.py' , 'vcs/backends/base.py' - , 'vcs/backends/hg.py' , 'vcs/nodes.py' , 'vcs/utils/__init__.py']) + , 'README.rst', 'docs/conf.py', 'docs/index.rst', 'setup.py' + , 'tests/test_hg.py', 'tests/test_nodes.py', 'vcs/__init__.py' + , 'vcs/backends/__init__.py', 'vcs/backends/base.py' + , 'vcs/backends/hg.py', 'vcs/nodes.py', 'vcs/utils/__init__.py']) added = set(['docs/api/backends/hg.rst' - , 'docs/api/backends/index.rst' , 'docs/api/index.rst' - , 'docs/api/nodes.rst' , 'docs/api/web/index.rst' - , 'docs/api/web/simplevcs.rst' , 'docs/installation.rst' - , 'docs/quickstart.rst' , 'setup.cfg' , 'vcs/utils/baseui_config.py' - , 'vcs/utils/web.py' , 'vcs/web/__init__.py' , 'vcs/web/exceptions.py' - , 'vcs/web/simplevcs/__init__.py' , 'vcs/web/simplevcs/exceptions.py' - , 'vcs/web/simplevcs/middleware.py' , 'vcs/web/simplevcs/models.py' - , 'vcs/web/simplevcs/settings.py' , 'vcs/web/simplevcs/utils.py' + , 'docs/api/backends/index.rst', 'docs/api/index.rst' + , 'docs/api/nodes.rst', 'docs/api/web/index.rst' + , 'docs/api/web/simplevcs.rst', 'docs/installation.rst' + , 'docs/quickstart.rst', 'setup.cfg', 'vcs/utils/baseui_config.py' + , 'vcs/utils/web.py', 'vcs/web/__init__.py', 'vcs/web/exceptions.py' + , 'vcs/web/simplevcs/__init__.py', 'vcs/web/simplevcs/exceptions.py' + , 'vcs/web/simplevcs/middleware.py', 'vcs/web/simplevcs/models.py' + , 'vcs/web/simplevcs/settings.py', 'vcs/web/simplevcs/utils.py' , 'vcs/web/simplevcs/views.py']) removed = set(['docs/api.rst']) @@ -538,17 +538,17 @@ def test_commit_message_is_unicode(self): for cm in self.repo: - assert type(cm.message) == unicode + assert isinstance(cm.message, unicode) def test_changeset_author_is_unicode(self): for cm in self.repo: - assert type(cm.author) == unicode + assert isinstance(cm.author, unicode) def test_repo_files_content_is_unicode(self): test_changeset = self.repo.get_changeset(100) for node in test_changeset.get_node('/'): if node.is_file(): - assert type(node.content) == unicode + assert isinstance(node.content, unicode) def test_wrong_path(self): # There is 'setup.py' in the root dir but not there:
--- a/kallithea/tests/vcs/test_nodes.py Sat Nov 30 10:39:37 2019 +0100 +++ b/kallithea/tests/vcs/test_nodes.py Thu Dec 19 20:50:33 2019 +0100 @@ -144,13 +144,13 @@ assert not mode & stat.S_IXOTH def test_file_node_is_executable(self): - node = FileNode('foobar', 'empty... almost', mode=0100755) + node = FileNode('foobar', 'empty... almost', mode=0o100755) assert node.is_executable - node = FileNode('foobar', 'empty... almost', mode=0100500) + node = FileNode('foobar', 'empty... almost', mode=0o100500) assert node.is_executable - node = FileNode('foobar', 'empty... almost', mode=0100644) + node = FileNode('foobar', 'empty... almost', mode=0o100644) assert not node.is_executable def test_mimetype(self):
--- a/setup.py Sat Nov 30 10:39:37 2019 +0100 +++ b/setup.py Thu Dec 19 20:50:33 2019 +0100 @@ -61,7 +61,7 @@ "Markdown >= 2.2.1, < 3.2", "docutils >= 0.11, < 0.15", "URLObject >= 2.3.4, < 2.5", - "Routes >= 1.13, < 2", # TODO: bumping to 2.0 will make test_file_annotation fail + "Routes >= 2.0, < 2.5", "dulwich >= 0.14.1, < 0.20", "mercurial >= 4.5, < 5.3", "decorator >= 3.3.2, < 4.5", @@ -69,6 +69,8 @@ "bleach >= 3.0, < 3.2", "Click >= 7.0, < 8", "ipaddr >= 2.1.10, < 2.3", + "paginate >= 0.5, < 0.6", + "paginate_sqlalchemy >= 0.3.0, < 0.4", ] if not is_windows: