-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Fix Ctrl+C and tests on Windows. #1808
Conversation
The code adds a background task that wakes up 10 times a second so that Python can receive signals (otherwise the signal handler is only called once Sanic receives network traffic). This is of course not needed on other operating systems. The signal handler cannot call It took too many hours to figure all that crap out, because it is not in the docs. Windows still cannot support multiple workers, so that side has not been considered in this fix. |
Codecov Report
@@ Coverage Diff @@
## master #1808 +/- ##
==========================================
+ Coverage 91.94% 92.05% +0.11%
==========================================
Files 23 23
Lines 2309 2342 +33
Branches 428 435 +7
==========================================
+ Hits 2123 2156 +33
Misses 143 143
Partials 43 43
Continue to review full report at Codecov.
|
…Python, and thus rebinding fails. For successful testing, reuse port instead.
… added warning on extra kwargs, made auto_reload explicit argument. Another go at Windows tests
…server), added warning on extra kwargs, made auto_reload explicit argument. Another go at Windows tests" This reverts commit dc5d682.
Okay, I think I'm finished with this. Care to review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix!
@@ -203,7 +207,7 @@ def __init__( | |||
|
|||
self.app = app | |||
|
|||
dispatch = SanicASGIDispatch(app=app, client=(ASGI_HOST, PORT)) | |||
dispatch = SanicASGIDispatch(app=app, client=(ASGI_HOST, PORT or 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 0 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typing says it needs to be int, so None
causes a failure. Either the type hint should be changed to Optional[int]
or 0 be used instead but I did not look at ASGI code to see how this is actually handled. In any case, 0 is the standard way to request for a free random port.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually all of Sanic should probably be changed to accept 0 port. Currently entering zero gives port 8000. But that is a potentially breaking change, and for another PR even if implemented.
sanic/server.py
Outdated
"Sanic tried to use loop.add_signal_handler " | ||
"but it is not implemented on this platform." | ||
) | ||
if os.name == "nt": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's define a constant and use that instead of string literal directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a constant in server.py but did not change this in skipif
of OS-dependent tests.
Do note that os.name
is the more coarse platform definition where all Windows are "nt"
, where sys.platform
uses many names for Windows (and quite confusingly, 64 bit Python on 64 bit Windows is still called "win32"
).
@@ -1177,6 +1178,7 @@ def run( | |||
|
|||
try: | |||
self.is_running = True | |||
self.is_stopping = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the diff between is_running
and is_stopping
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the app has not yet started, both are False. After calling app.stop
the app may keep running for a while, so in that period both are True. Asyncio implementation uses loop._stopping
in a similar fashion but uvloop doesn't have that and depending on private parts of the implementation is ugly in any case.
@@ -114,6 +115,9 @@ def _collect_request(request): | |||
url = "{scheme}://{host}:{port}{uri}".format( | |||
scheme=scheme, host=host, port=port, uri=uri | |||
) | |||
# Tests construct URLs using PORT = None, which means random port not | |||
# known until this function is called, so fix that here | |||
url = url.replace(":None/", f":{port}/") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a little bit concern on this, probably should avoid this kinda of hacky pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoiding this would require major restructuring of testing.py
which I am not quite up to, or rewriting/disabling all tests that depend on this.
Alternative signal handling logic for Windows (fix #1753) and allow green CI pipelines again.
app.is_stopping
for determining whetherapp.stop
has been called, which is different from app not running.