From e206c898689a39853aa539713a04cfd0b746136e Mon Sep 17 00:00:00 2001 From: Don Aldrich Date: Fri, 12 Aug 2022 20:07:20 +0000 Subject: [PATCH] added furl --- .gitignore | 2 + .../__pycache__/tomd.cpython-310.pyc | Bin 0 -> 247 bytes data_converion/tomd.ipynb | 87 + data_converion/tomd.py | 5 - .../Add Container Inventory to Directus.ipynb | 6 +- data_transfer/Add Inventories to wiki.ipynb | 2 +- .../Add Linkace Inventory to Directus.ipynb | 2 +- data_transfer/Add traefic routers.ipynb | 2 +- documentation/python-cheatsheet.ipynb | 640 +++++ documentation/terraform-cheatsheet.ipynb | 2485 +++++++++++++++++ documentation/terraform-docs.ipynb | 119 + documentation/weasyprint.ipynb | 18 +- http/furl/furl.ipynb | 287 ++ http/furl/readme.md | 784 ++++++ playground/nlp/spacy.ipynb | 126 +- storage/minio.ipynb | 53 + templating/Dashmachine Config Template.ipynb | 26 +- webscraping/pyppeteer/walgreens.ipynb | 15 +- webscraping/rendering/splash.ipynb | 174 +- webscraping/robot_framework/Untitled.ipynb | 40 +- 20 files changed, 4616 insertions(+), 257 deletions(-) create mode 100644 data_converion/__pycache__/tomd.cpython-310.pyc create mode 100644 data_converion/tomd.ipynb delete mode 100644 data_converion/tomd.py create mode 100644 documentation/python-cheatsheet.ipynb create mode 100644 documentation/terraform-cheatsheet.ipynb create mode 100644 documentation/terraform-docs.ipynb create mode 100644 http/furl/furl.ipynb create mode 100644 http/furl/readme.md create mode 100644 storage/minio.ipynb diff --git a/.gitignore b/.gitignore index f8dc5f2..82a94f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ unpublished/ .DS_Store +.env +temp/ diff --git a/data_converion/__pycache__/tomd.cpython-310.pyc b/data_converion/__pycache__/tomd.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aeedf9d94d89f53502fc379430f08a306423e6ba GIT binary patch literal 247 zcmYjKv2MaZ4D}^Z8@14n=!Pmdu}6rR0R}pCsX}ZigybCE9U%A`82O?~8DZ)#bgGXo zwdD7nW&1tu_n$!O?d!T)2;ai+zhW7t((Y9N0aXnjKyXqRomCa7pyQyG?3OTyzvT|* z$LFjZPs(wog$7BNcy6#u-UFshZa2t#(>)e!c4Ykp?1OyW;b*2xZb&1Wyhgh&O5Iwn zsPIlZDl7?C+xFne4h?%+F>jp4+~=QvZfiC=q>b7`mu3y)wx)>^dlxcN=tVElFRElh ATmS$7 literal 0 HcmV?d00001 diff --git a/data_converion/tomd.ipynb b/data_converion/tomd.ipynb new file mode 100644 index 0000000..5ddf447 --- /dev/null +++ b/data_converion/tomd.ipynb @@ -0,0 +1,87 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: tomd in /home/donaldrich/.local/lib/python3.10/site-packages (0.1.3)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install tomd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```python\n", + "import tomd\n", + "\n", + "tomd.Tomd('

h1

').markdown\n", + "# or\n", + "tomd.convert('

h1

')\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "partially initialized module 'tomd' has no attribute 'Tomd' (most likely due to a circular import)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/data_converion/tomd.ipynb Cell 2\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mtomd\u001b[39;00m\n\u001b[1;32m 2\u001b[0m \u001b[39m# from tomd import Tomd\u001b[39;00m\n\u001b[1;32m 3\u001b[0m \u001b[39m# tomd.Tomd('

h1

').markdown\u001b[39;00m\n\u001b[1;32m 4\u001b[0m \u001b[39m# or\u001b[39;00m\n\u001b[1;32m 5\u001b[0m tomd\u001b[39m.\u001b[39mconvert(\u001b[39m'\u001b[39m\u001b[39m

h1

\u001b[39m\u001b[39m'\u001b[39m)\n", + "File \u001b[0;32m~/projects/secrets/jupyter-notebooks/data_converion/tomd.py:3\u001b[0m, in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[39mimport\u001b[39;00m \u001b[39mtomd\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m tomd\u001b[39m.\u001b[39;49mTomd(\u001b[39m'\u001b[39m\u001b[39m

h1

\u001b[39m\u001b[39m'\u001b[39m)\u001b[39m.\u001b[39mmarkdown\n\u001b[1;32m 4\u001b[0m \u001b[39m# or\u001b[39;00m\n\u001b[1;32m 5\u001b[0m tomd\u001b[39m.\u001b[39mconvert(\u001b[39m'\u001b[39m\u001b[39m

h1

\u001b[39m\u001b[39m'\u001b[39m)\n", + "\u001b[0;31mAttributeError\u001b[0m: partially initialized module 'tomd' has no attribute 'Tomd' (most likely due to a circular import)" + ] + } + ], + "source": [ + "import tomd\n", + "\n", + "tomd.convert('

h1

')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.4 64-bit", + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/data_converion/tomd.py b/data_converion/tomd.py deleted file mode 100644 index 147b965..0000000 --- a/data_converion/tomd.py +++ /dev/null @@ -1,5 +0,0 @@ -import tomd - -tomd.Tomd('

h1

').markdown -# or -tomd.convert('

h1

') \ No newline at end of file diff --git a/data_transfer/Add Container Inventory to Directus.ipynb b/data_transfer/Add Container Inventory to Directus.ipynb index 2d3046b..e41de1a 100644 --- a/data_transfer/Add Container Inventory to Directus.ipynb +++ b/data_transfer/Add Container Inventory to Directus.ipynb @@ -90,7 +90,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.13 64-bit", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -104,14 +104,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.4" }, "toc-autonumbering": false, "toc-showcode": false, "toc-showtags": false, "vscode": { "interpreter": { - "hash": "340e956ee656efd8fdfb480dc033c937d9b626f8b21073bd1b5aa2a469586ea6" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" } }, "widgets": { diff --git a/data_transfer/Add Inventories to wiki.ipynb b/data_transfer/Add Inventories to wiki.ipynb index be552a6..ce3b285 100644 --- a/data_transfer/Add Inventories to wiki.ipynb +++ b/data_transfer/Add Inventories to wiki.ipynb @@ -301,7 +301,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.9" + "version": "3.10.4" }, "toc-autonumbering": false, "toc-showcode": false, diff --git a/data_transfer/Add Linkace Inventory to Directus.ipynb b/data_transfer/Add Linkace Inventory to Directus.ipynb index bfe865c..9d8b60f 100644 --- a/data_transfer/Add Linkace Inventory to Directus.ipynb +++ b/data_transfer/Add Linkace Inventory to Directus.ipynb @@ -175,7 +175,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.9" + "version": "3.10.4" }, "toc-autonumbering": false, "toc-showcode": false, diff --git a/data_transfer/Add traefic routers.ipynb b/data_transfer/Add traefic routers.ipynb index 9a6cead..982a7c3 100644 --- a/data_transfer/Add traefic routers.ipynb +++ b/data_transfer/Add traefic routers.ipynb @@ -101,7 +101,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.9" + "version": "3.10.4" }, "toc-autonumbering": false, "toc-showcode": false, diff --git a/documentation/python-cheatsheet.ipynb b/documentation/python-cheatsheet.ipynb new file mode 100644 index 0000000..fd05793 --- /dev/null +++ b/documentation/python-cheatsheet.ipynb @@ -0,0 +1,640 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fatal: destination path 'python-cheatsheet' already exists and is not an empty directory.\n", + "Running `brew update --auto-update`...\n", + "\u001b[34m==>\u001b[0m \u001b[1mAuto-updated Homebrew!\u001b[0m\n", + "Updated 2 taps (go-task/tap and homebrew/core).\n", + "\u001b[34m==>\u001b[0m \u001b[1mNew Formulae\u001b[0m\n", + "aws2-wrap nmrpflash\n", + "berkeley-db@5 ocl-icd\n", + "c2rust onlykey-agent\n", + "cargo-crev open62541\n", + "cargo-depgraph openvi\n", + "chain-bench page\n", + "cpp-httplib pint\n", + "create-api pkcs11-tools\n", + "dura pocl\n", + "enex2notion prql-compiler\n", + "felinks prr\n", + "gaze pymupdf\n", + "gcc@11 sgr\n", + "gcem smap\n", + "git-delete-merged-branches snapcast\n", + "git-sync spr\n", + "go@1.18 svt-av1\n", + "gum tere\n", + "hyx tlsx\n", + "jupp treefmt\n", + "ksh93 tuntox\n", + "libbpf vile\n", + "libeatmydata vulkan-loader\n", + "lucky-commit x86_64-linux-gnu-binutils\n", + "mesheryctl\n", + "\n", + "You have \u001b[1m19\u001b[0m outdated formulae installed.\n", + "You can upgrade them with \u001b[1mbrew upgrade\u001b[0m\n", + "or list them with \u001b[1mbrew outdated\u001b[0m.\n", + "\n", + "\u001b[34m==>\u001b[0m \u001b[1mDownloading https://ghcr.io/v2/homebrew/core/fetch/manifests/0.4.5\u001b[0m\n", + "######################################################################## 100.0%\n", + "\u001b[34m==>\u001b[0m \u001b[1mDownloading https://ghcr.io/v2/homebrew/core/fetch/blobs/sha256:35d3950442bc\u001b[0m\n", + "\u001b[34m==>\u001b[0m \u001b[1mDownloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh\u001b[0m\n", + "######################################################################## 100.0%\n", + "\u001b[34m==>\u001b[0m \u001b[1mPouring fetch--0.4.5.x86_64_linux.bottle.tar.gz\u001b[0m\n", + "🍺 /home/linuxbrew/.linuxbrew/Cellar/fetch/0.4.5: 5 files, 8.9MB\n", + "\u001b[34m==>\u001b[0m \u001b[1mRunning `brew cleanup fetch`...\u001b[0m\n", + "Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.\n", + "Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).\n" + ] + } + ], + "source": [ + "!git clone https://github.com/wilfredinni/python-cheatsheet.git\n", + "!brew install fetch\n", + "!mkdir temp\n", + "!fetch --branch=master --source-path=/docs --repo=https://github.com/wilfredinni/python-cheatsheet temp\n", + "# https://github.com/antonbabenko/terraform-best-practices.git\n", + "%pip install markdown\n", + "%pip install python-frontmatter" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Book if it doesn't exist\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\"name\":\"python-cheatsheet\",\"description\":\"python-cheatsheet\",\"created_by\":1,\"updated_by\":1,\"owned_by\":1,\"slug\":\"python-cheatsheet\",\"updated_at\":\"2022-08-10T00:14:32.000000Z\",\"created_at\":\"2022-08-10T00:14:32.000000Z\",\"id\":3}\n" + ] + } + ], + "source": [ + "import requests\n", + "import os\n", + "\n", + "url = \"https://donavanaldrich.com/api/bookstack/books\"\n", + "\n", + "payload = {\n", + " \"name\":\n", + " \"python-cheatsheet\",\n", + " \"description\":\n", + " \"python-cheatsheet\",\n", + " \"tags\": [{\n", + " \"name\": \"Platform\",\n", + " \"value\": \"Python\"\n", + " }, {\n", + " \"name\": \"Category\",\n", + " \"value\": \"Code\"\n", + " }, {\n", + " \"name\": \"Type\",\n", + " \"value\": \"Cheatsheet\"\n", + " }]\n", + "}\n", + "headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN)\n", + "}\n", + "\n", + "response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + "\n", + "print(response.text)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Chapters\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### List folders to map to chapters\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['cheatsheet', 'modules', 'builtin']\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir = os.listdir(\"../temp/python-cheatsheets\")\n", + "\n", + "print(dir)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "18\n", + "19\n", + "20\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "chapters = ['cheatsheet', 'modules', 'builtin']\n", + "\n", + "url = \"https://donavanaldrich.com/api/bookstack/chapters\"\n", + "\n", + "for chapter_name in chapters:\n", + "\n", + " payload = {\n", + " \"book_id\": 2,\n", + " \"name\": chapter_name,\n", + " \"description\": chapter_name,\n", + " \"tags\": [{\n", + " \"name\": \"chapter\",\n", + " \"value\": chapter_name\n", + " }]\n", + " }\n", + " headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN)\n", + " }\n", + "\n", + " response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + " response_json = response.json()\n", + " chapter_id = response_json['id']\n", + " print(chapter_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Pages\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'X_API_KEY' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/documentation/python-cheatsheet.ipynb Cell 9\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m chapters \u001b[39m=\u001b[39m [\u001b[39m'\u001b[39m\u001b[39mcheatsheet\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39mmodules\u001b[39m\u001b[39m'\u001b[39m, \u001b[39m'\u001b[39m\u001b[39mbuiltin\u001b[39m\u001b[39m'\u001b[39m]\n\u001b[1;32m 7\u001b[0m \u001b[39m# The Python interpreter has a number of functions and types built into it that are always available.\u001b[39;00m\n\u001b[1;32m 8\u001b[0m \u001b[39m# Standard Library\u001b[39;00m\n\u001b[1;32m 9\u001b[0m headers \u001b[39m=\u001b[39m {\n\u001b[0;32m---> 10\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mX-API-KEY\u001b[39m\u001b[39m\"\u001b[39m: os\u001b[39m.\u001b[39mgetenv(X_API_KEY),\n\u001b[1;32m 11\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mAuthorization\u001b[39m\u001b[39m\"\u001b[39m: os\u001b[39m.\u001b[39mgetenv(BOOKSTACK_API_TOKEN)\n\u001b[1;32m 12\u001b[0m }\n\u001b[1;32m 14\u001b[0m url \u001b[39m=\u001b[39m \u001b[39m\"\u001b[39m\u001b[39mhttps://donavanaldrich.com/api/bookstack/books\u001b[39m\u001b[39m\"\u001b[39m\n\u001b[1;32m 16\u001b[0m payload \u001b[39m=\u001b[39m {\n\u001b[1;32m 17\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mname\u001b[39m\u001b[39m\"\u001b[39m:\n\u001b[1;32m 18\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mpython-cheatsheet\u001b[39m\u001b[39m\"\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 30\u001b[0m }]\n\u001b[1;32m 31\u001b[0m }\n", + "\u001b[0;31mNameError\u001b[0m: name 'X_API_KEY' is not defined" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "import requests\n", + "import frontmatter\n", + "\n", + "chapters = ['cheatsheet', 'modules', 'builtin']\n", + "\n", + "headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN)\n", + "}\n", + "\n", + "url = \"https://donavanaldrich.com/api/bookstack/books\"\n", + "\n", + "payload = {\n", + " \"name\":\n", + " \"python-cheatsheet\",\n", + " \"description\":\n", + " \"python-cheatsheet\",\n", + " \"tags\": [{\n", + " \"name\": \"Platform\",\n", + " \"value\": \"Python\"\n", + " }, {\n", + " \"name\": \"Category\",\n", + " \"value\": \"Code\"\n", + " }, {\n", + " \"name\": \"Type\",\n", + " \"value\": \"Cheatsheet\"\n", + " }]\n", + "}\n", + "\n", + "response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + "response_json = response.json()\n", + "book_id = response_json['id']\n", + "\n", + "# book_id = xs\n", + "#\n", + "for chapter in chapters:\n", + " payload = {\n", + " \"book_id\": book_id,\n", + " \"name\": chapter,\n", + " \"description\": chapter,\n", + " \"tags\": [{\n", + " \"name\": \"chapter\",\n", + " \"value\": chapter\n", + " }]\n", + " }\n", + " url = \"https://donavanaldrich.com/api/bookstack/chapters\"\n", + " response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + " response_json = response.json()\n", + " chapter_id = response_json['id']\n", + " print(chapter_id)\n", + "\n", + " for page in Path('../temp/python-cheatsheets/' + chapter).iterdir():\n", + " url = \"https://donavanaldrich.com/api/bookstack/pages\"\n", + " with open(page, 'r') as f:\n", + " metadata, content = frontmatter.parse(f.read())\n", + " try:\n", + " page_name = metadata['title']\n", + " except:\n", + " page_name = str(page)\n", + " payload = {\n", + " \"book_id\": book_id,\n", + " \"chapter_id\": chapter_id,\n", + " \"name\": page_name,\n", + " \"markdown\": content,\n", + " \"tags\": [{\n", + " \"name\": \"chapter\",\n", + " \"value\": chapter\n", + " }]\n", + " }\n", + " response = requests.request(\"POST\",\n", + " url,\n", + " json=payload,\n", + " headers=headers)\n", + "\n", + " # print(response.text)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Development/Sandboxing\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Python Debugging\n", + "\n", + "\n", + " \n", + " Finding and resolving bugs\n", + " \n", + " \n", + " In computer programming and software development, debugging is the process of finding and resolving bugs (defects or problems that prevent correct operation) within computer programs, software, or systems.\n", + " \n", + "\n", + "\n", + "## Raising Exceptions\n", + "\n", + "Exceptions are raised with a raise statement. In code, a raise statement consists of the following:\n", + "\n", + "- The `raise` keyword\n", + "- A call to the `Exception()` function\n", + "- A string with a helpful error message passed to the `Exception()` function\n", + "\n", + "```python\n", + ">>> raise Exception('This is the error message.')\n", + "# Traceback (most recent call last):\n", + "# File \"\", line 1, in \n", + "# raise Exception('This is the error message.')\n", + "# Exception: This is the error message.\n", + "```\n", + "\n", + "Typically, it’s the code that calls the function, not the function itself, that knows how to handle an exception. So, you will commonly see a raise statement inside a function and the `try` and `except` statements in the code calling the function.\n", + "\n", + "```python\n", + ">>> def box_print(symbol, width, height):\n", + "... if len(symbol) != 1:\n", + "... raise Exception('Symbol must be a single character string.')\n", + "... if width <= 2:\n", + "... raise Exception('Width must be greater than 2.')\n", + "... if height <= 2:\n", + "... raise Exception('Height must be greater than 2.')\n", + "... print(symbol * width)\n", + "... for i in range(height - 2):\n", + "... print(symbol + (' ' * (width - 2)) + symbol)\n", + "... print(symbol * width)\n", + "...\n", + ">>> for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)):\n", + "... try:\n", + "... box_print(sym, w, h)\n", + "... except Exception as err:\n", + "... print('An exception happened: ' + str(err))\n", + "...\n", + "# ****\n", + "# * *\n", + "# * *\n", + "# ****\n", + "# OOOOOOOOOOOOOOOOOOOO\n", + "# O O\n", + "# O O\n", + "# O O\n", + "# OOOOOOOOOOOOOOOOOOOO\n", + "# An exception happened: Width must be greater than 2.\n", + "# An exception happened: Symbol must be a single character string.\n", + "```\n", + "\n", + "Read more about [Exception Handling](/cheatsheet/exception-handling).\n", + "\n", + "## Getting the Traceback as a string\n", + "\n", + "The `traceback` is displayed by Python whenever a raised exception goes unhandled. But can also obtain it as a string by calling traceback.format_exc(). This function is useful if you want the information from an exception’s traceback but also want an except statement to gracefully handle the exception. You will need to import Python’s traceback module before calling this function.\n", + "\n", + "```python\n", + ">>> import traceback\n", + "\n", + ">>> try:\n", + "... raise Exception('This is the error message.')\n", + ">>> except:\n", + "... with open('errorInfo.txt', 'w') as error_file:\n", + "... error_file.write(traceback.format_exc())\n", + "... print('The traceback info was written to errorInfo.txt.')\n", + "...\n", + "# 116\n", + "# The traceback info was written to errorInfo.txt.\n", + "```\n", + "\n", + "The 116 is the return value from the `write()` method, since 116 characters were written to the file. The `traceback` text was written to errorInfo.txt.\n", + "\n", + " Traceback (most recent call last):\n", + " File \"\", line 2, in \n", + " Exception: This is the error message.\n", + "\n", + "## Assertions\n", + "\n", + "An assertion is a sanity check to make sure your code isn’t doing something obviously wrong. These sanity checks are performed by `assert` statements. If the sanity check fails, then an `AssertionError` exception is raised. In code, an `assert` statement consists of the following:\n", + "\n", + "- The `assert` keyword\n", + "- A condition (that is, an expression that evaluates to `True` or `False`)\n", + "- A comma\n", + "- A `string` to display when the condition is `False`\n", + "\n", + "```python\n", + ">>> pod_bay_door_status = 'open'\n", + ">>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "\n", + ">>> pod_bay_door_status = 'I\\'m sorry, Dave. I\\'m afraid I can\\'t do that.'\n", + ">>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "# Traceback (most recent call last):\n", + "# File \"\", line 1, in \n", + "# assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "# AssertionError: The pod bay doors need to be \"open\".\n", + "```\n", + "\n", + "In plain English, an assert statement says, “I assert that this condition holds true, and if not, there is a bug somewhere in the program.” Unlike exceptions, your code should not handle assert statements with try and except; if an assert fails, your program should crash. By failing fast like this, you shorten the time between the original cause of the bug and when you first notice the bug. This will reduce the amount of code you will have to check before finding the code that’s causing the bug.\n", + "\n", + "### Disabling Assertions\n", + "\n", + "Assertions can be disabled by passing the `-O` option when running Python.\n", + "\n", + "## Logging\n", + "\n", + "To enable the `logging` module to display log messages on your screen as your program runs, copy the following to the top of your program:\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s')\n", + "```\n", + "\n", + "Say you wrote a function to calculate the factorial of a number. In mathematics, factorial 4 is 1 × 2 × 3 × 4, or 24. Factorial 7 is 1 × 2 × 3 × 4 × 5 × 6 × 7, or 5,040. Open a new file editor window and enter the following code. It has a bug in it, but you will also enter several log messages to help yourself figure out what is going wrong. Save the program as factorialLog.py.\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s')\n", + ">>> logging.debug('Start of program')\n", + "\n", + ">>> def factorial(n):\n", + "... logging.debug('Start of factorial(%s)' % (n))\n", + "... total = 1\n", + "... for i in range(1, n + 1):\n", + "... total *= i\n", + "... logging.debug('i is ' + str(i) + ', total is ' + str(total))\n", + "... logging.debug('End of factorial(%s)' % (n))\n", + "... return total\n", + "...\n", + ">>> print(factorial(5))\n", + ">>> logging.debug('End of program')\n", + "# 2015-05-23 16:20:12,664 - DEBUG - Start of program\n", + "# 2015-05-23 16:20:12,664 - DEBUG - Start of factorial(5)\n", + "# 2015-05-23 16:20:12,665 - DEBUG - i is 0, total is 0\n", + "# 2015-05-23 16:20:12,668 - DEBUG - i is 1, total is 0\n", + "# 2015-05-23 16:20:12,670 - DEBUG - i is 2, total is 0\n", + "# 2015-05-23 16:20:12,673 - DEBUG - i is 3, total is 0\n", + "# 2015-05-23 16:20:12,675 - DEBUG - i is 4, total is 0\n", + "# 2015-05-23 16:20:12,678 - DEBUG - i is 5, total is 0\n", + "# 2015-05-23 16:20:12,680 - DEBUG - End of factorial(5)\n", + "# 0\n", + "# 2015-05-23 16:20:12,684 - DEBUG - End of program\n", + "```\n", + "\n", + "## Logging Levels\n", + "\n", + "Logging levels provide a way to categorize your log messages by importance. There are five logging levels, described in Table 10-1 from least to most important. Messages can be logged at each level using a different logging function.\n", + "\n", + "| Level | Logging Function | Description |\n", + "| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n", + "| `DEBUG` | `logging.debug()` | The lowest level. Used for small details. Usually you care about these messages only when diagnosing problems. |\n", + "| `INFO` | `logging.info()` | Used to record information on general events in your program or confirm that things are working at their point in the program. |\n", + "| `WARNING` | `logging.warning()` | Used to indicate a potential problem that doesn’t prevent the program from working but might do so in the future. |\n", + "| `ERROR` | `logging.error()` | Used to record an error that caused the program to fail to do something. |\n", + "| `CRITICAL` | `logging.critical()` | The highest level. Used to indicate a fatal error that has caused or is about to cause the program to stop running entirely. |\n", + "\n", + "## Disabling Logging\n", + "\n", + "After you’ve debugged your program, you probably don’t want all these log messages cluttering the screen. The logging.disable() function disables these so that you don’t have to go into your program and remove all the logging calls by hand.\n", + "\n", + "```python\n", + ">>> import logging\n", + "\n", + ">>> logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s - %(message)s')\n", + ">>> logging.critical('Critical error! Critical error!')\n", + "# 2015-05-22 11:10:48,054 - CRITICAL - Critical error! Critical error!\n", + "\n", + ">>> logging.disable(logging.CRITICAL)\n", + ">>> logging.critical('Critical error! Critical error!')\n", + ">>> logging.error('Error! Error!')\n", + "```\n", + "\n", + "## Logging to a File\n", + "\n", + "Instead of displaying the log messages to the screen, you can write them to a text file. The `logging.basicConfig()` function takes a filename keyword argument, like so:\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')\n", + "```\n" + ] + } + ], + "source": [ + "# for page in Path('../temp/python-cheatsheets/' + chapter).iterdir():\n", + "page = '../temp/python-cheatsheets/cheatsheet/debugging.md'\n", + "url = \"https://donavanaldrich.com/api/bookstack/pages\"\n", + "\n", + "with open(page, 'r') as f:\n", + " # text = f.read()\n", + " # post = frontmatter.load(page)\n", + " metadata, content = frontmatter.parse(f.read())\n", + "print(content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference\n", + "\n", + "### Payloads\n", + "\n", + "#### book\n", + "\n", + "```json\n", + "{\n", + " \"name\": \"python-cheatsheet\",\n", + " \"description\": \"python-cheatsheet\",\n", + " \"tags\": [\n", + " { \"name\": \"Platform\", \"value\": \"Python\" },\n", + " { \"name\": \"Category\", \"value\": \"Code\" },\n", + " { \"name\": \"Type\", \"value\": \"Cheatsheet\" }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "#### Chapter\n", + "\n", + "```json\n", + "{\n", + " \"book_id\": 1,\n", + " \"name\": \"My fantastic new chapter\",\n", + " \"description\": \"This is a great new chapter that I've created via the API\",\n", + " \"tags\": [\n", + " { \"name\": \"Category\", \"value\": \"Top Content\" },\n", + " { \"name\": \"Rating\", \"value\": \"Highest\" }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "#### Page\n", + "\n", + "```json\n", + "{\n", + " \"book_id\": 1,\n", + " \"name\": \"My API Page\",\n", + " \"markdown\": \"

my new API page

\",\n", + " \"tags\": [\n", + " { \"name\": \"Category\", \"value\": \"Not Bad Content\" },\n", + " { \"name\": \"Rating\", \"value\": \"Average\" }\n", + " ]\n", + "}\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'frontmatter' has no attribute 'load'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/documentation/python-cheatsheet.ipynb Cell 19\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m file_parts \u001b[39m=\u001b[39m frontmatter\u001b[39m.\u001b[39mload(filepath)\n\u001b[1;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m {\n\u001b[1;32m 9\u001b[0m \u001b[39m# 'html': markdown_parser.convert(file_parts.content),\u001b[39;00m\n\u001b[1;32m 10\u001b[0m \u001b[39m'\u001b[39m\u001b[39mmetadata\u001b[39m\u001b[39m'\u001b[39m: file_parts\u001b[39m.\u001b[39mmetadata\n\u001b[1;32m 11\u001b[0m }\n\u001b[0;32m---> 13\u001b[0m parse_markdown(\u001b[39m'\u001b[39;49m\u001b[39m../temp/python-cheatsheets/cheatsheet/debugging.md\u001b[39;49m\u001b[39m'\u001b[39;49m)\n", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/documentation/python-cheatsheet.ipynb Cell 19\u001b[0m in \u001b[0;36mparse_markdown\u001b[0;34m(filepath)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mparse_markdown\u001b[39m(filepath):\n\u001b[1;32m 5\u001b[0m \u001b[39m# markdown_parser = markdown.Markdown(extensions=markdown_extensions)\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m file_parts \u001b[39m=\u001b[39m frontmatter\u001b[39m.\u001b[39;49mload(filepath)\n\u001b[1;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m {\n\u001b[1;32m 9\u001b[0m \u001b[39m# 'html': markdown_parser.convert(file_parts.content),\u001b[39;00m\n\u001b[1;32m 10\u001b[0m \u001b[39m'\u001b[39m\u001b[39mmetadata\u001b[39m\u001b[39m'\u001b[39m: file_parts\u001b[39m.\u001b[39mmetadata\n\u001b[1;32m 11\u001b[0m }\n", + "\u001b[0;31mAttributeError\u001b[0m: module 'frontmatter' has no attribute 'load'" + ] + } + ], + "source": [ + "import frontmatter # https://pypi.org/project/python-frontmatter/\n", + "import markdown\n", + "\n", + "\n", + "def parse_markdown(filepath):\n", + " # markdown_parser = markdown.Markdown(extensions=markdown_extensions)\n", + " file_parts = frontmatter.load(filepath)\n", + "\n", + " return {\n", + " # 'html': markdown_parser.convert(file_parts.content),\n", + " 'metadata': file_parts.metadata\n", + " }\n", + "\n", + "\n", + "parse_markdown('../temp/python-cheatsheets/cheatsheet/debugging.md')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.9 64-bit", + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/documentation/terraform-cheatsheet.ipynb b/documentation/terraform-cheatsheet.ipynb new file mode 100644 index 0000000..144a769 --- /dev/null +++ b/documentation/terraform-cheatsheet.ipynb @@ -0,0 +1,2485 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running `brew update --auto-update`...\n", + "\u001b[34m==>\u001b[0m \u001b[1mAuto-updated Homebrew!\u001b[0m\n", + "Updated 1 tap (homebrew/core).\n", + "\n", + "You have \u001b[1m19\u001b[0m outdated formulae installed.\n", + "You can upgrade them with \u001b[1mbrew upgrade\u001b[0m\n", + "or list them with \u001b[1mbrew outdated\u001b[0m.\n", + "\n", + "\u001b[33mWarning:\u001b[0m fetch 0.4.5 is already installed and up-to-date.\n", + "To reinstall 0.4.5, run:\n", + " brew reinstall fetch\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: requests-html in /home/donaldrich/.local/lib/python3.10/site-packages (0.10.0)\n", + "Requirement already satisfied: fake-useragent in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (0.1.11)\n", + "Requirement already satisfied: pyquery in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (1.4.3)\n", + "Requirement already satisfied: pyppeteer>=0.0.14 in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (1.0.2)\n", + "Requirement already satisfied: bs4 in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (0.0.1)\n", + "Requirement already satisfied: w3lib in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (1.22.0)\n", + "Requirement already satisfied: requests in /usr/lib/python3/dist-packages (from requests-html) (2.25.1)\n", + "Requirement already satisfied: parse in /home/donaldrich/.local/lib/python3.10/site-packages (from requests-html) (1.19.0)\n", + "Requirement already satisfied: urllib3<2.0.0,>=1.25.8 in /usr/lib/python3/dist-packages (from pyppeteer>=0.0.14->requests-html) (1.26.5)\n", + "Requirement already satisfied: tqdm<5.0.0,>=4.42.1 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (4.64.0)\n", + "Requirement already satisfied: pyee<9.0.0,>=8.1.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (8.2.2)\n", + "Requirement already satisfied: websockets<11.0,>=10.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (10.3)\n", + "Requirement already satisfied: appdirs<2.0.0,>=1.4.3 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (1.4.3)\n", + "Requirement already satisfied: certifi>=2021 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (2022.6.15)\n", + "Requirement already satisfied: importlib-metadata>=1.4 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyppeteer>=0.0.14->requests-html) (4.12.0)\n", + "Requirement already satisfied: beautifulsoup4 in /home/donaldrich/.local/lib/python3.10/site-packages (from bs4->requests-html) (4.11.1)\n", + "Requirement already satisfied: cssselect>0.7.9 in /home/donaldrich/.local/lib/python3.10/site-packages (from pyquery->requests-html) (1.1.0)\n", + "Requirement already satisfied: lxml>=2.1 in /usr/lib/python3/dist-packages (from pyquery->requests-html) (4.8.0)\n", + "Requirement already satisfied: six>=1.4.1 in /usr/lib/python3/dist-packages (from w3lib->requests-html) (1.16.0)\n", + "Requirement already satisfied: zipp>=0.5 in /usr/lib/python3/dist-packages (from importlib-metadata>=1.4->pyppeteer>=0.0.14->requests-html) (1.0.0)\n", + "Requirement already satisfied: soupsieve>1.2 in /home/donaldrich/.local/lib/python3.10/site-packages (from beautifulsoup4->bs4->requests-html) (2.3.2.post1)\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: html2text in /home/donaldrich/.local/lib/python3.10/site-packages (2020.1.16)\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: markdown in /home/donaldrich/.local/lib/python3.10/site-packages (3.4.1)\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: python-frontmatter in /home/donaldrich/.local/lib/python3.10/site-packages (1.0.0)\n", + "Requirement already satisfied: PyYAML in /home/donaldrich/.local/lib/python3.10/site-packages (from python-frontmatter) (5.1)\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: readability-lxml in /home/donaldrich/.local/lib/python3.10/site-packages (0.8.1)\n", + "Requirement already satisfied: lxml in /usr/lib/python3/dist-packages (from readability-lxml) (4.8.0)\n", + "Requirement already satisfied: cssselect in /home/donaldrich/.local/lib/python3.10/site-packages (from readability-lxml) (1.1.0)\n", + "Requirement already satisfied: chardet in /usr/lib/python3/dist-packages (from readability-lxml) (4.0.0)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "!brew install fetch\n", + "!mkdir temp\n", + "\n", + "%pip install requests-html\n", + "%pip install html2text\n", + "%pip install markdown\n", + "%pip install python-frontmatter\n", + "%pip install readability-lxml" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "url = \"https://spacelift.io/blog/terraform-commands-cheat-sheet\"\n", + "https://spacelift.io/blog/terraform\n", + "https://res.cloudinary.com/acloud-guru/image/fetch/c_thumb,f_auto,q_auto/https://acg-wordpress-content-production.s3.us-west-2.amazonaws.com/app/uploads/2020/11/terraform-cheatsheet-from-ACG.pdf\n", + "https://www.techbeatly.com/terraform-cheat-sheet\n", + "https://jayendrapatil.com/terraform-cheat-sheet/\n", + "https://github.com/antonbabenko/terraform-best-practices.git\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 151.101.129.137:443...\n", + "* Connected to res.cloudinary.com (151.101.129.137) port 443 (#0)\n", + "* ALPN, offering h2\n", + "* ALPN, offering http/1.1\n", + "* CAfile: /etc/ssl/certs/ca-certificates.crt\n", + "* CApath: /etc/ssl/certs\n", + "* TLSv1.0 (OUT), TLS header, Certificate Status (22):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS handshake, Client hello (1):\n", + "} [512 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Certificate Status (22):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Server hello (2):\n", + "{ [122 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Finished (20):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):\n", + "{ [19 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Certificate (11):\n", + "{ [2999 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, CERT verify (15):\n", + "{ [264 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Finished (20):\n", + "{ [52 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Finished (20):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):\n", + "} [1 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS handshake, Finished (20):\n", + "} [52 bytes data]\n", + "* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384\n", + "* ALPN, server accepted to use h2\n", + "* Server certificate:\n", + "* subject: C=IL; L=Petah Tikva; O=Cloudinary Ltd; CN=*.cloudinary.com\n", + "* start date: May 30 06:40:39 2022 GMT\n", + "* expire date: Jul 1 06:40:39 2023 GMT\n", + "* subjectAltName: host \"res.cloudinary.com\" matched cert's \"*.cloudinary.com\"\n", + "* issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure Certificate Authority - G2\n", + "* SSL certificate verify ok.\n", + "* Using HTTP2, server supports multiplexing\n", + "* Connection state changed (HTTP/2 confirmed)\n", + "* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* Using Stream ID: 1 (easy handle 0x55cc70ee6e80)\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "> GET /acloud-guru/image/fetch/c_thumb,f_auto,q_auto/https://acg-wordpress-content-production.s3.us-west-2.amazonaws.com/app/uploads/2020/11/terraform-cheatsheet-from-ACG.pdf HTTP/2\n", + "> Host: res.cloudinary.com\n", + "> user-agent: curl/7.81.0\n", + "> accept: */*\n", + "> \n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):\n", + "{ [193 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "< HTTP/2 200 \n", + "< content-type: image/png\n", + "< etag: \"4c05202557d1f3a51bac32d0ca142fe8\"\n", + "< last-modified: Mon, 23 Nov 2020 21:34:47 GMT\n", + "< date: Wed, 10 Aug 2022 09:16:25 GMT\n", + "< vary: Save-Data\n", + "< strict-transport-security: max-age=604800\n", + "< cache-control: private, no-transform, immutable, max-age=604800\n", + "< server-timing: fastly;dur=2;cpu=1;start=2022-08-10T09:16:25.962Z;desc=hit,rtt;dur=92\n", + "< server: Cloudinary\n", + "< timing-allow-origin: *\n", + "< access-control-allow-origin: *\n", + "< accept-ranges: bytes\n", + "< x-content-type-options: nosniff\n", + "< access-control-expose-headers: Content-Length,ETag,Server-Timing,Vary,X-Content-Type-Options\n", + "< content-length: 432177\n", + "< \n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "100 422k 100 422k 0 0 1685k 0 --:--:-- --:--:-- --:--:-- 1688k\n", + "* Connection #0 to host res.cloudinary.com left intact\n" + ] + } + ], + "source": [ + "!curl -fLvo tf.pdf https://res.cloudinary.com/acloud-guru/image/fetch/c_thumb,f_auto,q_auto/https://acg-wordpress-content-production.s3.us-west-2.amazonaws.com/app/uploads/2020/11/terraform-cheatsheet-from-ACG.pdf" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Terraform Cheat Sheet – techbeatly\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + " \n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "Skip to content\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\"\"
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

Terraform Cheat Sheet

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

Gineesh Mada Pparambath
\n", + "

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

\n", + "

\"\"

What is Terraform

\n", + "

Terraform is an open-source software tool to manage end to end lifecycle of your IT infrastructure. Terraform provides a consistent CLI workflow to manage hundreds of cloud services.

\n", + "

Latest Terraform Articles

\n", + "

Installing Terraform

\n", + "

You can download the Terraform software from HashiCorp’s download page and use native installation methods for your operating system. Also you can install Terraform using the package managers like yum, apt, homebrew, Chocolatey (choco) etc. Refer install Terraform page for the appropriate method for your operating system.

\n", + "
\"\"

Terraform CLI Cheat Sheet

\n", + "

Please note, this cheat sheet is a living document and I will make changes whenever there is an update or changes in the Terraform CLI options or versions. This cheat sheet does not written in an alphabetical order or based on workflow.

\n", + "

Planning HashiCorp Certified Terraform Associate Certification ? Watch the video for details.

\n", + "
\n", + "
\n", + "

terraform version

\n", + "
$ terraform version\n",
+      "Terraform v1.0.1\n",
+      "on darwin_amd64
\n", + "

terraform init

\n", + "
$ terraform init
\n", + "

Ask for input if necessary. If false, will error if input was required.

\n", + "
$ terraform init -input=false
\n", + "

You can also change the backend details using -backend-config option. -reconfigure will reconfigure the backend, ignoring any saved configuration.

\n", + "
$ terraform init -backend-config=PATH/TO/CONFIGURATION_FILE -reconfigure
\n", + "

terraform plan

\n", + "

The plan will check the configuration files (basically all the *.tf files in the directory) and will show you the items or changes going to made on target infrastructure or resources. Please note, this command will not actually perform the planned actions.

\n", + "
$ terraform plan
\n", + "

You can optionally save the plan to a file, which you can then pass to the apply command to perform exactly the actions described in the plan.

\n", + "
$ terraform plan -out plan.out
\n", + "

terraform get

\n", + "

Downloads and installs modules needed for the configuration given by PATH. get recursively downloads all modules needed, such as modules imported by modules imported by the root and so on. Module installation also happens automatically by default as part of
the “terraform init” command, so you should rarely need to run this command separately.

\n", + "
$ terraform get
\n", + "

You can update the already downloaded modules using -update=true option.

\n", + "
$ terraform get -update=true
\n", + "

terraform apply

\n", + "

apply will do the actual operation on the infrastructure resources. apply will show the plan and actions in detail.

\n", + "
$ terraform apply
\n", + "

apply will ask for your confirmation to proceed with changes. You can use -auto-approve for auto-confirmation.

\n", + "
$ terraform apply -auto-approve
\n", + "

You can pass different variables or variable files.

\n", + "
$ terraform plan -var=\"instancetype=t2.small\"\n",
+      "$ terraform plan -var-file=\"custom.tfvars
\n", + "

You can use -target option to target specific resources, modules, or collections of resources.

\n", + "
$ terraform apply -target=\"aws_s3_bucket_object.objects\"
\n", + "

terraform destroy

\n", + "

Warning: destroy will delete all resource but with confirmation.

\n", + "
$ terraform destroy
\n", + "

You can create a deletion plan as below.

\n", + "
$ terraform plan –destroy
\n", + "

Use the -target to destroy a specific resource.

\n", + "
$ terraform destroy -target=\"aws_s3_bucket_object.objects\"
\n", + "

Also note, you can comment out the resource, then terraform will detect it as not part of config and will remove when you do plan or apply.

\n", + "

terraform refresh

\n", + "

You can update the terraform state file with metadata that matches the physical resources they are tracking.

\n", + "
$ terraform refresh
\n", + "

terraform show

\n", + "

Show the terraform state information in a human readable format. You can also use it for displaying information from plan file.

\n", + "
$ terraform show
\n", + "

terraform validate

\n", + "

You can check the syntax and validate the configuration using validate subcommand.

\n", + "
$ terraform validate\n",
+      "Success! The configuration is valid.
\n", + "

terraform providers

\n", + "

You can see the providers in use by the modules and configurations in your Terraform files.

\n", + "
$ terraform providers\n",
+      "\n",
+      "Providers required by configuration:\n",
+      ".\n",
+      "└── provider[registry.terraform.io/hashicorp/aws]
\n", + "

terraform state

\n", + "

terraform state has multiple subcommands to manage the terraform state. You can move, rm (delete), list or show the resource state.

\n", + "
Subcommands:\n",
+      "    list                List resources in the state\n",
+      "    mv                  Move an item in the state\n",
+      "    pull                Pull current state and output to stdout\n",
+      "    push                Update remote state from a local state file\n",
+      "    replace-provider    Replace provider in the state\n",
+      "    rm                  Remove instances from the state\n",
+      "    show                Show a resource in the state
\n", + "

Example usages

\n", + "
# List state\n",
+      "$ terraform state list\n",
+      "aws_iam_user.lb\n",
+      "aws_instance.myec2\n",
+      "\n",
+      "# Show resource\n",
+      "$ terraform state show aws_instance.myec2\n",
+      "\n",
+      "# Push terraform state to remote backend\n",
+      "$ tarraform state push\n",
+      "\n",
+      "# Pull the remote terraform state to a local copy\n",
+      "$ terraform state pull > terraform.tfstate\n",
+      "\n",
+      "# Update and tell terraform that packet_device.worker has been renamed to packet_device.helper\n",
+      "$ terraform state mv packet_device.worker packet_device.helper\n",
+      "\n",
+      "# Move the resource block into the child module configuration\n",
+      "$ terraform state mv packet_device.worker module.worker.packet_device.worker\n",
+      "\n",
+      "# Remove the resource from state but it will not remove the resource from cloud/provider.\n",
+      "$ terraform state rm aws_instance.myec2
\n", + "

Remove the resource from state but it will not remove the resource from cloud/provider. But next time when you run terraform plan or apply, Terraform will recreate the instance as again as the resource definition is still there.

\n", + "
$ terraform state rm aws_instance.myec2\n",
+      "Removed aws_instance.myec2\n",
+      "Successfully removed 1 resource instance(s).
\n", + "

terraform graph

\n", + "

graph will generate the visual graph of your infrastructure based on Terraform configuration files.

\n", + "

Outputs the visual execution graph of Terraform resources according to
either the current configuration or an execution plan.

\n", + "
$ terraform graph
\n", + "

The output of terraform graph will be in DOT format and you can use tools like dot to generate image files from dot files.

\n", + "
sudo apt-get install graphviz\n",
+      "# or \n",
+      "sudo yum install graphviz\n",
+      "\n",
+      "$ terraform graph | dot –Tpng > graph.png
\n", + "

terraform fmt

\n", + "

Rewrites all Terraform configuration files to a canonical format with appropriate indentation and styling. (JSON files (.tf.json or .tfvars.json) are not modified.)

\n", + "
$ terraform fmt
\n", + "

terraform taint

\n", + "

You can manually mark a terraform managed resource as tainted and forcing it to be destroyed and recreated on the next apply. terraform taint command will make modification in the tfstate file and recreate action will happen in next apply. Please note, terraform taint command will not modify the .tf file or the infrastructure.

\n", + "
$ terraform taint aws_instance.myec2
\n", + "

terraform import

\n", + "

You can import your existing infrastructure into Terraform and manage using Terraform.

\n", + "
# Importing VMWare VM to terraform\n",
+      "$ terraform import vsphere_virtual_machine.vm /DC1/vm/DEV/DEV2
\n", + "

Read our detailed guide: How to Import Existing VMWare VM’s into Terraform

\n", + "

terraform workspaces

\n", + "

Terraform Workspaces will help to manage same terraform configurations for different environments (eg: dev, staging, production) in the same project directory.

\n", + "
# Check the workspace\n",
+      "$ terraform workspace show\n",
+      "default\n",
+      "\n",
+      "# Create new workspace\n",
+      "$ terraform workspace new dev\n",
+      "Created and switched to workspace \"dev\"!\n",
+      "\n",
+      "# List all workspaces\n",
+      "$ terraform workspace list\n",
+      "  default\n",
+      "* dev\n",
+      "\n",
+      "# Switch to a specific workspace\n",
+      "$ terraform workspace select dev\n",
+      "Switched to workspace \"dev\".
\n", + "

Terraform will create separate terraform.tfstate files in terraform.tfstate.d/WORKSPACE_NAME/ directories in the project directory.

\n", + "
$ tree terraform.tfstate.d/\n",
+      "terraform.tfstate.d/\n",
+      "├── dev\n",
+      "│   └── terraform.tfstate\n",
+      "├── prod\n",
+      "└── stage\n",
+      "    └── terraform.tfstate\n",
+      "\n",
+      "3 directories, 2 files
\n", + "

You can use ${terraform.workspace} interpolation to dynamically use the workspace name inside your terraform configuration (*.tf). Eg: you can use it for selecting instance type from an array based on workspace.

\n", + "
resource \"aws_instance\" \"myec2\" {\n",
+      "  ami           = \"ami-0cd31be676780afa7\"\n",
+      "  instance_type = lookup(var.instance_type,terraform.workspace)\n",
+      "\n",
+      "}\n",
+      "variable \"instance_type\" {\n",
+      " type = map\n",
+      "\n",
+      " default = {\n",
+      "   default = \"t2.nano\"\n",
+      "   stage = \"t2.nano\"\n",
+      "   dev = \"t2.micro\"\n",
+      "   prod = \"t2.large\"\n",
+      " }\n",
+      "}
\n", + "

Or you can use this ${terraform.workspace} for tagging the instance.

\n", + "
resource \"aws_instance\" \"example\" {\n",
+      "  # ... other arguments\n",
+      "  tags = {\n",
+      "    Name = \"web-${terraform.workspace}\"\n",
+      "  }\n",
+      "}
\n", + "

Refer Terraform Workspaces documentation for more details.

\n", + "

\n", + "

Terraform Default Plugin Directories

\n", + "
  • Windows: %APPDATA%\\terraform.d\\plugins
  • All other systems: ~/.terraform.d/plugins

Terraform Variable Assignment

\n", + "

You can pass variables to Terraform in different methods.

\n", + "

1. Environment variables – with a prefix TF_VAR_

\n", + "
$ export TF_VAR_instance_type=t2.micro
\n", + "

2. Command Line Flags

\n", + "
$ terraform plan -var=\"instancetype=t2.small\"
\n", + "

3. From a variable file – use terraform.tfvars – terraform will load all variables from this file. If different var files to be used then,

\n", + "
$ terraform plan -var-file=\"custom.tfvars
\n", + "

4. Variable Defaults – can keep variable default in another .tf file.

\n", + "
$ cat variables.tf\n",
+      "variable \"my_ip\" {\n",
+      "default = \"10.1.10.10/32\"\n",
+      "}
\n", + "
  • if no value mentioned, then default value will be used.
  • if default value not defined, then terraform will ask for variable when you do apply or plan operation.

Resources

\n", + "\n", + "
\n", + "\n", + "

\n", + "
\n", + "
\n", + "

Disclaimer: The views expressed and the content shared are those of the author and do not reflect the views of the author's employer or techbeatly platform.

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

Latest posts

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

Gineesh Mada Pparambath
\n", + "
Gineesh has worked as a Systems Engineer, Automation Specialist, and content author. His primary focus is on Ansible Automation, Containerisation (OpenShift & Kubernetes), and Infrastructure as Code (Terraform).\n", + "(aka Gini Gangadharan - iamgini.com)

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "

Comments

\n", + "
\n", + "

\n", + "

\n", + "

7 Responses

\n", + "
\n", + "
    \n", + "
  1. \n", + "
    \n", + "\n", + "\n", + "

    […] https://www.techbeatly.com/terraform-cheat-sheet/ […]

    \n", + "
    \n", + "\n", + "
    \n", + "
    \n", + "\n", + "
    \n", + "
  2. \n", + "
  3. \n", + "
    \n", + "
    \n", + " alexis says:
    \n", + "\n", + "\n", + "
    \n", + "\n", + "
  4. \n", + "
  5. \n", + "
    \n", + "
    \n", + " Vergnaud Damien says:
    \n", + "\n", + "

    Hey, Thank you dude for this usefull piece of information 🙂

    \n", + "

    Would you consider adding :
    \n", + "${terraform.workspace} variable inside your “terraform workspace” section ?

    \n", + "

    This variable is SOOO usefull, because you have multiples .tfstate … right … but you only have 1 .tf plan.

    \n", + "

    So you need sometimes to changes small things according to the workspace you’re in, right ? (I said small things, not the whole stack)
    \n", + "At least the name of the instances or the VPC you are connected to or others things like that.

    \n", + "

    I recommand you to make a small mention of it if you share my though 🙂
    \n", + "https://www.terraform.io/docs/language/state/workspaces.html

    \n", + "

    Have a great day 🙂

    \n", + "
    \n", + "\n", + "
    \n", + "
    \n", + "\n", + "
    \n", + "
      \n", + "
    • \n", + "
      \n", + "
      \n", + " Gineesh Madapparambath says:
      \n", + "\n", + "

      Thank you so much Damien,
      \n", + "Appreciate your suggestion and feedback. I have added the same under Workspace section.

      \n", + "
      \n", + "\n", + "
      \n", + "
      \n", + "\n", + "
      \n", + "
    • \n", + "
    \n", + "
  6. \n", + "
  7. \n", + "
    \n", + "
    \n", + " Vaishnavi says:
    \n", + "\n", + "

    thanks for the great article!!

    \n", + "
    \n", + "\n", + "
    \n", + "
    \n", + "\n", + "
    \n", + "
  8. \n", + "
  9. \n", + "
    \n", + "\n", + "\n", + "

    […] Terraform? Check this Terraform cheat sheet for quick […]

    \n", + "
    \n", + "\n", + "
    \n", + "
    \n", + "\n", + "
    \n", + "
  10. \n", + "
\n", + "
\n", + "
\n", + "

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

\n", + "

\n", + "

\n", + "

\n", + "\n", + "

\n", + "

This site uses Akismet to reduce spam. Learn how your comment data is processed.

\n", + "

\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "\n", + "
\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
%d bloggers like this:
    \n", + "\n", + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "from requests_html import HTMLSession\n", + "\n", + "session = HTMLSession()\n", + "r = session.get(\"https://www.techbeatly.com/terraform-cheat-sheet\")\n", + "\n", + "print(r.text)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Terraform Cheat Sheet - techbeatly\n", + "
    \n", + "

    \n", + "

    \"\"

    What is Terraform

    \n", + "

    Terraform is an open-source software tool to manage end to end lifecycle of your IT infrastructure. Terraform provides a consistent CLI workflow to manage hundreds of cloud services.

    \n", + "

    Latest Terraform Articles

    \n", + "

    Installing Terraform

    \n", + "

    You can download the Terraform software from HashiCorp’s download page and use native installation methods for your operating system. Also you can install Terraform using the package managers like yum, apt, homebrew, Chocolatey (choco) etc. Refer install Terraform page for the appropriate method for your operating system.

    \n", + "
    \"\"

    Terraform CLI Cheat Sheet

    \n", + "

    Please note, this cheat sheet is a living document and I will make changes whenever there is an update or changes in the Terraform CLI options or versions. This cheat sheet does not written in an alphabetical order or based on workflow.

    \n", + "

    Planning HashiCorp Certified Terraform Associate Certification ? Watch the video for details.

    \n", + "

    terraform version

    \n", + "
    $ terraform version\n",
    +      "Terraform v1.0.1\n",
    +      "on darwin_amd64
    \n", + "

    terraform init

    \n", + "
    $ terraform init
    \n", + "

    Ask for input if necessary. If false, will error if input was required.

    \n", + "
    $ terraform init -input=false
    \n", + "

    You can also change the backend details using -backend-config option. -reconfigure will reconfigure the backend, ignoring any saved configuration.

    \n", + "
    $ terraform init -backend-config=PATH/TO/CONFIGURATION_FILE -reconfigure
    \n", + "

    terraform plan

    \n", + "

    The plan will check the configuration files (basically all the *.tf files in the directory) and will show you the items or changes going to made on target infrastructure or resources. Please note, this command will not actually perform the planned actions.

    \n", + "
    $ terraform plan
    \n", + "

    You can optionally save the plan to a file, which you can then pass to the apply command to perform exactly the actions described in the plan.

    \n", + "
    $ terraform plan -out plan.out
    \n", + "

    terraform get

    \n", + "

    Downloads and installs modules needed for the configuration given by PATH. get recursively downloads all modules needed, such as modules imported by modules imported by the root and so on. Module installation also happens automatically by default as part of
    the “terraform init” command, so you should rarely need to run this command separately.

    \n", + "
    $ terraform get
    \n", + "

    You can update the already downloaded modules using -update=true option.

    \n", + "
    $ terraform get -update=true
    \n", + "

    terraform apply

    \n", + "

    apply will do the actual operation on the infrastructure resources. apply will show the plan and actions in detail.

    \n", + "
    $ terraform apply
    \n", + "

    apply will ask for your confirmation to proceed with changes. You can use -auto-approve for auto-confirmation.

    \n", + "
    $ terraform apply -auto-approve
    \n", + "

    You can pass different variables or variable files.

    \n", + "
    $ terraform plan -var=\"instancetype=t2.small\"\n",
    +      "$ terraform plan -var-file=\"custom.tfvars
    \n", + "

    You can use -target option to target specific resources, modules, or collections of resources.

    \n", + "
    $ terraform apply -target=\"aws_s3_bucket_object.objects\"
    \n", + "

    terraform destroy

    \n", + "

    Warning: destroy will delete all resource but with confirmation.

    \n", + "
    $ terraform destroy
    \n", + "

    You can create a deletion plan as below.

    \n", + "
    $ terraform plan –destroy
    \n", + "

    Use the -target to destroy a specific resource.

    \n", + "
    $ terraform destroy -target=\"aws_s3_bucket_object.objects\"
    \n", + "

    Also note, you can comment out the resource, then terraform will detect it as not part of config and will remove when you do plan or apply.

    \n", + "

    terraform refresh

    \n", + "

    You can update the terraform state file with metadata that matches the physical resources they are tracking.

    \n", + "
    $ terraform refresh
    \n", + "

    terraform show

    \n", + "

    Show the terraform state information in a human readable format. You can also use it for displaying information from plan file.

    \n", + "
    $ terraform show
    \n", + "

    terraform validate

    \n", + "

    You can check the syntax and validate the configuration using validate subcommand.

    \n", + "
    $ terraform validate\n",
    +      "Success! The configuration is valid.
    \n", + "

    terraform providers

    \n", + "

    You can see the providers in use by the modules and configurations in your Terraform files.

    \n", + "
    $ terraform providers\n",
    +      "\n",
    +      "Providers required by configuration:\n",
    +      ".\n",
    +      "└── provider[registry.terraform.io/hashicorp/aws]
    \n", + "

    terraform state

    \n", + "

    terraform state has multiple subcommands to manage the terraform state. You can move, rm (delete), list or show the resource state.

    \n", + "
    Subcommands:\n",
    +      "    list                List resources in the state\n",
    +      "    mv                  Move an item in the state\n",
    +      "    pull                Pull current state and output to stdout\n",
    +      "    push                Update remote state from a local state file\n",
    +      "    replace-provider    Replace provider in the state\n",
    +      "    rm                  Remove instances from the state\n",
    +      "    show                Show a resource in the state
    \n", + "

    Example usages

    \n", + "
    # List state\n",
    +      "$ terraform state list\n",
    +      "aws_iam_user.lb\n",
    +      "aws_instance.myec2\n",
    +      "\n",
    +      "# Show resource\n",
    +      "$ terraform state show aws_instance.myec2\n",
    +      "\n",
    +      "# Push terraform state to remote backend\n",
    +      "$ tarraform state push\n",
    +      "\n",
    +      "# Pull the remote terraform state to a local copy\n",
    +      "$ terraform state pull > terraform.tfstate\n",
    +      "\n",
    +      "# Update and tell terraform that packet_device.worker has been renamed to packet_device.helper\n",
    +      "$ terraform state mv packet_device.worker packet_device.helper\n",
    +      "\n",
    +      "# Move the resource block into the child module configuration\n",
    +      "$ terraform state mv packet_device.worker module.worker.packet_device.worker\n",
    +      "\n",
    +      "# Remove the resource from state but it will not remove the resource from cloud/provider.\n",
    +      "$ terraform state rm aws_instance.myec2
    \n", + "

    Remove the resource from state but it will not remove the resource from cloud/provider. But next time when you run terraform plan or apply, Terraform will recreate the instance as again as the resource definition is still there.

    \n", + "
    $ terraform state rm aws_instance.myec2\n",
    +      "Removed aws_instance.myec2\n",
    +      "Successfully removed 1 resource instance(s).
    \n", + "

    terraform graph

    \n", + "

    graph will generate the visual graph of your infrastructure based on Terraform configuration files.

    \n", + "

    Outputs the visual execution graph of Terraform resources according to
    either the current configuration or an execution plan.

    \n", + "
    $ terraform graph
    \n", + "

    The output of terraform graph will be in DOT format and you can use tools like dot to generate image files from dot files.

    \n", + "
    sudo apt-get install graphviz\n",
    +      "# or \n",
    +      "sudo yum install graphviz\n",
    +      "\n",
    +      "$ terraform graph | dot –Tpng > graph.png
    \n", + "

    terraform fmt

    \n", + "

    Rewrites all Terraform configuration files to a canonical format with appropriate indentation and styling. (JSON files (.tf.json or .tfvars.json) are not modified.)

    \n", + "
    $ terraform fmt
    \n", + "

    terraform taint

    \n", + "

    You can manually mark a terraform managed resource as tainted and forcing it to be destroyed and recreated on the next apply. terraform taint command will make modification in the tfstate file and recreate action will happen in next apply. Please note, terraform taint command will not modify the .tf file or the infrastructure.

    \n", + "
    $ terraform taint aws_instance.myec2
    \n", + "

    terraform import

    \n", + "

    You can import your existing infrastructure into Terraform and manage using Terraform.

    \n", + "
    # Importing VMWare VM to terraform\n",
    +      "$ terraform import vsphere_virtual_machine.vm /DC1/vm/DEV/DEV2
    \n", + "

    Read our detailed guide: How to Import Existing VMWare VM’s into Terraform

    \n", + "

    terraform workspaces

    \n", + "

    Terraform Workspaces will help to manage same terraform configurations for different environments (eg: dev, staging, production) in the same project directory.

    \n", + "
    # Check the workspace\n",
    +      "$ terraform workspace show\n",
    +      "default\n",
    +      "\n",
    +      "# Create new workspace\n",
    +      "$ terraform workspace new dev\n",
    +      "Created and switched to workspace \"dev\"!\n",
    +      "\n",
    +      "# List all workspaces\n",
    +      "$ terraform workspace list\n",
    +      "  default\n",
    +      "* dev\n",
    +      "\n",
    +      "# Switch to a specific workspace\n",
    +      "$ terraform workspace select dev\n",
    +      "Switched to workspace \"dev\".
    \n", + "

    Terraform will create separate terraform.tfstate files in terraform.tfstate.d/WORKSPACE_NAME/ directories in the project directory.

    \n", + "
    $ tree terraform.tfstate.d/\n",
    +      "terraform.tfstate.d/\n",
    +      "├── dev\n",
    +      "│   └── terraform.tfstate\n",
    +      "├── prod\n",
    +      "└── stage\n",
    +      "    └── terraform.tfstate\n",
    +      "\n",
    +      "3 directories, 2 files
    \n", + "

    You can use ${terraform.workspace} interpolation to dynamically use the workspace name inside your terraform configuration (*.tf). Eg: you can use it for selecting instance type from an array based on workspace.

    \n", + "
    resource \"aws_instance\" \"myec2\" {\n",
    +      "  ami           = \"ami-0cd31be676780afa7\"\n",
    +      "  instance_type = lookup(var.instance_type,terraform.workspace)\n",
    +      "\n",
    +      "}\n",
    +      "variable \"instance_type\" {\n",
    +      " type = map\n",
    +      "\n",
    +      " default = {\n",
    +      "   default = \"t2.nano\"\n",
    +      "   stage = \"t2.nano\"\n",
    +      "   dev = \"t2.micro\"\n",
    +      "   prod = \"t2.large\"\n",
    +      " }\n",
    +      "}
    \n", + "

    Or you can use this ${terraform.workspace} for tagging the instance.

    \n", + "
    resource \"aws_instance\" \"example\" {\n",
    +      "  # ... other arguments\n",
    +      "  tags = {\n",
    +      "    Name = \"web-${terraform.workspace}\"\n",
    +      "  }\n",
    +      "}
    \n", + "

    Refer Terraform Workspaces documentation for more details.

    \n", + "

    \n", + "

    Terraform Default Plugin Directories

    \n", + "
    • Windows: %APPDATA%\\terraform.d\\plugins
    • All other systems: ~/.terraform.d/plugins

    Terraform Variable Assignment

    \n", + "

    You can pass variables to Terraform in different methods.

    \n", + "

    1. Environment variables – with a prefix TF_VAR_

    \n", + "
    $ export TF_VAR_instance_type=t2.micro
    \n", + "

    2. Command Line Flags

    \n", + "
    $ terraform plan -var=\"instancetype=t2.small\"
    \n", + "

    3. From a variable file – use terraform.tfvars – terraform will load all variables from this file. If different var files to be used then,

    \n", + "
    $ terraform plan -var-file=\"custom.tfvars
    \n", + "

    4. Variable Defaults – can keep variable default in another .tf file.

    \n", + "
    $ cat variables.tf\n",
    +      "variable \"my_ip\" {\n",
    +      "default = \"10.1.10.10/32\"\n",
    +      "}
    \n", + "
    • if no value mentioned, then default value will be used.
    • if default value not defined, then terraform will ask for variable when you do apply or plan operation.

    Resources

    \n", + "

    Share this to your network:

    \n", + "

    \n", + "

    \n", + "

    \n", + "
    \n", + "
    \n" + ] + } + ], + "source": [ + "import requests\n", + "from readability import Document\n", + "\n", + "response = requests.get(\"https://www.techbeatly.com/terraform-cheat-sheet\")\n", + "doc = Document(response.text)\n", + "print(doc.title())\n", + "\n", + "print(doc.summary())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Book if it doesn't exist\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\"name\":\"python-cheatsheet\",\"description\":\"python-cheatsheet\",\"created_by\":1,\"updated_by\":1,\"owned_by\":1,\"slug\":\"python-cheatsheet\",\"updated_at\":\"2022-08-10T00:14:32.000000Z\",\"created_at\":\"2022-08-10T00:14:32.000000Z\",\"id\":3}\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "import os\n", + "\n", + "payload = {\n", + " \"name\": \"terraform-cheatsheet\",\n", + " \"description\": \"terraform-cheatsheet\",\n", + " \"tags\": [\n", + " {\"name\": \"Platform\", \"value\": \"terraform\"},\n", + " {\"name\": \"Category\", \"value\": \"Code\"},\n", + " {\"name\": \"Type\", \"value\": \"Cheatsheet\"},\n", + " ],\n", + "}\n", + "headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN),\n", + "}\n", + "\n", + "response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + "\n", + "print(response.text)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Chapters\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### List folders to map to chapters\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "18\n", + "19\n", + "20\n" + ] + } + ], + "source": [ + "import requests\n", + "\n", + "chapters = [\"cheatsheet\", \"modules\", \"builtin\"]\n", + "\n", + "url = \"https://donavanaldrich.com/api/bookstack/chapters\"\n", + "\n", + "for chapter_name in chapters:\n", + "\n", + " payload = {\n", + " \"book_id\": 2,\n", + " \"name\": chapter_name,\n", + " \"description\": chapter_name,\n", + " \"tags\": [{\"name\": \"chapter\", \"value\": chapter_name}],\n", + " }\n", + " headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN),\n", + " }\n", + "\n", + " response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + " response_json = response.json()\n", + " chapter_id = response_json[\"id\"]\n", + " print(chapter_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Pages\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "26\n", + "27\n", + "28\n" + ] + } + ], + "source": [ + "from pathlib import Path\n", + "import requests\n", + "import frontmatter\n", + "\n", + "chapters = [\"cheatsheet\", \"modules\", \"builtin\"]\n", + "# The Python interpreter has a number of functions and types built into it that are always available.\n", + "# Standard Library\n", + "headers = {\n", + " \"X-API-KEY\": os.getenv(X_API_KEY),\n", + " \"Authorization\": os.getenv(BOOKSTACK_API_TOKEN),\n", + "}\n", + "\n", + "url = \"https://donavanaldrich.com/api/bookstack/books\"\n", + "\n", + "payload = {\n", + " \"name\": \"python-cheatsheet\",\n", + " \"description\": \"python-cheatsheet\",\n", + " \"tags\": [\n", + " {\"name\": \"Platform\", \"value\": \"Python\"},\n", + " {\"name\": \"Category\", \"value\": \"Code\"},\n", + " {\"name\": \"Type\", \"value\": \"Cheatsheet\"},\n", + " ],\n", + "}\n", + "\n", + "response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + "response_json = response.json()\n", + "book_id = response_json[\"id\"]\n", + "\n", + "# book_id = xs\n", + "#\n", + "for chapter in chapters:\n", + " payload = {\n", + " \"book_id\": book_id,\n", + " \"name\": chapter,\n", + " \"description\": chapter,\n", + " \"tags\": [{\"name\": \"chapter\", \"value\": chapter}],\n", + " }\n", + " url = \"https://donavanaldrich.com/api/bookstack/chapters\"\n", + " response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + " response_json = response.json()\n", + " chapter_id = response_json[\"id\"]\n", + " print(chapter_id)\n", + "\n", + " for page in Path(\"../temp/python-cheatsheets/\" + chapter).iterdir():\n", + " url = \"https://donavanaldrich.com/api/bookstack/pages\"\n", + " with open(page, \"r\") as f:\n", + " # text = f.read()\n", + " # post = frontmatter.load(page)\n", + " metadata, content = frontmatter.parse(f.read())\n", + " try:\n", + " page_name = metadata[\"title\"]\n", + " except:\n", + " page_name = str(page)\n", + " payload = {\n", + " \"book_id\": book_id,\n", + " \"chapter_id\": chapter_id,\n", + " \"name\": page_name,\n", + " \"markdown\": content,\n", + " \"tags\": [{\"name\": \"chapter\", \"value\": chapter}],\n", + " }\n", + " response = requests.request(\"POST\", url, json=payload, headers=headers)\n", + "\n", + " # print(response.text)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Development/Sandboxing\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "# Python Debugging\n", + "\n", + "\n", + " \n", + " Finding and resolving bugs\n", + " \n", + " \n", + " In computer programming and software development, debugging is the process of finding and resolving bugs (defects or problems that prevent correct operation) within computer programs, software, or systems.\n", + " \n", + "\n", + "\n", + "## Raising Exceptions\n", + "\n", + "Exceptions are raised with a raise statement. In code, a raise statement consists of the following:\n", + "\n", + "- The `raise` keyword\n", + "- A call to the `Exception()` function\n", + "- A string with a helpful error message passed to the `Exception()` function\n", + "\n", + "```python\n", + ">>> raise Exception('This is the error message.')\n", + "# Traceback (most recent call last):\n", + "# File \"\", line 1, in \n", + "# raise Exception('This is the error message.')\n", + "# Exception: This is the error message.\n", + "```\n", + "\n", + "Typically, it’s the code that calls the function, not the function itself, that knows how to handle an exception. So, you will commonly see a raise statement inside a function and the `try` and `except` statements in the code calling the function.\n", + "\n", + "```python\n", + ">>> def box_print(symbol, width, height):\n", + "... if len(symbol) != 1:\n", + "... raise Exception('Symbol must be a single character string.')\n", + "... if width <= 2:\n", + "... raise Exception('Width must be greater than 2.')\n", + "... if height <= 2:\n", + "... raise Exception('Height must be greater than 2.')\n", + "... print(symbol * width)\n", + "... for i in range(height - 2):\n", + "... print(symbol + (' ' * (width - 2)) + symbol)\n", + "... print(symbol * width)\n", + "...\n", + ">>> for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)):\n", + "... try:\n", + "... box_print(sym, w, h)\n", + "... except Exception as err:\n", + "... print('An exception happened: ' + str(err))\n", + "...\n", + "# ****\n", + "# * *\n", + "# * *\n", + "# ****\n", + "# OOOOOOOOOOOOOOOOOOOO\n", + "# O O\n", + "# O O\n", + "# O O\n", + "# OOOOOOOOOOOOOOOOOOOO\n", + "# An exception happened: Width must be greater than 2.\n", + "# An exception happened: Symbol must be a single character string.\n", + "```\n", + "\n", + "Read more about [Exception Handling](/cheatsheet/exception-handling).\n", + "\n", + "## Getting the Traceback as a string\n", + "\n", + "The `traceback` is displayed by Python whenever a raised exception goes unhandled. But can also obtain it as a string by calling traceback.format_exc(). This function is useful if you want the information from an exception’s traceback but also want an except statement to gracefully handle the exception. You will need to import Python’s traceback module before calling this function.\n", + "\n", + "```python\n", + ">>> import traceback\n", + "\n", + ">>> try:\n", + "... raise Exception('This is the error message.')\n", + ">>> except:\n", + "... with open('errorInfo.txt', 'w') as error_file:\n", + "... error_file.write(traceback.format_exc())\n", + "... print('The traceback info was written to errorInfo.txt.')\n", + "...\n", + "# 116\n", + "# The traceback info was written to errorInfo.txt.\n", + "```\n", + "\n", + "The 116 is the return value from the `write()` method, since 116 characters were written to the file. The `traceback` text was written to errorInfo.txt.\n", + "\n", + " Traceback (most recent call last):\n", + " File \"\", line 2, in \n", + " Exception: This is the error message.\n", + "\n", + "## Assertions\n", + "\n", + "An assertion is a sanity check to make sure your code isn’t doing something obviously wrong. These sanity checks are performed by `assert` statements. If the sanity check fails, then an `AssertionError` exception is raised. In code, an `assert` statement consists of the following:\n", + "\n", + "- The `assert` keyword\n", + "- A condition (that is, an expression that evaluates to `True` or `False`)\n", + "- A comma\n", + "- A `string` to display when the condition is `False`\n", + "\n", + "```python\n", + ">>> pod_bay_door_status = 'open'\n", + ">>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "\n", + ">>> pod_bay_door_status = 'I\\'m sorry, Dave. I\\'m afraid I can\\'t do that.'\n", + ">>> assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "# Traceback (most recent call last):\n", + "# File \"\", line 1, in \n", + "# assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'\n", + "# AssertionError: The pod bay doors need to be \"open\".\n", + "```\n", + "\n", + "In plain English, an assert statement says, “I assert that this condition holds true, and if not, there is a bug somewhere in the program.” Unlike exceptions, your code should not handle assert statements with try and except; if an assert fails, your program should crash. By failing fast like this, you shorten the time between the original cause of the bug and when you first notice the bug. This will reduce the amount of code you will have to check before finding the code that’s causing the bug.\n", + "\n", + "### Disabling Assertions\n", + "\n", + "Assertions can be disabled by passing the `-O` option when running Python.\n", + "\n", + "## Logging\n", + "\n", + "To enable the `logging` module to display log messages on your screen as your program runs, copy the following to the top of your program:\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s')\n", + "```\n", + "\n", + "Say you wrote a function to calculate the factorial of a number. In mathematics, factorial 4 is 1 × 2 × 3 × 4, or 24. Factorial 7 is 1 × 2 × 3 × 4 × 5 × 6 × 7, or 5,040. Open a new file editor window and enter the following code. It has a bug in it, but you will also enter several log messages to help yourself figure out what is going wrong. Save the program as factorialLog.py.\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s- %(message)s')\n", + ">>> logging.debug('Start of program')\n", + "\n", + ">>> def factorial(n):\n", + "... logging.debug('Start of factorial(%s)' % (n))\n", + "... total = 1\n", + "... for i in range(1, n + 1):\n", + "... total *= i\n", + "... logging.debug('i is ' + str(i) + ', total is ' + str(total))\n", + "... logging.debug('End of factorial(%s)' % (n))\n", + "... return total\n", + "...\n", + ">>> print(factorial(5))\n", + ">>> logging.debug('End of program')\n", + "# 2015-05-23 16:20:12,664 - DEBUG - Start of program\n", + "# 2015-05-23 16:20:12,664 - DEBUG - Start of factorial(5)\n", + "# 2015-05-23 16:20:12,665 - DEBUG - i is 0, total is 0\n", + "# 2015-05-23 16:20:12,668 - DEBUG - i is 1, total is 0\n", + "# 2015-05-23 16:20:12,670 - DEBUG - i is 2, total is 0\n", + "# 2015-05-23 16:20:12,673 - DEBUG - i is 3, total is 0\n", + "# 2015-05-23 16:20:12,675 - DEBUG - i is 4, total is 0\n", + "# 2015-05-23 16:20:12,678 - DEBUG - i is 5, total is 0\n", + "# 2015-05-23 16:20:12,680 - DEBUG - End of factorial(5)\n", + "# 0\n", + "# 2015-05-23 16:20:12,684 - DEBUG - End of program\n", + "```\n", + "\n", + "## Logging Levels\n", + "\n", + "Logging levels provide a way to categorize your log messages by importance. There are five logging levels, described in Table 10-1 from least to most important. Messages can be logged at each level using a different logging function.\n", + "\n", + "| Level | Logging Function | Description |\n", + "| ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n", + "| `DEBUG` | `logging.debug()` | The lowest level. Used for small details. Usually you care about these messages only when diagnosing problems. |\n", + "| `INFO` | `logging.info()` | Used to record information on general events in your program or confirm that things are working at their point in the program. |\n", + "| `WARNING` | `logging.warning()` | Used to indicate a potential problem that doesn’t prevent the program from working but might do so in the future. |\n", + "| `ERROR` | `logging.error()` | Used to record an error that caused the program to fail to do something. |\n", + "| `CRITICAL` | `logging.critical()` | The highest level. Used to indicate a fatal error that has caused or is about to cause the program to stop running entirely. |\n", + "\n", + "## Disabling Logging\n", + "\n", + "After you’ve debugged your program, you probably don’t want all these log messages cluttering the screen. The logging.disable() function disables these so that you don’t have to go into your program and remove all the logging calls by hand.\n", + "\n", + "```python\n", + ">>> import logging\n", + "\n", + ">>> logging.basicConfig(level=logging.INFO, format=' %(asctime)s -%(levelname)s - %(message)s')\n", + ">>> logging.critical('Critical error! Critical error!')\n", + "# 2015-05-22 11:10:48,054 - CRITICAL - Critical error! Critical error!\n", + "\n", + ">>> logging.disable(logging.CRITICAL)\n", + ">>> logging.critical('Critical error! Critical error!')\n", + ">>> logging.error('Error! Error!')\n", + "```\n", + "\n", + "## Logging to a File\n", + "\n", + "Instead of displaying the log messages to the screen, you can write them to a text file. The `logging.basicConfig()` function takes a filename keyword argument, like so:\n", + "\n", + "```python\n", + ">>> import logging\n", + ">>> logging.basicConfig(filename='myProgramLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')\n", + "```\n" + ] + } + ], + "source": [ + "# for page in Path('../temp/python-cheatsheets/' + chapter).iterdir():\n", + "page = \"../temp/python-cheatsheets/cheatsheet/debugging.md\"\n", + "url = \"https://donavanaldrich.com/api/bookstack/pages\"\n", + "# print(page)\n", + "with open(page, \"r\") as f:\n", + " # text = f.read()\n", + " # post = frontmatter.load(page)\n", + " metadata, content = frontmatter.parse(f.read())\n", + "print(content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference\n", + "\n", + "### Payloads\n", + "\n", + "#### book\n", + "\n", + "```json\n", + "{\n", + " \"name\": \"python-cheatsheet\",\n", + " \"description\": \"python-cheatsheet\",\n", + " \"tags\": [\n", + " { \"name\": \"Platform\", \"value\": \"Python\" },\n", + " { \"name\": \"Category\", \"value\": \"Code\" },\n", + " { \"name\": \"Type\", \"value\": \"Cheatsheet\" }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "#### Chapter\n", + "\n", + "```json\n", + "{\n", + " \"book_id\": 1,\n", + " \"name\": \"My fantastic new chapter\",\n", + " \"description\": \"This is a great new chapter that I've created via the API\",\n", + " \"tags\": [\n", + " { \"name\": \"Category\", \"value\": \"Top Content\" },\n", + " { \"name\": \"Rating\", \"value\": \"Highest\" }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "#### Page\n", + "\n", + "```json\n", + "{\n", + " \"book_id\": 1,\n", + " \"name\": \"My API Page\",\n", + " \"markdown\": \"

    my new API page

    \",\n", + " \"tags\": [\n", + " { \"name\": \"Category\", \"value\": \"Not Bad Content\" },\n", + " { \"name\": \"Rating\", \"value\": \"Average\" }\n", + " ]\n", + "}\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "module 'frontmatter' has no attribute 'load'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/documentation/python-cheatsheet.ipynb Cell 19\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 6\u001b[0m file_parts \u001b[39m=\u001b[39m frontmatter\u001b[39m.\u001b[39mload(filepath)\n\u001b[1;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m {\n\u001b[1;32m 9\u001b[0m \u001b[39m# 'html': markdown_parser.convert(file_parts.content),\u001b[39;00m\n\u001b[1;32m 10\u001b[0m \u001b[39m'\u001b[39m\u001b[39mmetadata\u001b[39m\u001b[39m'\u001b[39m: file_parts\u001b[39m.\u001b[39mmetadata\n\u001b[1;32m 11\u001b[0m }\n\u001b[0;32m---> 13\u001b[0m parse_markdown(\u001b[39m'\u001b[39;49m\u001b[39m../temp/python-cheatsheets/cheatsheet/debugging.md\u001b[39;49m\u001b[39m'\u001b[39;49m)\n", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/documentation/python-cheatsheet.ipynb Cell 19\u001b[0m in \u001b[0;36mparse_markdown\u001b[0;34m(filepath)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39mparse_markdown\u001b[39m(filepath):\n\u001b[1;32m 5\u001b[0m \u001b[39m# markdown_parser = markdown.Markdown(extensions=markdown_extensions)\u001b[39;00m\n\u001b[0;32m----> 6\u001b[0m file_parts \u001b[39m=\u001b[39m frontmatter\u001b[39m.\u001b[39;49mload(filepath)\n\u001b[1;32m 8\u001b[0m \u001b[39mreturn\u001b[39;00m {\n\u001b[1;32m 9\u001b[0m \u001b[39m# 'html': markdown_parser.convert(file_parts.content),\u001b[39;00m\n\u001b[1;32m 10\u001b[0m \u001b[39m'\u001b[39m\u001b[39mmetadata\u001b[39m\u001b[39m'\u001b[39m: file_parts\u001b[39m.\u001b[39mmetadata\n\u001b[1;32m 11\u001b[0m }\n", + "\u001b[0;31mAttributeError\u001b[0m: module 'frontmatter' has no attribute 'load'" + ] + } + ], + "source": [ + "import frontmatter # https://pypi.org/project/python-frontmatter/\n", + "import markdown\n", + "\n", + "\n", + "def parse_markdown(filepath):\n", + " # markdown_parser = markdown.Markdown(extensions=markdown_extensions)\n", + " file_parts = frontmatter.load(filepath)\n", + "\n", + " return {\n", + " # 'html': markdown_parser.convert(file_parts.content),\n", + " \"metadata\": file_parts.metadata\n", + " }\n", + "\n", + "\n", + "parse_markdown(\"../temp/python-cheatsheets/cheatsheet/debugging.md\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.9 64-bit", + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/documentation/terraform-docs.ipynb b/documentation/terraform-docs.ipynb new file mode 100644 index 0000000..83efc43 --- /dev/null +++ b/documentation/terraform-docs.ipynb @@ -0,0 +1,119 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "!git clone https://github.com/hashicorp/terraform.git\n", + "\n", + "https://github.com/antonbabenko/terraform-best-practices.git\n", + "\n", + "https://github.com/hashicorp/terraform-guides.git" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running `brew update --auto-update`...\n", + "\u001b[34m==>\u001b[0m \u001b[1mAuto-updated Homebrew!\u001b[0m\n", + "Updated 1 tap (homebrew/core).\n", + "\n", + "You have \u001b[1m19\u001b[0m outdated formulae installed.\n", + "You can upgrade them with \u001b[1mbrew upgrade\u001b[0m\n", + "or list them with \u001b[1mbrew outdated\u001b[0m.\n", + "\n", + "\u001b[33mWarning:\u001b[0m fetch 0.4.5 is already installed and up-to-date.\n", + "To reinstall 0.4.5, run:\n", + " brew reinstall fetch\n", + "mkdir: cannot create directory ‘../temp’: File exists\n", + "mkdir: cannot create directory ‘../temp/terraform/docs’: No such file or directory\n", + "mkdir: cannot create directory ‘../temp/terraform/data’: No such file or directory\n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:41Z] Downloading latest commit from branch \"master\" of https://github.com/hashicorp/terraform ... \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:42Z] Extracting files from /website/docs to ../temp/terraform/docs ... \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:42Z] 329 files extracted \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:42Z] Download and file extraction complete. \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:42Z] Downloading latest commit from branch \"master\" of https://github.com/hashicorp/terraform ... \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:43Z] Extracting files from /website/data to ../temp/terraform/data ... \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:43Z] 5 files extracted \n", + "[fetch] \u001b[36mINFO\u001b[0m[2022-08-11T01:35:43Z] Download and file extraction complete. \n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: markdown in /home/donaldrich/.local/lib/python3.10/site-packages (3.4.1)\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Requirement already satisfied: python-frontmatter in /home/donaldrich/.local/lib/python3.10/site-packages (1.0.0)\n", + "Requirement already satisfied: PyYAML in /home/donaldrich/.local/lib/python3.10/site-packages (from python-frontmatter) (5.1)\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "!brew install fetch\n", + "\n", + "!mkdir ../temp\n", + "!mkdir ../temp/terraform/docs\n", + "!mkdir ../temp/terraform/data\n", + "\n", + "!fetch --branch=master --source-path=/website/docs --repo=https://github.com/hashicorp/terraform ../temp/terraform/docs\n", + "!fetch --branch=master --source-path=/website/data --repo=https://github.com/hashicorp/terraform ../temp/terraform/data\n", + "\n", + "\n", + "%pip install markdown\n", + "%pip install python-frontmatter" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['data', 'docs']\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "dir = os.listdir(\"../temp/terraform\")\n", + "\n", + "print(dir)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.10.4 64-bit", + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/documentation/weasyprint.ipynb b/documentation/weasyprint.ipynb index 77e01be..33a696d 100644 --- a/documentation/weasyprint.ipynb +++ b/documentation/weasyprint.ipynb @@ -58,7 +58,7 @@ } ], "source": [ - "!pip install weasyprint" + "%pip install weasyprint" ] }, { @@ -88,21 +88,11 @@ "\n", "HTML(\"http://weasyprint.org/\").write_pdf(\"/User/donaldrich/Desktop/weasyprint.pdf\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b66cdca8-02c4-4761-84b1-a0a91991fb0a", - "metadata": {}, - "outputs": [], - "source": [ - "https://selenium-python.readthedocs.io/_/downloads/en/latest/pdf/" - ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.13 64-bit", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -116,11 +106,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.4" }, "vscode": { "interpreter": { - "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" } }, "widgets": { diff --git a/http/furl/furl.ipynb b/http/furl/furl.ipynb new file mode 100644 index 0000000..3040da8 --- /dev/null +++ b/http/furl/furl.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# furl\n", + "\n", + "[Github](https://github.com/gruns/furl)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying 185.199.109.133:443...\n", + "* Connected to raw.githubusercontent.com (185.199.109.133) port 443 (#0)\n", + "* ALPN, offering h2\n", + "* ALPN, offering http/1.1\n", + "* CAfile: /etc/ssl/certs/ca-certificates.crt\n", + "* CApath: /etc/ssl/certs\n", + "* TLSv1.0 (OUT), TLS header, Certificate Status (22):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS handshake, Client hello (1):\n", + "} [512 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Certificate Status (22):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Server hello (2):\n", + "{ [122 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Finished (20):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):\n", + "{ [19 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Certificate (11):\n", + "{ [3051 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, CERT verify (15):\n", + "{ [264 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Finished (20):\n", + "{ [52 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Finished (20):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):\n", + "} [1 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.3 (OUT), TLS handshake, Finished (20):\n", + "} [52 bytes data]\n", + "* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384\n", + "* ALPN, server accepted to use h2\n", + "* Server certificate:\n", + "* subject: C=US; ST=California; L=San Francisco; O=GitHub, Inc.; CN=*.github.io\n", + "* start date: Mar 18 00:00:00 2022 GMT\n", + "* expire date: Mar 21 23:59:59 2023 GMT\n", + "* subjectAltName: host \"raw.githubusercontent.com\" matched cert's \"*.githubusercontent.com\"\n", + "* issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS RSA SHA256 2020 CA1\n", + "* SSL certificate verify ok.\n", + "* Using HTTP2, server supports multiplexing\n", + "* Connection state changed (HTTP/2 confirmed)\n", + "* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* Using Stream ID: 1 (easy handle 0x559fdfac8e80)\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "> GET /gruns/furl/master/README.md HTTP/2\n", + "> Host: raw.githubusercontent.com\n", + "> user-agent: curl/7.81.0\n", + "> accept: */*\n", + "> \n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):\n", + "{ [193 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (OUT), TLS header, Supplemental data (23):\n", + "} [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "< HTTP/2 200 \n", + "< cache-control: max-age=300\n", + "< content-security-policy: default-src 'none'; style-src 'unsafe-inline'; sandbox\n", + "< content-type: text/plain; charset=utf-8\n", + "< etag: \"a422674a44f080e982c2757a03c7a6085f6de3a85731442a2f14785510ef12e2\"\n", + "< strict-transport-security: max-age=31536000\n", + "< x-content-type-options: nosniff\n", + "< x-frame-options: deny\n", + "< x-xss-protection: 1; mode=block\n", + "< x-github-request-id: 5F04:63F8:20E44:3B90F:62F2032B\n", + "< accept-ranges: bytes\n", + "< date: Tue, 09 Aug 2022 06:48:11 GMT\n", + "< via: 1.1 varnish\n", + "< x-served-by: cache-iah17220-IAH\n", + "< x-cache: MISS\n", + "< x-cache-hits: 0\n", + "< x-timer: S1660027691.995129,VS0,VE126\n", + "< vary: Authorization,Accept-Encoding,Origin\n", + "< access-control-allow-origin: *\n", + "< x-fastly-request-id: 3ae1114575f72aef60b2af9ef7e0d60266073cbb\n", + "< expires: Tue, 09 Aug 2022 06:53:11 GMT\n", + "< source-age: 0\n", + "< content-length: 23922\n", + "< \n", + "{ [885 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "* TLSv1.2 (IN), TLS header, Supplemental data (23):\n", + "{ [5 bytes data]\n", + "100 23922 100 23922 0 0 76972 0 --:--:-- --:--:-- --:--:-- 77167\n", + "* Connection #0 to host raw.githubusercontent.com left intact\n" + ] + } + ], + "source": [ + "# Retrieve Decumentation\n", + "\n", + "!curl -fLvo readme.md https://raw.githubusercontent.com/gruns/furl/master/README.md" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Defaulting to user installation because normal site-packages is not writeable\n", + "Collecting furl\n", + " Downloading furl-2.1.3-py2.py3-none-any.whl (20 kB)\n", + "Requirement already satisfied: six>=1.8.0 in /usr/lib/python3/dist-packages (from furl) (1.16.0)\n", + "Collecting orderedmultidict>=1.0.1\n", + " Downloading orderedmultidict-1.0.1-py2.py3-none-any.whl (11 kB)\n", + "Installing collected packages: orderedmultidict, furl\n", + "Successfully installed furl-2.1.3 orderedmultidict-1.0.1\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install furl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function: URL Deconstructor\n", + "\n", + "Takes a URL, chops it up into all the little pieces, returns as a JSON dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'fragment': {'encoded': 'scheme-username-password-host-port-network-location-and-origin',\n", + " 'path': {'encoded': 'scheme-username-password-host-port-network-location-and-origin',\n", + " 'isabsolute': False,\n", + " 'isdir': False,\n", + " 'isfile': True,\n", + " 'segments': ['scheme-username-password-host-port-network-location-and-origin']},\n", + " 'query': {'encoded': '', 'params': []},\n", + " 'separator': True},\n", + " 'host': 'github.com',\n", + " 'host_encoded': 'github.com',\n", + " 'netloc': 'github.com',\n", + " 'origin': 'https://github.com',\n", + " 'password': None,\n", + " 'path': {'encoded': '/gruns/furl',\n", + " 'isabsolute': True,\n", + " 'isdir': False,\n", + " 'isfile': True,\n", + " 'segments': ['gruns', 'furl']},\n", + " 'port': 443,\n", + " 'query': {'encoded': '', 'params': []},\n", + " 'scheme': 'https',\n", + " 'url': 'https://github.com/gruns/furl#scheme-username-password-host-port-network-location-and-origin',\n", + " 'username': None}\n" + ] + } + ], + "source": [ + "from furl import furl\n", + "import pprint\n", + "\n", + "url = 'https://github.com/gruns/furl#scheme-username-password-host-port-network-location-and-origin'\n", + "\n", + "f = furl(url)\n", + "\n", + "result = f.asdict()\n", + "\n", + "pprint.pprint(result)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.8.10 64-bit", + "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.4" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/http/furl/readme.md b/http/furl/readme.md new file mode 100644 index 0000000..2aa8891 --- /dev/null +++ b/http/furl/readme.md @@ -0,0 +1,784 @@ +

    + furl +

    + +

    + + + + +

    + +## furl is a small Python library that makes parsing and
    manipulating URLs easy. + +Python's standard [urllib](https://docs.python.org/3/library/urllib.html) and +[urlparse](https://docs.python.org/3/library/urllib.parse.html) modules provide a +number of URL\ +related functions, but using these functions to perform common +URL\ +operations proves tedious. Furl makes parsing and manipulating URLs\ +easy. + +Furl is well tested, [Unlicensed](http://unlicense.org/) in the public domain, +and supports\ +Python 2, Python 3, PyPy2, and PyPy3. + +Code time: Paths and query arguments are easy. Really easy. + +```python +>>> from furl import furl +>>> f = furl('http://www.google.com/?one=1&two=2') +>>> f /= 'path' +>>> del f.args['one'] +>>> f.args['three'] = '3' +>>> f.url +'http://www.google.com/path?two=2&three=3' +``` + +Or use furl's inline modification methods. + +```python +>>> furl('http://www.google.com/?one=1').add({'two':'2'}).url +'http://www.google.com/?one=1&two=2' + +>>> furl('http://www.google.com/?one=1&two=2').set({'three':'3'}).url +'http://www.google.com/?three=3' + +>>> furl('http://www.google.com/?one=1&two=2').remove(['one']).url +'http://www.google.com/?two=2' +``` + +Encoding is handled for you. Unicode, too. + +```python +>>> f = furl('http://www.google.com/') +>>> f.path = 'some encoding here' +>>> f.args['and some encoding'] = 'here, too' +>>> f.url +'http://www.google.com/some%20encoding%20here?and+some+encoding=here,+too' +>>> f.set(host=u'ドメイン.テスト', path=u'джк', query=u'☃=☺') +>>> f.url +'http://xn--eckwd4c7c.xn--zckzah/%D0%B4%D0%B6%D0%BA?%E2%98%83=%E2%98%BA' +``` + +Fragments also have a path and a query. + +```python +>>> f = furl('http://www.google.com/') +>>> f.fragment.path.segments = ['two', 'directories'] +>>> f.fragment.args = {'one': 'argument'} +>>> f.url +'http://www.google.com/#two/directories?one=argument' +``` + +## Installation + +Installing furl with pip is easy. + +``` +$ pip install furl +``` + + +## API + + * [Basics](#basics) + * [Scheme, Username, Password, Host, Port, Network Location, and Origin](#scheme-username-password-host-port-network-location-and-origin) + * [Path](#path) + * [Manipulation](#manipulation) + * [Query](#query) + * [Manipulation](#manipulation-1) + * [Parameters](#parameters) + * [Fragment](#fragment) + * [Encoding](#encoding) + * [Inline manipulation](#inline-manipulation) + * [Miscellaneous](#miscellaneous) + + +### Basics + +furl objects let you access and modify the various components of a URL. + +``` +scheme://username:password@host:port/path?query#fragment +``` + + * __scheme__ is the scheme string (all lowercase) or None. None means no + scheme. An empty string means a protocol relative URL, like + `//www.google.com`. + * __username__ is the username string for authentication. + * __password__ is the password string for authentication with __username__. + * __host__ is the domain name, IPv4, or IPv6 address as a string. Domain names + are all lowercase. + * __port__ is an integer or None. A value of None means no port specified and + the default port for the given __scheme__ should be inferred, if possible + (e.g. port 80 for the scheme `http`). + * __path__ is a Path object comprised of path segments. + * __query__ is a Query object comprised of key:value query arguments. + * __fragment__ is a Fragment object comprised of a Path object and Query object + separated by an optional `?` separator. + + + +### Scheme, Username, Password, Host, Port, Network Location, and Origin + +__scheme__, __username__, __password__, and __host__ are strings or +None. __port__ is an integer or None. + +```python +>>> f = furl('http://user:pass@www.google.com:99/') +>>> f.scheme, f.username, f.password, f.host, f.port +('http', 'user', 'pass', 'www.google.com', 99) +``` + +furl infers the default port for common schemes. + +```python +>>> f = furl('https://secure.google.com/') +>>> f.port +443 + +>>> f = furl('unknown://www.google.com/') +>>> print(f.port) +None +``` + +__netloc__ is the string combination of __username__, __password__, __host__, +and __port__, not including __port__ if it's None or the default port for the +provided __scheme__. + +```python +>>> furl('http://www.google.com/').netloc +'www.google.com' + +>>> furl('http://www.google.com:99/').netloc +'www.google.com:99' + +>>> furl('http://user:pass@www.google.com:99/').netloc +'user:pass@www.google.com:99' +``` + +__origin__ is the string combination of __scheme__, __host__, and __port__, not +including __port__ if it's None or the default port for the provided __scheme__. + +```python +>>> furl('http://www.google.com/').origin +'http://www.google.com' + +>>> furl('http://www.google.com:99/').origin +'http://www.google.com:99' +``` + + + +### Path + +URL paths in furl are Path objects that have __segments__, a list of zero or +more path segments that can be manipulated directly. Path segments in +__segments__ are percent-decoded and all interaction with __segments__ should +take place with percent-decoded strings. + +```python +>>> f = furl('http://www.google.com/a/large%20ish/path') +>>> f.path +Path('/a/large ish/path') +>>> f.path.segments +['a', 'large ish', 'path'] +>>> str(f.path) +'/a/large%20ish/path' +``` + +#### Manipulation + +```python +>>> f.path.segments = ['a', 'new', 'path', ''] +>>> str(f.path) +'/a/new/path/' + +>>> f.path = 'o/hi/there/with%20some%20encoding/' +>>> f.path.segments +['o', 'hi', 'there', 'with some encoding', ''] +>>> str(f.path) +'/o/hi/there/with%20some%20encoding/' + +>>> f.url +'http://www.google.com/o/hi/there/with%20some%20encoding/' + +>>> f.path.segments = ['segments', 'are', 'maintained', 'decoded', '^`<>[]"#/?'] +>>> str(f.path) +'/segments/are/maintained/decoded/%5E%60%3C%3E%5B%5D%22%23%2F%3F' +``` + +A path that starts with `/` is considered absolute, and a Path can be absolute +or not as specified (or set) by the boolean attribute __isabsolute__. URL Paths +have a special restriction: they must be absolute if a __netloc__ (username, +password, host, and/or port) is present. This restriction exists because a URL +path must start with `/` to separate itself from the __netloc__, if +present. Fragment Paths have no such limitation and __isabsolute__ and can be +True or False without restriction. + +Here's a URL Path example that illustrates how __isabsolute__ becomes True and +read-only in the presence of a __netloc__. + +```python +>>> f = furl('/url/path') +>>> f.path.isabsolute +True +>>> f.path.isabsolute = False +>>> f.url +'url/path' +>>> f.host = 'blaps.ru' +>>> f.url +'blaps.ru/url/path' +>>> f.path.isabsolute +True +>>> f.path.isabsolute = False +Traceback (most recent call last): + ... +AttributeError: Path.isabsolute is True and read-only for URLs with a netloc (a username, password, host, and/or port). URL paths must be absolute if a netloc exists. +>>> f.url +'blaps.ru/url/path' +``` + +Conversely, the __isabsolute__ attribute of Fragment Paths isn't bound by the +same read-only restriction. URL fragments are always prefixed by a `#` character +and don't need to be separated from the __netloc__. + +```python +>>> f = furl('http://www.google.com/#/absolute/fragment/path/') +>>> f.fragment.path.isabsolute +True +>>> f.fragment.path.isabsolute = False +>>> f.url +'http://www.google.com/#absolute/fragment/path/' +>>> f.fragment.path.isabsolute = True +>>> f.url +'http://www.google.com/#/absolute/fragment/path/' +``` + +A path that ends with `/` is considered a directory, and otherwise considered a +file. The Path attribute __isdir__ returns True if the path is a directory, +False otherwise. Conversely, the attribute __isfile__ returns True if the path +is a file, False otherwise. + +```python +>>> f = furl('http://www.google.com/a/directory/') +>>> f.path.isdir +True +>>> f.path.isfile +False + +>>> f = furl('http://www.google.com/a/file') +>>> f.path.isdir +False +>>> f.path.isfile +True +``` + +A path can be normalized with __normalize()__, and __normalize()__ returns the +Path object for method chaining. + +```python +>>> f = furl('http://www.google.com////a/./b/lolsup/../c/') +>>> f.path.normalize() +>>> f.url +'http://www.google.com/a/b/c/' +``` + +Path segments can also be appended with the slash operator, like with +[pathlib.Path](https://docs.python.org/3/library/pathlib.html#operators). + +```python +>>> from __future__ import division # For Python 2.x. +>>> +>>> f = furl('path') +>>> f.path /= 'with' +>>> f.path = f.path / 'more' / 'path segments/' +>>> f.url +'/path/with/more/path%20segments/' +``` + +For a dictionary representation of a path, use __asdict()__. + +```python +>>> f = furl('http://www.google.com/some/enc%20oding') +>>> f.path.asdict() +{ 'encoded': '/some/enc%20oding', + 'isabsolute': True, + 'isdir': False, + 'isfile': True, + 'segments': ['some', 'enc oding'] } +``` + + + +### Query + +URL queries in furl are Query objects that have __params__, a one dimensional +[ordered multivalue dictionary](https://github.com/gruns/orderedmultidict) of +query keys and values. Query keys and values in __params__ are percent-decoded +and all interaction with __params__ should take place with percent-decoded +strings. + +```python +>>> f = furl('http://www.google.com/?one=1&two=2') +>>> f.query +Query('one=1&two=2') +>>> f.query.params +omdict1D([('one', '1'), ('two', '2')]) +>>> str(f.query) +'one=1&two=2' +``` + +furl objects and Fragment objects (covered below) contain a Query object, and +__args__ is provided as a shortcut on these objects to access __query.params__. + +```python +>>> f = furl('http://www.google.com/?one=1&two=2') +>>> f.query.params +omdict1D([('one', '1'), ('two', '2')]) +>>> f.args +omdict1D([('one', '1'), ('two', '2')]) +>>> f.args is f.query.params +True +``` + +#### Manipulation + +__params__ is a one dimensional +[ordered multivalue dictionary](https://github.com/gruns/orderedmultidict) that +maintains method parity with Python's standard dictionary. + +```python +>>> f.query = 'silicon=14&iron=26&inexorable%20progress=vae%20victus' +>>> f.query.params +omdict1D([('silicon', '14'), ('iron', '26'), ('inexorable progress', 'vae victus')]) +>>> del f.args['inexorable progress'] +>>> f.args['magnesium'] = '12' +>>> f.args +omdict1D([('silicon', '14'), ('iron', '26'), ('magnesium', '12')]) +``` + +__params__ can also store multiple values for the same key because it's a +multivalue dictionary. + +```python +>>> f = furl('http://www.google.com/?space=jams&space=slams') +>>> f.args['space'] +'jams' +>>> f.args.getlist('space') +['jams', 'slams'] +>>> f.args.addlist('repeated', ['1', '2', '3']) +>>> str(f.query) +'space=jams&space=slams&repeated=1&repeated=2&repeated=3' +>>> f.args.popvalue('space') +'slams' +>>> f.args.popvalue('repeated', '2') +'2' +>>> str(f.query) +'space=jams&repeated=1&repeated=3' +``` + +__params__ is one dimensional. If a list of values is provided as a query value, +that list is interpreted as multiple values. + +```python +>>> f = furl() +>>> f.args['repeated'] = ['1', '2', '3'] +>>> f.add(args={'space':['jams', 'slams']}) +>>> str(f.query) +'repeated=1&repeated=2&repeated=3&space=jams&space=slams' +``` + +This makes sense: URL queries are inherently one dimensional -- query values +can't have native subvalues. + +See the [orderedmultimdict](https://github.com/gruns/orderedmultidict) +documentation for more information on interacting with the ordered multivalue +dictionary __params__. + +#### Parameters + +To produce an empty query argument, like `http://sprop.su/?param=`, set the +argument's value to the empty string. + +```python +>>> f = furl('http://sprop.su') +>>> f.args['param'] = '' +>>> f.url +'http://sprop.su/?param=' +``` + +To produce an empty query argument without a trailing `=`, use `None` as the +parameter value. + +```python +>>> f = furl('http://sprop.su') +>>> f.args['param'] = None +>>> f.url +'http://sprop.su/?param' +``` + +__encode(delimiter='&', quote_plus=True, dont_quote='')__ can be used to encode +query strings with delimiters like `;`, encode spaces as `+` instead of `%20` +(i.e. application/x-www-form-urlencoded encoded), or avoid percent-encoding +valid query characters entirely (valid query characters are +`/?:@-._~!$&'()*+,;=`). + +```python +>>> f.query = 'space=jams&woofs=squeeze+dog' +>>> f.query.encode() +'space=jams&woofs=squeeze+dog' +>>> f.query.encode(';') +'space=jams;woofs=squeeze+dog' +>>> f.query.encode(quote_plus=False) +'space=jams&woofs=squeeze%20dog' +``` + +`dont_quote` accepts `True`, `False`, or a string of valid query characters to +not percent-enode. If `True`, all valid query characters `/?:@-._~!$&'()*+,;=` +aren't percent-encoded. + +```python +>>> f.query = 'one,two/three' +>>> f.query.encode() +'one%2Ctwo%2Fthree' +>>> f.query.encode(dont_quote=True) +'one,two/three' +>>> f.query.encode(dont_quote=',') +'one,two%2Fthree' +``` + +For a dictionary representation of a query, use __asdict()__. + +```python +>>> f = furl('http://www.google.com/?space=ja+ms&space=slams') +>>> f.query.asdict() +{ 'encoded': 'space=ja+ms&space=slams', + 'params': [('space', 'ja ms'), + ('space', 'slams')] } +``` + + + +### Fragment + +URL fragments in furl are Fragment objects that have a Path __path__ and Query +__query__ separated by an optional `?` __separator__. + +```python +>>> f = furl('http://www.google.com/#/fragment/path?with=params') +>>> f.fragment +Fragment('/fragment/path?with=params') +>>> f.fragment.path +Path('/fragment/path') +>>> f.fragment.query +Query('with=params') +>>> f.fragment.separator +True +``` + +Manipulation of Fragments is done via the Fragment's Path and Query instances, +__path__ and __query__. + +```python +>>> f = furl('http://www.google.com/#/fragment/path?with=params') +>>> str(f.fragment) +'/fragment/path?with=params' +>>> f.fragment.path.segments.append('file.ext') +>>> str(f.fragment) +'/fragment/path/file.ext?with=params' + +>>> f = furl('http://www.google.com/#/fragment/path?with=params') +>>> str(f.fragment) +'/fragment/path?with=params' +>>> f.fragment.args['new'] = 'yep' +>>> str(f.fragment) +'/fragment/path?new=yep&with=params' +``` + +Creating hash-bang fragments with furl illustrates the use of Fragment's boolean +attribute __separator__. When __separator__ is False, the `?` that separates +__path__ and __query__ isn't included. + +```python +>>> f = furl('http://www.google.com/') +>>> f.fragment.path = '!' +>>> f.fragment.args = {'a':'dict', 'of':'args'} +>>> f.fragment.separator +True +>>> str(f.fragment) +'!?a=dict&of=args' + +>>> f.fragment.separator = False +>>> str(f.fragment) +'!a=dict&of=args' +>>> f.url +'http://www.google.com/#!a=dict&of=args' +``` + +For a dictionary representation of a fragment, use __asdict()__. + +```python +>>> f = furl('http://www.google.com/#path?args=args') +>>> f.fragment.asdict() +{ 'encoded': 'path?args=args', + 'separator': True, + 'path': { 'encoded': 'path', + 'isabsolute': False, + 'isdir': False, + 'isfile': True, + 'segments': ['path']}, + 'query': { 'encoded': 'args=args', + 'params': [('args', 'args')]} } +``` + + + +### Encoding + +Furl handles encoding for you, and furl's philosophy on encoding is simple: raw +URL strings should always be percent-encoded. + +```python +>>> f = furl() +>>> f.netloc = '%40user:%3Apass@google.com' +>>> f.username, f.password +'@user', ':pass' + +>>> f = furl() +>>> f.path = 'supply%20percent%20encoded/path%20strings' +>>> f.path.segments +['supply percent encoded', 'path strings'] + +>>> f.set(query='supply+percent+encoded=query+strings,+too') +>>> f.query.params +omdict1D([('supply percent encoded', 'query strings, too')]) + +>>> f.set(fragment='percent%20encoded%20path?and+percent+encoded=query+too') +>>> f.fragment.path.segments +['percent encoded path'] +>>> f.fragment.args +omdict1D([('and percent encoded', 'query too')]) +``` + +Raw, non-URL strings should never be percent-encoded. + +```python +>>> f = furl('http://google.com') +>>> f.set(username='@prap', password=':porps') +>>> f.url +'http://%40prap:%3Aporps@google.com' + +>>> f = furl() +>>> f.set(path=['path segments are', 'decoded', '<>[]"#']) +>>> str(f.path) +'/path%20segments%20are/decoded/%3C%3E%5B%5D%22%23' + +>>> f.set(args={'query parameters':'and values', 'are':'decoded, too'}) +>>> str(f.query) +'query+parameters=and+values&are=decoded,+too' + +>>> f.fragment.path.segments = ['decoded', 'path segments'] +>>> f.fragment.args = {'and decoded':'query parameters and values'} +>>> str(f.fragment) +'decoded/path%20segments?and+decoded=query+parameters+and+values' +``` + +Python's +[urllib.quote()](http://docs.python.org/library/urllib.html#urllib.quote) and +[urllib.unquote()](http://docs.python.org/library/urllib.html#urllib.unquote) +can be used to percent-encode and percent-decode path strings. Similarly, +[urllib.quote_plus()](http://docs.python.org/library/urllib.html#urllib.quote_plus) +and +[urllib.unquote_plus()](http://docs.python.org/library/urllib.html#urllib.unquote_plus) +can be used to percent-encode and percent-decode query strings. + + + +### Inline manipulation + +For quick, single-line URL manipulation, the __add()__, __set()__, and +__remove()__ methods of furl objects manipulate various URL components and +return the furl object for method chaining. + +```python +>>> url = 'http://www.google.com/#fragment' +>>> furl(url).add(args={'example':'arg'}).set(port=99).remove(fragment=True).url +'http://www.google.com:99/?example=arg' +``` + +__add()__ adds items to a furl object with the optional arguments + + * __args__: Shortcut for __query_params__. + * __path__: A list of path segments to add to the existing path segments, or a + path string to join with the existing path string. + * __query_params__: A dictionary of query keys and values to add to the query. + * __fragment_path__: A list of path segments to add to the existing fragment + path segments, or a path string to join with the existing fragment path + string. + * __fragment_args__: A dictionary of query keys and values to add to the + fragment's query. + +```python +>>> f = furl('http://www.google.com/').add( +... path='/search', fragment_path='frag/path', fragment_args={'frag':'arg'}) +>>> f.url +'http://www.google.com/search#frag/path?frag=args' +``` + +__set()__ sets items of a furl object with the optional arguments + + * __args__: Shortcut for __query_params__. + * __path__: List of path segments or a path string to adopt. + * __scheme__: Scheme string to adopt. + * __netloc__: Network location string to adopt. + * __origin__: Origin string to adopt. + * __query__: Query string to adopt. + * __query_params__: A dictionary of query keys and values to adopt. + * __fragment__: Fragment string to adopt. + * __fragment_path__: A list of path segments to adopt for the fragment's path + or a path string to adopt as the fragment's path. + * __fragment_args__: A dictionary of query keys and values for the fragment's + query to adopt. + * __fragment_separator__: Boolean whether or not there should be a `?` + separator between the fragment path and the fragment query. + * __host__: Host string to adopt. + * __port__: Port number to adopt. + * __username__: Username string to adopt. + * __password__: password string to adopt. + + +```python +>>> f = furl().set( +... scheme='https', host='secure.google.com', port=99, path='index.html', +... args={'some':'args'}, fragment='great job') +>>> f.url +'https://secure.google.com:99/index.html?some=args#great%20job' +``` + +__remove()__ removes items from a furl object with the optional arguments + + * __args__: Shortcut for __query_params__. + * __path__: A list of path segments to remove from the end of the existing path + segments list, or a path string to remove from the end of the existing + path string, or True to remove the entire path portion of the URL. + * __query__: A list of query keys to remove from the query, if they exist, or + True to remove the entire query portion of the URL. + * __query_params__: A list of query keys to remove from the query, if they + exist. + * __fragment__: If True, remove the entire fragment portion of the URL. + * __fragment_path__: A list of path segments to remove from the end of the + fragment's path segments, or a path string to remove from the end of the + fragment's path string, or True to remove the entire fragment path. + * __fragment_args__: A list of query keys to remove from the fragment's query, + if they exist. + * __username__: If True, remove the username, if it exists. + * __password__: If True, remove the password, if it exists. + + +```python +>>> url = 'https://secure.google.com:99/a/path/?some=args#great job' +>>> furl(url).remove(args=['some'], path='path/', fragment=True, port=True).url +'https://secure.google.com/a/' +``` + + + +### Miscellaneous + +Like [pathlib.Path](https://docs.python.org/3/library/pathlib.html#operators), +path segments can be appended to a furl object's Path with the slash operator. + +```python +>>> from __future__ import division # For Python 2.x. +>>> f = furl('http://www.google.com/path?example=arg#frag') +>>> f /= 'add' +>>> f = f / 'seg ments/' +>>> f.url +'http://www.google.com/path/add/seg%20ments/?example=arg#frag' +``` + +__tostr(query_delimiter='&', query_quote_plus=True, query_dont_quote='')__ +creates and returns a URL string. `query_delimiter`, `query_quote_plus`, and +`query_dont_quote` are passed unmodified to `Query.encode()` as `delimiter`, +`quote_plus`, and `dont_quote` respectively. + +```python +>>> f = furl('http://spep.ru/?a+b=c+d&two%20tap=cat%20nap%24') +>>> f.tostr() +'http://spep.ru/?a+b=c+d&two+tap=cat+nap$' +>>> f.tostr(query_delimiter=';', query_quote_plus=False) +'http://spep.ru/?a%20b=c%20d;two%20tap=cat%20nap$' +>>> f.tostr(query_dont_quote='$') +'http://spep.ru/?a+b=c+d&two+tap=cat+nap$' +``` + +`furl.url` is a shortcut for `furl.tostr()`. + +```python +>>> f.url +'http://spep.ru/?a+b=c+d&two+tap=cat+nap$' +>>> f.url == f.tostr() == str(f) +True +``` + +__copy()__ creates and returns a new furl object with an identical URL. + +```python +>>> f = furl('http://www.google.com') +>>> f.copy().set(path='/new/path').url +'http://www.google.com/new/path' +>>> f.url +'http://www.google.com' +``` + +__join()__ joins the furl object's URL with the provided relative or absolute +URL and returns the furl object for method chaining. __join()__'s action is the +same as navigating to the provided URL from the current URL in a web browser. + +```python +>>> f = furl('http://www.google.com') +>>> f.join('new/path').url +'http://www.google.com/new/path' +>>> f.join('replaced').url +'http://www.google.com/new/replaced' +>>> f.join('../parent').url +'http://www.google.com/parent' +>>> f.join('path?query=yes#fragment').url +'http://www.google.com/path?query=yes#fragment' +>>> f.join('unknown://www.yahoo.com/new/url/').url +'unknown://www.yahoo.com/new/url/' +``` + +For a dictionary representation of a URL, use __asdict()__. + +```python +>>> f = furl('https://xn--eckwd4c7c.xn--zckzah/path?args=args#frag') +>>> f.asdict() +{ 'url': 'https://xn--eckwd4c7c.xn--zckzah/path?args=args#frag', + 'scheme': 'https', + 'username': None + 'password': None, + 'host': 'ドメイン.テスト', + 'host_encoded': 'xn--eckwd4c7c.xn--zckzah', + 'port': 443, + 'netloc': 'xn--eckwd4c7c.xn--zckzah', + 'origin': 'https://xn--eckwd4c7c.xn--zckzah', + 'path': { 'encoded': '/path', + 'isabsolute': True, + 'isdir': False, + 'isfile': True, + 'segments': ['path']}, + 'query': { 'encoded': 'args=args', + 'params': [('args', 'args')]}, + 'fragment': { 'encoded': 'frag', + 'path': { 'encoded': 'frag', + 'isabsolute': False, + 'isdir': False, + 'isfile': True, + 'segments': ['frag']}, + 'query': { 'encoded': '', + 'params': []}, + 'separator': True} } +``` diff --git a/playground/nlp/spacy.ipynb b/playground/nlp/spacy.ipynb index f7d1d17..2df18df 100644 --- a/playground/nlp/spacy.ipynb +++ b/playground/nlp/spacy.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "id": "4b1a1586-0823-4046-ab19-0fa99a0f241b", "metadata": { "scrolled": true, @@ -13,73 +13,56 @@ "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: spacy in /opt/conda/lib/python3.10/site-packages (3.3.1)\n", - "Requirement already satisfied: pathy>=0.3.5 in /opt/conda/lib/python3.10/site-packages (from spacy) (0.6.1)\n", - "Requirement already satisfied: thinc<8.1.0,>=8.0.14 in /opt/conda/lib/python3.10/site-packages (from spacy) (8.0.17)\n", - "Requirement already satisfied: numpy>=1.15.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (1.21.6)\n", - "Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (1.0.2)\n", - "Requirement already satisfied: requests<3.0.0,>=2.13.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (2.28.0)\n", - "Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (3.3.0)\n", - "Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /opt/conda/lib/python3.10/site-packages (from spacy) (2.0.6)\n", - "Requirement already satisfied: blis<0.8.0,>=0.4.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (0.7.7)\n", - "Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (1.0.7)\n", - "Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /opt/conda/lib/python3.10/site-packages (from spacy) (3.0.6)\n", - "Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /opt/conda/lib/python3.10/site-packages (from spacy) (2.4.3)\n", - "Requirement already satisfied: packaging>=20.0 in /home/jovyan/.local/lib/python3.10/site-packages (from spacy) (21.3)\n", - "Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /opt/conda/lib/python3.10/site-packages (from spacy) (2.0.7)\n", - "Requirement already satisfied: wasabi<1.1.0,>=0.9.1 in /opt/conda/lib/python3.10/site-packages (from spacy) (0.9.1)\n", - "Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (4.64.0)\n", - "Requirement already satisfied: setuptools in /opt/conda/lib/python3.10/site-packages (from spacy) (62.3.4)\n", - "Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.9 in /opt/conda/lib/python3.10/site-packages (from spacy) (3.0.9)\n", - "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from spacy) (3.1.2)\n", - "Requirement already satisfied: typer<0.5.0,>=0.3.0 in /opt/conda/lib/python3.10/site-packages (from spacy) (0.4.1)\n", - "Requirement already satisfied: pydantic!=1.8,!=1.8.1,<1.9.0,>=1.7.4 in /opt/conda/lib/python3.10/site-packages (from spacy) (1.8.2)\n", - "Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/lib/python3.10/site-packages (from packaging>=20.0->spacy) (3.0.9)\n", - "Requirement already satisfied: smart-open<6.0.0,>=5.0.0 in /opt/conda/lib/python3.10/site-packages (from pathy>=0.3.5->spacy) (5.2.1)\n", - "Requirement already satisfied: typing-extensions>=3.7.4.3 in /opt/conda/lib/python3.10/site-packages (from pydantic!=1.8,!=1.8.1,<1.9.0,>=1.7.4->spacy) (4.2.0)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy) (2022.6.15)\n", - "Requirement already satisfied: charset-normalizer~=2.0.0 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy) (2.0.12)\n", - "Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy) (3.3)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy) (1.26.9)\n", - "Requirement already satisfied: click<9.0.0,>=7.1.1 in /opt/conda/lib/python3.10/site-packages (from typer<0.5.0,>=0.3.0->spacy) (8.1.3)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->spacy) (2.1.1)\n", - "Collecting en-core-web-sm==3.3.0\n", - " Downloading https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.3.0/en_core_web_sm-3.3.0-py3-none-any.whl (12.8 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m12.8/12.8 MB\u001b[0m \u001b[31m21.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n", - "\u001b[?25hRequirement already satisfied: spacy<3.4.0,>=3.3.0.dev0 in /opt/conda/lib/python3.10/site-packages (from en-core-web-sm==3.3.0) (3.3.1)\n", - "Requirement already satisfied: srsly<3.0.0,>=2.4.3 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.4.3)\n", - "Requirement already satisfied: pydantic!=1.8,!=1.8.1,<1.9.0,>=1.7.4 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (1.8.2)\n", - "Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (4.64.0)\n", - "Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.0.6)\n", - "Requirement already satisfied: setuptools in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (62.3.4)\n", - "Requirement already satisfied: spacy-legacy<3.1.0,>=3.0.9 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.0.9)\n", - "Requirement already satisfied: typer<0.5.0,>=0.3.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (0.4.1)\n", - "Requirement already satisfied: wasabi<1.1.0,>=0.9.1 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (0.9.1)\n", - "Requirement already satisfied: spacy-loggers<2.0.0,>=1.0.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (1.0.2)\n", - "Requirement already satisfied: packaging>=20.0 in /home/jovyan/.local/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (21.3)\n", - "Requirement already satisfied: langcodes<4.0.0,>=3.2.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.3.0)\n", - "Requirement already satisfied: blis<0.8.0,>=0.4.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (0.7.7)\n", - "Requirement already satisfied: requests<3.0.0,>=2.13.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.28.0)\n", - "Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.0.6)\n", - "Requirement already satisfied: numpy>=1.15.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (1.21.6)\n", - "Requirement already satisfied: thinc<8.1.0,>=8.0.14 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (8.0.17)\n", - "Requirement already satisfied: pathy>=0.3.5 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (0.6.1)\n", - "Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (1.0.7)\n", - "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.1.2)\n", - "Requirement already satisfied: catalogue<2.1.0,>=2.0.6 in /opt/conda/lib/python3.10/site-packages (from spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.0.7)\n", - "Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/lib/python3.10/site-packages (from packaging>=20.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.0.9)\n", - "Requirement already satisfied: smart-open<6.0.0,>=5.0.0 in /opt/conda/lib/python3.10/site-packages (from pathy>=0.3.5->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (5.2.1)\n", - "Requirement already satisfied: typing-extensions>=3.7.4.3 in /opt/conda/lib/python3.10/site-packages (from pydantic!=1.8,!=1.8.1,<1.9.0,>=1.7.4->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (4.2.0)\n", - "Requirement already satisfied: charset-normalizer~=2.0.0 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.0.12)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2022.6.15)\n", - "Requirement already satisfied: idna<4,>=2.5 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (3.3)\n", - "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /opt/conda/lib/python3.10/site-packages (from requests<3.0.0,>=2.13.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (1.26.9)\n", - "Requirement already satisfied: click<9.0.0,>=7.1.1 in /opt/conda/lib/python3.10/site-packages (from typer<0.5.0,>=0.3.0->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (8.1.3)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->spacy<3.4.0,>=3.3.0.dev0->en-core-web-sm==3.3.0) (2.1.1)\n", - "Installing collected packages: en-core-web-sm\n", - "Successfully installed en-core-web-sm-3.3.0\n", - "\u001b[38;5;2m✔ Download and installation successful\u001b[0m\n", - "You can now load the package via spacy.load('en_core_web_sm')\n" + "Defaulting to user installation because normal site-packages is not writeable\n", + "Collecting spacy\n", + " Downloading spacy-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.4/6.4 MB\u001b[0m \u001b[31m15.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n", + "\u001b[?25hCollecting thinc<8.2.0,>=8.1.0\n", + " Downloading thinc-8.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (822 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m822.5/822.5 KB\u001b[0m \u001b[31m10.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m\n", + "\u001b[?25hCollecting cymem<2.1.0,>=2.0.2\n", + " Using cached cymem-2.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (35 kB)\n", + "Requirement already satisfied: requests<3.0.0,>=2.13.0 in /usr/lib/python3/dist-packages (from spacy) (2.25.1)\n", + "Collecting wasabi<1.1.0,>=0.9.1\n", + " Downloading wasabi-0.10.1-py3-none-any.whl (26 kB)\n", + "Requirement already satisfied: packaging>=20.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from spacy) (21.3)\n", + "Collecting spacy-legacy<3.1.0,>=3.0.9\n", + " Using cached spacy_legacy-3.0.9-py2.py3-none-any.whl (20 kB)\n", + "Requirement already satisfied: jinja2 in /usr/lib/python3/dist-packages (from spacy) (3.0.3)\n", + "Collecting langcodes<4.0.0,>=3.2.0\n", + " Using cached langcodes-3.3.0-py3-none-any.whl (181 kB)\n", + "Collecting pathy>=0.3.5\n", + " Downloading pathy-0.6.2-py3-none-any.whl (42 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m42.8/42.8 KB\u001b[0m \u001b[31m1.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting murmurhash<1.1.0,>=0.28.0\n", + " Using cached murmurhash-1.0.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (21 kB)\n", + "Collecting tqdm<5.0.0,>=4.38.0\n", + " Downloading tqdm-4.64.0-py2.py3-none-any.whl (78 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.4/78.4 KB\u001b[0m \u001b[31m2.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting preshed<3.1.0,>=3.0.2\n", + " Using cached preshed-3.0.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (128 kB)\n", + "Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from spacy) (59.6.0)\n", + "Collecting spacy-loggers<2.0.0,>=1.0.0\n", + " Downloading spacy_loggers-1.0.3-py3-none-any.whl (9.3 kB)\n", + "Collecting catalogue<2.1.0,>=2.0.6\n", + " Downloading catalogue-2.0.8-py3-none-any.whl (17 kB)\n", + "Requirement already satisfied: typer<0.5.0,>=0.3.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from spacy) (0.4.2)\n", + "Requirement already satisfied: numpy>=1.15.0 in /usr/lib/python3/dist-packages (from spacy) (1.21.5)\n", + "Collecting srsly<3.0.0,>=2.4.3\n", + " Downloading srsly-2.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (460 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m460.0/460.0 KB\u001b[0m \u001b[31m10.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4 in /home/donaldrich/.local/lib/python3.10/site-packages (from spacy) (1.9.1)\n", + "Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/lib/python3/dist-packages (from packaging>=20.0->spacy) (2.4.7)\n", + "Collecting smart-open<6.0.0,>=5.2.1\n", + " Using cached smart_open-5.2.1-py3-none-any.whl (58 kB)\n", + "Requirement already satisfied: typing-extensions>=3.7.4.3 in /home/donaldrich/.local/lib/python3.10/site-packages (from pydantic!=1.8,!=1.8.1,<1.10.0,>=1.7.4->spacy) (4.3.0)\n", + "Collecting blis<0.8.0,>=0.7.8\n", + " Downloading blis-0.7.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m10.2/10.2 MB\u001b[0m \u001b[31m19.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m0:01\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: click<9.0.0,>=7.1.1 in /usr/lib/python3/dist-packages (from typer<0.5.0,>=0.3.0->spacy) (8.0.3)\n", + "Installing collected packages: wasabi, murmurhash, cymem, tqdm, spacy-loggers, spacy-legacy, smart-open, preshed, langcodes, catalogue, blis, srsly, pathy, thinc, spacy\n", + "Successfully installed blis-0.7.8 catalogue-2.0.8 cymem-2.0.6 langcodes-3.3.0 murmurhash-1.0.7 pathy-0.6.2 preshed-3.0.6 smart-open-5.2.1 spacy-3.4.1 spacy-legacy-3.0.9 spacy-loggers-1.0.3 srsly-2.4.4 thinc-8.1.0 tqdm-4.64.0 wasabi-0.10.1\n", + "zsh:1: command not found: python\n" ] } ], @@ -116,7 +99,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -131,6 +114,11 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.4" + }, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } } }, "nbformat": 4, diff --git a/storage/minio.ipynb b/storage/minio.ipynb new file mode 100644 index 0000000..1ad1cf9 --- /dev/null +++ b/storage/minio.ipynb @@ -0,0 +1,53 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from minio import Minio\n", + "from minio.error import S3Error\n", + "\n", + "\n", + "def main():\n", + "\n", + " client = Minio(\n", + " \"minio_endpoint\",\n", + " access_key=\"key\",\n", + " secret_key=\"secret\",\n", + " )\n", + "\n", + " # Make 'asiatrip' bucket if not exist.\n", + " found = client.bucket_exists(\"bucket\")\n", + " if not found:\n", + " client.make_bucket(\"bucket\")\n", + " else:\n", + " print(\"Bucket already exists\")\n", + "\n", + " client.fput_object(\n", + " \"bucket\", \"bucket_file\", \"local_file_path\",\n", + " )\n", + " print(\n", + " \"'local_file_path' is successfully uploaded as \"\n", + " \"object 'bucket_file' to bucket 'bucket'.\"\n", + " )\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " try:\n", + " main()\n", + " except S3Error as exc:\n", + " print(\"error occurred.\", exc)" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/templating/Dashmachine Config Template.ipynb b/templating/Dashmachine Config Template.ipynb index b02459c..32abcb5 100644 --- a/templating/Dashmachine Config Template.ipynb +++ b/templating/Dashmachine Config Template.ipynb @@ -51,7 +51,7 @@ "{% endfor %}\n", "\n", "{# a comment #}\n", - "```" + "EOF" ] }, { @@ -126,13 +126,25 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 1, "id": "ad3a0cde-8e53-403d-bbb6-6fc95c392093", "metadata": { "scrolled": true, "tags": [] }, - "outputs": [], + "outputs": [ + { + "ename": "KeyError", + "evalue": "'data'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m/home/donaldrich/projects/secrets/jupyter-notebooks/templating/Dashmachine Config Template.ipynb Cell 3\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 11\u001b[0m response \u001b[39m=\u001b[39m requests\u001b[39m.\u001b[39mget(\n\u001b[1;32m 12\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mhttps://cms.donavanaldrich.com/items/containers\u001b[39m\u001b[39m\"\u001b[39m, headers\u001b[39m=\u001b[39mheaders\n\u001b[1;32m 13\u001b[0m )\n\u001b[1;32m 14\u001b[0m data \u001b[39m=\u001b[39m response\u001b[39m.\u001b[39mjson()\n\u001b[0;32m---> 16\u001b[0m items \u001b[39m=\u001b[39m data[\u001b[39m\"\u001b[39;49m\u001b[39mdata\u001b[39;49m\u001b[39m\"\u001b[39;49m]\n", + "\u001b[0;31mKeyError\u001b[0m: 'data'" + ] + } + ], "source": [ "import json\n", "import os\n", @@ -141,7 +153,7 @@ "import requests\n", "\n", "headers = {\n", - " \"Authorization\": \"os.env(directus_token)\",\n", + " \"Authorization\": os.env(directus_token),\n", " \"Content-Type\": \"application/json\",\n", "}\n", "response = requests.get(\n", @@ -1533,7 +1545,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.13 64-bit", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -1547,11 +1559,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.4" }, "vscode": { "interpreter": { - "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" } } }, diff --git a/webscraping/pyppeteer/walgreens.ipynb b/webscraping/pyppeteer/walgreens.ipynb index 8647178..35710f9 100644 --- a/webscraping/pyppeteer/walgreens.ipynb +++ b/webscraping/pyppeteer/walgreens.ipynb @@ -9,10 +9,12 @@ ] }, { - "cell_type": "raw", - "id": "d5ddb5e6-f653-43c8-958f-e0d249b8b30d", + "cell_type": "markdown", + "id": "82fee814", "metadata": {}, "source": [ + "\n", + "```javascript\n", "export default async ({ page }: { page: Page }) => {\n", " const navigationPromise = page.waitForNavigation()\n", "\n", @@ -31,13 +33,14 @@ " await page.waitForNavigation()\n", " await page.screenshot({ path: screenshot })\n", "\n", - "};" + "};\n", + "```" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.13 64-bit", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -51,11 +54,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.4" }, "vscode": { "interpreter": { - "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" } } }, diff --git a/webscraping/rendering/splash.ipynb b/webscraping/rendering/splash.ipynb index 22d9da9..77f0c0c 100644 --- a/webscraping/rendering/splash.ipynb +++ b/webscraping/rendering/splash.ipynb @@ -8,45 +8,6 @@ "# Splash" ] }, - { - "cell_type": "markdown", - "id": "18805201-1cec-439d-bb09-ac9beba683b2", - "metadata": {}, - "source": [ - "render.json\n", - "Return a json-encoded dictionary with information about javascript-rendered webpage. It can include HTML, PNG and other information, based on arguments passed.\n", - "\n", - "Arguments:\n", - "\n", - "Same as render.jpeg plus the following ones:\n", - "\n", - "html : integer : optional\n", - "Whether to include HTML in output. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "png : integer : optional\n", - "Whether to include PNG in output. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "jpeg : integer : optional\n", - "Whether to include JPEG in output. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "iframes : integer : optional\n", - "Whether to include information about child frames in output. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "script : integer : optional\n", - "Whether to include the result of the executed javascript final statement in output (see Executing custom Javascript code within page context). Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "console : integer : optional\n", - "Whether to include the executed javascript console messages in output. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "history : integer : optional\n", - "Whether to include the history of requests/responses for webpage main frame. Possible values are 1 (include) and 0 (exclude). Default is 0.\n", - "\n", - "Use it to get HTTP status codes and headers. Only information about “main” requests/responses is returned (i.e. information about related resources like images and AJAX queries is not returned). To get information about all requests and responses use ‘har’ argument.\n", - "\n", - "har : integer : optional\n", - "Whether to include HAR in output. Possible values are 1 (include) and 0 (exclude). Default is 0. If this option is ON the result will contain the same data as render.har provides under ‘har’ key.\n", - "\n", - "By default, request and response contents are not included. To enable each, use ‘request_body’ and ‘response_body’ options respectively.\n", - "\n", - "request_body : int : optional\n", - "Possible values are 1 and 0. When request_body=1, request content is included in HAR records. Default is request_body=0. This option has no effect when both ‘har’ and ‘history’ are 0.\n", - "response_body : int : optiona" - ] - }, { "cell_type": "code", "execution_count": 27, @@ -101,12 +62,6 @@ "\n", "import requests\n", "\n", - "# script = \"\"\"\n", - "# splash:go(args.url)\n", - "# return splash:png()\n", - "# \"\"\"\n", - "\n", - "\n", "resp = requests.post(\n", " \"https://kong.donavanaldrich.com/splash\",\n", " json={\n", @@ -117,6 +72,7 @@ " \"har\": 1,\n", " },\n", ")\n", + "\n", "png_data = resp.content\n", "\n", "pp = pprint.PrettyPrinter(indent=4, sort_dicts=True)\n", @@ -125,7 +81,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 2, "id": "1630de92-c015-43a3-b747-9d29ca5afba2", "metadata": { "execution": { @@ -142,87 +98,72 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[34mHTTP\u001b[39;49;00m/\u001b[34m1.1\u001b[39;49;00m \u001b[34m200\u001b[39;49;00m \u001b[36mOK\u001b[39;49;00m\n", - "\u001b[36mContent-Type\u001b[39;49;00m: application/json\n", - "\u001b[36mDate\u001b[39;49;00m: Tue, 26 Oct 2021 04:36:54 GMT\n", - "\u001b[36mServer\u001b[39;49;00m: TwistedWeb/19.7.0\n", - "\u001b[36mTransfer-Encoding\u001b[39;49;00m: chunked\n", - "\u001b[36mVia\u001b[39;49;00m: kong/2.6.0\n", - "\u001b[36mX-Cache-Status\u001b[39;49;00m: Bypass\n", - "\u001b[36mX-Frame-Options\u001b[39;49;00m: DENY\n", - "\u001b[36mX-Kong-Proxy-Latency\u001b[39;49;00m: 5\n", - "\u001b[36mX-Kong-Upstream-Latency\u001b[39;49;00m: 91\n", + "Defaulting to user installation because normal site-packages is not writeable\n", + "Collecting httpie\n", + " Downloading httpie-3.2.1-py3-none-any.whl (124 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m125.0/125.0 KB\u001b[0m \u001b[31m1.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pip in /usr/lib/python3/dist-packages (from httpie) (22.0.2)\n", + "Requirement already satisfied: multidict>=4.7.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from httpie) (6.0.2)\n", + "Requirement already satisfied: requests[socks]>=2.22.0 in /usr/lib/python3/dist-packages (from httpie) (2.25.1)\n", + "Requirement already satisfied: setuptools in /usr/lib/python3/dist-packages (from httpie) (59.6.0)\n", + "Requirement already satisfied: rich>=9.10.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from httpie) (12.4.4)\n", + "Requirement already satisfied: defusedxml>=0.6.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from httpie) (0.7.1)\n", + "Collecting requests-toolbelt>=0.9.1\n", + " Using cached requests_toolbelt-0.9.1-py2.py3-none-any.whl (54 kB)\n", + "Requirement already satisfied: Pygments>=2.5.2 in /home/donaldrich/.local/lib/python3.10/site-packages (from httpie) (2.12.0)\n", + "Collecting charset-normalizer>=2.0.0\n", + " Using cached charset_normalizer-2.1.0-py3-none-any.whl (39 kB)\n", + "Collecting PySocks!=1.5.7,>=1.5.6\n", + " Downloading PySocks-1.7.1-py3-none-any.whl (16 kB)\n", + "Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /home/donaldrich/.local/lib/python3.10/site-packages (from rich>=9.10.0->httpie) (0.9.1)\n", + "Installing collected packages: requests-toolbelt, PySocks, charset-normalizer, httpie\n", + "Successfully installed PySocks-1.7.1 charset-normalizer-2.1.0 httpie-3.2.1 requests-toolbelt-0.9.1\n", + "Note: you may need to restart the kernel to use updated packages.\n", + "\u001b[34mHTTP\u001b[39;49;00m/\u001b[34m1.1\u001b[39;49;00m \u001b[34m404\u001b[39;49;00m \u001b[36mNot Found\u001b[39;49;00m\n", + "\u001b[36mCF-Cache-Status\u001b[39;49;00m: DYNAMIC\n", + "\u001b[36mCF-RAY\u001b[39;49;00m: 738eaac46e3e9f0a-DFW\n", + "\u001b[36mConnection\u001b[39;49;00m: keep-alive\n", + "\u001b[36mContent-Length\u001b[39;49;00m: 0\n", + "\u001b[36mDate\u001b[39;49;00m: Thu, 11 Aug 2022 05:45:47 GMT\n", + "\u001b[36mExpect-CT\u001b[39;49;00m: max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"\n", + "\u001b[36mNEL\u001b[39;49;00m: {\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}\n", + "\u001b[36mReport-To\u001b[39;49;00m: {\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=KMwuG83peUj0Eb0KdC4TpbV04BRgFZJfc9kuZ0pAAB7i3CBBxRPPQvw6BJrQgw1FgPGvY1cxMOHPpWfm4ioawmG8T73vyyOB0Jy8424rGniO5s7VJlDc2kbHzGlBx1EwE3cnNxcfK45pZg%3D%3D\"}],\"group\":\"cf-nel\",\"max_age\":604800}\n", + "\u001b[36mServer\u001b[39;49;00m: cloudflare\n", + "\u001b[36mStrict-Transport-Security\u001b[39;49;00m: max-age=15552000; includeSubDomains; preload\n", + "\u001b[36mX-Content-Type-Options\u001b[39;49;00m: nosniff\n", + "\u001b[36maccess-control-allow-credentials\u001b[39;49;00m: true\n", + "\u001b[36maccess-control-allow-origin\u001b[39;49;00m: donavanaldrich.com\n", + "\u001b[36maccess-control-expose-headers\u001b[39;49;00m: Origin,Authorization\n", + "\u001b[36malt-svc\u001b[39;49;00m: h3=\":443\"; ma=86400, h3-29=\":443\"; ma=86400\n", + "\u001b[36mratelimit-limit\u001b[39;49;00m: 5\n", + "\u001b[36mratelimit-remaining\u001b[39;49;00m: 4\n", + "\u001b[36mratelimit-reset\u001b[39;49;00m: 1\n", + "\u001b[36mvary\u001b[39;49;00m: Origin\n", + "\u001b[36mvia\u001b[39;49;00m: kong/2.8.1\n", + "\u001b[36mwww-authenticate\u001b[39;49;00m: Key realm=\"kong\"\n", + "\u001b[36mx-cache-status\u001b[39;49;00m: Bypass\n", + "\u001b[36mx-kong-proxy-latency\u001b[39;49;00m: 16\n", + "\u001b[36mx-kong-upstream-latency\u001b[39;49;00m: 119\n", + "\u001b[36mx-ratelimit-limit-minute\u001b[39;49;00m: 60\n", + "\u001b[36mx-ratelimit-limit-second\u001b[39;49;00m: 5\n", + "\u001b[36mx-ratelimit-remaining-minute\u001b[39;49;00m: 59\n", + "\u001b[36mx-ratelimit-remaining-second\u001b[39;49;00m: 4\n", "\n", - "{\n", - " \u001b[94m\"geometry\"\u001b[39;49;00m: [\n", - " \u001b[34m0\u001b[39;49;00m,\n", - " \u001b[34m0\u001b[39;49;00m,\n", - " \u001b[34m1024\u001b[39;49;00m,\n", - " \u001b[34m768\u001b[39;49;00m\n", - " ],\n", - " \u001b[94m\"har\"\u001b[39;49;00m: {\n", - " \u001b[94m\"log\"\u001b[39;49;00m: {\n", - " \u001b[94m\"browser\"\u001b[39;49;00m: {\n", - " \u001b[94m\"comment\"\u001b[39;49;00m: \u001b[33m\"PyQt 5.14.2, Qt 5.14.1\"\u001b[39;49;00m,\n", - " \u001b[94m\"name\"\u001b[39;49;00m: \u001b[33m\"QWebKit\"\u001b[39;49;00m,\n", - " \u001b[94m\"version\"\u001b[39;49;00m: \u001b[33m\"602.1\"\u001b[39;49;00m\n", - " },\n", - " \u001b[94m\"creator\"\u001b[39;49;00m: {\n", - " \u001b[94m\"name\"\u001b[39;49;00m: \u001b[33m\"Splash\"\u001b[39;49;00m,\n", - " \u001b[94m\"version\"\u001b[39;49;00m: \u001b[33m\"3.5\"\u001b[39;49;00m\n", - " },\n", - " \u001b[94m\"entries\"\u001b[39;49;00m: [],\n", - " \u001b[94m\"pages\"\u001b[39;49;00m: [\n", - " {\n", - " \u001b[94m\"id\"\u001b[39;49;00m: \u001b[33m\"1\"\u001b[39;49;00m,\n", - " \u001b[94m\"pageTimings\"\u001b[39;49;00m: {\n", - " \u001b[94m\"_onIframesRendered\"\u001b[39;49;00m: \u001b[34m84\u001b[39;49;00m,\n", - " \u001b[94m\"_onPngRendered\"\u001b[39;49;00m: \u001b[34m83\u001b[39;49;00m,\n", - " \u001b[94m\"_onPrepareStart\"\u001b[39;49;00m: \u001b[34m12\u001b[39;49;00m,\n", - " \u001b[94m\"_onScreenshotPrepared\"\u001b[39;49;00m: \u001b[34m25\u001b[39;49;00m,\n", - " \u001b[94m\"_onStarted\"\u001b[39;49;00m: \u001b[34m1\u001b[39;49;00m,\n", - " \u001b[94m\"onContentLoad\"\u001b[39;49;00m: \u001b[34m3\u001b[39;49;00m,\n", - " \u001b[94m\"onLoad\"\u001b[39;49;00m: \u001b[34m12\u001b[39;49;00m\n", - " },\n", - " \u001b[94m\"startedDateTime\"\u001b[39;49;00m: \u001b[33m\"2021-10-26T04:36:54.558005Z\"\u001b[39;49;00m,\n", - " \u001b[94m\"title\"\u001b[39;49;00m: \u001b[33m\"Example Domain\"\u001b[39;49;00m\n", - " }\n", - " ],\n", - " \u001b[94m\"version\"\u001b[39;49;00m: \u001b[33m\"1.2\"\u001b[39;49;00m\n", - " }\n", - " },\n", - " \u001b[94m\"history\"\u001b[39;49;00m: [],\n", - " \u001b[94m\"html\"\u001b[39;49;00m: \u001b[33m\"\\n Example Domain\\n\\n \\n \\n \\n \\n\\n\\n\\n
    \\n

    Example Domain

    \\n

    This domain is for use in illustrative examples in documents. You may use this\\n domain in literature without prior coordination or asking for permission.

    \\n

    More information...

    \\n
    \\n\\n\\n\"\u001b[39;49;00m,\n", - " \u001b[94m\"png\"\u001b[39;49;00m: \u001b[33m\"iVBORw0KGgoAAAANSUhEUgAABAAAAAMACAYAAAC6uhUNAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAgAElEQVR4AezdCdxU0x/H8dO+KyUtitBmCZFo36VNmzYqskUlZSeJ7EuKSqHsKhIJ7YsWJSr9ZRey7yT7/j/fkzvu3GfmmXn2eZ77Oa/X08zcuev7nHub87vnnFto585d/xgSAggggAACCCCAAAIIIIAAAggUaIHCBfroODgEEEAAAQQQQAABBBBAAAEEEHACBAAoCAgggAACCCCAAAIIIIAAAgiEQIAAQAgymUNEAAEEEEAAAQQQQAABBBBAgAAAZQABBBBAAAEEEEAAAQQQQACBEAgQAAhBJnOICCCAAAIIIIAAAggggAACCBAAoAwggAACCCCAAAIIIIAAAgggEAIBAgAhyGQOEQEEEEAAAQQQQAABBBBAAAECAJQBBBBAAAEEEEAAAQQQQAABBEIgQAAgBJnMISKAAAIIIIAAAggggAACCCBAAIAygAACCCCAAAIIIIAAAggggEAIBAgAhCCTOUQEEEAAAQQQQAABBBBAAAEECABQBhBAAAEEEEAAAQQQQAABBBAIgQABgBBkMoeIAAIIIIAAAggggAACCCCAAAEAygACCCCAAAIIIIAAAggggAACIRAgABCCTOYQEUAAAQQQQAABBBBAAAEEECAAQBlAAAEEEEAAAQQQQAABBBBAIAQCBABCkMkcIgIIIIAAAggggAACCCCAAAIEACgDCCCAAAIIIIAAAggggAACCIRAgABACDKZQ0QAAQQQQAABBBBAAAEEEECAAABlAAEEEEAAAQQQQAABBBBAAIEQCBAACEEmc4gIIIAAAggggAACCCCAAAIIEACgDCCAAAIIIIAAAggggAACCCAQAgECACHIZA4RAQQQQAABBBBAAAEEEEAAAQIAlAEEEEAAAQQQQAABBBBAAAEEQiBAACAEmcwhIoAAAggggAACCCCAAAIIIEAAgDKAAAIIIIAAAggggAACCCCAQAgECACEIJM5RAQQQAABBBBAAAEEEEAAAQQIAFAGEEAAAQQQQAABBBBAAAEEEAiBAAGAEGQyh4gAAggggAACCCCAAAIIIIAAAQDKAAIIIIAAAggggAACCCCAAAIhECAAEIJM5hARQAABBBBAAAEEEEAAAQQQIABAGUAAAQQQQAABBBBAAAEEEEAgBAIEAEKQyRwiAggggAACCCCAAAIIIIAAAgQAKAMIIIAAAggggAACCCCAAAIIhECAAEAIMplDRAABBBBAAAEEEEAAAQQQQIAAAGUAAQQQQAABBBBAAAEEEEAAgRAIEAAIQSZziAgggAACCCCAAAIIIIAAAggQAKAMIIAAAggggAACCCCAAAIIIBACAQIAIchkDhEBBBBAAAEEEEAAAQQQQAABAgCUAQQQQAABBBBAAAEEEEAAAQRCIEAAIASZzCEigAACCCCAAAIIIIAAAgggQACAMoAAAggggAACCCCAAAIIIIBACAQIAIQgkzlEBBBAAAEEEEAAAQQQQAABBAgAUAYQQAABBBBAAAEEEEAAAQQQCIEAAYAQZDKHiAACCCCAAAIIIIAAAggggAABAMoAAggggAACCCCAAAIIIIAAAiEQIAAQgkzmEBFAAAEEEEAAAQQQQAABBBAgAEAZQAABBBBAAAEEEEAAAQQQQCAEAgQAQpDJHCICCCCAAAIIIIAAAggggAACBAAoAwgggAACCCCAAAIIIIAAAgiEQIAAQAgymUNEAAEEEEAAAQQQQAABBBBAgAAAZQABBBBAAAEEEEAAAQQQQACBEAgQAAhBJnOICCCAAAIIIIAAAggggAACCBAAoAwggAACCCCAAAIIIIAAAgggEAIBAgAhyGQOEQEEEEAAAQQQQAABBBBAAAECAJQBBBBAAAEEEEAAAQQQQAABBEIgQAAgBJnMISKAAAIIIIAAAggggAACCCBAAIAygAACCCCAAAIIIIAAAggggEAIBAgAhCCTOUQEEEAAAQQQQAABBBBAAAEECABQBhBAAAEEEEAAAQQQQAABBBAIgQABgBBkMoeIAAIIIIAAAggggAACCCCAAAEAygACCCCAAAIIIIAAAggggAACIRAgABCCTOYQEUAAAQQQQAABBBBAAAEEECAAQBlAAAEEEEAAAQQQQAABBBBAIAQCBABCkMkcIgIIIIAAAggggAACCCCAAAIEACgDCCCAAAIIIIAAAggggAACCIRAgABACDKZQ0QAAQQQQAABBBBAAAEEEECAAABlAAEEEEAAAQQQQAABBBBAAIEQCBAACEEmc4gIIIAAAggggAACCCCAAAIIEACgDCCAAAIIIIAAAggggAACCCAQAgECACHIZA4RAQQQQAABBBBAAAEEEEAAAQIAlAEEEEAAAQQQQAABBBBAAAEEQiBAACAEmcwhIoAAAggggAACCCCAAAIIIEAAgDKAAAIIIIAAAggggAACCCCAQAgECACEIJM5RAQQQAABBBBAAAEEEEAAAQQIAFAGEEAAAQQQQAABBBBAAAEEEAiBQNEQHCOHiEBE4J9//om85w0CCCCAAAIIIIAAAkGBQoUKBSfxGYECI0AAoMBkJQcST4BKfzwZpiOAAAIIIIAAAggEBfy/HQkGBHX4nN8FCADk9xxk/+MK+C/esWZK9H2sZZiGAAIIIIAAAgggUPAE4lX0vd+L8b4veBIcUUEXIABQ0HM4hMfnXah16EWKFDbFihW3r0VN4cKFDBfvEBYIDhkBBBBAAAEEEEhHQL8d//77H/PXX3+a33//3b7+5eb2/270fl/6p6WzSr5CIGUFCACkbNawY5kR8C7OWrZkyZKmePHimVkNyyCAAAIIIIAAAgiERECV+iJF9Ffc/XZUEOCXX34x3u9Kf6Vf0/yfQ0LEYRYgAZ4CUIAyM+yH4l2k5VCmTGkq/2EvEBw/AggggAACCCCQCQHdQCpTpkxkSf9vTE0Mfo7MyBsE8oEAAYB8kEnsYsYEdOdfTf5JCCCAAAIIIIAAAghkRqBo0aKmVKmSkco+lf7MKLJMKgoQAEjFXGGfMizgXZTV559m/xnmYwEEEEAAAQQQQACBgEDx4iXsTaUiMYMA3m/PwCJ8RCDlBQgApHwWsYMZEdCAfyQEEEAAAQQQQAABBLJDwLux5FX4vdfsWDfrQCAvBAgA5IU628xWAf+FmKb/2UrLyhBAAAEEEEAAgVALqCtAvOT/DRpvHqYjkGoCBABSLUfYn0wL6CKsR/2REEAAAQQQQAABBBDIDoHChQun6QJAxT87ZFlHXgkQAMgrebabIwI8liVHWFkpAggggAACCCAQSgF+W4Yy2wv0QRMAKNDZW/APjghswc9jjhABBBBAAAEEEMhrgXi/OeNNz+v9ZfsIxBMgABBPhukIIIAAAggggAACCCCAgE+ACr8Pg7f5UoAAQL7MNnYaAQQQQAABBBBAAAEEEEAAgYwJEADImBdzp6gA0dgUzRh2CwEEEEAAAQQQKIAC/PYsgJkakkMiABCSjOYwEUAAAQQQQAABBBBAAAEEwi1AACDc+c/RI4AAAggggAACCCCAAAIIhESAAEBIMprDRAABBBBAAAEEEEAAAQQQCLcAAYBw5z9HjwACCCCAAAIIIIAAAgggEBIBAgAhyWgOEwEEEEAAAQQQQAABBBBAINwCBADCnf8cPQIIIIAAAggggAACCCCAQEgECACEJKM5TAQQQAABBBBAAAEEEEAAgXALEAAId/5z9AgggAACCCCAAAIIIIAAAiERIAAQkozmMBFAAAEEEEAAAQQQQAABBMItQAAg3PnP0SOAAAIIIIAAAggggAACCIREgABASDKaw0QAAQQQQAABBBBAAAEEEAi3AAGAcOc/R48AAggggAACCCCAAAIIIBASAQIAIcloDhMBBBBAAAEEEEAAAQQQQCDcAgQAwp3/HD0CCCCAAAIIIIAAAggggEBIBAgAhCSjOUwEEEAAAQQQQAABBBBAAIFwCxAACHf+c/QIIIAAAggggAACCCCAAAIhESAAEJKM5jARQAABBBBAAAEEEEAAAQTCLUAAINz5z9EjgAACCCCAAAIIIIAAAgiERIAAQEgymsNEAAEEEEAAAQQQQAABBBAItwABgHDnP0ePAAIIIIAAAggggAACCCAQEgECACHJaA4TAQQQQAABBBBAAAEEEEAg3AIEAMKd/xw9AggggAACCCCAAAIIIIBASAQIAIQkozlMBBBAAAEEEEAAAQQQQACBcAsQAAh3/nP0CCCAAAIIIIAAAggggAACIREgABCSjOYwEUAAAQQQQAABBBBAAAEEwi1AACDc+c/RI4AAAggggAACCCCAAAIIhESAAEBIMprDRAABBBBAAAEEEEAAAQQQCLcAAYBw5z9HjwACCCCAAAIIIIAAAgggEBIBAgAhyWgOEwEEEEAAAQQQQAABBBBAINwCBADCnf8cPQIIIIAAAggggAACCCCAQEgECACEJKM5TAQQQAABBBBAAAEEEEAAgXALEAAId/5z9AgggAACCCCAAAIIIIAAAiERIAAQkozmMBFAAAEEEEAAAQQQQAABBMItQAAg3PnP0SOAAAIIIIAAAggggAACCIREgABASDKaw0QAAQQQQAABBBBAAAEEEAi3AAGAcOc/R48AAggggAACCCCAAAIIIBASAQIAIcloDhMBBBBAAAEEEEAAAQQQQCDcAgQAwp3/HD0CCCCAAAIIIIAAAggggEBIBAgAhCSjOUwEEEAAAQQQQAABBBBAAIFwCxAACHf+c/QIIIAAAggggAACCCCA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wACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgMrFm1IAABF5SURBVAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBAwAi5ZbAgQIECBAgAABAgQIECAQFTAARIsTmwABAgQIECBAgAABAgQILAIGgEXLLQECBAgQIECAAAECBAgQiAoYAKLFiU2AAAECBAgQIECAAAECBBYBA8Ci5ZYAAQIECBAgQIAAAQIECEQFDADR4sQmQIAAAQIECBAgQIAAAQKLgAFg0XJLgAABAgQIECBAgAABAgSiAgaAaHFiEyBAgAABAgQIECBAgACBRcAAsGi5JUCAAAECBAgQIECAAAECUQEDQLQ4sQkQIECAAAECBAgQIECAwCJgAFi03BIgQIAAAQIECBAgQIAAgaiAASBanNgECBAgQIAAAQIECBAgQGARMAAsWm4JECBAgAABAgQIECBAgEBUwAAQLU5sAgQIECBAgAABAgQIECCwCBgAFi23BAgQIECAAAECBAgQIEAgKmAAiBYnNgECBAgQIECAAAECBAgQWAQMAIuWWwIECBAgQIAAAQIECBAgEBUwAESLE5sAAQIECBAgQIAAAQIECCwCBoBFyy0BAgQIECBAgAABAgQIEIgKGACixYlNgAABAgQIECBAgAABAgQWAQPAouWWAAECBAgQIECAAAECBAhEBQwA0eLEJkCAAAECBAgQIECAAAECi4ABYNFyS4AAAQIECBAgQIAAAQIEogIGgGhxYhMgQIAAAQIECBAgQIAAgUXAALBouSVAgAABAgQIECBAgAABAlEBA0C0OLEJECBAgAABAgQIECBAgMAiYABYtNwSIECAAAECBAgQIECAAIGogAEgWpzYBAgQIECAAAECBAgQIEBgETAALFpuCRAgQIAAAQIECBAgQIBAVMAAEC1ObAIECBAgQIAAAQIECBAgsAgYABYttwQIECBAgAABAgQIECBAICpgAIgWJzYBAgQIECBAgAABAgQIEFgEDACLllsCBAgQIECAAAECBAgQIBAVMABEixObAAECBAgQIECAAAECBAgsAgaARcstAQIECBAgQIAAAQIECBCIChgAosWJTYAAAQIECBAgQIAAAQIEFgEDwKLllgABAgQIECBAgAABAgQIRAUMANHixCZAgAABAgQIECBAgAABAouAAWDRckuAAAECBAgQIECAAAECBKICBoBocWITIECAAAECBAgQIECAAIFFwACwaLklQIAAAQIECBAgQIAAAQJRAQNAtDixCRAgQIAAAQIECBAgQIDAImAAWLTcEiBAgAABAgQIECBAgACBqIABIFqc2AQIECBAgAABAgQIECBAYBEwACxabgkQIECAAAECBAgQIECAQFTAABAtTmwCBAgQIECAAAECBAgQILAIGAAWLbcECBAgQIAAAQIECBAgQCAqYACIFic2AQIECBAgQIAAAQIECBBYBP4BYNIDFE+DzWsAAAAASUVORK5CYII=\"\u001b[39;49;00m,\n", - " \u001b[94m\"requestedUrl\"\u001b[39;49;00m: \u001b[33m\"http://example.com/\"\u001b[39;49;00m,\n", - " \u001b[94m\"title\"\u001b[39;49;00m: \u001b[33m\"Example Domain\"\u001b[39;49;00m,\n", - " \u001b[94m\"url\"\u001b[39;49;00m: \u001b[33m\"http://example.com/\"\u001b[39;49;00m\n", - "}\n", "\n", "\n" ] } ], "source": [ - "# json: {url: \"http://example.com\", history: 1, html: 1, request_body: 1, har: 1}\n", - "# !http POST https://kong.donavanaldrich.com/splash < json\n", - "\n", + "%pip install httpie\n", "\n", "!http POST https://kong.donavanaldrich.com/splash url=http://example.com har:=1 html:=1 png:=1 history:=1 console:=1" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc1283f7-19f8-4430-9ae6-19a74c419a91", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -236,7 +177,12 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.10" + "version": "3.10.4" + }, + "vscode": { + "interpreter": { + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" + } } }, "nbformat": 4, diff --git a/webscraping/robot_framework/Untitled.ipynb b/webscraping/robot_framework/Untitled.ipynb index b2b49ce..c2e6d0a 100644 --- a/webscraping/robot_framework/Untitled.ipynb +++ b/webscraping/robot_framework/Untitled.ipynb @@ -11,22 +11,9 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "raw", "id": "d2f5ca23-5eed-4eb6-9c1b-2152b08009a5", "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31mFailed to start the Kernel. \n", - "Kernel Python 3.9.13 64-bit is not usable. Check the Jupyter output tab for more information. \n", - "View Jupyter log for further details." - ] - } - ], "source": [ "*** Settings ***\n", "Documentation Simple example using SeleniumLibrary.\n", @@ -64,30 +51,11 @@ "Welcome Page Should Be Open\n", " Title Should Be Welcome Page" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78a7b507-e8f5-4dab-9d38-fb5396e92b42", - "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31mFailed to start the Kernel. \n", - "Kernel Python 3.9.13 64-bit is not usable. Check the Jupyter output tab for more information. \n", - "View Jupyter log for further details." - ] - } - ], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3.9.13 64-bit", + "display_name": "Python 3.10.4 64-bit", "language": "python", "name": "python3" }, @@ -101,11 +69,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.4" }, "vscode": { "interpreter": { - "hash": "b0fa6594d8f4cbf19f97940f81e996739fb7646882a419484c72d19e05852a7e" + "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" } } },