'Pass URI to Bundled Application

I have a .app created with pyinstaller. The info.plist is excerpted below:

...
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>Flagship</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>flagship</string>
            </array>
        </dict>
    </array>
...

If I open anything that begins with flagship in the browser, i.e. flagship:foo, then it launches the application. However, sys.argv only contains the url to the compiled-python executable. How do I capture the parameters of the URI call, i.e. how do I get the foo from the URI?

Update 1: So the following works to launch the app: do shell script "open -a /Applications/AppName.app --args foo1 foo2" when I use this Apple Script bundled as a .app and using the same info.plist options:

on open location this_URL
    do shell script "open -a /Applications/AppName.app --args " & this_URL & ""
end open location

The question is: how can I achieve something similar directly from the URI? I would like to avoid the intermediary app if at all possible.

Update 2: I found a working example: https://pyobjc.readthedocs.io/en/latest/examples/WebKit/PyDocURLProtocol/index.html

It looks like it is definitely possible, just a pain. Will update if I get it working. This command, for example, pydoc:///credid=foo spits out perfectly in the example, so it is definitely possible.

Update 3: I ended up going to a workaround with PySide6. I posted it below.



Solution 1:[1]

I ended up figuring this out using PySide6. Using a subclass of QApplication allowed me to define an event handler that catches a 'file open' event... the browser, even if it is not 'opening' a file, still passes the full uri. Code below:

from PySide6.QtCore import QEvent, QUrl
from PySide6.QtWidgets import QApplication
from ..logger import logger # NOTE: this code uses a packaged logger I built and you will need to replace it or remove all of the calls to logger to get this code to work.


class CustomURIApplication(QApplication):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.on_uri_do = None
        self.last_uri = None

    def on_uri_call(self, func):
        """
        Run func when an uri is sent to the application; access the uri @ CustomURIApplication.last_uri
        :param func: the function to run (WITHOUT "()" AT THE END); use lambda for parameters
        :return: None
        """
        self.on_uri_do = func

    def event(self, e):
        """Handle macOS FileOpen events or pass to super."""
        if e.type() == QEvent.FileOpen:
            url: QUrl = e.url()
            self.last_uri: QUrl = url
            if url.isValid():
                logger.info(f"application received valid uri: {url}")
                logger.debug(f"executing callback function")
                self.on_uri_do()
            else:
                logger.warning(f"application received invalid uri: {url.errorString()}")
        else:
            return super().event(e)
        return True

Info.plist needs to include what I have in the original question and this only works once bundled with PyInstaller.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1