Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App registry #1979

Merged
merged 7 commits into from
Dec 28, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/sanic/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,21 @@ Open the address `http://0.0.0.0:8000 <http://0.0.0.0:8000>`_ in your web browse
the message *Hello world!*.

You now have a working Sanic server!

5. Application registry
vltr marked this conversation as resolved.
Show resolved Hide resolved
-----------------------

When you instantiate a Sanic instance, that can be retrieved at a later time from the Sanic app registry. This can be useful, for example, if you need to access your Sanic instance from a location where it is not otherwise accessible.

.. code-block:: python

# ./path/to/server.py
from sanic import Sanic

app = Sanic("my_awesome_server")

# ./path/to/somewhere_else.py
from sanic import Sanic

app = Sanic.get_app("my_awesome_server")

42 changes: 33 additions & 9 deletions sanic/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from asyncio import CancelledError, Protocol, ensure_future, get_event_loop
from collections import defaultdict, deque
from functools import partial
from inspect import getmodulename, isawaitable, signature, stack
from inspect import isawaitable, signature
from socket import socket
from ssl import Purpose, SSLContext, create_default_context
from traceback import format_exc
Expand Down Expand Up @@ -38,6 +38,9 @@


class Sanic:
_app_registry: Dict[str, "Sanic"] = {}
test_mode = False

def __init__(
self,
name=None,
Expand All @@ -52,15 +55,10 @@ def __init__(

# Get name from previous stack frame
if name is None:
warnings.warn(
"Sanic(name=None) is deprecated and None value support "
"for `name` will be removed in the next release. "
raise SanicException(
"Sanic instance cannot be unnamed. "
"Please use Sanic(name='your_application_name') instead.",
DeprecationWarning,
stacklevel=2,
)
frame_records = stack()[1]
name = getmodulename(frame_records[1])

# logging
if configure_logging:
Expand Down Expand Up @@ -90,7 +88,8 @@ def __init__(
self.named_response_middleware = {}
# Register alternative method names
self.go_fast = self.run
self.test_mode = False

self.__class__.register_app(self)

@property
def loop(self):
Expand Down Expand Up @@ -1453,9 +1452,34 @@ async def __call__(self, scope, receive, send):
# -------------------------------------------------------------------- #
# Configuration
# -------------------------------------------------------------------- #

def update_config(self, config: Union[bytes, str, dict, Any]):
"""Update app.config.

Please refer to config.py::Config.update_config for documentation."""

self.config.update_config(config)

# -------------------------------------------------------------------- #
# Class methods
# -------------------------------------------------------------------- #

@classmethod
def register_app(cls, app: "Sanic") -> None:
sjsadowski marked this conversation as resolved.
Show resolved Hide resolved
"""Register a Sanic instance"""
if not isinstance(app, cls):
raise SanicException("Registered app must be an instance of Sanic")

name = app.name
if name in cls._app_registry and not cls.test_mode:
raise SanicException(f'Sanic app name "{name}" already in use.')

cls._app_registry[name] = app

@classmethod
def get_app(cls, name: str) -> "Sanic":
"""Retrieve an instantiated Sanic instance"""
try:
return cls._app_registry[name]
except KeyError:
raise SanicException(f'Sanic app name "{name}" not found.')
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@


random.seed("Pack my box with five dozen liquor jugs.")
Sanic.test_mode = True

if sys.platform in ["win32", "cygwin"]:
collect_ignore = ["test_worker.py"]
Expand Down
30 changes: 20 additions & 10 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def handler(request):


def test_app_name_required():
with pytest.deprecated_call():
with pytest.raises(SanicException):
Sanic()


Expand All @@ -274,14 +274,24 @@ def handler(request):
assert response.status == 200


# @pytest.mark.asyncio
# async def test_app_has_test_mode_async():
# app = Sanic("test")
def test_app_registry():
instance = Sanic("test")
assert Sanic._app_registry["test"] is instance

# @app.get("/")
# async def handler(request):
# assert request.app.test_mode
# return text("test")

# _, response = await app.asgi_client.get("/")
# assert response.status == 200
def test_app_registry_wrong_type():
with pytest.raises(SanicException):
Sanic.register_app(1)


def test_app_registry_name_reuse():
Sanic("test")
Sanic.test_mode = False
with pytest.raises(SanicException):
Sanic("test")
Sanic.test_mode = True


def test_app_registry_retrieval():
instance = Sanic("test")
assert Sanic.get_app("test") is instance