Files @ ee1da602b590
Branch filter:

Location: kallithea/scripts/generate-ini.py - annotation

Thomas De Schampheleire
repo_groups: fix deletion of subgroups

Deletion of a repository group that has a parent group (i.e. is not at the
root of the repository group tree) failed as follows:

Traceback (most recent call last):
[...]
File ".../lib/python3.9/site-packages/tg/configurator/components/dispatch.py", line 114, in _call_controller
return controller(*remainder, **params)
File "<decorator-gen-5>", line 2, in delete

File "/home/tdescham/repo/contrib/kallithea/kallithea-release/kallithea/lib/auth.py", line 572, in __wrapper
return func(*fargs, **fkwargs)
File "/home/tdescham/repo/contrib/kallithea/kallithea-release/kallithea/controllers/admin/repo_groups.py", line 271, in delete
if gr.parent_group:
File ".../lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 294, in __get__
return self.impl.get(instance_state(instance), dict_)
File ".../lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 730, in get
value = self.callable_(state, passive)
File ".../lib/python3.9/site-packages/sqlalchemy/orm/strategies.py", line 717, in _load_for_state
raise orm_exc.DetachedInstanceError(
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <RepoGroup at 0x7f1f2664f4c0> is not bound to a Session; lazy load operation of attribute 'parent_group' cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)


In the reference 'gr.parent_group', 'gr' is an SQLAlchemy object referring
to the group being deleted, and 'gr.parent_group' is a lazy reference to its
parent group. The 'lazy' means that the parent group object is not loaded
automatically when 'gr' is assigned, but instead will be loaded on-the-fly
when the parent group is actually accessed. See [1] and [2] for more
information.

The problem was that the lazy 'parent_group' attribute was accessed _after_
deleting the database object it was part of.

Fix this by obtaining a handle to the parent group _before_ deleting the
subgroup.

Reported-by: André Klitzing (via mailing list)


[1] https://docs.sqlalchemy.org/en/13/errors.html#error-bhk3
[2] https://docs.sqlalchemy.org/en/13/orm/loading_relationships.html
#!/usr/bin/env python3
"""
Generate development.ini based on the ini template.
"""

import re

from kallithea.lib import inifile


# files to be generated from the mako template
ini_files = [
    ('development.ini',
        {
            '[server:main]': {
                'host': '0.0.0.0',
            },
            '[app:main]': {
                'debug': 'true',
                'app_instance_uuid': 'development-not-secret',
                'session.secret': 'development-not-secret',
            },
            '[logger_root]': {
                'handlers': 'console_color',
            },
            '[logger_routes]': {
                'level': 'DEBUG',
            },
            '[logger_beaker]': {
                'level': 'DEBUG',
            },
            '[logger_templates]': {
                'level': 'INFO',
            },
            '[logger_kallithea]': {
                'level': 'DEBUG',
            },
            '[logger_tg]': {
                'level': 'DEBUG',
            },
            '[logger_gearbox]': {
                'level': 'DEBUG',
            },
            '[logger_whoosh_indexer]': {
                'level': 'DEBUG',
            },
        },
    ),
]


def main():
    # make sure all mako lines starting with '#' (the '##' comments) are marked up as <text>
    makofile = inifile.template_file
    print('reading:', makofile)
    mako_org = open(makofile).read()
    mako_no_text_markup = re.sub(r'</?%text>', '', mako_org)
    mako_marked_up = re.sub(r'\n##(.*)', r'\n<%text>##</%text>\1', mako_no_text_markup, flags=re.MULTILINE)
    if mako_marked_up != mako_org:
        print('writing:', makofile)
        open(makofile, 'w').write(mako_marked_up)

    lines = re.findall(r'\n(# [^ ].*)', mako_marked_up)
    if lines:
        print('ERROR: the template .ini file convention is to use "## Foo Bar" for text comments and "#foo = bar" for disabled settings')
        for line in lines:
            print(line)
        raise SystemExit(1)

    # create ini files
    for fn, settings in ini_files:
        print('updating:', fn)
        inifile.create(fn, None, settings)


if __name__ == '__main__':
    main()