Repository: plotly/dash
File: dash/_validate.py (Python)

Code before:
    if not isinstance(
        value, (Component, patch_collections_abc("Callable"), list, tuple)
    ):
        raise exceptions.NoLayoutException(
            """
            Layout must be a single dash component, a list of dash components,
            or a function that returns a dash component.
            """
        )


def validate_layout(layout, layout_value):
    if layout is None:
        raise exceptions.NoLayoutException(
            """
            The layout was `None` at the time that `run_server` was called.
            Make sure to set the `layout` attribute of your application
            before running the server.
            """
        )

    component_ids = set()

    def _validate(value):
        def _validate_type(comp):
            deprecated_types = ["LogoutButton"]
            component_type = getattr(comp, "_type", None)
            if component_type and component_type in deprecated_types:
                warnings.warn(
                    f"{component_type} is deprecated, use a different component type instead",
                    DeprecationWarning,
                )

        def _validate_id(comp):
            component_id = stringify_id(getattr(comp, "id", None))
            if component_id and component_id in component_ids:
                raise exceptions.DuplicateIdError(
                    f"""
                    Duplicate component id found in the initial layout: `{component_id}`
                    """
                )
            component_ids.add(component_id)

        _validate_type(value)
        _validate_id(value)

        for component in value._traverse():  # pylint: disable=protected-access
            _validate_type(component)
            _validate_id(component)

    if isinstance(layout_value, (list, tuple)):

Review comment (refactor):
Just to make sure there is no collision, we should also check the `_namespace` attribute. Also could add a custom deprecation message to the component.

I would refactor the `deprecated_types` to a `collections.defaultdict(lambda: collections.defaultdict(dict))` then set:
```
deprecated_components["dash_core_components"]["LogoutButton"] = "''
The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A.
eg: html.A(href=os.getenv('DASH_LOGOUT_URL'))
"""
```
The for the check you could have:

```
_type = getattr(comp, "_type",  "")
_ns = getattr(comp, "_namespace", "")
deprecation_message = deprecated_components[_ns][_type]
if deprecation_message:
    ...
```


Code after:
def validate_layout_type(value):
    if not isinstance(
        value, (Component, patch_collections_abc("Callable"), list, tuple)
    ):
        raise exceptions.NoLayoutException(
            """
            Layout must be a single dash component, a list of dash components,
            or a function that returns a dash component.
            """
        )


def validate_layout(layout, layout_value):
    if layout is None:
        raise exceptions.NoLayoutException(
            """
            The layout was `None` at the time that `run_server` was called.
            Make sure to set the `layout` attribute of your application
            before running the server.
            """
        )

    component_ids = set()

    def _validate(value):
        def _validate_type(comp):
            deprecated_components = defaultdict(lambda: defaultdict(dict))
            deprecated_components["dash_core_components"][
                "LogoutButton"
            ] = """
                The Logout Button is no longer used with Dash Enterprise and can be replaced with a html.Button or html.A.
                eg: html.A(href=os.getenv('DASH_LOGOUT_URL'))
            """

            _type = getattr(comp, "_type", "")
            _ns = getattr(comp, "_namespace", "")
            deprecation_message = deprecated_components[_ns][_type]
            if deprecation_message:
                warnings.warn(dedent(deprecation_message), DeprecationWarning)

        def _validate_id(comp):
            component_id = stringify_id(getattr(comp, "id", None))
            if component_id and component_id in component_ids:
                raise exceptions.DuplicateIdError(
                    f"""
                    Duplicate component id found in the initial layout: `{component_id}`
                    """
                )
            component_ids.add(component_id)

        _validate_type(value)