mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 18:14:10 +00:00

Squashed merge of https://github.com/jupyterhub/jupyterhub/pull/4594 Co-authored-by: Samuel Gaist <samuel.gaist@idiap.ch>
686 lines
17 KiB
Plaintext
686 lines
17 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "8c5bd1ca-3329-4062-8851-9bd33009d805",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Using the sharing API from Python \n",
|
|
"\n",
|
|
"In this example, we use $JUPYTERHUB_API_TOKEN to communicate with the sharing API via Python.\n",
|
|
"\n",
|
|
"The permissions used here are granted via the `c.Spawner.server_token_scopes` config in jupyterhub_config.py\n",
|
|
"\n",
|
|
"By using this token, any user who has access to this server has access to sharing permissions.\n",
|
|
"\n",
|
|
"First, get some useful configuration from the server environment:\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "95fa629b-ac65-46da-86a6-9798f3d0a2ba",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"'http://127.0.0.1:8081/hub/api'"
|
|
]
|
|
},
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"import os\n",
|
|
"\n",
|
|
"hub_api = os.environ[\"JUPYTERHUB_API_URL\"]\n",
|
|
"token = os.environ[\"JUPYTERHUB_API_TOKEN\"]\n",
|
|
"username = os.environ[\"JUPYTERHUB_USER\"]\n",
|
|
"user_server = f\"{username}/{os.environ['JUPYTERHUB_SERVER_NAME']}\"\n",
|
|
"hub_host = os.environ[\"JUPYTERHUB_HOST\"]\n",
|
|
"server_base_url = os.environ[\"JUPYTERHUB_SERVICE_PREFIX\"]\n",
|
|
"\n",
|
|
"hub_api"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "456697ab-e7ce-4f75-bc8f-3ce424830e0d",
|
|
"metadata": {},
|
|
"source": [
|
|
"Create a requests.Session to make jupyterhub API requests with our token"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"id": "f3335eeb-64e5-4caa-acb5-1032aaf727bb",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import requests\n",
|
|
"\n",
|
|
"session = requests.Session()\n",
|
|
"session.headers = {\"Authorization\": f\"Bearer {token}\"}"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "3cf5605b-2022-4d2a-b0cc-de6f80e8f2fe",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can check the permissions our token has with a request to /hub/api/user:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"id": "17529af3-5ec8-4495-9183-bd5d423a1d76",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'kind': 'user',\n",
|
|
" 'last_activity': '2024-01-23T11:43:50.800864Z',\n",
|
|
" 'groups': [],\n",
|
|
" 'admin': False,\n",
|
|
" 'name': 'sharer',\n",
|
|
" 'servers': {'': {'name': '',\n",
|
|
" 'full_name': 'sharer/',\n",
|
|
" 'last_activity': '2024-01-23T11:43:50.800864Z',\n",
|
|
" 'started': '2024-01-23T11:28:44.948553Z',\n",
|
|
" 'pending': None,\n",
|
|
" 'ready': True,\n",
|
|
" 'stopped': False,\n",
|
|
" 'url': '/user/sharer/',\n",
|
|
" 'user_options': {},\n",
|
|
" 'progress_url': '/hub/api/users/sharer/server/progress'}},\n",
|
|
" 'session_id': None,\n",
|
|
" 'scopes': ['access:servers!server=sharer/',\n",
|
|
" 'delete:servers!server=sharer/',\n",
|
|
" 'groups:shares!server=sharer/',\n",
|
|
" 'read:groups:shares!server=sharer/',\n",
|
|
" 'read:servers!server=sharer/',\n",
|
|
" 'read:shares!server=sharer/',\n",
|
|
" 'read:users:activity!user=sharer',\n",
|
|
" 'read:users:groups!user=sharer',\n",
|
|
" 'read:users:name!user=sharer',\n",
|
|
" 'servers!server=sharer/',\n",
|
|
" 'shares!server=sharer/',\n",
|
|
" 'users:activity!server=sharer/',\n",
|
|
" 'users:activity!user=sharer',\n",
|
|
" 'users:shares!server=sharer/']}"
|
|
]
|
|
},
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"r = session.get(f\"{hub_api}/user\")\n",
|
|
"r.json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a14578b8-577e-4b0a-b74c-40d7a04a1a1a",
|
|
"metadata": {},
|
|
"source": [
|
|
"We can see who has access to this server:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"id": "63520e3c-3621-4ebf-9775-52e6150ccca5",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 0, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"shares_url = f\"{hub_api}/shares/{user_server}\"\n",
|
|
"share_codes_url = f\"{hub_api}/share-codes/{user_server}\"\n",
|
|
"r = session.get(shares_url)\n",
|
|
"r.json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5c8870d0-5b7e-4065-b227-fd463289cfdf",
|
|
"metadata": {},
|
|
"source": [
|
|
"and if there are any outstanding codes:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"id": "6fad5334-6034-48f8-b008-1fd3d6e4d139",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 0, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"r = session.get(share_codes_url)\n",
|
|
"r.json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "e6d598b3-5eab-4d56-b2ea-9aec345a3948",
|
|
"metadata": {},
|
|
"source": [
|
|
"Next, we can create a code:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "facb1872-44a5-4e4e-84d4-1fd829b1a27b",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'server': {'user': {'name': 'sharer'},\n",
|
|
" 'name': '',\n",
|
|
" 'url': '/user/sharer/',\n",
|
|
" 'ready': True},\n",
|
|
" 'scopes': ['access:servers!server=sharer/'],\n",
|
|
" 'id': 'sc_1',\n",
|
|
" 'created_at': '2024-01-23T11:46:32.154416Z',\n",
|
|
" 'expires_at': '2024-01-24T11:46:32.153582Z',\n",
|
|
" 'exchange_count': 0,\n",
|
|
" 'last_exchanged_at': None,\n",
|
|
" 'code': 'gTs7DmIhFu6UG5VQkOzRMk2MN-cdDiQ_RONCnkaq5Cg',\n",
|
|
" 'accept_url': '/hub/accept-share?code=gTs7DmIhFu6UG5VQkOzRMk2MN-cdDiQ_RONCnkaq5Cg'}"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"r = session.post(share_codes_url)\n",
|
|
"code_info = r.json()\n",
|
|
"code_info"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"id": "83e23f83-18cb-4e54-9683-68c2ab0fcfc2",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"\n",
|
|
" <a href='/hub/accept-share?code=gTs7DmIhFu6UG5VQkOzRMk2MN-cdDiQ_RONCnkaq5Cg'>share this link to grant access</a>\n",
|
|
" "
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"from IPython.display import display, HTML\n",
|
|
"full_accept_url = f\"{hub_host}{code_info['accept_url']}\"\n",
|
|
"\n",
|
|
"display(\n",
|
|
" HTML(f\"\"\"\n",
|
|
" <a href='{full_accept_url}'>share this link to grant access</a>\n",
|
|
" \"\"\")\n",
|
|
")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0ce4f6f8-a0f3-4556-ab25-1f86ef49a6db",
|
|
"metadata": {},
|
|
"source": [
|
|
"(in jupyterlab, shift-right-click to copy link)\n",
|
|
"\n",
|
|
"We can now give this to the shared-with user (i.e. us in another private browsing tab).\n",
|
|
"\n",
|
|
"After accepting the link, we can see who we've shared with again:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"id": "e056a9f0-7cec-414b-a3ce-03c8abfbc087",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [{'server': {'user': {'name': 'sharer'},\n",
|
|
" 'name': '',\n",
|
|
" 'url': '/user/sharer/',\n",
|
|
" 'ready': True},\n",
|
|
" 'scopes': ['access:servers!server=sharer/'],\n",
|
|
" 'user': {'name': 'shared-with'},\n",
|
|
" 'group': None,\n",
|
|
" 'kind': 'user',\n",
|
|
" 'created_at': '2024-01-23T11:46:43.585455Z'}],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 1, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.get(shares_url).json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "9933ef79-3f59-45fc-92a9-13e7bc1f3b82",
|
|
"metadata": {},
|
|
"source": [
|
|
"The share code can also include a `?next=` url parameter, to enable a link to take users to a specific file or view after accepting the code:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"id": "9c313d99-4dc9-45af-9643-f2b0e08b2bb4",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"/hub/accept-share?code=gTs7DmIhFu6UG5VQkOzRMk2MN-cdDiQ_RONCnkaq5Cg&next=%2Fuser%2Fsharer%2Flab%2Ftree%2Fshare-api.ipynb\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/html": [
|
|
"\n",
|
|
" share <a href='/hub/accept-share?code=gTs7DmIhFu6UG5VQkOzRMk2MN-cdDiQ_RONCnkaq5Cg&next=%2Fuser%2Fsharer%2Flab%2Ftree%2Fshare-api.ipynb'>this link</a>\n",
|
|
" to grant access and direct users to this notebook\n",
|
|
" "
|
|
],
|
|
"text/plain": [
|
|
"<IPython.core.display.HTML object>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"from urllib.parse import urlencode\n",
|
|
"\n",
|
|
"this_notebook_url = server_base_url + \"lab/tree/share-api.ipynb\"\n",
|
|
"this_notebook_accept_url = full_accept_url + \"&\" + urlencode({\"next\": this_notebook_url})\n",
|
|
"print(this_notebook_accept_url)\n",
|
|
"\n",
|
|
"display(\n",
|
|
" HTML(f\"\"\"\n",
|
|
" share <a href='{this_notebook_accept_url}'>this link</a>\n",
|
|
" to grant access and direct users to this notebook\n",
|
|
" \"\"\")\n",
|
|
")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "2455ba21-f558-4391-b8fc-6501feb3bbfb",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Reviewing and managing access\n",
|
|
"\n",
|
|
"Listing share codes doesn't reveal the code - if you need to get a code, issue a new sharing code.\n",
|
|
"\n",
|
|
"But we can see in `exchange_count` whether and how often the code has been used"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"id": "313116ed-2aa5-4a0a-bc91-f15a9428ae0c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [{'server': {'user': {'name': 'sharer'},\n",
|
|
" 'name': '',\n",
|
|
" 'url': '/user/sharer/',\n",
|
|
" 'ready': True},\n",
|
|
" 'scopes': ['access:servers!server=sharer/'],\n",
|
|
" 'id': 'sc_1',\n",
|
|
" 'created_at': '2024-01-23T11:46:32.154416Z',\n",
|
|
" 'expires_at': '2024-01-24T11:46:32.153582Z',\n",
|
|
" 'exchange_count': 1,\n",
|
|
" 'last_exchanged_at': '2024-01-23T11:46:43.589701Z'}],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 1, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.get(share_codes_url).json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "5f0d4d8e-fabb-4423-b9bd-d96643a9b9f7",
|
|
"metadata": {},
|
|
"source": [
|
|
"we can also revoke the code. Codes can be deleted by code or id"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"id": "666ac28c-70f5-4a75-9bfa-6d26c5ea610f",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [204]>"
|
|
]
|
|
},
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.delete(share_codes_url + f\"?id={code_info['id']}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "ce2fdb7f-a2de-4c95-996b-527ce0536794",
|
|
"metadata": {},
|
|
"source": [
|
|
"or if you're done sharing via code, you can delete all sharing codes for a server without looking it up their ids:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "4633b0af-ad75-4e72-9702-9cb16182aecb",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [204]>"
|
|
]
|
|
},
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.delete(share_codes_url)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "c322e058-bbf5-4058-8e20-415c7da0aa8a",
|
|
"metadata": {},
|
|
"source": [
|
|
"scopes and expiration can be customized in the request when creating the share code:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"id": "462bcec7-a899-4e3e-8e77-7d08adedb7a4",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [200]>"
|
|
]
|
|
},
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"import json\n",
|
|
"\n",
|
|
"options = {\n",
|
|
" \"scopes\": [\n",
|
|
" f\"access:servers!server={user_server}\", # access the server (default)\n",
|
|
" f\"servers!server={user_server}\", # start/stop the server\n",
|
|
" f\"shares!server={user_server}\", # further share the server with others\n",
|
|
" ],\n",
|
|
" \"expires_in\": 3600, # code expires in one hour\n",
|
|
"}\n",
|
|
"\n",
|
|
"\n",
|
|
"session.post(share_codes_url, data=json.dumps(options))\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"id": "c8babe6c-6339-4990-9241-d3db54205108",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [{'server': {'user': {'name': 'sharer'},\n",
|
|
" 'name': '',\n",
|
|
" 'url': '/user/sharer/',\n",
|
|
" 'ready': True},\n",
|
|
" 'scopes': ['access:servers!server=sharer/'],\n",
|
|
" 'user': {'name': 'shared-with'},\n",
|
|
" 'group': None,\n",
|
|
" 'kind': 'user',\n",
|
|
" 'created_at': '2024-01-23T11:46:43.585455Z'}],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 1, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"r = session.get(shares_url)\n",
|
|
"r.json()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "616d8f46-940d-4527-98a7-27894a23974f",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Revoking permissions\n",
|
|
"\n",
|
|
"We can revoke specific permssions via a PATCH request\n",
|
|
"by specifying the user (or group) and one or more scopes:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"id": "13d54943-e95a-4222-a67d-8f3832b70e3c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [200]>"
|
|
]
|
|
},
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"options = {\n",
|
|
" \"user\": \"shared-with\",\n",
|
|
" \"scopes\": ['shares!server=sharer/'],\n",
|
|
"}\n",
|
|
"\n",
|
|
"session.patch(shares_url, data=json.dumps(options))"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "0b2b2619-3bb4-41a7-852b-de019f49185e",
|
|
"metadata": {},
|
|
"source": [
|
|
"If scopes are unspecified, all permissions are revoked:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "1d8997fa-7093-44dd-abdf-405fe8cc7fcd",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [200]>"
|
|
]
|
|
},
|
|
"execution_count": 16,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"options = {\n",
|
|
" \"user\": \"shared-with\",\n",
|
|
"}\n",
|
|
"\n",
|
|
"session.patch(shares_url, data=json.dumps(options))\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "bb4e37a4-d206-420b-ad21-93a15c65bbd9",
|
|
"metadata": {},
|
|
"source": [
|
|
"_All_ shared access can be revoked via a DELETE request to the shares URL:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"id": "203ec184-df77-493b-bd8f-e6bb461abe7e",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<Response [204]>"
|
|
]
|
|
},
|
|
"execution_count": 17,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.delete(shares_url)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "129d98a3-df16-4607-8a9d-784843a7eeaf",
|
|
"metadata": {},
|
|
"source": [
|
|
"and we can see that nobody has shared access anymore"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"id": "d40f97b8-171f-4958-9cbf-bcc08a5f0db7",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"{'items': [],\n",
|
|
" '_pagination': {'offset': 0, 'limit': 200, 'total': 0, 'next': None}}"
|
|
]
|
|
},
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"session.get(shares_url).json()"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.10.13"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|