#34204: Django cannot load when Python is compiled with --without-doc-strings
enabled
-------------------------------+--------------------------------------
     Reporter:  Jon Janzen     |                    Owner:  nobody
         Type:  Uncategorized  |                   Status:  new
    Component:  Uncategorized  |                  Version:  4.1
     Severity:  Normal         |               Resolution:
     Keywords:                 |             Triage Stage:  Unreviewed
    Has patch:  0              |      Needs documentation:  0
  Needs tests:  0              |  Patch needs improvement:  0
Easy pickings:  0              |                    UI/UX:  0
-------------------------------+--------------------------------------
Description changed by Jon Janzen:

Old description:

> I'm not sure that this is even a supported configuration for Django, but
> CPython's `./configure` script offers an option called
> [https://docs.python.org/3/using/configure.html#cmdoption-without-doc-
> strings --without-doc-strings] which improves memory utilization
> slightly.
>
> Quoting the relevant portion from CPython docs:
>
> > --without-doc-strings
> > Disable static documentation strings to reduce the memory footprint
> (enabled by default). Documentation strings defined in Python are not
> affected.
>
> I have an use-case where I need to deploy a Django service on a device
> with low available memory. I'd like to disable built-in doc strings as
> part of an overall strategy to reduce memory but compiling CPython with
> that option and running the django service crashes:
>

> {{{
>   File ".../asgi.py", line 3, in <module>
>     from django.core.asgi import get_asgi_application
>   File ".../lib/python3.11/site-packages/django/core/asgi.py", line 2, in
> <module>
>     from django.core.handlers.asgi import ASGIHandler
>   File ".../lib/python3.11/site-packages/django/core/handlers/asgi.py",
> line 11, in <module>
>     from django.core.handlers import base
>   File ".../lib/python3.11/site-packages/django/core/handlers/base.py",
> line 11, in <module>
>     from django.urls import get_resolver, set_urlconf
>   File ".../lib/python3.11/site-packages/django/urls/__init__.py", line
> 1, in <module>
>     from .base import (
>   File ".../lib/python3.11/site-packages/django/urls/base.py", line 8, in
> <module>
>     from .exceptions import NoReverseMatch, Resolver404
>   File ".../lib/python3.11/site-packages/django/urls/exceptions.py", line
> 1, in <module>
>     from django.http import Http404
>   File ".../lib/python3.11/site-packages/django/http/__init__.py", line
> 2, in <module>
>     from django.http.request import (
>   File ".../lib/python3.11/site-packages/django/http/request.py", line 8,
> in <module>
>     from django.core import signing
>   File ".../lib/python3.11/site-packages/django/core/signing.py", line
> 43, in <module>
>     from django.utils.crypto import constant_time_compare, salted_hmac
>   File ".../lib/python3.11/site-packages/django/utils/crypto.py", line
> 85, in <module>
>     if func_supports_parameter(hashlib.md5, 'usedforsecurity'):
>        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line
> 74, in func_supports_parameter
>     return any(param.name == name for param in
> _get_callable_parameters(func))
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line
> 16, in _get_callable_parameters
>     return _get_func_parameters(func, remove_first=is_method)
>            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line
> 7, in _get_func_parameters
>     parameters = tuple(inspect.signature(func).parameters.values())
>                        ^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../cpython/Lib/inspect.py", line 3270, in signature
>     return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
>            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../cpython/Lib/inspect.py", line 3018, in from_callable
>     return _signature_from_callable(obj, sigcls=cls,
>            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../cpython/Lib/inspect.py", line 2510, in
> _signature_from_callable
>     return _signature_from_builtin(sigcls, obj,
>            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>   File ".../cpython/Lib/inspect.py", line 2317, in
> _signature_from_builtin
>     raise ValueError("no signature found for builtin {!r}".format(func))
> ValueError: no signature found for builtin <built-in function
> openssl_md5>
> }}}
>
> (irrelevant information lightly censored)
>
> An easy way to repro is to follow this script:
> {{{
> git clone https://github.com/python/cpython.git
> cd cpython
> git checkout 3.11 # or whatever version you have that already has django
> installed
> ./configure --without-doc-strings
> make -j
> ./python
> >>> import django.utils.crypto
> *crash*
> }}}
>
> Looking through this stack trace the problem looks to be
> [https://github.com/django/django/blob/d10c7bfe56f025ccc690721c9f13e7029b777b9c/django/utils/crypto.py#L80-L92
> some code] that executes at module import to determine whether or not md5
> supports the `usedforsecurity` parameter.
>
> If this ticket is accepted I'm happy to put up a PR to fix this, I've
> included my proposed solution on this ticket to save a roundtrip in
> discussion:
>
> {{{
> diff --git a/django/utils/inspect.py b/django/utils/inspect.py
> index 28418f7312..d8622a22df 100644
> --- a/django/utils/inspect.py
> +++ b/django/utils/inspect.py
> @@ -70,4 +70,12 @@ def method_has_no_args(meth):
>

>  def func_supports_parameter(func, name):
> -    return any(param.name == name for param in
> _get_callable_parameters(func))
> +    try:
> +        callable_parameters = _get_callable_parameters(func)
> +    except ValueError:
> +        # When Python is compiled with the --without-doc-strings
> +        # argument to ./configure the signatures for built-in
> +        # functions are not available. In such a case, be
> +        # conservative and assume no:
> +        return False
> +    return any(param.name == name for param in callable_parameters)
> }}}

New description:

 I'm not sure that this is even a supported configuration for Django, but
 CPython's `./configure` script offers an option called
 [https://docs.python.org/3/using/configure.html#cmdoption-without-doc-
 strings --without-doc-strings] which improves memory utilization slightly.

 Quoting the relevant portion from CPython docs:

 > --without-doc-strings
 > Disable static documentation strings to reduce the memory footprint
 (enabled by default). Documentation strings defined in Python are not
 affected.

 I have an use-case where I need to deploy a Django service on a device
 with low available memory. I'd like to disable built-in doc strings as
 part of an overall strategy to reduce memory but compiling CPython with
 that option and running the django service crashes:


 {{{
   File ".../asgi.py", line 3, in <module>
     from django.core.asgi import get_asgi_application
   File ".../lib/python3.11/site-packages/django/core/asgi.py", line 2, in
 <module>
     from django.core.handlers.asgi import ASGIHandler
   File ".../lib/python3.11/site-packages/django/core/handlers/asgi.py",
 line 11, in <module>
     from django.core.handlers import base
   File ".../lib/python3.11/site-packages/django/core/handlers/base.py",
 line 11, in <module>
     from django.urls import get_resolver, set_urlconf
   File ".../lib/python3.11/site-packages/django/urls/__init__.py", line 1,
 in <module>
     from .base import (
   File ".../lib/python3.11/site-packages/django/urls/base.py", line 8, in
 <module>
     from .exceptions import NoReverseMatch, Resolver404
   File ".../lib/python3.11/site-packages/django/urls/exceptions.py", line
 1, in <module>
     from django.http import Http404
   File ".../lib/python3.11/site-packages/django/http/__init__.py", line 2,
 in <module>
     from django.http.request import (
   File ".../lib/python3.11/site-packages/django/http/request.py", line 8,
 in <module>
     from django.core import signing
   File ".../lib/python3.11/site-packages/django/core/signing.py", line 43,
 in <module>
     from django.utils.crypto import constant_time_compare, salted_hmac
   File ".../lib/python3.11/site-packages/django/utils/crypto.py", line 85,
 in <module>
     if func_supports_parameter(hashlib.md5, 'usedforsecurity'):
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line
 74, in func_supports_parameter
     return any(param.name == name for param in
 _get_callable_parameters(func))
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line
 16, in _get_callable_parameters
     return _get_func_parameters(func, remove_first=is_method)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../lib/python3.11/site-packages/django/utils/inspect.py", line 7,
 in _get_func_parameters
     parameters = tuple(inspect.signature(func).parameters.values())
                        ^^^^^^^^^^^^^^^^^^^^^^^
   File ".../cpython/Lib/inspect.py", line 3270, in signature
     return Signature.from_callable(obj, follow_wrapped=follow_wrapped,
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../cpython/Lib/inspect.py", line 3018, in from_callable
     return _signature_from_callable(obj, sigcls=cls,
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../cpython/Lib/inspect.py", line 2510, in
 _signature_from_callable
     return _signature_from_builtin(sigcls, obj,
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".../cpython/Lib/inspect.py", line 2317, in _signature_from_builtin
     raise ValueError("no signature found for builtin {!r}".format(func))
 ValueError: no signature found for builtin <built-in function openssl_md5>
 }}}

 (irrelevant information lightly censored)

 An easy way to repro is to follow this script:
 {{{
 git clone https://github.com/python/cpython.git
 cd cpython
 git checkout 3.11 # or whatever version you have that already has django
 installed
 ./configure --without-doc-strings
 make -j
 ./python -c "import django.utils.crypto"
 }}}

 Looking through this stack trace the problem looks to be
 
[https://github.com/django/django/blob/d10c7bfe56f025ccc690721c9f13e7029b777b9c/django/utils/crypto.py#L80-L92
 some code] that executes at module import to determine whether or not md5
 supports the `usedforsecurity` parameter.

 If this ticket is accepted I'm happy to put up a PR to fix this, I've
 included my proposed solution on this ticket to save a roundtrip in
 discussion:

 {{{
 diff --git a/django/utils/inspect.py b/django/utils/inspect.py
 index 28418f7312..d8622a22df 100644
 --- a/django/utils/inspect.py
 +++ b/django/utils/inspect.py
 @@ -70,4 +70,12 @@ def method_has_no_args(meth):


  def func_supports_parameter(func, name):
 -    return any(param.name == name for param in
 _get_callable_parameters(func))
 +    try:
 +        callable_parameters = _get_callable_parameters(func)
 +    except ValueError:
 +        # When Python is compiled with the --without-doc-strings
 +        # argument to ./configure the signatures for built-in
 +        # functions are not available. In such a case, be
 +        # conservative and assume no:
 +        return False
 +    return any(param.name == name for param in callable_parameters)
 }}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34204#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/01070184f57c79d3-a8d1c280-569f-4eb7-84be-54c04bb4cfde-000000%40eu-central-1.amazonses.com.

Reply via email to