bocadilloproject / bocadillo Goto Github PK
View Code? Open in Web Editor NEW(UNMAINTAINED) Fast, scalable and real-time capable web APIs for everyone
Home Page: https://bocadilloproject.github.io
License: MIT License
(UNMAINTAINED) Fast, scalable and real-time capable web APIs for everyone
Home Page: https://bocadilloproject.github.io
License: MIT License
Currently, Bocadillo's res.media
only allows to send JSON, but there are more serialization formats out there, such as XML, YAML or MessagePack.
It should be possible to add more media handlers to allow extension while providing built-in support for most common formats.
This would require a refactoring of how res.media
works (see bocadillo/response.py
). Still thinking about how this would materialize API-wise.
Is your feature request related to a problem? Please describe.
For now, the default HTTPError
handler returns an HTML response. Whether this is a sensible default for most applications is debatable, for there is also no built-in handler that returns JSON, which means we need to write a JSON handler ourselves.
Describe the solution you'd like
Provide a handler (e.g. to_json
) to be used like so:
from bocadillo.error_handlers import to_json
from bocadillo.exceptions import HTTPError
api.add_error_handler(HTTPError, to_json)
Also, refactor the current code so that it uses the to_html
handler by default.
Describe alternatives you've considered
Should there be an option on the API
as a shortcut for this?
Additional context
I realised this while working on #49.
All issues for v0.7 have been sorted out and the CHANGELOG is growing to a decent size — a few nice new features, some bug fixes and important changes. Let's release v0.7.
Since the beginning, Bocadillo has been async-first in that it also supports regular Python functions everywhere an async function can be used.
This led to supporting regular functions in views, hooks, middleware callbacks, server events, etc.
Yet, I'm beginning to wonder whether this was a sane idea. Regular functions are supported solely for syntax simplicity, and this caused increased code complexity and, I suspect, decreased performance as numerous checks and wrappings are required to support both kinds of functions.
So if we go async-only, I believe it would just be a matter of educating users to use the async
keyword, right? Any thoughts?
Users should be able to easily validate JSON contained in the body of incoming requests (e.g. from POST, PUT or PATCH requests).
Two popular ways of doing this in Python seem to be jsonschema and marshmallow.
We could add a concept of "JSON validation backend" — which would rely on jsonschema
or marshmallow
being installed (Bocadillo wouldn't ship with them by default). We'd be able to select which one to use when creating the API
object, e.g. through a json_validation_backend
option.
JSON validation could materialize using a decorator on a view, e.g. @api.validate_json(...)
.
For jsonschema
, we'd provide the schema dictionary:
schema = {
'type' : 'object',
'properties' : {
'price' : {'type' : 'number'},
'name' : {'type' : 'string'},
},
}
@api.validate_json(schema)
async def create_products(req, res):
pass
class ProductsView:
@api.validate_json(schema)
async def post(req, res):
pass
For marshmallow
, we'd pass a Schema
class:
from marshmallow import Schema, fields
class ProductSchema(Schema):
price = fields.Integer()
name = fields.Str()
@api.validate_json(ProductSchema)
async def create_products(req, res):
pass
A possible implementation for validate_json()
would be to wrap the view and consume req.json()
. (From first investigation, it is possible to call await req.json()
multiple times, so consuming JSON should not cause issues if the view function also needs to access it.)
As it is now, RoutingMiddleware only allows synchronous callbacks (before_dispatch()
, after_dispatch()
) to be used.
This won't allow developers to call asynchronous code is said callbacks, such as perform an async HTTP request or even just consume the request body.
We need to add support for async callbacks too.
Although I don't use them very often, I think having cookie-based sessions in Bocadillo is basic enough that it should be built-in.
Starlette already provides a fully-featured SessionMiddleware, so the implementation shouldn't be too hard!
Is your feature request related to a problem? Please describe.
SSE is a feature that allows a server to send "events" to the client. It is more light-weight than WebSocket, especially when taking into account deployment considerations, and can be enough when two-way communication is not required.
Describe the solution you'd like
Features:
Content-Type: text/event-stream
, Cache-Control: no-cache
, Connection: keep-alive
.For simplicity:
Last-Event-ID
header is not supported.retry
configured cannot be configured.Example that sends server events from a Redis channel using aio-redis:
from aioredis import create_redis
from bocadillo import App, server_event
app = App()
REDIS_URL = "redis://localhost"
pub = None
@app.on("startup")
async def on_startup():
global pub
pub = await create_redis("redis://localhost")
app.on("shutdown", pub.close)
@app.route("/events")
class Events:
async def get(self, req, res):
sub = await create_redis(REDIS_URL)
channel = await sub.subscribe("events")
@res.event_stream
async def send_events():
while (await channel.wait_message()):
message = await channel.get(encoding="utf-8")
yield server_event(message, name="event")
@res.background
async def cleanup():
sub.close()
async def post(self, req, res):
await pub.publish_json("events", await req.json())
Describe alternatives you've considered
A context manager-based solution may have looked cleaner, but after trying it out it is not feasible because we'd need to return a generator anyway, and that generator could only be built after the context exits (i.e. when all events have been created, which is a non-sense).
Additional context
As a developer, I'd find it very useful if there was a way to intercept the routing mechanism and execute code before or after a specific view is executed.
This is different from [RoutingMiddleware] because such hooks would be applied on a per-route basis, e.g.:
def do_something(req, res, view, params):
pass
@api.before(do_something)
@api.route('/')
async def index(req, res):
pass
@api.route('/')
class IndexView:
@api.before(do_something)
async def get(self, req, res):
pass
In order to have a basic level of reusability, it should be possible to pass extra args or kwargs to hooks, e.g.:
def check_for_header(req, res, view, params, header):
assert header in req.headers
@api.before(check_for_header, header='x-my-header')
@api.route('/')
async def index(req, res):
pass
This is a (non-critical) bug remaining from the implementation of static assets handling in v0.3:
On the API object, if the directory identified by static_dir
does not exist, WhiteNoise raises a warning. We can see this warning in Travis builds:
/home/travis/virtualenv/python3.7.1/lib/python3.7/site-packages/whitenoise/base.py:104: UserWarning: No directory at: static/
warnings.warn(u'No directory at: {}'.format(root))
A good option probably, which involves modifying the static()
function in bocadillo/static.py
, would be to not call add_files(directory)
at all if the directory
does not exist. This is a non-intrusive approach for the developer.
The change should also be documented in Static files.
Add tests to check for the following:
before_dispatch()
, after_dispatch()
, @api.before()
and @api.after()
should not run if the method is not allowedbefore_dispatch()
and after_dispatch()
should be handled by registered error handlers (test_error_handling.py
)async
See #18 for background
Complementary to #4 , we should write how-to guides explaining how to configure and use an async database client in order to perform database queries asynchronously in a Bocadillo app.
Note: this is a temporary but reasonable solution waiting for a sane async ORM to come around, like Tortoise.
Available async clients for popular databases are:
PyMySQL
sqlite3
If possible and relevant, we should also provide snippets that abstract away common logic such as connecting to the database or handling cursors, so that app-side code looks something like:
from asyncdb import query # <- snippet
async def get_products(req, res):
results = await query('SELECT id, name, price FROM products;')
# do something with `results`
Guidelines on contributing documentation have been added to CONTRIBUTING.md. Feel free to reach out to discuss any ideas!
Edit (Dec 19th): it actually seems like a good idea to make databases more approachable and suggest an integration with Tortoise ORM in the how-to guide.
I've realised that Starlette provides a built-in WSGI -> ASGI converter in the form of the undocumented WSGIResponder
(see code), which seems to behave exactly like asgiref's WsgiToAsgi
utility does.
We should ditch asgiref
in favor of using Starlette's helper, hence removing an entire dependency (plus asgiref comes with a lot of other stuff we were not using at all).
Is your feature request related to a problem? Please describe.
The implementation for middleware is currently brittle. There's the public RoutingMiddleware
class and the unofficial CommonMiddleware
; responsibilities between the two are unclear and so is the flow of how requests are passed to middleware classes.
I think this is because we apply middleware at the same level Starlette does — at the ASGI level.
Describe the solution you'd like
(req: Request) -> Response
. This is inspired by how Django deals with middleware.dispatch()
function on init (which initially corresponds to the API.dispatch()
function, which is actually middleware-like), and need to call it inside their __call__()
method.before_dispatch(req)
before the call to dispatch()
and after_dispatch(req, res)
after the call to dispatch()
. Sounds logical, right?We use Chain of responsibility when wrapping middleware classes around API.dispatch()
: each middleware is given the request, processes it, gets a response from the middleware beneath (down to API.dispatch()
), then processes the request and response and returns the response.
Describe alternatives you've considered
/
Additional context
/
Am yet to get optional query params to work using Bocadillo and here is what i'm working with from the Sports Reference Python API -> https://sportsreference.readthedocs.io/en/v0.3.1/
Here is my code:
@nba.route('/schedule')
def get_schedule(req, res):
param1 = req.query_params['team']
schedule = Schedule(param1)
res.media = [{
'date': game.date,
'game': game.game,
'opponent_abbr': game.opponent_abbr,
'result': game.result,
'boxscore': game.boxscore_index
} for game in schedule]
pass
local address i use for above is http://localhost:8000/league/nba/schedule?team=LAC and works
...i would like to use the year param, but not with all my requests. When it's not provided it defaults to the this year's season and no matter what i try i get this error...
KeyError: 'year'
Specs
Is your feature request related to a problem? Please describe.
Bocadillo's current logo is the Twitter emoji for a kebab sandwich. It's fine but not great, and I think we can do better.
Describe the solution you'd like
We need to get Bocadillo a proper logo — and perhaps graphic charter, with official colors and all that 😄
The logo should:
A list of useful adjectives: crispy, friendly, lightweight, powerful, simple, progressive.
It's 100% possible to start from Bocadillo meaning 'sandwich' in Spanish. That's why the current logo is that of a pitta sandwich. But it shouldn't be gross in any case (no burgers dripping in sauce), nor convey a sense of cumbersomeness.
Describe alternatives you've considered
I tried to create a logo myself some time ago, this is what came out:
Additional context
Current logo:
Is your feature request related to a problem? Please describe.
It is possible to return a Response
object in an HTTP middleware's hooks to halt the processing of the request and return immediately, however this is not documented on the docs site.
Describe the solution you'd like
Document this behavior.
Is your feature request related to a problem? Please describe.
It turns out we do support chunked responses by setting the raw Transfer-Encoding
header in the response, but that is a bit too low-level.
Describe the solution you'd like
chunked
attribute to the response which, when True
, should set the Transfer-Encoding
header to chunked
.Additional context
See how uvicorn uses the Transfer-Encoding header.
Is your feature request related to a problem? Please describe.
All HTTP methods (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) are currently supported, which is an implicit behaviour and forces the developer to secure the endpoint by manually passing methods=['get']
, which is kind of absurd.
Describe the solution you'd like
Only GET
should be accepted by default. The docs on specifying HTTP methods should be updated.
Describe alternatives you've considered
The current situation, which is not satisfying.
Additional context
I was re-reading the docs on specifying HTTP methods when this struck me.
Expected behavior
We should be able to access the url
on the WebSocket
object, [as described in the docs].
Actual behavior
AttributeError: 'WebSocket' object has no attribute 'url'
To Reproduce
Create a WebSocket route and try to access ws.url
.
Specs
Possible solutions
This is because the url
property on the underlying Starlette WebSocket object has not been exposed. 🤦♂️ It should be fixed by adding:
@property
def url(self):
return self._websocket.url
on our WebSocket
class. We should also add some tests.
There should be a way for people to provide third-party extensions to Bocadillo. This is a prerequisite to building an ecosystem around Bocadillo.
One typical use case would be an auth
extension which would provide login/logout routes, perhaps a User model and a permission system.
Django has apps, Flask has blueprints, Falcon has middleware… What should Bocadillo have?
We already have RoutingMiddleware
to allow intercepting the routing mechanism. We could provide official support for any kind of middleware (that would wrap the API
object itself), but I don't think this is enough. Flask blueprints and Django apps also allow to ship views, routes, models and more. I like this way of grouping instances of a framework's concept into a single, coherent "bundle".
If you have any thoughts on this, feel free to comment!
Is your feature request related to a problem? Please describe.
For now, HTTPError
only allows to specify the HTTP status
, but we cannot provide any detail about what went wrong.
Describe the solution you'd like
Allow to provide a detail
attribute to HTTPError
, which should be correctly rendered by each of the built-in error handlers.
Is your feature request related to a problem? Please describe.
Waiting for tutorials to come around, we still really need a way to onboard new users. Otherwise Bocadillo is just unusable for most people.
Describe the solution you'd like
Let's write a quickstart guide with a hello world example and a feature tour (similar to what Falcon has).
Additional context
Related to #25
Provide a short guide about how to test Bocadillo apps, including description and usage for:
client
fixture)Is your feature request related to a problem? Please describe.
There should be an easier way to retrieve the current Bocadillo than writing an inline Python script or entering the shell (as described in the Installation instructions).
Describe the solution you'd like
Let's get boca
a version command/option:
boca version
boca --version
boca -v
boca -V
All these should show the currently installed, e.g. "0.6.1"
.
A lot of web frameworks (except Django) give you complete control over how to deal with the database — most likely you'll resort to SQLAlchemy and Alembic.
But setting up and using a database is one of the most common tasks web developers have to deal with. I've always needed one at some point for every single web project I've worked on.
I've used SQLAlchemy/Alembic, but it was a pain because I had to be extremely careful about integrating them in my project and workflow. Really not straight-forward.
So the idea is to have an officially supported ORM into Bocadillo (although the ORM would most likely be 3rd-party), but to make it optional. We could provide an extension (in the form of a setup.py extra) that would allow to install any dependencies as pip install bocadillo[db]
. See this StackOverflow question for more info about extras.
One issue, though, is that most ORMs out there are synchronous, whereas one of the core promises of Bocadillo is the ability to go fully async. Plus, database queries are one area where asynchronous I/O really helps with performance and multi-request handling.
That said, there is hope in the name of Tortoise. It is an async ORM supporting the three major DB backends (sqlite, mysql, postgres). However, it is still in alpha (pre-v1) and most likely won't be tackling migrations before a few months. And I feel like migration management is a key part of what Bocadillo should offer.
I already began working on this on feature/db, experimenting with Orator ORM (a synchronous ORM) to get a feel for how databases could be optionally incorporated within a Bocadillo app. I'm not fond of the result because it seems to expose Orator too much. I even had issues with the DB configuration because of how Orator works.
I'm raising the issue because I'm still not sure which path to go. If you have ideas or suggestions on this topic (like recommendations for ORMs w/ async), feel free to comment! 🎉
Is your feature request related to a problem? Please describe.
Before the rewrite of the middleware system, add_middleware
worked with ASGI middleware seamlessly. Now it doesn't work anymore.
Describe the solution you'd like
Implement an add_asgi_middleware
to support that.
Describe alternatives you've considered
None
Additional context
There aren't many ASGI middlewares out there, but those that exist are important and I guess nobody would want to write that code twice instead of just using a lib. Also ASGI is gaining popularity, maybe more middlewares will pop up.
Is your feature request related to a problem? Please describe.
If a Response
object is returned by the before_dispatch
hook of an HTTP middleware, it is used without further processing. However, it is not clear how the Response
should be built in the first place.
Describe the solution you'd like
Refactor the middleware code so that the current Response
is passed, with a signature similar to after_dispatch
and HTTP views: def before_dispatch(self, req, res)
.
Expected behavior
Views should always support the HEAD
method by mapping it to the GET
method, when supported.
Actual behavior
If the function-based view does not have HEAD
in its methods or the class-based view does not define .head()
, the HEAD
method is interpreted as unsupported and a 405
error is returned.
To Reproduce
import bocadillo
api = bocadillo.API()
@api.route('/', methods=['get'])
async def index(req, res):
pass
response = api.client.head('/')
assert response.status_code == 200
Screenshots/Traceback
> assert response.status_code == 200
E assert 405 == 200
E + where 405 = <Response [405]>.status_code
Specs
Additional context
HEAD
requests are used to obtain meta info about a resource without transferring the whole body, and are aimed at being identical to GET
but without a body (source: w3.org).
Web servers automatically strip down the body of HEAD
responses to comply with the HTTP specs. So handling a HEAD
request as GET
(i.e. returning a body) should be fine. Plus, some systems rely on HEAD
to check for existence of the resource (e.g. web crawlers), so we should always be supporting HEAD
even if not specified (source: a note in the Django docs).
Related to #4
I really like how Django allows you to manage databases through its management command system. Ideally, I would like databases to be managed through the Bocadillo CLI, with commands such as:
# Initialize the Bocadillo app for databases, creating files or asking for information as necessary
boca init:db [<backend: (sqlite|postgres|mysql)>]
# Apply all unapplied migrations
boca migrate
# Apply a specific migration (perhaps an older one, meaning rolling back)
boca migrate <migration_number>
# Generate a migration script
boca migrations:create [<name>] [--table <table>]
# See applied/unapplied migrations
boca migrations:show
The Orator ORM provides similar management commands, so perhaps it could be an inspiration.
Is your feature request related to a problem? Please describe.
There is currently no way of processing the request as a stream or sending a response as a stream. Yet, this is useful when either the request's or the response's body is too large to afford loading it in full in memory.
Note: this is not the same as chunked requests or responses, which is determined by the Transfer-Encoding
header.
Describe the solution you'd like
We should be able to read the request body as a stream or send a response as a stream.
Describe alternatives you've considered
req
object, i.e. async for chunk in req
.from bocadillo import API
api = API()
@api.route("/")
async def index(req, res):
data = ""
async for chunk in req:
data += chunk
res.text = data
yield
chunks of the response. The generator could be registered by decorating it, e.g. @res.stream
.from bocadillo import API
from asyncio import sleep
api = API()
@api.route("/stream/{word}")
async def stream_word(req, res, word):
@res.stream
async def streamed():
async for chunk in req:
await sleep(0.1)
yield chunk
# Use other attributes on `res`
res.headers["x-foo"] = "foo"
@res.background
async def do_more():
pass
Describe alternatives you've considered
I thought about providing an @stream
decorator for view handlers themselves, for example:
from asyncio import sleep
from bocadillo import API, stream
async def streamed(word: str):
for c in word:
await sleep(0.1)
yield c.encode()
# Function-based
@api.route("/stream/{word}")
@stream
async def stream_word(req, res, word):
yield from streamed(word)
But that would not have allowed to use the response object at all. This is because the generator is called by Starlette while the response is being sent, and the Bocadillo Response
is pretty much a Starlette response builder which does its work before the response is even created.
Additional context
Useful Starlette features: request.stream(), StreamingResponse
Is your feature request related to a problem? Please describe.
All views, whether they are function-based or class-based, have a natural name attached to them (the name of the function or the class). Yet, we have to manually specify a name
for the route, which is kind of counter-intuitive and a burden.
Describe the solution you'd like
All routes should have a name
, that is either based on the function's or class' name (with a well-documented naming convention) or an explicit name given in @api.route()
.
This would have the effect of simplifying how routes are stored: we'd have a single dict of routes instead of two (named vs unnamed).
Describe alternatives you've considered
/
Additional context
Got this idea while re-reading the "routes and URL design" docs.
As suggested by @alin23 in #18, consider setting up Black to save everyone's time on style and formatting.
Possible implementation using pre-commit:
repos:
- repo: https://github.com/ambv/black
rev: 18.9b0
hooks:
- id: black
language_version: python3.7
- repo: https://github.com/asottile/seed-isort-config
rev: v1.2.0
hooks:
- id: seed-isort-config
args: [--exclude=tests/.+\.py]
- repo: https://github.com/pre-commit/mirrors-isort
rev: v4.3.4
hooks:
- id: isort
- repo: local
hooks:
- id: pylint
name: pylint
stages: [push]
entry: pylint
language: system
types: [python]
- repo: local
hooks:
- id: pytest
name: pytest
stages: [push]
entry: pytest
language: system
pass_filenames: false
types: [python]
This does the following:
- On commit
- formats code
- sorts imports
- On push
- analyzes code with pylint
runs tests using pytest
More and more backend services deal with JSON data (e.g. REST APIs). Bocadillo should make it clear how to access, modify and send JSON data without needing the developer to navigate the API reference.
That's why I think we should create a new topic guide: "Working with JSON data".
Is your feature request related to a problem? Please describe.
Anytime we manipulate inbound JSON data, we need to make sure it is not malformed. It's a burden and should be automated.
Describe the solution you'd like
When calling await req.json()
, a 400 Bad Request
error response should be returned if the body could not be parsed as JSON.
Describe alternatives you've considered
None.
Additional context
None.
Is your feature request related to a problem? Please describe.
Currently there isn't an easy way to initialize libs that need a running event loop before the server starts.
Describe the solution you'd like
An async function should be allowed to be registered as a startup event and have it run before the server starts. Same for shutdown.
Describe alternatives you've considered
Starlette already has startup and shutdown events, maybe we can use those.
Additional context
Related to #16
Subj.
Is your feature request related to a problem? Please describe.
Security is an important topic, and especially as Bocadillo is a young framework, we need to address it.
Describe the solution you'd like
Add a "Security" topic guide discussing common gotchas of web security (MITM, XSS, CSRF, SQL injection…) and how to protect against those.
Additional context
Motivated by #12
Is your feature request related to a problem? Please describe.
WebSockets enable a whole set of real-time web applications, which look like great candidates for async web servers. It should be as easy to build a backend websocket server than it is to create a websocket client in the frontend world.
Describe the solution you'd like
Implement a friendly, async/await-based interface for building websocket views, which would allow to write something like:
import json
from bocadillo import API, WebSocket
api = API()
@api.websocket_route("/chat", value_type="json")
async def chat(ws: WebSocket):
async with ws:
# The connection has been accepted.
async for message in ws:
await ws.send({"type": "echo", "message": message})
# Connection closes when leaving this block.
# The client has disconnected normally. Perform extra cleanup if needed.
async for
syntax should just be a shortcut for a while True
loop that repeatedly calls receive_<value_type>()
on the WebSocket object.async with
syntax allows to automatically accept()
and close()
the WebSocket.
Disconnect
exceptions with normal close codes (1000 or 1001).caught_close_codes: Tuple[int]
argument.value_type=X
is a shortcut for receive_type=X, send_type=X
. Possible values are ["text", "json", "bytes", "event"]
.Note: with the syntax above, hooks do not have to be ported to WebSockets. The developer can perform any operation before/after the context enters/exits.
For the implementation, this feature should mostly be a wrapper around Starlette WebSockets.
Describe alternatives you've considered
Additional context
Came to this conclusion while thinking about example projects for a tutorial. :)
Is your feature request related to a problem? Please describe.
Most likely, as Bocadillo applications grow in size, the need for grouping things together in order to deal with smaller components and keep things manageable will arise.
Describe the solution you'd like
Django has applications, Flask and Sanic have blueprints. I propose Bocadillo get recipes. Recipes are basically a port of Flask's concept of blueprints. You can group routes together and use all the features already exposes on the API
object (such as templates, routes, hooks, middleware, etc.).
Describe alternatives you've considered
#11 suggests that we add an extension mechanism, but I've noticed that the related PR (#19 ) grows in size as I'm trying to squeeze 2 features into one.
Additional context
I was glazing over the docs for Sanic and thought blueprints actually make a lot of sense and provide another way to build third-party (or built-in) extensions.
Is your feature request related to a problem? Please describe.
Pydoc-Markdown has been added to the repo, we can now start writing API reference documentation. Ideally all public classes, functions and constants in Bocadillo should appear in the API reference. This issue is to describe how to write such API reference docs.
Describe the solution you'd like
First, install the repo as described in the CONTRIBUTING guide (including the Documentation section). Then, run the docs server (npm start
).
For each module inside the bocadillo
package, we need to:
generate
section of pydocmd.yml
with an entry like:# pydocmd.yml
generate:
- <module_name.md>:
- bocadillo.<module_name>+
Open said module and clean up the docstrings (most of which are written in NumPy style, which is fairly close to Pydoc-Markdown's format) by:
# Header
format (no ----
underline)parameter : type1[, optional]
with parameter (type1):
:
in special sections (like Parameters
, Returns
or Attributes
). Colons clash with Pydoc-Markdown's formatting. Try to replace them with something else, like a space or a comma. Caution: Markdown links (http://...
) won't work either… you should instead reference links outside of special sections.To preview the result (and everytime you want to see changes), run $ pydocmd generate
in the terminal. Then open the docs site and check that the formatting is fine. You may have to hack a bit to get it right.
Describe alternatives you've considered
I could generate the pages myself, but this is also a great opportunity for new contributors to get started and discover the repo. 😊
Additional context
For more details about Pydoc-Markdown and its formatting rules, check out their docs.
Is your feature request related to a problem? Please describe.
If I were someone getting started with Bocadillo, I'd be pretty sad seeing that only specific how-to and API guides are available.
Describe the solution you'd like
I'd need a tutorial to take me through building something with Bocadillo, and get a feel for what it has to offer.
Note: this differs from a "quick start" guide that explains what the most important available features are and how to use them.
Describe alternatives you've considered
One possible application could be that of a blog:
sqlite3
, without spending too much time on it though.Another option could be a mailer service:
POST
endpoint to send an emailGET
endpoint to check service healthYet another option to demonstrate using Bocadillo for async stuff is building a simple web crawler (see Tornado's tutorial).
Additional context
This is a TODO item in the docs. It's high priority because it's hindering getting people (especially beginners) to use the framework.
Is your feature request related to a problem? Please describe.
Currently, Bocadillo provides no way of deferring an async function to a background task that runs after the response is sent. Awaiting the task should be enough as, once the response has been sent, Bocadillo can still process other incoming requests (unless the background task is performing blocking operations).
Describe the solution you'd like
The API could look something like:
import asyncio
@api.route('/notify/{who}')
async def notify(req, res, who):
async def send_email():
await asyncio.sleep(1) # do something I/O-bound here
res.background = send_email()
res.text = 'OK'
That is, res.background
would expect an awaitable (or None
) and, if present, await
it once send()
has been called.
Starlette's Response
already supports background tasks, see responses.py, so we should be able to wrap around it fairly easily.
Describe alternatives you've considered
We could have used a Responder-like API, e.g.
@api.route('/notify/{who}')
async def notify(req, res, who):
@api.background_task
async def send_email():
await asyncio.sleep(1) # do something I/O-bound here
send_email()
res.text = 'OK'
but I find this makes it less clear as to when the task is actually executed. There's also less code involved (no API.background()
method, it's just all tied to the response, which also means the user could gather()
multiple tasks if needed).
Additional context
I was working on Tarantula, a web crawling service, for a tutorial.
Is your feature request related to a problem? Please describe.
Pydoc-Markdown has been added to the repo, we can now start writing API reference documentation, so let's start with the API
class.
Describe the solution you'd like
Refactor the API
class' docstrings to be Pydoc-Markdown-friendly, then generate the docs and add a new child to the API Reference section.
Is your feature request related to a problem? Please describe.
We don't have any measure of test coverage yet.
Describe the solution you'd like
Let's set up test coverage, and show it in the README
.
Provide a short guide about how to deploy Bocadillo apps.
The page already exists at docs/guide/topics/deployment.md
, but is empty for now (see it live).
Is your feature request related to a problem? Please describe.
Starlette's GZip middleware is really useful for big responses and easy to add.
Describe the solution you'd like
Add a flag that should enable GZip middleware on API initialization.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.