├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── CHANGES.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── docs ├── Makefile ├── _static │ └── .keep ├── changelog.rst ├── codes.rst ├── conf.py ├── exceptions.rst ├── helpgendoc.sh ├── index.rst ├── make.bat └── status_map.rst ├── requirements-dev.in ├── requirements-dev.txt ├── setup.cfg ├── setup.py ├── staty ├── __init__.py ├── base.py ├── codes.py ├── exceptions.py ├── handlers │ ├── __init__.py │ └── requests.py ├── status_map.py └── utils.py ├── tests ├── __init__.py ├── conftest.py ├── test_request_handler.py ├── test_staty.py └── test_utils.py └── tox.ini /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [osantana] 4 | patreon: computos 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *.so 4 | .Python 5 | env/ 6 | build/ 7 | develop-eggs/ 8 | dist/ 9 | downloads/ 10 | eggs/ 11 | .eggs/ 12 | lib/ 13 | lib64/ 14 | parts/ 15 | sdist/ 16 | *.egg-info/ 17 | .installed.cfg 18 | *.egg 19 | pip-log.txt 20 | pip-delete-this-directory.txt 21 | .tox/ 22 | .coverage 23 | .coverage.* 24 | .cache 25 | coverage.xml 26 | *,cover 27 | .idea 28 | .DS_Store 29 | docs/_build 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "3.6" 5 | - "pypy3" 6 | 7 | sudo: false 8 | 9 | install: 10 | - pip install -r requirements-dev.txt 11 | - pip install tox-travis 12 | - pip install coveralls 13 | 14 | before_script: 15 | - flake8 --max-line-length=120 . 16 | 17 | script: 18 | - tox 19 | 20 | after_success: 21 | - coveralls 22 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | 1.2.4 2 | ----- 3 | 4 | - Use .format() instead of f-string 5 | - Official support only for CPython 3.7 and 3.8 6 | - Update requirements 7 | 8 | 1.2.3 9 | ----- 10 | 11 | - Update requirements to fix security issues 12 | 13 | 1.2.2 14 | ----- 15 | 16 | - Fix an circular import issue 17 | 18 | 1.2.1 19 | ----- 20 | 21 | - Fix an small issue with base HTTPError exception due to lack of status attribute 22 | 23 | 1.2.0 24 | ----- 25 | 26 | - Add status object in exception objects 27 | 28 | 1.1.1 29 | ----- 30 | 31 | - Replace bumpversion support with CHANGES.rst option 32 | - Add support for setup.py version command 33 | - Adopt twine for PyPI release/upload 34 | - Use README.rst as a long description at PyPI 35 | - Update some requirements 36 | 37 | 1.1.0 38 | ----- 39 | 40 | - Add an HTTPStatus base class for type annotation usage 41 | 42 | 1.0.0 43 | ----- 44 | 45 | - Refactor status check/test implementation 46 | - Add numeric code to status map 47 | 48 | 0.5.0 49 | ----- 50 | 51 | - Remove HTTP statuses global maps 52 | - Create global StatusMap registry with constant attributes support 53 | - Add support for HTTP code comparison with integers, strings, HTTP classes, etc 54 | - Improve documentation organization, presentation and formatting 55 | 56 | 0.4.3 57 | ----- 58 | 59 | - Fixed CI and documentation builds 60 | 61 | 0.4.2 62 | ----- 63 | 64 | - Fix setup.py errors 65 | 66 | 0.4.0 67 | ----- 68 | 69 | - Add initial documentation 70 | - Update dev requirements 71 | - Configure bumpversion for library release 72 | 73 | 0.3.1 74 | ----- 75 | 76 | - Fix SwitchProxy class message 77 | 78 | 0.3.0 79 | ----- 80 | 81 | - Add RFC references when available. 82 | 83 | 0.2.2 84 | ----- 85 | 86 | - Small refactoring in HTTPError class implementation 87 | 88 | 0.2.1 89 | ----- 90 | 91 | - Fix CI (travis/tox) errors 92 | 93 | 0.2 94 | --- 95 | 96 | - Initial support for requests response handling and wrapping. 97 | - Exception mixins must be derived from mixins to be used with try:/except: 98 | - Minor improvements and :lipstick: in code formatting and project organization 99 | 100 | 0.1 101 | --- 102 | 103 | - First version 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE.txt 3 | include CHANGES.rst 4 | include requirements*.txt 5 | 6 | global-exclude __pycache__/* 7 | global-exclude .deps/* 8 | global-exclude *.so 9 | global-exclude *.pyd 10 | global-exclude *.pyc 11 | global-exclude .git* 12 | global-exclude .DS_Store 13 | global-exclude .mailmap 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | deps: 2 | pip install -r requirements-dev.txt 3 | 4 | tox: deps 5 | tox 6 | 7 | test: 8 | py.test 9 | 10 | clean: 11 | -find . -iname "*.py[ocd]" -delete 12 | -find . -iname "__pycache__" -exec rm -rf {} \; 13 | -rm -rf dist 14 | 15 | release: 16 | git push origin master 17 | git tag `python setup.py -q version` 18 | git push origin master --tags 19 | rm -rf dist/* 20 | python setup.py sdist 21 | twine check dist/* 22 | twine upload dist/* 23 | 24 | update-reqs: 25 | pip-compile --upgrade requirements-dev.in | uniq > requirements-dev.txt 26 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | staty 2 | ===== 3 | 4 | |Build Status| |Coverage Status| 5 | 6 | 7 | Staty is a Python Library with complete information and error handling for HTTP 8 | response status codes. 9 | 10 | 11 | Documentation 12 | ------------- 13 | 14 | You can find ``staty`` documentation at `Read the Docs`_ website. 15 | 16 | 17 | .. _`Read the Docs`: http://staty.readthedocs.org/ 18 | 19 | .. |Build Status| image:: https://travis-ci.org/osantana/staty.png?branch=master 20 | :target: https://travis-ci.org/osantana/staty 21 | .. |Coverage Status| image:: https://coveralls.io/repos/github/osantana/staty/badge.svg?branch=master 22 | :target: https://coveralls.io/github/osantana/staty?branch=master 23 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = staty 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osantana/staty/d9a6717716fa45ebd8ac073f3c61007fc86b8b5f/docs/_static/.keep -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changes 2 | ======= 3 | 4 | .. include:: ../CHANGES.rst 5 | -------------------------------------------------------------------------------- /docs/codes.rst: -------------------------------------------------------------------------------- 1 | HTTP Status Codes 2 | ----------------- 3 | 4 | Module: ``staty.codes`` 5 | 6 | 7 | Informational 8 | ~~~~~~~~~~~~~ 9 | 10 | .. autoclass:: staty.codes.Continue 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | .. autoclass:: staty.codes.SwitchingProtocols 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | .. autoclass:: staty.codes.Processing 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | .. autoclass:: staty.codes.Checkpoint 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | 31 | Successful 32 | ~~~~~~~~~~ 33 | 34 | .. autoclass:: staty.codes.Ok 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | .. autoclass:: staty.codes.Created 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | .. autoclass:: staty.codes.Accepted 45 | :members: 46 | :undoc-members: 47 | :show-inheritance: 48 | 49 | .. autoclass:: staty.codes.NonAuthoritativeInformation 50 | :members: 51 | :undoc-members: 52 | :show-inheritance: 53 | 54 | .. autoclass:: staty.codes.NoContent 55 | :members: 56 | :undoc-members: 57 | :show-inheritance: 58 | 59 | .. autoclass:: staty.codes.ResetContent 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | 64 | .. autoclass:: staty.codes.PartialContent 65 | :members: 66 | :undoc-members: 67 | :show-inheritance: 68 | 69 | .. autoclass:: staty.codes.MultiStatus 70 | :members: 71 | :undoc-members: 72 | :show-inheritance: 73 | 74 | .. autoclass:: staty.codes.AlreadyReported 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | .. autoclass:: staty.codes.IMUsed 80 | :members: 81 | :undoc-members: 82 | :show-inheritance: 83 | 84 | 85 | .. autoclass:: staty.codes.MultipleChoices 86 | :members: 87 | :undoc-members: 88 | :show-inheritance: 89 | 90 | .. autoclass:: staty.codes.MovedPermanently 91 | :members: 92 | :undoc-members: 93 | :show-inheritance: 94 | 95 | .. autoclass:: staty.codes.Found 96 | :members: 97 | :undoc-members: 98 | :show-inheritance: 99 | 100 | .. autoclass:: staty.codes.SeeOther 101 | :members: 102 | :undoc-members: 103 | :show-inheritance: 104 | 105 | .. autoclass:: staty.codes.NotModified 106 | :members: 107 | :undoc-members: 108 | :show-inheritance: 109 | 110 | .. autoclass:: staty.codes.UseProxy 111 | :members: 112 | :undoc-members: 113 | :show-inheritance: 114 | 115 | .. autoclass:: staty.codes.SwitchProxy 116 | :members: 117 | :undoc-members: 118 | :show-inheritance: 119 | 120 | .. autoclass:: staty.codes.TemporaryRedirect 121 | :members: 122 | :undoc-members: 123 | :show-inheritance: 124 | 125 | .. autoclass:: staty.codes.PermanentRedirect 126 | :members: 127 | :undoc-members: 128 | :show-inheritance: 129 | 130 | 131 | Client Error 132 | ~~~~~~~~~~~~ 133 | 134 | .. autoclass:: staty.codes.BadRequest 135 | :members: 136 | :undoc-members: 137 | :show-inheritance: 138 | 139 | .. autoclass:: staty.codes.Unauthorized 140 | :members: 141 | :undoc-members: 142 | :show-inheritance: 143 | 144 | .. autoclass:: staty.codes.PaymentRequired 145 | :members: 146 | :undoc-members: 147 | :show-inheritance: 148 | 149 | .. autoclass:: staty.codes.Forbidden 150 | :members: 151 | :undoc-members: 152 | :show-inheritance: 153 | 154 | .. autoclass:: staty.codes.NotFound 155 | :members: 156 | :undoc-members: 157 | :show-inheritance: 158 | 159 | .. autoclass:: staty.codes.MethodNotAllowed 160 | :members: 161 | :undoc-members: 162 | :show-inheritance: 163 | 164 | .. autoclass:: staty.codes.NotAcceptable 165 | :members: 166 | :undoc-members: 167 | :show-inheritance: 168 | 169 | .. autoclass:: staty.codes.ProxyAuthenticationRequired 170 | :members: 171 | :undoc-members: 172 | :show-inheritance: 173 | 174 | .. autoclass:: staty.codes.RequestTimeout 175 | :members: 176 | :undoc-members: 177 | :show-inheritance: 178 | 179 | .. autoclass:: staty.codes.Conflict 180 | :members: 181 | :undoc-members: 182 | :show-inheritance: 183 | 184 | .. autoclass:: staty.codes.Gone 185 | :members: 186 | :undoc-members: 187 | :show-inheritance: 188 | 189 | .. autoclass:: staty.codes.LengthRequired 190 | :members: 191 | :undoc-members: 192 | :show-inheritance: 193 | 194 | .. autoclass:: staty.codes.PreconditionFailed 195 | :members: 196 | :undoc-members: 197 | :show-inheritance: 198 | 199 | .. autoclass:: staty.codes.PayloadTooLarge 200 | :members: 201 | :undoc-members: 202 | :show-inheritance: 203 | 204 | .. autoclass:: staty.codes.URITooLong 205 | :members: 206 | :undoc-members: 207 | :show-inheritance: 208 | 209 | .. autoclass:: staty.codes.UnsupportedMediaType 210 | :members: 211 | :undoc-members: 212 | :show-inheritance: 213 | 214 | .. autoclass:: staty.codes.RangeNotSatisfiable 215 | :members: 216 | :undoc-members: 217 | :show-inheritance: 218 | 219 | .. autoclass:: staty.codes.ExpectationFailed 220 | :members: 221 | :undoc-members: 222 | :show-inheritance: 223 | 224 | .. autoclass:: staty.codes.IAmATeapot 225 | :members: 226 | :undoc-members: 227 | :show-inheritance: 228 | 229 | .. autoclass:: staty.codes.MethodFailure 230 | :members: 231 | :undoc-members: 232 | :show-inheritance: 233 | 234 | .. autoclass:: staty.codes.MisdirectedRequest 235 | :members: 236 | :undoc-members: 237 | :show-inheritance: 238 | 239 | .. autoclass:: staty.codes.UnprocessableEntity 240 | :members: 241 | :undoc-members: 242 | :show-inheritance: 243 | 244 | .. autoclass:: staty.codes.Locked 245 | :members: 246 | :undoc-members: 247 | :show-inheritance: 248 | 249 | .. autoclass:: staty.codes.FailedDependency 250 | :members: 251 | :undoc-members: 252 | :show-inheritance: 253 | 254 | .. autoclass:: staty.codes.UpgradeRequired 255 | :members: 256 | :undoc-members: 257 | :show-inheritance: 258 | 259 | .. autoclass:: staty.codes.PreconditionRequired 260 | :members: 261 | :undoc-members: 262 | :show-inheritance: 263 | 264 | .. autoclass:: staty.codes.TooManyRequests 265 | :members: 266 | :undoc-members: 267 | :show-inheritance: 268 | 269 | .. autoclass:: staty.codes.RequestHeaderFieldsTooLarge 270 | :members: 271 | :undoc-members: 272 | :show-inheritance: 273 | 274 | .. autoclass:: staty.codes.LoginTimeout 275 | :members: 276 | :undoc-members: 277 | :show-inheritance: 278 | 279 | .. autoclass:: staty.codes.NoResponse 280 | :members: 281 | :undoc-members: 282 | :show-inheritance: 283 | 284 | .. autoclass:: staty.codes.RetryWith 285 | :members: 286 | :undoc-members: 287 | :show-inheritance: 288 | 289 | .. autoclass:: staty.codes.BlockedByWindowsParentalControls 290 | :members: 291 | :undoc-members: 292 | :show-inheritance: 293 | 294 | .. autoclass:: staty.codes.UnavailableForLegalReasons 295 | :members: 296 | :undoc-members: 297 | :show-inheritance: 298 | 299 | .. autoclass:: staty.codes.Redirect 300 | :members: 301 | :undoc-members: 302 | :show-inheritance: 303 | 304 | .. autoclass:: staty.codes.SSLCertificateError 305 | :members: 306 | :undoc-members: 307 | :show-inheritance: 308 | 309 | .. autoclass:: staty.codes.SSLCertificateRequired 310 | :members: 311 | :undoc-members: 312 | :show-inheritance: 313 | 314 | .. autoclass:: staty.codes.HTTPRequestSentToHTTPSPort 315 | :members: 316 | :undoc-members: 317 | :show-inheritance: 318 | 319 | .. autoclass:: staty.codes.InvalidToken 320 | :members: 321 | :undoc-members: 322 | :show-inheritance: 323 | 324 | .. autoclass:: staty.codes.ClientClosedRequest 325 | :members: 326 | :undoc-members: 327 | :show-inheritance: 328 | 329 | .. autoclass:: staty.codes.RequestHasBeenForbiddenByAntivirus 330 | :members: 331 | :undoc-members: 332 | :show-inheritance: 333 | 334 | .. autoclass:: staty.codes.TokenRequired 335 | :members: 336 | :undoc-members: 337 | :show-inheritance: 338 | 339 | 340 | Server Error 341 | ~~~~~~~~~~~~ 342 | 343 | .. autoclass:: staty.codes.InternalServerError 344 | :members: 345 | :undoc-members: 346 | :show-inheritance: 347 | 348 | .. autoclass:: staty.codes.NotImplemented 349 | :members: 350 | :undoc-members: 351 | :show-inheritance: 352 | 353 | .. autoclass:: staty.codes.BadGateway 354 | :members: 355 | :undoc-members: 356 | :show-inheritance: 357 | 358 | .. autoclass:: staty.codes.ServiceUnavailable 359 | :members: 360 | :undoc-members: 361 | :show-inheritance: 362 | 363 | .. autoclass:: staty.codes.GatewayTimeout 364 | :members: 365 | :undoc-members: 366 | :show-inheritance: 367 | 368 | .. autoclass:: staty.codes.HTTPVersionNotSupported 369 | :members: 370 | :undoc-members: 371 | :show-inheritance: 372 | 373 | .. autoclass:: staty.codes.VariantAlsoNegotiates 374 | :members: 375 | :undoc-members: 376 | :show-inheritance: 377 | 378 | .. autoclass:: staty.codes.InsufficientStorage 379 | :members: 380 | :undoc-members: 381 | :show-inheritance: 382 | 383 | .. autoclass:: staty.codes.LoopDetected 384 | :members: 385 | :undoc-members: 386 | :show-inheritance: 387 | 388 | .. autoclass:: staty.codes.BandwidthLimitExceeded 389 | :members: 390 | :undoc-members: 391 | :show-inheritance: 392 | 393 | .. autoclass:: staty.codes.NotExtended 394 | :members: 395 | :undoc-members: 396 | :show-inheritance: 397 | 398 | .. autoclass:: staty.codes.NetworkAuthenticationRequired 399 | :members: 400 | :undoc-members: 401 | :show-inheritance: 402 | 403 | .. autoclass:: staty.codes.UnknownError 404 | :members: 405 | :undoc-members: 406 | :show-inheritance: 407 | 408 | .. autoclass:: staty.codes.WebServerIsDown 409 | :members: 410 | :undoc-members: 411 | :show-inheritance: 412 | 413 | .. autoclass:: staty.codes.ConnectionTimedOut 414 | :members: 415 | :undoc-members: 416 | :show-inheritance: 417 | 418 | .. autoclass:: staty.codes.OriginIsUnreachable 419 | :members: 420 | :undoc-members: 421 | :show-inheritance: 422 | 423 | .. autoclass:: staty.codes.ATimeoutOccurred 424 | :members: 425 | :undoc-members: 426 | :show-inheritance: 427 | 428 | .. autoclass:: staty.codes.SSLHandshakeFailed 429 | :members: 430 | :undoc-members: 431 | :show-inheritance: 432 | 433 | .. autoclass:: staty.codes.InvalidSSLCertificate 434 | :members: 435 | :undoc-members: 436 | :show-inheritance: 437 | 438 | .. autoclass:: staty.codes.SiteIsFrozen 439 | :members: 440 | :undoc-members: 441 | :show-inheritance: 442 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # staty documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Aug 15 00:09:36 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | 23 | sys.path.insert(0, os.path.abspath('..')) 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.todo', 37 | 'sphinx.ext.viewcode', 38 | 'sphinx.ext.githubpages', 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # 47 | # source_suffix = ['.rst', '.md'] 48 | source_suffix = '.rst' 49 | 50 | # The master toctree document. 51 | master_doc = 'index' 52 | 53 | # General information about the project. 54 | project = 'staty' 55 | copyright = '2017, Osvaldo Santana Neto' 56 | author = 'Osvaldo Santana Neto' 57 | 58 | # The version info for the project you're documenting, acts as replacement for 59 | # |version| and |release|, also used in various other places throughout the 60 | # built documents. 61 | # 62 | # The short X.Y version. 63 | version = '1.1.0' 64 | # The full version, including alpha/beta/rc tags. 65 | release = '1.1.0' 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = None 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = 'sphinx' 81 | 82 | # If true, `todo` and `todoList` produce output, else they produce nothing. 83 | todo_include_todos = True 84 | 85 | # -- Options for HTML output ---------------------------------------------- 86 | 87 | # The theme to use for HTML and HTML Help pages. See the documentation for 88 | # a list of builtin themes. 89 | # 90 | html_theme = 'alabaster' 91 | 92 | # Theme options are theme-specific and customize the look and feel of a theme 93 | # further. For a list of options available for each theme, see the 94 | # documentation. 95 | # 96 | # html_theme_options = {} 97 | 98 | # Add any paths that contain custom static files (such as style sheets) here, 99 | # relative to this directory. They are copied after the builtin static files, 100 | # so a file named "default.css" will overwrite the builtin "default.css". 101 | html_static_path = ['_static'] 102 | 103 | # Custom sidebar templates, must be a dictionary that maps document names 104 | # to template names. 105 | # 106 | # This is required for the alabaster theme 107 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 108 | html_sidebars = { 109 | '**': [ 110 | 'about.html', 111 | 'navigation.html', 112 | 'relations.html', # needs 'show_related': True theme option to display 113 | 'searchbox.html', 114 | 'donate.html', 115 | ] 116 | } 117 | 118 | # -- Options for HTMLHelp output ------------------------------------------ 119 | 120 | # Output file base name for HTML help builder. 121 | htmlhelp_basename = 'statydoc' 122 | 123 | # -- Options for LaTeX output --------------------------------------------- 124 | 125 | latex_elements = { 126 | # The paper size ('letterpaper' or 'a4paper'). 127 | # 128 | # 'papersize': 'letterpaper', 129 | 130 | # The font size ('10pt', '11pt' or '12pt'). 131 | # 132 | # 'pointsize': '10pt', 133 | 134 | # Additional stuff for the LaTeX preamble. 135 | # 136 | # 'preamble': '', 137 | 138 | # Latex figure (float) alignment 139 | # 140 | # 'figure_align': 'htbp', 141 | } 142 | 143 | # Grouping the document tree into LaTeX files. List of tuples 144 | # (source start file, target name, title, 145 | # author, documentclass [howto, manual, or own class]). 146 | latex_documents = [ 147 | (master_doc, 'staty.tex', 'staty Documentation', 148 | 'Osvaldo Santana Neto', 'manual'), 149 | ] 150 | 151 | # -- Options for manual page output --------------------------------------- 152 | 153 | # One entry per manual page. List of tuples 154 | # (source start file, name, description, authors, manual section). 155 | man_pages = [ 156 | (master_doc, 'staty', 'staty Documentation', 157 | [author], 1) 158 | ] 159 | 160 | # -- Options for Texinfo output ------------------------------------------- 161 | 162 | # Grouping the document tree into Texinfo files. List of tuples 163 | # (source start file, target name, title, author, 164 | # dir menu entry, description, category) 165 | texinfo_documents = [ 166 | (master_doc, 'staty', 'staty Documentation', 167 | author, 'staty', 'One line description of project.', 168 | 'Miscellaneous'), 169 | ] 170 | -------------------------------------------------------------------------------- /docs/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ---------- 3 | 4 | Module: ``staty.exceptions`` 5 | 6 | 7 | Staty Base Exception 8 | ~~~~~~~~~~~~~~~~~~~~ 9 | 10 | .. autoclass:: staty.exceptions.StatyBaseException 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | .. autoclass:: staty.exceptions.RegistrationException 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | .. autoclass:: staty.exceptions.MissingHandlerException 21 | :members: 22 | :undoc-members: 23 | :show-inheritance: 24 | 25 | .. autoclass:: staty.exceptions.HTTPError 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | .. autoclass:: staty.exceptions.ConnectionException 31 | :members: 32 | :undoc-members: 33 | :show-inheritance: 34 | 35 | 36 | Mixins 37 | ~~~~~~ 38 | 39 | .. autoclass:: staty.exceptions.RecoverableErrorMixin 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | 45 | .. autoclass:: staty.exceptions.UnrecoverableErrorMixin 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | 50 | 51 | Connection Exceptions 52 | ~~~~~~~~~~~~~~~~~~~~~ 53 | 54 | .. autoclass:: staty.exceptions.ConnectionException 55 | :members: 56 | :undoc-members: 57 | :show-inheritance: 58 | 59 | .. autoclass:: staty.exceptions.ConnectionTimeoutException 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | 64 | .. autoclass:: staty.exceptions.NameResolutionException 65 | :members: 66 | :undoc-members: 67 | :show-inheritance: 68 | 69 | .. autoclass:: staty.exceptions.HostAddressException 70 | :members: 71 | :undoc-members: 72 | :show-inheritance: 73 | 74 | .. autoclass:: staty.exceptions.SSLException 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | .. autoclass:: staty.exceptions.ProxyException 80 | :members: 81 | :undoc-members: 82 | :show-inheritance: 83 | 84 | 85 | Client Error Exceptions 86 | ~~~~~~~~~~~~~~~~~~~~~~~ 87 | 88 | .. autoclass:: staty.exceptions.ClientErrorException 89 | :members: 90 | :undoc-members: 91 | :show-inheritance: 92 | 93 | .. autoclass:: staty.exceptions.RecoverableClientErrorException 94 | :members: 95 | :undoc-members: 96 | :show-inheritance: 97 | 98 | .. autoclass:: staty.exceptions.UnrecoverableClientErrorException 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | .. autoclass:: staty.exceptions.PreconditionRequiredException 104 | :members: 105 | :undoc-members: 106 | :show-inheritance: 107 | 108 | .. autoclass:: staty.exceptions.TooManyRequestsException 109 | :members: 110 | :undoc-members: 111 | :show-inheritance: 112 | 113 | .. autoclass:: staty.exceptions.BadRequestException 114 | :members: 115 | :undoc-members: 116 | :show-inheritance: 117 | 118 | .. autoclass:: staty.exceptions.UnauthorizedException 119 | :members: 120 | :undoc-members: 121 | :show-inheritance: 122 | 123 | .. autoclass:: staty.exceptions.PaymentRequiredException 124 | :members: 125 | :undoc-members: 126 | :show-inheritance: 127 | 128 | .. autoclass:: staty.exceptions.ForbiddenException 129 | :members: 130 | :undoc-members: 131 | :show-inheritance: 132 | 133 | .. autoclass:: staty.exceptions.NotFoundException 134 | :members: 135 | :undoc-members: 136 | :show-inheritance: 137 | 138 | .. autoclass:: staty.exceptions.MethodNotAllowedException 139 | :members: 140 | :undoc-members: 141 | :show-inheritance: 142 | 143 | .. autoclass:: staty.exceptions.NotAcceptableException 144 | :members: 145 | :undoc-members: 146 | :show-inheritance: 147 | 148 | .. autoclass:: staty.exceptions.ProxyAuthenticationRequiredException 149 | :members: 150 | :undoc-members: 151 | :show-inheritance: 152 | 153 | .. autoclass:: staty.exceptions.RequestTimeoutException 154 | :members: 155 | :undoc-members: 156 | :show-inheritance: 157 | 158 | .. autoclass:: staty.exceptions.ConflictException 159 | :members: 160 | :undoc-members: 161 | :show-inheritance: 162 | 163 | .. autoclass:: staty.exceptions.GoneException 164 | :members: 165 | :undoc-members: 166 | :show-inheritance: 167 | 168 | .. autoclass:: staty.exceptions.LengthRequiredException 169 | :members: 170 | :undoc-members: 171 | :show-inheritance: 172 | 173 | .. autoclass:: staty.exceptions.PreconditionFailedException 174 | :members: 175 | :undoc-members: 176 | :show-inheritance: 177 | 178 | .. autoclass:: staty.exceptions.PayloadTooLargeException 179 | :members: 180 | :undoc-members: 181 | :show-inheritance: 182 | 183 | .. autoclass:: staty.exceptions.URITooLongException 184 | :members: 185 | :undoc-members: 186 | :show-inheritance: 187 | 188 | .. autoclass:: staty.exceptions.UnsupportedMediaTypeException 189 | :members: 190 | :undoc-members: 191 | :show-inheritance: 192 | 193 | .. autoclass:: staty.exceptions.RangeNotSatisfiableException 194 | :members: 195 | :undoc-members: 196 | :show-inheritance: 197 | 198 | .. autoclass:: staty.exceptions.ExpectationFailedException 199 | :members: 200 | :undoc-members: 201 | :show-inheritance: 202 | 203 | .. autoclass:: staty.exceptions.IAmATeapotException 204 | :members: 205 | :undoc-members: 206 | :show-inheritance: 207 | 208 | .. autoclass:: staty.exceptions.MisdirectedRequestException 209 | :members: 210 | :undoc-members: 211 | :show-inheritance: 212 | 213 | .. autoclass:: staty.exceptions.UnprocessableEntityException 214 | :members: 215 | :undoc-members: 216 | :show-inheritance: 217 | 218 | .. autoclass:: staty.exceptions.LockedException 219 | :members: 220 | :undoc-members: 221 | :show-inheritance: 222 | 223 | .. autoclass:: staty.exceptions.FailedDependencyException 224 | :members: 225 | :undoc-members: 226 | :show-inheritance: 227 | 228 | .. autoclass:: staty.exceptions.UpgradeRequiredException 229 | :members: 230 | :undoc-members: 231 | :show-inheritance: 232 | 233 | .. autoclass:: staty.exceptions.UnavailableForLegalReasonsException 234 | :members: 235 | :undoc-members: 236 | :show-inheritance: 237 | 238 | .. autoclass:: staty.exceptions.RequestHeaderFieldsTooLargeException 239 | :members: 240 | :undoc-members: 241 | :show-inheritance: 242 | 243 | .. autoclass:: staty.exceptions.MethodFailureException 244 | :members: 245 | :undoc-members: 246 | :show-inheritance: 247 | 248 | .. autoclass:: staty.exceptions.BlockedByWindowsParentalControlsException 249 | :members: 250 | :undoc-members: 251 | :show-inheritance: 252 | 253 | .. autoclass:: staty.exceptions.InvalidTokenException 254 | :members: 255 | :undoc-members: 256 | :show-inheritance: 257 | 258 | .. autoclass:: staty.exceptions.TokenRequiredException 259 | :members: 260 | :undoc-members: 261 | :show-inheritance: 262 | 263 | .. autoclass:: staty.exceptions.RequestHasBeenForbiddenByAntivirusException 264 | :members: 265 | :undoc-members: 266 | :show-inheritance: 267 | 268 | .. autoclass:: staty.exceptions.LoginTimeoutException 269 | :members: 270 | :undoc-members: 271 | :show-inheritance: 272 | 273 | .. autoclass:: staty.exceptions.RetryWithException 274 | :members: 275 | :undoc-members: 276 | :show-inheritance: 277 | 278 | .. autoclass:: staty.exceptions.SSLCertificateErrorException 279 | :members: 280 | :undoc-members: 281 | :show-inheritance: 282 | 283 | .. autoclass:: staty.exceptions.SSLCertificateRequiredException 284 | :members: 285 | :undoc-members: 286 | :show-inheritance: 287 | 288 | .. autoclass:: staty.exceptions.HTTPRequestSentToHTTPSPortException 289 | :members: 290 | :undoc-members: 291 | :show-inheritance: 292 | 293 | .. autoclass:: staty.exceptions.NoResponseException 294 | :members: 295 | :undoc-members: 296 | :show-inheritance: 297 | 298 | .. autoclass:: staty.exceptions.ClientClosedRequestException 299 | :members: 300 | :undoc-members: 301 | :show-inheritance: 302 | 303 | 304 | Server Error Exceptions 305 | ~~~~~~~~~~~~~~~~~~~~~~~ 306 | 307 | .. autoclass:: staty.exceptions.ServerErrorException 308 | :members: 309 | :undoc-members: 310 | :show-inheritance: 311 | 312 | .. autoclass:: staty.exceptions.InternalServerErrorException 313 | :members: 314 | :undoc-members: 315 | :show-inheritance: 316 | 317 | .. autoclass:: staty.exceptions.NotImplementedException 318 | :members: 319 | :undoc-members: 320 | :show-inheritance: 321 | 322 | .. autoclass:: staty.exceptions.BadGatewayException 323 | :members: 324 | :undoc-members: 325 | :show-inheritance: 326 | 327 | .. autoclass:: staty.exceptions.ServiceUnavailableException 328 | :members: 329 | :undoc-members: 330 | :show-inheritance: 331 | 332 | .. autoclass:: staty.exceptions.GatewayTimeoutException 333 | :members: 334 | :undoc-members: 335 | :show-inheritance: 336 | 337 | .. autoclass:: staty.exceptions.HTTPVersionNotSupportedException 338 | :members: 339 | :undoc-members: 340 | :show-inheritance: 341 | 342 | .. autoclass:: staty.exceptions.VariantAlsoNegotiatesException 343 | :members: 344 | :undoc-members: 345 | :show-inheritance: 346 | 347 | .. autoclass:: staty.exceptions.InsufficientStorageException 348 | :members: 349 | :undoc-members: 350 | :show-inheritance: 351 | 352 | .. autoclass:: staty.exceptions.LoopDetectedException 353 | :members: 354 | :undoc-members: 355 | :show-inheritance: 356 | 357 | .. autoclass:: staty.exceptions.NotExtendedException 358 | :members: 359 | :undoc-members: 360 | :show-inheritance: 361 | 362 | .. autoclass:: staty.exceptions.NetworkAuthenticationRequiredException 363 | :members: 364 | :undoc-members: 365 | :show-inheritance: 366 | 367 | .. autoclass:: staty.exceptions.BandwidthLimitExceededException 368 | :members: 369 | :undoc-members: 370 | :show-inheritance: 371 | 372 | .. autoclass:: staty.exceptions.SiteIsFrozenException 373 | :members: 374 | :undoc-members: 375 | :show-inheritance: 376 | 377 | .. autoclass:: staty.exceptions.InvalidSSLCertificateException 378 | :members: 379 | :undoc-members: 380 | :show-inheritance: 381 | 382 | .. autoclass:: staty.exceptions.SSLHandshakeFailedException 383 | :members: 384 | :undoc-members: 385 | :show-inheritance: 386 | 387 | .. autoclass:: staty.exceptions.ATimeoutOccurredException 388 | :members: 389 | :undoc-members: 390 | :show-inheritance: 391 | 392 | .. autoclass:: staty.exceptions.OriginIsUnreachableException 393 | :members: 394 | :undoc-members: 395 | :show-inheritance: 396 | 397 | .. autoclass:: staty.exceptions.ConnectionTimedOutException 398 | :members: 399 | :undoc-members: 400 | :show-inheritance: 401 | 402 | .. autoclass:: staty.exceptions.WebServerIsDownException 403 | :members: 404 | :undoc-members: 405 | :show-inheritance: 406 | 407 | .. autoclass:: staty.exceptions.UnknownErrorException 408 | :members: 409 | :undoc-members: 410 | :show-inheritance: 411 | -------------------------------------------------------------------------------- /docs/helpgendoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | module="$1" 4 | category="$2" 5 | title="$(echo $category | sed 's/\([A-Z]\)/ \1/g' | sed 's/^\s\+//')" 6 | 7 | if [ -z "${module}" ]; then 8 | echo "ERROR: missing module name." >&2 9 | exit 1 10 | fi 11 | 12 | if [ -z "${module}" ]; then 13 | echo "ERROR: missing category name." >&2 14 | exit 1 15 | fi 16 | 17 | echo 18 | echo "${title}" 19 | echo "$(echo $title | sed 's/./~/g')" 20 | echo 21 | cat "../staty/${module}.py" |\ 22 | sed -n "/${category}/s/^class \(.*\)(.*):$/.. autoclass:: staty.${module}.\1\n :members:\n :undoc-members:\n :show-inheritance:\n/p" 23 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | staty - Python HTTP Status Library 2 | ================================== 3 | 4 | 5 | Staty is a Python Library with complete information and error handling for HTTP 6 | response status codes. 7 | 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | status_map 13 | codes 14 | exceptions 15 | 16 | changelog 17 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=staty 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/status_map.rst: -------------------------------------------------------------------------------- 1 | Status Map 2 | ---------- 3 | 4 | You can register a custom HTTP status code in staty's global 5 | status map using the ``@status.register()`` decorator: 6 | 7 | .. code-block:: python3 8 | 9 | >>> from staty import status, base 10 | >>> @status.register 11 | ... class MyCustomClientError(base.ClientError): 12 | ... code = 494 13 | ... message = "My Client Error" 14 | 15 | 16 | You can reference to registered status as an attribute of ``status`` 17 | using ``status.HTTP_{CODE}_{CLASS_NAME_IN_CONSTANT_NAME_CASE}``: 18 | 19 | .. code-block:: python3 20 | 21 | >>> from staty import status 22 | >>> status.HTTP_494_MY_CUSTOM_CLIENT_ERROR == 494 23 | True 24 | 25 | You can reference an status code using it code as index: 26 | 27 | .. code-block:: python3 28 | 29 | >>> status[494] == status.HTTP_494_MY_CUSTOM_CLIENT_ERROR 30 | True 31 | 32 | 33 | Class Reference 34 | ~~~~~~~~~~~~~~~ 35 | 36 | .. autoclass:: staty.status_map.HTTPStatusMap 37 | :members: 38 | :undoc-members: 39 | :show-inheritance: 40 | -------------------------------------------------------------------------------- /requirements-dev.in: -------------------------------------------------------------------------------- 1 | pip-tools 2 | requests 3 | pytest 4 | pytest-cov 5 | coverage 6 | tox 7 | flake8 8 | sphinx 9 | sphinx-autobuild 10 | sphinx-rtd-theme 11 | twine 12 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This file is autogenerated by pip-compile 3 | # To update, run: 4 | # 5 | # pip-compile --output-file=requirements-dev.txt requirements-dev.in 6 | # 7 | alabaster==0.7.12 8 | # via sphinx 9 | appdirs==1.4.3 10 | # via virtualenv 11 | argh==0.26.2 12 | # via sphinx-autobuild 13 | attrs==19.3.0 14 | # via pytest 15 | babel==2.8.0 16 | # via sphinx 17 | bleach==3.3.0 18 | # via readme-renderer 19 | certifi==2019.11.28 20 | # via requests 21 | cffi==1.14.0 22 | # via cryptography 23 | chardet==3.0.4 24 | # via requests 25 | click==7.1.1 26 | # via pip-tools 27 | coverage==5.0.4 28 | # via 29 | # -r requirements-dev.in 30 | # pytest-cov 31 | cryptography==3.3.2 32 | # via secretstorage 33 | distlib==0.3.0 34 | # via virtualenv 35 | docutils==0.16 36 | # via 37 | # readme-renderer 38 | # sphinx 39 | entrypoints==0.3 40 | # via flake8 41 | filelock==3.0.12 42 | # via 43 | # tox 44 | # virtualenv 45 | flake8==3.7.9 46 | # via -r requirements-dev.in 47 | idna==2.9 48 | # via requests 49 | imagesize==1.2.0 50 | # via sphinx 51 | jeepney==0.4.3 52 | # via 53 | # keyring 54 | # secretstorage 55 | jinja2==2.11.3 56 | # via sphinx 57 | keyring==21.2.0 58 | # via twine 59 | livereload==2.6.1 60 | # via sphinx-autobuild 61 | markupsafe==1.1.1 62 | # via jinja2 63 | mccabe==0.6.1 64 | # via flake8 65 | more-itertools==8.2.0 66 | # via pytest 67 | packaging==20.3 68 | # via 69 | # bleach 70 | # pytest 71 | # sphinx 72 | # tox 73 | pathtools==0.1.2 74 | # via 75 | # sphinx-autobuild 76 | # watchdog 77 | pip-tools==4.5.1 78 | # via -r requirements-dev.in 79 | pkginfo==1.5.0.1 80 | # via twine 81 | pluggy==0.13.1 82 | # via 83 | # pytest 84 | # tox 85 | port_for==0.3.1 86 | # via sphinx-autobuild 87 | py==1.10.0 88 | # via 89 | # pytest 90 | # tox 91 | pycodestyle==2.5.0 92 | # via flake8 93 | pycparser==2.20 94 | # via cffi 95 | pyflakes==2.1.1 96 | # via flake8 97 | pygments==2.7.4 98 | # via 99 | # readme-renderer 100 | # sphinx 101 | pyparsing==2.4.6 102 | # via packaging 103 | pytest-cov==2.8.1 104 | # via -r requirements-dev.in 105 | pytest==5.4.1 106 | # via 107 | # -r requirements-dev.in 108 | # pytest-cov 109 | pytz==2019.3 110 | # via babel 111 | pyyaml==5.4 112 | # via sphinx-autobuild 113 | readme-renderer==25.0 114 | # via twine 115 | requests-toolbelt==0.9.1 116 | # via twine 117 | requests==2.23.0 118 | # via 119 | # -r requirements-dev.in 120 | # requests-toolbelt 121 | # sphinx 122 | # twine 123 | secretstorage==3.1.2 124 | # via keyring 125 | six==1.14.0 126 | # via 127 | # bleach 128 | # cryptography 129 | # livereload 130 | # packaging 131 | # pip-tools 132 | # readme-renderer 133 | # tox 134 | # virtualenv 135 | snowballstemmer==2.0.0 136 | # via sphinx 137 | sphinx-autobuild==0.7.1 138 | # via -r requirements-dev.in 139 | sphinx-rtd-theme==0.4.3 140 | # via -r requirements-dev.in 141 | sphinx==2.4.4 142 | # via 143 | # -r requirements-dev.in 144 | # sphinx-rtd-theme 145 | sphinxcontrib-applehelp==1.0.2 146 | # via sphinx 147 | sphinxcontrib-devhelp==1.0.2 148 | # via sphinx 149 | sphinxcontrib-htmlhelp==1.0.3 150 | # via sphinx 151 | sphinxcontrib-jsmath==1.0.1 152 | # via sphinx 153 | sphinxcontrib-qthelp==1.0.3 154 | # via sphinx 155 | sphinxcontrib-serializinghtml==1.1.4 156 | # via sphinx 157 | toml==0.10.0 158 | # via tox 159 | tornado==6.0.4 160 | # via 161 | # livereload 162 | # sphinx-autobuild 163 | tox==3.14.5 164 | # via -r requirements-dev.in 165 | tqdm==4.43.0 166 | # via twine 167 | twine==3.1.1 168 | # via -r requirements-dev.in 169 | urllib3==1.25.8 170 | # via requests 171 | virtualenv==20.0.13 172 | # via tox 173 | watchdog==0.10.2 174 | # via sphinx-autobuild 175 | wcwidth==0.1.9 176 | # via pytest 177 | webencodings==0.5.1 178 | # via bleach 179 | 180 | # The following packages are considered to be unsafe in a requirements file: 181 | # setuptools 182 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.rst 3 | 4 | [wheel] 5 | universal = 1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import re 17 | 18 | from setuptools import Command, setup 19 | 20 | 21 | def get_readme(): 22 | with open("README.rst") as readme: 23 | return readme.read() 24 | 25 | 26 | # noinspection PyShadowingNames 27 | def get_version(): 28 | version = "0.0.0" 29 | 30 | with open("CHANGES.rst") as changes: 31 | for line in changes: 32 | version = line.strip() 33 | if re.search(r'^[0-9]+\.[0-9]+(\.[0-9]+)?$', version): 34 | break 35 | 36 | return version 37 | 38 | 39 | class VersionCommand(Command): 40 | user_options = [] 41 | 42 | def initialize_options(self): 43 | pass 44 | 45 | def finalize_options(self): 46 | pass 47 | 48 | def run(self): 49 | print(version) 50 | 51 | 52 | version = get_version() 53 | 54 | setup( 55 | name='staty', 56 | version=version, 57 | description='HTTP response and status code handling', 58 | long_description=get_readme(), 59 | long_description_content_type="text/x-rst", 60 | author="Osvaldo Santana Neto", author_email="staty@osantana.me", 61 | license="MIT", 62 | packages=['staty'], 63 | platforms='any', 64 | classifiers=[ 65 | 'Development Status :: 5 - Production/Stable', 66 | 'Intended Audience :: Developers', 67 | 'License :: OSI Approved :: MIT License', 68 | 'Natural Language :: English', 69 | 'Operating System :: OS Independent', 70 | 'Programming Language :: Python', 71 | 'Programming Language :: Python :: 3', 72 | 'Programming Language :: Python :: 3.7', 73 | 'Programming Language :: Python :: 3.8', 74 | 'Topic :: Software Development :: Libraries', 75 | ], 76 | url='http://github.com/osantana/staty', 77 | download_url='https://github.com/osantana/staty/tarball/{}'.format(version), 78 | test_suite="tests", 79 | extras_require={ 80 | 'requests': ["requests"], 81 | }, 82 | cmdclass={'version': VersionCommand}, 83 | ) 84 | -------------------------------------------------------------------------------- /staty/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .base import * # noqa: F403,F401 16 | from .codes import * # noqa: F403,F401 17 | from .exceptions import * # noqa: F403,F401 18 | from .status_map import status # noqa: F403,F401 19 | -------------------------------------------------------------------------------- /staty/base.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class HTTP10Mixin(object): 17 | pass 18 | 19 | 20 | class HTTP11Mixin(object): 21 | pass 22 | 23 | 24 | class HTTP20Mixin(object): 25 | pass 26 | 27 | 28 | class WebDAVMixin(HTTP11Mixin): 29 | pass 30 | 31 | 32 | class HTCPCP10Mixin(object): 33 | pass 34 | 35 | 36 | class UnofficialMixin(object): 37 | reference = None 38 | 39 | 40 | class IISMixin(UnofficialMixin): 41 | reference = "Internet Information Services (IIS)" 42 | 43 | 44 | class NginxMixin(UnofficialMixin): 45 | reference = "Nginx" 46 | 47 | 48 | class CloudflareMixin(UnofficialMixin): 49 | reference = "Cloudflare" 50 | 51 | 52 | class ErrorCodeMixin(object): 53 | exception = None 54 | 55 | 56 | class StatusType(type): 57 | pass 58 | 59 | 60 | class AbstractStatus(metaclass=StatusType): 61 | code = None 62 | message = "" 63 | category_code = "" 64 | category_name = "" 65 | category_range = None 66 | rfcs = () 67 | 68 | def __eq__(self, other): 69 | if not other: 70 | return False 71 | 72 | if isinstance(other, int): 73 | return self.code == other 74 | 75 | if isinstance(other, str): 76 | return self.message.lower() == other.lower() 77 | 78 | if isinstance(other, AbstractStatus): 79 | return self.code == other.code 80 | 81 | if isinstance(other, StatusType): 82 | return isinstance(self, other) 83 | 84 | return super().__eq__(other) 85 | 86 | def __str__(self): 87 | return f"{self.code} {self.message}" 88 | 89 | 90 | class HTTPStatus(AbstractStatus): 91 | pass 92 | 93 | 94 | class Informational(HTTPStatus): 95 | category_code = "1xx" 96 | category_name = "Informational" 97 | category_range = 100, 200 98 | 99 | 100 | class Successful(HTTPStatus): 101 | category_code = "2xx" 102 | category_name = "Successful" 103 | category_range = 200, 300 104 | 105 | 106 | class Redirection(HTTPStatus): 107 | category_code = "3xx" 108 | category_name = "Redirection" 109 | category_range = 300, 400 110 | 111 | 112 | class ClientError(HTTPStatus, ErrorCodeMixin): 113 | category_code = "4xx" 114 | category_name = "Client Error" 115 | category_range = 400, 500 116 | 117 | 118 | class ServerError(HTTPStatus, ErrorCodeMixin): 119 | category_code = "5xx" 120 | category_name = "Server Error" 121 | category_range = 500, 600 122 | -------------------------------------------------------------------------------- /staty/codes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | from . import base 17 | from . import exceptions 18 | from .status_map import status 19 | 20 | 21 | @status.register 22 | class Continue(base.Informational, base.HTTP11Mixin): 23 | code = 100 24 | message = "Continue" 25 | rfcs = ( 26 | ("7231", "6.2.1"), 27 | ) 28 | 29 | 30 | @status.register 31 | class SwitchingProtocols(base.Informational, base.HTTP11Mixin): 32 | code = 101 33 | message = "Switching Protocols" 34 | rfcs = ( 35 | ("7231", "6.2.2"), 36 | ) 37 | 38 | 39 | @status.register 40 | class Processing(base.Informational, base.WebDAVMixin): 41 | code = 102 42 | message = "Processing" 43 | rfcs = ( 44 | ("2518", ""), 45 | ) 46 | 47 | 48 | @status.register 49 | class Checkpoint(base.Informational, base.UnofficialMixin): 50 | code = 103 51 | message = "Checkpoint" 52 | reference = "https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes" 53 | 54 | 55 | @status.register 56 | class Ok(base.Successful, base.HTTP10Mixin): 57 | code = 200 58 | message = "OK" 59 | rfcs = ( 60 | ("7231", "6.3.1"), 61 | ) 62 | 63 | 64 | @status.register 65 | class Created(base.Successful, base.HTTP10Mixin): 66 | code = 201 67 | message = "Created" 68 | rfcs = ( 69 | ("7231", "6.3.2"), 70 | ) 71 | 72 | 73 | @status.register 74 | class Accepted(base.Successful, base.HTTP10Mixin): 75 | code = 202 76 | message = "Accepted" 77 | rfcs = ( 78 | ("7231", "6.3.3"), 79 | ) 80 | 81 | 82 | @status.register 83 | class NonAuthoritativeInformation(base.Successful, base.HTTP11Mixin): 84 | code = 203 85 | message = "Non-Authoritative Information" 86 | rfcs = ( 87 | ("7231", "6.3.4"), 88 | ) 89 | 90 | 91 | @status.register 92 | class NoContent(base.Successful, base.HTTP10Mixin): 93 | code = 204 94 | message = "No Content" 95 | rfcs = ( 96 | ("7231", "6.3.5"), 97 | ) 98 | 99 | 100 | @status.register 101 | class ResetContent(base.Successful, base.HTTP11Mixin): 102 | code = 205 103 | message = "Reset Content" 104 | rfcs = ( 105 | ("7231", "6.3.6"), 106 | ) 107 | 108 | 109 | @status.register 110 | class PartialContent(base.Successful, base.HTTP11Mixin): 111 | code = 206 112 | message = "Partial Content" 113 | rfcs = ( 114 | ("7233", "4.1"), 115 | ) 116 | 117 | 118 | @status.register 119 | class MultiStatus(base.Successful, base.WebDAVMixin): 120 | code = 207 121 | message = "Multi-Status" 122 | rfcs = ( 123 | ("4918", ""), 124 | ) 125 | 126 | 127 | @status.register 128 | class AlreadyReported(base.Successful, base.WebDAVMixin): 129 | code = 208 130 | message = "Already Reported" 131 | rfcs = ( 132 | ("5842", ""), 133 | ) 134 | 135 | 136 | @status.register 137 | class IMUsed(base.Successful, base.HTTP11Mixin): 138 | code = 226 139 | message = "IM Used" 140 | rfcs = ( 141 | ("3229", ""), 142 | ) 143 | 144 | 145 | @status.register 146 | class MultipleChoices(base.Redirection, base.HTTP10Mixin): 147 | code = 300 148 | message = "Multiple Choices" 149 | rfcs = ( 150 | ("7231", "6.4.1"), 151 | ) 152 | 153 | 154 | @status.register 155 | class MovedPermanently(base.Redirection, base.HTTP10Mixin): 156 | code = 301 157 | message = "Moved Permanently" 158 | rfcs = ( 159 | ("7231", "6.4.2"), 160 | ) 161 | 162 | 163 | @status.register 164 | class Found(base.Redirection, base.HTTP11Mixin): 165 | code = 302 166 | message = "Found" 167 | rfcs = ( 168 | ("7231", "6.4.3"), 169 | ) 170 | 171 | 172 | @status.register 173 | class SeeOther(base.Redirection, base.HTTP11Mixin): 174 | code = 303 175 | message = "See Other" 176 | rfcs = ( 177 | ("7231", "6.4.4"), 178 | ) 179 | 180 | 181 | @status.register 182 | class NotModified(base.Redirection, base.HTTP11Mixin): 183 | code = 304 184 | message = "Not Modified" 185 | rfcs = ( 186 | ("7232", "4.1"), 187 | ) 188 | 189 | 190 | @status.register 191 | class UseProxy(base.Redirection, base.HTTP11Mixin): 192 | code = 305 193 | message = "Use Proxy" 194 | rfcs = ( 195 | ("7231", "6.4.5"), 196 | ) 197 | 198 | 199 | @status.register 200 | class SwitchProxy(base.Redirection, base.HTTP11Mixin): 201 | code = 306 202 | message = "Switch Proxy" # Unused 203 | rfcs = ( 204 | ("7231", "6.4.6"), 205 | ("draft", "1.2"), 206 | ) 207 | 208 | 209 | @status.register 210 | class TemporaryRedirect(base.Redirection, base.HTTP11Mixin): 211 | code = 307 212 | message = "Temporary Redirect" 213 | rfcs = ( 214 | ("7231", "6.4.7"), 215 | ) 216 | 217 | 218 | @status.register 219 | class PermanentRedirect(base.Redirection, base.HTTP11Mixin): 220 | code = 308 221 | message = "Permanent Redirect" 222 | rfcs = ( 223 | ("7538", ""), 224 | ) 225 | 226 | 227 | @status.register 228 | class BadRequest(base.ClientError, base.HTTP10Mixin): 229 | code = 400 230 | message = "Bad Request" 231 | exception = exceptions.BadRequestException 232 | rfcs = ( 233 | ("7231", "6.5.1"), 234 | ) 235 | 236 | 237 | @status.register 238 | class Unauthorized(base.ClientError, base.HTTP10Mixin): 239 | code = 401 240 | message = "Unauthorized" 241 | exception = exceptions.UnauthorizedException 242 | rfcs = ( 243 | ("7235", "3.1"), 244 | ) 245 | 246 | 247 | @status.register 248 | class PaymentRequired(base.ClientError, base.HTTP10Mixin): 249 | code = 402 250 | message = "Payment Required" 251 | exception = exceptions.PaymentRequiredException 252 | rfcs = ( 253 | ("7231", "6.5.2"), 254 | ) 255 | 256 | 257 | @status.register 258 | class Forbidden(base.ClientError, base.HTTP10Mixin): 259 | code = 403 260 | message = "Forbidden" 261 | exception = exceptions.ForbiddenException 262 | rfcs = ( 263 | ("7231", "6.5.3"), 264 | ) 265 | 266 | 267 | @status.register 268 | class NotFound(base.ClientError, base.HTTP10Mixin): 269 | code = 404 270 | message = "Not Found" 271 | exception = exceptions.NotFoundException 272 | rfcs = ( 273 | ("7231", "6.5.4"), 274 | ) 275 | 276 | 277 | @status.register 278 | class MethodNotAllowed(base.ClientError, base.HTTP11Mixin): 279 | code = 405 280 | message = "Method Not Allowed" 281 | exception = exceptions.MethodNotAllowedException 282 | rfcs = ( 283 | ("7231", "6.5.5"), 284 | ) 285 | 286 | 287 | @status.register 288 | class NotAcceptable(base.ClientError, base.HTTP11Mixin): 289 | code = 406 290 | message = "Not Acceptable" 291 | exception = exceptions.NotAcceptableException 292 | rfcs = ( 293 | ("7231", "6.5.6"), 294 | ) 295 | 296 | 297 | @status.register 298 | class ProxyAuthenticationRequired(base.ClientError, base.HTTP11Mixin): 299 | code = 407 300 | message = "Proxy Authentication Required" 301 | exception = exceptions.ProxyAuthenticationRequiredException 302 | rfcs = ( 303 | ("7235", "3.2"), 304 | ) 305 | 306 | 307 | @status.register 308 | class RequestTimeout(base.ClientError, base.HTTP11Mixin): 309 | code = 408 310 | message = "Request Timeout" 311 | exception = exceptions.RequestTimeoutException 312 | rfcs = ( 313 | ("7231", "6.5.7"), 314 | ) 315 | 316 | 317 | @status.register 318 | class Conflict(base.ClientError, base.HTTP11Mixin): 319 | code = 409 320 | message = "Conflict" 321 | exception = exceptions.ConflictException 322 | rfcs = ( 323 | ("7231", "6.5.8"), 324 | ) 325 | 326 | 327 | @status.register 328 | class Gone(base.ClientError, base.HTTP11Mixin): 329 | code = 410 330 | message = "Gone" 331 | exception = exceptions.GoneException 332 | rfcs = ( 333 | ("7231", "6.5.9"), 334 | ) 335 | 336 | 337 | @status.register 338 | class LengthRequired(base.ClientError, base.HTTP11Mixin): 339 | code = 411 340 | message = "Length Required" 341 | exception = exceptions.LengthRequiredException 342 | rfcs = ( 343 | ("7231", "6.5.10"), 344 | ) 345 | 346 | 347 | @status.register 348 | class PreconditionFailed(base.ClientError, base.HTTP11Mixin): 349 | code = 412 350 | message = "Precondition Failed" 351 | exception = exceptions.PreconditionFailedException 352 | rfcs = ( 353 | ("7232", "4.2"), 354 | ("8144", "3.2"), 355 | ) 356 | 357 | 358 | @status.register 359 | class PayloadTooLarge(base.ClientError, base.HTTP11Mixin): 360 | code = 413 361 | message = "Payload Too Large" 362 | exception = exceptions.PayloadTooLargeException 363 | rfcs = ( 364 | ("7231", "6.5.11"), 365 | ) 366 | 367 | 368 | @status.register 369 | class URITooLong(base.ClientError, base.HTTP11Mixin): 370 | code = 414 371 | message = "URI Too Long" 372 | exception = exceptions.URITooLongException 373 | rfcs = ( 374 | ("7231", "6.5.12"), 375 | ) 376 | 377 | 378 | @status.register 379 | class UnsupportedMediaType(base.ClientError, base.HTTP11Mixin): 380 | code = 415 381 | message = "Unsupported Media Type" 382 | exception = exceptions.UnsupportedMediaTypeException 383 | rfcs = ( 384 | ("7231", "6.5.13"), 385 | ("7694", "3"), 386 | ) 387 | 388 | 389 | @status.register 390 | class RangeNotSatisfiable(base.ClientError, base.HTTP11Mixin): 391 | code = 416 392 | message = "Range Not Satisfiable" 393 | exception = exceptions.RangeNotSatisfiableException 394 | rfcs = ( 395 | ("7233", "4.4"), 396 | ) 397 | 398 | 399 | @status.register 400 | class ExpectationFailed(base.ClientError, base.HTTP11Mixin): 401 | code = 417 402 | message = "Expectation Failed" 403 | exception = exceptions.ExpectationFailedException 404 | rfcs = ( 405 | ("7231", "6.5.14"), 406 | ) 407 | 408 | 409 | @status.register 410 | class IAmATeapot(base.ClientError, base.HTCPCP10Mixin): 411 | code = 418 412 | message = "I am a teapot" 413 | exception = exceptions.IAmATeapotException 414 | 415 | 416 | @status.register 417 | class MethodFailure(base.ClientError, base.UnofficialMixin): 418 | code = 420 419 | message = "Method Failure" 420 | exception = exceptions.MethodFailureException 421 | reference = "Spring Framework" 422 | 423 | 424 | @status.register 425 | class MisdirectedRequest(base.ClientError, base.HTTP20Mixin): 426 | code = 421 427 | message = "Misdirected Request" 428 | exception = exceptions.MisdirectedRequestException 429 | rfcs = ( 430 | ("7540", "9.1.2"), 431 | ) 432 | 433 | 434 | @status.register 435 | class UnprocessableEntity(base.ClientError, base.WebDAVMixin): 436 | code = 422 437 | message = "Unprocessable Entity" 438 | exception = exceptions.UnprocessableEntityException 439 | rfcs = ( 440 | ("4918", ""), 441 | ) 442 | 443 | 444 | @status.register 445 | class Locked(base.ClientError, base.WebDAVMixin): 446 | code = 423 447 | message = "Locked" 448 | exception = exceptions.LockedException 449 | rfcs = ( 450 | ("4918", ""), 451 | ) 452 | 453 | 454 | @status.register 455 | class FailedDependency(base.ClientError, base.WebDAVMixin): 456 | code = 424 457 | message = "Failed Dependency" 458 | exception = exceptions.FailedDependencyException 459 | rfcs = ( 460 | ("4918", ""), 461 | ) 462 | 463 | 464 | @status.register 465 | class UpgradeRequired(base.ClientError, base.HTTP11Mixin): 466 | code = 426 467 | message = "Upgrade Required" 468 | exception = exceptions.UpgradeRequiredException 469 | rfcs = ( 470 | ("7231", "6.5.15"), 471 | ) 472 | 473 | 474 | @status.register 475 | class PreconditionRequired(base.ClientError, base.HTTP11Mixin): 476 | code = 428 477 | message = "Precondition Required" 478 | exception = exceptions.PreconditionRequiredException 479 | rfcs = ( 480 | ("6585", ""), 481 | ) 482 | 483 | 484 | @status.register 485 | class TooManyRequests(base.ClientError, base.HTTP11Mixin): 486 | code = 429 487 | message = "Too Many Requests" 488 | exception = exceptions.TooManyRequestsException 489 | rfcs = ( 490 | ("6585", ""), 491 | ) 492 | 493 | 494 | @status.register 495 | class RequestHeaderFieldsTooLarge(base.ClientError, base.HTTP11Mixin): 496 | code = 431 497 | message = "Request Header Fields Too Large" 498 | exception = exceptions.RequestHeaderFieldsTooLargeException 499 | rfcs = ( 500 | ("6585", ""), 501 | ) 502 | 503 | 504 | @status.register 505 | class LoginTimeout(base.ClientError, base.IISMixin): 506 | code = 440 507 | message = "Login Timeout" 508 | exception = exceptions.LoginTimeoutException 509 | 510 | 511 | @status.register 512 | class NoResponse(base.ClientError, base.NginxMixin): 513 | code = 444 514 | message = "No Response" 515 | exception = exceptions.NoResponseException 516 | 517 | 518 | @status.register 519 | class RetryWith(base.ClientError, base.IISMixin): 520 | code = 449 521 | message = "Retry With" 522 | exception = exceptions.RetryWithException 523 | 524 | 525 | @status.register 526 | class BlockedByWindowsParentalControls(base.ClientError, base.UnofficialMixin): 527 | code = 450 528 | message = "Blocked By Windows Parental Controls" 529 | reference = "Microsoft" 530 | exception = exceptions.BlockedByWindowsParentalControlsException 531 | 532 | 533 | @status.register 534 | class UnavailableForLegalReasons(base.ClientError, base.HTTP11Mixin): 535 | code = 451 536 | message = "Unavailable For Legal Reasons" 537 | exception = exceptions.UnavailableForLegalReasonsException 538 | rfcs = ( 539 | ("7725", ""), 540 | ) 541 | 542 | 543 | class Redirect(base.ClientError, base.IISMixin): 544 | code = 451 545 | message = "Redirect" 546 | exception = exceptions.BadGatewayException 547 | 548 | 549 | @status.register 550 | class SSLCertificateError(base.ClientError, base.NginxMixin): 551 | code = 495 552 | message = "SSL Certificate Error" 553 | exception = exceptions.SSLCertificateErrorException 554 | 555 | 556 | @status.register 557 | class SSLCertificateRequired(base.ClientError, base.NginxMixin): 558 | code = 496 559 | message = "SSL Certificate Required" 560 | exception = exceptions.SSLCertificateRequiredException 561 | 562 | 563 | @status.register 564 | class HTTPRequestSentToHTTPSPort(base.ClientError, base.NginxMixin): 565 | code = 497 566 | message = "HTTP Request Sent To HTTPS Port" 567 | exception = exceptions.HTTPRequestSentToHTTPSPortException 568 | 569 | 570 | @status.register 571 | class InvalidToken(base.ClientError, base.UnofficialMixin): 572 | code = 498 573 | message = "Invalid Token" 574 | reference = "ArcGIS for Server" 575 | exception = exceptions.InvalidTokenException 576 | 577 | 578 | @status.register 579 | class ClientClosedRequest(base.ClientError, base.NginxMixin): 580 | code = 499 581 | message = "Client Closed Request" 582 | exception = exceptions.ClientClosedRequestException 583 | 584 | 585 | class RequestHasBeenForbiddenByAntivirus(base.ClientError, base.UnofficialMixin): 586 | code = 499 587 | message = "Request Has Been Forbidden By Antivirus" 588 | reference = "https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#Unofficial_codes" 589 | exception = exceptions.RequestHasBeenForbiddenByAntivirusException 590 | 591 | 592 | class TokenRequired(base.ClientError, base.UnofficialMixin): 593 | code = 499 594 | message = "Token Required" 595 | reference = "ArcGIS for Server" 596 | exception = exceptions.TokenRequiredException 597 | 598 | 599 | @status.register 600 | class InternalServerError(base.ServerError, base.HTTP10Mixin): 601 | code = 500 602 | message = "Internal Server Error" 603 | exception = exceptions.InternalServerErrorException 604 | rfcs = ( 605 | ("7231", "6.6.1"), 606 | ) 607 | 608 | 609 | # noinspection PyShadowingBuiltins 610 | @status.register 611 | class NotImplemented(base.ServerError, base.HTTP10Mixin): 612 | code = 501 613 | message = "Not Implemented" 614 | exception = exceptions.NotImplementedException 615 | rfcs = ( 616 | ("7231", "6.6.2"), 617 | ) 618 | 619 | 620 | @status.register 621 | class BadGateway(base.ServerError, base.HTTP10Mixin): 622 | code = 502 623 | message = "Bad Gateway" 624 | exception = exceptions.BadGatewayException 625 | rfcs = ( 626 | ("7231", "6.6.3"), 627 | ) 628 | 629 | 630 | @status.register 631 | class ServiceUnavailable(base.ServerError, base.HTTP10Mixin): 632 | code = 503 633 | message = "Service Unavailable" 634 | exception = exceptions.ServiceUnavailableException 635 | rfcs = ( 636 | ("7231", "6.6.4"), 637 | ) 638 | 639 | 640 | @status.register 641 | class GatewayTimeout(base.ServerError, base.HTTP11Mixin): 642 | code = 504 643 | message = "Gateway Timeout" 644 | exception = exceptions.GatewayTimeoutException 645 | rfcs = ( 646 | ("7231", "6.6.5"), 647 | ) 648 | 649 | 650 | @status.register 651 | class HTTPVersionNotSupported(base.ServerError, base.HTTP11Mixin): 652 | code = 505 653 | message = "HTTP Version Not Supported" 654 | exception = exceptions.HTTPVersionNotSupportedException 655 | rfcs = ( 656 | ("7231", "6.6.6"), 657 | ) 658 | 659 | 660 | @status.register 661 | class VariantAlsoNegotiates(base.ServerError, base.HTTP11Mixin): 662 | code = 506 663 | message = "Variant Also Negotiates" 664 | exception = exceptions.VariantAlsoNegotiatesException 665 | rfcs = ( 666 | ("2295", ""), 667 | ) 668 | 669 | 670 | @status.register 671 | class InsufficientStorage(base.ServerError, base.WebDAVMixin): 672 | code = 507 673 | message = "Insufficient Storage" 674 | exception = exceptions.InsufficientStorageException 675 | rfcs = ( 676 | ("4918", ""), 677 | ) 678 | 679 | 680 | @status.register 681 | class LoopDetected(base.ServerError, base.WebDAVMixin): 682 | code = 508 683 | message = "Loop Detected" 684 | exception = exceptions.LoopDetectedException 685 | rfcs = ( 686 | ("5842", ""), 687 | ) 688 | 689 | 690 | class BandwidthLimitExceeded(base.ServerError, base.UnofficialMixin): 691 | code = 509 692 | message = "Bandwidth Limit Exceeded" 693 | reference = "Apache Web Server/cPanel" 694 | exception = exceptions.BandwidthLimitExceededException 695 | 696 | 697 | @status.register 698 | class NotExtended(base.ServerError, base.HTTP11Mixin): 699 | code = 510 700 | message = "Not Extended" 701 | exception = exceptions.NotExtendedException 702 | rfcs = ( 703 | ("2774", ""), 704 | ) 705 | 706 | 707 | @status.register 708 | class NetworkAuthenticationRequired(base.ServerError, base.HTTP11Mixin): 709 | code = 511 710 | message = "Network Authentication Required" 711 | exception = exceptions.NetworkAuthenticationRequiredException 712 | rfcs = ( 713 | ("6585", ""), 714 | ) 715 | 716 | 717 | @status.register 718 | class UnknownError(base.ServerError, base.CloudflareMixin): 719 | code = 520 720 | message = "Unknown Error" 721 | exception = exceptions.UnknownErrorException 722 | 723 | 724 | @status.register 725 | class WebServerIsDown(base.ServerError, base.CloudflareMixin): 726 | code = 521 727 | message = "Web Server Is Down" 728 | exception = exceptions.WebServerIsDownException 729 | 730 | 731 | @status.register 732 | class ConnectionTimedOut(base.ServerError, base.CloudflareMixin): 733 | code = 522 734 | message = "Connection Timed Out" 735 | exception = exceptions.ConnectionTimedOutException 736 | 737 | 738 | @status.register 739 | class OriginIsUnreachable(base.ServerError, base.CloudflareMixin): 740 | code = 523 741 | message = "Origin Is Unreachable" 742 | exception = exceptions.OriginIsUnreachableException 743 | 744 | 745 | @status.register 746 | class ATimeoutOccurred(base.ServerError, base.CloudflareMixin): 747 | code = 524 748 | message = "A Timeout Occurred" 749 | exception = exceptions.ATimeoutOccurredException 750 | 751 | 752 | @status.register 753 | class SSLHandshakeFailed(base.ServerError, base.CloudflareMixin): 754 | code = 525 755 | message = "SSL Handshake Failed" 756 | exception = exceptions.SSLHandshakeFailedException 757 | 758 | 759 | @status.register 760 | class InvalidSSLCertificate(base.ServerError, base.CloudflareMixin): 761 | code = 526 762 | message = "Invalid SSL Certificate" 763 | exception = exceptions.InvalidSSLCertificateException 764 | 765 | 766 | @status.register 767 | class SiteIsFrozen(base.ServerError, base.UnofficialMixin): 768 | code = 530 769 | message = "Site Is Frozen" 770 | reference = "Pantheon" 771 | exception = exceptions.SiteIsFrozenException 772 | -------------------------------------------------------------------------------- /staty/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | # noinspection PyPep8Naming 17 | from socket import gaierror as SocketNameResolutionError, herror as SocketHostError, timeout as SocketTimeout 18 | 19 | from .status_map import status 20 | 21 | 22 | # Base Exceptions & Mixins 23 | # ======================== 24 | 25 | 26 | class StatyBaseException(Exception): 27 | pass 28 | 29 | 30 | class RecoverableErrorMixin(Exception): 31 | pass 32 | 33 | 34 | class UnrecoverableErrorMixin(Exception): 35 | pass 36 | 37 | 38 | class RegistrationException(StatyBaseException): 39 | pass 40 | 41 | 42 | class MissingHandlerException(StatyBaseException): 43 | pass 44 | 45 | 46 | class HTTPError(StatyBaseException): 47 | def __init__(self, *args, response=None, request=None): 48 | self.response = response 49 | self.request = request 50 | if self.request is None and self.response is not None: 51 | self.request = getattr(self.response, "request", None) 52 | super().__init__(*args) 53 | 54 | @property 55 | def status(self): 56 | return status.exceptions[self.__class__] 57 | 58 | 59 | # Connection Errors 60 | # ================= 61 | 62 | class ConnectionException(StatyBaseException): 63 | pass 64 | 65 | 66 | class ConnectionTimeoutException(SocketTimeout, ConnectionException, RecoverableErrorMixin): 67 | pass 68 | 69 | 70 | class NameResolutionException(SocketNameResolutionError, ConnectionException, RecoverableErrorMixin): 71 | pass 72 | 73 | 74 | class HostAddressException(SocketHostError, ConnectionException, RecoverableErrorMixin): 75 | pass 76 | 77 | 78 | class SSLException(ConnectionException, RecoverableErrorMixin): 79 | pass 80 | 81 | 82 | class ProxyException(ConnectionException, RecoverableErrorMixin): 83 | pass 84 | 85 | 86 | # HTTP Client Errors 87 | # ================== 88 | 89 | class ClientErrorException(HTTPError): 90 | pass 91 | 92 | 93 | class RecoverableClientErrorException(ClientErrorException, RecoverableErrorMixin): 94 | pass 95 | 96 | 97 | class UnrecoverableClientErrorException(ClientErrorException, UnrecoverableErrorMixin): 98 | pass 99 | 100 | 101 | class PreconditionRequiredException(UnrecoverableClientErrorException): 102 | pass 103 | 104 | 105 | class TooManyRequestsException(RecoverableClientErrorException): 106 | pass 107 | 108 | 109 | class BadRequestException(UnrecoverableClientErrorException): 110 | pass 111 | 112 | 113 | class UnauthorizedException(UnrecoverableClientErrorException): 114 | pass 115 | 116 | 117 | class PaymentRequiredException(UnrecoverableClientErrorException): 118 | pass 119 | 120 | 121 | class ForbiddenException(UnrecoverableClientErrorException): 122 | pass 123 | 124 | 125 | class NotFoundException(UnrecoverableClientErrorException): 126 | pass 127 | 128 | 129 | class MethodNotAllowedException(UnrecoverableClientErrorException): 130 | pass 131 | 132 | 133 | class NotAcceptableException(UnrecoverableClientErrorException): 134 | pass 135 | 136 | 137 | class ProxyAuthenticationRequiredException(UnrecoverableClientErrorException): 138 | pass 139 | 140 | 141 | class RequestTimeoutException(RecoverableClientErrorException): 142 | pass 143 | 144 | 145 | class ConflictException(UnrecoverableClientErrorException): 146 | pass 147 | 148 | 149 | class GoneException(UnrecoverableClientErrorException): 150 | pass 151 | 152 | 153 | class LengthRequiredException(UnrecoverableClientErrorException): 154 | pass 155 | 156 | 157 | class PreconditionFailedException(UnrecoverableClientErrorException): 158 | pass 159 | 160 | 161 | class PayloadTooLargeException(UnrecoverableClientErrorException): 162 | pass 163 | 164 | 165 | class URITooLongException(UnrecoverableClientErrorException): 166 | pass 167 | 168 | 169 | class UnsupportedMediaTypeException(UnrecoverableClientErrorException): 170 | pass 171 | 172 | 173 | class RangeNotSatisfiableException(UnrecoverableClientErrorException): 174 | pass 175 | 176 | 177 | class ExpectationFailedException(UnrecoverableClientErrorException): 178 | pass 179 | 180 | 181 | class IAmATeapotException(UnrecoverableClientErrorException): 182 | pass 183 | 184 | 185 | class MisdirectedRequestException(UnrecoverableClientErrorException): 186 | pass 187 | 188 | 189 | class UnprocessableEntityException(UnrecoverableClientErrorException): 190 | pass 191 | 192 | 193 | class LockedException(UnrecoverableClientErrorException): 194 | pass 195 | 196 | 197 | class FailedDependencyException(UnrecoverableClientErrorException): 198 | pass 199 | 200 | 201 | class UpgradeRequiredException(UnrecoverableClientErrorException): 202 | pass 203 | 204 | 205 | class UnavailableForLegalReasonsException(UnrecoverableClientErrorException): 206 | pass 207 | 208 | 209 | class RequestHeaderFieldsTooLargeException(UnrecoverableClientErrorException): 210 | pass 211 | 212 | 213 | class MethodFailureException(UnrecoverableClientErrorException): 214 | pass 215 | 216 | 217 | class BlockedByWindowsParentalControlsException(UnrecoverableClientErrorException): 218 | pass 219 | 220 | 221 | class InvalidTokenException(UnrecoverableClientErrorException): 222 | pass 223 | 224 | 225 | class TokenRequiredException(UnrecoverableClientErrorException): 226 | pass 227 | 228 | 229 | class RequestHasBeenForbiddenByAntivirusException(UnrecoverableClientErrorException): 230 | pass 231 | 232 | 233 | class LoginTimeoutException(RecoverableClientErrorException): 234 | pass 235 | 236 | 237 | class RetryWithException(RecoverableClientErrorException): 238 | pass 239 | 240 | 241 | class SSLCertificateErrorException(UnrecoverableClientErrorException): 242 | pass 243 | 244 | 245 | class SSLCertificateRequiredException(UnrecoverableClientErrorException): 246 | pass 247 | 248 | 249 | class HTTPRequestSentToHTTPSPortException(UnrecoverableClientErrorException): 250 | pass 251 | 252 | 253 | class NoResponseException(UnrecoverableClientErrorException): 254 | pass 255 | 256 | 257 | class ClientClosedRequestException(RecoverableClientErrorException): 258 | pass 259 | 260 | 261 | # HTTP Server Errors 262 | # ================== 263 | 264 | class ServerErrorException(HTTPError, RecoverableErrorMixin): 265 | pass 266 | 267 | 268 | class InternalServerErrorException(ServerErrorException): 269 | pass 270 | 271 | 272 | class NotImplementedException(ServerErrorException): 273 | pass 274 | 275 | 276 | class BadGatewayException(ServerErrorException): 277 | pass 278 | 279 | 280 | class ServiceUnavailableException(ServerErrorException): 281 | pass 282 | 283 | 284 | class GatewayTimeoutException(ServerErrorException): 285 | pass 286 | 287 | 288 | class HTTPVersionNotSupportedException(ServerErrorException): 289 | pass 290 | 291 | 292 | class VariantAlsoNegotiatesException(ServerErrorException): 293 | pass 294 | 295 | 296 | class InsufficientStorageException(ServerErrorException): 297 | pass 298 | 299 | 300 | class LoopDetectedException(ServerErrorException): 301 | pass 302 | 303 | 304 | class NotExtendedException(ServerErrorException): 305 | pass 306 | 307 | 308 | class NetworkAuthenticationRequiredException(ServerErrorException): 309 | pass 310 | 311 | 312 | class BandwidthLimitExceededException(ServerErrorException): 313 | pass 314 | 315 | 316 | class SiteIsFrozenException(ServerErrorException): 317 | pass 318 | 319 | 320 | class InvalidSSLCertificateException(ServerErrorException): 321 | pass 322 | 323 | 324 | class SSLHandshakeFailedException(ServerErrorException): 325 | pass 326 | 327 | 328 | class ATimeoutOccurredException(ServerErrorException): 329 | pass 330 | 331 | 332 | class OriginIsUnreachableException(ServerErrorException): 333 | pass 334 | 335 | 336 | class ConnectionTimedOutException(ServerErrorException): 337 | pass 338 | 339 | 340 | class WebServerIsDownException(ServerErrorException): 341 | pass 342 | 343 | 344 | class UnknownErrorException(ServerErrorException): 345 | pass 346 | -------------------------------------------------------------------------------- /staty/handlers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osantana/staty/d9a6717716fa45ebd8ac073f3c61007fc86b8b5f/staty/handlers/__init__.py -------------------------------------------------------------------------------- /staty/handlers/requests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import warnings 17 | from functools import wraps 18 | 19 | from .. import exceptions 20 | from staty import status 21 | 22 | try: 23 | from requests import exceptions as request_exceptions 24 | except ImportError: 25 | warnings.warn("missing requests library", ImportWarning) # pragma: nocover 26 | request_exceptions = None # pragma: nocover 27 | 28 | 29 | def raise_for_status(response): 30 | if response.status_code in status.errors: 31 | status_class = status.errors[response.status_code] 32 | raise status_class.exception(response=response) 33 | 34 | return response 35 | 36 | 37 | class RequestSessionWrapper: 38 | def __init__(self, wrapped): 39 | self.wrapped = wrapped 40 | 41 | def __getattr__(self, item): 42 | attr = getattr(self.wrapped, item) 43 | if not callable(attr): 44 | return attr 45 | 46 | @wraps(attr) 47 | def wrapper(*args, **kwargs): 48 | try: 49 | return attr(*args, **kwargs) 50 | except request_exceptions.Timeout as exc: 51 | raise exceptions.ConnectionTimeoutException() from exc 52 | 53 | return wrapper 54 | 55 | 56 | def wrap(session): 57 | return RequestSessionWrapper(session) 58 | -------------------------------------------------------------------------------- /staty/status_map.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | from staty import exceptions, base 17 | from staty.utils import camel2snake 18 | 19 | 20 | class HTTPStatusMap: 21 | def __init__(self): 22 | self.codes = {} 23 | self.errors = {} 24 | self.attribute_names = {} 25 | self.exceptions = {} 26 | 27 | def __getattr__(self, item): 28 | try: 29 | return self.attribute_names[item] 30 | except KeyError as exc: 31 | raise AttributeError("{!r} object has no attribute {!r}".format(self.__class__.__name__, item)) from exc 32 | 33 | def __getitem__(self, item): 34 | return self.codes[item] 35 | 36 | def register(self, http_status_class): 37 | code = http_status_class.code 38 | name = http_status_class.__name__ 39 | 40 | if code in self.codes: 41 | raise exceptions.RegistrationException("Status code is already registered") 42 | 43 | http_status = http_status_class() 44 | self.codes[code] = http_status 45 | 46 | if issubclass(http_status_class, base.ErrorCodeMixin): 47 | self.errors[code] = http_status 48 | 49 | if hasattr(http_status_class, 'exception'): 50 | self.exceptions[http_status_class.exception] = http_status 51 | 52 | name = camel2snake(name, upper=True) 53 | 54 | status_name = "HTTP_{}_{}".format(code, name) 55 | self.attribute_names[status_name] = http_status 56 | 57 | return http_status_class 58 | 59 | 60 | status = HTTPStatusMap() 61 | -------------------------------------------------------------------------------- /staty/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import re 17 | 18 | 19 | def camel2snake(name, upper=False): 20 | name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) 21 | name = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name) 22 | return name.upper() if upper else name.lower() 23 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osantana/staty/d9a6717716fa45ebd8ac073f3c61007fc86b8b5f/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from requests import Response 3 | 4 | from staty import Ok, BadRequest 5 | 6 | 7 | @pytest.fixture 8 | def response(): 9 | return Response() 10 | 11 | 12 | @pytest.fixture 13 | def ok_response(response): 14 | response.status_code = Ok.code 15 | response.reason = Ok.message 16 | return response 17 | 18 | 19 | @pytest.fixture 20 | def bad_request_response(response): 21 | response.status_code = BadRequest.code 22 | response.reason = BadRequest.message 23 | return response 24 | -------------------------------------------------------------------------------- /tests/test_request_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import pytest 17 | 18 | from staty import BadRequestException 19 | from staty.handlers.requests import raise_for_status 20 | 21 | 22 | def test_basic_response(ok_response): 23 | response = raise_for_status(ok_response) 24 | assert response == ok_response 25 | 26 | 27 | # noinspection PyTypeChecker,PyShadowingNames 28 | def test_error_response(bad_request_response): 29 | with pytest.raises(BadRequestException): 30 | raise_for_status(bad_request_response) 31 | -------------------------------------------------------------------------------- /tests/test_staty.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import pytest 17 | 18 | from staty import InternalServerError, Ok, base, exceptions, status, BadRequest 19 | 20 | 21 | def test_class_names_and_messages(): 22 | for st in status.codes.values(): 23 | message = st.message.replace(" ", "").replace("-", "").lower() 24 | assert st.__class__.__name__.lower() == message 25 | 26 | 27 | @pytest.mark.parametrize("category_class,quantity", [ 28 | (base.Informational, 4), 29 | (base.Successful, 10), 30 | (base.Redirection, 9), 31 | (base.ClientError, 38), 32 | (base.ServerError, 19), 33 | ]) 34 | def test_status_in_categories(category_class, quantity): 35 | statuses = [s for s in status.codes.values() if isinstance(s, category_class)] 36 | assert len(statuses) == quantity 37 | 38 | 39 | def test_status_instance(): 40 | ok = Ok() 41 | assert str(ok) == "200 OK" 42 | assert ok == 200 43 | 44 | 45 | def test_exception_class_names(): 46 | for http_status in status.codes.values(): 47 | if not hasattr(http_status, "exception"): 48 | continue 49 | 50 | assert "{}Exception".format(http_status.__class__.__name__) == http_status.exception.__name__ 51 | 52 | 53 | def test_exception_response_argument(): 54 | try: 55 | raise exceptions.HTTPError(response="response") 56 | except exceptions.HTTPError as exc: 57 | assert exc.response == "response" 58 | 59 | 60 | def test_exception_request_argument(): 61 | try: 62 | raise exceptions.HTTPError(request="request") 63 | except exceptions.HTTPError as exc: 64 | assert exc.request == "request" 65 | 66 | 67 | def test_exception_response_request_argument(): 68 | class FakeResponse: 69 | request = "request" 70 | 71 | fake_response = FakeResponse() 72 | 73 | try: 74 | raise exceptions.HTTPError(response=fake_response) 75 | except exceptions.HTTPError as exc: 76 | assert exc.response == fake_response 77 | assert exc.request == "request" 78 | 79 | 80 | def test_register_class(): 81 | class DummyClass: 82 | code = 999 83 | message = "Dummy Class" 84 | 85 | registered = status.register(DummyClass) 86 | assert registered == DummyClass 87 | 88 | 89 | def test_cannot_register_twice(): 90 | class DummyClass(object): 91 | code = 100 # Status code 100 is already registered 92 | 93 | with pytest.raises(exceptions.RegistrationException): 94 | status.register(DummyClass) 95 | 96 | 97 | def test_registered_attributes_in_status_map(): 98 | assert status.HTTP_200_OK == 200 99 | assert status.HTTP_400_BAD_REQUEST == BadRequest() 100 | assert status.HTTP_404_NOT_FOUND == "not found" 101 | assert status.HTTP_500_INTERNAL_SERVER_ERROR == InternalServerError 102 | assert status.HTTP_304_NOT_MODIFIED == status.HTTP_304_NOT_MODIFIED 103 | 104 | assert status[200] == 200 105 | assert status[400] == BadRequest() 106 | assert status[404] == "not found" 107 | assert status[500] == InternalServerError 108 | assert status[304] == status.HTTP_304_NOT_MODIFIED 109 | 110 | assert status[200] != [] 111 | assert status[200] != ["invalid comparison"] 112 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Osvaldo Santana Neto 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | import pytest 17 | 18 | from staty.utils import camel2snake 19 | 20 | 21 | @pytest.mark.parametrize("camel,snake", [ 22 | ('CamelCase', 'camel_case'), 23 | ('XYZCamelCase', 'xyz_camel_case'), 24 | ('xyzCamelCase', 'xyz_camel_case'), 25 | ('Camel123Case', 'camel123_case'), 26 | ('CamelCaseXYZ', 'camel_case_xyz'), 27 | ]) 28 | def test_camel_to_snake_case_lower(camel, snake): 29 | assert camel2snake(camel) == snake 30 | 31 | 32 | @pytest.mark.parametrize("camel,snake", [ 33 | ('CamelCase', 'CAMEL_CASE'), 34 | ('XYZCamelCase', 'XYZ_CAMEL_CASE'), 35 | ('xyzCamelCase', 'XYZ_CAMEL_CASE'), 36 | ('Camel123Case', 'CAMEL123_CASE'), 37 | ('CamelCaseXYZ', 'CAMEL_CASE_XYZ'), 38 | ]) 39 | def test_camel_to_snake_case_upper(camel, snake): 40 | assert camel2snake(camel, upper=True) == snake 41 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py37, py38 3 | 4 | [testenv] 5 | passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH 6 | deps = 7 | -rrequirements-dev.txt 8 | commands = 9 | py.test --cov=staty 10 | 11 | [tox:travis] 12 | 3.7 = py37 13 | 3.8 = py38 14 | --------------------------------------------------------------------------------