-
-
Notifications
You must be signed in to change notification settings - Fork 297
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
support overriding fetched tags when adding a bookmark #775
Conversation
like_escape = lambda s, c='`': s.replace(c, c+c).replace('_', c+'_').replace('%', c+'%') | ||
|
||
def taglist_str(tag_str, convert=None): | ||
tags = taglist(tag_str.split(DELIM)) | ||
return delim_wrap(DELIM.join(tags if not convert else taglist(convert(tags)))) |
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.
Optionally applying a conversion to the processed tags before converting them back to a sting
del_error: Optional[Set[int] | range] = None) -> int: # Optional[IntSet] | ||
del_error: Optional[Set[int] | range] = None, | ||
tags_fetch: bool = True, | ||
tags_except: Optional[str] = None) -> int: # Optional[IntSet] |
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.
New parameters for add_rec()
The method has no *
enforcing keyword arguments, so I added them to the end of the list
@@ -735,8 +740,11 @@ class BukuDb: | |||
title_in : str, optional | |||
Title to add manually. Default is None. | |||
tags_in : str, optional | |||
Comma-separated tags to add manually. | |||
Must start and end with comma. Default is None. | |||
Comma-separated tags to add manually, instead of fetching them. Default is None. |
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.
Must start and end with comma.
This is not a condition that is actually observed, even in the tests for this method 😅
"""Format and get tag string from tokens. | ||
|
||
Parameters | ||
---------- | ||
keywords : list | ||
List of tags to parse. Default is empty list. | ||
edit_input : bool | ||
Whether the taglist is an edit input (i.e. may start with '+'/'-'). | ||
Defaults to False. |
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.
Added support of the +
/-
prefix to parse_tags()
for convenience
return DELIM | ||
|
||
tags = DELIM | ||
if edit_input and keywords[0] in ('+', '-'): | ||
return keywords[0] + parse_tags(keywords[1:]) |
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 several changes but this is the only one with an effect (specifically when edit_input=True
supplied)
This is based on how parse_tags()
is used in respective places within main()
@@ -5668,7 +5685,7 @@ def parse_range(tokens: Optional[str | Sequence[str] | Set[str]], # Optional[st | |||
|
|||
|
|||
# main starts here | |||
def main(): | |||
def main(argv=sys.argv[1:], *, program_name=os.path.basename(sys.argv[0])): |
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 does not affect current usage, but allows to invoke main()
without relying on sys.argv
(i.e. for testing)
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.
Note that slicing is necessary for that purpose (since ArgumentParser.parse_args()
ignores the 1st value in the list only if the list is sys.argv
)
|
||
# Show help and exit if help requested | ||
if args.help: | ||
argparser.print_help(sys.stdout) | ||
argparser.print_help() |
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.
sys.stdout
is the default value of the argument
@@ -5972,7 +5990,7 @@ POSITIONAL ARGUMENTS: | |||
browse.override_text_browser = False | |||
|
|||
# Fallback to prompt if no arguments | |||
if len(sys.argv) <= 2 and (len(sys.argv) == 1 or (len(sys.argv) == 2 and args.nostdin)): | |||
if argv in ([], ['--nostdin']): |
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 was the point of the 1st check if only 2 specific argument lists are allowed anyway? 😅)
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.
…Incidentally, I believe this could be moved higher (possibly before the piped_input()
call, thus also allowing for a simpler condition in there) 🤔
keywords_except, keywords = [], args.add[1:] | ||
# taglists are taken from --add (starting from 2nd value) and from --tags | ||
# if taglist starts with '-', its contents are excluded from resulting tags | ||
# if BOTH taglists is are either empty or start with '+'/'-', fetched tags are included |
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.
Ended up changing the logic here a little; I had to think hard what would make sense to do here, so I decided to elaborate in a comment and made sure to test it thoroughly as well.
|
||
tags, tags_except = parse_tags(keywords), parse_tags(keywords_except) | ||
tags, tags_except = ((s if s and s != DELIM else None) for s in [tags, tags_except]) |
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.
Replacing blank tag lists with None
is optional, but I did it anyway for sake of consistency
bdb.add_rec(url, title_in, tags, desc_in, _immutable(args), delay_commit=False, fetch=not args.offline, | ||
url_redirect=args.url_redirect, tag_redirect=tag_redirect, tag_error=tag_error, del_error=del_error) | ||
network_test = args.url_redirect or tag_redirect or tag_error or del_error | ||
fetch = not args.offline and (network_test or tags_add or title_in is None) |
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.
fetch
is calculated in main()
("tag1, tag2", ",t a g 1,t a g 2,"), | ||
([""], DELIM), | ||
([","], DELIM), | ||
(["tag1, tag2"], ",tag1,tag2,"), |
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 docs specify it must be a list… and what sort of logic is "put spaces after every character in tag names" anyway? 😅
@@ -114,14 +114,17 @@ def test_get_PoolManager(m_myproxy): | |||
), | |||
], | |||
) | |||
def test_parse_tags(keywords, exp_res): | |||
@pytest.mark.parametrize('prefix', [None, '', '+', '-']) |
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.
Testing for edit_input
(None
means False
, others mean True
with the value itself defining what prefix is expected before the list)
@@ -793,7 +793,7 @@ def test_close_quit(self): | |||
# self.fail() | |||
|
|||
|
|||
@pytest.mark.parametrize('status', [None, 200, 302, 307, 404, 500]) | |||
@pytest.mark.parametrize('status', [None, 200, 302, 308, 404, 500]) |
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 figured that at least one permanent redirect HTTP code is necessary to test how add_rec()
handles it 😅
'except,bar,tags,there,foo', ',baz,fetched,foo bar,have been,http:redirect,qux,some,'), | ||
(308, 'there,have been,some,tags,fetched', 'foo,foo bar,qux,bar,baz', | ||
'except,http:redirect,bar,tags,there,foo', ',baz,fetched,foo bar,have been,qux,some,'), | ||
]) |
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.
Verifying various combinations of supplied tag arguments, along with fetch result (None
means fetch=False
)
Tag override doesn't apply to network test tags (these are opt-in anyway), but they can be filtered out via tags_except
(e.g. tags_except='http:500'
would allow to exclude HTTP 500 error tags from generated output, since these tend to be transient)
def test_version(BukuDb, piped_input, capsys): | ||
with pytest.raises(SystemExit): | ||
buku.main(['--version']) | ||
assert capsys.readouterr().out.splitlines() == [buku.__version__] |
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.
CLI tests
@pytest.fixture | ||
def piped_input(): | ||
with mock.patch('buku.piped_input') as fn: | ||
yield fn |
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.
Technically I could use mock.patch()
as a decorator, but these are harder to name
|
||
@pytest.fixture | ||
def bdb(BukuDb): | ||
yield BukuDb.return_value |
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 BukuDb
fixture can be used when testing invocation of constructor/static methods; otherwise bdb
is more convenient
'tags_in': ',baz,qux,', 'tags_except': ',bar,baz,foo,'}, | ||
{'add_tags': ['+', 'foo,baz,', 'bar,'], 'tag': ['-', 'baz,', 'qux,'], | ||
'tags_in': ',bar,baz,foo,', 'tags_except': ',baz,qux,'}, | ||
]) |
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.
That's 84 test cases… which is still way better than ~3000 that I got when I tried out all combinations (…and I wasn't even done writing them 😆)
if del_error: | ||
argv += ['--del-error'] + del_range | ||
with pytest.raises(SystemExit): | ||
buku.main(argv) |
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.
Yanno, maybe we should close the DB without calling sys.exit()
, at least from within main()
– that would make it a better fit for external use 😅
buffer.seek(0, os.SEEK_END) | ||
return buffer.write(text) | ||
finally: | ||
buffer.seek(pos) |
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 isn't really used as of yet, but it could come in handy when testing the stdin
fixture (…which could've been placed here as well, but pylint
doesn't like imported fixtures)
tags_fetch=tags_fetch, tags_except=tags_except, url_redirect=url_redirect, | ||
tag_redirect=tag_redirect, tag_error=tag_error, del_error=del_error) | ||
bdb.searchdb.assert_not_called() | ||
prompt.assert_not_called() |
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.
These are mostly due to initial issues I was having with main()
(…which were basically fixed by making sys.argv[1:]
the default value; but I left this in just to be sure)
Thank you! |
Addressing the case discussed in #759
--add
command, tag lists supplied via--tag
or--add
(2nd value and further) can be prepended with+
or-
, same as when editing tags+
means tags in the list should be included in the resulting record tags-
means tags in the list should be excluded from the resulting record tags (this is applied last),
or a blank string), but doesn't start with+
/-
, it has same effect as+
except it overrides fetched tags-
, they're simply combined)Also added a few CLI tests (mainly related to the changes)