Compare commits
	
		
			7 Commits
		
	
	
		
			v21.12.0
			...
			py37-catch
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 162cb43c4b | ||
|   | 698a359808 | ||
|   | c4da66bf1f | ||
|   | d50d3b8448 | ||
|   | 313f97ac77 | ||
|   | a23547d73b | ||
|   | 34d1dee407 | 
							
								
								
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							| @@ -2,9 +2,13 @@ name: "CodeQL" | |||||||
|  |  | ||||||
| on: | on: | ||||||
|   push: |   push: | ||||||
|     branches: [ main ] |     branches: | ||||||
|  |       - main | ||||||
|  |       - "*LTS" | ||||||
|   pull_request: |   pull_request: | ||||||
|     branches: [ main ] |     branches: | ||||||
|  |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|   schedule: |   schedule: | ||||||
|     - cron: '25 16 * * 0' |     - cron: '25 16 * * 0' | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/coverage.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/coverage.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   push: |   push: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     tags: |     tags: | ||||||
|       - "!*" # Do not execute on tags |       - "!*" # Do not execute on tags | ||||||
|   pull_request: |   pull_request: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-bandit.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-bandit.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-docs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-linter.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-linter.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-python310.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-python310.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-python37.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-python37.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-python38.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-python38.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-python39.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-python39.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.github/workflows/pr-type-check.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/pr-type-check.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
| @@ -15,7 +16,7 @@ jobs: | |||||||
|       matrix: |       matrix: | ||||||
|         os: [ubuntu-latest] |         os: [ubuntu-latest] | ||||||
|         config: |         config: | ||||||
|           - { python-version: 3.7, tox-env: type-checking} |           # - { python-version: 3.7, tox-env: type-checking} | ||||||
|           - { python-version: 3.8, tox-env: type-checking} |           - { python-version: 3.8, tox-env: type-checking} | ||||||
|           - { python-version: 3.9, tox-env: type-checking} |           - { python-version: 3.9, tox-env: type-checking} | ||||||
|           - { python-version: "3.10", tox-env: type-checking} |           - { python-version: "3.10", tox-env: type-checking} | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.github/workflows/pr-windows.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/pr-windows.yml
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ on: | |||||||
|   pull_request: |   pull_request: | ||||||
|     branches: |     branches: | ||||||
|       - main |       - main | ||||||
|  |       - "*LTS" | ||||||
|     types: [opened, synchronize, reopened, ready_for_review] |     types: [opened, synchronize, reopened, ready_for_review] | ||||||
|  |  | ||||||
| jobs: | jobs: | ||||||
|   | |||||||
| @@ -1,3 +1,9 @@ | |||||||
|  | ## Version 21.12.1 | ||||||
|  |  | ||||||
|  | - [#2349](https://github.com/sanic-org/sanic/pull/2349) Only display MOTD on startup | ||||||
|  | - [#2354](https://github.com/sanic-org/sanic/pull/2354) Ignore name argument in Python 3.7 | ||||||
|  | - [#2355](https://github.com/sanic-org/sanic/pull/2355) Add config.update support for all config values | ||||||
|  |  | ||||||
| ## Version 21.12.0 | ## Version 21.12.0 | ||||||
|  |  | ||||||
| ### Features | ### Features | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| __version__ = "21.12.0" | __version__ = "21.12.1" | ||||||
|   | |||||||
							
								
								
									
										62
									
								
								sanic/app.py
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								sanic/app.py
									
									
									
									
									
								
							| @@ -1004,10 +1004,10 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|         cancelled = False |         cancelled = False | ||||||
|         try: |         try: | ||||||
|             await fut |             await fut | ||||||
|         except Exception as e: |  | ||||||
|             self.error_handler.log(request, e) |  | ||||||
|         except (CancelledError, ConnectionClosed): |         except (CancelledError, ConnectionClosed): | ||||||
|             cancelled = True |             cancelled = True | ||||||
|  |         except Exception as e: | ||||||
|  |             self.error_handler.log(request, e) | ||||||
|         finally: |         finally: | ||||||
|             self.websocket_tasks.remove(fut) |             self.websocket_tasks.remove(fut) | ||||||
|             if cancelled: |             if cancelled: | ||||||
| @@ -1552,10 +1552,19 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|         name: Optional[str] = None, |         name: Optional[str] = None, | ||||||
|         register: bool = True, |         register: bool = True, | ||||||
|     ) -> Task: |     ) -> Task: | ||||||
|         prepped = cls._prep_task(task, app, loop) |         if not isinstance(task, Future): | ||||||
|         task = loop.create_task(prepped, name=name) |             prepped = cls._prep_task(task, app, loop) | ||||||
|  |             if sys.version_info < (3, 8): | ||||||
|  |                 if name: | ||||||
|  |                     error_logger.warning( | ||||||
|  |                         "Cannot set a name for a task when using Python 3.7. " | ||||||
|  |                         "Your task will be created without a name." | ||||||
|  |                     ) | ||||||
|  |                 task = loop.create_task(prepped) | ||||||
|  |             else: | ||||||
|  |                 task = loop.create_task(prepped, name=name) | ||||||
|  |  | ||||||
|         if name and register: |         if name and register and sys.version_info > (3, 7): | ||||||
|             app._task_registry[name] = task |             app._task_registry[name] = task | ||||||
|  |  | ||||||
|         return task |         return task | ||||||
| @@ -1617,10 +1626,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|     def get_task( |     def get_task( | ||||||
|         self, name: str, *, raise_exception: bool = True |         self, name: str, *, raise_exception: bool = True | ||||||
|     ) -> Optional[Task]: |     ) -> Optional[Task]: | ||||||
|         if sys.version_info == (3, 7): |         if sys.version_info < (3, 8): | ||||||
|             raise RuntimeError( |             error_logger.warning( | ||||||
|                 "This feature is only supported on using Python 3.8+." |                 "This feature (get_task) is only supported on using " | ||||||
|  |                 "Python 3.8+." | ||||||
|             ) |             ) | ||||||
|  |             return | ||||||
|         try: |         try: | ||||||
|             return self._task_registry[name] |             return self._task_registry[name] | ||||||
|         except KeyError: |         except KeyError: | ||||||
| @@ -1637,10 +1648,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|         *, |         *, | ||||||
|         raise_exception: bool = True, |         raise_exception: bool = True, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         if sys.version_info == (3, 7): |         if sys.version_info < (3, 8): | ||||||
|             raise RuntimeError( |             error_logger.warning( | ||||||
|                 "This feature is only supported on using Python 3.8+." |                 "This feature (cancel_task) is only supported on using " | ||||||
|  |                 "Python 3.8+." | ||||||
|             ) |             ) | ||||||
|  |             return | ||||||
|         task = self.get_task(name, raise_exception=raise_exception) |         task = self.get_task(name, raise_exception=raise_exception) | ||||||
|         if task and not task.cancelled(): |         if task and not task.cancelled(): | ||||||
|             args: Tuple[str, ...] = () |             args: Tuple[str, ...] = () | ||||||
| @@ -1659,10 +1672,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|                 ... |                 ... | ||||||
|  |  | ||||||
|     def purge_tasks(self): |     def purge_tasks(self): | ||||||
|         if sys.version_info == (3, 7): |         if sys.version_info < (3, 8): | ||||||
|             raise RuntimeError( |             error_logger.warning( | ||||||
|                 "This feature is only supported on using Python 3.8+." |                 "This feature (purge_tasks) is only supported on using " | ||||||
|  |                 "Python 3.8+." | ||||||
|             ) |             ) | ||||||
|  |             return | ||||||
|         for task in self.tasks: |         for task in self.tasks: | ||||||
|             if task.done() or task.cancelled(): |             if task.done() or task.cancelled(): | ||||||
|                 name = task.get_name() |                 name = task.get_name() | ||||||
| @@ -1675,10 +1690,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|     def shutdown_tasks( |     def shutdown_tasks( | ||||||
|         self, timeout: Optional[float] = None, increment: float = 0.1 |         self, timeout: Optional[float] = None, increment: float = 0.1 | ||||||
|     ): |     ): | ||||||
|         if sys.version_info == (3, 7): |         if sys.version_info < (3, 8): | ||||||
|             raise RuntimeError( |             error_logger.warning( | ||||||
|                 "This feature is only supported on using Python 3.8+." |                 "This feature (shutdown_tasks) is only supported on using " | ||||||
|  |                 "Python 3.8+." | ||||||
|             ) |             ) | ||||||
|  |             return | ||||||
|         for task in self.tasks: |         for task in self.tasks: | ||||||
|             task.cancel() |             task.cancel() | ||||||
|  |  | ||||||
| @@ -1692,10 +1709,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def tasks(self): |     def tasks(self): | ||||||
|         if sys.version_info == (3, 7): |         if sys.version_info < (3, 8): | ||||||
|             raise RuntimeError( |             error_logger.warning( | ||||||
|                 "This feature is only supported on using Python 3.8+." |                 "This feature (tasks) is only supported on using " | ||||||
|  |                 "Python 3.8+." | ||||||
|             ) |             ) | ||||||
|  |             return | ||||||
|         return iter(self._task_registry.values()) |         return iter(self._task_registry.values()) | ||||||
|  |  | ||||||
|     # -------------------------------------------------------------------- # |     # -------------------------------------------------------------------- # | ||||||
| @@ -1709,7 +1728,8 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta): | |||||||
|         details: https://asgi.readthedocs.io/en/latest |         details: https://asgi.readthedocs.io/en/latest | ||||||
|         """ |         """ | ||||||
|         self.asgi = True |         self.asgi = True | ||||||
|         self.motd("") |         if scope["type"] == "lifespan": | ||||||
|  |             self.motd("") | ||||||
|         self._asgi_app = await ASGIApp.create(self, scope, receive, send) |         self._asgi_app = await ASGIApp.create(self, scope, receive, send) | ||||||
|         asgi_app = self._asgi_app |         asgi_app = self._asgi_app | ||||||
|         await asgi_app() |         await asgi_app() | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ DEFAULT_CONFIG = { | |||||||
|     "REQUEST_TIMEOUT": 60,  # 60 seconds |     "REQUEST_TIMEOUT": 60,  # 60 seconds | ||||||
|     "RESPONSE_TIMEOUT": 60,  # 60 seconds |     "RESPONSE_TIMEOUT": 60,  # 60 seconds | ||||||
|     "USE_UVLOOP": _default, |     "USE_UVLOOP": _default, | ||||||
|     "WEBSOCKET_MAX_SIZE": 2 ** 20,  # 1 megabyte |     "WEBSOCKET_MAX_SIZE": 2**20,  # 1 megabyte | ||||||
|     "WEBSOCKET_PING_INTERVAL": 20, |     "WEBSOCKET_PING_INTERVAL": 20, | ||||||
|     "WEBSOCKET_PING_TIMEOUT": 20, |     "WEBSOCKET_PING_TIMEOUT": 20, | ||||||
| } | } | ||||||
| @@ -124,22 +124,27 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|             raise AttributeError(f"Config has no '{ke.args[0]}'") |             raise AttributeError(f"Config has no '{ke.args[0]}'") | ||||||
|  |  | ||||||
|     def __setattr__(self, attr, value) -> None: |     def __setattr__(self, attr, value) -> None: | ||||||
|         if attr in self.__class__.__setters__: |  | ||||||
|             try: |  | ||||||
|                 super().__setattr__(attr, value) |  | ||||||
|             except AttributeError: |  | ||||||
|                 ... |  | ||||||
|             else: |  | ||||||
|                 return None |  | ||||||
|         self.update({attr: value}) |         self.update({attr: value}) | ||||||
|  |  | ||||||
|     def __setitem__(self, attr, value) -> None: |     def __setitem__(self, attr, value) -> None: | ||||||
|         self.update({attr: value}) |         self.update({attr: value}) | ||||||
|  |  | ||||||
|     def update(self, *other, **kwargs) -> None: |     def update(self, *other, **kwargs) -> None: | ||||||
|         other_mapping = {k: v for item in other for k, v in dict(item).items()} |         kwargs.update({k: v for item in other for k, v in dict(item).items()}) | ||||||
|         super().update(*other, **kwargs) |         setters: Dict[str, Any] = { | ||||||
|         for attr, value in {**other_mapping, **kwargs}.items(): |             k: kwargs.pop(k) | ||||||
|  |             for k in {**kwargs}.keys() | ||||||
|  |             if k in self.__class__.__setters__ | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for key, value in setters.items(): | ||||||
|  |             try: | ||||||
|  |                 super().__setattr__(key, value) | ||||||
|  |             except AttributeError: | ||||||
|  |                 ... | ||||||
|  |  | ||||||
|  |         super().update(**kwargs) | ||||||
|  |         for attr, value in {**setters, **kwargs}.items(): | ||||||
|             self._post_set(attr, value) |             self._post_set(attr, value) | ||||||
|  |  | ||||||
|     def _post_set(self, attr, value) -> None: |     def _post_set(self, attr, value) -> None: | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ Options = Dict[str, Union[int, str]]  # key=value fields in various headers | |||||||
| OptionsIterable = Iterable[Tuple[str, str]]  # May contain duplicate keys | OptionsIterable = Iterable[Tuple[str, str]]  # May contain duplicate keys | ||||||
|  |  | ||||||
| _token, _quoted = r"([\w!#$%&'*+\-.^_`|~]+)", r'"([^"]*)"' | _token, _quoted = r"([\w!#$%&'*+\-.^_`|~]+)", r'"([^"]*)"' | ||||||
| _param = re.compile(fr";\s*{_token}=(?:{_token}|{_quoted})", re.ASCII) | _param = re.compile(rf";\s*{_token}=(?:{_token}|{_quoted})", re.ASCII) | ||||||
| _firefox_quote_escape = re.compile(r'\\"(?!; |\s*$)') | _firefox_quote_escape = re.compile(r'\\"(?!; |\s*$)') | ||||||
| _ipv6 = "(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}" | _ipv6 = "(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}" | ||||||
| _ipv6_re = re.compile(_ipv6) | _ipv6_re = re.compile(_ipv6) | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ from os import environ | |||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from tempfile import TemporaryDirectory | from tempfile import TemporaryDirectory | ||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from unittest.mock import Mock | from unittest.mock import Mock, call | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| @@ -385,5 +385,24 @@ def test_config_set_methods(app, monkeypatch): | |||||||
|     post_set.assert_called_once_with("FOO", 5) |     post_set.assert_called_once_with("FOO", 5) | ||||||
|     post_set.reset_mock() |     post_set.reset_mock() | ||||||
|  |  | ||||||
|     app.config.update_config({"FOO": 6}) |     app.config.update({"FOO": 6}, {"BAR": 7}) | ||||||
|     post_set.assert_called_once_with("FOO", 6) |     post_set.assert_has_calls( | ||||||
|  |         calls=[ | ||||||
|  |             call("FOO", 6), | ||||||
|  |             call("BAR", 7), | ||||||
|  |         ] | ||||||
|  |     ) | ||||||
|  |     post_set.reset_mock() | ||||||
|  |  | ||||||
|  |     app.config.update({"FOO": 8}, BAR=9) | ||||||
|  |     post_set.assert_has_calls( | ||||||
|  |         calls=[ | ||||||
|  |             call("FOO", 8), | ||||||
|  |             call("BAR", 9), | ||||||
|  |         ], | ||||||
|  |         any_order=True, | ||||||
|  |     ) | ||||||
|  |     post_set.reset_mock() | ||||||
|  |  | ||||||
|  |     app.config.update_config({"FOO": 10}) | ||||||
|  |     post_set.assert_called_once_with("FOO", 10) | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import asyncio | |||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from threading import Event | from threading import Event | ||||||
|  | from unittest.mock import Mock | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| @@ -77,6 +78,25 @@ def test_create_named_task(app): | |||||||
|     app.run() |     app.run() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_named_task_called(app): | ||||||
|  |     e = Event() | ||||||
|  |  | ||||||
|  |     async def coro(): | ||||||
|  |         e.set() | ||||||
|  |  | ||||||
|  |     @app.route("/") | ||||||
|  |     async def isset(request): | ||||||
|  |         await asyncio.sleep(0.05) | ||||||
|  |         return text(str(e.is_set())) | ||||||
|  |  | ||||||
|  |     @app.before_server_start | ||||||
|  |     async def setup(app, _): | ||||||
|  |         app.add_task(coro, name="dummy_task") | ||||||
|  |  | ||||||
|  |     request, response = app.test_client.get("/") | ||||||
|  |     assert response.body == b"True" | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.mark.skipif(sys.version_info < (3, 8), reason="Not supported in 3.7") | @pytest.mark.skipif(sys.version_info < (3, 8), reason="Not supported in 3.7") | ||||||
| def test_create_named_task_fails_outside_app(app): | def test_create_named_task_fails_outside_app(app): | ||||||
|     async def dummy(): |     async def dummy(): | ||||||
|   | |||||||
| @@ -334,6 +334,22 @@ def test_config_fallback_before_and_after_startup(app): | |||||||
|     assert response.content_type == "text/plain; charset=utf-8" |     assert response.content_type == "text/plain; charset=utf-8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_config_fallback_using_update_dict(app): | ||||||
|  |     app.config.update({"FALLBACK_ERROR_FORMAT": "text"}) | ||||||
|  |  | ||||||
|  |     _, response = app.test_client.get("/error") | ||||||
|  |     assert response.status == 500 | ||||||
|  |     assert response.content_type == "text/plain; charset=utf-8" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_config_fallback_using_update_kwarg(app): | ||||||
|  |     app.config.update(FALLBACK_ERROR_FORMAT="text") | ||||||
|  |  | ||||||
|  |     _, response = app.test_client.get("/error") | ||||||
|  |     assert response.status == 500 | ||||||
|  |     assert response.content_type == "text/plain; charset=utf-8" | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_config_fallback_bad_value(app): | def test_config_fallback_bad_value(app): | ||||||
|     message = "Unknown format: fake" |     message = "Unknown format: fake" | ||||||
|     with pytest.raises(SanicException, match=message): |     with pytest.raises(SanicException, match=message): | ||||||
|   | |||||||
| @@ -62,19 +62,15 @@ def test_streaming_body_requests(app): | |||||||
|  |  | ||||||
|     data = ["hello", "world"] |     data = ["hello", "world"] | ||||||
|  |  | ||||||
|     class Data(AsyncByteStream): |  | ||||||
|         def __init__(self, data): |  | ||||||
|             self.data = data |  | ||||||
|  |  | ||||||
|         async def __aiter__(self): |  | ||||||
|             for value in self.data: |  | ||||||
|                 yield value.encode("utf-8") |  | ||||||
|  |  | ||||||
|     client = ReusableClient(app, port=1234) |     client = ReusableClient(app, port=1234) | ||||||
|  |  | ||||||
|  |     async def stream(data): | ||||||
|  |         for value in data: | ||||||
|  |             yield value.encode("utf-8") | ||||||
|  |  | ||||||
|     with client: |     with client: | ||||||
|         _, response1 = client.post("/", data=Data(data)) |         _, response1 = client.post("/", data=stream(data)) | ||||||
|         _, response2 = client.post("/", data=Data(data)) |         _, response2 = client.post("/", data=stream(data)) | ||||||
|  |  | ||||||
|     assert response1.status == response2.status == 200 |     assert response1.status == response2.status == 200 | ||||||
|     assert response1.json["data"] == response2.json["data"] == data |     assert response1.json["data"] == response2.json["data"] == data | ||||||
|   | |||||||
| @@ -4,8 +4,8 @@ from unittest.mock import Mock, patch | |||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| from sanic.server import loop |  | ||||||
| from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED | from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED | ||||||
|  | from sanic.server import loop | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.mark.skipif( | @pytest.mark.skipif( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user