Skip to content

Commit

Permalink
Convert %help requests to URL-safe
Browse files Browse the repository at this point in the history
  • Loading branch information
hugetim committed Oct 28, 2024
1 parent d4ca8ab commit 79a34b1
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 19 deletions.
154 changes: 136 additions & 18 deletions nbs/09_magics.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,8 @@
"def _get_help_html(self, code):\n",
" html_base = \"https://www.stata.com\"\n",
" html_help = urllib.parse.urljoin(html_base, \"help.cgi?{}\")\n",
" reply = urllib.request.urlopen(html_help.format(code))\n",
" url_safe_code = urllib.parse.quote(code)\n",
" reply = urllib.request.urlopen(html_help.format(url_safe_code))\n",
" html = reply.read().decode(\"utf-8\")\n",
"\n",
" # Remove excessive extra lines (Note css: \"white-space: pre-wrap\")\n",
Expand Down Expand Up @@ -1129,28 +1130,145 @@
"metadata": {},
"outputs": [
{
"ename": "HTTPError",
"evalue": "HTTP Error 403: Forbidden",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mHTTPError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[52], line 3\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;66;03m#| eval: False\u001b[39;00m\n\u001b[0;32m 2\u001b[0m test_instance \u001b[38;5;241m=\u001b[39m StataMagics()\n\u001b[1;32m----> 3\u001b[0m out \u001b[38;5;241m=\u001b[39m \u001b[43mtest_instance\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_get_help_html\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43morder\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 4\u001b[0m HTML(out)\n",
"Cell \u001b[1;32mIn[49], line 6\u001b[0m, in \u001b[0;36m_get_help_html\u001b[1;34m(self, code)\u001b[0m\n\u001b[0;32m 4\u001b[0m html_base \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttps://www.stata.com\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 5\u001b[0m html_help \u001b[38;5;241m=\u001b[39m urllib\u001b[38;5;241m.\u001b[39mparse\u001b[38;5;241m.\u001b[39murljoin(html_base, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhelp.cgi?\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m----> 6\u001b[0m reply \u001b[38;5;241m=\u001b[39m \u001b[43murllib\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43murlopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43mhtml_help\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mformat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcode\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 7\u001b[0m html \u001b[38;5;241m=\u001b[39m reply\u001b[38;5;241m.\u001b[39mread()\u001b[38;5;241m.\u001b[39mdecode(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mutf-8\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 9\u001b[0m \u001b[38;5;66;03m# Remove excessive extra lines (Note css: \"white-space: pre-wrap\")\u001b[39;00m\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:216\u001b[0m, in \u001b[0;36murlopen\u001b[1;34m(url, data, timeout, cafile, capath, cadefault, context)\u001b[0m\n\u001b[0;32m 214\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 215\u001b[0m opener \u001b[38;5;241m=\u001b[39m _opener\n\u001b[1;32m--> 216\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mopener\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43murl\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:525\u001b[0m, in \u001b[0;36mOpenerDirector.open\u001b[1;34m(self, fullurl, data, timeout)\u001b[0m\n\u001b[0;32m 523\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m processor \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprocess_response\u001b[38;5;241m.\u001b[39mget(protocol, []):\n\u001b[0;32m 524\u001b[0m meth \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(processor, meth_name)\n\u001b[1;32m--> 525\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mmeth\u001b[49m\u001b[43m(\u001b[49m\u001b[43mreq\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mresponse\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 527\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:634\u001b[0m, in \u001b[0;36mHTTPErrorProcessor.http_response\u001b[1;34m(self, request, response)\u001b[0m\n\u001b[0;32m 631\u001b[0m \u001b[38;5;66;03m# According to RFC 2616, \"2xx\" code indicates that the client's\u001b[39;00m\n\u001b[0;32m 632\u001b[0m \u001b[38;5;66;03m# request was successfully received, understood, and accepted.\u001b[39;00m\n\u001b[0;32m 633\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;241m200\u001b[39m \u001b[38;5;241m<\u001b[39m\u001b[38;5;241m=\u001b[39m code \u001b[38;5;241m<\u001b[39m \u001b[38;5;241m300\u001b[39m):\n\u001b[1;32m--> 634\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 635\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mhttp\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrequest\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mresponse\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcode\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmsg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mhdrs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 637\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:563\u001b[0m, in \u001b[0;36mOpenerDirector.error\u001b[1;34m(self, proto, *args)\u001b[0m\n\u001b[0;32m 561\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m http_err:\n\u001b[0;32m 562\u001b[0m args \u001b[38;5;241m=\u001b[39m (\u001b[38;5;28mdict\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdefault\u001b[39m\u001b[38;5;124m'\u001b[39m, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mhttp_error_default\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;241m+\u001b[39m orig_args\n\u001b[1;32m--> 563\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_chain\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:496\u001b[0m, in \u001b[0;36mOpenerDirector._call_chain\u001b[1;34m(self, chain, kind, meth_name, *args)\u001b[0m\n\u001b[0;32m 494\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m handler \u001b[38;5;129;01min\u001b[39;00m handlers:\n\u001b[0;32m 495\u001b[0m func \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(handler, meth_name)\n\u001b[1;32m--> 496\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 497\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 498\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m result\n",
"File \u001b[1;32mc:\\Users\\tjhuegerich\\AppData\\Local\\anaconda3\\envs\\nbstata_dev\\Lib\\urllib\\request.py:643\u001b[0m, in \u001b[0;36mHTTPDefaultErrorHandler.http_error_default\u001b[1;34m(self, req, fp, code, msg, hdrs)\u001b[0m\n\u001b[0;32m 642\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mhttp_error_default\u001b[39m(\u001b[38;5;28mself\u001b[39m, req, fp, code, msg, hdrs):\n\u001b[1;32m--> 643\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(req\u001b[38;5;241m.\u001b[39mfull_url, code, msg, hdrs, fp)\n",
"\u001b[1;31mHTTPError\u001b[0m: HTTP Error 403: Forbidden"
]
"data": {
"text/html": [
"\n",
"<html>\n",
"<head>\n",
"<meta content=\"noindex, nofollow\" name=\"robots\"/>\n",
"<title>StataNow 18 help for graph set</title>\n",
"<link href=\"/includes/css/stylehelp.css\" rel=\"stylesheet\" type=\"text/css\"/>\n",
"<style type=\"text/css\">h2 {\n",
" font-family: Arial,Helvetica,Helv,sans-serif;\n",
" color: #000000;\n",
"}\n",
"\n",
"pre {\n",
" margin: 10px;\n",
"}\n",
"\n",
"table {\n",
" background-color: transparent;\n",
" border-color: transparent;\n",
" bgcolor: transparent;\n",
"}\n",
"\n",
"tr {\n",
" background-color: transparent;\n",
" border-color: transparent;\n",
" bgcolor: transparent;\n",
"}\n",
"\n",
"body {\n",
" background-color: transparent;\n",
" border-color: transparent;\n",
" margin: 0px;\n",
"}\n",
"\n",
"div {\n",
" background-color: transparent;\n",
" border-color: transparent;\n",
"}\n",
"</style>\n",
"</head>\n",
"<body>\n",
"\n",
"<p><table align=\"center\" bgcolor=\"transparent\" border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n",
"<tr bgcolor=\"transparent\"><td align=\"left\" bgcolor=\"transparent\" valign=\"top\">\n",
"<!-- END HEAD -->\n",
"\n",
"<pre>\n",
"<p><b>[G-2] graph set</b> -- Set graphics options\n",
"<p><p><a name=\"syntax\"></a><b><u>Syntax</u></b>\n",
"<p> Manage graph print settings\n",
"<p> <b><u>gr</u></b><b>aph</b> <b>set</b> <b>print</b> [<i>setopt</i> <i>setval</i>]\n",
"<p><p> Manage graph export settings\n",
"<p> <b><u>gr</u></b><b>aph</b> <b>set</b> [<i>exporttype</i>] [<i>setopt</i> <i>setval</i>]\n",
"<p> where <i>exporttype</i> is the export file type and may be one of \n",
"<p> <b>ps</b> | <b>eps</b> | <b>svg</b> | <b>pdf</b>\n",
"<p> and <i>setopt</i> is the option to set with the setting <i>setval</i>.\n",
"<p><p> Manage Graph window font settings\n",
"<p> <b><u>gr</u></b><b>aph</b> <b>set</b> <b>window</b> <b>fontface</b> { <i>fontname</i> | <b>default</b> }\n",
" <b><u>gr</u></b><b>aph</b> <b>set</b> <b>window</b> <b>fontfacemono</b> { <i>fontname</i> | <b>default</b> }\n",
" <b><u>gr</u></b><b>aph</b> <b>set</b> <b>window</b> <b>fontfacesans</b> { <i>fontname</i> | <b>default</b> }\n",
" <b><u>gr</u></b><b>aph</b> <b>set</b> <b>window</b> <b>fontfaceserif</b> { <i>fontname</i> | <b>default</b> }\n",
" <b><u>gr</u></b><b>aph</b> <b>set</b> <b>window</b> <b>fontfacesymbol</b> { <i>fontname</i> | <b>default</b> }\n",
"<p><p><a name=\"description\"></a><b><u>Description</u></b>\n",
"<p> <b>graph</b> <b>set</b> without options lists the current graphics font, print, and\n",
" export settings for all <i>exporttype</i>s. <b>graph set</b> with <b>window</b>, <b>print</b>, or\n",
" <i>exporttype</i> lists the current settings for the Graph window, for printing,\n",
" or for the specified <i>exporttype</i>, respectively.\n",
"<p> <b>graph</b> <b>set</b> <b>print</b> allows you to change the print settings for graphics.\n",
"<p> <b>graph</b> <b>set</b> <i>exporttype</i> allows you to change the graphics export settings\n",
" for export file type <i>exporttype</i>.\n",
"<p> <b>graph</b> <b>set</b> <b>window</b> <b>fontface</b><i>*</i> allows you to change the Graph window font\n",
" settings. (To change font settings for graphs exported to PostScript,\n",
" Encapsulated PostScript, Scalable Vector Graphic, or Portable Document\n",
" Format files, use <b>graph</b> <b>set</b> {<b>ps</b>|<b>eps</b>|<b>svg</b>|<b>pdf</b>} <b>fontface</b><i>*</i>; see <a href=\"https://www.stata.com/help.cgi?ps_options\" target=\"_blank\"><b>[G-3]</b></a>\n",
" <a href=\"https://www.stata.com/help.cgi?ps_options\" target=\"_blank\"><i>ps_options</i></a>, <a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"><i>eps_options</i></a>, <a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"><i>svg_options</i></a>, or <a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"><i>pdf_options</i></a>.)\n",
" If <i>fontname</i> contains spaces, enclose it in double quotes. If you specify\n",
" <b>default</b> for any of the <b>fontface</b><i>*</i> settings, the default setting will be\n",
" restored.\n",
"<p><p><a name=\"remarks\"></a><b><u>Remarks</u></b>\n",
"<p> Remarks are presented under the following headings:\n",
"<p> <a href=\"https://www.stata.com/help.cgi?graph_set%23remarks1\" target=\"_blank\">Overview</a>\n",
" <a href=\"https://www.stata.com/help.cgi?graph_set%23remarks2\" target=\"_blank\">Setting defaults</a>\n",
"<p><p><a name=\"remarks1\"></a><b><u>Overview</u></b>\n",
"<p> <b>graph set</b> allows you to permanently set the primary font face used in the\n",
" Graph window as well as the font faces to be used for the four Stata\n",
" \"font faces\" supported by the graph SMCL tags <b>{stMono}</b>, <b>{stSans}</b>,\n",
" <b>{stSerif}</b>, and <b>{stSymbol}</b>. See <a href=\"https://www.stata.com/help.cgi?graph_text\" target=\"_blank\"><b>[G-4]</b></a><a href=\"https://www.stata.com/help.cgi?graph_text\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?graph_text\" target=\"_blank\"><i>text</i></a> for more details on these SMCL\n",
" tags.\n",
"<p> <b>graph set</b> also allows you to permanently set any of the options supported\n",
" by <b>graph print</b> (see <a href=\"https://www.stata.com/help.cgi?graph_print\" target=\"_blank\"><b>[G-2] graph print</b></a>) or by the specific export file\n",
" types provided by <b>graph export</b> (see <a href=\"https://www.stata.com/help.cgi?graph_export\" target=\"_blank\"><b>[G-2] graph export</b></a>).\n",
"<p> To find out more about the <b>graph</b> <b>set</b> <b>print</b> <i>setopt</i> options and their\n",
" associated values (<i>setval</i>), see <a href=\"https://www.stata.com/help.cgi?pr_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?pr_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?pr_options\" target=\"_blank\"><i>pr_options</i></a>.\n",
"<p> Some graphics file types supported by <a href=\"https://www.stata.com/help.cgi?graph%2Bexport\" target=\"_blank\"><b>graph export</b></a> have options that can\n",
" be set. The file types that allow option settings and their associated\n",
" <i>exporttype</i>s are\n",
"<p> <i>exporttype</i> Description Available settings\n",
" ------------------------------------------------------------\n",
" <b>ps</b> PostScript <a href=\"https://www.stata.com/help.cgi?ps_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?ps_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?ps_options\" target=\"_blank\"><i>ps_options</i></a>\n",
" <b>eps</b> Encapsulated PostScript <a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?eps_options\" target=\"_blank\"><i>eps_options</i></a>\n",
" <b>svg</b> Scalable Vector Graphics <a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?svg_options\" target=\"_blank\"><i>svg_options</i></a>\n",
" <b>pdf</b> Portable Document Format <a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"><b>[G-3]</b></a><a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"> </a><a href=\"https://www.stata.com/help.cgi?pdf_options\" target=\"_blank\"><i>pdf_options</i></a>\n",
" ------------------------------------------------------------\n",
"<p><p><a name=\"remarks2\"></a><b><u>Setting defaults</u></b>\n",
"<p> If you always want the Graph window to use Times New Roman as its default\n",
" font, you could type\n",
"<p> <b>. graph set window fontface \"Times New Roman\"</b>\n",
"<p> Later, you could type\n",
"<p> <b>. graph set window fontface default</b>\n",
"<p> to restore the factory setting.\n",
"<p> To change the font used by <b>{stMono}</b> in the Graph window, you could type\n",
"<p> <b>. graph set window fontfacemono \"Lucida Console\"</b>\n",
"<p> and to reset it, you could type\n",
"<p> <b>. graph set window fontfacemono default</b>\n",
"<p> You can list the current graph settings by typing\n",
"<p> <b>. graph set</b>\n",
"</p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></p></pre>\n",
"<!-- BEGIN FOOT -->\n",
"</td></tr></table>\n",
"\n",
"\n",
"\n",
"</p></body>\n",
"</html>\n"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"execution_count": null,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"#| eval: False\n",
"test_instance = StataMagics()\n",
"out = test_instance._get_help_html(\"order\")\n",
"out = test_instance._get_help_html(\"graph set\")\n",
"HTML(out)"
]
},
Expand Down
3 changes: 2 additions & 1 deletion nbstata/magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,8 @@ def _magic_frheadtail(self, code, kernel, cell, tail):
def _get_help_html(self, code):
html_base = "https://www.stata.com"
html_help = urllib.parse.urljoin(html_base, "help.cgi?{}")
reply = urllib.request.urlopen(html_help.format(code))
url_safe_code = urllib.parse.quote(code)
reply = urllib.request.urlopen(html_help.format(url_safe_code))
html = reply.read().decode("utf-8")

# Remove excessive extra lines (Note css: "white-space: pre-wrap")
Expand Down

0 comments on commit 79a34b1

Please sign in to comment.