From 490e8493f511fc7bb89d6aa770ce0fe8ae19d071 Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 15 May 2020 20:26:54 +0300 Subject: [PATCH 1/3] Julia packge generator --- dash/development/_jl_components_generation.py | 128 ++++++++++++++++++ dash/development/component_generator.py | 5 + 2 files changed, 133 insertions(+) diff --git a/dash/development/_jl_components_generation.py b/dash/development/_jl_components_generation.py index 7b45a394aa..dec24323bf 100644 --- a/dash/development/_jl_components_generation.py +++ b/dash/development/_jl_components_generation.py @@ -6,15 +6,22 @@ import shutil import glob import warnings +import sys +import importlib +import uuid from ._all_keywords import julia_keywords from ._py_components_generation import reorder_props +#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...) {funcname}(children::Any;kwargs...) @@ -50,6 +57,57 @@ {funcname}(children_maker::Function; kwargs...) = {funcname}(children_maker(); kwargs...) ''' # noqa:E501 +jl_package_file_string = ''' +module {package_name} +using Dash + +const resources_path = realpath(joinpath( @__DIR__, "..", "deps")) +const version = "{version}" + +{component_includes} + +function __init__() + Dash.register_package( + Dash.ResourcePkg( + "{project_shortname}", + resources_path, + version = version, + [ + {resources_dist} + ] + ) + ) +end +end +''' + +jl_projecttoml_string = ''' +name = "{package_name}" +uuid = "{package_uuid}" +{authors} +version = "{version}" + +[deps] +Dash = "{dash_uuid}" + +[compact] +julia = "1.1" +Dash = ">=0.1" +''' + +jl_component_include_string = 'include("{name}.jl")' + +jl_resource_tuple_string = '''Dash.Resource( + relative_package_path = {relative_package_path}, + external_url = {external_url}, + dynamic = {dynamic}, + async = {async_string}, + type = :{type} +)''' + +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: @@ -306,6 +364,69 @@ def format_fn_name(prefix, name): return name.lower() +def generate_metadata_strings(resources, type): + def noting_or_string(v): + return '"{}"'.format(v) if v else "nothing" + return [jl_resource_tuple_string.format( + relative_package_path = noting_or_string(resource.get("relative_package_path", "")), + external_url = noting_or_string(resource.get("external_url", "")), + dynamic = str(resource.get("dynamic", 'nothing')).lower(), + type = type, + async_string = ":{}".format(str(resource.get("async")).lower()) + if "async" in resource.keys() + else 'nothing' + ) for resource in resources] + + +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 + + ) + file_path = os.path.join("src", package_name + ".jl") + with open(file_path, "w") 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] + hex(hash(package_name))[-12:]) + + authors_string = 'authors = ["{}"]'.format(package_author) if package_author else "" + + toml_string = jl_projecttoml_string.format( + package_name = package_name, + package_uuid = package_uuid, + version = project_ver, + authors = authors_string, + dash_uuid = jl_dash_uuid + ) + file_path = "Project.toml" + with open(file_path, "w") 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)) @@ -374,6 +495,7 @@ def generate_module( project_shortname, components, metadata, + pkg_data, prefix, **kwargs ): @@ -398,3 +520,9 @@ def generate_module( for sourcemap in glob.glob("{}/*.map".format(project_shortname)): shutil.copy(sourcemap, "deps/") + + generate_package_file(project_shortname, components, pkg_data, prefix) + generate_toml_file(project_shortname, pkg_data) + + + diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py index 6ddc64db05..9512b8864a 100644 --- a/dash/development/component_generator.py +++ b/dash/development/component_generator.py @@ -111,6 +111,10 @@ def generate_components( ) if jlprefix is not False: + if pkg_data is None: + with open("package.json", "r") as f: + pkg_data = safe_json_loads(f.read()) + generator_methods.append( functools.partial(generate_struct_file, prefix=jlprefix) ) @@ -140,6 +144,7 @@ def generate_components( project_shortname, components, metadata, + pkg_data, jlprefix ) From ac65fe940153d850f7410a380bf2af765a8a57ff Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Fri, 12 Jun 2020 20:45:58 +0300 Subject: [PATCH 2/3] reverse Dash dependency --- dash/development/_jl_components_generation.py | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/dash/development/_jl_components_generation.py b/dash/development/_jl_components_generation.py index 1cbf19d63b..baa86f3942 100644 --- a/dash/development/_jl_components_generation.py +++ b/dash/development/_jl_components_generation.py @@ -13,6 +13,9 @@ 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" @@ -23,43 +26,31 @@ export {funcname} """ - {funcname}(;kwargs...) - {funcname}(children::Any;kwargs...) - {funcname}(children_maker::Function;kwargs...) + {funcname}(;kwargs...){childrens_signatures} {docstring} """ function {funcname}(; kwargs...) - available_props = Set(Symbol[{component_props}]) - wild_props = Set(Symbol[{wildcard_symbols}]) - wild_regs = r"^(?{wildcard_names})" - - result = Component("{element_name}", "{module_name}", Dict{{Symbol, Any}}(), available_props, Set(Symbol[{wildcard_symbols}])) - - for (prop, value) = pairs(kwargs) - m = match(wild_regs, string(prop)) - if (length(wild_props) == 0 || isnothing(m)) && !(prop in available_props) - throw(ArgumentError("Invalid property $(string(prop)) for component " * "{funcname}")) - end - - push!(result.props, prop => Front.to_dash(value)) - end - - return result + available_props = Symbol[{component_props}] + wild_props = Symbol[{wildcard_symbols}] + return Component("{funcname}", "{element_name}", "{module_name}", available_props, wild_props; kwargs...) end +{childrens_definitions} +''' # noqa:E501 -function {funcname}(children::Any; kwargs...) - result = {funcname}(;kwargs...) - push!(result.props, :children => Front.to_dash(children)) - return result -end +jl_childrens_signatures = ''' + {funcname}(children::Any;kwargs...) + {funcname}(children_maker::Function;kwargs...) +''' +jl_childrens_definitions = ''' +{funcname}(children::Any; kwargs...) = {funcname}(;kwargs..., children = children) {funcname}(children_maker::Function; kwargs...) = {funcname}(children_maker(); kwargs...) -''' # noqa:E501 +''' jl_package_file_string = ''' module {package_name} -using Dash +using {base_package} const resources_path = realpath(joinpath( @__DIR__, "..", "deps")) const version = "{version}" @@ -67,8 +58,8 @@ {component_includes} function __init__() - Dash.register_package( - Dash.ResourcePkg( + DashBase.register_package( + DashBase.ResourcePkg( "{project_shortname}", resources_path, version = version, @@ -76,6 +67,7 @@ {resources_dist} ] ) + ) end end @@ -87,16 +79,16 @@ {authors}version = "{version}" [deps] -Dash = "{dash_uuid}" +{base_package} = "{dash_uuid}" [compact] -julia = "1.1" -Dash = ">=0.1" +julia = "1.2" +{base_package} = ">=0.1" ''' jl_component_include_string = 'include("{name}.jl")' -jl_resource_tuple_string = '''Dash.Resource( +jl_resource_tuple_string = '''DashBase.Resource( relative_package_path = {relative_package_path}, external_url = {external_url}, dynamic = {dynamic}, @@ -104,6 +96,7 @@ type = :{type} )''' +core_packages = ['dash_html_components', 'dash_core_components', 'dash_table'] def jl_package_name(namestring): s = namestring.split("_") @@ -370,6 +363,14 @@ def nothing_or_string(v): 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_base_uuid def generate_package_file(project_shortname, components, pkg_data, prefix): package_name = jl_package_name(project_shortname) @@ -391,7 +392,8 @@ def generate_package_file(project_shortname, components, pkg_data, prefix): ), resources_dist=resources_dist, version=project_ver, - project_shortname=project_shortname + project_shortname=project_shortname, + base_package=base_package_name(project_shortname) ) file_path = os.path.join("src", package_name + ".jl") @@ -413,7 +415,8 @@ def generate_toml_file(project_shortname, pkg_data): package_uuid=package_uuid, version=project_ver, authors=authors_string, - dash_uuid=jl_dash_uuid + base_package=base_package_name(project_shortname), + dash_uuid=base_package_uid(project_shortname), ) file_path = "Project.toml" with open(file_path, "w") as f: @@ -428,7 +431,7 @@ def generate_class_string(name, props, description, project_shortname, prefix): docstring = create_docstring_jl( component_name=name, props=filtered_props, description=description - ).replace("\r\n", "\n") + ).replace("\r\n", "\n").replace('$', '\$') wclist = get_wildcards_jl(props) default_paramtext = "" @@ -451,6 +454,10 @@ def generate_class_string(name, props, description, project_shortname, prefix): for p in prop_keys ) + has_children = "children" in prop_keys + funcname = format_fn_name(prefix, name) + childrens_signatures = jl_childrens_signatures.format(funcname=funcname) if has_children else "" + childrens_definitions = jl_childrens_definitions.format(funcname=funcname) if has_children else "" return jl_component_string.format( funcname=format_fn_name(prefix, name), docstring=docstring, @@ -459,6 +466,8 @@ def generate_class_string(name, props, description, project_shortname, prefix): wildcard_names=stringify_wildcards(wclist, no_symbol=True), element_name=name, module_name=project_shortname, + childrens_signatures = childrens_signatures, + childrens_definitions = childrens_definitions ) From 650b9d3fc06008c1e595a7f83cce002ea4c976c8 Mon Sep 17 00:00:00 2001 From: Alexandr Romanenko Date: Tue, 16 Jun 2020 17:04:39 +0300 Subject: [PATCH 3/3] naming fix --- dash/development/_jl_components_generation.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dash/development/_jl_components_generation.py b/dash/development/_jl_components_generation.py index baa86f3942..6d82aced73 100644 --- a/dash/development/_jl_components_generation.py +++ b/dash/development/_jl_components_generation.py @@ -26,7 +26,7 @@ export {funcname} """ - {funcname}(;kwargs...){childrens_signatures} + {funcname}(;kwargs...){children_signatures} {docstring} """ @@ -35,15 +35,15 @@ wild_props = Symbol[{wildcard_symbols}] return Component("{funcname}", "{element_name}", "{module_name}", available_props, wild_props; kwargs...) end -{childrens_definitions} +{children_definitions} ''' # noqa:E501 -jl_childrens_signatures = ''' +jl_children_signatures = ''' {funcname}(children::Any;kwargs...) {funcname}(children_maker::Function;kwargs...) ''' -jl_childrens_definitions = ''' +jl_children_definitions = ''' {funcname}(children::Any; kwargs...) = {funcname}(;kwargs..., children = children) {funcname}(children_maker::Function; kwargs...) = {funcname}(children_maker(); kwargs...) ''' @@ -456,8 +456,8 @@ def generate_class_string(name, props, description, project_shortname, prefix): has_children = "children" in prop_keys funcname = format_fn_name(prefix, name) - childrens_signatures = jl_childrens_signatures.format(funcname=funcname) if has_children else "" - childrens_definitions = jl_childrens_definitions.format(funcname=funcname) if has_children else "" + 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, @@ -466,8 +466,8 @@ def generate_class_string(name, props, description, project_shortname, prefix): wildcard_names=stringify_wildcards(wclist, no_symbol=True), element_name=name, module_name=project_shortname, - childrens_signatures = childrens_signatures, - childrens_definitions = childrens_definitions + children_signatures = children_signatures, + children_definitions = children_definitions )