litestar-org/litestar

Bug: Litestar crashes on initialization with Python 3.12.4 and a read-only root filesystem

jakeintel opened this issue · 3 comments

Description

Litestar throws an exception when starting on a container with a readonly root filesystem and python 3.12.4.

With earlier versions of 3.12 it does not throw.

With a read/write filesystem is does not throw.

URL to code causing the issue

https://github.com/jakeintel/litestar_bug_demo

MCVE

The repo link above has a simple, 'working' example.

Basically,
- create a hello-world app.
- Build it into a docker container that starts with uvicorn
- start the container with --readonly

Steps to reproduce

1. clone https://github.com/jakeintel/litestar_bug_demo
2. docker build -t demo .
3. docker run --read-only -it --rm demo

Screenshots

No response

Logs

warnings.warn(f'directory "{self.secrets_path}" does not exist')
Process SyncManager-1:
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
    self.run()
  File "/usr/local/lib/python3.12/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.12/multiprocessing/managers.py", line 591, in _run_server
    server = cls._Server(registry, address, authkey, serializer)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/managers.py", line 156, in __init__
    self.listener = Listener(address=address, backlog=128)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/connection.py", line 458, in __init__
    address = address or arbitrary_address(family)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/connection.py", line 77, in arbitrary_address
    return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
                                                   ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/util.py", line 149, in get_temp_dir
    tempdir = tempfile.mkdtemp(prefix='pymp-')
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/tempfile.py", line 373, in mkdtemp
    prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir)
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/tempfile.py", line 126, in _sanitize_params
    dir = gettempdir()
          ^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/tempfile.py", line 315, in gettempdir
    return _os.fsdecode(_gettempdir())
                        ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/tempfile.py", line 308, in _gettempdir
    tempdir = _get_default_tempdir()
              ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/tempfile.py", line 223, in _get_default_tempdir
    raise FileNotFoundError(_errno.ENOENT,
FileNotFoundError: [Errno 2] No usable temporary directory found in ['/tmp', '/var/tmp', '/usr/tmp', '/application']
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/logging/config.py", line 581, in configure
    handler = self.configure_handler(handlers[name])
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/logging/config.py", line 792, in configure_handler
    proxy_queue = MM().Queue()
                  ^^^^
  File "/usr/local/lib/python3.12/multiprocessing/context.py", line 57, in Manager
    m.start()
  File "/usr/local/lib/python3.12/multiprocessing/managers.py", line 566, in start
    self._address = reader.recv()
                    ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/connection.py", line 250, in recv
    buf = self._recv_bytes()
          ^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/connection.py", line 430, in _recv_bytes
    buf = self._recv(4)
          ^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/multiprocessing/connection.py", line 399, in _recv
    raise EOFError
EOFError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/uvicorn", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/uvicorn/main.py", line 418, in main
    run(
  File "/usr/local/lib/python3.12/site-packages/uvicorn/main.py", line 587, in run
    server.run()
  File "/usr/local/lib/python3.12/site-packages/uvicorn/server.py", line 62, in run
    return asyncio.run(self.serve(sockets=sockets))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/base_events.py", line 687, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/uvicorn/server.py", line 69, in serve
    config.load()
  File "/usr/local/lib/python3.12/site-packages/uvicorn/config.py", line 458, in load
    self.loaded_app = import_from_string(self.app)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/uvicorn/importer.py", line 21, in import_from_string
    module = importlib.import_module(module_str)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/importlib/__init__.py", line 90, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 995, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/application/api/app.py", line 42, in <module>
    app = create_app()
          ^^^^^^^^^^^^
  File "/application/api/app_factory.py", line 89, in create_app
    return Litestar(
           ^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/litestar/app.py", line 487, in __init__
    self.get_logger = self.logging_config.configure()
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/litestar/logging/config.py", line 276, in configure
    config.dictConfig(values)
  File "/usr/local/lib/python3.12/logging/config.py", line 920, in dictConfig
    dictConfigClass(config).configure()
  File "/usr/local/lib/python3.12/logging/config.py", line 588, in configure
    raise ValueError('Unable to configure handler '
ValueError: Unable to configure handler 'queue_listener'

Litestar Version

I tried the following

  • 2.9.1 (this is in the repo above)
  • 2.9.0
  • 2.8.3

Platform

  • Linux
  • Mac
  • Windows
  • Other (Please specify in the description above)

Note

While we are open for sponsoring on GitHub Sponsors and
OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.
Fund with Polar

Hey @jakeintel, thanks for reporting this!

I'm not really sure if this classifies as a Litestar bug, and I believe it might be a bug in the 3.12.4 release, caused by the changes introduced in this PR: python/cpython#93269

It can be reproduced with this simple test setup. When you run this on a read-only FS, you'll get the exception:

import logging.config

logging.config.dictConfig(
    {
        "version": 1,
        "handlers": {
            "queue_listener": {
                "class": "logging.handlers.QueueHandler",
                "queue": {"()": "queue.Queue", "maxsize": -1},
            },
        },
    }
)

@jakeintel I've filed a bug report at the CPython repo for this: python/cpython#120868