Skip to content

Memory leak every time a Prompt class is created #88

@jericjan

Description

@jericjan

I know this project has been unmaintained for quite a while but I'd like to point this out.

Consider the following code:
a.py

from InquirerPy import inquirer
import objgraph
import uuid

thing = inquirer.select("asdasd", ["A", "B"])

while True:
    a = thing.execute()
    thing = inquirer.select(
        f"test + {uuid.uuid4()}",
        [
            {"name": "aaaaaa", "value": f"AAAAAAA {uuid.uuid4()}", "enabled": False},
            {"name": "bbbb", "value": f"BBBBB {uuid.uuid4()}", "enabled": False},
        ],
    )
    print(f"selected {a}")
    objgraph.show_growth()

b.py

from InquirerPy import inquirer
import objgraph
import uuid

thing = inquirer.select("foo", ["bar", "foobar"])

while True:
    a = thing.execute()
    thing._message = f"bar + {uuid.uuid4()}"
    thing.content_control.choices = [
        {"name": f"a {uuid.uuid4()}", "value": f"A {uuid.uuid4()}", "enabled": False},
        {"name": f"b {uuid.uuid4()}" , "value": f"B {uuid.uuid4()}", "enabled": False},
    ]
    print(f"selected {a}")
    objgraph.show_growth()

a.py re-initializes ListPrompt while b.py just initialized it once. You would think that the resources used by a.py would be garbage collected, but it doesn't. Here is the output of each script:

a.py

? foo bar
selected bar
function                       7680     +7680
dict                           6378     +6378
tuple                          5313     +5313
Binding                        2334     +2334
weakref                        1731     +1731
wrapper_descriptor             1327     +1327
getset_descriptor              1187     +1187
method_descriptor              1087     +1087
builtin_function_or_method     1030     +1030
frozenset                       968      +968
? bar + 26ce67ea-6d8b-4820-87cc-9b44fa917c0c a 9c807c14-8215-44eb-b208-739c19259a6f
selected A 679a82a5-c027-4354-9b9c-20f6662c5802
dict          8300     +1922
Binding       4018     +1684
tuple         6320     +1007
function      8200      +520
frozenset     1315      +347
Attrs          968      +331
cell          1193      +258
Char           178      +131
list          1018      +124
deque          258       +87
? bar + 30c63a10-5540-4b97-b328-7112a795290b a 56ffc2e5-5dca-4726-bf9a-d710cb35aa38
selected A 9bd5c882-653d-463e-92fe-e6793eef0749
dict           10223     +1923
Binding         5702     +1684
tuple           7332     +1012
function        8721      +521
frozenset       1662      +347
Attrs           1301      +333
cell            1452      +259
list            1147      +129
deque            345       +87
SimpleCache      327       +83
? bar + 1a92660f-addb-4a06-acec-da0be4e8d738 
  a 21345dd0-9eec-4a8f-9126-29698d825188a 5c4fa568-a791-4933-ba3f-1837c5c513fb

... it loops after this

b.py

? foo bar
selected bar
function                       7231     +7231
dict                           5600     +5600
tuple                          4712     +4712
Binding                        1740     +1740
weakref                        1730     +1730
wrapper_descriptor             1327     +1327
getset_descriptor              1187     +1187
method_descriptor              1087     +1087
builtin_function_or_method     1029     +1029
frozenset                       947      +947
? bar + 7e54e57b-9d7a-4ee8-b39e-bafd356b7759 a 6d83e40b-e46b-4ecd-8128-642b6dd41b60
selected A 7781c1b7-8494-4d40-aa8e-03cf899f7c21
Char           133       +86
list           807        +7
tuple         4716        +4
dict          5603        +3
function      7234        +3
cell           759        +3
Point            8        +3
UIContent        7        +3
? bar + a737cfee-ffa1-4855-9a1c-756c9c54b626 a 07c9ecfc-a19b-4d0e-b575-61ad013c5b16
selected A d5a31898-0823-400d-afc2-495c69968d79
Char           155       +22
list           814        +7
tuple         4723        +7
dict          5606        +3
function      7237        +3
cell           762        +3
Point           11        +3
UIContent       10        +3
? bar + 1a169725-c394-47a9-820b-32c3e2580147 a b6624fa8-2550-40ab-a7b8-5ba0ff9b18d3
selected A b9e230b7-bca0-48ba-ae61-43357f64f665
list           821        +7
tuple         4729        +6
dict          5609        +3
function      7240        +3
cell           765        +3
Point           14        +3
UIContent       13        +3
Char           157        +2

... some loops later

? bar + efaaca86-4174-4e83-80f6-a352d06f9ee8 b 2dba6eb7-8058-42d0-8cd5-223d84f8f68a
selected B 01bced50-bf14-4aa3-9d98-e3b9e7bc8ac8
? bar + 48d57b55-ba3e-40f5-b127-2eedadb898ac b 7629c3f9-ecfc-4dc9-80fa-5156683c88ef
selected B ac8bd003-3b02-42dc-8381-5bbd90792d79

I'm assuming it stores the choices to a cache of some sort. Whatever that is happening in a.py, it could easily bloat the app's memory in programs that go through a lot of prompts

EDIT: There is a bit of a workaround to reduce the memory leak. It doesn't completely get rid of it but it's something

def clean_prompt(prompt: Union[BaseComplexPrompt, InputPrompt]):
    if isinstance(prompt, BaseComplexPrompt):
        prompt.application.reset() 
        prompt.application = None
    if isinstance(prompt, BaseListPrompt):
        prompt.content_control.reset()
        prompt.content_control = None 
    del prompt
    gc.collect()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions