Repository: plotly/dash

Issue:
[BUG] Children prop is not always validated 
moved from https://github.com/plotly/dash-core/issues/214

@alexcjohnson 

I would expect the Dash app that follows to throw an error when the `dcc.Input` component tries to set `children={"a dict": "is not allowed for children-input"}`. 

However, the error is only thrown when the `html.Div` component attempts to do the same. 

```py
import dash
import dash_html_components as html
import dash_core_components as dcc


app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Input(children={"a dict": "is not allowed for children-input"}),
    html.Div(children={"a dict": "is not allowed for children-div"})

])

if __name__ == '__main__':
    app.run_server(debug=True)

```

Error:

```
An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
  "a dict": "is not allowed for children-div"
}
```




Discussion:

Resolving patch:
diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py
--- a/dash/development/_py_components_generation.py
+++ b/dash/development/_py_components_generation.py
@@ -1,7 +1,7 @@
 from collections import OrderedDict
 import copy
 import os
-from textwrap import fill
+from textwrap import fill, dedent
 
 from dash.development.base_component import _explicitize_args
 from dash.exceptions import NonExistentEventException
@@ -65,11 +65,8 @@ def __init__(self, {default_argtext}):
         _explicit_args = kwargs.pop('_explicit_args')
         _locals = locals()
         _locals.update(kwargs)  # For wildcard attrs and excess named props
-        args = {{k: _locals[k] for k in _explicit_args if k != 'children'}}
-        for k in {required_props}:
-            if k not in args:
-                raise TypeError(
-                    'Required argument `' + k + '` was not specified.')
+        args = {args}
+        {required_validation}
         super({typename}, self).__init__({argtext})
 '''
 
@@ -87,18 +84,40 @@ def __init__(self, {default_argtext}):
         description=description,
         prop_reorder_exceptions=prop_reorder_exceptions,
     ).replace("\r\n", "\n")
+    required_args = required_props(filtered_props)
+    is_children_required = 'children' in required_args
+    required_args = [arg for arg in required_args if arg != "children"]
 
     prohibit_events(props)
 
     # pylint: disable=unused-variable
     prop_keys = list(props.keys())
-    if "children" in props:
+    if "children" in props and "children" in list_of_valid_keys:
         prop_keys.remove("children")
         default_argtext = "children=None, "
+        args = "{k: _locals[k] for k in _explicit_args if k != 'children'}"
         argtext = "children=children, **args"
     else:
         default_argtext = ""
+        args = "{k: _locals[k] for k in _explicit_args}"
         argtext = "**args"
+
+    if len(required_args) == 0:
+        required_validation = ""
+    else:
+        required_validation = f"""
+        for k in {required_args}:
+            if k not in args:
+                raise TypeError(
+                    'Required argument `' + k + '` was not specified.')
+        """
+
+    if is_children_required:
+        required_validation += """
+        if 'children' not in _explicit_args:
+            raise TypeError('Required argument children was not specified.')
+        """
+
     default_arglist = [
         (
             f"{p:s}=Component.REQUIRED"
@@ -121,20 +140,23 @@ def __init__(self, {default_argtext}):
             )
 
     default_argtext += ", ".join(default_arglist + ["**kwargs"])
-    required_args = required_props(filtered_props)
     nodes = collect_nodes({k: v for k, v in props.items() if k != "children"})
-    return c.format(
-        typename=typename,
-        namespace=namespace,
-        filtered_props=filtered_props,
-        list_of_valid_wildcard_attr_prefixes=wildcard_prefixes,
-        list_of_valid_keys=list_of_valid_keys,
-        docstring=docstring,
-        default_argtext=default_argtext,
-        argtext=argtext,
-        required_props=required_args,
-        children_props=nodes,
-        base_nodes=filter_base_nodes(nodes) + ["children"],
+
+    return dedent(
+        c.format(
+            typename=typename,
+            namespace=namespace,
+            filtered_props=filtered_props,
+            list_of_valid_wildcard_attr_prefixes=wildcard_prefixes,
+            list_of_valid_keys=list_of_valid_keys,
+            docstring=docstring,
+            default_argtext=default_argtext,
+            args=args,
+            argtext=argtext,
+            required_validation=required_validation,
+            children_props=nodes,
+            base_nodes=filter_base_nodes(nodes) + ["children"],
+        )
     )
 
 


Test patch:
diff --git a/tests/integration/test_generation.py b/tests/integration/test_generation.py
--- a/tests/integration/test_generation.py
+++ b/tests/integration/test_generation.py
@@ -9,6 +9,7 @@
     TypeScriptComponent,
     TypeScriptClassComponent,
     StandardComponent,
+    RequiredChildrenComponent,
 )
 from dash_test_components import StyledComponent
 from dash.html import Button, Div
@@ -99,3 +100,10 @@ def test_gene003_max_props():
 
     with pytest.raises(TypeError):
         MyNestedComponent(valuey="nor this")
+
+
+def test_gene004_required_children_prop():
+    with pytest.raises(TypeError):
+        RequiredChildrenComponent()
+
+    RequiredChildrenComponent(children='worked')
diff --git a/tests/unit/development/metadata_test.py b/tests/unit/development/metadata_test.py
--- a/tests/unit/development/metadata_test.py
+++ b/tests/unit/development/metadata_test.py
@@ -100,8 +100,5 @@ def __init__(self, children=None, optionalArray=Component.UNDEFINED, optionalBoo
         _locals = locals()
         _locals.update(kwargs)  # For wildcard attrs and excess named props
         args = {k: _locals[k] for k in _explicit_args if k != 'children'}
-        for k in []:
-            if k not in args:
-                raise TypeError(
-                    'Required argument `' + k + '` was not specified.')
+
         super(Table, self).__init__(children=children, **args)
diff --git a/tests/unit/development/test_base_component.py b/tests/unit/development/test_base_component.py
--- a/tests/unit/development/test_base_component.py
+++ b/tests/unit/development/test_base_component.py
@@ -534,3 +534,8 @@ def update(v):
     @app.callback(Output(output1, "children"), Input(input1, "value"))
     def update2(v):
         return f"Input 1 {v}"
+
+
+def test_debc030_invalid_children_args():
+    with pytest.raises(TypeError):
+        dcc.Input(children='invalid children')