Viewing gemini:// links from linkulator #102

Open
samhunter wants to merge 5 commits from samhunter/linkulator2:master into master
First-time contributor

Tested on my RCT account.

Tested on my RCT account.
samhunter added 3 commits 2021-08-11 15:32:06 +00:00
samhunter changed title from WIP: browsing for gemini:// links to Browsing for gemini:// links 2021-08-11 15:32:38 +00:00
samhunter changed title from Browsing for gemini:// links to Viewing gemini:// links from linkulator 2021-08-11 18:18:36 +00:00
asdf self-assigned this 2021-08-12 09:35:48 +00:00
Collaborator

hi @samhunter thanks for coming up with this and submitting it

this actually covers part of #69

i'll review this when i get some solid free time, should be in the next day or so

hi @samhunter thanks for coming up with this and submitting it this actually covers part of #69 i'll review this when i get some solid free time, should be in the next day or so
Author
First-time contributor

this actually covers part of #69

I was actually thinking about moving the if/elif/else into a hash/assoc. array based construct with flexible (if executable handling a protocol is found - add the browser to the hash) number of handlers.

Proof of concept

from shutil import which
from urllib.parse import urlparse
import subprocess

browser = { 
        "http": "lynx", 
        "https": "lynx", 
        "gopher": "lynx", 
        "gemini": "amfora",
        "ssh": "putty"
}

url="https://rawtext.club"
url="gemini://rawtext.club"
url="gopher://rawtext.club"

#for proto in browser: 
url_scheme = urlparse(url).scheme
if url_scheme in browser:
    if which(browser[url_scheme]):
        subprocess.call([browser[url_scheme], url])
    else:
        print("No handler for "+ url_scheme + ".")

The "next little step"(tm) perhaps...

> this actually covers part of #69 I was actually thinking about moving the if/elif/else into a hash/assoc. array based construct with flexible (if executable handling a protocol is found - add the browser to the hash) number of handlers. ### Proof of concept ``` from shutil import which from urllib.parse import urlparse import subprocess browser = { "http": "lynx", "https": "lynx", "gopher": "lynx", "gemini": "amfora", "ssh": "putty" } url="https://rawtext.club" url="gemini://rawtext.club" url="gopher://rawtext.club" #for proto in browser: url_scheme = urlparse(url).scheme if url_scheme in browser: if which(browser[url_scheme]): subprocess.call([browser[url_scheme], url]) else: print("No handler for "+ url_scheme + ".") ``` The "next little step"(tm) perhaps...
samhunter added 1 commit 2021-08-12 12:58:45 +00:00
samhunter added 1 commit 2021-08-12 13:04:05 +00:00
Collaborator

I'm not entirely sure what that would mean, like a subclass of dict with methods to add browser entries if they are found on the system?

Overall I actually want to make this a bit simpler, and maybe not have any defaults specified. The checks using which could probably be removed and handled if subprocess.call (or subprocess.run) fails?

All of this is step 5 or 6 though, I'll start with step 1 for now. Thanks again.

I'm not entirely sure what that would mean, like a subclass of dict with methods to add browser entries if they are found on the system? Overall I actually want to make this a bit simpler, and maybe not have any defaults specified. The checks using `which` could probably be removed and handled if subprocess.call (or subprocess.run) fails? All of this is step 5 or 6 though, I'll start with step 1 for now. Thanks again.
Author
First-time contributor

I'm not entirely sure what that would mean, like a subclass of dict with methods to add browser entries if they are found on the system?

Browser is selected on 'b' from the dictionary, if there's no entry for the URL schema and/or the named executable wasn't found - the program offers opening it with the standard browser.

Overall I actually want to make this a bit simpler, and maybe not have any defaults specified.

Where will you find the programs matching the protocols? Under X it's easy - everything can be opened with xdg-open {url}...

The checks using which could probably be removed and handled if subprocess.call (or subprocess.run) fails?

I don't like the idea of exception as a regular control mechanism, but well it can be done. You're the boss.

> I'm not entirely sure what that would mean, like a subclass of dict with methods to add browser entries if they are found on the system? Browser is selected on 'b' from the dictionary, if there's no entry for the URL schema and/or the named executable wasn't found - the program offers opening it with the standard browser. > Overall I actually want to make this a bit simpler, and maybe not have any defaults specified. Where will you find the programs matching the protocols? Under X it's easy - everything can be opened with xdg-open {url}... > The checks using `which` could probably be removed and handled if subprocess.call (or subprocess.run) fails? I don't like the idea of exception as a regular control mechanism, but well it can be done. You're the boss.
Collaborator

Hi @samhunter,

Looks like this throws a key error when attempting to open an existing configuration file:

Traceback (most recent call last):
  File "/linkulator2/./linkulator.py", line 526, in <module>
    main()
  File "/linkulator2/./linkulator.py", line 494, in main
    config.init()
  File "/linkulator2/config.py", line 111, in init
    USER.load()
  File "/linkulator2/config.py", line 58, in load
    self.handler = config["URL Handler"]
  File "/usr/lib/python3.9/configparser.py", line 960, in __getitem__
    raise KeyError(key)
KeyError: 'URL Handler'

Here's an example file:

[User Status]
lastlogin = 1628674059.068926

[User Settings]
browser = lynx

This looks a bit more complicated than it might seem. We should probably use the config parser's methods and exceptions better, like get or items so that we can handle this using NoSectionHeader instead. This might look like:

      def load(self):
          """Loads from config file"""
          config = configparser.ConfigParser()
          ret = config.read(self.settingsfile)
          
          self.lastlogin = config.get("User Status", "lastlogin")
          self.browser = config.get("User Settings", "browser")
          self.handler = config.items("URL Handler") # ???, or maybe
          
          if config.has_section("URL Handler") and config.has_option("URL Handler", "https"):
              self.handler["https"] = config.get("URL Handler", "https")
          
...


    try:
        USER.load()
    except configparser.MissingSectionHeaderError, configparser.NoSection:
        print(
            "Warning: config file has invalid syntax and will be overwritten with default values"
        )

With that said, however it might be handled, the user experience from the change should be a good one. Having both browser AND handler in the config file might be confusing, so it might be better to just move to the single setting in one go. Also I'd prefer no defaults set, but importing the existing setting for https/gopher would be ok if it wasn't too complicated.

I guess it's a bit more involved than your original suggestion, sorry about that. If you have some different ideas let me know. We could push this to a feature branch instead of master and work on it there?

Hi @samhunter, Looks like this throws a key error when attempting to open an existing configuration file: ```none Traceback (most recent call last): File "/linkulator2/./linkulator.py", line 526, in <module> main() File "/linkulator2/./linkulator.py", line 494, in main config.init() File "/linkulator2/config.py", line 111, in init USER.load() File "/linkulator2/config.py", line 58, in load self.handler = config["URL Handler"] File "/usr/lib/python3.9/configparser.py", line 960, in __getitem__ raise KeyError(key) KeyError: 'URL Handler' ``` Here's an example file: ```ini [User Status] lastlogin = 1628674059.068926 [User Settings] browser = lynx ``` This looks a bit more complicated than it might seem. We should probably use the config parser's methods and exceptions better, like [get](https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.get) or [items](https://docs.python.org/3/library/configparser.html#configparser.ConfigParser.items) so that we can handle this using [NoSectionHeader](https://docs.python.org/3/library/configparser.html#configparser.NoSectionError) instead. This might look like: ```python def load(self): """Loads from config file""" config = configparser.ConfigParser() ret = config.read(self.settingsfile) self.lastlogin = config.get("User Status", "lastlogin") self.browser = config.get("User Settings", "browser") self.handler = config.items("URL Handler") # ???, or maybe if config.has_section("URL Handler") and config.has_option("URL Handler", "https"): self.handler["https"] = config.get("URL Handler", "https") ... try: USER.load() except configparser.MissingSectionHeaderError, configparser.NoSection: print( "Warning: config file has invalid syntax and will be overwritten with default values" ) ``` With that said, however it might be handled, the user experience from the change should be a good one. Having both browser AND handler in the config file might be confusing, so it might be better to just move to the single setting in one go. Also I'd prefer no defaults set, but importing the existing setting for https/gopher would be ok if it wasn't too complicated. I guess it's a bit more involved than your original suggestion, sorry about that. If you have some different ideas let me know. We could push this to a feature branch instead of master and work on it there?
Author
First-time contributor

Hi @asdf,

Looks like this throws a key error when attempting to open an existing configuration file

In my un-pushed local copy I handle it in a rather simplistic way:

try:
	self.lastlogin = config["User Status"]["lastlogin"]
	self.browser = config["User Settings"]["browser"]
	self.handler = config["URL Handler"]
except:
	print("Section missing, using defaults.")

On closing the file is overwritten with:

lastlogin = 1628938297.0196295

[User Settings]
browser = lynx

[URL Handler]
http = lynx
https = lynx
gopher = lynx
gemini = amfora
ssh = putty

Having editable defaults makes the system more flexible and adjustable to the specific user needs. If we only could 'plumb' everything ;-)

With that said, however it might be handled, the user experience from the change should be a good one.

Oh, I enjoy it. Recently added a (crude, it's merely a PoC thing) telnet:// handler too:

#! /bin/bash
# runtel - telnet:// URL handler
# Author: samhunter@rawtext.club
# 
PROGNAME="${0##*/}"
URL="${1?Syntax: $PROGNAME <url>}"
URL="${URL##telnet://}"
THOST="${URL%%:*}"
TPORT="${URL##*:}"
telnet "$THOST" "$TPORT"

In general I think letting linkulator accept, store and return URLs, and moving resonsibility for the correct interpretation of URLs to the called programs is a reasonable way of keeping the bloat low. Good Unix behaviour too ("Do one thing, well.")

Having both browser AND handler in the config file might be confusing, so it might be better to just move to the single setting in one go.

I considered replacing the config.USER.browser either with the value of handler["http"] or env["PAGER"] (with fallback to env["EDITOR"]).

Also I'd prefer no defaults set, but importing the existing setting for https/gopher would be ok if it wasn't too complicated

It actually should check the local variables for BROWSER, EDITOR, PAGER.
The standarization in the area didn't go very far (kind of hijacked by xdg-open).

I guess it's a bit more involved than your original suggestion, sorry about that. If you have some different ideas let me know. We could push this to a feature branch instead of master and work on it there?

Anything that can help to add it without much fuss and in an efficient way.

I am "eating my own dog food" anyway ;)

[UPDATE]

  • I didn't think about handling an incomplete [URL Handler] section:
    Removing 'gopher' entry causes linkulator to ask about opening gopher:// links with the default browser.
[User Status]
lastlogin = 1628943390.796759

[User Settings]
browser = lynx

[URL Handler]
http = lynx
https = lynx
gemini = amfora
ssh = putty
  • Using $BROWSER for gopher:// might be tricky:
    • lynx, w3m, elinks - support the protocol
    • links - doesn't
Hi @asdf, > Looks like this throws a key error when attempting to open an existing configuration file In my un-pushed local copy I handle it in a rather simplistic way: ```python try: self.lastlogin = config["User Status"]["lastlogin"] self.browser = config["User Settings"]["browser"] self.handler = config["URL Handler"] except: print("Section missing, using defaults.") ``` On closing the file is overwritten with: ```ini lastlogin = 1628938297.0196295 [User Settings] browser = lynx [URL Handler] http = lynx https = lynx gopher = lynx gemini = amfora ssh = putty ``` Having *editable* defaults makes the system more flexible and adjustable to the specific user needs. If we only could 'plumb' everything ;-) > With that said, however it might be handled, the user experience from the change should be a good one. Oh, I enjoy it. Recently added a (crude, it's merely a PoC thing) telnet:// handler too: ```bash #! /bin/bash # runtel - telnet:// URL handler # Author: samhunter@rawtext.club # PROGNAME="${0##*/}" URL="${1?Syntax: $PROGNAME <url>}" URL="${URL##telnet://}" THOST="${URL%%:*}" TPORT="${URL##*:}" telnet "$THOST" "$TPORT" ``` In general I think letting linkulator accept, store and return URLs, and moving resonsibility for the correct interpretation of URLs to the called programs is a reasonable way of keeping the bloat low. Good Unix behaviour too ("Do one thing, well.") > Having both browser AND handler in the config file might be confusing, so it might be better to just move to the single setting in one go. I considered replacing the config.USER.browser either with the value of handler["http"] or env["PAGER"] (with fallback to env["EDITOR"]). > Also I'd prefer no defaults set, but importing the existing setting for https/gopher would be ok if it wasn't too complicated It actually should check the local variables for BROWSER, EDITOR, PAGER. The standarization in the area didn't go very far (kind of hijacked by xdg-open). > I guess it's a bit more involved than your original suggestion, sorry about that. If you have some different ideas let me know. We could push this to a feature branch instead of master and work on it there? Anything that can help to add it without much fuss and in an efficient way. I am "eating my own dog food" anyway ;) [UPDATE] + I didn't think about handling an incomplete [URL Handler] section: Removing 'gopher' entry causes `linkulator` to ask about opening gopher:// links with the default browser. ```ini [User Status] lastlogin = 1628943390.796759 [User Settings] browser = lynx [URL Handler] http = lynx https = lynx gemini = amfora ssh = putty ``` + Using `$BROWSER` for gopher:// might be tricky: - lynx, w3m, elinks - support the protocol - links - doesn't
Collaborator

Hi @samhunter,

From this discussion, I've made additional comments on #69 with proposed functionality.

My proposal goes beyond your current pull request, and I don't expect you to take this on and implement as specified. Also, I apologise that this may delay your requested functionality, but I see this change as maybe being backwards incompatible so want to do it all in one go.

If you want to contribute your existing work, or anything in addition, you can change this PR to merge to the branch additional-url-handlers.

Also, feel free to provide any other feedback on my proposal at #69.

Regards,
asdf

Hi @samhunter, From this discussion, I've made additional comments on #69 with proposed functionality. My proposal goes beyond your current pull request, and I don't expect you to take this on and implement as specified. Also, I apologise that this may delay your requested functionality, but I see this change as maybe being backwards incompatible so want to do it all in one go. If you want to contribute your existing work, or anything in addition, you can change this PR to merge to the branch **additional-url-handlers**. Also, feel free to provide any other feedback on my proposal at #69. Regards, asdf
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
You can also view command line instructions.

Step 1:

From your project repository, check out a new branch and test the changes.
git checkout -b samhunter-master master
git pull master

Step 2:

Merge the changes and update on Gitea.
git checkout master
git merge --no-ff samhunter-master
git push origin master
Sign in to join this conversation.
No description provided.