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

ASGI refactoring attempt #1475

Merged
merged 19 commits into from
Jun 20, 2019
Merged

ASGI refactoring attempt #1475

merged 19 commits into from
Jun 20, 2019

Conversation

tomchristie
Copy link
Contributor

This isn't intended to be graceful at this point, rather just to make a start on getting passing tests, using an ASGI-based app, connected to an ASGI-based test client.

The application changes are intended to be the dumbest possible thing that could provide the ASGI interface, with the intention that once the test suite was passing or sufficiently passing, then refactoring towards something far more graceful could start.

There are two changes:

  • The Sanic class has an ASGI based __call__ interface, that calls into a dumb shim layer.
  • The SanicTestClient is replaced from an aiohttp client that makes actual network requests, into a requests based client, that has an adapter to make ASGI requests direct to the application.

Because the requests interface differs in places to the aiohttp client interface, there's a bit of fiddling around to ensure that API compatibility. Again, this is just the dumbest possible place to start, with a view to moving to something more graceful later.

@tomchristie
Copy link
Contributor Author

@sjsadowski
Copy link
Contributor

@tomchristie since we've got X's across the board, how would you like to handle this? New PR after more ASGI work or update this guy when you're ready?

@tomchristie
Copy link
Contributor Author

Sure, so the blocker I ran into on first attempt was the test client. I figured the first thing is to switch the test client out so that it's pointing at the ASGI interface instead.

Sanic uses an aiohttp-based test client, which makes actual network requests.
Starlette has a requests-based test client, which interacts directly with ASGI.

I naively tried just swapping one out for the other, but there's enough API differences that it doesn't really fly.

Anyways, the right way to progress this next would be...

  1. Sort out an aiohttp-based ASGI test client, by subclassing the exsiting client so that it calls into an ASGI interface rather than making actual network requests.

Once that's sorted then we'll be able to add an ASGI interface onto Sanic, and run the existing test suite against it.

I've got a few other bits on my plate at the moment, so it might take me a bit to get onto it, but defo on the TODO list.

@sjsadowski
Copy link
Contributor

So I was digging a bit and it looks like the aiohttp folks are somewhat resistant to ASGI inclusion.

Is it time to look at overhauling testing completely to be requests based in order to make us future-ready for doing ASGI? Personally I don't have it in me to go lobby over at aiohttp to push "closed" issues.

@sjsadowski
Copy link
Contributor

Further, this looks like it's going to be a bigger chunk of work - do we want to split this off in to a feature branch that we can work on so more people can assist? @huge-success/sanic-core-devs

@tomchristie
Copy link
Contributor Author

So I was digging a bit and it looks like the aiohttp folks are somewhat resistant to ASGI inclusion.

Doesn't really matter from the POV of Sanic's Test Client. It wouldn't be in aiohttp's codebase.

Is it time to look at overhauling testing completely to be requests based in order to make us future-ready for doing ASGI? Personally I don't have it in me to go lobby over at aiohttp to push "closed" issues.

It'd make the ASGI switch easier from my POV, but I don't think it's a blocker.

(Conversly, if sanic does stay with aiohttp interface for now, then once we've made the ASGI switch, we'll get a requests-based alternative for free.)

@yunstanford
Copy link
Member

Any progress here or any blocker ?

@ahopkins
Copy link
Member

Yes. I am working on swapping out the test client to using requests-async. I should be able to push something either later this week or over the weekend.

@tomchristie
Copy link
Contributor Author

@ahopkins Great - make sure to ping me when there's progress there, and I'll see if there's something I oughta be contributing from my side.

@ahopkins
Copy link
Member

@ahopkins ahopkins changed the title ASGI refactoring attempt WIP: ASGI refactoring attempt Apr 18, 2019
@ahopkins
Copy link
Member

@tomchristie I have a question about requests-async... Is ASGISession setup to handle class based ASGI apps? It appears the answer is no.

TypeError: __call__() takes 2 positional arguments but 4 were given

If I add a new method, and pass that to ASGISession as the app, it works as expected.

class Sanic:
    ...
    def __call__(self, scope):
        return ASGIApp(self, scope)

    def asgi(self, scope, receive, send):
        asgi_app = ASGIApp(self, scope)
        return asgi_app(receive, send)

It looks to me like this is something that should be addressed. But in the mean time, I will press forward with something like the above.

@tomchristie
Copy link
Contributor Author

ASGISession works against the ASGI 3 interface - a single Async callable taking scope, receive, and send. This PR is a bit out of date since it targets the old double callable style. I can update this PR if that’s helpful, tho I think the first thing would be to update the underlying test client without considering ASGI. (Just making regular http calls direct to the app in the same way as the existing client)

@ahopkins
Copy link
Member

Makes sense, and that is basically what I am doing. But, I wanted to also test it against this work so far.

@ahopkins ahopkins mentioned this pull request Apr 23, 2019
@ahopkins
Copy link
Member

ahopkins commented May 5, 2019

@tomchristie Can you let me know if this is something you plan to pick up again with, or if you want me to continue on? Again, I really appreciate the work you have put in so far.

@tomchristie
Copy link
Contributor Author

Both options possible. I’ll try to take a look over where things are at in the coming days.

@tomchristie tomchristie closed this May 6, 2019
@tomchristie tomchristie reopened this May 6, 2019
@yunstanford
Copy link
Member

any timeline on this ?

@ahopkins
Copy link
Member

I'm not able to put time on this this weekend. Bird my thought was next week to put some effort in to try and make it before the end of this month so we at least have it as an option for 19.6.

@ahopkins
Copy link
Member

Not ready for review yet. But progress nonetheless ...

@harshanarayana
Copy link
Contributor

@ahopkins anything I can do to help?

@sjsadowski
Copy link
Contributor

Just as a note here: https://community.sanicframework.org/t/june-19-6-0-release/310 I was suggesting on Jun-15, but I think as long as we get it out this month (before Jun-30) we're still following the release pattern.

@ahopkins
Copy link
Member

ahopkins commented Jun 5, 2019

To complete:

  • Docs
  • Test coverage on asgi.py
  • Fixing the minor linting issues (it seems black in the Makefile is not formatting as flake8 is requiring in tox, maybe a separate issue worth looking at?)

Copy link
Member

@yunstanford yunstanford left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've not fully reviewed yet, but looks good overall.
Seems like still missing documentation around this, can we add that ? I'm thinking we probably should just mention ASGI support still in Beta for 19.6 release.

sanic/testing.py Outdated Show resolved Hide resolved
sanic/testing.py Show resolved Hide resolved
@@ -477,7 +477,7 @@ def write_response(self, response):
async def drain(self):
await self._not_paused.wait()

def push_data(self, data):
async def push_data(self, data):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we add async here ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll go back and look again. I don't remember exactly, but I do remember there was a reason. I'll add a comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ASGI equivalent needs to be awaitable. Therefore, I opted to make the Sanic version also awaitable to reduce complexity. The alternative would be either adding an isawaitable check, or a try/except. I am up for changing it, but I thought the following in the StreamingHTTPResponse was the simplest.

        if self.chunked:
            await self.protocol.push_data(b"%x\r\n%b\r\n" % (len(data), data))
        else:
            await self.protocol.push_data(data)

sanic/asgi.py Outdated Show resolved Hide resolved
sanic/asgi.py Outdated Show resolved Hide resolved
sanic/asgi.py Show resolved Hide resolved
@ahopkins
Copy link
Member

ahopkins commented Jun 15, 2019

@yunstanford

Seems like still missing documentation around this, can we add that ? I'm thinking we probably should just mention ASGI support still in Beta for 19.6 release.

Yes, I did write some, but I don't know what happened to it. I will write again and push it. I agree with you. That's actually what I was suggesting we do. I put a section in deployment and then said that ASGI was in beta for now.

ahopkins
ahopkins previously approved these changes Jun 18, 2019
Copy link
Member

@ahopkins ahopkins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are ready to merge. Anyone else? @yunstanford? @sjsadowski?

I'll try to add some more testing coverage.

@ahopkins ahopkins dismissed their stale review June 18, 2019 08:53

remove self-approval

@yunstanford
Copy link
Member

Sounds good, I'll take a look tonight. Thanks!

@ahopkins
Copy link
Member

ahopkins commented Jun 18, 2019

image
Getting there. Just a little more test coverage and codecov should be happy.

I will push this later and then rebase the commits that I can.

@ahopkins
Copy link
Member

Uh.... weird that the CI is failing:
image

@ahopkins
Copy link
Member

Ahh.... websockets 6.0 conflict with uvicorn which is requestion 7.0. Specifying it in tox. We need to tackle that one soon. I will put it on my radar for 19.9

@yunstanford
Copy link
Member

Thanks, I'll prepare the release.

@yunstanford yunstanford merged commit 891f99d into sanic-org:master Jun 20, 2019
@ahopkins
Copy link
Member

Thanks @tomchristie for your help in pushing this forward. I hope this helps the overall Python community as we all continue to build a richer set of tools.

@tomchristie tomchristie deleted the asgi-refactor-attempt branch June 21, 2019 09:07
@tomchristie
Copy link
Contributor Author

@yunstanford @ahopkins - Great work folks!

@harshanarayana
Copy link
Contributor

@ahopkins This is wonderful. Thank you so much @tomchristie Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants