EmilStenstrom / django-components

Create simple reusable template components in Django.
MIT License
953 stars 59 forks source link

"django_components.component" does not explicitly export attribute "register" #458

Open thacoon opened 2 weeks ago

thacoon commented 2 weeks ago

I have a component like this

from typing import Dict, Any

from django_components import component

from server.example.models import Example

@component.register('example')
class ExampleComponent(component.Component):
    template_name = 'example/template.html'

    def get_context_data(self, instance: Example) -> Dict[str, Any]:
        return {
            'instance': instance,
        }

When running mypy I get the following error: error: Module "django_components.component" does not explicitly export attribute "register" [attr-defined]

EmilStenstrom commented 2 weeks ago

@thacoon How would we fix this, and are you open to writing a PR?

JuroOravec commented 2 weeks ago

Looking at this thread, I guess a temporary workaround could be to either:

What would be a proper fix? @thacoon can you share your mypy config?

thacoon commented 2 weeks ago

@JuroOravec my latest configuration is:

[mypy]
# Mypy configuration:
# https://mypy.readthedocs.io/en/latest/config_file.html
enable_error_code =
    truthy-bool,
    truthy-iterable,
    redundant-expr,
    unused-awaitable,
    ignore-without-code,
    possibly-undefined,
    redundant-self,

explicit_package_bases = true
ignore_missing_imports = true
strict = true
warn_unreachable = true

plugins =
    mypy_django_plugin.main

[mypy-server.apps.*.migrations.*]
# Django migrations should not produce any errors:
ignore_errors = true

[mypy.plugins.django-stubs]
# Docs: https://github.com/typeddjango/django-stubs
django_settings_module = config.settings
strict_settings = false
EmilStenstrom commented 2 weeks ago

I think the problem this mypy error is trying to point us at, is that we rely on the component module importing register as an API, but it's all implicit. So if we would refactor the module, and accidentally remove that import, we wouldn't notice that the API broke.

A simple way to explicitly define the public API is specifying all, and list all the symbols we export. Isn't that the simplest solution here?

JuroOravec commented 2 weeks ago

@EmilStenstrom So we want to define __all__ inside ~src/__init__.py~ src/component.py, is that correct?

JuroOravec commented 2 weeks ago

@thacoon I wasn't able to get the same error in my project, but it might be because there I'm on django_components 0.66 and mypy 1.7.0. What versions are you on?

Could you possibly test the solution below to check if it solves the issue? In your python packages folder, navigate to django_components/components.py, and on line 22 or 23, change

from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered, register  # NOQA

to

from django_components.component_registry import (
    AlreadyRegistered as AlreadyRegistered,
    ComponentRegistry as ComponentRegistry,
    NotRegistered as NotRegistered,
    register as register,
)

And then run mypy.

Aliasing at import should make mypy happy

mikucz commented 6 days ago

@thacoon I wasn't able to get the same error in my project, but it might be because there I'm on django_components 0.66 and mypy 1.7.0. What versions are you on?

Could you possibly test the solution below to check if it solves the issue? In your python packages folder, navigate to django_components/components.py, and on line 22 or 23, change

from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered, register  # NOQA

to

from django_components.component_registry import (
    AlreadyRegistered as AlreadyRegistered,
    ComponentRegistry as ComponentRegistry,
    NotRegistered as NotRegistered,
    register as register,
)

And then run mypy.

Aliasing at import should make mypy happy

I can confirm that this works with:

EmilStenstrom commented 6 days ago

@mikucz Excellent! Mind writing up a PR as well? Should be a good first contribution, don't you think? :)