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

Error with 3.13.3 and 3.13.4: AttributeError: '_io.TextIOWrapper' object has no attribute 'getvalue' #284

Closed
philgyford opened this issue Mar 26, 2025 · 11 comments

Comments

@philgyford
Copy link

When I run the tests in one of my apps – which includes one set of tests that use mocket – they quit with an error 90% of the time when I'm using mocket 3.13.3 or 3.13.4, but run fine with 3.13.2.

I've spent hours trying to narrow it down and can't pin it down to a specific test. If I only run the set of tests that use mocket, they usually work. I can try and pin it down more tomorrow, but I've run out of time today.

When the testing fails it always aborts after one of the tests that uses mocket. Here's the output:

...
test_logs_favicon_saved (tests.directory.favicons.test_favicons.FaviconFetcherTestCase)
It should INFO log where the favicon was saved to ... ok
Used shuffle seed: 937773765 (generated)
Destroying test database for alias 'default' ('test_oohdir_1')...
Destroying test database for alias 'default' ('test_oohdir_2')...
Destroying test database for alias 'default' ('test_oohdir_3')...
Destroying test database for alias 'default' ('test_oohdir_4')...
Destroying test database for alias 'default' ('test_oohdir_5')...
Destroying test database for alias 'default' ('test_oohdir_6')...
Destroying test database for alias 'default' ('test_oohdir_7')...
Destroying test database for alias 'default' ('test_oohdir_8')...
Destroying test database for alias 'default' ('test_oohdir')...
Traceback (most recent call last):
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/manage.py", line 23, in <module>
    main()
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/manage.py", line 19, in main
    execute_from_command_line(sys.argv)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
    super().run_from_argv(argv)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/base.py", line 413, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/base.py", line 459, in execute
    output = self.handle(*args, **options)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/core/management/commands/test.py", line 63, in handle
    failures = test_runner.run_tests(test_labels)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/test/runner.py", line 1073, in run_tests
    result = self.run_suite(suite)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/test/runner.py", line 1000, in run_suite
    return runner.run(suite)
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/runner.py", line 184, in run
    test(result)
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/suite.py", line 84, in __call__
    return self.run(*args, **kwds)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/test/runner.py", line 556, in run
    handler(test, *args)
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/runner.py", line 68, in addError
    super(TextTestResult, self).addError(test, err)
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/result.py", line 17, in inner
    return method(self, *args, **kw)
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/result.py", line 115, in addError
    self.errors.append((test, self._exc_info_to_string(err, test)))
  File "/Users/phil/.local/share/uv/python/cpython-3.10.13-macos-aarch64-none/lib/python3.10/unittest/result.py", line 183, in _exc_info_to_string
    output = sys.stdout.getvalue()
AttributeError: '_io.TextIOWrapper' object has no attribute 'getvalue'

I get the same error running the tests in GitHub Actions on a different platform from macOS:

  File "/home/runner/.local/share/uv/python/cpython-3.10.13-linux-x86_64-gnu/lib/python3.10/unittest/result.py", line 183, in _exc_info_to_string
    output = sys.stdout.getvalue()
AttributeError: '_io.TextIOWrapper' object has no attribute 'getvalue'

Sorry that I don't have replicable code for this yet, but it seems consistent enough to report.

@philgyford
Copy link
Author

Oh, and in case you wonder, it's the same if I don't run the tests with --parallel on.

@mindflayer
Copy link
Owner

mindflayer commented Mar 26, 2025

@philgyford this stacktrace is about unittest failures, there is nothing useful even though I understand you are convinced that's an issue related to Mocket.
I don't see a single line of the stacktrace pointing to something from Mocket, I'll need more help from you to understand what's wrong.

@philgyford
Copy link
Author

Yes I realise that. I was sharing what I have so far after some hours of narrowing it down. If I make more progress tomorrow I will share that.

@philgyford
Copy link
Author

This is probably useless to you, so feel free to close it, but here's where I've got to.

I have this function and a test for it:

import requests
from django.test import TestCase
from mocket import mocketize
from mocket.mockhttp import Entry
from mocket.plugins.httpretty import httprettified, httpretty

def fetch():
    try:
        response = requests.get("https://example.org/")
        response.raise_for_status()
    except:
        return False
    else:
        return response

class FetchTestCase(TestCase):
    @mocketize
    @httprettified
    def test_fetch(self):
        httpretty.register_uri(httpretty.HEAD, "https://example.org/", "hello")
        Entry.single_register(Entry.GET, "https://example.org/", exception=OSError())
        result = fetch()
        self.assertFalse(result)

I get the error when I have mocket 3.13.3 or 3.13.4 installed, but only when I also run a bunch of other tests in the same app, which don't use mocket.

I never get the error when mocket 3.13.2 is installed.

And, crucially, I never get the error if I comment out @httprettified and the httpretty.register_url(...) call.

I've also found, only when not running tests in parallel, that the error trace above is preceded by:

/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/db/backends/postgresql/base.py:512: RuntimeWarning: Normally Django will use a connection to the 'postgres' database to avoid running initialization queries against the production database when it's not needed (for example, when running tests). Django was unable to create a connection to the 'postgres' database and will use the first PostgreSQL database instead.
  warnings.warn(
Traceback (most recent call last):
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/db/backends/base/base.py", line 279, in ensure_connection
    self.connect()
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/db/backends/base/base.py", line 256, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/django/db/backends/postgresql/base.py", line 332, in get_new_connection
    connection = self.Database.connect(**conn_params)
  File "/Users/phil/Projects/personal/ooh-directory/oohdir/.venv/lib/python3.10/site-packages/psycopg/connection.py", line 117, in connect
    raise last_ex.with_traceback(None)
psycopg.OperationalError: connection is bad: could not parse network address "localhost": nodename nor servname provided, or not known

Why it has that problem with the db connection only when I'm using @httprettified in that test makes no sense to me.

As I say, it doesn't seem to be something I can narrow down further to a reliable single piece of code that causes the error, sorry.

@mindflayer
Copy link
Owner

mindflayer commented Mar 27, 2025

Why are you using both @mocketize and @httprettified? In your case the decorator from the plugin - the second one - should be enough.

@mindflayer
Copy link
Owner

mindflayer commented Mar 27, 2025

In the end they are the very same thing (see here):

httprettified = mocketize

@mindflayer
Copy link
Owner

mindflayer commented Mar 27, 2025

I never get the error when mocket 3.13.2 is installed.

You are the second person who writes here for something similar, but I still have nothing useful for trying to replicate the problem. :(

@mindflayer
Copy link
Owner

mindflayer commented Mar 27, 2025

@philgyford Could you please replace TestCase with SimpleTestCase from the same module?

@mindflayer
Copy link
Owner

mindflayer commented Mar 27, 2025

This said, the code runs normally for me:

(.venv) /tmp $ pip freeze
asgiref==3.8.1
certifi==2025.1.31
charset-normalizer==3.4.1
decorator==5.2.1
Django==5.1.7
h11==0.14.0
idna==3.10
mocket==3.13.4
puremagic==1.28
requests==2.32.3
sqlparse==0.5.3
typing_extensions==4.13.0
urllib3==2.3.0
(.venv) /tmp $ python -V
Python 3.12.3
(.venv) /tmp $ python test.py

Of course I am not loading a Django project.

@philgyford
Copy link
Author

Why are you using both @mocketize and @httprettified? In your case the decorator from the plugin - the second one - should be enough.

Because I use @httprettified everywhere I need it – because I'm mainly using python-mocket as a replacement for httpretty. But this test also needed to use Entry and the README only uses @mocketize in all its examples when using Entry. So I used both.

And, you know what... when I remove @mocketize I no longer get the error! If I put it back, the error returns. I think you found the solution. It hadn't even occurred to me to try that.

@mindflayer
Copy link
Owner

mindflayer commented Mar 28, 2025

Happy to read that! I could not replicate your problem but at least I know that is something I should explore more.

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

No branches or pull requests

2 participants