Repository: tornadoweb/tornado

Issue:
Test failures with python 3.11
Fedora is starting to test building packages with python 3.11.0a1.  tornado fails with:
```
======================================================================
ERROR: test_future_error (tornado.test.concurrent_test.GeneratorClientTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/concurrent_test.py",
line 125, in test_future_error
    self.assertRaisesRegexp(CapError, "already capitalized", future.result)  #
type: ignore
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'GeneratorClientTest' object has no attribute
'assertRaisesRegexp'

======================================================================
ERROR: test_generator_error (tornado.test.concurrent_test.GeneratorClientTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/concurrent_test.py",
line 141, in test_generator_error
    self.io_loop.run_sync(f)
    ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/ioloop.py", line 530, in
run_sync
    return future_cell[0].result()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/gen.py", line 234, in
wrapper
    yielded = ctx_run(next, result)
              ^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/concurrent_test.py",
line 138, in f
    with self.assertRaisesRegexp(CapError, "already capitalized"):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'GeneratorClientTest' object has no attribute
'assertRaisesRegexp'

======================================================================
ERROR: test_header_callback
(tornado.test.httpclient_test.HTTPClientCommonTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/httpclient_test.py",
line 477, in test_header_callback
    self.assertRegexpMatches(first_line[0], "HTTP/[0-9]\\.[0-9] 200.*\r\n")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'HTTPClientCommonTestCase' object has no attribute
'assertRegexpMatches'

======================================================================
ERROR: test_read_until_close_with_error
(tornado.test.iostream_test.TestIOStream)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 599, in
post_coroutine
    return self.io_loop.run_sync(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/ioloop.py", line 530, in
run_sync
    return future_cell[0].result()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/gen.py", line 775, in run
    yielded = self.gen.send(value)
              ^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/iostream_test.py",
line 787, in test_read_until_close_with_error
    with self.assertRaisesRegexp(IOError, "boom"):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'TestIOStream' object has no attribute 'assertRaisesRegexp'

======================================================================
ERROR: test_read_until_close_with_error
(tornado.test.iostream_test.TestIOStreamSSL)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 599, in
post_coroutine
    return self.io_loop.run_sync(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/ioloop.py", line 530, in
run_sync
    return future_cell[0].result()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/gen.py", line 775, in run
    yielded = self.gen.send(value)
              ^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/iostream_test.py",
line 787, in test_read_until_close_with_error
    with self.assertRaisesRegexp(IOError, "boom"):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'TestIOStreamSSL' object has no attribute 'assertRaisesRegexp'

======================================================================
ERROR: test_read_until_close_with_error
(tornado.test.iostream_test.TestIOStreamSSLContext)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 599, in
post_coroutine
    return self.io_loop.run_sync(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/ioloop.py", line 530, in
run_sync
    return future_cell[0].result()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/gen.py", line 775, in run
    yielded = self.gen.send(value)
              ^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/iostream_test.py",
line 787, in test_read_until_close_with_error
    with self.assertRaisesRegexp(IOError, "boom"):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'TestIOStreamSSLContext' object has no attribute
'assertRaisesRegexp'

======================================================================
ERROR: test_log_file (tornado.test.log_test.EnablePrettyLoggingTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/log_test.py", line
151, in test_log_file
    self.assertRegexpMatches(f.read(), r"^\[E [^]]*\] hello$")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'EnablePrettyLoggingTest' object has no attribute
'assertRegexpMatches'

======================================================================
ERROR: test_log_file_with_timed_rotating
(tornado.test.log_test.EnablePrettyLoggingTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/log_test.py", line
171, in test_log_file_with_timed_rotating
    self.assertRegexpMatches(f.read(), r"^\[E [^]]*\] hello$")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'EnablePrettyLoggingTest' object has no attribute
'assertRegexpMatches'

======================================================================
ERROR: test_bytes_exception_logging (tornado.test.log_test.LogFormatterTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/log_test.py", line
115, in test_bytes_exception_logging
    self.assertRegexpMatches(output, br"Exception.*\\xe9")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'LogFormatterTest' object has no attribute
'assertRegexpMatches'

======================================================================
ERROR: test_bytes_exception_logging
(tornado.test.log_test.UnicodeLogFormatterTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/log_test.py", line
115, in test_bytes_exception_logging
    self.assertRegexpMatches(output, br"Exception.*\\xe9")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'UnicodeLogFormatterTest' object has no attribute
'assertRegexpMatches'

======================================================================
ERROR: test_error_redefine (tornado.test.options_test.OptionsTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/options_test.py", line
265, in test_error_redefine
    self.assertRegexpMatches(str(cm.exception), "Option.*foo.*already defined")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'OptionsTest' object has no attribute 'assertRegexpMatches'

======================================================================
ERROR: test_error_redefine_underscore (tornado.test.options_test.OptionsTest)
(a='foo-bar', b='foo-bar')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/options_test.py", line
282, in test_error_redefine_underscore
    self.assertRegexpMatches(
    ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'OptionsTest' object has no attribute 'assertRegexpMatches'

======================================================================
ERROR: test_error_redefine_underscore (tornado.test.options_test.OptionsTest)
(a='foo_bar', b='foo_bar')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/options_test.py", line
282, in test_error_redefine_underscore
    self.assertRegexpMatches(
    ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'OptionsTest' object has no attribute 'assertRegexpMatches'

======================================================================
ERROR: test_error_redefine_underscore (tornado.test.options_test.OptionsTest)
(a='foo-bar', b='foo_bar')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/options_test.py", line
282, in test_error_redefine_underscore
    self.assertRegexpMatches(
    ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'OptionsTest' object has no attribute 'assertRegexpMatches'

======================================================================
ERROR: test_error_redefine_underscore (tornado.test.options_test.OptionsTest)
(a='foo_bar', b='foo-bar')
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/options_test.py", line
282, in test_error_redefine_underscore
    self.assertRegexpMatches(
    ^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'OptionsTest' object has no attribute 'assertRegexpMatches'

======================================================================
ERROR: test_header_callback
(tornado.test.simple_httpclient_test.SimpleHTTPClientCommonTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/testing.py", line 98, in
__call__
    result = self.orig_method(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/builddir/build/BUILD/tornado-6.1.0/tornado/test/httpclient_test.py",
line 477, in test_header_callback
    self.assertRegexpMatches(first_line[0], "HTTP/[0-9]\\.[0-9] 200.*\r\n")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'SimpleHTTPClientCommonTestCase' object has no attribute
'assertRegexpMatches'

----------------------------------------------------------------------
Ran 1167 tests in 16.284s

FAILED (errors=16, skipped=52)
```
In Python 3.11 many old deprecated unittest features are removed:

    TestCase method aliases failUnlessEqual, failIfEqual, failUnless, failIf,
failUnlessRaises, failUnlessAlmostEqual, failIfAlmostEqual (deprecated in
Python 3.1), assertEquals, assertNotEquals, assert_, assertAlmostEquals,
assertNotAlmostEquals, assertRegexpMatches, assertRaisesRegexp (deprecated in
Python 3.2), and assertNotRegexpMatches (deprecated in Python 3.5).

    Undocumented and broken TestCase method assertDictContainsSubset
(deprecated in Python 3.2).

    Undocumented <unittest.TestLoader.loadTestsFromModule>
TestLoader.loadTestsFromModule() parameter use_load_tests (deprecated and
ignored since Python 3.2).

    An alias of the TextTestResult class: _TextTestResult (deprecated in Python
3.2).

(Contributed by Serhiy Storchaka in bpo-45162.)

https://docs.python.org/3.11/whatsnew/3.11.html

For the build logs, see:
https://copr-be.cloud.fedoraproject.org/results/@python/python3.11/fedora-rawhide-x86_64/02910746-python-tornado/

For all our attempts to build python-tornado with Python 3.11, see:
https://copr.fedorainfracloud.org/coprs/g/python/python3.11/package/python-tornado/

Testing and mass rebuild of packages is happening in copr. You can follow these
instructions to test locally in mock if your package builds with Python 3.11:
https://copr.fedorainfracloud.org/coprs/g/python/python3.11/

https://bugzilla.redhat.com/show_bug.cgi?id=2019007


Discussion:
fixed in 5c16457c6c460ef25b52c467ad50895a94335b20 https://github.com/tornadoweb/tornado/pull/3052 (not yet in any tagged release)
Debian is turning to Python 3.10 too. Should I add a patch to the current package or can I wait for the next stable release?
Depends on your timeline. I'm trying to get a new release out the door before the end of the year, but I've got some yaks to shave first (dealing with the 3.10 deprecations and moving the CI/build process off travis-ci), and I haven't had much time for Tornado recently so I can't promise anything. 

Resolving patch:
diff --git a/tornado/options.py b/tornado/options.py
--- a/tornado/options.py
+++ b/tornado/options.py
@@ -90,7 +90,7 @@ def start_server():
 
    `parse_command_line` or `parse_config_file` function should called after
    logging configuration and user-defined command line flags using the
-   `callback` option definition, or these configurations will not take effect.
+   ``callback`` option definition, or these configurations will not take effect.
 
 .. versionchanged:: 4.3
    Dashes and underscores are fully interchangeable in option names;


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -44,6 +44,8 @@ jobs:
             tox_env: py39-full
           - python: '3.10'
             tox_env: py310-full
+          - python: '3.11.0-alpha - 3.11'
+            tox_env: py311-full
           - python: 'pypy-3.8'
             # Pypy is a lot slower due to jit warmup costs, so don't run the
             # "full" test config there.