Created
January 18, 2019 15:49
-
-
Save minrk/0ea42a1d4bafde85c7699bac84d078a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "!touch test.html", | |
"execution_count": 13, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import webbrowser\nwebbrowser.get('Safari').open('test.html')", | |
"execution_count": 30, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 30, | |
"data": { | |
"text/plain": "True" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Call LSCopyDefaultApplicationURLForURL to discover the location of the application associated with a given url.\n\nUse ctypes to call apple APIs." | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import ctypes\nfrom ctypes import c_void_p, Structure, POINTER\n\nkCFStringEncodingUTF8 = 0x08000100\n\nCoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))\nLaunchServices = ctypes.cdll.LoadLibrary(ctypes.util.find_library('LaunchServices'))\n\n# create CFURL from bytes\nCFURLCreateWithBytes = CoreFoundation.CFURLCreateWithBytes\nCFURLCreateWithBytes.restype = c_void_p\nCFURLCreateWithBytes.argtypes = [c_void_p, ctypes.c_char_p, ctypes.c_int, ctypes.c_uint32, c_void_p]\n\n# for debugging CreateWithBytes\nCFURLGetBytes = CoreFoundation.CFURLGetBytes\nCFURLGetBytes.restype = ctypes.c_int\nCFURLGetBytes.argtypes = [c_void_p, ctypes.c_char_p, ctypes.c_int]\n\n# get App associated with CFURL\nLSCopyDefaultApplicationURLForURL = LaunchServices.LSCopyDefaultApplicationURLForURL\nLSCopyDefaultApplicationURLForURL.restype = c_void_p\nLSCopyDefaultApplicationURLForURL.argtypes = [c_void_p, c_void_p, c_void_p]", | |
"execution_count": 2, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "def app_for_url(url):\n \"\"\"Returns file:// url for the app associated with a url\"\"\"\n cf_url = CFURLCreateWithBytes(\n None,\n url.encode('utf8'),\n len(url),\n kCFStringEncodingUTF8,\n None,\n )\n cf_app = LSCopyDefaultApplicationURLForURL(cf_url, None, None)\n if cf_app is None:\n raise ValueError(\"No app found for %s\" % url)\n buf = (ctypes.c_char * 1024)()\n CFURLGetBytes(cf_app, buf, len(buf))\n return buf.value.decode('utf8')\n ", | |
"execution_count": 39, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "app_for_url(\"http://google.com\")", | |
"execution_count": 40, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 40, | |
"data": { | |
"text/plain": "'file:///Applications/Safari.app/'" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "Raises if there's no application found" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import os\napp_for_url(\"file://\" + os.path.abspath(\"mac-detect-browser.ipynb\"))", | |
"execution_count": 41, | |
"outputs": [ | |
{ | |
"output_type": "error", | |
"ename": "ValueError", | |
"evalue": "No app found for file:///Users/benjaminrk/dev/mine/notebooks/empty/mac-detect-browser.ipynb", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mValueError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-41-bd1a3a99e3f4>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mos\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mapp_for_url\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"file://\"\u001b[0m \u001b[1;33m+\u001b[0m \u001b[0mos\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mabspath\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"mac-detect-browser.ipynb\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;32m<ipython-input-39-d5c013765a13>\u001b[0m in \u001b[0;36mapp_for_url\u001b[1;34m(url)\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[0mcf_app\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mLSCopyDefaultApplicationURLForURL\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcf_url\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mcf_app\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"No app found for %s\"\u001b[0m \u001b[1;33m%\u001b[0m \u001b[0murl\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 13\u001b[0m \u001b[0mbuf\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m(\u001b[0m\u001b[0mctypes\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mc_char\u001b[0m \u001b[1;33m*\u001b[0m \u001b[1;36m1024\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[0mCFURLGetBytes\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcf_app\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mbuf\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbuf\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mValueError\u001b[0m: No app found for file:///Users/benjaminrk/dev/mine/notebooks/empty/mac-detect-browser.ipynb" | |
] | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import os\napp_for_url(\"file://\" + os.path.abspath(\"untitled.txt\"))", | |
"execution_count": 42, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 42, | |
"data": { | |
"text/plain": "'file:///Applications/Sublime+Text.app/'" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "webbrowser doesn't know how to handle absolute paths:" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "webbrowser.get(\"/Applications/Safari.app\")", | |
"execution_count": 44, | |
"outputs": [ | |
{ | |
"output_type": "error", | |
"ename": "Error", | |
"evalue": "could not locate runnable browser", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[1;31mError\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-44-1f7ad1cf2a7d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mwebbrowser\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"/Applications/Safari.app\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;32m~/conda/lib/python3.6/webbrowser.py\u001b[0m in \u001b[0;36mget\u001b[1;34m(using)\u001b[0m\n\u001b[0;32m 49\u001b[0m \u001b[1;32melif\u001b[0m \u001b[0mcommand\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;32mis\u001b[0m \u001b[1;32mnot\u001b[0m \u001b[1;32mNone\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 50\u001b[0m \u001b[1;32mreturn\u001b[0m \u001b[0mcommand\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 51\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"could not locate runnable browser\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 52\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 53\u001b[0m \u001b[1;31m# Please note: the following definition hides a builtin function.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", | |
"\u001b[1;31mError\u001b[0m: could not locate runnable browser" | |
] | |
} | |
] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "So we pick out the base name of the .app and hope that works:" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "from urllib.parse import unquote\n\ndef app_name_for_url(url):\n \"\"\"Return the name of an app associated with a url\"\"\"\n app = app_for_url(url)\n path = app.split('://', 1)[1]\n basename = path.rstrip('/')\n name, dotapp = os.path.splitext(os.path.basename(path.rstrip('/')))\n return unquote(name)", | |
"execution_count": 45, | |
"outputs": [] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "app_name_for_url('http://google.com')", | |
"execution_count": 46, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 46, | |
"data": { | |
"text/plain": "'Safari'" | |
}, | |
"metadata": {} | |
} | |
] | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "browser = webbrowser.get(app_name_for_url('http://jupyter.org'))\nbrowser.open(\"file://\" + os.path.abspath(\"test.html\"))", | |
"execution_count": 47, | |
"outputs": [ | |
{ | |
"output_type": "execute_result", | |
"execution_count": 47, | |
"data": { | |
"text/plain": "True" | |
}, | |
"metadata": {} | |
} | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3", | |
"language": "python" | |
}, | |
"language_info": { | |
"name": "python", | |
"version": "3.6.5", | |
"mimetype": "text/x-python", | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"pygments_lexer": "ipython3", | |
"nbconvert_exporter": "python", | |
"file_extension": ".py" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment