Skip to content

Commit 366d1aa

Browse files
authored
Merge pull request #18 from SmileyChris/channels2
New module to support Django Channels v2, including a major rewrite deduplicating code between the sync and async modules
2 parents b21da67 + efcd905 commit 366d1aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1653
-776
lines changed

.travis.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ deploy:
1010
tags: true
1111
install: pip install -U tox-travis
1212
language: python
13+
dist: focal
1314
python:
15+
- 3.9
1416
- 3.8
1517
- 3.7
1618
- 3.6
17-
- 3.5
1819
- 2.7
1920
script: tox

CONTRIBUTING.rst

+2-13
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ Ready to contribute? Here's how to set up `graphql_ws` for local development.
6868

6969
$ mkvirtualenv graphql_ws
7070
$ cd graphql_ws/
71-
$ python setup.py develop
71+
$ pip install -e .[dev]
7272

7373
4. Create a branch for local development::
7474

@@ -79,11 +79,8 @@ Ready to contribute? Here's how to set up `graphql_ws` for local development.
7979
5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox::
8080

8181
$ flake8 graphql_ws tests
82-
$ python setup.py test or py.test
8382
$ tox
8483

85-
To get flake8 and tox, just pip install them into your virtualenv.
86-
8784
6. Commit your changes and push your branch to GitHub::
8885

8986
$ git add .
@@ -101,14 +98,6 @@ Before you submit a pull request, check that it meets these guidelines:
10198
2. If the pull request adds functionality, the docs should be updated. Put
10299
your new functionality into a function with a docstring, and add the
103100
feature to the list in README.rst.
104-
3. The pull request should work for Python 2.6, 2.7, 3.3, 3.4 and 3.5, and for PyPy. Check
101+
3. The pull request should work for Python 2.7, 3.5, 3.6, 3.7 and 3.8. Check
105102
https://travis-ci.org/graphql-python/graphql_ws/pull_requests
106103
and make sure that the tests pass for all supported Python versions.
107-
108-
Tips
109-
----
110-
111-
To run a subset of tests::
112-
113-
$ py.test tests.test_graphql_ws
114-

MANIFEST.in

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ include LICENSE
55
include README.rst
66
include tox.ini
77

8+
graft graphql_ws/django/templates
9+
graft examples
10+
prune examples/django_channels2/.cache
811
graft tests
912
global-exclude __pycache__
1013
global-exclude *.py[co]

README.rst

+137-88
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
==========
12
GraphQL WS
23
==========
34

4-
Websocket server for GraphQL subscriptions.
5+
Websocket backend for GraphQL subscriptions.
6+
7+
Supports the following application servers:
8+
9+
Python 3 application servers, using asyncio:
10+
11+
* `aiohttp`_
12+
* `websockets compatible servers`_ such as Sanic
13+
(via `websockets <https://github.com/aaugustin/websockets/>`__ library)
14+
* `Django v2+`_
515

6-
Currently supports:
16+
Python 2 application servers:
17+
18+
* `Gevent compatible servers`_ such as Flask
19+
* `Django v1.x`_
20+
(via `channels v1.x <https://channels.readthedocs.io/en/1.x/inshort.html>`__)
721

8-
* `aiohttp <https://github.com/graphql-python/graphql-ws#aiohttp>`__
9-
* `Gevent <https://github.com/graphql-python/graphql-ws#gevent>`__
10-
* Sanic (uses `websockets <https://github.com/aaugustin/websockets/>`__
11-
library)
1222

1323
Installation instructions
1424
=========================
@@ -19,21 +29,54 @@ For instaling graphql-ws, just run this command in your shell
1929
2030
pip install graphql-ws
2131
32+
2233
Examples
23-
--------
34+
========
35+
36+
Python 3 servers
37+
----------------
38+
39+
Create a subscribable schema like this:
40+
41+
.. code:: python
42+
43+
import asyncio
44+
import graphene
45+
46+
47+
class Query(graphene.ObjectType):
48+
hello = graphene.String()
49+
50+
@staticmethod
51+
def resolve_hello(obj, info, **kwargs):
52+
return "world"
53+
54+
55+
class Subscription(graphene.ObjectType):
56+
count_seconds = graphene.Float(up_to=graphene.Int())
57+
58+
async def resolve_count_seconds(root, info, up_to):
59+
for i in range(up_to):
60+
yield i
61+
await asyncio.sleep(1.)
62+
yield up_to
63+
64+
65+
schema = graphene.Schema(query=Query, subscription=Subscription)
2466
2567
aiohttp
2668
~~~~~~~
2769

28-
For setting up, just plug into your aiohttp server.
70+
Then just plug into your aiohttp server.
2971

3072
.. code:: python
3173
3274
from graphql_ws.aiohttp import AiohttpSubscriptionServer
33-
75+
from .schema import schema
3476
3577
subscription_server = AiohttpSubscriptionServer(schema)
3678
79+
3780
async def subscriptions(request):
3881
ws = web.WebSocketResponse(protocols=('graphql-ws',))
3982
await ws.prepare(request)
@@ -47,16 +90,20 @@ For setting up, just plug into your aiohttp server.
4790
4891
web.run_app(app, port=8000)
4992
50-
Sanic
51-
~~~~~
93+
You can see a full example here:
94+
https://github.com/graphql-python/graphql-ws/tree/master/examples/aiohttp
95+
96+
97+
websockets compatible servers
98+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5299

53-
Works with any framework that uses the websockets library for it’s
54-
websocket implementation. For this example, plug in your Sanic server.
100+
Works with any framework that uses the websockets library for its websocket
101+
implementation. For this example, plug in your Sanic server.
55102

56103
.. code:: python
57104
58105
from graphql_ws.websockets_lib import WsLibSubscriptionServer
59-
106+
from . import schema
60107
61108
app = Sanic(__name__)
62109
@@ -70,49 +117,61 @@ websocket implementation. For this example, plug in your Sanic server.
70117
71118
app.run(host="0.0.0.0", port=8000)
72119
73-
And then, plug into a subscribable schema:
74120
75-
.. code:: python
121+
Django v2+
122+
~~~~~~~~~~
76123

77-
import asyncio
78-
import graphene
79124

125+
Django Channels 2
126+
~~~~~~~~~~~~~~~~~
80127

81-
class Query(graphene.ObjectType):
82-
base = graphene.String()
128+
Set up with Django Channels just takes three steps:
83129

130+
1. Install the apps
131+
2. Set up your schema
132+
3. Configure the channels router application
84133

85-
class Subscription(graphene.ObjectType):
86-
count_seconds = graphene.Float(up_to=graphene.Int())
134+
First ``pip install channels`` and it to your ``INSTALLED_APPS``. If you
135+
want graphiQL, install the ``graphql_ws.django`` app before
136+
``graphene_django`` to serve a graphiQL template that will work with
137+
websockets:
87138

88-
async def resolve_count_seconds(root, info, up_to):
89-
for i in range(up_to):
90-
yield i
91-
await asyncio.sleep(1.)
92-
yield up_to
139+
.. code:: python
93140
141+
INSTALLED_APPS = [
142+
"channels",
143+
"graphql_ws.django",
144+
"graphene_django",
145+
# ...
146+
]
94147
95-
schema = graphene.Schema(query=Query, subscription=Subscription)
148+
Point to your schema in Django settings:
96149

97-
You can see a full example here:
98-
https://github.com/graphql-python/graphql-ws/tree/master/examples/aiohttp
150+
.. code:: python
99151
100-
Gevent
101-
~~~~~~
152+
GRAPHENE = {
153+
'SCHEMA': 'yourproject.schema.schema'
154+
}
102155
103-
For setting up, just plug into your Gevent server.
156+
Finally, you can set up channels routing yourself (maybe using
157+
``graphql_ws.django.routing.websocket_urlpatterns`` in your
158+
``URLRouter``), or you can just use one of the preset channels
159+
applications:
104160

105161
.. code:: python
106162
107-
subscription_server = GeventSubscriptionServer(schema)
108-
app.app_protocol = lambda environ_path_info: 'graphql-ws'
163+
ASGI_APPLICATION = 'graphql_ws.django.routing.application'
164+
# or
165+
ASGI_APPLICATION = 'graphql_ws.django.routing.auth_application'
109166
110-
@sockets.route('/subscriptions')
111-
def echo_socket(ws):
112-
subscription_server.handle(ws)
113-
return []
167+
Run ``./manage.py runserver`` and go to
168+
`http://localhost:8000/graphql`__ to test!
114169

115-
And then, plug into a subscribable schema:
170+
171+
Python 2 servers
172+
-----------------
173+
174+
Create a subscribable schema like this:
116175

117176
.. code:: python
118177
@@ -121,7 +180,11 @@ And then, plug into a subscribable schema:
121180
122181
123182
class Query(graphene.ObjectType):
124-
base = graphene.String()
183+
hello = graphene.String()
184+
185+
@staticmethod
186+
def resolve_hello(obj, info, **kwargs):
187+
return "world"
125188
126189
127190
class Subscription(graphene.ObjectType):
@@ -135,71 +198,54 @@ And then, plug into a subscribable schema:
135198
136199
schema = graphene.Schema(query=Query, subscription=Subscription)
137200
138-
You can see a full example here:
139-
https://github.com/graphql-python/graphql-ws/tree/master/examples/flask_gevent
140-
141-
Django Channels
142-
~~~~~~~~~~~~~~~
201+
Gevent compatible servers
202+
~~~~~~~~~~~~~~~~~~~~~~~~~
143203

144-
First ``pip install channels`` and it to your django apps
145-
146-
Then add the following to your settings.py
204+
Then just plug into your Gevent server, for example, Flask:
147205

148206
.. code:: python
149207
150-
CHANNELS_WS_PROTOCOLS = ["graphql-ws", ]
151-
CHANNEL_LAYERS = {
152-
"default": {
153-
"BACKEND": "asgiref.inmemory.ChannelLayer",
154-
"ROUTING": "django_subscriptions.urls.channel_routing",
155-
},
156-
157-
}
158-
159-
Setup your graphql schema
208+
from flask_sockets import Sockets
209+
from graphql_ws.gevent import GeventSubscriptionServer
210+
from schema import schema
160211
161-
.. code:: python
162-
163-
import graphene
164-
from rx import Observable
165-
166-
167-
class Query(graphene.ObjectType):
168-
hello = graphene.String()
212+
subscription_server = GeventSubscriptionServer(schema)
213+
app.app_protocol = lambda environ_path_info: 'graphql-ws'
169214
170-
def resolve_hello(self, info, **kwargs):
171-
return 'world'
172215
173-
class Subscription(graphene.ObjectType):
216+
@sockets.route('/subscriptions')
217+
def echo_socket(ws):
218+
subscription_server.handle(ws)
219+
return []
174220
175-
count_seconds = graphene.Int(up_to=graphene.Int())
221+
You can see a full example here:
222+
https://github.com/graphql-python/graphql-ws/tree/master/examples/flask_gevent
176223

224+
Django v1.x
225+
~~~~~~~~~~~
177226

178-
def resolve_count_seconds(
179-
root,
180-
info,
181-
up_to=5
182-
):
183-
return Observable.interval(1000)\
184-
.map(lambda i: "{0}".format(i))\
185-
.take_while(lambda i: int(i) <= up_to)
186-
227+
For Django v1.x and Django Channels v1.x, setup your schema in ``settings.py``
187228

229+
.. code:: python
188230
189-
schema = graphene.Schema(
190-
query=Query,
191-
subscription=Subscription
192-
)
231+
GRAPHENE = {
232+
'SCHEMA': 'yourproject.schema.schema'
233+
}
193234
194-
Setup your schema in settings.py
235+
Then ``pip install "channels<1"`` and it to your django apps, adding the
236+
following to your ``settings.py``
195237

196238
.. code:: python
197239
198-
GRAPHENE = {
199-
'SCHEMA': 'path.to.schema'
240+
CHANNELS_WS_PROTOCOLS = ["graphql-ws", ]
241+
CHANNEL_LAYERS = {
242+
"default": {
243+
"BACKEND": "asgiref.inmemory.ChannelLayer",
244+
"ROUTING": "django_subscriptions.urls.channel_routing",
245+
},
200246
}
201247
202-
and finally add the channel routes
248+
And finally add the channel routes
203249

204250
.. code:: python
205251
@@ -209,3 +255,6 @@ and finally add the channel routes
209255
channel_routing = [
210256
route_class(GraphQLSubscriptionConsumer, path=r"^/subscriptions"),
211257
]
258+
259+
You can see a full example here:
260+
https://github.com/graphql-python/graphql-ws/tree/master/examples/django_subscriptions

0 commit comments

Comments
 (0)