{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# surplus 2.x.y playground notebook\n", "\n", "wrangling with environments for devbox users using codium/vs code:\n", "\n", "```text\n", "$ devbox shell # enter devbox env\n", "(surplus-py3.11) (devbox) $ exit # leave poetry env\n", "(devbox) $ codium . # open ide\n", "```" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OUTPUT_LINE_X_KEYS: Final[tuple[str, ...]] = (\"region\",\"county\",\"state\",\"state_district\",\"country\",\"continent\",)\n" ] } ], "source": [ "# converting nominatim keys to OUTPUT_LINE_X_KEYS format\n", "\n", "keys = \"\"\"\n", "region, county, state, state_district, country, continent\n", "\"\"\"\n", "\n", "split_keys = [f'\"{key.strip()}\"' for key in keys.strip().split(\",\")]\n", "\n", "print(f\"OUTPUT_LINE_X_KEYS: Final[tuple[str, ...]] = ({','.join(split_keys)},)\")" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from surplus import PlusCodeQuery, LocalCodeQuery, LatlongQuery, StringQuery\n", "from surplus import Latlong, Result\n", "from surplus import SurplusDefaultGeocoding\n", "\n", "geocoding = SurplusDefaultGeocoding()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generic Result NamedTuple" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\tNone \t3\n", "False\tZeroDivisionError('division by zero') \tdivision by zero (ZeroDivisionError)\n" ] }, { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[1;32m/home/m/works/surplus/playground.ipynb Cell 5\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{:<40}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{}\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(\u001b[39mbool\u001b[39m(nom_result), \u001b[39mrepr\u001b[39m(nom_result\u001b[39m.\u001b[39merror), nom_result\u001b[39m.\u001b[39mget()))\n\u001b[1;32m 9\u001b[0m \u001b[39mprint\u001b[39m(\n\u001b[1;32m 10\u001b[0m \u001b[39m\"\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{:<40}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{}\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(\n\u001b[1;32m 11\u001b[0m \u001b[39mbool\u001b[39m(exc_result), \u001b[39mrepr\u001b[39m(exc_result\u001b[39m.\u001b[39merror), exc_result\u001b[39m.\u001b[39mcry(string\u001b[39m=\u001b[39m\u001b[39mTrue\u001b[39;00m)\n\u001b[1;32m 12\u001b[0m )\n\u001b[1;32m 13\u001b[0m )\n\u001b[0;32m---> 14\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39m{}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{:<40}\u001b[39;00m\u001b[39m\\t\u001b[39;00m\u001b[39m{}\u001b[39;00m\u001b[39m\"\u001b[39m\u001b[39m.\u001b[39mformat(\u001b[39mbool\u001b[39m(exc_result), \u001b[39mrepr\u001b[39m(exc_result\u001b[39m.\u001b[39merror), exc_result\u001b[39m.\u001b[39;49mget()))\n", "File \u001b[0;32m~/works/surplus/surplus/surplus.py:270\u001b[0m, in \u001b[0;36mResult.get\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 268\u001b[0m \u001b[39m\"\"\"method that returns self.value if Result is non-erroneous else raises error\"\"\"\u001b[39;00m\n\u001b[1;32m 269\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39misinstance\u001b[39m(\u001b[39mself\u001b[39m\u001b[39m.\u001b[39merror, \u001b[39mBaseException\u001b[39;00m):\n\u001b[0;32m--> 270\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39merror\n\u001b[1;32m 271\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mvalue\n", "\u001b[1;32m/home/m/works/surplus/playground.ipynb Cell 5\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m nom_result \u001b[39m=\u001b[39m Result[\u001b[39mint\u001b[39m](\u001b[39m3\u001b[39m)\n\u001b[1;32m 3\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m----> 4\u001b[0m \u001b[39m1\u001b[39;49m \u001b[39m/\u001b[39;49m \u001b[39m0\u001b[39;49m\n\u001b[1;32m 5\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mException\u001b[39;00m \u001b[39mas\u001b[39;00m exc:\n\u001b[1;32m 6\u001b[0m exc_result \u001b[39m=\u001b[39m Result[\u001b[39mint\u001b[39m](\u001b[39m-\u001b[39m\u001b[39m1\u001b[39m, error\u001b[39m=\u001b[39mexc)\n", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "nom_result = Result[int](3)\n", "\n", "try:\n", " 1 / 0\n", "except Exception as exc:\n", " exc_result = Result[int](-1, error=exc)\n", "\n", "print(\"{}\\t{:<40}\\t{}\".format(bool(nom_result), repr(nom_result.error), nom_result.get()))\n", "print(\n", " \"{}\\t{:<40}\\t{}\".format(\n", " bool(exc_result), repr(exc_result.error), exc_result.cry(string=True)\n", " )\n", ")\n", "print(\"{}\\t{:<40}\\t{}\".format(bool(exc_result), repr(exc_result.error), exc_result.get()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Query Types" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Result(value=Latlong(latitude=1.3336875, longitude=103.7746875), error=None)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "PlusCodeQuery(code=\"6PH58QMF+FV\").to_lat_long_coord(geocoder=geocoding.geocoder)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Result(value=Latlong(latitude=1.3336875, longitude=103.7746875), error=None)" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plus_code = LocalCodeQuery(code=\"8QMF+FV\", locality=\"Singapore\").to_full_plus_code(\n", " geocoder=geocoding.geocoder\n", ")\n", "\n", "PlusCodeQuery(code=plus_code.get()).to_lat_long_coord(geocoder=geocoding.geocoder)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Result(value=Latlong(latitude=1.3336875, longitude=103.7746875), error=None)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LocalCodeQuery(code=\"8QMF+FV\", locality=\"Singapore\").to_lat_long_coord(\n", " geocoder=geocoding.geocoder\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Result(value=Latlong(latitude=1.33318835, longitude=103.77461234638255), error=None)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "LatlongQuery(\n", " latlong=Latlong(latitude=1.33318835, longitude=103.77461234638255)\n", ").to_lat_long_coord(geocoder=geocoding.geocoder)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Result(value=Latlong(latitude=1.33318835, longitude=103.77461234638255), error=None)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "StringQuery(query=\"Ngee Ann Polytechnic\").to_lat_long_coord(geocoder=geocoding.geocoder)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## return dictionary of `reverser` function\n", "\n", "all the necessary keys (see `SHAREABLE_TEXT_LINE_*` constants) should be at the top-level of the dictionary.\n", "these keys will be casted into strings for safety guarantee when shareable text lines are being generated.\n", "\n", "while not necessary, consider keeping the original response dict under the \"raw\" key.\n", "helps with debugging using `-d/--debug`!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "reverser_return = {\n", " \"amenity\": \"Ngee Ann Polytechnic\",\n", " \"house_number\": \"535\",\n", " \"road\": \"Clementi Road\",\n", " \"suburb\": \"Bukit Timah\",\n", " \"city\": \"Singapore\",\n", " \"county\": \"Northwest\",\n", " \"ISO3166-2-lvl6\": \"SG-03\",\n", " \"postcode\": \"599489\",\n", " \"country\": \"Singapore\",\n", " \"country_code\": \"sg\",\n", " \"raw\": {\n", " \"place_id\": 297946059,\n", " \"licence\": \"Data © OpenStreetMap contributors, ODbL 1.0. http://osm.org/copyright\",\n", " \"osm_type\": \"relation\",\n", " \"osm_id\": 2535118,\n", " \"lat\": \"1.33318835\",\n", " \"lon\": \"103.77461234638255\",\n", " \"class\": \"amenity\",\n", " \"type\": \"university\",\n", " \"place_rank\": 30,\n", " \"importance\": 0.34662169301918117,\n", " \"addresstype\": \"amenity\",\n", " \"name\": \"Ngee Ann Polytechnic\",\n", " \"display_name\": \"Ngee Ann Polytechnic, 535, Clementi Road, Bukit Timah, Singapore, Northwest, 599489, Singapore\",\n", " \"address\": {\n", " \"amenity\": \"Ngee Ann Polytechnic\",\n", " \"house_number\": \"535\",\n", " \"road\": \"Clementi Road\",\n", " \"suburb\": \"Bukit Timah\",\n", " \"city\": \"Singapore\",\n", " \"county\": \"Northwest\",\n", " \"ISO3166-2-lvl6\": \"SG-03\",\n", " \"postcode\": \"599489\",\n", " \"country\": \"Singapore\",\n", " \"country_code\": \"sg\",\n", " },\n", " \"boundingbox\": [\"1.3289692\", \"1.3372184\", \"103.7701481\", \"103.7783945\"],\n", " },\n", " \"latitude\": 1.33318835,\n", " \"longitude\": 103.77461234638255,\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'ISO3166-2-lvl6': 'SG-03',\n", " 'amenity': 'Ngee Ann Polytechnic',\n", " 'city': 'Singapore',\n", " 'country': 'Singapore',\n", " 'country_code': 'sg',\n", " 'county': 'Northwest',\n", " 'house_number': '535',\n", " 'latitude': 1.33318835,\n", " 'longitude': 103.77461234638255,\n", " 'neighbourhood': 'Ewart Park',\n", " 'postcode': '599489',\n", " 'raw': {'address': {'ISO3166-2-lvl6': 'SG-03',\n", " 'amenity': 'Ngee Ann Polytechnic',\n", " 'city': 'Singapore',\n", " 'country': 'Singapore',\n", " 'country_code': 'sg',\n", " 'county': 'Northwest',\n", " 'house_number': '535',\n", " 'neighbourhood': 'Ewart Park',\n", " 'postcode': '599489',\n", " 'road': 'Clementi Road',\n", " 'suburb': 'Bukit Timah'},\n", " 'addresstype': 'amenity',\n", " 'boundingbox': ['1.3289692',\n", " '1.3372184',\n", " '103.7701481',\n", " '103.7783945'],\n", " 'class': 'amenity',\n", " 'display_name': 'Ngee Ann Polytechnic, 535, Clementi Road, Ewart '\n", " 'Park, Bukit Timah, Singapore, Northwest, 599489, '\n", " 'Singapore',\n", " 'importance': 0.34662169301918117,\n", " 'lat': '1.33318835',\n", " 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '\n", " 'http://osm.org/copyright',\n", " 'lon': '103.77461234638255',\n", " 'name': 'Ngee Ann Polytechnic',\n", " 'osm_id': 2535118,\n", " 'osm_type': 'relation',\n", " 'place_id': 250910125,\n", " 'place_rank': 30,\n", " 'type': 'university'},\n", " 'road': 'Clementi Road',\n", " 'suburb': 'Bukit Timah'}\n" ] } ], "source": [ "import pprint\n", "\n", "latlong = LocalCodeQuery(code=\"8QMF+FV\", locality=\"Singapore\").to_lat_long_coord(\n", " geocoder=geocoding.geocoder\n", ")\n", "if not latlong:\n", " latlong.cry()\n", "\n", "else:\n", " location = geocoding.reverser(latlong.get())\n", " pprint.pprint(location)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2.1.0: adventures in of shortening global/full Plus Codes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### testing rate-limited and cached default geocoding functions" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "test_geocoding = SurplusDefaultGeocoding(user_agent=\"surplus/playground\")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "2\n", "3\n", "4\n", "5\n", "\n", "1\n", "2\n", "3\n", "4\n", "5\n", "\n", "3.1107698050s\t->\t0.0000886890s\t\t(-3.1106811160002508s)\n" ] } ], "source": [ "from timeit import timeit\n", "\n", "\n", "test_stmt = \"\"\"\\\n", "print(1)\n", "test_geocoding.geocoder(\"Wisma Atria\") # instant\n", "print(2)\n", "test_geocoding.geocoder(\"Temasek Polytechnic\") # after 1 second\n", "print(3)\n", "location = test_geocoding.geocoder(\"Ngee Ann Polytechnic\") # after 1 second\n", "print(4)\n", "test_geocoding.reverser(f\"{location.latitude}, {location.longitude}\") # instant\n", "print(5)\n", "test_geocoding.reverser(f\"{location.latitude}, {location.longitude}\") # instant (cached)\n", "print()\n", "\"\"\"\n", "\n", "time_cold_call = timeit(test_stmt, globals=globals(), number=1) # expecting 3-4 seconds\n", "time_2nd_call = timeit(test_stmt, globals=globals(), number=1) # should be instant\n", "\n", "print(\n", " f\"{time_cold_call:.10f}s\\t->\\t{time_2nd_call:.10f}s\\t\\t({time_2nd_call - time_cold_call}s)\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### reversing the query latlong and using the address information to form a locality" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "level = 13" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "St Lucia, St Lucia, Queensland, Australia\n", "Austin, Travis County, Texas, United States\n" ] } ], "source": [ "(\n", " au_response := geocoding.reverser(\n", " (\n", " au_target := (\n", " LocalCodeQuery(\n", " \"G227+XF\", \"St Lucia, Queensland, Australia\"\n", " ).to_lat_long_coord(geocoding.geocoder)\n", " )\n", " ).get(),\n", " level=level,\n", " )\n", ")\n", "\n", "au_locality = f\"{au_response['suburb']}, {au_response['city_district']}, {au_response['state']}, {au_response['country']}\"\n", "print(au_locality)\n", "\n", "(\n", " us_response := geocoding.reverser(\n", " (\n", " us_target := (\n", " LocalCodeQuery(\"77Q4+7X\", \"Austin, Texas, USA\").to_lat_long_coord(\n", " geocoding.geocoder\n", " )\n", " )\n", " ).get(),\n", " level=level,\n", " )\n", ")\n", "\n", "us_locality = f\"{us_response['city']}, {us_response['county']}, {us_response['state']}, {us_response['country']}\"\n", "print(us_locality)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### getting boundary boxes" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'addresstype': 'suburb',\n", " 'boundingbox': ['-27.5187362', '-27.4787362', '152.9881642', '153.0281642'],\n", " 'class': 'place',\n", " 'display_name': 'St Lucia, Brisbane City, Queensland, 4072, Australia',\n", " 'importance': 0.27501,\n", " 'lat': '-27.4987362',\n", " 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '\n", " 'http://osm.org/copyright',\n", " 'lon': '153.0081642',\n", " 'name': 'St Lucia',\n", " 'osm_id': 88800268,\n", " 'osm_type': 'node',\n", " 'place_id': 54477898,\n", " 'place_rank': 19,\n", " 'type': 'suburb'}\n", "\n", "Latlong(latitude=-27.4987362, longitude=153.0081642, bounding_box=[-27.5187362, -27.4787362, 152.9881642, 153.0281642])\n" ] } ], "source": [ "from geopy.geocoders import Nominatim\n", "from pprint import pprint\n", "\n", "target_query: Result[Latlong] = au_target\n", "target_locality: str = au_locality\n", "\n", "raw_geocoding = Nominatim(user_agent=\"surplus/playground\")\n", "latlong = raw_geocoding.geocode(target_locality)\n", "pprint(latlong.raw)\n", "print()\n", "\n", "# done: now implmented in surplus as surplus.Latlong.bounding_box\n", "locality_latlong = geocoding.geocoder(target_locality)\n", "pprint(locality_latlong)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-27.5187362, -27.4787362, 152.9881642, 153.0281642]\n", "(True, True, True, True)\n", "(True, True, True, True)\n" ] } ], "source": [ "# based on \n", "\n", "target_latlong = target_query.get()\n", "if locality_latlong.bounding_box is None:\n", " ... # raise some error\n", "\n", "print(locality_latlong.bounding_box)\n", "check1 = (\n", " # The center point of the feature is within 0.4 degrees latitude and 0.4 degrees longitude\n", " (\n", " (target_latlong.latitude - 0.4)\n", " <= locality_latlong.latitude\n", " <= (target_latlong.latitude + 0.4)\n", " ),\n", " (\n", " (target_latlong.longitude - 0.4)\n", " <= locality_latlong.longitude\n", " <= (target_latlong.longitude + 0.4)\n", " ),\n", " # The bounding box of the feature is less than 0.8 degrees high and wide.\n", " abs(locality_latlong.bounding_box[0] - locality_latlong.bounding_box[1]) < 0.8,\n", " abs(locality_latlong.bounding_box[2] - locality_latlong.bounding_box[3]) < 0.8,\n", ")\n", "\n", "\n", "check2 = (\n", " # The center point of the feature is within 0.4 degrees latitude and 0.4 degrees longitude\n", " (\n", " (target_latlong.latitude - 8)\n", " <= locality_latlong.latitude\n", " <= (target_latlong.latitude + 8)\n", " ),\n", " (\n", " (target_latlong.longitude - 8)\n", " <= locality_latlong.longitude\n", " <= (target_latlong.longitude + 8)\n", " ),\n", " # The bounding box of the feature is less than 0.8 degrees high and wide.\n", " abs(locality_latlong.bounding_box[0] - locality_latlong.bounding_box[1]) < 16,\n", " abs(locality_latlong.bounding_box[2] - locality_latlong.bounding_box[3]) < 16,\n", ")\n", "\n", "print(check1)\n", "print(check2)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "G227+XF St Lucia, St Lucia, Queensland, Australia\n" ] } ], "source": [ "from pluscodes import encode\n", "\n", "target_plus_code = encode(\n", " lat=target_latlong.latitude, lon=target_latlong.longitude, code_length=10\n", ")\n", "portion_plus_code = \"\"\n", "\n", "if check1:\n", " portion_plus_code = target_plus_code[4:]\n", " print(portion_plus_code, target_locality)\n", "\n", "elif check2:\n", " portion_plus_code = target_plus_code[2:]\n", " print(portion_plus_code, target_locality)\n", "\n", "else:\n", " print(\n", " \"info: could not determine a suitable geographical feature to use as locality for shortening.\"\n", " )\n", " print(plus_code)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## machine fingerprinting attempt\n", "\n", "because of nominatim's acceptable usage policy \n", "" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from hashlib import shake_256 as _hashlib_shake_256\n", "from platform import platform as _platform_platform\n", "from socket import gethostname as _socket_gethostname\n", "from uuid import getnode as _uuid_getnode\n", "from surplus import VERSION, VERSION_SUFFIX\n", "\n", "\n", "def generate_fingerprinted_user_agent() -> Result[str]:\n", " \"\"\"\n", " function that attempts to return a unique user agent string.\n", "\n", " returns Result[str]\n", " this result will always have a valid value as erroneous results will have a\n", " resulting value of 'surplus//generic-user'\n", " valid results will have a value of 'surplus//', where\n", " fingerprint is a 12 character hexadecimal string\n", " \"\"\"\n", " version: str = \".\".join([str(v) for v in VERSION]) + VERSION_SUFFIX\n", "\n", " try:\n", " system_info: str = _platform_platform()\n", " hostname: str = _socket_gethostname()\n", " mac_address: str = \":\".join(\n", " [\n", " \"{:02x}\".format((_uuid_getnode() >> elements) & 0xFF)\n", " for elements in range(0, 2 * 6, 2)\n", " ][::-1]\n", " )\n", " unique_info: str = f\"{version}-{system_info}-{hostname}-{mac_address}\"\n", "\n", " print(f\"{version=}\")\n", " print(f\"{system_info=}\")\n", " print(f\"{hostname=}\")\n", " print(f\"{mac_address=}\")\n", "\n", " except Exception as exc:\n", " return Result[str](f\"surplus/{version} (generic-user)\", error=exc)\n", "\n", " fingerprint: str = _hashlib_shake_256(unique_info.encode()).hexdigest(5)\n", "\n", " return Result[str](f\"surplus/{version} ({fingerprint})\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.11.1" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }