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

Strict pytest #929

Merged
merged 31 commits into from
Jun 1, 2021
Merged

Strict pytest #929

merged 31 commits into from
Jun 1, 2021

Conversation

euri10
Copy link
Member

@euri10 euri10 commented Dec 30, 2020

The refactored test suite is able to cope with a stricter pytest (see previous draft attempt before the refactor that was way way more difficult to handle ....)

This new pytest config sets the bar high as it turns warnings into exceptions and since the pytest 6.2 release, on python versions > 3.8 you get warnings on unclosed resources (https://docs.pytest.org/en/stable/usage.html#unraisable). We open / close stuff a lot so that's possible in tests that we forget to close things, this pytest config is unforgiving in that respect !

I'm not married to that PR, it's just a proposal, stricter is not always good, in that case I think it could add value on noticing things we would skip otherwise, happy to hear your thoughts

@euri10
Copy link
Member Author

euri10 commented Dec 30, 2020

ok another one that passes locally and fails in CI ! will take a look later

@euri10 euri10 marked this pull request as draft December 30, 2020 09:39
@euri10 euri10 marked this pull request as ready for review December 30, 2020 13:02
@euri10 euri10 requested a review from a team February 24, 2021 08:19
xfail_strict=True
filterwarnings=
# Turn warnings that aren't filtered into exceptions
error

Copy link
Member Author

Choose a reason for hiding this comment

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

it turns exceptions into errors

@@ -123,9 +123,10 @@ def set_protocol(self, protocol):

Copy link
Member Author

@euri10 euri10 Feb 24, 2021

Choose a reason for hiding this comment

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

the whole diff may seem huge, but it is simple: the MockLoop takes a parameter event_loop, which is the pytest-asyncio fixture, we get nice cleanup with it.
So in order to use that, each test is decorated with @pytest.mark.asyncio
And the get_connected_protocol function is turned into a context manager so that we can also clean it on every use

@euri10
Copy link
Member Author

euri10 commented Feb 24, 2021

I added some comments on the diffs to help understand it, LMK if that is enough, will explain with pleasure if something is not clear

Copy link
Member

@florimondmanca florimondmanca left a comment

Choose a reason for hiding this comment

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

Seems legit, resource warnings are worth catching as soon as possible. :) I left some questions and suggestions



@pytest.mark.asyncio
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need this marker, or can we have the mock setup the event loop by itself? Might help simplify things. I also find it surprising to use this marker on a sync test function (usually it is used to invoke async test functions).

Copy link
Member Author

Choose a reason for hiding this comment

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

ok I thought that it was necessary because I'm using their (pytest-asyncio) event_loop fixture, but it is not, so I removed it.

To be complete you can use the marker on a sync test, pytest_asyncio will wrap it, but that's effectively unnecessary in our case

@@ -151,6 +151,8 @@ def connection_lost(self, exc):
self.cycle.message_event.set()
if self.flow is not None:
self.flow.resume_writing()
if exc is None:
self.transport.close()
Copy link
Member

Choose a reason for hiding this comment

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

Could you explain these two lines?

The questions I have are:

  • I assume this was prompted by warnings raising errors in certain situations. Did it happen always or only in specific conditions?
  • Maybe related, why only close in case there was no exception? Does closing in case of exception cause issues?

Copy link
Member Author

Choose a reason for hiding this comment

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

ok if you remove those 2 lines that close the transport inside the connection_lost method you get errors in the test_ssl

E               Traceback (most recent call last):
E                 File "/home/lotso/.asdf/installs/python/3.9.1/lib/python3.9/asyncio/sslproto.py", line 320, in __del__
E                   _warn(f"unclosed transport {self!r}", ResourceWarning, source=self)
E               ResourceWarning: unclosed transport <asyncio.sslproto._SSLProtocolTransport object at 0x7f3d548c8460>

../venv/lib/python3.9/site-packages/_pytest/unraisableexception.py:78: PytestUnraisableExceptionWarning

it's only true for httptools_impl.py you dont have the error should you remove the exact same lines in h11_impl.py or the 2 ws protocols.

reason it works without for those last 3 is simply (I think) the ssl tests defaults to the httptools implementation, so I decided to put it in 4 places but it's a bet.

the ResourceWarning error happens on every test, it's very reproducible.

the reason I opted for the exc condition is debatable, I'm not sure about it : I saw in the BaseProtocol the following docstring:

    def connection_lost(self, exc):
        """Called when the connection is lost or closed.

        The argument is an exception object or None (the latter
        meaning a regular EOF is received or the connection was
        aborted or closed).
        """

while debugging it I had a None parameter, so I decided to close the transport only in that case, but I'll be lying if I said I'm 100% sure it's the correct move

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd be also interested in knowing if that change (closing the transport to fix the test_ssl ressource warning) would help on the weird issue #1021

@euri10
Copy link
Member Author

euri10 commented May 28, 2021

I think the deprecation warning that would have been caught by this PR when we unpinned websockets is another good reason it should be in, anyone fancy taking a look at it, @florimondmanca if you still here ? @Kludex ?

@euri10 euri10 requested review from florimondmanca and Kludex May 28, 2021 08:35
@@ -92,7 +94,7 @@ async def process_request(self, path, headers):
"""
path_portion, _, query_string = path.partition("?")

websockets.handshake.check_request(headers)
websockets.legacy.handshake.check_request(headers)
Copy link
Member

@Kludex Kludex May 29, 2021

Choose a reason for hiding this comment

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

This should fail on websockets==0.8, right? @euri10

Copy link
Member Author

Choose a reason for hiding this comment

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

yes it does fail, I'm wondering if we should just pin websockets higher ?

Copy link
Member

Choose a reason for hiding this comment

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

Either that, or doing something like:

if websockets.__version__ < "0.9":
    handshake = websockets.handshake
else:
   handshake = websockets.legacy.handshake

Maybe also adding a warning? Anyway, I'm fine with both.

Copy link
Member Author

Choose a reason for hiding this comment

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

ok now I have given it more thoughts and considering that:

  1. we do a minor bump soon
  2. a cve has been issued on 8.* and is solved in 9.1
    we should just keep this PR the way it currently is and up wesockets min reqs to 9.1

thoughts ?

Copy link
Member

Choose a reason for hiding this comment

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

I didn't know about 2.. So yeah, let's do this: we should just keep this PR the way it currently is and up wesockets min reqs to 9.1.

@euri10 euri10 mentioned this pull request May 31, 2021
3 tasks
@euri10 euri10 merged commit a877ccc into encode:master Jun 1, 2021
Vibhu-Agarwal added a commit to Vibhu-Agarwal/uvicorn that referenced this pull request Jun 1, 2021
@euri10 euri10 deleted the strict_pytest branch June 8, 2021 13:30
Kludex pushed a commit that referenced this pull request Nov 17, 2021
* Magically turn warnings into errors !

* Close that socket

* Close those loops

* Is it because of lifespan was not explicitely set to off ?

* Closing loop explicitely, maybe consider switching to a context manager ?

* trigger GitHub actions

* Close the transport when connection is lost

* Close the transport when connection is lost works locally
Adds some trace logs

* Close the transport when connection is lost works locally
Adds some trace logs

* Fixed bug in message_logger.py, doesnt return
Added trace logger in ws test that fails

* trace log on failing wds test in ci, cant get it locally

* Hard to know which test doesnt close and make the CI fail

* Test order in protocols

* Rewrite protocols http with context manager

* Lint

* rename order counts and still a bug

* WOOT ?

* Minimized diff, I moved tests around

* Minimized diff, ws test should stay the same

* Minimized diff, httptools debug stuff ermoved

* Removing trace logs from diff

* Reduce diff again, we take care of that bug in #967

* Removed pytest.mark.asyncio

* Fix lifespan close loop post merge master

* Fix websockets 9 deprecation warning
Kludex pushed a commit to sephioh/uvicorn that referenced this pull request Oct 29, 2022
* Magically turn warnings into errors !

* Close that socket

* Close those loops

* Is it because of lifespan was not explicitely set to off ?

* Closing loop explicitely, maybe consider switching to a context manager ?

* trigger GitHub actions

* Close the transport when connection is lost

* Close the transport when connection is lost works locally
Adds some trace logs

* Close the transport when connection is lost works locally
Adds some trace logs

* Fixed bug in message_logger.py, doesnt return
Added trace logger in ws test that fails

* trace log on failing wds test in ci, cant get it locally

* Hard to know which test doesnt close and make the CI fail

* Test order in protocols

* Rewrite protocols http with context manager

* Lint

* rename order counts and still a bug

* WOOT ?

* Minimized diff, I moved tests around

* Minimized diff, ws test should stay the same

* Minimized diff, httptools debug stuff ermoved

* Removing trace logs from diff

* Reduce diff again, we take care of that bug in encode#967

* Removed pytest.mark.asyncio

* Fix lifespan close loop post merge master

* Fix websockets 9 deprecation warning
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants