Files @ f734d107296e
Branch filter:

Location: kallithea/setup.py - annotation

Mads Kiilerich
auth: for default permissions, use existing explicit query result values instead of following dot references in ORM result objects

There has been reports of spurious crashes on resolving references like
.repository from Permissions:

File ".../kallithea/lib/auth.py", line 678, in __wrapper
if self.check_permissions(user):
File ".../kallithea/lib/auth.py", line 718, in check_permissions
return user.has_repository_permission_level(repo_name, self.required_perm)
File ".../kallithea/lib/auth.py", line 450, in has_repository_permission_level
actual_perm = self.permissions['repositories'].get(repo_name)
File ".../kallithea/lib/vcs/utils/lazy.py", line 41, in __get__
value = self._func(obj)
File ".../kallithea/lib/auth.py", line 442, in permissions
return self.__get_perms(user=self, cache=False)
File ".../kallithea/lib/auth.py", line 498, in __get_perms
return compute(user_id, user_is_admin)
File ".../kallithea/lib/auth.py", line 190, in _cached_perms_data
r_k = perm.UserRepoToPerm.repository.repo_name
File ".../sqlalchemy/orm/attributes.py", line 285, in __get__
return self.impl.get(instance_state(instance), dict_)
File ".../sqlalchemy/orm/attributes.py", line 721, in get
value = self.callable_(state, passive)
File ".../sqlalchemy/orm/strategies.py", line 710, in _load_for_state
% (orm_util.state_str(state), self.key)

sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <UserRepoToPerm at ...> is not bound to a Session; lazy load operation of attribute 'repository' cannot proceed (Background on this error at: http://sqlalche.me/e/bhk3)

Permissions are cached between requests: SA result records are stored in in
beaker.cache.sql_cache_short and resued in following requests after the initial
session as been removed. References in Permission objects would usually give
lazy lookup ... but not outside the original session, where we would get an
error like this.

Permissions are indeed implemented/used incorrectly. That might explain a part
of the problem. Even if not fully explaining or fixing this problem, it is
still worth fixing:

Permissions are fetched from the database using Session().query with multiple
class/table names (joined together in way that happens to match the references
specified in the table definitions) - including Repository. The results are
thus "structs" with selected objects. If repositories always were retrieved
using this selected repository, everything would be fine. In some places, this
was what we did.

But in some places, the code happened to do what was more intuitive: just use
.repository and rely on "lazy" resolving. SA was not aware that this one
already was present in the result struct, and would try to fetch it again. Best
case, that could be inefficient. Worst case, it would fail as we see here.

Fix this by only querying from one table but use the "joinedload" option to
also fetch other referenced tables in the same select. (This might
inefficiently return the main record multiple times ... but that was already
the case with the previous approach.)

This change is thus doing multiple things with circular dependencies that can't
be split up in minor parts without taking detours:

The existing repository join like:
.join((Repository, UserGroupRepoToPerm.repository_id == Repository.repo_id))
is thus replaced by:
.options(joinedload(UserGroupRepoToPerm.repository))

Since we only are doing Session.query() on one table, the results will be of
that type instead of "structs" with multiple objects. If only querying for
UserRepoToPerm this means:
- perm.UserRepoToPerm.repository becomes perm.repository
- perm.Permission.permission_name looked at the explicitly queried Permission
in the result struct - instead it should look in the the dereferenced
repository as perm.permission.permission_name
aa6f17a53b49
266a3cbc0302
9382e88eae22
0a277465fddf
65c27fd21769
0a277465fddf
0a277465fddf
0a277465fddf
0a277465fddf
0a277465fddf
9382e88eae22
aa6f17a53b49
aa6f17a53b49
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
5725fa4cfecd
5725fa4cfecd
5725fa4cfecd
5725fa4cfecd
9382e88eae22
a553bc3a3d0e
9382e88eae22
9382e88eae22
9382e88eae22
5725fa4cfecd
5725fa4cfecd
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
9382e88eae22
3a1cf70e0f42
2d7a94f3eaae
9382e88eae22
d122a9532630
8e0efe7b3b10
d122a9532630
b72e8b7c33ae
55fc0bcce916
af1b0a59e605
55fc0bcce916
390b99920d02
b077cf7e7f90
fd59e56301a8
ed67d1df7125
d122a9532630
d122a9532630
1d1f5598702d
f8f50d3b6512
d122a9532630
64b76a3150da
b302d4254bd0
d122a9532630
55fc0bcce916
a99b7e388979
55b4e5cb4866
aa6f17a53b49
b015fa0bfecb
d122a9532630
e965ff6f8cb3
e4b9a1d1fea1
f254dd2f9dcb
7433775cc53b
7433775cc53b
1e0632b6ec27
9382e88eae22
9382e88eae22
880a39e5d8df
880a39e5d8df
880a39e5d8df
fc6063e6630b
cc48c1541c7e
fc6063e6630b
fc6063e6630b
fc6063e6630b
fc6063e6630b
fc6063e6630b
01aca0a4f876
01aca0a4f876
01aca0a4f876
cc48c1541c7e
fc6063e6630b
a60cd29ba7e2
b0e2c949c34b
2afa6b8c2ade
2afa6b8c2ade
65c27fd21769
65c27fd21769
24c0d584ba86
326a9336fbe5
faad9dd06b58
266a3cbc0302
266a3cbc0302
7e5f8c12a3fc
266a3cbc0302
266a3cbc0302
266a3cbc0302
266a3cbc0302
2afa6b8c2ade
266a3cbc0302
65c27fd21769
7c732f2047f8
d69aa464f373
266a3cbc0302
27c4ad3e584f
27c4ad3e584f
266a3cbc0302
ad2e97c6f17f
2642f128ad46
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
c79e4f89bfd3
a922e91a4f02
564e40829f80
a922e91a4f02
24c0d584ba86
9382e88eae22
ad2e97c6f17f
2642f128ad46
2d7a94f3eaae
f4807acf643d
9382e88eae22
3315e9263a53
880a39e5d8df
9382e88eae22
2642f128ad46
a60cd29ba7e2
2642f128ad46
65c27fd21769
20dc7a5eb748
7e5f8c12a3fc
564e40829f80
564e40829f80
0e6035a85980
564e40829f80
20dc7a5eb748
20dc7a5eb748
7ac09514a178
9793473d74be
9793473d74be
9793473d74be
e4b9a1d1fea1
7ac09514a178
564e40829f80
7e5f8c12a3fc
564e40829f80
564e40829f80
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import platform
import sys

import setuptools
# monkey patch setuptools to use distutils owner/group functionality
from setuptools.command import sdist


if sys.version_info < (3, 6):
    raise Exception('Kallithea requires Python 3.6 or later')


here = os.path.abspath(os.path.dirname(__file__))


def _get_meta_var(name, data, callback_handler=None):
    import re
    matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
    if matches:
        s = eval(matches.groups()[0])
        if callable(callback_handler):
            return callback_handler(s)
        return s

_meta = open(os.path.join(here, 'kallithea', '__init__.py'), 'r')
_metadata = _meta.read()
_meta.close()

def callback(V):
    return '.'.join(map(str, V[:3])) + '.'.join(V[3:])
__version__ = _get_meta_var('VERSION', _metadata, callback)
__license__ = _get_meta_var('__license__', _metadata)
__author__ = _get_meta_var('__author__', _metadata)
__url__ = _get_meta_var('__url__', _metadata)
# defines current platform
__platform__ = platform.system()

is_windows = __platform__ in ['Windows']

requirements = [
    "alembic >= 1.0.10, < 1.5",
    "gearbox >= 0.1.0, < 1",
    "waitress >= 0.8.8, < 1.5",
    "WebOb >= 1.8, < 1.9",
    "backlash >= 0.1.2, < 1",
    "TurboGears2 >= 2.4, < 2.5",
    "tgext.routes >= 0.2.0, < 1",
    "Beaker >= 1.10.1, < 2",
    "WebHelpers2 >= 2.0, < 2.1",
    "FormEncode >= 1.3.1, < 1.4",
    "SQLAlchemy >= 1.2.9, < 1.4",
    "Mako >= 0.9.1, < 1.2",
    "Pygments >= 2.2.0, < 2.6",
    "Whoosh >= 2.7.1, < 2.8",
    "celery >= 4.3, < 4.5",
    "Babel >= 1.3, < 2.9",
    "python-dateutil >= 2.1.0, < 2.9",
    "Markdown >= 2.2.1, < 3.2",
    "docutils >= 0.11, < 0.17",
    "URLObject >= 2.3.4, < 2.5",
    "Routes >= 2.0, < 2.5",
    "dulwich >= 0.19.0, < 0.20",
    "mercurial >= 5.2, < 5.4",
    "decorator >= 4.2.1, < 4.5",
    "Paste >= 2.0.3, < 3.4",
    "bleach >= 3.0, < 3.1.4",
    "Click >= 7.0, < 8",
    "ipaddr >= 2.2.0, < 2.3",
    "paginate >= 0.5, < 0.6",
    "paginate_sqlalchemy >= 0.3.0, < 0.4",
    "bcrypt >= 3.1.0, < 3.2",
]

dependency_links = [
]

classifiers = [
    'Development Status :: 4 - Beta',
    'Environment :: Web Environment',
    'Framework :: Pylons',
    'Intended Audience :: Developers',
    'License :: OSI Approved :: GNU General Public License (GPL)',
    'Operating System :: OS Independent',
    'Programming Language :: Python :: 3.6',
    'Programming Language :: Python :: 3.7',
    'Programming Language :: Python :: 3.8',
    'Topic :: Software Development :: Version Control',
]


# additional files from project that goes somewhere in the filesystem
# relative to sys.prefix
data_files = []

description = ('Kallithea is a fast and powerful management tool '
               'for Mercurial and Git with a built in push/pull server, '
               'full text search and code-review.')

keywords = ' '.join([
    'kallithea', 'mercurial', 'git', 'code review',
    'repo groups', 'ldap', 'repository management', 'hgweb replacement',
    'hgwebdir', 'gitweb replacement', 'serving hgweb',
])

# long description
README_FILE = 'README.rst'
try:
    long_description = open(README_FILE).read()
except IOError as err:
    sys.stderr.write(
        "[WARNING] Cannot find file specified as long_description (%s): %s\n"
        % (README_FILE, err)
    )
    long_description = description


sdist_org = sdist.sdist
class sdist_new(sdist_org):
    def initialize_options(self):
        sdist_org.initialize_options(self)
        self.owner = self.group = 'root'
sdist.sdist = sdist_new

packages = setuptools.find_packages(exclude=['ez_setup'])

setuptools.setup(
    name='Kallithea',
    version=__version__,
    description=description,
    long_description=long_description,
    keywords=keywords,
    license=__license__,
    author=__author__,
    author_email='kallithea@sfconservancy.org',
    dependency_links=dependency_links,
    url=__url__,
    install_requires=requirements,
    classifiers=classifiers,
    data_files=data_files,
    packages=packages,
    include_package_data=True,
    message_extractors={'kallithea': [
            ('**.py', 'python', None),
            ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
            ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
            ('public/**', 'ignore', None)]},
    zip_safe=False,
    entry_points="""
    [console_scripts]
    kallithea-api =    kallithea.bin.kallithea_api:main
    kallithea-gist =   kallithea.bin.kallithea_gist:main
    kallithea-config = kallithea.bin.kallithea_config:main
    kallithea-cli =    kallithea.bin.kallithea_cli:cli

    [paste.app_factory]
    main = kallithea.config.middleware:make_app
    """,
)