550 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			550 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								# pylint: disable=consider-using-f-string
							 | 
						||
| 
								 | 
							
								# type: ignore
							 | 
						||
| 
								 | 
							
								import copy
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import shutil
							 | 
						||
| 
								 | 
							
								import warnings
							 | 
						||
| 
								 | 
							
								import sys
							 | 
						||
| 
								 | 
							
								import importlib
							 | 
						||
| 
								 | 
							
								import uuid
							 | 
						||
| 
								 | 
							
								import hashlib
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from ._all_keywords import julia_keywords
							 | 
						||
| 
								 | 
							
								from ._py_components_generation import reorder_props
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# uuid of DashBase Julia package.
							 | 
						||
| 
								 | 
							
								jl_dash_base_uuid = "03207cf0-e2b3-4b91-9ca8-690cf0fb507e"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# uuid of Dash Julia package. Used as base for component package uuid
							 | 
						||
| 
								 | 
							
								jl_dash_uuid = "1b08a953-4be3-4667-9a23-3db579824955"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Declaring longer string templates as globals to improve
							 | 
						||
| 
								 | 
							
								# readability, make method logic clearer to anyone inspecting
							 | 
						||
| 
								 | 
							
								# code below
							 | 
						||
| 
								 | 
							
								jl_component_string = '''
							 | 
						||
| 
								 | 
							
								export {funcname}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								    {funcname}(;kwargs...){children_signatures}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								{docstring}
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								function {funcname}(; kwargs...)
							 | 
						||
| 
								 | 
							
								        available_props = Symbol[{component_props}]
							 | 
						||
| 
								 | 
							
								        wild_props = Symbol[{wildcard_symbols}]
							 | 
						||
| 
								 | 
							
								        return Component("{funcname}", "{element_name}", "{module_name}", available_props, wild_props; kwargs...)
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								{children_definitions}
							 | 
						||
| 
								 | 
							
								'''  # noqa:E501
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_children_signatures = """
							 | 
						||
| 
								 | 
							
								    {funcname}(children::Any;kwargs...)
							 | 
						||
| 
								 | 
							
								    {funcname}(children_maker::Function;kwargs...)
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_children_definitions = """
							 | 
						||
| 
								 | 
							
								{funcname}(children::Any; kwargs...) = {funcname}(;kwargs..., children = children)
							 | 
						||
| 
								 | 
							
								{funcname}(children_maker::Function; kwargs...) = {funcname}(children_maker(); kwargs...)
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_package_file_string = """
							 | 
						||
| 
								 | 
							
								module {package_name}
							 | 
						||
| 
								 | 
							
								using {base_package}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const resources_path = realpath(joinpath( @__DIR__, "..", "deps"))
							 | 
						||
| 
								 | 
							
								const version = "{version}"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								{component_includes}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function __init__()
							 | 
						||
| 
								 | 
							
								    DashBase.register_package(
							 | 
						||
| 
								 | 
							
								        DashBase.ResourcePkg(
							 | 
						||
| 
								 | 
							
								            "{project_shortname}",
							 | 
						||
| 
								 | 
							
								            resources_path,
							 | 
						||
| 
								 | 
							
								            version = version,
							 | 
						||
| 
								 | 
							
								            [
							 | 
						||
| 
								 | 
							
								                {resources_dist}
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								end
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_projecttoml_string = """
							 | 
						||
| 
								 | 
							
								name = "{package_name}"
							 | 
						||
| 
								 | 
							
								uuid = "{package_uuid}"
							 | 
						||
| 
								 | 
							
								{authors}version = "{version}"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[deps]
							 | 
						||
| 
								 | 
							
								{base_package} = "{dash_uuid}"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								[compat]
							 | 
						||
| 
								 | 
							
								julia = "1.2"
							 | 
						||
| 
								 | 
							
								{base_package} = "{base_version}"
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_base_version = {
							 | 
						||
| 
								 | 
							
								    "Dash": "0.1.3, 1.0",
							 | 
						||
| 
								 | 
							
								    "DashBase": "0.1",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_component_include_string = 'include("jl/{name}.jl")'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								jl_resource_tuple_string = """DashBase.Resource(
							 | 
						||
| 
								 | 
							
								    relative_package_path = {relative_package_path},
							 | 
						||
| 
								 | 
							
								    external_url = {external_url},
							 | 
						||
| 
								 | 
							
								    dynamic = {dynamic},
							 | 
						||
| 
								 | 
							
								    async = {async_string},
							 | 
						||
| 
								 | 
							
								    type = :{type}
							 | 
						||
| 
								 | 
							
								)"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								core_packages = ["dash_html_components", "dash_core_components", "dash_table"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def jl_package_name(namestring):
							 | 
						||
| 
								 | 
							
								    s = namestring.split("_")
							 | 
						||
| 
								 | 
							
								    return "".join(w.capitalize() for w in s)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def stringify_wildcards(wclist, no_symbol=False):
							 | 
						||
| 
								 | 
							
								    if no_symbol:
							 | 
						||
| 
								 | 
							
								        wcstring = "|".join("{}-".format(item) for item in wclist)
							 | 
						||
| 
								 | 
							
								    else:
							 | 
						||
| 
								 | 
							
								        wcstring = ", ".join('Symbol("{}-")'.format(item) for item in wclist)
							 | 
						||
| 
								 | 
							
								    return wcstring
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_wildcards_jl(props):
							 | 
						||
| 
								 | 
							
								    return [key.replace("-*", "") for key in props if key.endswith("-*")]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_jl_prop_types(type_object):
							 | 
						||
| 
								 | 
							
								    """Mapping from the PropTypes js type object to the Julia type."""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def shape_or_exact():
							 | 
						||
| 
								 | 
							
								        return "lists containing elements {}.\n{}".format(
							 | 
						||
| 
								 | 
							
								            ", ".join("'{}'".format(t) for t in type_object["value"]),
							 | 
						||
| 
								 | 
							
								            "Those elements have the following types:\n{}".format(
							 | 
						||
| 
								 | 
							
								                "\n".join(
							 | 
						||
| 
								 | 
							
								                    create_prop_docstring_jl(
							 | 
						||
| 
								 | 
							
								                        prop_name=prop_name,
							 | 
						||
| 
								 | 
							
								                        type_object=prop,
							 | 
						||
| 
								 | 
							
								                        required=prop["required"],
							 | 
						||
| 
								 | 
							
								                        description=prop.get("description", ""),
							 | 
						||
| 
								 | 
							
								                        indent_num=1,
							 | 
						||
| 
								 | 
							
								                    )
							 | 
						||
| 
								 | 
							
								                    for prop_name, prop in type_object["value"].items()
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return dict(
							 | 
						||
| 
								 | 
							
								        array=lambda: "Array",
							 | 
						||
| 
								 | 
							
								        bool=lambda: "Bool",
							 | 
						||
| 
								 | 
							
								        number=lambda: "Real",
							 | 
						||
| 
								 | 
							
								        string=lambda: "String",
							 | 
						||
| 
								 | 
							
								        object=lambda: "Dict",
							 | 
						||
| 
								 | 
							
								        any=lambda: "Bool | Real | String | Dict | Array",
							 | 
						||
| 
								 | 
							
								        element=lambda: "dash component",
							 | 
						||
| 
								 | 
							
								        node=lambda: "a list of or a singular dash component, string or number",
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.oneOf
							 | 
						||
| 
								 | 
							
								        enum=lambda: "a value equal to: {}".format(
							 | 
						||
| 
								 | 
							
								            ", ".join("{}".format(str(t["value"])) for t in type_object["value"])
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.oneOfType
							 | 
						||
| 
								 | 
							
								        union=lambda: "{}".format(
							 | 
						||
| 
								 | 
							
								            " | ".join(
							 | 
						||
| 
								 | 
							
								                "{}".format(get_jl_type(subType))
							 | 
						||
| 
								 | 
							
								                for subType in type_object["value"]
							 | 
						||
| 
								 | 
							
								                if get_jl_type(subType) != ""
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.arrayOf
							 | 
						||
| 
								 | 
							
								        arrayOf=lambda: (
							 | 
						||
| 
								 | 
							
								            "Array"
							 | 
						||
| 
								 | 
							
								            + (
							 | 
						||
| 
								 | 
							
								                " of {}s".format(get_jl_type(type_object["value"]))
							 | 
						||
| 
								 | 
							
								                if get_jl_type(type_object["value"]) != ""
							 | 
						||
| 
								 | 
							
								                else ""
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.objectOf
							 | 
						||
| 
								 | 
							
								        objectOf=lambda: "Dict with Strings as keys and values of type {}".format(
							 | 
						||
| 
								 | 
							
								            get_jl_type(type_object["value"])
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.shape
							 | 
						||
| 
								 | 
							
								        shape=shape_or_exact,
							 | 
						||
| 
								 | 
							
								        # React's PropTypes.exact
							 | 
						||
| 
								 | 
							
								        exact=shape_or_exact,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    filtered_props = copy.deepcopy(props)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for arg_name, arg in list(filtered_props.items()):
							 | 
						||
| 
								 | 
							
								        if "type" not in arg and "flowType" not in arg:
							 | 
						||
| 
								 | 
							
								            filtered_props.pop(arg_name)
							 | 
						||
| 
								 | 
							
								            continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Filter out functions and instances --
							 | 
						||
| 
								 | 
							
								        if "type" in arg:  # These come from PropTypes
							 | 
						||
| 
								 | 
							
								            arg_type = arg["type"]["name"]
							 | 
						||
| 
								 | 
							
								            if arg_type in {"func", "symbol", "instanceOf"}:
							 | 
						||
| 
								 | 
							
								                filtered_props.pop(arg_name)
							 | 
						||
| 
								 | 
							
								        elif "flowType" in arg:  # These come from Flow & handled differently
							 | 
						||
| 
								 | 
							
								            arg_type_name = arg["flowType"]["name"]
							 | 
						||
| 
								 | 
							
								            if arg_type_name == "signature":
							 | 
						||
| 
								 | 
							
								                # This does the same as the PropTypes filter above, but "func"
							 | 
						||
| 
								 | 
							
								                # is under "type" if "name" is "signature" vs just in "name"
							 | 
						||
| 
								 | 
							
								                if "type" not in arg["flowType"] or arg["flowType"]["type"] != "object":
							 | 
						||
| 
								 | 
							
								                    filtered_props.pop(arg_name)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise ValueError
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return filtered_props
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def get_jl_type(type_object):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Convert JS types to Julia types for the component definition
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    type_object: dict
							 | 
						||
| 
								 | 
							
								        react-docgen-generated prop type dictionary
							 | 
						||
| 
								 | 
							
								    Returns
							 | 
						||
| 
								 | 
							
								    -------
							 | 
						||
| 
								 | 
							
								    str
							 | 
						||
| 
								 | 
							
								        Julia type string
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    js_type_name = type_object["name"]
							 | 
						||
| 
								 | 
							
								    js_to_jl_types = get_jl_prop_types(type_object=type_object)
							 | 
						||
| 
								 | 
							
								    if js_type_name in js_to_jl_types:
							 | 
						||
| 
								 | 
							
								        prop_type = js_to_jl_types[js_type_name]()
							 | 
						||
| 
								 | 
							
								        return prop_type
							 | 
						||
| 
								 | 
							
								    return ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def print_jl_type(typedata):
							 | 
						||
| 
								 | 
							
								    typestring = get_jl_type(typedata).capitalize()
							 | 
						||
| 
								 | 
							
								    if typestring:
							 | 
						||
| 
								 | 
							
								        typestring += ". "
							 | 
						||
| 
								 | 
							
								    return typestring
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def create_docstring_jl(component_name, props, description):
							 | 
						||
| 
								 | 
							
								    """Create the Dash component docstring.
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    component_name: str
							 | 
						||
| 
								 | 
							
								        Component name
							 | 
						||
| 
								 | 
							
								    props: dict
							 | 
						||
| 
								 | 
							
								        Dictionary with {propName: propMetadata} structure
							 | 
						||
| 
								 | 
							
								    description: str
							 | 
						||
| 
								 | 
							
								        Component description
							 | 
						||
| 
								 | 
							
								    Returns
							 | 
						||
| 
								 | 
							
								    -------
							 | 
						||
| 
								 | 
							
								    str
							 | 
						||
| 
								 | 
							
								        Dash component docstring
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    # Ensure props are ordered with children first
							 | 
						||
| 
								 | 
							
								    props = reorder_props(props=props)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return "A{n} {name} component.\n{description}\nKeyword arguments:\n{args}".format(
							 | 
						||
| 
								 | 
							
								        n="n" if component_name[0].lower() in "aeiou" else "",
							 | 
						||
| 
								 | 
							
								        name=component_name,
							 | 
						||
| 
								 | 
							
								        description=description,
							 | 
						||
| 
								 | 
							
								        args="\n".join(
							 | 
						||
| 
								 | 
							
								            create_prop_docstring_jl(
							 | 
						||
| 
								 | 
							
								                prop_name=p,
							 | 
						||
| 
								 | 
							
								                type_object=prop["type"] if "type" in prop else prop["flowType"],
							 | 
						||
| 
								 | 
							
								                required=prop["required"],
							 | 
						||
| 
								 | 
							
								                description=prop["description"],
							 | 
						||
| 
								 | 
							
								                indent_num=0,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            for p, prop in filter_props(props).items()
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def create_prop_docstring_jl(
							 | 
						||
| 
								 | 
							
								    prop_name,
							 | 
						||
| 
								 | 
							
								    type_object,
							 | 
						||
| 
								 | 
							
								    required,
							 | 
						||
| 
								 | 
							
								    description,
							 | 
						||
| 
								 | 
							
								    indent_num,
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Create the Dash component prop docstring
							 | 
						||
| 
								 | 
							
								    Parameters
							 | 
						||
| 
								 | 
							
								    ----------
							 | 
						||
| 
								 | 
							
								    prop_name: str
							 | 
						||
| 
								 | 
							
								        Name of the Dash component prop
							 | 
						||
| 
								 | 
							
								    type_object: dict
							 | 
						||
| 
								 | 
							
								        react-docgen-generated prop type dictionary
							 | 
						||
| 
								 | 
							
								    required: bool
							 | 
						||
| 
								 | 
							
								        Component is required?
							 | 
						||
| 
								 | 
							
								    description: str
							 | 
						||
| 
								 | 
							
								        Dash component description
							 | 
						||
| 
								 | 
							
								    indent_num: int
							 | 
						||
| 
								 | 
							
								        Number of indents to use for the context block
							 | 
						||
| 
								 | 
							
								        (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
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    jl_type_name = get_jl_type(type_object=type_object)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    indent_spacing = "  " * indent_num
							 | 
						||
| 
								 | 
							
								    if "\n" in jl_type_name:
							 | 
						||
| 
								 | 
							
								        return (
							 | 
						||
| 
								 | 
							
								            "{indent_spacing}- `{name}` ({is_required}): {description}. "
							 | 
						||
| 
								 | 
							
								            "{name} has the following type: {type}".format(
							 | 
						||
| 
								 | 
							
								                indent_spacing=indent_spacing,
							 | 
						||
| 
								 | 
							
								                name=prop_name,
							 | 
						||
| 
								 | 
							
								                type=jl_type_name,
							 | 
						||
| 
								 | 
							
								                description=description,
							 | 
						||
| 
								 | 
							
								                is_required="required" if required else "optional",
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    return "{indent_spacing}- `{name}` ({type}{is_required}){description}".format(
							 | 
						||
| 
								 | 
							
								        indent_spacing=indent_spacing,
							 | 
						||
| 
								 | 
							
								        name=prop_name,
							 | 
						||
| 
								 | 
							
								        type="{}; ".format(jl_type_name) if jl_type_name else "",
							 | 
						||
| 
								 | 
							
								        description=(": {}".format(description) if description != "" else ""),
							 | 
						||
| 
								 | 
							
								        is_required="required" if required else "optional",
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# this logic will permit passing blank Julia prefixes to
							 | 
						||
| 
								 | 
							
								# dash-generate-components, while also enforcing
							 | 
						||
| 
								 | 
							
								# lower case names for the resulting functions; if a prefix
							 | 
						||
| 
								 | 
							
								# is supplied, leave it as-is
							 | 
						||
| 
								 | 
							
								def format_fn_name(prefix, name):
							 | 
						||
| 
								 | 
							
								    if prefix:
							 | 
						||
| 
								 | 
							
								        return "{}_{}".format(prefix, name.lower())
							 | 
						||
| 
								 | 
							
								    return name.lower()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_metadata_strings(resources, metatype):
							 | 
						||
| 
								 | 
							
								    def nothing_or_string(v):
							 | 
						||
| 
								 | 
							
								        return '"{}"'.format(v) if v else "nothing"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return [
							 | 
						||
| 
								 | 
							
								        jl_resource_tuple_string.format(
							 | 
						||
| 
								 | 
							
								            relative_package_path=nothing_or_string(
							 | 
						||
| 
								 | 
							
								                resource.get("relative_package_path", "")
							 | 
						||
| 
								 | 
							
								            ),
							 | 
						||
| 
								 | 
							
								            external_url=nothing_or_string(resource.get("external_url", "")),
							 | 
						||
| 
								 | 
							
								            dynamic=str(resource.get("dynamic", "nothing")).lower(),
							 | 
						||
| 
								 | 
							
								            type=metatype,
							 | 
						||
| 
								 | 
							
								            async_string=":{}".format(str(resource.get("async")).lower())
							 | 
						||
| 
								 | 
							
								            if "async" in resource.keys()
							 | 
						||
| 
								 | 
							
								            else "nothing",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        for resource in resources
							 | 
						||
| 
								 | 
							
								    ]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def is_core_package(project_shortname):
							 | 
						||
| 
								 | 
							
								    return project_shortname in core_packages
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def base_package_name(project_shortname):
							 | 
						||
| 
								 | 
							
								    return "DashBase" if is_core_package(project_shortname) else "Dash"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def base_package_uid(project_shortname):
							 | 
						||
| 
								 | 
							
								    return jl_dash_base_uuid if is_core_package(project_shortname) else jl_dash_uuid
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_package_file(project_shortname, components, pkg_data, prefix):
							 | 
						||
| 
								 | 
							
								    package_name = jl_package_name(project_shortname)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    sys.path.insert(0, os.getcwd())
							 | 
						||
| 
								 | 
							
								    mod = importlib.import_module(project_shortname)
							 | 
						||
| 
								 | 
							
								    js_dist = getattr(mod, "_js_dist", [])
							 | 
						||
| 
								 | 
							
								    css_dist = getattr(mod, "_css_dist", [])
							 | 
						||
| 
								 | 
							
								    project_ver = pkg_data.get("version")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    resources_dist = ",\n".join(
							 | 
						||
| 
								 | 
							
								        generate_metadata_strings(js_dist, "js")
							 | 
						||
| 
								 | 
							
								        + generate_metadata_strings(css_dist, "css")
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    package_string = jl_package_file_string.format(
							 | 
						||
| 
								 | 
							
								        package_name=package_name,
							 | 
						||
| 
								 | 
							
								        component_includes="\n".join(
							 | 
						||
| 
								 | 
							
								            [
							 | 
						||
| 
								 | 
							
								                jl_component_include_string.format(
							 | 
						||
| 
								 | 
							
								                    name=format_fn_name(prefix, comp_name)
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                for comp_name in components
							 | 
						||
| 
								 | 
							
								            ]
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        resources_dist=resources_dist,
							 | 
						||
| 
								 | 
							
								        version=project_ver,
							 | 
						||
| 
								 | 
							
								        project_shortname=project_shortname,
							 | 
						||
| 
								 | 
							
								        base_package=base_package_name(project_shortname),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    file_path = os.path.join("src", package_name + ".jl")
							 | 
						||
| 
								 | 
							
								    with open(file_path, "w", encoding="utf-8") as f:
							 | 
						||
| 
								 | 
							
								        f.write(package_string)
							 | 
						||
| 
								 | 
							
								    print("Generated {}".format(file_path))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_toml_file(project_shortname, pkg_data):
							 | 
						||
| 
								 | 
							
								    package_author = pkg_data.get("author", "")
							 | 
						||
| 
								 | 
							
								    project_ver = pkg_data.get("version")
							 | 
						||
| 
								 | 
							
								    package_name = jl_package_name(project_shortname)
							 | 
						||
| 
								 | 
							
								    u = uuid.UUID(jl_dash_uuid)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    package_uuid = uuid.UUID(
							 | 
						||
| 
								 | 
							
								        hex=u.hex[:-12] + hashlib.sha256(package_name.encode("utf-8")).hexdigest()[-12:]
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    authors_string = (
							 | 
						||
| 
								 | 
							
								        'authors = ["{}"]\n'.format(package_author) if package_author else ""
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    base_package = base_package_name(project_shortname)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    toml_string = jl_projecttoml_string.format(
							 | 
						||
| 
								 | 
							
								        package_name=package_name,
							 | 
						||
| 
								 | 
							
								        package_uuid=package_uuid,
							 | 
						||
| 
								 | 
							
								        version=project_ver,
							 | 
						||
| 
								 | 
							
								        authors=authors_string,
							 | 
						||
| 
								 | 
							
								        base_package=base_package,
							 | 
						||
| 
								 | 
							
								        base_version=jl_base_version[base_package],
							 | 
						||
| 
								 | 
							
								        dash_uuid=base_package_uid(project_shortname),
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    file_path = "Project.toml"
							 | 
						||
| 
								 | 
							
								    with open(file_path, "w", encoding="utf-8") as f:
							 | 
						||
| 
								 | 
							
								        f.write(toml_string)
							 | 
						||
| 
								 | 
							
								    print("Generated {}".format(file_path))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_class_string(name, props, description, project_shortname, prefix):
							 | 
						||
| 
								 | 
							
								    # Ensure props are ordered with children first
							 | 
						||
| 
								 | 
							
								    filtered_props = reorder_props(filter_props(props))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    prop_keys = list(filtered_props.keys())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    docstring = (
							 | 
						||
| 
								 | 
							
								        create_docstring_jl(
							 | 
						||
| 
								 | 
							
								            component_name=name, props=filtered_props, description=description
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        .replace("\r\n", "\n")
							 | 
						||
| 
								 | 
							
								        .replace("$", "\\$")
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    wclist = get_wildcards_jl(props)
							 | 
						||
| 
								 | 
							
								    default_paramtext = ""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Filter props to remove those we don't want to expose
							 | 
						||
| 
								 | 
							
								    for item in prop_keys[:]:
							 | 
						||
| 
								 | 
							
								        if item.endswith("-*") or item == "setProps":
							 | 
						||
| 
								 | 
							
								            prop_keys.remove(item)
							 | 
						||
| 
								 | 
							
								        elif item in julia_keywords:
							 | 
						||
| 
								 | 
							
								            prop_keys.remove(item)
							 | 
						||
| 
								 | 
							
								            warnings.warn(
							 | 
						||
| 
								 | 
							
								                (
							 | 
						||
| 
								 | 
							
								                    'WARNING: prop "{}" in component "{}" is a Julia keyword'
							 | 
						||
| 
								 | 
							
								                    " - REMOVED FROM THE JULIA COMPONENT"
							 | 
						||
| 
								 | 
							
								                ).format(item, name)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default_paramtext += ", ".join(":{}".format(p) for p in prop_keys)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    has_children = "children" in prop_keys
							 | 
						||
| 
								 | 
							
								    funcname = format_fn_name(prefix, name)
							 | 
						||
| 
								 | 
							
								    children_signatures = (
							 | 
						||
| 
								 | 
							
								        jl_children_signatures.format(funcname=funcname) if has_children else ""
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    children_definitions = (
							 | 
						||
| 
								 | 
							
								        jl_children_definitions.format(funcname=funcname) if has_children else ""
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								    return jl_component_string.format(
							 | 
						||
| 
								 | 
							
								        funcname=format_fn_name(prefix, name),
							 | 
						||
| 
								 | 
							
								        docstring=docstring,
							 | 
						||
| 
								 | 
							
								        component_props=default_paramtext,
							 | 
						||
| 
								 | 
							
								        wildcard_symbols=stringify_wildcards(wclist, no_symbol=False),
							 | 
						||
| 
								 | 
							
								        wildcard_names=stringify_wildcards(wclist, no_symbol=True),
							 | 
						||
| 
								 | 
							
								        element_name=name,
							 | 
						||
| 
								 | 
							
								        module_name=project_shortname,
							 | 
						||
| 
								 | 
							
								        children_signatures=children_signatures,
							 | 
						||
| 
								 | 
							
								        children_definitions=children_definitions,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def generate_struct_file(name, props, description, project_shortname, prefix):
							 | 
						||
| 
								 | 
							
								    props = reorder_props(props=props)
							 | 
						||
| 
								 | 
							
								    import_string = "# AUTO GENERATED FILE - DO NOT EDIT\n"
							 | 
						||
| 
								 | 
							
								    class_string = generate_class_string(
							 | 
						||
| 
								 | 
							
								        name, props, description, project_shortname, prefix
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    file_name = format_fn_name(prefix, name) + ".jl"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # put component files in src/jl subdir,
							 | 
						||
| 
								 | 
							
								    # this also creates the Julia source directory for the package
							 | 
						||
| 
								 | 
							
								    # if it is missing
							 | 
						||
| 
								 | 
							
								    if not os.path.exists("src/jl"):
							 | 
						||
| 
								 | 
							
								        os.makedirs("src/jl")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    file_path = os.path.join("src", "jl", file_name)
							 | 
						||
| 
								 | 
							
								    with open(file_path, "w", encoding="utf-8") as f:
							 | 
						||
| 
								 | 
							
								        f.write(import_string)
							 | 
						||
| 
								 | 
							
								        f.write(class_string)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    print("Generated {}".format(file_name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# pylint: disable=unused-argument
							 | 
						||
| 
								 | 
							
								def generate_module(
							 | 
						||
| 
								 | 
							
								    project_shortname, components, metadata, pkg_data, prefix, **kwargs
							 | 
						||
| 
								 | 
							
								):
							 | 
						||
| 
								 | 
							
								    # copy over all JS dependencies from the (Python) components dir
							 | 
						||
| 
								 | 
							
								    # the inst/lib directory for the package won't exist on first call
							 | 
						||
| 
								 | 
							
								    # create this directory if it is missing
							 | 
						||
| 
								 | 
							
								    if os.path.exists("deps"):
							 | 
						||
| 
								 | 
							
								        shutil.rmtree("deps")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    os.makedirs("deps")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for rel_dirname, _, filenames in os.walk(project_shortname):
							 | 
						||
| 
								 | 
							
								        for filename in filenames:
							 | 
						||
| 
								 | 
							
								            extension = os.path.splitext(filename)[1]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if extension in [".py", ".pyc", ".json"]:
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            target_dirname = os.path.join(
							 | 
						||
| 
								 | 
							
								                "deps/", os.path.relpath(rel_dirname, project_shortname)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if not os.path.exists(target_dirname):
							 | 
						||
| 
								 | 
							
								                os.makedirs(target_dirname)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            shutil.copy(os.path.join(rel_dirname, filename), target_dirname)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    generate_package_file(project_shortname, components, pkg_data, prefix)
							 | 
						||
| 
								 | 
							
								    generate_toml_file(project_shortname, pkg_data)
							 |