django-extra-checks
django-extra-checks copied to clipboard
Getting "OSError: could not find class definition" for most checks
I tried installing django-extra-checks, but only one of the check I've tried worked without errors.
My EXTRA_CHECKS dict looks like this:
EXTRA_CHECKS = {
"checks": [
# FileField/ImageField must have non empty `upload_to` argument
"field-file-upload-to",
#
# Use UniqueConstraint with the constraints option instead
# "no-unique-together",
#
# Use the indexes option instead
# "no-index-together",
#
# All field's `verbose_name` must use gettext
# "field-verbose-name-gettext",
#
# All field's `help_text` must use gettext
# "field-help-text-gettext",
#
# TextField() shouldn't use null=True
# "field-text-null",
]
}
The field-file-upload-to
check works fine if it's the only check. If I uncomment any of the other checks in EXTRA_CHECKS
above (whether field-file-upload-to
is commented out or not), I get the following:
Traceback (most recent call last):
File "/app/manage.py", line 31, in <module>
execute_from_command_line(sys.argv)
File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
utility.execute()
File "/usr/local/lib/python3.9/site-packages/django/core/management/__init__.py", line 413, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 354, in run_from_argv
self.execute(*args, **cmd_options)
File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 398, in execute
output = self.handle(*args, **options)
File "/usr/local/lib/python3.9/site-packages/django/core/management/commands/check.py", line 63, in handle
self.check(
File "/usr/local/lib/python3.9/site-packages/django/core/management/base.py", line 419, in check
all_issues = checks.run_checks(
File "/usr/local/lib/python3.9/site-packages/django/core/checks/registry.py", line 80, in run_checks
errors.extend(new_errors)
File "/usr/local/lib/python3.9/site-packages/extra_checks/checks/model_checks.py", line 63, in check_models
yield from check(field, ast=field_ast, model=model)
File "/usr/local/lib/python3.9/site-packages/extra_checks/checks/model_field_checks.py", line 31, in __call__
yield from super().__call__(obj, ast=ast, **kwargs)
File "/usr/local/lib/python3.9/site-packages/extra_checks/checks/base_checks.py", line 40, in __call__
if not ast or not ast.is_disabled_by_comment(error.id):
File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 246, in inner
self._setup()
File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 382, in _setup
self._wrapped = self._setupfunc()
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 96, in <lambda>
FieldAST, SimpleLazyObject(lambda: get_field_ast(self, field)) # type: ignore
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 117, in get_field_ast
model_ast._assignments[field.name], field, model_ast._source_provider
File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 84, in _assignments
self._parse()
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 54, in _parse
for node in self._nodes:
File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 48, in _nodes
if self._source_provider.source is None:
File "/usr/local/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 45, in source
return textwrap.dedent(inspect.getsource(self._obj))
File "/usr/local/lib/python3.9/inspect.py", line 1024, in getsource
lines, lnum = getsourcelines(object)
File "/usr/local/lib/python3.9/inspect.py", line 1006, in getsourcelines
lines, lnum = findsource(object)
File "/usr/local/lib/python3.9/inspect.py", line 851, in findsource
raise OSError('could not find class definition')
OSError: could not find class definition
ERROR: 1
I'm not exactly sure where to go from here to troubleshoot this. Any ideas? Am I missing something obvious?
@OmenApps inspect.getsource can't find source code of one of your models:
File "/usr/local/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 45, in source
return textwrap.dedent(inspect.getsource(self._obj))
check what is inside of self._obj
and if its source code file is reachable from the environment where you run check command.
Try to run inspect.getsource on that object from manage.py shell
.
Judging by the paths of the stack trace you're most probably running the app inside docker, so my best guess is that something broken in environment setup that leads to the problem when inspect
can't find source file.
Hello, I am using django-extra-checks in a project and I have this same problem with the django-simple-history library. I tried to ignore the line that generates the error according to the documentation, but it still does not work. This is the case:
# Standard Libraries
import textwrap
from typing import Final, final
# Django Libraries
from django.db import models
from django.utils.translation import gettext_lazy as _
# Thirdparty Libraries
from main.models.main import MAX_LENGTH_CHAR_FIELD, BaseAbstractModel
from simple_history.models import HistoricalRecords
#: That's how constants should be defined.
_POST_TITLE_MAX_LENGTH: Final = 80
@final
class BlogPost(BaseAbstractModel):
title = models.CharField(max_length=_POST_TITLE_MAX_LENGTH)
body = models.TextField()
# extra-checks-disable-next-line
history = HistoricalRecords()
class Meta(object):
verbose_name = _("BlogPost")
verbose_name_plural = _("BlogPosts")
def __str__(self) -> str:
return textwrap.wrap(self.title, _POST_TITLE_MAX_LENGTH // 4)[0]
This is the output:
Performing system checks...
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/project/.pyenv/versions/3.9.13/lib/python3.9/threading.py", line 980, in _bootstrap_inner
self.run()
File "/project/.pyenv/versions/3.9.13/lib/python3.9/threading.py", line 917, in run
self._target(*self._args, **self._kwargs)
File "/project/.venv/lib/python3.9/site-packages/django/utils/autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "/project/.venv/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 118, in inner_run
self.check(display_num_errors=True)
File "/project/.venv/lib/python3.9/site-packages/django/core/management/base.py", line 419, in check
all_issues = checks.run_checks(
File "/project/.venv/lib/python3.9/site-packages/django/core/checks/registry.py", line 80, in run_checks
errors.extend(new_errors)
File "/project/.venv/lib/python3.9/site-packages/extra_checks/checks/model_checks.py", line 59, in check_models
yield from check(model, ast=model_ast)
File "/project/.venv/lib/python3.9/site-packages/extra_checks/checks/base_checks.py", line 40, in __call__
if not ast or not ast.is_disabled_by_comment(error.id):
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/ast.py", line 112, in is_disabled_by_comment
return check in self._source_provider.get_disabled_checks_for_line(1)
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 69, in get_disabled_checks_for_line
self._comments_cache[line_no] = _find_disabled_checks(comments)
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 30, in _find_disabled_checks
for line in comments:
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 59, in _get_line_comments
for line in (self.source or "").splitlines()[line_no::-1]:
File "/project/.venv/lib/python3.9/site-packages/django/utils/functional.py", line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 45, in source
return textwrap.dedent(inspect.getsource(self._obj))
File "/project/.pyenv/versions/3.9.13/lib/python3.9/inspect.py", line 1024, in getsource
lines, lnum = getsourcelines(object)
File "/project/.pyenv/versions/3.9.13/lib/python3.9/inspect.py", line 1006, in getsourcelines
lines, lnum = findsource(object)
File "/project/.pyenv/versions/3.9.13/lib/python3.9/inspect.py", line 851, in findsource
raise OSError('could not find class definition')
OSError: could not find class definition
Is there any possible solution that you can recommend me?
Thank you
@gvizquel Hi, you can disable HistoricalRecords field check with skipif, see example skipif_streamfield
https://github.com/kalekseev/django-extra-checks#ignoring-check-problems
If you have time to debug please provide information what you have in self._obj
here:
File "/project/.venv/lib/python3.9/site-packages/extra_checks/ast/source_provider.py", line 45, in source
return textwrap.dedent(inspect.getsource(self._obj))
I just installed/configured django-extra-checks
and I have exactly the same problem.
Maybe adding a new extra-check on the app itself for catching unexpected errors is needed here :)
@gvizquel thank you for the source code, I was able to reproduce the error with simple history field. The root cause of the error is that simple history generate a companion model in the runtime so it's impossible to get its source code.
If django-extra-checks emits errors for that model you can disable them using skipif
:
def skipif_simple_history(model_cls, *args, **kwargs):
from simple_history.models import HistoricalChanges
return issubclass(model_cls, HistoricalChanges)
EXTRA_CHECKS = {
"checks": [
{
"id": "model-attribute",
"attrs": ["tenant_link"],
"level": "ERROR",
"skipif": skipif_simple_history,
},
{
"id": "model-meta-attribute",
"attrs": ["db_table"],
"level": "ERROR",
"skipif": skipif_simple_history,
},
}
@kalekseev thank you very much for the quick fix and release!