RisingOrange/anki-enhanced-cloze

RecursionError: maximum recursion depth exceeded / breaks other add-on / changes how mw.col.models works

ijgnd opened this issue · 3 comments

ijgnd commented

When you have only the add-on "Enhanced Cloze" installed (in Anki 2.1.49) running print(mw.col.models) from the debug console does not work for me but instead throws an error message like this:

Traceback (most recent call last):
  File "aqt/main.py", line 1524, in onDebugRet
  File "<string>", line 1, in <module>
...
  File "anki/models.py", line 91, in __repr__
  File "pprint.py", line 58, in pformat
  File "pprint.py", line 153, in pformat
  File "pprint.py", line 170, in _format
  File "pprint.py", line 404, in _repr
  File "pprint.py", line 417, in format
  File "pprint.py", line 523, in _safe_repr
  File "pprint.py", line 101, in _safe_tuple
RecursionError: maximum recursion depth exceeded while calling a Python object

The add-on "Enhanced Cloze" also breaks the add-on "CrowdAnki" with a similar error message. I tested with just these two add-ons with Anki 2.1.49 in linux and 2.1.54 in Windows.

@aplaice found out that your compat.add_compatibility_aliasseems to cause a problem and posted a preliminary fix, see Stvad/CrowdAnki#179 (comment).

I'm posting here because this might also be relevant for other add-ons. As far as I see you have the same line if new_name not in list(namespace.__dict__.keys()) also in the AnKing Note Types (Easy Customization) add-on though "AnKing Note Types" doesn't seem to break the CrowdAnki add-on.

@ijgnd Thanks, I'll look into it.

From a cursory analysis, AFAIU there are two issues with the compat code:

  1. An object's methods are usually not listed in object.__dict__. (They are/should be present in dir(object), though.)

    This means that if new_name not in list(...) will always be true, for all Anki versions, so the compat renaming will occur even when it's not necessary.

    I believe that this can be easily fixed by using dir(namespace).

  2. setattr and getattr behave asymmetrically. getattr will "get" an attribute/method even if it's not present in the class's/objects __dict__. setattr however, will add the attribute to the object's __dict__. (i.e. setattr(foo, "x", bar) is not completely equivalent to foo.x = bar.)

    This means that when the object is serialized, python will also try to serialise the newly added attributes. (This is what happens in CrowdAnki.)

    I'm not sure how to easily/properly/neatly fix this, though. However, it's also a far more niche problem than 1 — most of the time Anki classes/objects don't need to be serialised.

(Fixing 1 will fix the crash from print(mw.col.models) in the console and from the combination of CrowdAnki and Enhanced cloze for current versions of Anki.)

(Sorry for jumping from a crash triggered by CrowdAnki, to your add-on, but it's easier to fix on your end :) (I'd have to exclude the various attributes from the serialisation of models which would be tricky and ugly), and I think that 1 at least is a very minor bug in your add-on, in itself, (e.g. it causes spurious byName is deprecated: please use 'by_name' background messages).)

also in the AnKing Note Types (Easy Customization) add-on though "AnKing Note Types" doesn't seem to break the CrowdAnki add-on

I'd expect that's because the attributes of aqt.col.models probably aren't being aliased, there (only those of other objects).

@aplaice @ijgnd
The new version containing the fix (using dir(namespace)) is live on AnkiWeb. Thanks for the detailed report and explanation!