├── .gitignore ├── AUTHORS.rst ├── CHANGES.rst ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile └── source │ ├── _theme │ └── default │ │ ├── layout.html │ │ ├── static │ │ ├── default.css_t │ │ └── sidebar.js │ │ └── theme.conf │ ├── conf.py │ └── index.rst ├── setup.py └── src └── httpcode └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | build 4 | dist 5 | src/*.egg-info -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | `httpcode` is written and maintained by Ruslan Spivak and 2 | various contributors: 3 | 4 | Patches and Suggestions 5 | ``````````````````````` 6 | 7 | - Flavio Curella, https://github.com/fcurella 8 | - Peter Aronoff, https://github.com/telemachus 9 | - Mark Striemer, https://github.com/mstriemer -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Change History 2 | ============== 3 | 4 | 0.6 (2017-04-03) 5 | ---------------- 6 | - Use argparse instead of optparse 7 | - Declare as Python 2.7+ and Python 3 compatible 8 | 9 | 0.5 (2011-12-30) 10 | ---------------- 11 | - Colorize HTTP codes 12 | 13 | 0.4 (2011-12-27) 14 | ---------------- 15 | - regex and 'x' can be used as part of an HTTP code 16 | https://github.com/rspivak/httpcode/pull/6 17 | 18 | 0.3 (2011-12-22) 19 | ---------------- 20 | - Add -s/--search option to search for code by description 21 | https://github.com/rspivak/httpcode/pull/3 22 | 23 | 0.2 (2011-12-21) 24 | ---------------- 25 | - Add HTTP code 418 (I'm a teapot): https://github.com/rspivak/httpcode/pull/2 26 | - Bugfix: https://github.com/rspivak/httpcode/issues/1 27 | 28 | 0.1 (2011-12-21) 29 | ---------------- 30 | - Initial release 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst CHANGES.rst -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | :: 2 | 3 | _ _ _____ _____ ____ ____ ___ ____ _____ 4 | | | | |_ _|_ _| _ \ / ___/ _ \| _ \| ____| 5 | | |_| | | | | | | |_) | | | | | | | | | _| 6 | | _ | | | | | | __/| |__| |_| | |_| | |___ 7 | |_| |_| |_| |_| |_| \____\___/|____/|_____| 8 | 9 | 10 | `httpcode` is a little utility that explains the meaning of an HTTP 11 | status code on the command line. 12 | 13 | `http://httpcode.readthedocs.org `_ 14 | 15 | Installation 16 | ------------ 17 | 18 | :: 19 | 20 | $ [sudo] pip install httpcode 21 | 22 | There is also an official DEB package available at 23 | `http://packages.debian.org/sid/httpcode `_ 24 | 25 | 26 | Usage 27 | ----- 28 | 29 | Explain 405 status code 30 | 31 | :: 32 | 33 | $ hc 405 34 | Status code 405 35 | Message: Method Not Allowed 36 | Code explanation: Specified method is invalid for this resource. 37 | 38 | Or 418 status code :) 39 | 40 | :: 41 | 42 | $ hc 418 43 | Status code 418 44 | Message: I'm a teapot 45 | Code explanation: The HTCPCP server is a teapot 46 | 47 | List all codes 48 | 49 | :: 50 | 51 | $ hc 52 | Status code 100 53 | Message: Continue 54 | Code explanation: Request received, please continue 55 | 56 | Status code 101 57 | Message: Switching Protocols 58 | Code explanation: Switching to new protocol; obey Upgrade header 59 | 60 | Status code 200 61 | Message: OK 62 | Code explanation: Request fulfilled, document follows 63 | 64 | ... 65 | 66 | Search code(s) by description (case-insensitive) 67 | 68 | :: 69 | 70 | $ hc -s too 71 | Status code 413 72 | Message: Request Entity Too Large 73 | Code explanation: Entity is too large. 74 | 75 | Status code 414 76 | Message: Request-URI Too Long 77 | Code explanation: URI is too long. 78 | 79 | Filter codes with a regex 80 | 81 | :: 82 | 83 | $ hc 30[12] 84 | Status code 301 85 | Message: Moved Permanently 86 | Code explanation: Object moved permanently -- see URI list 87 | 88 | Status code 302 89 | Message: Found 90 | Code explanation: Object moved temporarily -- see URI list 91 | 92 | Use an 'x' for any digit 93 | 94 | :: 95 | 96 | $ hc 1xx 97 | Status code 100 98 | Message: Continue 99 | Code explanation: Request received, please continue 100 | 101 | Status code 101 102 | Message: Switching Protocols 103 | Code explanation: Switching to new protocol; obey Upgrade header 104 | 105 | Show help 106 | 107 | :: 108 | 109 | $ hc -h 110 | Usage: hc [code] [options] 111 | 112 | code may contain regular expression or use 'x' to denote any digit 113 | code examples: 418, 30[12], 3.*, 1xx 114 | 115 | Without parameters lists all available 116 | HTTP status codes and their description 117 | 118 | 119 | Options: 120 | -h, --help show this help message and exit 121 | -s SEARCH, --search=SEARCH 122 | Search for a code by name or description. Search text 123 | may contain regular expressions. 124 | 125 | Roadmap 126 | ------- 127 | 128 | Add more codes 129 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/httpcode.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/httpcode.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/httpcode" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/httpcode" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/source/_theme/default/layout.html: -------------------------------------------------------------------------------- 1 | {% extends "basic/layout.html" %} 2 | 3 | {%- block extrahead %} 4 | Fork me on GitHub 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /docs/source/_theme/default/static/default.css_t: -------------------------------------------------------------------------------- 1 | /* 2 | * default.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- default theme. 6 | * 7 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: {{ theme_bodyfont }}; 18 | font-size: 100%; 19 | background-color: {{ theme_footerbgcolor }}; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: {{ theme_sidebarbgcolor }}; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 {{ theme_sidebarwidth|toint }}px; 36 | } 37 | 38 | div.body { 39 | background-color: {{ theme_bgcolor }}; 40 | color: {{ theme_textcolor }}; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | {%- if theme_rightsidebar|tobool %} 45 | div.bodywrapper { 46 | margin: 0 {{ theme_sidebarwidth|toint }}px 0 0; 47 | } 48 | {%- endif %} 49 | 50 | div.footer { 51 | color: {{ theme_footertextcolor }}; 52 | width: 100%; 53 | padding: 9px 0 9px 0; 54 | text-align: center; 55 | font-size: 75%; 56 | } 57 | 58 | div.footer a { 59 | color: {{ theme_footertextcolor }}; 60 | text-decoration: underline; 61 | } 62 | 63 | div.related { 64 | background-color: {{ theme_relbarbgcolor }}; 65 | line-height: 30px; 66 | color: {{ theme_relbartextcolor }}; 67 | } 68 | 69 | div.related a { 70 | color: {{ theme_relbarlinkcolor }}; 71 | } 72 | 73 | div.sphinxsidebar { 74 | {%- if theme_stickysidebar|tobool %} 75 | top: 30px; 76 | bottom: 0; 77 | margin: 0; 78 | position: fixed; 79 | overflow: auto; 80 | height: auto; 81 | {%- endif %} 82 | {%- if theme_rightsidebar|tobool %} 83 | float: right; 84 | {%- if theme_stickysidebar|tobool %} 85 | right: 0; 86 | {%- endif %} 87 | {%- endif %} 88 | } 89 | 90 | {%- if theme_stickysidebar|tobool %} 91 | /* this is nice, but it it leads to hidden headings when jumping 92 | to an anchor */ 93 | /* 94 | div.related { 95 | position: fixed; 96 | } 97 | 98 | div.documentwrapper { 99 | margin-top: 30px; 100 | } 101 | */ 102 | {%- endif %} 103 | 104 | div.sphinxsidebar h3 { 105 | font-family: {{ theme_headfont }}; 106 | color: {{ theme_sidebartextcolor }}; 107 | font-size: 1.4em; 108 | font-weight: normal; 109 | margin: 0; 110 | padding: 0; 111 | } 112 | 113 | div.sphinxsidebar h3 a { 114 | color: {{ theme_sidebartextcolor }}; 115 | } 116 | 117 | div.sphinxsidebar h4 { 118 | font-family: {{ theme_headfont }}; 119 | color: {{ theme_sidebartextcolor }}; 120 | font-size: 1.3em; 121 | font-weight: normal; 122 | margin: 5px 0 0 0; 123 | padding: 0; 124 | } 125 | 126 | div.sphinxsidebar p { 127 | color: {{ theme_sidebartextcolor }}; 128 | } 129 | 130 | div.sphinxsidebar p.topless { 131 | margin: 5px 10px 10px 10px; 132 | } 133 | 134 | div.sphinxsidebar ul { 135 | margin: 10px; 136 | padding: 0; 137 | color: {{ theme_sidebartextcolor }}; 138 | } 139 | 140 | div.sphinxsidebar a { 141 | color: {{ theme_sidebarlinkcolor }}; 142 | } 143 | 144 | div.sphinxsidebar input { 145 | border: 1px solid {{ theme_sidebarlinkcolor }}; 146 | font-family: sans-serif; 147 | font-size: 1em; 148 | } 149 | 150 | {% if theme_collapsiblesidebar|tobool %} 151 | /* for collapsible sidebar */ 152 | div#sidebarbutton { 153 | background-color: {{ theme_sidebarbtncolor }}; 154 | } 155 | {% endif %} 156 | 157 | /* -- hyperlink styles ------------------------------------------------------ */ 158 | 159 | a { 160 | color: {{ theme_linkcolor }}; 161 | text-decoration: none; 162 | } 163 | 164 | a:visited { 165 | color: {{ theme_visitedlinkcolor }}; 166 | text-decoration: none; 167 | } 168 | 169 | a:hover { 170 | text-decoration: underline; 171 | } 172 | 173 | {% if theme_externalrefs|tobool %} 174 | a.external { 175 | text-decoration: none; 176 | border-bottom: 1px dashed {{ theme_linkcolor }}; 177 | } 178 | 179 | a.external:hover { 180 | text-decoration: none; 181 | border-bottom: none; 182 | } 183 | 184 | a.external:visited { 185 | text-decoration: none; 186 | border-bottom: 1px dashed {{ theme_visitedlinkcolor }}; 187 | } 188 | {% endif %} 189 | 190 | /* -- body styles ----------------------------------------------------------- */ 191 | 192 | div.body h1, 193 | div.body h2, 194 | div.body h3, 195 | div.body h4, 196 | div.body h5, 197 | div.body h6 { 198 | font-family: {{ theme_headfont }}; 199 | background-color: {{ theme_headbgcolor }}; 200 | font-weight: normal; 201 | color: {{ theme_headtextcolor }}; 202 | border-bottom: 1px solid #ccc; 203 | margin: 20px -20px 10px -20px; 204 | padding: 3px 0 3px 10px; 205 | } 206 | 207 | div.body h1 { margin-top: 0; font-size: 200%; } 208 | div.body h2 { font-size: 160%; } 209 | div.body h3 { font-size: 140%; } 210 | div.body h4 { font-size: 120%; } 211 | div.body h5 { font-size: 110%; } 212 | div.body h6 { font-size: 100%; } 213 | 214 | a.headerlink { 215 | color: {{ theme_headlinkcolor }}; 216 | font-size: 0.8em; 217 | padding: 0 4px 0 4px; 218 | text-decoration: none; 219 | } 220 | 221 | a.headerlink:hover { 222 | background-color: {{ theme_headlinkcolor }}; 223 | color: white; 224 | } 225 | 226 | div.body p, div.body dd, div.body li { 227 | text-align: justify; 228 | line-height: 130%; 229 | } 230 | 231 | div.admonition p.admonition-title + p { 232 | display: inline; 233 | } 234 | 235 | div.admonition p { 236 | margin-bottom: 5px; 237 | } 238 | 239 | div.admonition pre { 240 | margin-bottom: 5px; 241 | } 242 | 243 | div.admonition ul, div.admonition ol { 244 | margin-bottom: 5px; 245 | } 246 | 247 | div.note { 248 | background-color: #eee; 249 | border: 1px solid #ccc; 250 | } 251 | 252 | div.seealso { 253 | background-color: #ffc; 254 | border: 1px solid #ff6; 255 | } 256 | 257 | div.topic { 258 | background-color: #eee; 259 | } 260 | 261 | div.warning { 262 | background-color: #ffe4e4; 263 | border: 1px solid #f66; 264 | } 265 | 266 | p.admonition-title { 267 | display: inline; 268 | } 269 | 270 | p.admonition-title:after { 271 | content: ":"; 272 | } 273 | 274 | pre { 275 | padding: 5px; 276 | background-color: {{ theme_codebgcolor }}; 277 | color: {{ theme_codetextcolor }}; 278 | line-height: 120%; 279 | border: 1px solid #ac9; 280 | border-left: none; 281 | border-right: none; 282 | } 283 | 284 | tt { 285 | background-color: #ecf0f3; 286 | padding: 0 1px 0 1px; 287 | font-size: 0.95em; 288 | } 289 | 290 | th { 291 | background-color: #ede; 292 | } 293 | 294 | .warning tt { 295 | background: #efc2c2; 296 | } 297 | 298 | .note tt { 299 | background: #d6d6d6; 300 | } 301 | 302 | .viewcode-back { 303 | font-family: {{ theme_bodyfont }}; 304 | } 305 | 306 | div.viewcode-block:target { 307 | background-color: #f4debf; 308 | border-top: 1px solid #ac9; 309 | border-bottom: 1px solid #ac9; 310 | } 311 | -------------------------------------------------------------------------------- /docs/source/_theme/default/static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | // global elements used by the functions. 26 | // the 'sidebarbutton' element is defined as global after its 27 | // creation, in the add_sidebar_button function 28 | var bodywrapper = $('.bodywrapper'); 29 | var sidebar = $('.sphinxsidebar'); 30 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 31 | 32 | // for some reason, the document has no sidebar; do not run into errors 33 | if (!sidebar.length) return; 34 | 35 | // original margin-left of the bodywrapper and width of the sidebar 36 | // with the sidebar expanded 37 | var bw_margin_expanded = bodywrapper.css('margin-left'); 38 | var ssb_width_expanded = sidebar.width(); 39 | 40 | // margin-left of the bodywrapper and width of the sidebar 41 | // with the sidebar collapsed 42 | var bw_margin_collapsed = '.8em'; 43 | var ssb_width_collapsed = '.8em'; 44 | 45 | // colors used by the current theme 46 | var dark_color = $('.related').css('background-color'); 47 | var light_color = $('.document').css('background-color'); 48 | 49 | function sidebar_is_collapsed() { 50 | return sidebarwrapper.is(':not(:visible)'); 51 | } 52 | 53 | function toggle_sidebar() { 54 | if (sidebar_is_collapsed()) 55 | expand_sidebar(); 56 | else 57 | collapse_sidebar(); 58 | } 59 | 60 | function collapse_sidebar() { 61 | sidebarwrapper.hide(); 62 | sidebar.css('width', ssb_width_collapsed); 63 | bodywrapper.css('margin-left', bw_margin_collapsed); 64 | sidebarbutton.css({ 65 | 'margin-left': '0', 66 | 'height': bodywrapper.height() 67 | }); 68 | sidebarbutton.find('span').text('»'); 69 | sidebarbutton.attr('title', _('Expand sidebar')); 70 | document.cookie = 'sidebar=collapsed'; 71 | } 72 | 73 | function expand_sidebar() { 74 | bodywrapper.css('margin-left', bw_margin_expanded); 75 | sidebar.css('width', ssb_width_expanded); 76 | sidebarwrapper.show(); 77 | sidebarbutton.css({ 78 | 'margin-left': ssb_width_expanded-12, 79 | 'height': bodywrapper.height() 80 | }); 81 | sidebarbutton.find('span').text('«'); 82 | sidebarbutton.attr('title', _('Collapse sidebar')); 83 | document.cookie = 'sidebar=expanded'; 84 | } 85 | 86 | function add_sidebar_button() { 87 | sidebarwrapper.css({ 88 | 'float': 'left', 89 | 'margin-right': '0', 90 | 'width': ssb_width_expanded - 28 91 | }); 92 | // create the button 93 | sidebar.append( 94 | '
«
' 95 | ); 96 | var sidebarbutton = $('#sidebarbutton'); 97 | light_color = sidebarbutton.css('background-color'); 98 | // find the height of the viewport to center the '<<' in the page 99 | var viewport_height; 100 | if (window.innerHeight) 101 | viewport_height = window.innerHeight; 102 | else 103 | viewport_height = $(window).height(); 104 | sidebarbutton.find('span').css({ 105 | 'display': 'block', 106 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 107 | }); 108 | 109 | sidebarbutton.click(toggle_sidebar); 110 | sidebarbutton.attr('title', _('Collapse sidebar')); 111 | sidebarbutton.css({ 112 | 'color': '#FFFFFF', 113 | 'border-left': '1px solid ' + dark_color, 114 | 'font-size': '1.2em', 115 | 'cursor': 'pointer', 116 | 'height': bodywrapper.height(), 117 | 'padding-top': '1px', 118 | 'margin-left': ssb_width_expanded - 12 119 | }); 120 | 121 | sidebarbutton.hover( 122 | function () { 123 | $(this).css('background-color', dark_color); 124 | }, 125 | function () { 126 | $(this).css('background-color', light_color); 127 | } 128 | ); 129 | } 130 | 131 | function set_position_from_cookie() { 132 | if (!document.cookie) 133 | return; 134 | var items = document.cookie.split(';'); 135 | for(var k=0; k v documentation". 106 | html_title = 'httpcode' 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | html_static_path = ['_static'] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | #html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_domain_indices = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 153 | #html_show_sphinx = True 154 | 155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 156 | #html_show_copyright = True 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = None 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'httpcodedoc' 168 | 169 | 170 | # -- Options for LaTeX output -------------------------------------------------- 171 | 172 | latex_elements = { 173 | # The paper size ('letterpaper' or 'a4paper'). 174 | #'papersize': 'letterpaper', 175 | 176 | # The font size ('10pt', '11pt' or '12pt'). 177 | #'pointsize': '10pt', 178 | 179 | # Additional stuff for the LaTeX preamble. 180 | #'preamble': '', 181 | } 182 | 183 | # Grouping the document tree into LaTeX files. List of tuples 184 | # (source start file, target name, title, author, documentclass [howto/manual]). 185 | latex_documents = [ 186 | ('index', 'httpcode.tex', u'httpcode Documentation', 187 | u'Ruslan Spivak', 'manual'), 188 | ] 189 | 190 | # The name of an image file (relative to this directory) to place at the top of 191 | # the title page. 192 | #latex_logo = None 193 | 194 | # For "manual" documents, if this is true, then toplevel headings are parts, 195 | # not chapters. 196 | #latex_use_parts = False 197 | 198 | # If true, show page references after internal links. 199 | #latex_show_pagerefs = False 200 | 201 | # If true, show URL addresses after external links. 202 | #latex_show_urls = False 203 | 204 | # Documents to append as an appendix to all manuals. 205 | #latex_appendices = [] 206 | 207 | # If false, no module index is generated. 208 | #latex_domain_indices = True 209 | 210 | 211 | # -- Options for manual page output -------------------------------------------- 212 | 213 | # One entry per manual page. List of tuples 214 | # (source start file, name, description, authors, manual section). 215 | man_pages = [ 216 | ('index', 'httpcode', u'httpcode Documentation', 217 | [u'Ruslan Spivak'], 1) 218 | ] 219 | 220 | # If true, show URL addresses after external links. 221 | #man_show_urls = False 222 | 223 | 224 | # -- Options for Texinfo output ------------------------------------------------ 225 | 226 | # Grouping the document tree into Texinfo files. List of tuples 227 | # (source start file, target name, title, author, 228 | # dir menu entry, description, category) 229 | texinfo_documents = [ 230 | ('index', 'httpcode', u'httpcode Documentation', 231 | u'Ruslan Spivak', 'httpcode', 'One line description of project.', 232 | 'Miscellaneous'), 233 | ] 234 | 235 | # Documents to append as an appendix to all manuals. 236 | #texinfo_appendices = [] 237 | 238 | # If false, no module index is generated. 239 | #texinfo_domain_indices = True 240 | 241 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 242 | #texinfo_show_urls = 'footnote' 243 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. httpcode documentation master file, created by 2 | sphinx-quickstart on Wed Dec 21 15:35:58 2011. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to httpcode 7 | =================== 8 | 9 | :: 10 | 11 | _ _ _____ _____ ____ ____ ___ ____ _____ 12 | | | | |_ _|_ _| _ \ / ___/ _ \| _ \| ____| 13 | | |_| | | | | | | |_) | | | | | | | | | _| 14 | | _ | | | | | | __/| |__| |_| | |_| | |___ 15 | |_| |_| |_| |_| |_| \____\___/|____/|_____| 16 | 17 | 18 | `httpcode` is a little utility that explains the meaning of an HTTP 19 | status code on the command line. 20 | 21 | Installation 22 | ------------ 23 | 24 | .. code-block:: bash 25 | 26 | $ [sudo] pip install httpcode 27 | 28 | There is also an official DEB package available at 29 | `http://packages.debian.org/sid/httpcode `_ 30 | 31 | Usage 32 | ----- 33 | 34 | Explain 405 status code 35 | 36 | .. code-block:: bash 37 | 38 | $ hc 405 39 | Status code 405 40 | Message: Method Not Allowed 41 | Code explanation: Specified method is invalid for this resource. 42 | 43 | Or 418 status code :) 44 | 45 | .. code-block:: bash 46 | 47 | $ hc 418 48 | Status code 418 49 | Message: I'm a teapot 50 | Code explanation: The HTCPCP server is a teapot 51 | 52 | List all codes 53 | 54 | .. code-block:: bash 55 | 56 | $ hc 57 | Status code 100 58 | Message: Continue 59 | Code explanation: Request received, please continue 60 | 61 | Status code 101 62 | Message: Switching Protocols 63 | Code explanation: Switching to new protocol; obey Upgrade header 64 | 65 | Status code 200 66 | Message: OK 67 | Code explanation: Request fulfilled, document follows 68 | 69 | ... 70 | 71 | Search code(s) by description (case-insensitive) 72 | 73 | .. code-block:: bash 74 | 75 | $ hc -s too 76 | Status code 413 77 | Message: Request Entity Too Large 78 | Code explanation: Entity is too large. 79 | 80 | Status code 414 81 | Message: Request-URI Too Long 82 | Code explanation: URI is too long. 83 | 84 | Filter codes with a regex 85 | 86 | .. code-block:: bash 87 | 88 | $ hc 30[12] 89 | Status code 301 90 | Message: Moved Permanently 91 | Code explanation: Object moved permanently -- see URI list 92 | 93 | Status code 302 94 | Message: Found 95 | Code explanation: Object moved temporarily -- see URI list 96 | 97 | Use an 'x' for any digit 98 | 99 | .. code-block:: bash 100 | 101 | $ hc 1xx 102 | Status code 100 103 | Message: Continue 104 | Code explanation: Request received, please continue 105 | 106 | Status code 101 107 | Message: Switching Protocols 108 | Code explanation: Switching to new protocol; obey Upgrade header 109 | 110 | Show help 111 | 112 | .. code-block:: bash 113 | 114 | $ hc -h 115 | Usage: hc [code] [options] 116 | 117 | code may contain regular expression or use 'x' to denote any digit 118 | code examples: 418, 30[12], 3.*, 1xx 119 | 120 | Without parameters lists all available 121 | HTTP status codes and their description 122 | 123 | 124 | Options: 125 | -h, --help show this help message and exit 126 | -s SEARCH, --search=SEARCH 127 | Search for a code by name or description. Search text 128 | may contain regular expressions. 129 | 130 | Roadmap 131 | ------- 132 | 133 | Add more codes 134 | 135 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from setuptools import setup, find_packages 4 | 5 | 6 | classifiers = """\ 7 | Intended Audience :: Developers 8 | License :: OSI Approved :: MIT License 9 | Programming Language :: Python 10 | Programming Language :: Python :: 2.7 11 | Programming Language :: Python :: 3 12 | Topic :: Internet :: WWW/HTTP 13 | Operating System :: Unix 14 | """ 15 | 16 | def read(*rel_names): 17 | return open(os.path.join(os.path.dirname(__file__), *rel_names)).read() 18 | 19 | 20 | setup( 21 | name='httpcode', 22 | version='0.6', 23 | url='http://github.com/rspivak/httpcode', 24 | license='MIT', 25 | description='httpcode - explain HTTP status code', 26 | author='Ruslan Spivak', 27 | author_email='ruslan.spivak@gmail.com', 28 | packages=find_packages('src'), 29 | package_dir={'': 'src'}, 30 | install_requires=['colorama'], 31 | zip_safe=False, 32 | entry_points="""\ 33 | [console_scripts] 34 | hc = httpcode:main 35 | """, 36 | classifiers=filter(None, classifiers.split('\n')), 37 | long_description=read('README.rst') + '\n\n' + read('CHANGES.rst'), 38 | extras_require={'test': []} 39 | ) 40 | -------------------------------------------------------------------------------- /src/httpcode/__init__.py: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # Copyright (c) 2011 - 2017 Ruslan Spivak 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | # 23 | ############################################################################### 24 | 25 | __author__ = 'Ruslan Spivak ' 26 | 27 | import sys 28 | import argparse 29 | import textwrap 30 | import re 31 | 32 | from colorama import Style, init, deinit 33 | 34 | # Codes with messages are taken verbatim from BaseHTTPServer.py 35 | 36 | # Table mapping response codes to messages; entries have the 37 | # form {code: (shortmessage, longmessage)}. 38 | # See RFC 2616. 39 | STATUS_CODES = { 40 | 100: ('Continue', 'Request received, please continue'), 41 | 101: ('Switching Protocols', 42 | 'Switching to new protocol; obey Upgrade header'), 43 | 102: ('Processing', 'WebDAV; RFC 2518'), 44 | 45 | 200: ('OK', 'Request fulfilled, document follows'), 46 | 201: ('Created', 'Document created, URL follows'), 47 | 202: ('Accepted', 48 | 'Request accepted, processing continues off-line'), 49 | 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), 50 | 204: ('No Content', 'Request fulfilled, nothing follows'), 51 | 205: ('Reset Content', 'Clear input form for further input.'), 52 | 206: ('Partial Content', 'Partial content follows.'), 53 | 207: ('Multi-Status', 'WebDAV; RFC 4918'), 54 | 208: ('Already Reported', 'WebDAV; RFC 5842'), 55 | 226: ('IM Used', 'RFC 3229'), 56 | 57 | 300: ('Multiple Choices', 58 | 'Object has several resources -- see URI list'), 59 | 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), 60 | 302: ('Found', 'Object moved temporarily -- see URI list'), 61 | 303: ('See Other', 'Object moved -- see Method and URL list'), 62 | 304: ('Not Modified', 63 | 'Document has not changed since given time'), 64 | 305: ('Use Proxy', 65 | 'You must use proxy specified in Location to access this ' 66 | 'resource.'), 67 | 306: ('Switch Proxy', 'Subsequent requests should use the specified proxy'), 68 | 307: ('Temporary Redirect', 69 | 'Object moved temporarily -- see URI list'), 70 | 308: ('Permanent Redirect', 'Object moved permanently'), 71 | 72 | 400: ('Bad Request', 73 | 'Bad request syntax or unsupported method'), 74 | 401: ('Unauthorized', 75 | 'No permission -- see authorization schemes'), 76 | 402: ('Payment Required', 77 | 'No payment -- see charging schemes'), 78 | 403: ('Forbidden', 79 | 'Request forbidden -- authorization will not help'), 80 | 404: ('Not Found', 'Nothing matches the given URI'), 81 | 405: ('Method Not Allowed', 82 | 'Specified method is invalid for this resource.'), 83 | 406: ('Not Acceptable', 'URI not available in preferred format.'), 84 | 407: ('Proxy Authentication Required', 'You must authenticate with ' 85 | 'this proxy before proceeding.'), 86 | 408: ('Request Timeout', 'Request timed out; try again later.'), 87 | 409: ('Conflict', 'Request conflict.'), 88 | 410: ('Gone', 89 | 'URI no longer exists and has been permanently removed.'), 90 | 411: ('Length Required', 'Client must specify Content-Length.'), 91 | 412: ('Precondition Failed', 'Precondition in headers is false.'), 92 | 413: ('Payload Too Large', 'Payload is too large.'), 93 | 414: ('Request-URI Too Long', 'URI is too long.'), 94 | 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), 95 | 416: ('Requested Range Not Satisfiable', 96 | 'Cannot satisfy request range.'), 97 | 417: ('Expectation Failed', 98 | 'Expect condition could not be satisfied.'), 99 | 418: ("I'm a teapot", 'The HTCPCP server is a teapot'), 100 | 419: ('Authentication Timeout', 'previously valid authentication has expired'), 101 | 420: ('Method Failure / Enhance Your Calm', 'Spring Framework / Twitter'), 102 | 422: ('Unprocessable Entity', 'WebDAV; RFC 4918'), 103 | 423: ('Locked', 'WebDAV; RFC 4918'), 104 | 424: ('Failed Dependency / Method Failure', 'WebDAV; RFC 4918'), 105 | 425: ('Unordered Collection', 'Internet draft'), 106 | 426: ('Upgrade Required', 'client should switch to a different protocol'), 107 | 428: ('Precondition Required', 'RFC 6585'), 108 | 429: ('Too Many Requests', 'RFC 6585'), 109 | 431: ('Request Header Fields Too Large', 'RFC 6585'), 110 | 440: ('Login Timeout', 'Microsoft'), 111 | 444: ('No Response', 'Nginx'), 112 | 449: ('Retry With', 'Microsoft'), 113 | 450: ('Blocked by Windows Parental Controls', 'Microsoft'), 114 | 451: ('Unavailable For Legal Reasons', 'RFC 7725'), 115 | 494: ('Request Header Too Large', 'Nginx'), 116 | 495: ('Cert Error', 'Nginx'), 117 | 496: ('No Cert', 'Nginx'), 118 | 497: ('HTTP to HTTPS', 'Nginx'), 119 | 498: ('Token expired/invalid', 'Esri'), 120 | 499: ('Client Closed Request', 'Nginx'), 121 | 122 | 500: ('Internal Server Error', 'Server got itself in trouble'), 123 | 501: ('Not Implemented', 124 | 'Server does not support this operation'), 125 | 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), 126 | 503: ('Service Unavailable', 127 | 'The server cannot process the request due to a high load'), 128 | 504: ('Gateway Timeout', 129 | 'The gateway server did not receive a timely response'), 130 | 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), 131 | 506: ('Variant Also Negotiates', 'RFC 2295'), 132 | 507: ('Insufficient Storage', 'WebDAV; RFC 4918'), 133 | 508: ('Loop Detected', 'WebDAV; RFC 5842'), 134 | 509: ('Bandwidth Limit Exceeded', 'Apache bw/limited extension'), 135 | 510: ('Not Extended', 'RFC 2774'), 136 | 511: ('Network Authentication Required', 'RFC 6585'), 137 | 598: ('Network read timeout error', 'Unknown'), 138 | 599: ('Network connect timeout error', 'Unknown'), 139 | } 140 | 141 | MSG_FMT = """\ 142 | Status code {code} 143 | Message: {message} 144 | Code explanation: {explain} 145 | 146 | """ 147 | 148 | _IS_THREE_DIGIT_CODE = re.compile(r'\d{3}$').match 149 | 150 | 151 | def _colorize(text): 152 | return Style.BRIGHT + text + Style.RESET_ALL 153 | 154 | def _exit(msg): 155 | sys.stderr.write(msg) 156 | # stop colorama 157 | deinit() 158 | sys.exit(-1) 159 | 160 | def _print_filtered_codes(code): 161 | """Search and print codes based on passed regexp. 162 | 163 | Examples of codes with regexps: 164 | $ hc 30[12] 165 | 166 | $ hc 3.. 167 | 168 | Special case, use 'x' for any digit 169 | $ hc 1xx 170 | """ 171 | _has_code_match = re.compile(code.replace('x', '\d') + '$').match 172 | found_codes = [c for c in STATUS_CODES if _has_code_match(str(c))] 173 | if not found_codes: 174 | _exit('No code found corresponding to: %s\n' % code) 175 | _print_codes(codes=found_codes) 176 | 177 | def _print_search(text, noformat=False): 178 | """Search for a code by description and print it.""" 179 | _is_text_found = re.compile(text, re.IGNORECASE).search 180 | found_codes = [ 181 | code for code, (short, long) in STATUS_CODES.items() 182 | if _is_text_found(short + long) 183 | ] 184 | if found_codes: 185 | _print_codes(codes=found_codes, noformat=noformat) 186 | else: 187 | _exit('No status code found for search: %s\n' % text) 188 | 189 | def _print_codes(codes=STATUS_CODES.keys(), noformat=False): 190 | for code in sorted(codes): 191 | _print_code(code, noformat) 192 | 193 | def _print_code(code, noformat=False): 194 | try: 195 | short, long = STATUS_CODES[code] 196 | except KeyError as e: 197 | _exit('No description found for code: %s\n' % code) 198 | else: 199 | # colorize code 200 | code = str(code) if noformat else _colorize(str(code)) 201 | msg = MSG_FMT.format(code=code, message=short, explain=long) 202 | sys.stdout.write(msg) 203 | 204 | def main(): 205 | usage = """\ 206 | usage: hc [code] [options] 207 | 208 | Without parameters lists all available 209 | HTTP status codes and their description 210 | """ 211 | parser = argparse.ArgumentParser(usage=textwrap.dedent(usage)) 212 | parser.add_argument( 213 | 'code', 214 | nargs='?', 215 | help=("Code may contain regular expression or use 'x' to denote " 216 | "any digit. Code examples: 418, 30[12], 3.*, 1xx") 217 | ) 218 | 219 | parser.add_argument( 220 | '-s', '--search', 221 | dest='search', 222 | help=('Search for a code by name or description. ' 223 | 'Search text may contain regular expressions.') 224 | ) 225 | parser.add_argument( 226 | '-p', '--plain', 227 | action='store_true', 228 | dest='noformat', 229 | default=False, 230 | help=( 'Disable formatting of output') 231 | ) 232 | args = parser.parse_args() 233 | 234 | # initialize colorama 235 | init() 236 | 237 | if args.search is not None: 238 | _print_search(args.search, noformat=args.noformat) 239 | elif args.code: 240 | code = args.code 241 | if _IS_THREE_DIGIT_CODE(code): 242 | _print_code(int(code), args.noformat) 243 | else: 244 | _print_filtered_codes(code) 245 | else: 246 | _print_codes(noformat=args.noformat) 247 | 248 | if __name__ == '__main__': 249 | main() 250 | --------------------------------------------------------------------------------