Skip to content
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

Add default browser support #123

Merged
merged 10 commits into from Jan 26, 2021

Conversation

ObaraEmmanuel
Copy link
Contributor

@ObaraEmmanuel ObaraEmmanuel commented Jan 10, 2021

Description

This PR adds support for fetching history and bookmarks from default browsers.

Default browser support Tracking

Linux Mac Windows
Chrome ✔️ 🔲 ✔️
firefox ✔️ 🔲 ✔️
Chromium ✔️ 🔲 ✔️
Edge ✔️ 🔲 ✔️
Brave 🔲 🔲 ✔️
Opera ✔️ 🔲 ✔️
OperaGX 🔲 🔲 ✔️
Safari 🔲
Vivaldi ✔️ 🔲 🔲

Fixes #96

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist:

  • I have read the contribution guidelines and followed it as far as possible.
  • I have performed a self-review of my own code (if applicable)
  • I have commented my code, particularly in hard-to-understand areas (if applicable)
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have enabled the pre-commit hook and it's not detecting any issue.
  • Any dependent and pending changes have been merged and published

@codecov-io
Copy link

codecov-io commented Jan 10, 2021

Codecov Report

Merging #123 (83154d5) into master (7927531) will decrease coverage by 1.05%.
The diff coverage is 73.68%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #123      +/-   ##
==========================================
- Coverage   88.07%   87.02%   -1.06%     
==========================================
  Files           3        3              
  Lines         302      316      +14     
  Branches       51       53       +2     
==========================================
+ Hits          266      275       +9     
- Misses         25       29       +4     
- Partials       11       12       +1     
Flag Coverage Δ
unittests 87.02% <73.68%> (-1.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
browser_history/cli.py 85.45% <50.00%> (-8.55%) ⬇️
browser_history/browsers.py 100.00% <100.00%> (ø)
browser_history/generic.py 82.63% <100.00%> (+0.09%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7927531...83154d5. Read the comment docs.

@ObaraEmmanuel
Copy link
Contributor Author

@Samyak2 I will not be able to add support for mac OS since I have no access to the platform 😅

Copy link
Member

@Samyak2 Samyak2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I appreciate you taking the time to find aliases 😄

I have a few comments (in the review below).

Other than that, I had another concern. From what I can see, the default_browser() function returns only the browser name as a string. From the point of view of an user of the API, the name isn't of much use since it cannot be used to get history of that browser. Instead, it would be better to have it return the browser class directly.

@Samyak2 I will not be able to add support for mac OS since I have no access to the platform 😅

That's okay, we'll have it only for linux for now.

browser_history/__init__.py Outdated Show resolved Hide resolved
browser_history/__init__.py Outdated Show resolved Hide resolved
browser_history/__init__.py Outdated Show resolved Hide resolved
@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

Also, will it be possible to add some basic tests for this?

@ObaraEmmanuel
Copy link
Contributor Author

Also, will it be possible to add some basic tests for this?

It will be tough but I guess we can work it out

@ObaraEmmanuel ObaraEmmanuel changed the title Add default browser support on linux Add default browser support Jan 11, 2021
@ObaraEmmanuel
Copy link
Contributor Author

I don't know why the tests are failing in ubuntu and I have wrapped the winreg import in a platform check

@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

It will be tough but I guess we can work it out

I was thinking of using pytest monkeypatching to set the return values of webbrowser.get() to a known value and test that it returns the correct browser. This is just to ensure that the implementation does not break.

@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

I don't know why the tests are failing in ubuntu and I have wrapped the winreg import in a platform check

I'll see if it works on my linux system

@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

The issue was due to pylint, adding the following to .pylintrc fixes it:

ignored-modules=winreg

@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

Or we can add a # pylint: disable=import-error to that import. I'm not sure which solution is better (the previous solution seems like it ignores the entire module, while this only disables the import error). What do you think?

@Samyak2
Copy link
Member

Samyak2 commented Jan 11, 2021

Also, I have another question. In the issue you said that it was a jungle inside the windows registry 😆

How did you get all the browsers working for windows without mapping out those values?

(sorry for all the spam pings xD)

@ObaraEmmanuel
Copy link
Contributor Author

Or we can add a # pylint: disable=import-error to that import. I'm not sure which solution is better (the previous solution seems like it ignores the entire module, while this only disables the import error). What do you think?

If we disable import errors we might not be able to capture genuine issues in future, we can just ignore winreg for now

Also, I have another question. In the issue you said that it was a jungle inside the windows registry 😆

How did you get all the browsers working for windows without mapping out those values?

(sorry for all the spam pings xD)

With the exception of firefox ( which I have handled in a special case ) for the rest we just needed to map the weird alias with no extra processing, since we are not supporting legacy edge and internet explorer (which have inherently sinister names) the rest is easy. Legacy edge doesn't even have a name just random hex values 💀

@ObaraEmmanuel
Copy link
Contributor Author

@Samyak2 If you have any of these browsers installed could you tell me what names they have when using webbrowser.get() or even when fetching from the registry

@Samyak2
Copy link
Member

Samyak2 commented Jan 12, 2021

If we disable import errors we might not be able to capture genuine issues in future, we can just ignore winreg for now

That line can be added only for the winreg import, that way pylint would ignore only that import.

@Samyak2 If you have any of these browsers installed could you tell me what names they have when using webbrowser.get() or even when fetching from the registry

I don't have access to a windows system atm, I will try those out tomorrow (IST).

@Samyak2
Copy link
Member

Samyak2 commented Jan 12, 2021

On linux:

  • Firefox: firefox
  • Chromium: chromium-browser

I don't have other browsers currently, I will try them out tomorrow or day after (IST) if that's ok with you.

@Samyak2
Copy link
Member

Samyak2 commented Jan 12, 2021

I tried it on Mac OS and looks like the object returned by webbrowser.get() is completely different (it's an object of MacOSXOSAScript) which does not have .name attribute - it has only args, open(), open_new(), open_new_tab().

We'll have to skip Mac OS support for now :(

@ObaraEmmanuel
Copy link
Contributor Author

I tried it on Mac OS and looks like the object returned by webbrowser.get() is completely different (it's an object of MacOSXOSAScript) which does not have .name attribute - it has only args, open(), open_new(), open_new_tab().

We'll have to skip Mac OS support for now :(

I guess we can create a new issue for that

@ObaraEmmanuel
Copy link
Contributor Author

ObaraEmmanuel commented Jan 12, 2021

That line can be added only for the winreg import, that way pylint would ignore only that import.

I also don't know how I can fix the failing mypy tests. Is there a way to tell it to ignore the winreg module?

@Samyak2
Copy link
Member

Samyak2 commented Jan 13, 2021

I also don't know how I can fix the failing mypy tests. Is there a way to tell it to ignore the winreg module?

mypy didn't report any errors when I ran it locally on my system.

According to the docs here, we can add # type: ignore to the import.

To suppress a single missing import error, add a # type: ignore at the end of the line containing the import.

@ObaraEmmanuel ObaraEmmanuel force-pushed the feature-default branch 2 times, most recently from f228484 to a7df77a Compare January 15, 2021 21:06
@ObaraEmmanuel
Copy link
Contributor Author

@Samyak2 what's the way forward concerning the testing and monkeypatching involved? Any ideas?

@ObaraEmmanuel ObaraEmmanuel marked this pull request as ready for review January 15, 2021 21:09
Copy link
Member

@Samyak2 Samyak2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just one thing.

I will try writing some tests by EOD, I'm still not sure of the details.

Comment on lines 139 to 140
if browser in default:
return browser_aliases[browser]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might cause issues if the browser_aliases are expanded in the future by someone else. For example, adding opera as a key would match operagx also. Maybe add a warning near browser-aliases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I was actually thinking of letting the browser implementations provide their aliases through an aliases attribute which will be a list of all aliases for the specific browser. This will simplify adding new browsers. Maintaining this aliases list is cumbersome. Also, we would need to perform a direct match before looking for deeper searches to try mitigate this issue. For a default value like opera, we would try finding a direct match with opera before we match deeper cases like operagx

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good, having aliases in the browser classes would be better for maintainability.

Direct matching is also a good idea, but might fail in some cases. For example, if there are operagx and opera-browser keys, and the browser name is opera it might match operagx. But I understand that this might require some advanced techniques (maybe some NLP?), so checking for a direct match is good enough for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I solved the issue using two separate passes (loops). First loop checks for direct matches first therefore, operagx will be matched with operagx first no matter what.
The next loop now checks for rough matches. Even though operagx here would match opera (the check will assume gx is just "noise"), it will have already be matched correctly in the first loop so it won't even get to this point.
NLP would be overkill but good; still, I think python's difflib could do the same job of checking for similarity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks good. It's probably better to leave enhancements such as using difflib for a future PR. The current implementation seems quite good! Just needs more testing from the community.

I'll try to finish the tests soon.

@Samyak2
Copy link
Member

Samyak2 commented Jan 18, 2021

I see that you have moved the utilities to utils, it looks good! The namespace is much cleaner now.

I have added some basic tests for default browser on Linux and MacOS. Tests for windows will be more difficult as we will have to monkeypatch OpenKey and OpenValueEx. The CI is failing on MacOS for some reason, need to fix that.

@Samyak2
Copy link
Member

Samyak2 commented Jan 22, 2021

I tested this on Windows and it works reliably for Chrome, Firefox and new Edge. It failed for internet explorer and the old edge, but that was expected.

I think this is complete now, except for a few browsers on Linux.

@Samyak2
Copy link
Member

Samyak2 commented Jan 22, 2021

The code coverage seems to have decreased because Codecov doesn't recognize browser_history/utils.py for some reason.

Also, we should add some default browser tests in CLI too:
image

@ObaraEmmanuel
Copy link
Contributor Author

The code coverage seems to have decreased because Codecov doesn't recognize browser_history/utils.py for some reason.

Also, we should add some default browser tests in CLI too:

I will get on this today. I have a slight break from exams.

@Samyak2
Copy link
Member

Samyak2 commented Jan 22, 2021

The code coverage seems to have decreased because Codecov doesn't recognize browser_history/utils.py for some reason.

It seems like a bug in codecov, where it ignores files if another file of the same name exists in another directory (ref). Since we have __init__.py and utils.py in both browser-history and tests, they are ignored. There must be a workaround for this, but I couldn't figure it out.

@Samyak2
Copy link
Member

Samyak2 commented Jan 22, 2021

There is another issue. I tried Microsoft Edge on Linux and it looks like webbrowser does not support it yet. webbrowser.get().name simply returns xdg-open. This also happens for Chromium Freeworld.

From the source code of webbrowser, I could see that it uses xdg-settings get default-web-browser command to figure out the default browser. On my system with Edge as default, the command prints microsoft-edge-dev.desktop.

It might be better to use the output of this command directly instead of relying on webbrowser. I will try working on this today.


Sorry for dragging the PR for so long 😅, I want to ensure that everything works before merging.

@ObaraEmmanuel
Copy link
Contributor Author

I have made everything tidy (Even edge dev on linux is being detected correctly) but I haven't tested it on windows yet. Nothing should be affected by the refactor. I also noticed an unrelated issue where fetching from a browser not supported on the current platform throws an AssertionError which is completely unhandled on the CLI code. Trying to fetch from a default browser that is not supported also throws this error.

File "/home/obara/projects/python/browser-history/browser_history/cli.py", 
 line 150, in main cli(sys.argv[1:])
File "/home/obara/projects/python/browser-history/browser_history/cli.py", 
 line 132, in cli outputs = browser_class().fetch_history()
File "/home/obara/projects/python/browser-history/browser_history/generic.py", 
 line 139, in __init__ assert self.linux_path is not None, error_string.format("Linux")
AssertionError: Edge browser is not supported on Linux

AFAIK this should just be a warning not a full fledged traceback. Maybe create an issue for this since we can't fix it in this feature branch.

@Samyak2
Copy link
Member

Samyak2 commented Jan 23, 2021

I have made everything tidy (Even edge dev on linux is being detected correctly) but I haven't tested it on windows yet. Nothing should be affected by the refactor.

That's great!

I also noticed an unrelated issue where fetching from a browser not supported on the current platform throws an AssertionError which is completely unhandled on the CLI code. Trying to fetch from a default browser that is not supported also throws this error.

File "/home/obara/projects/python/browser-history/browser_history/cli.py", 
 line 150, in main cli(sys.argv[1:])
File "/home/obara/projects/python/browser-history/browser_history/cli.py", 
 line 132, in cli outputs = browser_class().fetch_history()
File "/home/obara/projects/python/browser-history/browser_history/generic.py", 
 line 139, in __init__ assert self.linux_path is not None, error_string.format("Linux")
AssertionError: Edge browser is not supported on Linux

AFAIK this should just be a warning not a full fledged traceback. Maybe create an issue for this since we can't fix it in this feature branch.

I noticed this too, I'll open an issue for it.

Copy link
Member

@Samyak2 Samyak2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great overall!

Just a few small things. I will try fixing them today.

Comment on lines +23 to +62
@pytest.fixture
def change_linux_default(monkeypatch, request):
"""Changes utils._default_browser_linux to return a specific named
browser. use @pytest.mark.browser_name(name) to set
browser name
"""

marker = request.node.get_closest_marker("browser_name")

if marker is None:
browser_name = None
else:
browser_name = marker.args[0]

def mock_get():
return browser_name

monkeypatch.setattr(utils, "_default_browser_linux", mock_get)


@pytest.fixture
def change_win_default(monkeypatch, request):
"""Changes utils._default_browser_win to return a specific named
browser. use @pytest.mark.browser_name(name) to set
browser name
"""
if platform != utils.Platform.WINDOWS:
pytest.skip("Skipping windows registry based test")

marker = request.node.get_closest_marker("browser_name")

if marker is None:
browser_name = None
else:
browser_name = marker.args[0]

def mock_get():
return browser_name

monkeypatch.setattr(utils, "_default_browser_win", mock_get)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with testing it like this is that _default_browser_linux and _default_browser_win are left untested. Since the code in them is very platform-dependent, we should test them thoroughly (ex: if the process returns an empty output, etc.). So we could have separate tests for these.

I'll try to write these tests today.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we need to test this deeply, without monkeypatching these complex steps like reading the registry and piping output from the linux command line. I don't know if it can be done.

tests/test_default_browser.py Show resolved Hide resolved
browser_history/utils.py Show resolved Hide resolved
@ObaraEmmanuel
Copy link
Contributor Author

Looks great overall!

Just a few small things. I will try fixing them today.

And the cli tests as well

@ObaraEmmanuel
Copy link
Contributor Author

@Samyak2 I noticed Vivaldi support has been added ... we need to support it in defaults as well.

@Samyak2
Copy link
Member

Samyak2 commented Jan 25, 2021

@Samyak2 I noticed Vivaldi support has been added ... we need to support it in defaults as well.

Right, we need to do that.

@OBITORASU would it be possible for you to show us the outputs of _default_browser_win() and/or _default_browser_linux() functions when Vivaldi is set as the default browser (these functions are available only on this PR's branch)?

@OBITORASU
Copy link
Contributor

Yes I can show the output of _default_browser_linux().

Branch: feature-default
OS: Arch Linux
Default Browser: Vivaldi (as asked)

The output for _default_browser_linux() is:

# vivaldi-stable

Image attached:
image

@Samyak2
Copy link
Member

Samyak2 commented Jan 26, 2021

Yes I can show the output of _default_browser_linux().

Branch: feature-default
OS: Arch Linux
Default Browser: Vivaldi (as asked)

The output for _default_browser_linux() is:

# vivaldi-stable

Image attached:
image

Thank you 👍


@ObaraEmmanuel I think it's all complete now, except for the tests for _default_browser_win() and _default_browser_linux(). We can open a new issue for those. What do you think?

@ObaraEmmanuel
Copy link
Contributor Author

@Samyak2 Sure thing, we've done the most we could.

Highlighted default browser as a feature in the README since the
feature is a bit discreet.

Fixed a spacing issues in `aliases :`, moved docstring of `aliases`
to the correct position (bottom of it) and fixed some references
to `browser_history.generic.Browser`.
Copy link
Member

@Samyak2 Samyak2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Great work! Thank you for contributing again!

@Samyak2 Samyak2 merged commit b28ccf3 into browser-history:master Jan 26, 2021

# first quick pass for direct matches
for browser in all_browsers:
if default == browser.name.lower() or default in browser.aliases:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realised that since we're using the membership operator here (in), this will be slightly more efficient if the aliases are a set instead of a tuple. (ref).

The aliases don't have many elements, so it shouldn't make a big difference. @ObaraEmmanuel what do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE REQ]fetch history and bookmarks from default browser
4 participants