Bug 495802 - Krita::documents() leaks all elements when called from Python
Summary: Krita::documents() leaks all elements when called from Python
Status: ASSIGNED
Alias: None
Product: krita
Classification: Applications
Component: Scripting (show other bugs)
Version: 5.2.3
Platform: Other Linux
: NOR normal
Target Milestone: ---
Assignee: Halla Rempt
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-11-04 20:52 UTC by FeepingCreature
Modified: 2024-11-18 14:57 UTC (History)
1 user (show)

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description FeepingCreature 2024-11-04 20:52:06 UTC
***
If you're not sure this is actually a bug, instead post about it at https://discuss.kde.org

If you're reporting a crash, attach a backtrace with debug symbols; see https://community.kde.org/Guidelines_and_HOWTOs/Debugging/How_to_create_useful_crash_reports

Please remove this comment after reading and before submitting - thanks!
***

SUMMARY

The `Krita::documents()` method returns a `QList<Document*>`. When called from a Python plugin, all the `Document*` are leaked.


STEPS TO REPRODUCE
1. Open the scripter
2. Run
```
for i in range(100000):
    docs = Krita.instance().documents()
```

OBSERVED RESULT

Heap usage rises permanently

EXPECTED RESULT

Heap usage stays stable

SOFTWARE/OS VERSIONS
Windows: 
macOS: 
(available in the Info Center app, or by running `kinfo` in a terminal window)
Linux/KDE Plasma: 
KDE Plasma Version: 
KDE Frameworks Version: 
Qt Version: 5.15.14

ADDITIONAL INFORMATION

`documents()` is defined as `QList<Document *> documents() const /Factory/;`. But a `QList<*>` cannot track ownership. `/Factory/` just communicates that the caller owns the `QList`, but `QList` cleanup won't (can't!) clean up the `Document*`s. The method should probably return a `QList<QScopedPointer<Document>>`?
Comment 1 FeepingCreature 2024-11-04 20:52:54 UTC
Woops - forgot to remove the readme :)
Comment 2 FeepingCreature 2024-11-04 22:23:51 UTC
We worked around it on the Python side with this helper:
```

def acquire_elements(l: list[QOBJECT]) -> list[QOBJECT]:
    # Many Pykrita functions return a `QList<QObject*>` where the objects are
    # allocated for the callee. SIP does not handle this case and just leaks
    # the objects outright. Fix this by taking explicit ownership of the objects.
    # Note: ONLY call this if you are confident that the Pykrita function
    # allocates the list members!
    for obj in l:
        if obj is not None:
            sip.transferback(obj)
    return l
```
Comment 3 Halla Rempt 2024-11-18 14:57:00 UTC
You cannot add a QScopedPointer to a QList, but I'm trying to find alternative solutions...