Codebase list python-werkzeug / upstream/0.15.6+dfsg1
New upstream version 0.15.6+dfsg1 Ondřej Nový 4 years ago
30 changed file(s) with 281 addition(s) and 179 deletion(s). Raw diff Collapse all Expand all
+0
-26
.appveyor.yml less more
0 environment:
1 global:
2 TOXENV: py,codecov
3
4 matrix:
5 - PYTHON: C:\Python37-x64
6 - PYTHON: C:\Python27-x64
7
8 init:
9 - SET PATH=%PYTHON%;%PATH%
10
11 install:
12 - python -m pip install -U tox
13
14 build: false
15
16 test_script:
17 - python -m tox --skip-missing-interpreters false
18
19 branches:
20 only:
21 - master
22 - /^\d+(\.\d+)*(\.x)?$/
23
24 cache:
25 - '%LOCALAPPDATA%\pip\Cache'
0 trigger:
1 - master
2 - '*.x'
3
4 variables:
5 vmImage: ubuntu-latest
6 python.version: 3.7
7 TOXENV: py,coverage-ci
8 hasTestResults: true
9
10 strategy:
11 matrix:
12 Python 3.7 Linux:
13 vmImage: ubuntu-latest
14 Python 3.7 Windows:
15 vmImage: windows-latest
16 Python 3.7 Mac:
17 vmImage: macos-latest
18 PyPy 3 Linux:
19 python.version: pypy3
20 Python 3.6 Linux:
21 python.version: 3.6
22 Python 3.5 Linux:
23 python.version: 3.5
24 Python 2.7 Linux:
25 python.version: 2.7
26 Python 2.7 Windows:
27 python.version: 2.7
28 vmImage: windows-latest
29 Docs:
30 TOXENV: docs-html
31 hasTestResults: false
32 Style:
33 TOXENV: style
34 hasTestResults: false
35
36 pool:
37 vmImage: $[ variables.vmImage ]
38
39 steps:
40 - task: UsePythonVersion@0
41 inputs:
42 versionSpec: $(python.version)
43 displayName: Use Python $(python.version)
44
45 - script: pip --disable-pip-version-check install -U tox
46 displayName: Install tox
47
48 - script: tox -s false -- --junit-xml=test-results.xml
49 displayName: Run tox
50
51 - task: PublishTestResults@2
52 inputs:
53 testResultsFiles: test-results.xml
54 testRunTitle: $(Agent.JobName)
55 condition: eq(variables['hasTestResults'], 'true')
56 displayName: Publish test results
57
58 - task: PublishCodeCoverageResults@1
59 inputs:
60 codeCoverageTool: Cobertura
61 summaryFileLocation: coverage.xml
62 condition: eq(variables['hasTestResults'], 'true')
63 displayName: Publish coverage results
0 # Normalize CRLF to LF for all text files
1 * text=auto
2
3 # Declare binary file types so they won't be normalized
4 *.png binary
5 *.jpg binary
06 tests/**/*.http binary
7 tests/res/test.txt binary
+0
-47
.travis.yml less more
0 os: linux
1 dist: xenial
2 language: python
3 python:
4 - "3.7"
5 - "3.6"
6 - "3.5"
7 - "3.4"
8 - "2.7"
9 - "nightly"
10 - "pypy3.5-6.0"
11 env: TOXENV=py,codecov
12
13 matrix:
14 include:
15 - env: TOXENV=stylecheck,docs-html
16 - os: osx
17 language: generic
18 env: TOXENV=py3,codecov
19 cache:
20 directories:
21 - $HOME/Library/Caches/Homebrew
22 - $HOME/Library/Caches/pip
23 allow_failures:
24 - python: nightly
25 - python: pypy3.5-6.0
26 - os: osx
27 fast_finish: true
28
29 install:
30 - pip install -U tox
31
32 script:
33 - tox --skip-missing-interpreters false
34
35 cache:
36 directories:
37 - $HOME/.cache/pip
38 - $HOME/.cache/pre-commit
39
40 branches:
41 only:
42 - master
43 - /^\d+(\.\d+)*(\.x)?$/
44
45 notifications:
46 email: false
00 .. currentmodule:: werkzeug
1
2 Version 0.15.6
3 --------------
4
5 Released 2019-09-04
6
7 - Work around a bug in pip that caused the reloader to fail on
8 Windows when the script was an entry point. This fixes the issue
9 with Flask's `flask run` command failing with "No module named
10 Scripts\flask". :issue:`1614`
11 - ``ProxyFix`` trusts the ``X-Forwarded-Proto`` header by default.
12 :issue:`1630`
13 - The deprecated ``num_proxies`` argument to ``ProxyFix`` sets
14 ``x_for``, ``x_proto``, and ``x_host`` to match 0.14 behavior. This
15 is intended to make intermediate upgrades less disruptive, but the
16 argument will still be removed in 1.0. :issue:`1630`
17
18
19 Version 0.15.5
20 --------------
21
22 Released 2019-07-17
23
24 - Fix a ``TypeError`` due to changes to ``ast.Module`` in Python 3.8.
25 :issue:`1551`
26 - Fix a C assertion failure in debug builds of some Python 2.7
27 releases. :issue:`1553`
28 - :class:`~exceptions.BadRequestKeyError` adds the ``KeyError``
29 message to the description if ``e.show_exception`` is set to
30 ``True``. This is a more secure default than the original 0.15.0
31 behavior and makes it easier to control without losing information.
32 :pr:`1592`
33 - Upgrade the debugger to jQuery 3.4.1. :issue:`1581`
34 - Work around an issue in some external debuggers that caused the
35 reloader to fail. :issue:`1607`
36 - Work around an issue where the reloader couldn't introspect a
37 setuptools script installed as an egg. :issue:`1600`
38 - The reloader will use ``sys.executable`` even if the script is
39 marked executable, reverting a behavior intended for NixOS
40 introduced in 0.15. The reloader should no longer cause
41 ``OSError: [Errno 8] Exec format error``. :issue:`1482`,
42 :issue:`1580`
43 - ``SharedDataMiddleware`` safely handles paths with Windows drive
44 names. :issue:`1589`
45
146
247 Version 0.15.4
348 --------------
6464 Links
6565 -----
6666
67 - Website: https://www.palletsprojects.com/p/werkzeug/
67 - Website: https://palletsprojects.com/p/werkzeug/
6868 - Documentation: https://werkzeug.palletsprojects.com/
6969 - Releases: https://pypi.org/project/Werkzeug/
7070 - Code: https://github.com/pallets/werkzeug
7171 - Issue tracker: https://github.com/pallets/werkzeug/issues
72 - Test status:
73
74 - Linux, Mac: https://travis-ci.org/pallets/werkzeug
75 - Windows: https://ci.appveyor.com/project/pallets/werkzeug
76
77 - Test coverage: https://codecov.io/gh/pallets/werkzeug
72 - Test status: https://dev.azure.com/pallets/werkzeug/_build
7873 - Official chat: https://discord.gg/t6rrQZH
7974
8075 .. _WSGI: https://wsgi.readthedocs.io/en/latest/
22
33 .. warning::
44 ``werkzeug.contrib.profiler`` has moved to
5 :mod:`werkzeug.middleware.profile`. The old import is deprecated as
5 :mod:`werkzeug.middleware.profiler`. The old import is deprecated as
66 of version 0.15 and will be removed in version 1.0.
77
88 .. autoclass:: werkzeug.contrib.profiler.MergeStream
11 API Levels
22 ==========
33
4 .. module:: werkzeug
4 .. currentmodule:: werkzeug
55
66 Werkzeug is intended to be a utility rather than a framework. Because of that
77 the user-friendly API is separated from the lower-level API so that Werkzeug
11 Quickstart
22 ==========
33
4 .. module:: werkzeug
4 .. currentmodule:: werkzeug
55
66 This part of the documentation shows how to use the most important parts of
77 Werkzeug. It's intended as a starting point for developers with basic
22 Dealing with Request Data
33 =========================
44
5 .. module:: werkzeug
5 .. currentmodule:: werkzeug
66
77 The most important rule about web development is "Do not trust the user".
88 This is especially true for incoming request data on the input stream.
11 Important Terms
22 ===============
33
4 .. module:: werkzeug
4 .. currentmodule:: werkzeug
55
66 This page covers important terms used in the documentation and Werkzeug
77 itself.
11 Werkzeug Tutorial
22 =================
33
4 .. module:: werkzeug
4 .. currentmodule:: werkzeug
55
66 Welcome to the Werkzeug tutorial in which we will create a `TinyURL`_ clone
77 that stores URLs in a redis instance. The libraries we will use for this
33 Unicode
44 =======
55
6 .. module:: werkzeug
6 .. currentmodule:: werkzeug
77
88 Since early Python 2 days unicode was part of all default Python builds. It
99 allows developers to write applications that deal with non-ASCII characters
2121 license="BSD-3-Clause",
2222 author="Armin Ronacher",
2323 author_email="armin.ronacher@active-4.com",
24 maintainer="The Pallets Team",
24 maintainer="Pallets",
2525 maintainer_email="contact@palletsprojects.com",
2626 description="The comprehensive WSGI web application library.",
2727 long_description=readme,
1616 import sys
1717 from types import ModuleType
1818
19 __version__ = "0.15.4"
19 __version__ = "0.15.6"
2020
2121 # This import magic raises concerns quite often which is why the implementation
2222 # and motivation is explained here in detail now.
6565 "redirect",
6666 "cached_property",
6767 "import_string",
68 "dump_cookie",
69 "parse_cookie",
7068 "unescape",
7169 "format_string",
7270 "find_modules",
145143 "unquote_header_value",
146144 "quote_header_value",
147145 "HTTP_STATUS_CODES",
146 "dump_cookie",
147 "parse_cookie",
148148 ],
149149 "werkzeug.wrappers": [
150150 "BaseResponse",
6060
6161
6262 def _get_args_for_reloading():
63 """Returns the executable. This contains a workaround for windows
64 if the executable is incorrectly reported to not have the .exe
65 extension which can cause bugs on reloading. This also contains
66 a workaround for linux where the file is executable (possibly with
67 a program other than python)
63 """Determine how the script was executed, and return the args needed
64 to execute it again in a new process.
6865 """
6966 rv = [sys.executable]
70 py_script = os.path.abspath(sys.argv[0])
67 py_script = sys.argv[0]
7168 args = sys.argv[1:]
7269 # Need to look at main module to determine how it was executed.
7370 __main__ = sys.modules["__main__"]
7471
75 if __main__.__package__ is None:
72 # The value of __package__ indicates how Python was called. It may
73 # not exist if a setuptools script is installed as an egg. It may be
74 # set incorrectly for entry points created with pip on Windows.
75 if getattr(__main__, "__package__", None) is None or (
76 os.name == "nt"
77 and __main__.__package__ == ""
78 and not os.path.exists(py_script)
79 and os.path.exists(py_script + ".exe")
80 ):
7681 # Executed a file, like "python app.py".
82 py_script = os.path.abspath(py_script)
83
7784 if os.name == "nt":
7885 # Windows entry points have ".exe" extension and should be
7986 # called directly.
8188 py_script += ".exe"
8289
8390 if (
84 os.path.splitext(rv[0])[1] == ".exe"
91 os.path.splitext(sys.executable)[1] == ".exe"
8592 and os.path.splitext(py_script)[1] == ".exe"
8693 ):
8794 rv.pop(0)
88
89 elif os.path.isfile(py_script) and os.access(py_script, os.X_OK):
90 # The file is marked as executable. Nix adds a wrapper that
91 # shouldn't be called with the Python executable.
92 rv.pop(0)
9395
9496 rv.append(py_script)
9597 else:
100102 # TODO remove this once Flask no longer misbehaves
101103 args = sys.argv
102104 else:
103 py_module = __main__.__package__
104 name = os.path.splitext(os.path.basename(py_script))[0]
105
106 if name != "__main__":
107 py_module += "." + name
105 if os.path.isfile(py_script):
106 # Rewritten by Python from "-m script" to "/path/to/script.py".
107 py_module = __main__.__package__
108 name = os.path.splitext(os.path.basename(py_script))[0]
109
110 if name != "__main__":
111 py_module += "." + name
112 else:
113 # Incorrectly rewritten by pydevd debugger from "-m script" to "script".
114 py_module = py_script
108115
109116 rv.extend(("-m", py_module.lstrip(".")))
110117
6161 display: inline;
6262 }
6363
64 div.traceback blockquote { margin: 1em 0 0 0; padding: 0; }
64 div.traceback blockquote { margin: 1em 0 0 0; padding: 0; white-space: pre-line; }
6565 div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; }
6666 div.traceback img:hover { background-color: #ddd; cursor: pointer;
6767 border-color: #BFDDE0; }
8383 description = None
8484
8585 def __init__(self, description=None, response=None):
86 super(Exception, self).__init__()
86 super(HTTPException, self).__init__()
8787 if description is not None:
8888 self.description = description
8989 self.response = response
9595
9696 The first argument to the class will be passed to the
9797 wrapped ``exception``, the rest to the HTTP exception. If
98 ``self.args`` is not empty, the wrapped exception message is
99 added to the HTTP exception description.
100
101 .. versionchanged:: 0.15
98 ``e.args`` is not empty and ``e.show_exception`` is ``True``,
99 the wrapped exception message is added to the HTTP error
100 description.
101
102 .. versionchanged:: 0.15.5
103 The ``show_exception`` attribute controls whether the
104 description includes the wrapped exception message.
105
106 .. versionchanged:: 0.15.0
102107 The description includes the wrapped exception message.
103108 """
104109
105110 class newcls(cls, exception):
111 _description = cls.description
112 show_exception = False
113
106114 def __init__(self, arg=None, *args, **kwargs):
107115 super(cls, self).__init__(*args, **kwargs)
108116
111119 else:
112120 exception.__init__(self, arg)
113121
114 def get_description(self, environ=None):
115 out = super(cls, self).get_description(environ=environ)
116
117 if self.args:
118 out += "<p><pre><code>{}: {}</code></pre></p>".format(
119 exception.__name__, escape(exception.__str__(self))
122 @property
123 def description(self):
124 if self.show_exception:
125 return "{}\n{}: {}".format(
126 self._description, exception.__name__, exception.__str__(self)
120127 )
121128
122 return out
129 return self._description
130
131 @description.setter
132 def description(self, value):
133 self._description = value
123134
124135 newcls.__module__ = sys._getframe(1).f_globals.get("__name__")
125 newcls.__name__ = name or cls.__name__ + exception.__name__
136 name = name or cls.__name__ + exception.__name__
137 newcls.__name__ = newcls.__qualname__ = name
126138 return newcls
127139
128140 @property
132144
133145 def get_description(self, environ=None):
134146 """Get the description."""
135 return u"<p>%s</p>" % escape(self.description)
147 return u"<p>%s</p>" % escape(self.description).replace("\n", "<br>")
136148
137149 def get_body(self, environ=None):
138150 """Get the HTML body."""
764776 _aborter = Aborter()
765777
766778
767 #: an exception that is used internally to signal both a key error and a
768 #: bad request. Used by a lot of the datastructures.
779 #: An exception that is used to signal both a :exc:`KeyError` and a
780 #: :exc:`BadRequest`. Used by many of the datastructures.
769781 BadRequestKeyError = BadRequest.wrap(KeyError)
770782
771783 # imported here because of circular dependencies of werkzeug.utils
7676 """
7777
7878 def __init__(
79 self, app, num_proxies=None, x_for=1, x_proto=0, x_host=0, x_port=0, x_prefix=0
79 self, app, num_proxies=None, x_for=1, x_proto=1, x_host=0, x_port=0, x_prefix=0
8080 ):
8181 self.app = app
8282 self.x_for = x_for
111111 if value is not None:
112112 warnings.warn(
113113 "'num_proxies' is deprecated as of version 0.15 and"
114 " will be removed in version 1.0. Use 'x_for' instead.",
114 " will be removed in version 1.0. Use"
115 " 'x_for={value}, x_proto={value}, x_host={value}'"
116 " instead.".format(value=value),
115117 DeprecationWarning,
116118 stacklevel=2,
117119 )
118120 self.x_for = value
121 self.x_proto = value
122 self.x_host = value
119123
120124 def get_remote_addr(self, forwarded_for):
121125 """Get the real ``remote_addr`` by looking backwards ``x_for``
2121 from ..filesystem import get_filesystem_encoding
2222 from ..http import http_date
2323 from ..http import is_resource_modified
24 from ..security import safe_join
2425 from ..wsgi import get_path_info
2526 from ..wsgi import wrap_file
2627
148149 if path is None:
149150 return None, None
150151
151 path = posixpath.join(package_path, path)
152 path = safe_join(package_path, path)
152153
153154 if not provider.has_resource(path):
154155 return None, None
169170 def get_directory_loader(self, directory):
170171 def loader(path):
171172 if path is not None:
172 path = os.path.join(directory, path)
173 path = safe_join(directory, path)
173174 else:
174175 path = directory
175176
191192 )
192193
193194 def __call__(self, environ, start_response):
194 cleaned_path = get_path_info(environ)
195 path = get_path_info(environ)
195196
196197 if PY2:
197 cleaned_path = cleaned_path.encode(get_filesystem_encoding())
198
199 # sanitize the path for non unix systems
200 cleaned_path = cleaned_path.strip("/")
201
202 for sep in os.sep, os.altsep:
203 if sep and sep != "/":
204 cleaned_path = cleaned_path.replace(sep, "/")
205
206 path = "/" + "/".join(x for x in cleaned_path.split("/") if x and x != "..")
198 path = path.encode(get_filesystem_encoding())
199
207200 file_loader = None
208201
209202 for search_path, loader in self.exports:
938938 func_ast.args.args.append(ast.arg(arg, None))
939939 func_ast.args.kwarg = ast.arg(".kwargs", None)
940940 else:
941 func_ast.args.args.append(ast.Name(".self", ast.Load()))
941 func_ast.args.args.append(ast.Name(".self", ast.Param()))
942942 for arg in pargs + kargs:
943 func_ast.args.args.append(ast.Name(arg, ast.Load()))
943 func_ast.args.args.append(ast.Name(arg, ast.Param()))
944944 func_ast.args.kwarg = ".kwargs"
945945 for _ in kargs:
946946 func_ast.args.defaults.append(ast.Str(""))
947947 func_ast.body = body
948948
949 module = ast.fix_missing_locations(ast.Module([func_ast]))
949 # use `ast.parse` instead of `ast.Module` for better portability
950 # python3.8 changes the signature of `ast.Module`
951 module = ast.parse("")
952 module.body = [func_ast]
953
954 # mark everything as on line 1, offset 0
955 # less error-prone than `ast.fix_missing_locations`
956 # bad line numbers cause an assert to fail in debug builds
957 for node in ast.walk(module):
958 if "lineno" in node._attributes:
959 node.lineno = 1
960 if "col_offset" in node._attributes:
961 node.col_offset = 0
962
950963 code = compile(module, "<werkzeug routing>", "exec")
951964 return self._get_func_code(code, func_ast.name)
952965
221221
222222
223223 def safe_join(directory, *pathnames):
224 """Safely join `directory` and one or more untrusted `pathnames`. If this
225 cannot be done, this function returns ``None``.
226
227 :param directory: the base directory.
228 :param pathnames: the untrusted pathnames relative to that directory.
224 """Safely join zero or more untrusted path components to a base
225 directory to avoid escaping the base directory.
226
227 :param directory: The trusted base directory.
228 :param pathnames: The untrusted path components relative to the
229 base directory.
230 :return: A safe path, otherwise ``None``.
229231 """
230232 parts = [directory]
233
231234 for filename in pathnames:
232235 if filename != "":
233236 filename = posixpath.normpath(filename)
234 for sep in _os_alt_seps:
235 if sep in filename:
236 return None
237 if os.path.isabs(filename) or filename == ".." or filename.startswith("../"):
237
238 if (
239 any(sep in filename for sep in _os_alt_seps)
240 or os.path.isabs(filename)
241 or filename == ".."
242 or filename.startswith("../")
243 ):
238244 return None
245
239246 parts.append(filename)
247
240248 return posixpath.join(*parts)
205205
206206 @classmethod
207207 def application(cls, f):
208 """Decorate a function as responder that accepts the request as first
209 argument. This works like the :func:`responder` decorator but the
210 function is passed the request object as first argument and the
211 request object will be closed automatically::
208 """Decorate a function as responder that accepts the request as
209 the last argument. This works like the :func:`responder`
210 decorator but the function is passed the request object as the
211 last argument and the request object will be closed
212 automatically::
212213
213214 @Request.application
214215 def my_wsgi_app(request):
224225 #: and calls the function with all the arguments up to that one and
225226 #: the request. The return value is then called with the latest
226227 #: two arguments. This makes it possible to use this decorator for
227 #: both methods and standalone WSGI functions.
228 #: both standalone WSGI functions as well as bound methods and
229 #: partially applied functions.
228230 from ..exceptions import HTTPException
229231
230232 def application(*args):
1717 "REMOTE_ADDR": "192.168.0.2",
1818 "HTTP_HOST": "spam",
1919 "HTTP_X_FORWARDED_FOR": "192.168.0.1",
20 "HTTP_X_FORWARDED_PROTO": "https",
2021 },
21 "http://spam/",
22 "https://spam/",
2223 id="for",
2324 ),
2425 pytest.param(
177178 def test_proxy_fix_deprecations():
178179 app = pytest.deprecated_call(ProxyFix, None, 2)
179180 assert app.x_for == 2
181 assert app.x_proto == 2
182 assert app.x_host == 2
180183
181184 with pytest.deprecated_call():
182185 assert app.num_proxies == 2
00 # -*- coding: utf-8 -*-
11 import os
2 import sys
23 from contextlib import closing
4
5 import pytest
36
47 from werkzeug._compat import to_native
58 from werkzeug.middleware.shared_data import SharedDataMiddleware
1215 assert callable(app.get_file_loader("foo"))
1316
1417
18 @pytest.mark.xfail(
19 sys.version_info.major == 2 and sys.platform == "win32",
20 reason="TODO fix test for Python 2 on Windows",
21 )
1522 def test_shared_data_middleware(tmpdir):
1623 def null_application(environ, start_response):
1724 start_response("404 NOT FOUND", [("Content-Type", "text/plain")])
582582 with pytest.raises(BadRequestKeyError) as exc_info:
583583 data["baz"]
584584
585 assert "baz" not in exc_info.value.get_description()
586 exc_info.value.show_exception = True
585587 assert "baz" in exc_info.value.get_description()
586588
587589 with pytest.raises(BadRequestKeyError) as exc_info:
588590 data.pop("baz")
589591
592 exc_info.value.show_exception = True
590593 assert "baz" in exc_info.value.get_description()
591594 exc_info.value.args = ()
592595 assert "baz" not in exc_info.value.get_description()
146146
147147
148148 @pytest.mark.skipif(watchdog is None, reason="Watchdog not installed.")
149 @pytest.mark.xfail(
150 sys.version_info.major == 2 and sys.platform == "win32",
151 reason="TODO fix test for Python 2 on Windows",
152 )
149153 def test_reloader_broken_imports(tmpdir, dev_server):
150154 # We explicitly assert that the server reloads on change, even though in
151155 # this case the import could've just been retried. This is to assert
236240
237241
238242 @pytest.mark.skipif(watchdog is None, reason="Watchdog not installed.")
243 @pytest.mark.xfail(
244 sys.version_info.major == 2 and sys.platform == "win32",
245 reason="TODO fix test for Python 2 on Windows",
246 )
239247 def test_reloader_reports_correct_file(tmpdir, dev_server):
240248 real_app = tmpdir.join("real_app.py")
241249 real_app.write(
202202 def test_import_string_attribute_error(tmpdir, monkeypatch):
203203 monkeypatch.syspath_prepend(str(tmpdir))
204204 tmpdir.join("foo_test.py").write("from bar_test import value")
205 tmpdir.join("bar_test.py").write('raise AttributeError("screw you!")')
206 with pytest.raises(AttributeError) as foo_exc:
205 tmpdir.join("bar_test.py").write("raise AttributeError('bad')")
206
207 with pytest.raises(AttributeError) as info:
207208 utils.import_string("foo_test")
208 assert "screw you!" in str(foo_exc)
209
210 with pytest.raises(AttributeError) as bar_exc:
209
210 assert "bad" in str(info.value)
211
212 with pytest.raises(AttributeError) as info:
211213 utils.import_string("bar_test")
212 assert "screw you!" in str(bar_exc)
214
215 assert "bad" in str(info.value)
213216
214217
215218 def test_find_modules():
362362 strict_eq(response.status, "0 wtf")
363363
364364 # invalid status codes
365 with pytest.raises(ValueError) as empty_string_error:
365 with pytest.raises(ValueError) as info:
366366 wrappers.BaseResponse(None, "")
367 assert "Empty status argument" in str(empty_string_error)
368
369 with pytest.raises(TypeError) as invalid_type_error:
367
368 assert "Empty status argument" in str(info.value)
369
370 with pytest.raises(TypeError) as info:
370371 wrappers.BaseResponse(None, tuple())
371 assert "Invalid status argument" in str(invalid_type_error)
372
373 assert "Invalid status argument" in str(info.value)
372374
373375
374376 def test_type_forcing():
00 [tox]
11 envlist =
2 py{37,36,35,34,27,py3,py}
3 stylecheck
2 py{37,36,35,27,py3,py}
3 style
44 docs-html
5 coverage-report
5 coverage
66 skip_missing_interpreters = true
77
88 [testenv]
1818 watchdog
1919 commands = coverage run -p -m pytest --tb=short --basetemp={envtmpdir} {posargs}
2020
21 [testenv:stylecheck]
21 [testenv:style]
2222 deps = pre-commit
2323 skip_install = true
2424 commands = pre-commit run --all-files --show-diff-on-failure
3030 sphinx-issues
3131 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
3232
33 [testenv:coverage-report]
33 [testenv:coverage]
3434 deps = coverage
3535 skip_install = true
3636 commands =
3838 coverage html
3939 coverage report
4040
41 [testenv:codecov]
42 passenv = CI TRAVIS TRAVIS_* APPVEYOR APPVEYOR_*
43 deps = codecov
41 [testenv:coverage-ci]
42 deps = coverage
4443 skip_install = true
4544 commands =
4645 coverage combine
47 codecov
46 coverage xml
4847 coverage report