Skip to content

Instantly share code, notes, and snippets.

@minrk
Created January 18, 2019 15:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minrk/0ea42a1d4bafde85c7699bac84d078a1 to your computer and use it in GitHub Desktop.
Save minrk/0ea42a1d4bafde85c7699bac84d078a1 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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