From ee35164000957243bced08e147197d42ff28dcf5 Mon Sep 17 00:00:00 2001 From: Ryan Patrick Kyle Date: Tue, 7 Jan 2020 14:39:26 -0500 Subject: [PATCH] R package generator improvements + async support (#1048) * :sparkles: async/dynamic support in R pkg deps * :hocho: remove Authors block * :hocho: insert package version number * :hammer: use verbose :package: title and desc from YAML * :sparkles: autodetect vignettes * check for author/maintainer address * :hand: halt processing if fatal errors found * autopopulate KeepSource * auto-escape % in docstrings * :hocho: filter examples from docstrings in R --- dash/development/_r_components_generation.py | 92 ++++++++++++++++++-- 1 file changed, 83 insertions(+), 9 deletions(-) diff --git a/dash/development/_r_components_generation.py b/dash/development/_r_components_generation.py index bb0b7c335a..f1e43af931 100644 --- a/dash/development/_r_components_generation.py +++ b/dash/development/_r_components_generation.py @@ -46,14 +46,14 @@ file = "deps"), meta = NULL, script = {script_name}, stylesheet = {css_name}, head = NULL, attachment = NULL, package = "{rpkgname}", -all_files = FALSE), class = "html_dependency")""" # noqa:E501 +all_files = FALSE{async_or_dynamic}), class = "html_dependency")""" # noqa:E501 frame_body_template = """`{project_shortname}` = structure(list(name = "{project_shortname}", version = "{project_ver}", src = list(href = NULL, file = "deps"), meta = NULL, script = {script_name}, stylesheet = {css_name}, head = NULL, attachment = NULL, package = "{rpkgname}", -all_files = FALSE), class = "html_dependency")""" # noqa:E501 +all_files = FALSE{async_or_dynamic}), class = "html_dependency")""" # noqa:E501 frame_close_template = """) return(deps_metadata) @@ -81,9 +81,8 @@ """ description_template = """Package: {package_name} -Title: {package_description} +Title: {package_title} Version: {package_version} -Authors @R: as.person(c({package_author})) Description: {package_description} Depends: R (>= 3.0.2){package_depends} Imports: {package_imports} @@ -92,7 +91,8 @@ URL: {package_url} BugReports: {package_issues} Encoding: UTF-8 -LazyData: true +LazyData: true{vignette_builder} +KeepSource: true Author: {package_author_no_email} Maintainer: {maintainer} """ @@ -276,18 +276,23 @@ def generate_js_metadata(pkg_data, project_shortname): # pylint: disable=consider-using-enumerate if len(alldist) > 1: for dep in range(len(alldist)): - rpp = alldist[dep]["relative_package_path"] + curr_dep = alldist[dep] + rpp = curr_dep["relative_package_path"] + + async_or_dynamic = get_async_type(curr_dep) + if "dash_" in rpp: dep_name = rpp.split(".")[0] else: dep_name = "{}".format(project_shortname) - project_ver = str(dep) + if "css" in rpp: css_name = "'{}'".format(rpp) script_name = 'NULL' else: script_name = "'{}'".format(rpp) css_name = 'NULL' + function_frame += [ frame_element_template.format( dep_name=dep_name, @@ -296,23 +301,30 @@ def generate_js_metadata(pkg_data, project_shortname): project_shortname=project_shortname, script_name=script_name, css_name=css_name, + async_or_dynamic=async_or_dynamic, ) ] function_frame_body = ",\n".join(function_frame) elif len(alldist) == 1: - rpp = alldist[0]["relative_package_path"] + dep = alldist[0] + rpp = dep["relative_package_path"] + + async_or_dynamic = get_async_type(dep) + if "css" in rpp: css_name = "'{}'".format(rpp) script_name = "NULL" else: script_name = "'{}'".format(rpp) css_name = "NULL" + function_frame_body = frame_body_template.format( project_shortname=project_shortname, project_ver=project_ver, rpkgname=rpkgname, script_name=script_name, css_name=css_name, + async_or_dynamic=async_or_dynamic, ) function_string = "".join( @@ -322,6 +334,24 @@ def generate_js_metadata(pkg_data, project_shortname): return function_string +# determine whether dependency uses async or dynamic flag +# then return the properly formatted string if so, i.e. +# " async = TRUE,". a dependency can have async or +# dynamic elements, neither of these, but never both. +def get_async_type(dep): + async_or_dynamic = "" + for key in dep.keys(): + if (key in ['async', 'dynamic']): + keyval = dep[key] + if not isinstance(keyval, bool): + keyval = "'{}'".format(keyval.lower()) + else: + keyval = str(keyval).upper() + async_or_dynamic = \ + ", {} = {}".format(key, keyval) + return async_or_dynamic + + # This method wraps code within arbitrary LaTeX-like tags, which are used # by R's internal help parser for constructing man pages def wrap(tag, code): @@ -369,6 +399,15 @@ def write_help_file(name, props, description, prefix, rpkg_data): for p in prop_keys ) + # auto-replace any unescaped backslashes for compatibility with R docs + description = re.sub(r"(? brackets in package.json. ", + file=sys.stderr, + ) + sys.exit(1) + if not (os.path.isfile("LICENSE") or os.path.isfile("LICENSE.txt")): package_license = pkg_data.get("license", "") else: @@ -565,6 +629,14 @@ def generate_rpkg( for rpackage in rpackage_list: packages_string += "\nimport({})\n".format(rpackage) + if os.path.exists("vignettes"): + vignette_builder = "VignetteBuilder: knitr\n" + if "knitr" not in package_suggests and \ + "rmarkdown" not in package_suggests: + package_suggests += ", knitr, rmarkdown".lstrip(", ") + else: + vignette_builder = "" + pkghelp_stub_path = os.path.join("man", package_name + "-package.Rd") # generate the internal (not exported to the user) functions which @@ -582,6 +654,7 @@ def generate_rpkg( description_string = description_template.format( package_name=package_name, + package_title=package_title, package_description=package_description, package_version=package_version, package_author=package_author, @@ -591,6 +664,7 @@ def generate_rpkg( package_license=package_license, package_url=package_url, package_issues=package_issues, + vignette_builder=vignette_builder, package_author_no_email=package_author_no_email, maintainer=maintainer, )