diff --git a/README.md b/README.md index 2c9f5b0..1949ecc 100644 --- a/README.md +++ b/README.md @@ -799,8 +799,6 @@ class for documentation and static type checking of surplus reverser functions [`functools.lru_cache()`-wrapped](https://docs.python.org/3/library/functools.html#functools.lru_cache) if the geocoding service asks for caching - see the [playground notebook](/playground.ipynb) in repository root for detailed - sample output exceptions are handled by the caller ### `class Behaviour` diff --git a/playground.ipynb b/playground.ipynb deleted file mode 100644 index fa9a3f5..0000000 --- a/playground.ipynb +++ /dev/null @@ -1,726 +0,0 @@ -{ - "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 -} diff --git a/poetry.lock b/poetry.lock index 2cf0be5..6204d86 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,34 +1,5 @@ # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. -[[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = "*" -files = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] - -[[package]] -name = "asttokens" -version = "2.4.1" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[package.dependencies] -six = ">=1.12.0" - -[package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - [[package]] name = "black" version = "24.2.0" @@ -62,12 +33,10 @@ files = [ [package.dependencies] click = ">=8.0.0" -ipython = {version = ">=7.8.0", optional = true, markers = "extra == \"jupyter\""} mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tokenize-rt = {version = ">=3.2.0", optional = true, markers = "extra == \"jupyter\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -100,31 +69,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "executing" -version = "2.0.1" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.5" -files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - [[package]] name = "geographiclib" version = "2.0" @@ -159,42 +103,6 @@ dev-test = ["coverage", "pytest (>=3.10)", "pytest-asyncio (>=0.17)", "sphinx (< requests = ["requests (>=2.16.2)", "urllib3 (>=1.24.2)"] timezone = ["pytz"] -[[package]] -name = "ipython" -version = "8.17.2" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.9" -files = [ - {file = "ipython-8.17.2-py3-none-any.whl", hash = "sha256:1e4d1d666a023e3c93585ba0d8e962867f7a111af322efff6b9c58062b3e5444"}, - {file = "ipython-8.17.2.tar.gz", hash = "sha256:126bb57e1895594bb0d91ea3090bbd39384f6fe87c3d57fd558d0670f50339bb"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" - -[package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] - [[package]] name = "isort" version = "5.13.2" @@ -209,39 +117,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "jedi" -version = "0.19.1" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, -] - -[package.dependencies] -parso = ">=0.8.3,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "matplotlib-inline" -version = "0.1.6" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.5" -files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, -] - -[package.dependencies] -traitlets = "*" - [[package]] name = "mypy" version = "1.8.0" @@ -310,60 +185,31 @@ files = [ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - [[package]] name = "platformdirs" -version = "3.11.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluscodes" @@ -378,138 +224,18 @@ files = [ [package.extras] dev = ["black (==22.3.0)", "build (==0.8.0)", "coverage (==6.4)", "isort (==5.8.0)", "pylintv (==2.15.0)", "pytest (==7.1.1)", "pytest-cov (==3.0.0)", "twine (==4.0.1)"] -[[package]] -name = "prompt-toolkit" -version = "3.0.39" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"}, - {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pygments" -version = "2.16.1" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, -] - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "tokenize-rt" -version = "5.2.0" -description = "A wrapper around the stdlib `tokenize` which roundtrips." -optional = false -python-versions = ">=3.8" -files = [ - {file = "tokenize_rt-5.2.0-py2.py3-none-any.whl", hash = "sha256:b79d41a65cfec71285433511b50271b05da3584a1da144a0752e9c621a285289"}, - {file = "tokenize_rt-5.2.0.tar.gz", hash = "sha256:9fe80f8a5c1edad2d3ede0f37481cc0cc1538a2f442c9c2f9e4feacd2792d054"}, -] - -[[package]] -name = "traitlets" -version = "5.13.0" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.13.0-py3-none-any.whl", hash = "sha256:baf991e61542da48fe8aef8b779a9ea0aa38d8a54166ee250d5af5ecf4486619"}, - {file = "traitlets-5.13.0.tar.gz", hash = "sha256:9b232b9430c8f57288c1024b34a8f0251ddcc47268927367a0dd3eeaca40deb5"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.6.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] - [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, -] - -[[package]] -name = "wcwidth" -version = "0.2.9" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.9-py2.py3-none-any.whl", hash = "sha256:9a929bd8380f6cd9571a968a9c8f4353ca58d7cd812a4822bba831f8d685b223"}, - {file = "wcwidth-0.2.9.tar.gz", hash = "sha256:a675d1a4a2d24ef67096a04b85b02deeecd8e226f57b5e3a72dbb9ed99d27da8"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "085d65d20f0d122a12add332a750f86857f077363734ffe7e6f112026b561b10" +content-hash = "7e98867e42e78af873d571b1f5e1ac66b21f4147b88ea629eb9f4d808c8f84c5" diff --git a/surplus/surplus.py b/surplus/surplus.py index cf9e4fd..f8579a2 100644 --- a/surplus/surplus.py +++ b/surplus/surplus.py @@ -475,8 +475,7 @@ class SurplusReverserProtocol(Protocol): function can and should be at minimum functools.lru_cache()-wrapped if the geocoding service asks for caching - exceptions are handled by the caller, - see the playground notebook in repository root for sample output + exceptions are handled by the caller """ def __call__(self, latlong: Latlong, level: int = 18) -> dict[str, Any]: