Last active
November 14, 2025 05:00
-
-
Save clayote/f1aa49fad1067bf9aee2c3eebf69a383 to your computer and use it in GitHub Desktop.
Trying to make my Kivy 3.0.0 app backward-compatible with Kivy 2.3.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This happens at import time, before anything else is imported from kivy | |
| from kivy import __version__ as kivy_version_str, parse_kivy_version | |
| if parse_kivy_version(kivy_version_str)[0][0] < 3: | |
| from kivy.properties import BooleanProperty | |
| from kivy.uix import button | |
| def on_state(self, *_): | |
| self.pressed = self.state == "down" | |
| button.ButtonBehavior.pressed = BooleanProperty(False) | |
| button.ButtonBehavior.on_state = on_state | |
| from kivy.uix import togglebutton | |
| def on_state(self, *_): | |
| self.active = self.state == "down" | |
| togglebutton.ToggleButtonBehavior.active = BooleanProperty(False) | |
| togglebutton.ToggleButtonBehavior.on_state = on_state |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # this happens when I use a copy of the standard kivy_init fixture in my conftest.py | |
| test setup failed | |
| self = <kivy.lang.builder.BuilderBase object at 0x7e5603918170> | |
| widget = <Screen name='funcs'> | |
| rule = <ParserRuleProperty name='store_name' filename=/home/sanotehu/src/Lisien/elide/elide/stores.kv:244 value="'method'" watched_keys=None> | |
| rootrule = <ParserRule name='<FuncsEdScreen>'>, template_ctx = None | |
| ignored_consts = {'name', 'toggle'} | |
| rule_children = [<elide.util.SelectableRecycleBoxLayout object at 0x7e5582eb5710>, <elide.stores.StoreList object at 0x7e5582ec59b0>, ...e040>, <elide.stores.FunctionNameInput object at 0x7e5582e69c50>, <kivy.uix.label.Label object at 0x7e5582e09630>, ...] | |
| def _apply_rule(self, widget, rule, rootrule, template_ctx=None, | |
| ignored_consts=set(), rule_children=None): | |
| # widget: the current instantiated widget | |
| # rule: the current rule | |
| # rootrule: the current root rule (for children of a rule) | |
| # will collect reference to all the id in children | |
| assert rule not in self.rulectx | |
| self.rulectx[rule] = rctx = { | |
| 'ids': {'root': widget.proxy_ref}, | |
| 'set': [], 'hdl': []} | |
| # extract the context of the rootrule (not rule!) | |
| assert rootrule in self.rulectx | |
| rctx = self.rulectx[rootrule] | |
| # if a template context is passed, put it as "ctx" | |
| if template_ctx is not None: | |
| rctx['ids']['ctx'] = QueryDict(template_ctx) | |
| # if we got an id, put it in the root rule for a later global usage | |
| if rule.id: | |
| # use only the first word as `id` discard the rest. | |
| rule.id = rule.id.split('#', 1)[0].strip() | |
| rctx['ids'][rule.id] = widget.proxy_ref | |
| # set id name as a attribute for root widget so one can in python | |
| # code simply access root_widget.id_name | |
| _ids = dict(rctx['ids']) | |
| _root = _ids.pop('root') | |
| _new_ids = _root.ids | |
| for _key, _value in _ids.items(): | |
| if _value == _root: | |
| # skip on self | |
| continue | |
| _new_ids[_key] = _value | |
| _root.ids = _new_ids | |
| # first, ensure that the widget have all the properties used in | |
| # the rule if not, they will be created as ObjectProperty. | |
| rule.create_missing(widget) | |
| # build the widget canvas | |
| if rule.canvas_before: | |
| with widget.canvas.before: | |
| self._build_canvas(widget.canvas.before, widget, | |
| rule.canvas_before, rootrule) | |
| if rule.canvas_root: | |
| with widget.canvas: | |
| self._build_canvas(widget.canvas, widget, | |
| rule.canvas_root, rootrule) | |
| if rule.canvas_after: | |
| with widget.canvas.after: | |
| self._build_canvas(widget.canvas.after, widget, | |
| rule.canvas_after, rootrule) | |
| # create children tree | |
| Factory_get = Factory.get | |
| Factory_is_template = Factory.is_template | |
| for crule in rule.children: | |
| cname = crule.name | |
| if cname in ('canvas', 'canvas.before', 'canvas.after'): | |
| raise ParserException( | |
| crule.ctx, crule.line, | |
| 'Canvas instructions added in kv must ' | |
| 'be declared before child widgets.') | |
| # depending if the child rule is a template or not, we are not | |
| # having the same approach | |
| cls = Factory_get(cname) | |
| if Factory_is_template(cname): | |
| # we got a template, so extract all the properties and | |
| # handlers, and push them in a "ctx" dictionary. | |
| ctx = {} | |
| idmap = copy(global_idmap) | |
| idmap.update({'root': rctx['ids']['root']}) | |
| if 'ctx' in rctx['ids']: | |
| idmap.update({'ctx': rctx['ids']['ctx']}) | |
| try: | |
| for prule in crule.properties.values(): | |
| value = prule.co_value | |
| if type(value) is CodeType: | |
| value = eval(value, idmap) | |
| ctx[prule.name] = value | |
| for prule in crule.handlers: | |
| value = eval(prule.value, idmap) | |
| ctx[prule.name] = value | |
| except Exception as e: | |
| tb = sys.exc_info()[2] | |
| raise BuilderException( | |
| prule.ctx, prule.line, | |
| '{}: {}'.format(e.__class__.__name__, e), cause=tb) | |
| # create the template with an explicit ctx | |
| child = cls(**ctx) | |
| widget.add_widget(child) | |
| # reference it on our root rule context | |
| if crule.id: | |
| rctx['ids'][crule.id] = child | |
| else: | |
| # we got a "normal" rule, construct it manually | |
| # we can't construct it without __no_builder=True, because the | |
| # previous implementation was doing the add_widget() before | |
| # apply(), and so, we could use "self.parent". | |
| child = cls(__no_builder=True) | |
| widget.add_widget(child) | |
| child.apply_class_lang_rules( | |
| root=rctx['ids']['root'], rule_children=rule_children) | |
| self._apply_rule( | |
| child, crule, rootrule, rule_children=rule_children) | |
| if rule_children is not None: | |
| rule_children.append(child) | |
| # append the properties and handlers to our final resolution task | |
| if rule.properties: | |
| rctx['set'].append((widget.proxy_ref, | |
| list(rule.properties.values()))) | |
| for key, crule in rule.properties.items(): | |
| # clear previously applied rules if asked | |
| if crule.ignore_prev: | |
| Builder.unbind_property(widget, key) | |
| if rule.handlers: | |
| rctx['hdl'].append((widget.proxy_ref, rule.handlers)) | |
| # if we are applying another rule that the root one, then it's done for | |
| # us! | |
| if rootrule is not rule: | |
| del self.rulectx[rule] | |
| return | |
| # normally, we can apply a list of properties with a proper context | |
| try: | |
| rule = None | |
| for widget_set, rules in reversed(rctx['set']): | |
| for rule in rules: | |
| assert isinstance(rule, ParserRuleProperty) | |
| key = rule.name | |
| value = rule.co_value | |
| if type(value) is CodeType: | |
| value, bound = create_handler( | |
| widget_set, widget_set, key, value, rule, | |
| rctx['ids']) | |
| # if there's a rule | |
| if (widget_set != widget or bound or | |
| key not in ignored_consts): | |
| setattr(widget_set, key, value) | |
| else: | |
| if (widget_set != widget or | |
| key not in ignored_consts): | |
| setattr(widget_set, key, value) | |
| except Exception as e: | |
| if rule is not None: | |
| tb = sys.exc_info()[2] | |
| raise BuilderException(rule.ctx, rule.line, | |
| '{}: {}'.format(e.__class__.__name__, | |
| e), cause=tb) | |
| raise e | |
| # build handlers | |
| try: | |
| crule = None | |
| for widget_set, rules in rctx['hdl']: | |
| for crule in rules: | |
| assert isinstance(crule, ParserRuleProperty) | |
| assert crule.name.startswith('on_') | |
| key = crule.name | |
| if not widget_set.is_event_type(key): | |
| key = key[3:] | |
| idmap = copy(global_idmap) | |
| idmap.update(rctx['ids']) | |
| idmap['self'] = widget_set.proxy_ref | |
| if not widget_set.fbind(key, custom_callback, crule, | |
| idmap): | |
| > raise AttributeError(key) | |
| E AttributeError: active | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/lang/builder.py:734: AttributeError | |
| During handling of the above exception, another exception occurred: | |
| kivy = None | |
| prefix = '/tmp/pytest-of-sanotehu/pytest-29/test_character_switcher0' | |
| @pytest.fixture | |
| def elide_app(kivy, prefix): | |
| app = make_elide_app( | |
| prefix, immediate_start=True, character_name="physical", workers=0 | |
| ) | |
| app.leave_game = True | |
| app.config = ConfigParser(None) | |
| app.build_config(app.config) | |
| > Window.add_widget(app.build()) | |
| elide/elide/tests/conftest.py:115: | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| elide/elide/app.py:389: in build | |
| self.start_game() | |
| elide/elide/app.py:683: in start_game | |
| self._add_screens() | |
| elide/elide/app.py:604: in _add_screens | |
| self.funcs = FuncsEdScreen(name="funcs", toggle=toggler("funcs")) | |
| elide/elide/stores.py:749: in __init__ | |
| super().__init__(**kw) | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/uix/relativelayout.py:274: in __init__ | |
| super(RelativeLayout, self).__init__(**kw) | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/uix/floatlayout.py:65: in __init__ | |
| super(FloatLayout, self).__init__(**kwargs) | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/uix/layout.py:76: in __init__ | |
| super(Layout, self).__init__(**kwargs) | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/uix/widget.py:366: in __init__ | |
| self.apply_class_lang_rules( | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/uix/widget.py:470: in apply_class_lang_rules | |
| Builder.apply( | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/lang/builder.py:545: in apply | |
| self._apply_rule( | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| self = <kivy.lang.builder.BuilderBase object at 0x7e5603918170> | |
| widget = <Screen name='funcs'> | |
| rule = <ParserRuleProperty name='store_name' filename=/home/sanotehu/src/Lisien/elide/elide/stores.kv:244 value="'method'" watched_keys=None> | |
| rootrule = <ParserRule name='<FuncsEdScreen>'>, template_ctx = None | |
| ignored_consts = {'name', 'toggle'} | |
| rule_children = [<elide.util.SelectableRecycleBoxLayout object at 0x7e5582eb5710>, <elide.stores.StoreList object at 0x7e5582ec59b0>, ...e040>, <elide.stores.FunctionNameInput object at 0x7e5582e69c50>, <kivy.uix.label.Label object at 0x7e5582e09630>, ...] | |
| def _apply_rule(self, widget, rule, rootrule, template_ctx=None, | |
| ignored_consts=set(), rule_children=None): | |
| # widget: the current instantiated widget | |
| # rule: the current rule | |
| # rootrule: the current root rule (for children of a rule) | |
| # will collect reference to all the id in children | |
| assert rule not in self.rulectx | |
| self.rulectx[rule] = rctx = { | |
| 'ids': {'root': widget.proxy_ref}, | |
| 'set': [], 'hdl': []} | |
| # extract the context of the rootrule (not rule!) | |
| assert rootrule in self.rulectx | |
| rctx = self.rulectx[rootrule] | |
| # if a template context is passed, put it as "ctx" | |
| if template_ctx is not None: | |
| rctx['ids']['ctx'] = QueryDict(template_ctx) | |
| # if we got an id, put it in the root rule for a later global usage | |
| if rule.id: | |
| # use only the first word as `id` discard the rest. | |
| rule.id = rule.id.split('#', 1)[0].strip() | |
| rctx['ids'][rule.id] = widget.proxy_ref | |
| # set id name as a attribute for root widget so one can in python | |
| # code simply access root_widget.id_name | |
| _ids = dict(rctx['ids']) | |
| _root = _ids.pop('root') | |
| _new_ids = _root.ids | |
| for _key, _value in _ids.items(): | |
| if _value == _root: | |
| # skip on self | |
| continue | |
| _new_ids[_key] = _value | |
| _root.ids = _new_ids | |
| # first, ensure that the widget have all the properties used in | |
| # the rule if not, they will be created as ObjectProperty. | |
| rule.create_missing(widget) | |
| # build the widget canvas | |
| if rule.canvas_before: | |
| with widget.canvas.before: | |
| self._build_canvas(widget.canvas.before, widget, | |
| rule.canvas_before, rootrule) | |
| if rule.canvas_root: | |
| with widget.canvas: | |
| self._build_canvas(widget.canvas, widget, | |
| rule.canvas_root, rootrule) | |
| if rule.canvas_after: | |
| with widget.canvas.after: | |
| self._build_canvas(widget.canvas.after, widget, | |
| rule.canvas_after, rootrule) | |
| # create children tree | |
| Factory_get = Factory.get | |
| Factory_is_template = Factory.is_template | |
| for crule in rule.children: | |
| cname = crule.name | |
| if cname in ('canvas', 'canvas.before', 'canvas.after'): | |
| raise ParserException( | |
| crule.ctx, crule.line, | |
| 'Canvas instructions added in kv must ' | |
| 'be declared before child widgets.') | |
| # depending if the child rule is a template or not, we are not | |
| # having the same approach | |
| cls = Factory_get(cname) | |
| if Factory_is_template(cname): | |
| # we got a template, so extract all the properties and | |
| # handlers, and push them in a "ctx" dictionary. | |
| ctx = {} | |
| idmap = copy(global_idmap) | |
| idmap.update({'root': rctx['ids']['root']}) | |
| if 'ctx' in rctx['ids']: | |
| idmap.update({'ctx': rctx['ids']['ctx']}) | |
| try: | |
| for prule in crule.properties.values(): | |
| value = prule.co_value | |
| if type(value) is CodeType: | |
| value = eval(value, idmap) | |
| ctx[prule.name] = value | |
| for prule in crule.handlers: | |
| value = eval(prule.value, idmap) | |
| ctx[prule.name] = value | |
| except Exception as e: | |
| tb = sys.exc_info()[2] | |
| raise BuilderException( | |
| prule.ctx, prule.line, | |
| '{}: {}'.format(e.__class__.__name__, e), cause=tb) | |
| # create the template with an explicit ctx | |
| child = cls(**ctx) | |
| widget.add_widget(child) | |
| # reference it on our root rule context | |
| if crule.id: | |
| rctx['ids'][crule.id] = child | |
| else: | |
| # we got a "normal" rule, construct it manually | |
| # we can't construct it without __no_builder=True, because the | |
| # previous implementation was doing the add_widget() before | |
| # apply(), and so, we could use "self.parent". | |
| child = cls(__no_builder=True) | |
| widget.add_widget(child) | |
| child.apply_class_lang_rules( | |
| root=rctx['ids']['root'], rule_children=rule_children) | |
| self._apply_rule( | |
| child, crule, rootrule, rule_children=rule_children) | |
| if rule_children is not None: | |
| rule_children.append(child) | |
| # append the properties and handlers to our final resolution task | |
| if rule.properties: | |
| rctx['set'].append((widget.proxy_ref, | |
| list(rule.properties.values()))) | |
| for key, crule in rule.properties.items(): | |
| # clear previously applied rules if asked | |
| if crule.ignore_prev: | |
| Builder.unbind_property(widget, key) | |
| if rule.handlers: | |
| rctx['hdl'].append((widget.proxy_ref, rule.handlers)) | |
| # if we are applying another rule that the root one, then it's done for | |
| # us! | |
| if rootrule is not rule: | |
| del self.rulectx[rule] | |
| return | |
| # normally, we can apply a list of properties with a proper context | |
| try: | |
| rule = None | |
| for widget_set, rules in reversed(rctx['set']): | |
| for rule in rules: | |
| assert isinstance(rule, ParserRuleProperty) | |
| key = rule.name | |
| value = rule.co_value | |
| if type(value) is CodeType: | |
| value, bound = create_handler( | |
| widget_set, widget_set, key, value, rule, | |
| rctx['ids']) | |
| # if there's a rule | |
| if (widget_set != widget or bound or | |
| key not in ignored_consts): | |
| setattr(widget_set, key, value) | |
| else: | |
| if (widget_set != widget or | |
| key not in ignored_consts): | |
| setattr(widget_set, key, value) | |
| except Exception as e: | |
| if rule is not None: | |
| tb = sys.exc_info()[2] | |
| raise BuilderException(rule.ctx, rule.line, | |
| '{}: {}'.format(e.__class__.__name__, | |
| e), cause=tb) | |
| raise e | |
| # build handlers | |
| try: | |
| crule = None | |
| for widget_set, rules in rctx['hdl']: | |
| for crule in rules: | |
| assert isinstance(crule, ParserRuleProperty) | |
| assert crule.name.startswith('on_') | |
| key = crule.name | |
| if not widget_set.is_event_type(key): | |
| key = key[3:] | |
| idmap = copy(global_idmap) | |
| idmap.update(rctx['ids']) | |
| idmap['self'] = widget_set.proxy_ref | |
| if not widget_set.fbind(key, custom_callback, crule, | |
| idmap): | |
| raise AttributeError(key) | |
| # hack for on_parent | |
| if crule.name == 'on_parent': | |
| Factory.Widget.parent.dispatch(widget_set.__self__) | |
| except Exception as e: | |
| if crule is not None: | |
| tb = sys.exc_info()[2] | |
| > raise BuilderException( | |
| crule.ctx, crule.line, | |
| '{}: {}'.format(e.__class__.__name__, e), cause=tb) | |
| E kivy.lang.builder.BuilderException: Parser: File "/home/sanotehu/src/Lisien/elide/elide/stores.kv", line 240: | |
| E ... | |
| E 238: id: method | |
| E 239: text: 'Method' | |
| E >> 240: on_active: methods.save() | |
| E 241: MethodEdBox: | |
| E 242: id: methods | |
| E ... | |
| E AttributeError: active | |
| E File "/home/sanotehu/.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/lang/builder.py", line 734, in _apply_rule | |
| E raise AttributeError(key) | |
| ../../.local/share/virtualenv/LiSE312/lib/python3.12/site-packages/kivy/lang/builder.py:741: BuilderException |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment