sanic
sanic copied to clipboard
RFC: Blueprint pre-registration
Background
I see a lot of developers getting stuck because of import ordering. This usually happens because of a mistake in trying to import a module with reference to the application before the application has actually been created.
While I think there are some helpful patterns to avoid this (factories, late imports, etc), it might also be helpful to provide a little leeway. This PR is meant to add the ability for a Blueprint to declare its application up front.
Then, as long as that module has been imported somewhere, it will automatically be registered when the application starts up.
What it would look like
# some.other.module
bp = Blueprint("SomeBP")
bp.pre_register("SomeAppName")
# anywhere
import some.other.module
This could be simplified, and made to look similar to the Sanic.get_app
pattern. In this example, we call Sanic.lazy
, which will return a preregistered Blueprint object.
# some.other.module
from sanic import Sanic
app = Sanic.lazy("SomeApp")
@app.before_server_start
async def before_server_start(app: Sanic, _):
...
@app.get("/foo")
async def get(_):
...
# server
import some.other.module
app = Sanic("SomeApp")
Taking it further
We also can borrow the pattern from Sanic.get_app()
and use the only application instance (if there is only one). This would allow:
lazy_app = Sanic.lazy()
If this is the case, then it would be natural--and highly likely--that someone may attempt the run the application from that lazily created Blueprint. We could allow that by simply creating a basic application instance for them at startup.
$ sanic path.to.server:lazy_app
I am myself still undecided on this last bit. It seems logical and convenient, but I have not fully thought thru the issue.
Potential PR implementation
See PR #2339
Another thought...
When Sanic.lazy
is used without a name, the implied blueprint name should use the current module's name:
name = inspect.getmodulename(inspect.stack()[1].filename)