New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completion refactor V3 #2295
Completion refactor V3 #2295
Conversation
Can we close #2144 and/or #2178 with this open?
I've been thinking about that too. I originally wanted everything to be plain-text, but at least for cookies I can't do that anymore with QtWebEngine either. I wonder if we could just ship a script (say, We need the conversion code in qutebrowser anyways (to migrate existing data), so the only question is whether the performance hit on start is too big. If it is, I'm open to doing that. |
a89ac2f
to
8001680
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've only taken a relatively quick look, and I already love how this makes everything simpler, despite involving SQL
The only thing I'm really unsure about is that it's a QAbstractItemModel
actually used in the view, and not the SQL model. Doesn't this hinder performance a lot again? Can we use the Qt model directly?
qutebrowser/browser/history.py
Outdated
|
||
Signals: | ||
add_completion_item: Emitted before a new Entry is added. | ||
Used to sync with the completion. | ||
arg: The new Entry. | ||
item_added: Emitted after a new Entry is added. | ||
Used to tell the savemanager that the history is dirty. | ||
arg: The new Entry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably can be removed as the signal is gone.
qutebrowser/browser/urlmarks.py
Outdated
@@ -53,43 +53,25 @@ class InvalidUrlError(Error): | |||
pass | |||
|
|||
|
|||
class DoesNotExistError(Error): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why get rid of this? Abusing a KeyError
(which I view more of a Python built-in exception and can also happen by accident) for this seems wrong to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To expand on this: I think it's fine to use KeyError
on a lower level, i.e. with the SqlTable
object with a dict-like interface. However, on this higher level, I think it makes sense to re-raise them as more specific exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -48,21 +47,12 @@ def __init__(self, source, parent=None): | |||
self.srcmodel = source | |||
self.pattern = '' | |||
self.pattern_re = None | |||
|
|||
dumb_sort = self.srcmodel.DUMB_SORT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing all the dumb_sort
stuff can go because it was only used for the URL model, which doesn't use QSortFilterProxyModel
anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
else: | ||
self.setSortRole(completion.Role.sort) | ||
self._sort_order = dumb_sort | ||
self.lessThan = self.intelligentLessThan |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can just rename the method now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, right
from qutebrowser.utils import usertypes, log | ||
|
||
|
||
class SqlCompletionModel(QAbstractItemModel): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original reason we wanted to have sqlite models was performance, as Qt probably implements lazy loading and all that there very efficiently. Don't we lose all that by having this be a QAbstractItemModel
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no 'natural' way to make a SqlTableModel
/SqlQueryModel
look like a tree. This is a wrapper around several SqlTableModels
, each of which populates a single category. I hope I'm not losing out on lazy loading here! At the very least I'm still relying on SqlTableModel
for filtering!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense - I originally thought about ditching the tree model entirely and using a QVBoxLayout
of QTableView
s, but that'd probably make things like sizing a lot more difficult.
Makes me wonder whether it somehow helps to implement canFetchMore and fetchMore - though we don't really have a slow "data generation" step, so I guess it won't help.
I think a few benchmark tests (after #2281 is in) would probably help there too, as we can test what's helping and what's not much easier.
fields = (t.record().fieldName(i) for i in self.columns_to_filter) | ||
query = ' or '.join("{} like '%{}%' escape '\\'" | ||
.format(field, pattern) | ||
for field in fields) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if there's a '
in the pattern? Can't we use QSqlQuery
here too?
qutebrowser/misc/sql.py
Outdated
return query | ||
|
||
|
||
class SqlTable(QObject): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the pythonic interface over SQL stuff!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! That actually answers your question about KeyError
though. I saw this as a dict-like interface over a sql table. When you try to access or delete a key that doesn't exist, you get a KeyError
just as you would if it were a python dict. I didn't see a reason to invent a new exception just for that (I do have SqlException
, but that's more for things like 'your SQL connection died')
# You should have received a copy of the GNU General Public License | ||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
"""Tests for the base sql completion model.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those should probably use qtmodeltester
where appropriate, and maybe pytest-benchmark
for some interesting operations (which I'm introducing in #2281).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I must've lost that while cherry-picking over from my previous branch. Working on this model actually made me really appreciate qmodeltester
, it caught like 10 dumb things I was doing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like it's still not back?
completion view. | ||
DUMB_SORT: the dumb sorting used by the model | ||
dumb_sort: the dumb sorting used by the model |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can go too
To answer your main question more fully, no, we can't use a Furthermore, we may actually want a model that combines both SQL and item-based sources. Otherwise, to support #2191, we'd need to create a sql table to include searchengines. I'm imagining something like:
|
8001680
to
bd3635d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick look at Travis shows that you have a circular import Python 3.4 doesn't like, some actual failures, and of course some linting errors, but I'm guessing that's all WIP. The Ubuntu Xenial one fails to init the sqlite backend, I'm guessing some additional package is needed there?
edit: Looks like it's this one. We should also check if QtSql and sqlite is available in earlyinit.py
to show an error if it isn't.
qutebrowser/browser/urlmarks.py
Outdated
@@ -53,43 +53,25 @@ class InvalidUrlError(Error): | |||
pass | |||
|
|||
|
|||
class DoesNotExistError(Error): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To expand on this: I think it's fine to use KeyError
on a lower level, i.e. with the SqlTable
object with a dict-like interface. However, on this higher level, I think it makes sense to re-raise them as more specific exceptions.
from qutebrowser.utils import usertypes, log | ||
|
||
|
||
class SqlCompletionModel(QAbstractItemModel): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense - I originally thought about ditching the tree model entirely and using a QVBoxLayout
of QTableView
s, but that'd probably make things like sizing a lot more difficult.
Makes me wonder whether it somehow helps to implement canFetchMore and fetchMore - though we don't really have a slow "data generation" step, so I guess it won't help.
I think a few benchmark tests (after #2281 is in) would probably help there too, as we can test what's helping and what's not much easier.
I also just noticed this from the Qt docs:
Not sure if that applies to us, and if/how we need to handle it. |
Hmm, if I'm understanding that correctly, it could be a problem if you try to enter a history item while you have completion open and it is select()ing. Maybe if you hit |
8a361b7
to
386e227
Compare
Hmm ... I can't figure out what's up with this failure. For some reason it only has a problem initializing SQL for those tests. Other tests, like the whole completion test suite, run fine, so Sqlite is clearly installed ... Also, for Xenial, do I need to install |
Bleh, never mind. Its one of those ones that only reproes when you run the full test suite (or at least everything under tests/unit/browser). Maybe calling |
I can reproduce it with |
Ok, looks like all that's left is one failure on master and that tricky circular import. I thought I solved that early on but for some reason py3.4 still isn't happy with it. |
Once that's fixed up, I think this is feature-complete and ready for review. There are a few pieces of work that I think are non-critical and could come in a later review:
|
oh, that's unfortunate.
None are great options. Well, ok, I like 3, but I'm guessing a fair number of |
Sorry, must've missed that one earlier! Travis runs various test environments - some of them in their native (Ubuntu Trusty) environment, others (with For the native environments, what is installed in
Hmm, I guess it'd make sense to close #2191 and reimplement that with the new completion API at some point then? Kind of a bad situation for @rsteube though...
I really like that idea. We badly need some benchmarks for the completion (mainly filtering with many items), and it'd make sense to have them in before this PR, as that performance really shouldn't get worse. Do you want to work on this?
Dang. I noticed the first one, but I didn't get how it'd cause SQL errors and fixing it didn't seem to help, so I dropped it again.
Looks like you found a good solution to this, but it makes me wonder if we're going to run into similar issues with this structure in the feature... I vaguely remember something similar was the reason for the
I've actually been thinking about that, mostly because of #1456 and because most people on an old Debian/Ubuntu would probably be happy with #1912. Not so sure yet... Some other observations: (everthing after this was added as an edit)
|
Yeah, that's unfortunate, but I can probably at least re-use the tests.
Yes.
Yeah, I listed 3 options and picked the fourth
Is it not an unused variable? I was about to remove it, along with |
I agree - with #640 we'd probably just have a
Oh, right. I assumed it was an argument for some reason, not an attribute. |
dadc21d
to
6bd573a
Compare
I think I fixed all the things, except earlyinit. Is there any advantage to |
I made all the conversions, but the times aren't coming out right. I don't think we actually have the problem we think we have. timestamps are timezone-independent. So maybe nothing needs to change in the implementation, but just in the test. You're right, timezones are painful. Review status: all files reviewed at latest revision, 9 unresolved discussions. qutebrowser/browser/history.py, line 146 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/browser/history.py, line 161 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/browser/history.py, line 211 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/browser/qutescheme.py, line 221 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/completion/models/histcategory.py, line 42 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/javascript/history.js, line 51 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/javascript/history.js, line 178 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. Nothing to do here now that item.time is utc qutebrowser/javascript/history.js, line 180 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. tests/unit/completion/test_histcategory.py, line 122 at r17 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Just pushed the UTC conversion. Hope that fixes it! Comments from Reviewable |
tests/unit/completion/test_histcategory.py, line 122 at r17 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
oops, shouldn't have submitted my review, just the single comment about timestamps. I actually I didn't push it (except to my WIP branch) Comments from Reviewable |
Review status: all files reviewed at latest revision, 3 unresolved discussions. qutebrowser/completion/models/histcategory.py, line 42 at r18 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
This one still applies, see #2830. qutebrowser/javascript/history.js, line 51 at r18 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
This one still applies (but no hard feelings either way) tests/unit/completion/test_histcategory.py, line 122 at r17 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
I resolved the discussions which are outdated now with #2830 and things being a bit clearer. I commented on the rest. This is all based on what's pushed right now - i.e. ignoring the WIP branch. Comments from Reviewable |
tests/unit/completion/test_histcategory.py, line 122 at r17 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
hmm, I can't actually reproduce your failure. I've tried playing around with a bunch of different Comments from Reviewable |
Review status: all files reviewed at latest revision, 3 unresolved discussions. tests/unit/completion/test_histcategory.py, line 122 at r17 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
That's because diff --git a/tox.ini b/tox.ini
index 04cf826d1..ce3da0edb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -13,7 +13,7 @@ skipsdist = true
setenv =
QT_QPA_PLATFORM_PLUGIN_PATH={envdir}/Lib/site-packages/PyQt5/plugins/platforms
PYTEST_QT_API=pyqt5
-passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_* DOCKER
+passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI TRAVIS XDG_* QUTE_* DOCKER TZ
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/misc/requirements/requirements-tests.txt Comments from Reviewable |
We need to tell sqlite to convert the timestamps to localtime during formatting, otherwise it formats them as though you are in UTC. Also fix up a few uses of mktime.
Reviewed 3 of 3 files at r19. Comments from Reviewable |
Review status: all files reviewed at latest revision, 2 unresolved discussions. qutebrowser/browser/qutescheme.py, line 254 at r19 (raw file):
Mea culpa, this acually breaks because this is a Comments from Reviewable |
A date object doesn't have a timestamp property. Go back to using mktime.
qutebrowser/browser/qutescheme.py, line 254 at r19 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Fixed, but I'm wondering why tests only failed on a few builds ... and why I couldn't get them to fail locally. I need to look into whether this is actually covered. Also I don't think the Comments from Reviewable |
I think this is ready apart from the Reviewed 1 of 1 files at r20. qutebrowser/browser/qutescheme.py, line 254 at r19 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
This only happens on some builds because if the check above which uses the javascript page with a new QtWebKit or QtWebEngine. So it's only tested on legacy QtWebKit. I don't think it's worth it to consolidate the two. I'll drop the HTML one with v1.0, which will drop legacy QtWebKit and allow to force JS for scripts/dev/check_coverage.py, line 160 at r20 (raw file):
Looks like you can add Comments from Reviewable |
pushed a fix for the coverage. I'll wait for one more test run to make sure nothing crazy happens, then one of us can merge it Review status: 54 of 55 files reviewed at latest revision, 3 unresolved discussions. qutebrowser/browser/qutescheme.py, line 254 at r19 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
oh, good. I'm all for dropping the HTML page qutebrowser/completion/models/histcategory.py, line 42 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. qutebrowser/javascript/history.js, line 51 at r18 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
This is obsolete now, correct? scripts/dev/check_coverage.py, line 160 at r20 (raw file): Previously, The-Compiler (Florian Bruhin) wrote…
Done. Comments from Reviewable |
Reviewed 1 of 1 files at r21. qutebrowser/javascript/history.js, line 51 at r18 (raw file): Previously, rcorre (Ryan Roden-Corrent) wrote…
Yep! They could still be UTC, but it really doesn't matter, and if someone reads the source of the page, localtime is probably less confusing. Comments from Reviewable |
I've never been this excited about clicking a button... let's do this, and thanks so much! |
Great! |
Wow, I felt this change hard after a git pull, it feels really awesome on my older x230. Honestly the old completion system is the only thing that dragged this computer down... now nothing can stop it! Thank you all for the hard work! |
This is a combination of my previous efforts for #74 and #1765.
It refactors completion models into functions that instantiate models on-demand. In order to avoid taking a performace hit, some models are backed by SQL tables.
This approach is a little different than last time in that the SQL does not
exist solely in the completion module. Instead, the
bookmark-manager
,quickmark-manager
, andweb-history
are now backed by an in-memory SQLdatabase rather than a python dict (see e130063 and f095dbe). On startup, they
read from a file on disk and write entries into the in-memory database. This is
the main part I'm looking for feedback on right now. How does it feel? The
advantage of this approach is that SQL-based completion models can be
returned on-demand just as the simple completion models are.
It would likely be even more performant and simpler to replace the old bookmark/quickmark/history text files with an on-disk database, but that could
be a usability hit for those that like to mess with these files manually.
P.S. don't expect this to work just yet, but it will be there soon.
This change is