From 5e89dd4074656b59521378e9d6977785b6bcba25 Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Sun, 17 Jan 2021 08:55:43 -0700 Subject: [PATCH 1/7] Update docstrings to address #1205 --- dash/development/_py_components_generation.py | 173 ++++++++++-------- 1 file changed, 100 insertions(+), 73 deletions(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index 24928d1fcb..db94df6ddc 100644 --- a/dash/development/_py_components_generation.py +++ b/dash/development/_py_components_generation.py @@ -1,6 +1,7 @@ from collections import OrderedDict import copy import os +from textwrap import fill, indent from dash.development.base_component import _explicitize_args from dash.exceptions import NonExistentEventException @@ -12,16 +13,13 @@ def generate_class_string(typename, props, description, namespace): """Dynamically generate class strings to have nicely formatted docstrings, keyword arguments, and repr. - Inspired by http://jameso.be/2013/08/06/namedtuple.html - Parameters ---------- typename props description namespace - Returns ------- string @@ -53,12 +51,10 @@ def __init__(self, {default_argtext}): self.available_properties = {list_of_valid_keys} self.available_wildcard_properties =\ {list_of_valid_wildcard_attr_prefixes} - _explicit_args = kwargs.pop('_explicit_args') _locals = locals() _locals.update(kwargs) # For wildcard attrs args = {{k: _locals[k] for k in _explicit_args if k != 'children'}} - for k in {required_props}: if k not in args: raise TypeError( @@ -112,14 +108,12 @@ def __init__(self, {default_argtext}): def generate_class_file(typename, props, description, namespace): """Generate a Python class file (.py) given a class string. - Parameters ---------- typename props description namespace - Returns ------- """ @@ -170,14 +164,12 @@ def generate_classes_files(project_shortname, metadata, *component_generators): def generate_class(typename, props, description, namespace): """Generate a Python class object given a class string. - Parameters ---------- typename props description namespace - Returns ------- """ @@ -191,11 +183,9 @@ def generate_class(typename, props, description, namespace): def required_props(props): """Pull names of required props from the props object. - Parameters ---------- props: dict - Returns ------- list @@ -206,7 +196,6 @@ def required_props(props): def create_docstring(component_name, props, description): """Create the Dash component docstring. - Parameters ---------- component_name: str @@ -215,7 +204,6 @@ def create_docstring(component_name, props, description): Dictionary with {propName: propMetadata} structure description: str Component description - Returns ------- str @@ -248,12 +236,10 @@ def create_docstring(component_name, props, description): def prohibit_events(props): """Events have been removed. Raise an error if we see dashEvents or fireEvents. - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Raises ------- ? @@ -267,12 +253,10 @@ def prohibit_events(props): def parse_wildcards(props): """Pull out the wildcard attributes from the Component props. - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- list @@ -287,43 +271,37 @@ def parse_wildcards(props): def reorder_props(props): """If "children" is in props, then move it to the front to respect dash - convention. - + convention, then 'id', then the remaining props sorted by prop name Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- dict Dictionary with {propName: propMetadata} structure """ - if "children" in props: - # Constructing an OrderedDict with duplicate keys, you get the order - # from the first one but the value from the last. - # Doing this to avoid mutating props, which can cause confusion. - props = OrderedDict([("children", "")] + list(props.items())) - return props + # Constructing an OrderedDict with duplicate keys, you get the order + # from the first one but the value from the last. + # Doing this to avoid mutating props, which can cause confusion. + props1 = [("children", "")] if "children" in props else [] + props2 = [("id", "")] if "id" in props else [] + return OrderedDict(props1 + props2 + sorted(list(props.items()))) def filter_props(props): """Filter props from the Component arguments to exclude: - - Those without a "type" or a "flowType" field - Those with arg.type.name in {'func', 'symbol', 'instanceOf'} - Parameters ---------- props: dict Dictionary with {propName: propMetadata} structure - Returns ------- dict Filtered dictionary with {propName: propMetadata} structure - Examples -------- ```python @@ -380,7 +358,18 @@ def filter_props(props): return filtered_props +def fix_keywords(txt): + """ + replaces javascript keywords true, false, null with Python keywords + """ + fix_word = {"true": "True", "false": "False", "null": "None"} + for js_keyword, python_keyword in fix_word.items(): + txt = txt.replace(js_keyword, python_keyword) + return txt + + # pylint: disable=too-many-arguments +# pylint: disable=too-many-locals def create_prop_docstring( prop_name, type_object, @@ -391,7 +380,6 @@ def create_prop_docstring( is_flow_type=False, ): """Create the Dash component prop docstring. - Parameters ---------- prop_name: str @@ -411,71 +399,109 @@ def create_prop_docstring( (creates 2 spaces for every indent) is_flow_type: bool Does the prop use Flow types? Otherwise, uses PropTypes - Returns ------- str Dash component prop docstring """ py_type_name = js_to_py_type( - type_object=type_object, is_flow_type=is_flow_type, indent_num=indent_num + 1 + type_object=type_object, is_flow_type=is_flow_type, indent_num=indent_num ) indent_spacing = " " * indent_num - if default is None: - default = "" - else: - default = default["value"] - - if default in ["true", "false"]: - default = default.title() + default = default["value"] if default else "" + default = fix_keywords(default) is_required = "optional" if required: is_required = "required" - elif default and default not in ["null", "{}", "[]"]: - is_required = "default {}".format(default.replace("\n", "\n" + indent_spacing)) + elif default and default not in ["None", "{}", "[]"]: + is_required = "default {}".format(default.replace("\n", "")) + + # formats description + period = "." if description else "" + description = description.strip().strip(".") + period + desc_indent = indent_spacing + " " + description = fill( + description, + initial_indent=desc_indent, + subsequent_indent=desc_indent, + break_long_words=False, + break_on_hyphens=False, + ) + description = "\n{} ".format(description) if description else "" + colon = ":" if description else "" + description = fix_keywords(description) if "\n" in py_type_name: + # corrects the type + dict_or_list = "list of dicts" if py_type_name.startswith("list") else "dict" + + # format and rewrite the intro to the nested dicts + intro1, intro2, dict_descr = py_type_name.partition("with keys:") + intro = "".join([f"`{prop_name}`", " is a ", intro1, intro2]) + intro = fill( + intro, + initial_indent=desc_indent, + subsequent_indent=desc_indent, + break_long_words=False, + break_on_hyphens=False, + ) + + # captures optional nested dict description and puts the "or" condition on a new line + if "| dict with keys:" in dict_descr: + dict_part1, dict_part2 = dict_descr.split("|", 1) + dict_part1 = dict_part1 + " " + dict_part2 = "".join([desc_indent, "Or", dict_part2]) + dict_descr = "{} \n\n {}".format(dict_part1, dict_part2) + + # ensures indent is correct + current_indent = dict_descr.lstrip("\n").find("-") + if current_indent == len(indent_spacing): + dict_descr = indent(dict_descr, " ") + return ( - "{indent_spacing}- {name} (dict; {is_required}): " - "{description}{period}" - "{name} has the following type: {type}".format( + "\n{indent_spacing}- {name} ({dict_or_list}; {is_required}){colon}" + "{description} " + "\n\n{intro} {dict_descr}".format( indent_spacing=indent_spacing, name=prop_name, - type=py_type_name, - description=description.strip().strip("."), - period=". " if description else "", + colon=colon, + description=description, + intro=intro, + dict_descr=dict_descr, + dict_or_list=dict_or_list, is_required=is_required, ) ) - return "{indent_spacing}- {name} ({type}{is_required}){description}".format( - indent_spacing=indent_spacing, - name=prop_name, - type="{}; ".format(py_type_name) if py_type_name else "", - description=(": {}".format(description) if description != "" else ""), - is_required=is_required, + return ( + "\n{indent_spacing}- {name} ({type}{is_required}){colon}" + "{description}".format( + indent_spacing=indent_spacing, + name=prop_name, + type="{}; ".format(py_type_name) if py_type_name else "", + colon=colon, + description=description, + is_required=is_required, + ) ) -def map_js_to_py_types_prop_types(type_object): +def map_js_to_py_types_prop_types(type_object, indent_num): """Mapping from the PropTypes js type object to the Python type.""" def shape_or_exact(): - return "dict containing keys {}.\n{}".format( - ", ".join("'{}'".format(t) for t in list(type_object["value"].keys())), - "Those keys have the following types:\n{}".format( - "\n".join( - create_prop_docstring( - prop_name=prop_name, - type_object=prop, - required=prop["required"], - description=prop.get("description", ""), - default=prop.get("defaultValue"), - indent_num=1, - ) - for prop_name, prop in list(type_object["value"].items()) + return "dict with keys: \n{}".format( + " \n".join( + create_prop_docstring( + prop_name=prop_name, + type_object=prop, + required=prop["required"], + description=prop.get("description", ""), + default=prop.get("defaultValue"), + indent_num=indent_num + 2, ) + for prop_name, prop in sorted(list(type_object["value"].items())) ), ) @@ -505,7 +531,7 @@ def shape_or_exact(): "list" + ( " of {}".format( - js_to_py_type(type_object["value"]) + "s" + js_to_py_type(type_object["value"]) if js_to_py_type(type_object["value"]).split(" ")[0] != "dict" else js_to_py_type(type_object["value"]).replace("dict", "dicts", 1) ) @@ -551,7 +577,7 @@ def map_js_to_py_types_flow_types(type_object): else "" ), # React's PropTypes.shape - signature=lambda indent_num: "dict containing keys {}.\n{}".format( + signature=lambda indent_num: "dict with keys: {}.\n{}".format( ", ".join( "'{}'".format(d["key"]) for d in type_object["signature"]["properties"] ), @@ -576,7 +602,6 @@ def map_js_to_py_types_flow_types(type_object): def js_to_py_type(type_object, is_flow_type=False, indent_num=0): """Convert JS types to Python types for the component definition. - Parameters ---------- type_object: dict @@ -585,17 +610,19 @@ def js_to_py_type(type_object, is_flow_type=False, indent_num=0): Does the prop use Flow types? Otherwise, uses PropTypes indent_num: int Number of indents to use for the docstring for the prop - Returns ------- str Python type string """ + js_type_name = type_object["name"] js_to_py_types = ( map_js_to_py_types_flow_types(type_object=type_object) if is_flow_type - else map_js_to_py_types_prop_types(type_object=type_object) + else map_js_to_py_types_prop_types( + type_object=type_object, indent_num=indent_num + ) ) if ( From fa8c7624a257fd8af871875189c28e01c401a3ef Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Tue, 19 Jan 2021 13:46:54 -0700 Subject: [PATCH 2/7] escape double quotes in prop description --- dash/development/_py_components_generation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index db94df6ddc..92b23f16bc 100644 --- a/dash/development/_py_components_generation.py +++ b/dash/development/_py_components_generation.py @@ -420,7 +420,7 @@ def create_prop_docstring( # formats description period = "." if description else "" - description = description.strip().strip(".") + period + description = description.strip().strip(".").replace('"', r"\"") + period desc_indent = indent_spacing + " " description = fill( description, From 05a5a5448ebc74dfa5e75d2baa2ec29b0fd49b83 Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Sat, 23 Jan 2021 14:44:14 -0700 Subject: [PATCH 3/7] updated tests for new docstring format --- dash/development/_py_components_generation.py | 6 +- tests/unit/development/__init__.py | 112 ++++++++----- tests/unit/development/metadata_test.py | 110 ++++++++---- .../test_flow_metadata_conversions.py | 156 +++++++++++------- tests/unit/development/test_generate_class.py | 2 +- .../development/test_generate_class_file.py | 5 +- .../development/test_metadata_conversions.py | 40 +++-- 7 files changed, 271 insertions(+), 160 deletions(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index 92b23f16bc..59c8e48c95 100644 --- a/dash/development/_py_components_generation.py +++ b/dash/development/_py_components_generation.py @@ -439,7 +439,7 @@ def create_prop_docstring( # format and rewrite the intro to the nested dicts intro1, intro2, dict_descr = py_type_name.partition("with keys:") - intro = "".join([f"`{prop_name}`", " is a ", intro1, intro2]) + intro = "".join(["`{}`".format(prop_name), " is a ", intro1, intro2]) intro = fill( intro, initial_indent=desc_indent, @@ -531,7 +531,7 @@ def shape_or_exact(): "list" + ( " of {}".format( - js_to_py_type(type_object["value"]) + js_to_py_type(type_object["value"]) + "s" if js_to_py_type(type_object["value"]).split(" ")[0] != "dict" else js_to_py_type(type_object["value"]).replace("dict", "dicts", 1) ) @@ -590,7 +590,7 @@ def map_js_to_py_types_flow_types(type_object): required=prop["value"]["required"], description=prop["value"].get("description", ""), default=prop.get("defaultValue"), - indent_num=indent_num, + indent_num=indent_num + 2, is_flow_type=True, ) for prop in type_object["signature"]["properties"] diff --git a/tests/unit/development/__init__.py b/tests/unit/development/__init__.py index b8d2f0b34c..6b1016c438 100644 --- a/tests/unit/development/__init__.py +++ b/tests/unit/development/__init__.py @@ -13,51 +13,81 @@ def has_trailing_space(s): "It's multiple lines long.", "", "Keyword arguments:", + "", "- children (a list of or a singular dash component, string or number; optional)", - "- optionalArray (list; optional): Description of optionalArray", + "", + "- id (string; optional)", + "", + "- aria-* (string; optional)", + "", + "- customArrayProp (list; optional)", + "", + "- customProp (optional)", + "", + "- data-* (string; optional)", + "", + "- in (string; optional)", + "", + "- optionalAny (boolean | number | string | dict | list; optional)", + "", + "- optionalArray (list; optional):", + " Description of optionalArray. ", + "", + "- optionalArrayOf (list of numbers; optional)", + "", "- optionalBool (boolean; optional)", + "", + "- optionalElement (dash component; optional)", + "", + "- optionalEnum (a value equal to: 'News'," " 'Photos'; optional)", + "", + "- optionalNode (a list of or a singular dash component, string or number; optional)", + "", "- optionalNumber (number; default 42)", + "", "- optionalObject (dict; optional)", + "", + "- optionalObjectOf (dict with strings as keys and values of type number; optional)", + "", + "- optionalObjectWithExactAndNestedDescription (dict; optional) ", + "", + " `optionalObjectWithExactAndNestedDescription` is a dict with keys: ", + "", + " - color (string; optional) ", + "", + " - figure (dict; optional):", + " Figure is a plotly graph object. ", + "", + " `figure` is a dict with keys: ", + "", + " - data (list of dicts; optional):", + " data is a collection of traces. ", + "", + " - layout (dict; optional):", + " layout describes the rest of the figure. ", + "", + " - fontSize (number; optional)", + "", + "- optionalObjectWithShapeAndNestedDescription (dict; optional) ", + "", + " `optionalObjectWithShapeAndNestedDescription` is a dict with keys: ", + "", + " - color (string; optional) ", + "", + " - figure (dict; optional):", + " Figure is a plotly graph object. ", + "", + " `figure` is a dict with keys: ", + "", + " - data (list of dicts; optional):", + " data is a collection of traces. ", + "", + " - layout (dict; optional):", + " layout describes the rest of the figure. ", + "", + " - fontSize (number; optional)", + "", "- optionalString (string; default 'hello world')", - "- optionalNode (a list of or a singular dash component, " - "string or number; optional)", - "- optionalElement (dash component; optional)", - "- optionalEnum (a value equal to: 'News', 'Photos'; optional)", + "", "- optionalUnion (string | number; optional)", - "- optionalArrayOf (list of numbers; optional)", - "- optionalObjectOf (dict with strings as keys and values " - "of type number; optional)", - "- optionalObjectWithExactAndNestedDescription (dict; optional): " - "optionalObjectWithExactAndNestedDescription has the " - "following type: dict containing keys " - "'color', 'fontSize', 'figure'.", - "Those keys have the following types:", - " - color (string; optional)", - " - fontSize (number; optional)", - " - figure (dict; optional): Figure is a plotly graph object. " - "figure has the following type: dict containing " - "keys 'data', 'layout'.", - "Those keys have the following types:", - " - data (list of dicts; optional): data is a collection of traces", - " - layout (dict; optional): layout describes " "the rest of the figure", - "- optionalObjectWithShapeAndNestedDescription (dict; optional): " - "optionalObjectWithShapeAndNestedDescription has the " - "following type: dict containing keys " - "'color', 'fontSize', 'figure'.", - "Those keys have the following types:", - " - color (string; optional)", - " - fontSize (number; optional)", - " - figure (dict; optional): Figure is a plotly graph object. " - "figure has the following type: dict containing " - "keys 'data', 'layout'.", - "Those keys have the following types:", - " - data (list of dicts; optional): data is a collection of traces", - " - layout (dict; optional): layout describes " "the rest of the figure", - "- optionalAny (boolean | number | string | dict | " "list; optional)", - "- customProp (optional)", - "- customArrayProp (list; optional)", - "- data-* (string; optional)", - "- aria-* (string; optional)", - "- in (string; optional)", - "- id (string; optional)", ] diff --git a/tests/unit/development/metadata_test.py b/tests/unit/development/metadata_test.py index 042b443851..ada13d960d 100644 --- a/tests/unit/development/metadata_test.py +++ b/tests/unit/development/metadata_test.py @@ -9,57 +9,97 @@ class Table(Component): It's multiple lines long. Keyword arguments: + - children (a list of or a singular dash component, string or number; optional) -- optionalArray (list; optional): Description of optionalArray + +- id (string; optional) + +- aria-* (string; optional) + +- customArrayProp (list; optional) + +- customProp (optional) + +- data-* (string; optional) + +- in (string; optional) + +- optionalAny (boolean | number | string | dict | list; optional) + +- optionalArray (list; optional): + Description of optionalArray. + +- optionalArrayOf (list of numbers; optional) + - optionalBool (boolean; optional) -- optionalNumber (number; default 42) -- optionalObject (dict; optional) -- optionalString (string; default 'hello world') -- optionalNode (a list of or a singular dash component, string or number; optional) + - optionalElement (dash component; optional) + - optionalEnum (a value equal to: 'News', 'Photos'; optional) -- optionalUnion (string | number; optional) -- optionalArrayOf (list of numbers; optional) + +- optionalNode (a list of or a singular dash component, string or number; optional) + +- optionalNumber (number; default 42) + +- optionalObject (dict; optional) + - optionalObjectOf (dict with strings as keys and values of type number; optional) -- optionalObjectWithExactAndNestedDescription (dict; optional): optionalObjectWithExactAndNestedDescription has the following type: dict containing keys 'color', 'fontSize', 'figure'. -Those keys have the following types: - - color (string; optional) - - fontSize (number; optional) - - figure (dict; optional): Figure is a plotly graph object. figure has the following type: dict containing keys 'data', 'layout'. -Those keys have the following types: - - data (list of dicts; optional): data is a collection of traces - - layout (dict; optional): layout describes the rest of the figure -- optionalObjectWithShapeAndNestedDescription (dict; optional): optionalObjectWithShapeAndNestedDescription has the following type: dict containing keys 'color', 'fontSize', 'figure'. -Those keys have the following types: - - color (string; optional) - - fontSize (number; optional) - - figure (dict; optional): Figure is a plotly graph object. figure has the following type: dict containing keys 'data', 'layout'. -Those keys have the following types: - - data (list of dicts; optional): data is a collection of traces - - layout (dict; optional): layout describes the rest of the figure -- optionalAny (boolean | number | string | dict | list; optional) -- customProp (optional) -- customArrayProp (list; optional) -- data-* (string; optional) -- aria-* (string; optional) -- in (string; optional) -- id (string; optional)""" + +- optionalObjectWithExactAndNestedDescription (dict; optional) + + `optionalObjectWithExactAndNestedDescription` is a dict with keys: + + - color (string; optional) + + - figure (dict; optional): + Figure is a plotly graph object. + + `figure` is a dict with keys: + + - data (list of dicts; optional): + data is a collection of traces. + + - layout (dict; optional): + layout describes the rest of the figure. + + - fontSize (number; optional) + +- optionalObjectWithShapeAndNestedDescription (dict; optional) + + `optionalObjectWithShapeAndNestedDescription` is a dict with keys: + + - color (string; optional) + + - figure (dict; optional): + Figure is a plotly graph object. + + `figure` is a dict with keys: + + - data (list of dicts; optional): + data is a collection of traces. + + - layout (dict; optional): + layout describes the rest of the figure. + + - fontSize (number; optional) + +- optionalString (string; default 'hello world') + +- optionalUnion (string | number; optional)""" @_explicitize_args def __init__(self, children=None, optionalArray=Component.UNDEFINED, optionalBool=Component.UNDEFINED, optionalFunc=Component.UNDEFINED, optionalNumber=Component.UNDEFINED, optionalObject=Component.UNDEFINED, optionalString=Component.UNDEFINED, optionalSymbol=Component.UNDEFINED, optionalNode=Component.UNDEFINED, optionalElement=Component.UNDEFINED, optionalMessage=Component.UNDEFINED, optionalEnum=Component.UNDEFINED, optionalUnion=Component.UNDEFINED, optionalArrayOf=Component.UNDEFINED, optionalObjectOf=Component.UNDEFINED, optionalObjectWithExactAndNestedDescription=Component.UNDEFINED, optionalObjectWithShapeAndNestedDescription=Component.UNDEFINED, optionalAny=Component.UNDEFINED, customProp=Component.UNDEFINED, customArrayProp=Component.UNDEFINED, id=Component.UNDEFINED, **kwargs): - self._prop_names = ['children', 'optionalArray', 'optionalBool', 'optionalNumber', 'optionalObject', 'optionalString', 'optionalNode', 'optionalElement', 'optionalEnum', 'optionalUnion', 'optionalArrayOf', 'optionalObjectOf', 'optionalObjectWithExactAndNestedDescription', 'optionalObjectWithShapeAndNestedDescription', 'optionalAny', 'customProp', 'customArrayProp', 'data-*', 'aria-*', 'in', 'id'] + self._prop_names = ['children', 'id', 'aria-*', 'customArrayProp', 'customProp', 'data-*', 'in', 'optionalAny', 'optionalArray', 'optionalArrayOf', 'optionalBool', 'optionalElement', 'optionalEnum', 'optionalNode', 'optionalNumber', 'optionalObject', 'optionalObjectOf', 'optionalObjectWithExactAndNestedDescription', 'optionalObjectWithShapeAndNestedDescription', 'optionalString', 'optionalUnion'] self._type = 'Table' self._namespace = 'TableComponents' self._valid_wildcard_attributes = ['data-', 'aria-'] - self.available_properties = ['children', 'optionalArray', 'optionalBool', 'optionalNumber', 'optionalObject', 'optionalString', 'optionalNode', 'optionalElement', 'optionalEnum', 'optionalUnion', 'optionalArrayOf', 'optionalObjectOf', 'optionalObjectWithExactAndNestedDescription', 'optionalObjectWithShapeAndNestedDescription', 'optionalAny', 'customProp', 'customArrayProp', 'data-*', 'aria-*', 'in', 'id'] + self.available_properties = ['children', 'id', 'aria-*', 'customArrayProp', 'customProp', 'data-*', 'in', 'optionalAny', 'optionalArray', 'optionalArrayOf', 'optionalBool', 'optionalElement', 'optionalEnum', 'optionalNode', 'optionalNumber', 'optionalObject', 'optionalObjectOf', 'optionalObjectWithExactAndNestedDescription', 'optionalObjectWithShapeAndNestedDescription', 'optionalString', 'optionalUnion'] self.available_wildcard_properties = ['data-', 'aria-'] - _explicit_args = kwargs.pop('_explicit_args') _locals = locals() _locals.update(kwargs) # For wildcard attrs 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) \ No newline at end of file + super(Table, self).__init__(children=children, **args) diff --git a/tests/unit/development/test_flow_metadata_conversions.py b/tests/unit/development/test_flow_metadata_conversions.py index 822df417e9..32386cbcdb 100644 --- a/tests/unit/development/test_flow_metadata_conversions.py +++ b/tests/unit/development/test_flow_metadata_conversions.py @@ -1,3 +1,4 @@ +# flake8: ignore=E501 import json import os from collections import OrderedDict @@ -27,17 +28,19 @@ "optionalSignature(shape)", "\n".join( [ - "dict containing keys 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - "Those keys have the following types:", - "- checked (boolean; optional)", - "- children (a list of or a singular dash component, string or number; optional)", - "- customData (bool | number | str | dict | list; required): A test description", - "- disabled (boolean; optional)", - "- label (string; optional)", - "- primaryText (string; required): Another test description", - "- secondaryText (string; optional)", - "- style (dict; optional)", - "- value (bool | number | str | dict | list; required)", + "dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", + "Those keys have the following types:\n", + " - checked (boolean; optional)\n", + " - children (a list of or a singular dash component, string or number; optional)\n", + " - customData (bool | number | str | dict | list; required):\n" + " A test description. \n", + " - disabled (boolean; optional)\n", + " - label (string; optional)\n", + " - primaryText (string; required):\n" + " Another test description. \n", + " - secondaryText (string; optional)\n", + " - style (dict; optional)\n", + " - value (bool | number | str | dict | list; required)", ] ), ], @@ -45,20 +48,21 @@ "requiredNested", "\n".join( [ - "dict containing keys 'customData', 'value'.", - "Those keys have the following types:", - "- customData (dict; required): customData has the following type: dict containing keys 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - " Those keys have the following types:", - " - checked (boolean; optional)", - " - children (a list of or a singular dash component, string or number; optional)", - " - customData (bool | number | str | dict | list; required)", - " - disabled (boolean; optional)", - " - label (string; optional)", - " - primaryText (string; required)", - " - secondaryText (string; optional)", - " - style (dict; optional)", - " - value (bool | number | str | dict | list; required)", - "- value (bool | number | str | dict | list; required)", + "dict with keys: 'customData', 'value'.", + "Those keys have the following types:\n", + " - customData (dict; required) \n\n" + " `customData` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", + " Those keys have the following types:\n", + " - checked (boolean; optional)\n", + " - children (a list of or a singular dash component, string or number; optional)\n", + " - customData (bool | number | str | dict | list; required)\n", + " - disabled (boolean; optional)\n", + " - label (string; optional)\n", + " - primaryText (string; required)\n", + " - secondaryText (string; optional)\n", + " - style (dict; optional)\n", + " - value (bool | number | str | dict | list; required)\n", + " - value (bool | number | str | dict | list; required)", ] ), ], @@ -71,46 +75,81 @@ "It's multiple lines long.", "", "Keyword arguments:", - "- requiredString (string; required): A required string", - "- optionalString (string; default ''): A string that isn't required.", - "- optionalBoolean (boolean; default False): A boolean test", - "- optionalNode (a list of or a singular dash component, string or number; optional): " - "A node test", - "- optionalArray (list; optional): An array test with a particularly ", - "long description that covers several lines. It includes the newline character ", - "and should span 3 lines in total.", - "- requiredUnion (string | number; required)", - "- optionalSignature(shape) (dict; optional): This is a test of an object's shape. " - "optionalSignature(shape) has the following type: dict containing keys 'checked', " - "'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', " - "'style', 'value'.", - " Those keys have the following types:", - " - checked (boolean; optional)", - " - children (a list of or a singular dash component, string or number; optional)", - " - customData (bool | number | str | dict | list; required): A test description", - " - disabled (boolean; optional)", - " - label (string; optional)", - " - primaryText (string; required): Another test description", - " - secondaryText (string; optional)", - " - style (dict; optional)", - " - value (bool | number | str | dict | list; required)", - "- requiredNested (dict; required): requiredNested has the following type: dict containing " - "keys 'customData', 'value'.", - " Those keys have the following types:", - " - customData (dict; required): customData has the following type: dict containing " - "keys 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', " - "'secondaryText', 'style', 'value'.", - " Those keys have the following types:", + "", + "- optionalArray (list; optional):", + " An array test with a particularly long description that covers", + " several lines. It includes the newline character and should span", + " 3 lines in total. ", + "", + "- optionalBoolean (boolean; default False):", + " A boolean test. ", + "", + "- optionalNode (a list of or a singular dash component, string or number; optional):", + " A node test. ", + "", + "- optionalSignature(shape) (dict; optional):", + " This is a test of an object's shape. ", + "", + " `optionalSignature(shape)` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", + "Those keys have the following types:", + "", " - checked (boolean; optional)", + "", " - children (a list of or a singular dash component, string or number; optional)", - " - customData (bool | number | str | dict | list; required)", + "", + " - customData (bool | number | str | dict | list; required):", + " A test description. ", + "", " - disabled (boolean; optional)", + "", " - label (string; optional)", - " - primaryText (string; required)", + "", + " - primaryText (string; required):", + " Another test description. ", + "", " - secondaryText (string; optional)", + "", " - style (dict; optional)", + "", " - value (bool | number | str | dict | list; required)", - " - value (bool | number | str | dict | list; required)", + "", + "- optionalString (string; default ''):", + " A string that isn't required. ", + "", + "- requiredNested (dict; required) ", + "", + " `requiredNested` is a dict with keys: 'customData', 'value'.", + "Those keys have the following types:", + "", + " - customData (dict; required) ", + "", + " `customData` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", + " Those keys have the following types:", + "", + " - checked (boolean; optional)", + "", + " - children (a list of or a singular dash component, string or number; optional)", + "", + " - customData (bool | number | str | dict | list; required)", + "", + " - disabled (boolean; optional)", + "", + " - label (string; optional)", + "", + " - primaryText (string; required)", + "", + " - secondaryText (string; optional)", + "", + " - style (dict; optional)", + "", + " - value (bool | number | str | dict | list; required)", + "", + " - value (bool | number | str | dict | list; required)", + "", + "- requiredString (string; required):", + " A required string. ", + "", + "- requiredUnion (string | number; required)", ] @@ -135,7 +174,6 @@ def test_docstring(load_test_flow_metadata_json): def test_docgen_to_python_args(load_test_flow_metadata_json): props = load_test_flow_metadata_json["props"] - for prop_name, prop in list(props.items()): assert ( js_to_py_type(prop["flowType"], is_flow_type=True) diff --git a/tests/unit/development/test_generate_class.py b/tests/unit/development/test_generate_class.py index 49ba7d5684..2c7c0de494 100644 --- a/tests/unit/development/test_generate_class.py +++ b/tests/unit/development/test_generate_class.py @@ -83,7 +83,7 @@ def test_repr_multiple_arguments(component_class): # Note how the order in which keyword arguments are supplied is # not always equal to the order in the repr of the component c = component_class(id="my id", optionalArray=[1, 2, 3]) - assert repr(c) == "Table(optionalArray=[1, 2, 3], id='my id')" + assert repr(c) == "Table(id='my id', optionalArray=[1, 2, 3])" def test_repr_nested_arguments(component_class): diff --git a/tests/unit/development/test_generate_class_file.py b/tests/unit/development/test_generate_class_file.py index eecc2ba34e..f4cb1b8bd8 100644 --- a/tests/unit/development/test_generate_class_file.py +++ b/tests/unit/development/test_generate_class_file.py @@ -9,7 +9,7 @@ generate_class_string, generate_class_file, ) -from . import _dir, has_trailing_space +from . import _dir # Import string not included in generated class string import_string = ( @@ -68,8 +68,6 @@ def test_class_string(expected_class_string, component_class_string): ) ) - assert not has_trailing_space(component_class_string) - def test_class_file(expected_class_string, written_class_string): assert not list( @@ -77,4 +75,3 @@ def test_class_file(expected_class_string, written_class_string): expected_class_string.splitlines(), written_class_string.splitlines() ) ) - assert not has_trailing_space(written_class_string) diff --git a/tests/unit/development/test_metadata_conversions.py b/tests/unit/development/test_metadata_conversions.py index 008efebe66..4cf4aa3b18 100644 --- a/tests/unit/development/test_metadata_conversions.py +++ b/tests/unit/development/test_metadata_conversions.py @@ -29,15 +29,18 @@ "optionalObjectWithExactAndNestedDescription", "\n".join( [ - "dict containing keys 'color', 'fontSize', 'figure'.", - "Those keys have the following types:", - " - color (string; optional)", - " - fontSize (number; optional)", - " - figure (dict; optional): Figure is a plotly graph object. figure has the following type: dict containing keys 'data', 'layout'.", + "dict with keys: \n", + " - color (string; optional) \n", + " - figure (dict; optional):", + " Figure is a plotly graph object. \n", + " `figure` is a dict with keys: \n", # noqa: E501 - "Those keys have the following types:", - " - data (list of dicts; optional): data is a collection of traces", - " - layout (dict; optional): layout describes the rest of the figure", # noqa: E501 + " - data (list of dicts; optional):", + " data is a collection of traces. \n", + " - layout (dict; optional):", + " layout describes the rest of the figure. \n", + # noqa: E501 + " - fontSize (number; optional)", ] ), ], @@ -45,16 +48,19 @@ "optionalObjectWithShapeAndNestedDescription", "\n".join( [ - "dict containing keys 'color', 'fontSize', 'figure'.", - "Those keys have the following types:", - " - color (string; optional)", - " - fontSize (number; optional)", - " - figure (dict; optional): Figure is a plotly graph object. figure has the following type: dict containing keys 'data', 'layout'.", + "dict with keys: \n", + " - color (string; optional) \n", + " - figure (dict; optional):", + " Figure is a plotly graph object. \n", + " `figure` is a dict with keys: \n", # noqa: E501 - "Those keys have the following types:", - " - data (list of dicts; optional): data is a collection of traces", - " - layout (dict; optional): layout describes the rest of the figure", # noqa: E501 - ] + " - data (list of dicts; optional):", + " data is a collection of traces. \n", + " - layout (dict; optional):", + " layout describes the rest of the figure. \n", + # noqa: E501 + " - fontSize (number; optional)", + ], ), ], ["optionalAny", "boolean | number | string | dict | list"], From f224c17964d786856af4ee6d278eb767c3a20d1c Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Sat, 23 Jan 2021 19:21:27 -0700 Subject: [PATCH 4/7] more changes for python 2.7 tests --- dash/development/_py_components_generation.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index 59c8e48c95..804c664e12 100644 --- 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, indent +from textwrap import fill from dash.development.base_component import _explicitize_args from dash.exceptions import NonExistentEventException @@ -455,10 +455,10 @@ def create_prop_docstring( dict_part2 = "".join([desc_indent, "Or", dict_part2]) dict_descr = "{} \n\n {}".format(dict_part1, dict_part2) - # ensures indent is correct + # ensures indent is correct if there is a second nested dict current_indent = dict_descr.lstrip("\n").find("-") - if current_indent == len(indent_spacing): - dict_descr = indent(dict_descr, " ") + if current_indent == len(indent_spacing) + 4: + dict_descr = "".join(" " + line for line in dict_descr.splitlines(True)) return ( "\n{indent_spacing}- {name} ({dict_or_list}; {is_required}){colon}" @@ -552,7 +552,6 @@ def shape_or_exact(): def map_js_to_py_types_flow_types(type_object): """Mapping from the Flow js types to the Python type.""" - return dict( array=lambda: "list", boolean=lambda: "boolean", From ad0f6383a337293c4ec0b6caabe7e3b3331b2920 Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Sat, 30 Jan 2021 09:46:56 -0700 Subject: [PATCH 5/7] updates for flow types --- dash/development/_py_components_generation.py | 32 +++---- .../test_flow_metadata_conversions.py | 87 +++++++++---------- 2 files changed, 54 insertions(+), 65 deletions(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index 804c664e12..7224f087fe 100644 --- a/dash/development/_py_components_generation.py +++ b/dash/development/_py_components_generation.py @@ -455,7 +455,7 @@ def create_prop_docstring( dict_part2 = "".join([desc_indent, "Or", dict_part2]) dict_descr = "{} \n\n {}".format(dict_part1, dict_part2) - # ensures indent is correct if there is a second nested dict + # ensures indent is correct if there is a second nested list of dicts current_indent = dict_descr.lstrip("\n").find("-") if current_indent == len(indent_spacing) + 4: dict_descr = "".join(" " + line for line in dict_descr.splitlines(True)) @@ -576,24 +576,18 @@ def map_js_to_py_types_flow_types(type_object): else "" ), # React's PropTypes.shape - signature=lambda indent_num: "dict with keys: {}.\n{}".format( - ", ".join( - "'{}'".format(d["key"]) for d in type_object["signature"]["properties"] - ), - "{}Those keys have the following types:\n{}".format( - " " * indent_num, - "\n".join( - create_prop_docstring( - prop_name=prop["key"], - type_object=prop["value"], - required=prop["value"]["required"], - description=prop["value"].get("description", ""), - default=prop.get("defaultValue"), - indent_num=indent_num + 2, - is_flow_type=True, - ) - for prop in type_object["signature"]["properties"] - ), + signature=lambda indent_num: "dict with keys: \n{}".format( + " \n".join( + create_prop_docstring( + prop_name=prop["key"], + type_object=prop["value"], + required=prop["value"]["required"], + description=prop["value"].get("description", ""), + default=prop.get("defaultValue"), + indent_num=indent_num + 2, + is_flow_type=True, + ) + for prop in type_object["signature"]["properties"] ), ), ) diff --git a/tests/unit/development/test_flow_metadata_conversions.py b/tests/unit/development/test_flow_metadata_conversions.py index 32386cbcdb..d1befa46b7 100644 --- a/tests/unit/development/test_flow_metadata_conversions.py +++ b/tests/unit/development/test_flow_metadata_conversions.py @@ -28,18 +28,17 @@ "optionalSignature(shape)", "\n".join( [ - "dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - "Those keys have the following types:\n", - " - checked (boolean; optional)\n", - " - children (a list of or a singular dash component, string or number; optional)\n", + "dict with keys: \n", + " - checked (boolean; optional) \n", + " - children (a list of or a singular dash component, string or number; optional) \n", " - customData (bool | number | str | dict | list; required):\n" - " A test description. \n", - " - disabled (boolean; optional)\n", - " - label (string; optional)\n", + " A test description. \n", + " - disabled (boolean; optional) \n", + " - label (string; optional) \n", " - primaryText (string; required):\n" - " Another test description. \n", - " - secondaryText (string; optional)\n", - " - style (dict; optional)\n", + " Another test description. \n", + " - secondaryText (string; optional) \n", + " - style (dict; optional) \n", " - value (bool | number | str | dict | list; required)", ] ), @@ -48,20 +47,18 @@ "requiredNested", "\n".join( [ - "dict with keys: 'customData', 'value'.", - "Those keys have the following types:\n", + "dict with keys: \n", " - customData (dict; required) \n\n" - " `customData` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - " Those keys have the following types:\n", - " - checked (boolean; optional)\n", - " - children (a list of or a singular dash component, string or number; optional)\n", - " - customData (bool | number | str | dict | list; required)\n", - " - disabled (boolean; optional)\n", - " - label (string; optional)\n", - " - primaryText (string; required)\n", - " - secondaryText (string; optional)\n", - " - style (dict; optional)\n", - " - value (bool | number | str | dict | list; required)\n", + " `customData` is a dict with keys: \n", + " - checked (boolean; optional) \n", + " - children (a list of or a singular dash component, string or number; optional) \n", + " - customData (bool | number | str | dict | list; required) \n", + " - disabled (boolean; optional) \n", + " - label (string; optional) \n", + " - primaryText (string; required) \n", + " - secondaryText (string; optional) \n", + " - style (dict; optional) \n", + " - value (bool | number | str | dict | list; required) \n", " - value (bool | number | str | dict | list; required)", ] ), @@ -90,26 +87,25 @@ "- optionalSignature(shape) (dict; optional):", " This is a test of an object's shape. ", "", - " `optionalSignature(shape)` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - "Those keys have the following types:", + " `optionalSignature(shape)` is a dict with keys: ", "", - " - checked (boolean; optional)", + " - checked (boolean; optional) ", "", - " - children (a list of or a singular dash component, string or number; optional)", + " - children (a list of or a singular dash component, string or number; optional) ", "", " - customData (bool | number | str | dict | list; required):", - " A test description. ", + " A test description. ", "", - " - disabled (boolean; optional)", + " - disabled (boolean; optional) ", "", - " - label (string; optional)", + " - label (string; optional) ", "", " - primaryText (string; required):", - " Another test description. ", + " Another test description. ", "", - " - secondaryText (string; optional)", + " - secondaryText (string; optional) ", "", - " - style (dict; optional)", + " - style (dict; optional) ", "", " - value (bool | number | str | dict | list; required)", "", @@ -118,31 +114,29 @@ "", "- requiredNested (dict; required) ", "", - " `requiredNested` is a dict with keys: 'customData', 'value'.", - "Those keys have the following types:", + " `requiredNested` is a dict with keys: ", "", " - customData (dict; required) ", "", - " `customData` is a dict with keys: 'checked', 'children', 'customData', 'disabled', 'label', 'primaryText', 'secondaryText', 'style', 'value'.", - " Those keys have the following types:", + " `customData` is a dict with keys: ", "", - " - checked (boolean; optional)", + " - checked (boolean; optional) ", "", - " - children (a list of or a singular dash component, string or number; optional)", + " - children (a list of or a singular dash component, string or number; optional) ", "", - " - customData (bool | number | str | dict | list; required)", + " - customData (bool | number | str | dict | list; required) ", "", - " - disabled (boolean; optional)", + " - disabled (boolean; optional) ", "", - " - label (string; optional)", + " - label (string; optional) ", "", - " - primaryText (string; required)", + " - primaryText (string; required) ", "", - " - secondaryText (string; optional)", + " - secondaryText (string; optional) ", "", - " - style (dict; optional)", + " - style (dict; optional) ", "", - " - value (bool | number | str | dict | list; required)", + " - value (bool | number | str | dict | list; required) ", "", " - value (bool | number | str | dict | list; required)", "", @@ -168,6 +162,7 @@ def test_docstring(load_test_flow_metadata_json): load_test_flow_metadata_json["props"], load_test_flow_metadata_json["description"], ) + print(docstring.splitlines()) prohibit_events(load_test_flow_metadata_json["props"]), assert not list(unified_diff(expected_doc, docstring.splitlines())) From c593ea0543711841816c36e7f9195e7a6e75e30a Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Sun, 31 Jan 2021 12:22:25 -0700 Subject: [PATCH 6/7] remove trailing whitespace --- dash/development/_py_components_generation.py | 25 ++--- tests/unit/development/__init__.py | 30 +++--- tests/unit/development/metadata_test.py | 30 +++--- .../test_flow_metadata_conversions.py | 98 +++++++++---------- .../development/test_generate_class_file.py | 4 +- .../development/test_metadata_conversions.py | 24 ++--- 6 files changed, 107 insertions(+), 104 deletions(-) diff --git a/dash/development/_py_components_generation.py b/dash/development/_py_components_generation.py index 7224f087fe..a9ade10760 100644 --- a/dash/development/_py_components_generation.py +++ b/dash/development/_py_components_generation.py @@ -429,7 +429,7 @@ def create_prop_docstring( break_long_words=False, break_on_hyphens=False, ) - description = "\n{} ".format(description) if description else "" + description = "\n{}".format(description) if description else "" colon = ":" if description else "" description = fix_keywords(description) @@ -450,20 +450,21 @@ def create_prop_docstring( # captures optional nested dict description and puts the "or" condition on a new line if "| dict with keys:" in dict_descr: - dict_part1, dict_part2 = dict_descr.split("|", 1) - dict_part1 = dict_part1 + " " + dict_part1, dict_part2 = dict_descr.split(" |", 1) dict_part2 = "".join([desc_indent, "Or", dict_part2]) - dict_descr = "{} \n\n {}".format(dict_part1, dict_part2) + dict_descr = "{}\n\n {}".format(dict_part1, dict_part2) # ensures indent is correct if there is a second nested list of dicts current_indent = dict_descr.lstrip("\n").find("-") - if current_indent == len(indent_spacing) + 4: - dict_descr = "".join(" " + line for line in dict_descr.splitlines(True)) + if current_indent == len(indent_spacing): + dict_descr = "".join( + "\n\n " + line for line in dict_descr.splitlines() if line != "" + ) return ( "\n{indent_spacing}- {name} ({dict_or_list}; {is_required}){colon}" - "{description} " - "\n\n{intro} {dict_descr}".format( + "{description}" + "\n\n{intro}{dict_descr}".format( indent_spacing=indent_spacing, name=prop_name, colon=colon, @@ -491,8 +492,8 @@ def map_js_to_py_types_prop_types(type_object, indent_num): """Mapping from the PropTypes js type object to the Python type.""" def shape_or_exact(): - return "dict with keys: \n{}".format( - " \n".join( + return "dict with keys:\n{}".format( + "\n".join( create_prop_docstring( prop_name=prop_name, type_object=prop, @@ -576,8 +577,8 @@ def map_js_to_py_types_flow_types(type_object): else "" ), # React's PropTypes.shape - signature=lambda indent_num: "dict with keys: \n{}".format( - " \n".join( + signature=lambda indent_num: "dict with keys:\n{}".format( + "\n".join( create_prop_docstring( prop_name=prop["key"], type_object=prop["value"], diff --git a/tests/unit/development/__init__.py b/tests/unit/development/__init__.py index 6b1016c438..9ec687e80d 100644 --- a/tests/unit/development/__init__.py +++ b/tests/unit/development/__init__.py @@ -31,7 +31,7 @@ def has_trailing_space(s): "- optionalAny (boolean | number | string | dict | list; optional)", "", "- optionalArray (list; optional):", - " Description of optionalArray. ", + " Description of optionalArray.", "", "- optionalArrayOf (list of numbers; optional)", "", @@ -49,41 +49,41 @@ def has_trailing_space(s): "", "- optionalObjectOf (dict with strings as keys and values of type number; optional)", "", - "- optionalObjectWithExactAndNestedDescription (dict; optional) ", + "- optionalObjectWithExactAndNestedDescription (dict; optional)", "", - " `optionalObjectWithExactAndNestedDescription` is a dict with keys: ", + " `optionalObjectWithExactAndNestedDescription` is a dict with keys:", "", - " - color (string; optional) ", + " - color (string; optional)", "", " - figure (dict; optional):", - " Figure is a plotly graph object. ", + " Figure is a plotly graph object.", "", - " `figure` is a dict with keys: ", + " `figure` is a dict with keys:", "", " - data (list of dicts; optional):", - " data is a collection of traces. ", + " data is a collection of traces.", "", " - layout (dict; optional):", - " layout describes the rest of the figure. ", + " layout describes the rest of the figure.", "", " - fontSize (number; optional)", "", - "- optionalObjectWithShapeAndNestedDescription (dict; optional) ", + "- optionalObjectWithShapeAndNestedDescription (dict; optional)", "", - " `optionalObjectWithShapeAndNestedDescription` is a dict with keys: ", + " `optionalObjectWithShapeAndNestedDescription` is a dict with keys:", "", - " - color (string; optional) ", + " - color (string; optional)", "", " - figure (dict; optional):", - " Figure is a plotly graph object. ", + " Figure is a plotly graph object.", "", - " `figure` is a dict with keys: ", + " `figure` is a dict with keys:", "", " - data (list of dicts; optional):", - " data is a collection of traces. ", + " data is a collection of traces.", "", " - layout (dict; optional):", - " layout describes the rest of the figure. ", + " layout describes the rest of the figure.", "", " - fontSize (number; optional)", "", diff --git a/tests/unit/development/metadata_test.py b/tests/unit/development/metadata_test.py index ada13d960d..c7440d2090 100644 --- a/tests/unit/development/metadata_test.py +++ b/tests/unit/development/metadata_test.py @@ -27,7 +27,7 @@ class Table(Component): - optionalAny (boolean | number | string | dict | list; optional) - optionalArray (list; optional): - Description of optionalArray. + Description of optionalArray. - optionalArrayOf (list of numbers; optional) @@ -45,41 +45,41 @@ class Table(Component): - optionalObjectOf (dict with strings as keys and values of type number; optional) -- optionalObjectWithExactAndNestedDescription (dict; optional) +- optionalObjectWithExactAndNestedDescription (dict; optional) - `optionalObjectWithExactAndNestedDescription` is a dict with keys: + `optionalObjectWithExactAndNestedDescription` is a dict with keys: - - color (string; optional) + - color (string; optional) - figure (dict; optional): - Figure is a plotly graph object. + Figure is a plotly graph object. - `figure` is a dict with keys: + `figure` is a dict with keys: - data (list of dicts; optional): - data is a collection of traces. + data is a collection of traces. - layout (dict; optional): - layout describes the rest of the figure. + layout describes the rest of the figure. - fontSize (number; optional) -- optionalObjectWithShapeAndNestedDescription (dict; optional) +- optionalObjectWithShapeAndNestedDescription (dict; optional) - `optionalObjectWithShapeAndNestedDescription` is a dict with keys: + `optionalObjectWithShapeAndNestedDescription` is a dict with keys: - - color (string; optional) + - color (string; optional) - figure (dict; optional): - Figure is a plotly graph object. + Figure is a plotly graph object. - `figure` is a dict with keys: + `figure` is a dict with keys: - data (list of dicts; optional): - data is a collection of traces. + data is a collection of traces. - layout (dict; optional): - layout describes the rest of the figure. + layout describes the rest of the figure. - fontSize (number; optional) diff --git a/tests/unit/development/test_flow_metadata_conversions.py b/tests/unit/development/test_flow_metadata_conversions.py index d1befa46b7..cf2b10d9e7 100644 --- a/tests/unit/development/test_flow_metadata_conversions.py +++ b/tests/unit/development/test_flow_metadata_conversions.py @@ -28,17 +28,17 @@ "optionalSignature(shape)", "\n".join( [ - "dict with keys: \n", - " - checked (boolean; optional) \n", - " - children (a list of or a singular dash component, string or number; optional) \n", + "dict with keys:\n", + " - checked (boolean; optional)\n", + " - children (a list of or a singular dash component, string or number; optional)\n", " - customData (bool | number | str | dict | list; required):\n" - " A test description. \n", - " - disabled (boolean; optional) \n", - " - label (string; optional) \n", + " A test description.\n", + " - disabled (boolean; optional)\n", + " - label (string; optional)\n", " - primaryText (string; required):\n" - " Another test description. \n", - " - secondaryText (string; optional) \n", - " - style (dict; optional) \n", + " Another test description.\n", + " - secondaryText (string; optional)\n", + " - style (dict; optional)\n", " - value (bool | number | str | dict | list; required)", ] ), @@ -47,18 +47,18 @@ "requiredNested", "\n".join( [ - "dict with keys: \n", - " - customData (dict; required) \n\n" - " `customData` is a dict with keys: \n", - " - checked (boolean; optional) \n", - " - children (a list of or a singular dash component, string or number; optional) \n", - " - customData (bool | number | str | dict | list; required) \n", - " - disabled (boolean; optional) \n", - " - label (string; optional) \n", - " - primaryText (string; required) \n", - " - secondaryText (string; optional) \n", - " - style (dict; optional) \n", - " - value (bool | number | str | dict | list; required) \n", + "dict with keys:\n", + " - customData (dict; required)\n\n" + " `customData` is a dict with keys:\n", + " - checked (boolean; optional)\n", + " - children (a list of or a singular dash component, string or number; optional)\n", + " - customData (bool | number | str | dict | list; required)\n", + " - disabled (boolean; optional)\n", + " - label (string; optional)\n", + " - primaryText (string; required)\n", + " - secondaryText (string; optional)\n", + " - style (dict; optional)\n", + " - value (bool | number | str | dict | list; required)\n", " - value (bool | number | str | dict | list; required)", ] ), @@ -76,72 +76,72 @@ "- optionalArray (list; optional):", " An array test with a particularly long description that covers", " several lines. It includes the newline character and should span", - " 3 lines in total. ", + " 3 lines in total.", "", "- optionalBoolean (boolean; default False):", - " A boolean test. ", + " A boolean test.", "", "- optionalNode (a list of or a singular dash component, string or number; optional):", - " A node test. ", + " A node test.", "", "- optionalSignature(shape) (dict; optional):", - " This is a test of an object's shape. ", + " This is a test of an object's shape.", "", - " `optionalSignature(shape)` is a dict with keys: ", + " `optionalSignature(shape)` is a dict with keys:", "", - " - checked (boolean; optional) ", + " - checked (boolean; optional)", "", - " - children (a list of or a singular dash component, string or number; optional) ", + " - children (a list of or a singular dash component, string or number; optional)", "", " - customData (bool | number | str | dict | list; required):", - " A test description. ", + " A test description.", "", - " - disabled (boolean; optional) ", + " - disabled (boolean; optional)", "", - " - label (string; optional) ", + " - label (string; optional)", "", " - primaryText (string; required):", - " Another test description. ", + " Another test description.", "", - " - secondaryText (string; optional) ", + " - secondaryText (string; optional)", "", - " - style (dict; optional) ", + " - style (dict; optional)", "", " - value (bool | number | str | dict | list; required)", "", "- optionalString (string; default ''):", - " A string that isn't required. ", + " A string that isn't required.", "", - "- requiredNested (dict; required) ", + "- requiredNested (dict; required)", "", - " `requiredNested` is a dict with keys: ", + " `requiredNested` is a dict with keys:", "", - " - customData (dict; required) ", + " - customData (dict; required)", "", - " `customData` is a dict with keys: ", + " `customData` is a dict with keys:", "", - " - checked (boolean; optional) ", + " - checked (boolean; optional)", "", - " - children (a list of or a singular dash component, string or number; optional) ", + " - children (a list of or a singular dash component, string or number; optional)", "", - " - customData (bool | number | str | dict | list; required) ", + " - customData (bool | number | str | dict | list; required)", "", - " - disabled (boolean; optional) ", + " - disabled (boolean; optional)", "", - " - label (string; optional) ", + " - label (string; optional)", "", - " - primaryText (string; required) ", + " - primaryText (string; required)", "", - " - secondaryText (string; optional) ", + " - secondaryText (string; optional)", "", - " - style (dict; optional) ", + " - style (dict; optional)", "", - " - value (bool | number | str | dict | list; required) ", + " - value (bool | number | str | dict | list; required)", "", " - value (bool | number | str | dict | list; required)", "", "- requiredString (string; required):", - " A required string. ", + " A required string.", "", "- requiredUnion (string | number; required)", ] diff --git a/tests/unit/development/test_generate_class_file.py b/tests/unit/development/test_generate_class_file.py index f4cb1b8bd8..b3ba34109b 100644 --- a/tests/unit/development/test_generate_class_file.py +++ b/tests/unit/development/test_generate_class_file.py @@ -9,7 +9,7 @@ generate_class_string, generate_class_file, ) -from . import _dir +from . import _dir, has_trailing_space # Import string not included in generated class string import_string = ( @@ -67,6 +67,7 @@ def test_class_string(expected_class_string, component_class_string): expected_class_string.splitlines(), component_class_string.splitlines() ) ) + assert not has_trailing_space(component_class_string) def test_class_file(expected_class_string, written_class_string): @@ -75,3 +76,4 @@ def test_class_file(expected_class_string, written_class_string): expected_class_string.splitlines(), written_class_string.splitlines() ) ) + assert not has_trailing_space(written_class_string) diff --git a/tests/unit/development/test_metadata_conversions.py b/tests/unit/development/test_metadata_conversions.py index 4cf4aa3b18..23203821bf 100644 --- a/tests/unit/development/test_metadata_conversions.py +++ b/tests/unit/development/test_metadata_conversions.py @@ -29,16 +29,16 @@ "optionalObjectWithExactAndNestedDescription", "\n".join( [ - "dict with keys: \n", - " - color (string; optional) \n", + "dict with keys:\n", + " - color (string; optional)\n", " - figure (dict; optional):", - " Figure is a plotly graph object. \n", - " `figure` is a dict with keys: \n", + " Figure is a plotly graph object.\n", + " `figure` is a dict with keys:\n", # noqa: E501 " - data (list of dicts; optional):", - " data is a collection of traces. \n", + " data is a collection of traces.\n", " - layout (dict; optional):", - " layout describes the rest of the figure. \n", + " layout describes the rest of the figure.\n", # noqa: E501 " - fontSize (number; optional)", ] @@ -48,16 +48,16 @@ "optionalObjectWithShapeAndNestedDescription", "\n".join( [ - "dict with keys: \n", - " - color (string; optional) \n", + "dict with keys:\n", + " - color (string; optional)\n", " - figure (dict; optional):", - " Figure is a plotly graph object. \n", - " `figure` is a dict with keys: \n", + " Figure is a plotly graph object.\n", + " `figure` is a dict with keys:\n", # noqa: E501 " - data (list of dicts; optional):", - " data is a collection of traces. \n", + " data is a collection of traces.\n", " - layout (dict; optional):", - " layout describes the rest of the figure. \n", + " layout describes the rest of the figure.\n", # noqa: E501 " - fontSize (number; optional)", ], From 67f8957eb0421865cafef08a731916700115d4b7 Mon Sep 17 00:00:00 2001 From: Ann Marie Ward Date: Mon, 1 Feb 2021 09:56:28 -0700 Subject: [PATCH 7/7] Changelog entry --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cba6cbe3a..7591df1008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to `dash` will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). +## UNRELEASED + +### Changed + +- [#1531](https://github.com/plotly/dash/pull/1531). Updates the format of the docstrings to make them easier to read in + the reference pages of Dash Docs and in the console. This also addresses [#1205](https://github.com/plotly/dash/issues/1205) + + ## [1.19.0] - 2021-01-19 ## Dash and Dash Renderer