Skip to content

Commit c16fb3f

Browse files
committed
Optimize package search
1 parent 2350a67 commit c16fb3f

1 file changed

Lines changed: 89 additions & 31 deletions

File tree

usr/lib/linuxmint/mintinstall/mintinstall.py

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from pathlib import Path
2020
import traceback
2121
from operator import attrgetter
22+
from collections import namedtuple
2223

2324
import gi
2425
gi.require_version('Gtk', '3.0')
@@ -611,6 +612,8 @@ def __init__(self, category):
611612

612613
self.add(box)
613614

615+
PkgInfoSearchCache = namedtuple('PkgInfoSearchCache', ['name', 'display_name', 'keywords', 'summary', 'description'])
616+
614617
class Application(Gtk.Application):
615618
(ACTION_TAB, PROGRESS_TAB, SPINNER_TAB) = list(range(3))
616619

@@ -663,6 +666,7 @@ def __init__(self):
663666
self.installer_pulse_timer = 0
664667
self.search_changed_timer = 0
665668
self.search_idle_timer = 0
669+
self.generate_search_cache_idle_timer = 0
666670

667671
self.action_button_signal_id = 0
668672
self.launch_button_signal_id = 0
@@ -1052,6 +1056,9 @@ def on_appstream_changed(self, installer):
10521056
if self.banner_tile is not None:
10531057
self.banner_tile.repopulate_tile()
10541058

1059+
GLib.idle_add(self.pregenerate_search_cache)
1060+
1061+
10551062
def on_installer_ready(self):
10561063
self.page_stack.set_visible_child_name(self.PAGE_LOADING)
10571064
try:
@@ -1072,10 +1079,14 @@ def on_installer_ready(self):
10721079
# Can take some time, don't block for it (these are categorizing packages based on apt info, not our listings)
10731080
GLib.idle_add(self.process_unmatched_packages)
10741081

1082+
if not self.installer.have_flatpak:
1083+
GLib.idle_add(self.pregenerate_search_cache)
1084+
10751085
housekeeping.run()
10761086

10771087
self.refresh_cache_menuitem.set_sensitive(True)
10781088
self.print_startup_time()
1089+
10791090
except Exception as e:
10801091
print("Loading error: %s" % e)
10811092
traceback.print_tb(e.__traceback__)
@@ -1397,11 +1408,11 @@ def on_process_exited(proc, result):
13971408
# Add a callback when we exit mintsources
13981409
p.wait_async(None, on_process_exited)
13991410

1400-
def should_show_pkginfo(self, pkginfo):
1411+
def should_show_pkginfo(self, pkginfo, allow_unverified_flatpaks):
14011412
if pkginfo.pkg_hash.startswith("apt"):
14021413
return True
14031414

1404-
if not self.settings.get_boolean(prefs.ALLOW_UNVERIFIED_FLATPAKS):
1415+
if not allow_unverified_flatpaks:
14051416
return pkginfo.verified
14061417

14071418
return pkginfo.refid.startswith("app/")
@@ -1753,6 +1764,10 @@ def on_search_changed(self, searchentry):
17531764
self.show_category(self.current_category)
17541765
elif terms != "" and len(terms) >= 3:
17551766
self.show_search_results(terms)
1767+
elif terms == "":
1768+
page = self.page_stack.get_visible_child_name()
1769+
if page == self.PAGE_LIST or page == self.PAGE_SEARCHING:
1770+
self.go_back_action()
17561771

17571772
self.search_changed_timer = 0
17581773
return False
@@ -2185,6 +2200,11 @@ def show_active_tasks(self):
21852200
def on_back_button_clicked(self, button):
21862201
self.go_back_action()
21872202

2203+
def cancel_running_search(self):
2204+
if self.search_idle_timer > 0:
2205+
GLib.source_remove(self.search_idle_timer)
2206+
self.search_idle_timer = 0
2207+
21882208
def go_back_action(self):
21892209
XApp.set_window_progress(self.main_window, 0)
21902210
self.stop_progress_pulse()
@@ -2202,6 +2222,8 @@ def go_back_action(self):
22022222
self.installer.cancel_task(self.current_task)
22032223
self.current_task = None
22042224

2225+
self.cancel_running_search()
2226+
22052227
if self.page_stack.get_visible_child_name() == self.PAGE_PREFS:
22062228
self.search_tool_item.set_sensitive(True)
22072229

@@ -2299,6 +2321,42 @@ def get_application_icon(self, pkginfo, size):
22992321

23002322
return imaging.get_icon(icon_string, size)
23012323

2324+
def update_package_search_cache(self, pkginfo, search_in_description):
2325+
if not hasattr(pkginfo, "search_cache"):
2326+
pkginfo.search_cache = PkgInfoSearchCache(
2327+
name=pkginfo.name.upper(),
2328+
display_name=pkginfo.get_display_name().upper(),
2329+
keywords=pkginfo.get_keywords().upper(),
2330+
summary=pkginfo.get_summary().upper(),
2331+
description=None
2332+
if not search_in_description
2333+
else self.installer.get_description(pkginfo, for_search=True).upper()
2334+
)
2335+
2336+
# installer.get_description() is very slow, so we only fetch it if it's required
2337+
if search_in_description and pkginfo.search_cache.description is None:
2338+
description = self.installer.get_description(pkginfo, for_search=True).upper()
2339+
pkginfo.search_cache = pkginfo.search_cache._replace(description=description)
2340+
2341+
def pregenerate_search_cache(self):
2342+
if self.generate_search_cache_idle_timer > 0:
2343+
GLib.source_remove(self.generate_search_cache_idle_timer)
2344+
self.generate_search_cache_idle_timer = 0
2345+
2346+
search_in_description = self.settings.get_boolean(prefs.SEARCH_IN_DESCRIPTION)
2347+
pkginfos = self.installer.cache.values()
2348+
2349+
def generate_package_cache(pkginfos_iter):
2350+
try:
2351+
pkginfo = next(pkginfos_iter)
2352+
self.update_package_search_cache(pkginfo, search_in_description)
2353+
return True
2354+
except StopIteration:
2355+
self.generate_search_cache_idle_timer = 0
2356+
return False
2357+
2358+
self.generate_search_cache_idle_timer = GLib.idle_add(generate_package_cache, iter(pkginfos))
2359+
23022360
@print_timing
23032361
def show_search_results(self, terms):
23042362
if not self.gui_ready:
@@ -2333,53 +2391,65 @@ def show_search_results(self, terms):
23332391

23342392
searched_packages = []
23352393

2336-
if self.search_idle_timer > 0:
2337-
GLib.source_remove(self.search_idle_timer)
2338-
self.search_idle_timer = 0
2394+
self.cancel_running_search()
23392395

23402396
search_in_summary = self.settings.get_boolean(prefs.SEARCH_IN_SUMMARY)
23412397
search_in_description = self.settings.get_boolean(prefs.SEARCH_IN_DESCRIPTION)
23422398

23432399
package_type_preference = self.settings.get_string(prefs.PACKAGE_TYPE_PREFERENCE)
23442400
hidden_packages = set()
2401+
allow_unverified_flatpaks = self.settings.get_boolean(prefs.ALLOW_UNVERIFIED_FLATPAKS)
23452402

23462403
def idle_search_one_package(pkginfos):
23472404
try:
2348-
pkginfo = pkginfos.pop(0)
2349-
except IndexError:
2405+
pkginfo = next(pkginfos)
2406+
except StopIteration:
23502407
self.search_idle_timer = 0
2408+
2409+
if package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_APT:
2410+
results = [p for p in searched_packages if not (p.pkg_hash.startswith("f") and p.name in hidden_packages)]
2411+
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK:
2412+
results = [p for p in searched_packages if not (p.pkg_hash.startswith("a") and p.name in hidden_packages)]
2413+
else:
2414+
results = searched_packages
2415+
2416+
GLib.idle_add(self.on_search_results_complete, results)
23512417
return False
23522418

23532419
flatpak = pkginfo.pkg_hash.startswith("f")
23542420
is_match = False
23552421

23562422
while True:
2357-
if not self.should_show_pkginfo(pkginfo):
2423+
if not self.should_show_pkginfo(pkginfo, allow_unverified_flatpaks):
23582424
break
23592425

2360-
if all(piece in pkginfo.name.upper() for piece in termsSplit):
2426+
self.update_package_search_cache(pkginfo, search_in_description)
2427+
2428+
if all(piece in pkginfo.search_cache.name for piece in termsSplit):
23612429
is_match = True
23622430
pkginfo.search_tier = 0
23632431
break
2432+
23642433
# pkginfo.name for flatpaks is their id (org.foo.BarMaker), which
23652434
# may not actually contain the app's name. In this case their display
23662435
# names are better. The 'name' is still checked first above, because
23672436
# it's static - get_display_name() may involve a lookup with appstream.
2368-
if flatpak and all(piece in pkginfo.get_display_name().upper() for piece in termsSplit):
2437+
if flatpak and all(piece in pkginfo.search_cache.display_name for piece in termsSplit):
23692438
is_match = True
23702439
pkginfo.search_tier = 0
23712440
break
23722441

2373-
if termsUpper in pkginfo.get_keywords().upper():
2442+
if termsUpper in pkginfo.search_cache.keywords:
23742443
is_match = True
23752444
pkginfo.search_tier = 50
23762445
break
23772446

2378-
if (search_in_summary and termsUpper in pkginfo.get_summary().upper()):
2447+
if (search_in_summary and termsUpper in pkginfo.search_cache.summary):
23792448
is_match = True
23802449
pkginfo.search_tier = 100
23812450
break
2382-
if(search_in_description and termsUpper in self.installer.get_description(pkginfo).upper()):
2451+
2452+
if (search_in_description and termsUpper in pkginfo.search_cache.description):
23832453
is_match = True
23842454
pkginfo.search_tier = 200
23852455
break
@@ -2392,23 +2462,9 @@ def idle_search_one_package(pkginfos):
23922462
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak:
23932463
hidden_packages.add(DEB_EQUIVS.get(pkginfo.name))
23942464

2395-
# Repeat until empty
2396-
if len(pkginfos) > 0:
2397-
return True
2398-
2399-
self.search_idle_timer = 0
2400-
2401-
if package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_APT:
2402-
results = [p for p in searched_packages if not (p.pkg_hash.startswith("f") and p.name in hidden_packages)]
2403-
elif package_type_preference == prefs.PACKAGE_TYPE_PREFERENCE_FLATPAK:
2404-
results = [p for p in searched_packages if not (p.pkg_hash.startswith("a") and p.name in hidden_packages)]
2405-
else:
2406-
results = searched_packages
2407-
2408-
GLib.idle_add(self.on_search_results_complete, results)
2409-
return False
2465+
return True
24102466

2411-
self.search_idle_timer = GLib.idle_add(idle_search_one_package, list(listing))
2467+
self.search_idle_timer = GLib.idle_add(idle_search_one_package, iter(listing))
24122468

24132469
def on_search_results_complete(self, results):
24142470
self.page_stack.set_visible_child_name(self.PAGE_LIST)
@@ -2479,6 +2535,7 @@ def sort_packages(self, pkgs, key_func):
24792535

24802536
def show_packages(self, pkginfos, from_search=False):
24812537
self.stop_slideshow_timer()
2538+
allow_unverified_flatpaks = self.settings.get_boolean(prefs.ALLOW_UNVERIFIED_FLATPAKS)
24822539

24832540
if self.one_package_idle_timer > 0:
24842541
GLib.source_remove(self.one_package_idle_timer)
@@ -2515,7 +2572,7 @@ def show_packages(self, pkginfos, from_search=False):
25152572
apps = [info for info in pkginfos] # should_show_pkginfo was applied during search matching
25162573
apps = self.sort_packages(apps, attrgetter("unverified", "search_tier", "score_desc", "name"))
25172574
else:
2518-
apps = [info for info in pkginfos if self.should_show_pkginfo(info)]
2575+
apps = [info for info in pkginfos if self.should_show_pkginfo(info, allow_unverified_flatpaks)]
25192576
apps = self.sort_packages(apps, attrgetter("unverified", "score_desc", "name"))
25202577
apps = apps[0:201]
25212578

@@ -2845,10 +2902,11 @@ def on_package_type_button_clicked(self, button, pkginfo):
28452902
self.show_package(pkginfo, self.previous_page)
28462903

28472904
def get_flatpak_for_deb(self, pkginfo):
2905+
allow_unverified_flatpaks = self.settings.get_boolean(prefs.ALLOW_UNVERIFIED_FLATPAKS)
28482906
try:
28492907
fp_name = FLATPAK_EQUIVS[pkginfo.name]
28502908
flatpak_pkginfo = self.installer.find_pkginfo(fp_name, installer.PKG_TYPE_FLATPAK)
2851-
if self.should_show_pkginfo(flatpak_pkginfo):
2909+
if self.should_show_pkginfo(flatpak_pkginfo, allow_unverified_flatpaks):
28522910
return flatpak_pkginfo
28532911
except:
28542912
return None

0 commit comments

Comments
 (0)