Skip to content
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
708e82d
:pencil2: append dash_ to wildcards validator fn
Jun 19, 2019
4e71941
:bug: remove duplicated fn name in usage
Jun 19, 2019
63e8132
added pkg help desc for dashTable
Jun 19, 2019
363c95e
:hocho: leading comma in DESC fields
Jun 19, 2019
2cb4739
append dash_ to filter_null
Jun 19, 2019
9ba7c6a
:sparkles: add maintainer option
Jun 19, 2019
3111a09
:sparkles: add component helpers to internal.R
Jun 19, 2019
8e29ca1
:pencil2: update .Rbuildignore
Jun 20, 2019
320e52e
:hocho: filter_null, :hammer: internal.R
Jun 20, 2019
02afe2a
:sparkles: add get_wildcards_r method
Jun 20, 2019
9cee1d3
:shirt: remove whitespace
Jun 20, 2019
0f4ed2d
:see_no_evil: fix missing doubled braces for R
Jun 20, 2019
6eb6f04
:hammer: documentation for dynamic WCs
Jun 20, 2019
b104e7a
:bug: fix missing quotation marks
Jun 20, 2019
e28724f
Merge branch 'master' into modify-R-deps-handling
rpkyle Jun 20, 2019
fcd82a5
use key.endswith
Jun 20, 2019
1578e9a
use p.endswith
Jun 20, 2019
c6a013a
check WCs before f.write
Jun 20, 2019
8f50b8a
:paw_prints: relocate component_helpers to template section
Jun 21, 2019
628e00d
:sparkles: initial use of YAML for R :package: config
Jun 21, 2019
4e2b859
use maintainer if available
Jun 21, 2019
393ab8b
:hammer: add + to create config files if nonexistent
Jun 21, 2019
5c1a104
:hocho: links, redundant given DESCRIPTION
Jun 21, 2019
7a8da3b
remove blank line
Jun 21, 2019
2d31fdc
:hocho: component_handlers
Jun 21, 2019
857bb91
:see_no_evil: fix check for pkg description
Jun 21, 2019
9b812ce
Merge branch 'master' into modify-R-deps-handling
rpkyle Jun 22, 2019
f0bfd07
:bug: fix quotation bug in template
Jun 22, 2019
862fe70
turn missing YaML error into warning
Jun 26, 2019
7ae5710
Merge branch 'master' into modify-R-deps-handling
alexcjohnson Jun 29, 2019
e35f040
warn on R keyword prop removal
alexcjohnson Jun 29, 2019
15cbea8
include dash_assert_valid_wildcards only when necessary
alexcjohnson Jun 29, 2019
4bea826
tweaks to regex and failure message in dash_assert_valid_wildcards
alexcjohnson Jun 29, 2019
23d8b79
lint
alexcjohnson Jun 29, 2019
1c56d9a
add PyYAML dep
alexcjohnson Jun 29, 2019
e029904
lint & simplify component_generator
alexcjohnson Jun 29, 2019
77d6580
:arrow_down: lower pytest version until the proper fix in flask 1.0.3…
Jul 2, 2019
d53959f
Merge branch 'master' into modify-R-deps-handling
byronz Jul 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 80 additions & 68 deletions dash/development/_r_components_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
# code below
r_component_string = """{funcname} <- function({default_argtext}{wildcards}) {{
{wildcard_declaration}
props <- list({default_paramtext}{wildcards})
if (length(props) > 0) {{
props <- props[!vapply(props, is.null, logical(1))]
}}
component <- list(
props = list({default_paramtext}{wildcards}),
props = props,
type = '{name}',
namespace = '{project_shortname}',
propNames = c({prop_names}{wildcard_names}),
package = '{package_name}'
)

component$props <- filter_null(component$props)

structure(component, class = c('dash_component', 'list'))
}}
""" # noqa:E501
Expand Down Expand Up @@ -83,15 +85,15 @@
Authors @R: as.person(c({package_author}))
Description: {package_description}
Depends: R (>= 3.0.2){package_depends}
Imports: dash{package_imports}
Imports: {package_imports}
Suggests: {package_suggests}
License: {package_license}
URL: {package_url}
BugReports: {package_issues}
Encoding: UTF-8
LazyData: true
Author: {package_author_no_email}
Maintainer: {package_author}
Maintainer: {maintainer}
"""

rbuild_ignore_string = r"""# ignore JS config files/folders
Expand All @@ -103,6 +105,11 @@
.builderrc
.eslintrc
.npmignore
.editorconfig
.eslintignore
.prettierrc
.circleci
.github

# demo folder has special meaning in R
# this should hopefully make it still
Expand All @@ -129,22 +136,34 @@
\\docType{{package}}
\\name{{{package_name}-package}}
\\alias{{{package_name}}}
\\title{{{pkg_help_header}}}
\\title{{{pkg_help_title}}}
\\description{{
{pkg_help_desc}
}}
\\seealso{{
Useful links:
\\itemize{{
\\item \\url{{https://github.com/plotly/{lib_name}}}
\\item Report bugs at \\url{{https://github.com/plotly/{lib_name}/issues}}
}}
{pkg_help_description}
}}
\\author{{
\\strong{{Maintainer}}: {package_author}
\\strong{{Maintainer}}: {maintainer}
}}
"""

component_helpers = """
dash_assert_valid_wildcards <- function (attrib = list("data", "aria"), ...)
{
args <- list(...)
validation_results <- lapply(names(args), function(x) {
grepl(paste0("^", attrib, "-[a-zA-Z0-9]{1,}$", collapse = "|"),
Comment thread
alexcjohnson marked this conversation as resolved.
Outdated
x)
})
if (FALSE %in% validation_results) {
stop(sprintf("The following wildcards are not currently valid in Dash: '%s'",
Comment thread
alexcjohnson marked this conversation as resolved.
Outdated
paste(names(args)[grepl(FALSE, unlist(validation_results))],
collapse = ", ")), call. = FALSE)
}
else {
return(args)
}
}
""" # noqa:E501


# pylint: disable=R0914
def generate_class_string(name, props, project_shortname, prefix):
Expand All @@ -159,35 +178,25 @@ def generate_class_string(name, props, project_shortname, prefix):
wildcards = ""
wildcard_declaration = ""
wildcard_names = ""
default_paramtext = ""
default_argtext = ""
accepted_wildcards = ""

if any("-*" in key for key in prop_keys):
accepted_wildcards = get_wildcards_r(prop_keys)
wildcards = ", ..."
wildcard_declaration = (
"\n wildcard_names = names(assert_valid_wildcards(...))\n"
)
wildcard_declaration = """
wildcard_names = names(dash_assert_valid_wildcards(attrib = list({}), ...))
""".format(accepted_wildcards.replace("-*", "")) # noqa:E501
wildcard_names = ", wildcard_names"

default_paramtext = ""
default_argtext = ""
default_wildcards = ""

# Produce a string with all property names other than WCs
prop_names = ", ".join(
"'{}'".format(p)
for p in prop_keys
if "*" not in p and p not in ["setProps"]
)

# in R, we set parameters with no defaults to NULL
# Here we'll do that if no default value exists
default_wildcards += ", ".join("'{}'".format(p)
for p in prop_keys if "*" in p)

if default_wildcards == "":
default_wildcards = "NULL"
else:
default_wildcards = "c({})".format(default_wildcards)

# Filter props to remove those we don't want to expose
for item in prop_keys[:]:
if item.endswith("-*") or item in r_keywords or item == "setProps":
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also not new in this PR, but item in r_keywords worries me to just be silently dropped - if nothing else it seems like this should log a warning. Do any of our own components use R keywords?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a warning if we drop something from r_keywords, in e35f040

We should keep an eye out for this when rebuilding all the components - if we never see this warning, perhaps we can lock the R keywords out by turning the warning into an error.

Expand Down Expand Up @@ -278,10 +287,10 @@ def generate_js_metadata(pkg_data, project_shortname):
elif len(alldist) == 1:
rpp = alldist[0]["relative_package_path"]
if "css" in rpp:
css_name = rpp
css_name = "'{}'".format(rpp)
script_name = "NULL"
else:
script_name = rpp
script_name = "'{}'".format(rpp)
css_name = "NULL"
function_frame_body = frame_body_template.format(
project_shortname=project_shortname,
Expand Down Expand Up @@ -321,7 +330,10 @@ def write_help_file(name, props, description, prefix):

prop_keys = list(props.keys())

has_wildcards = any("-*" in key for key in prop_keys)
has_wildcards = any(key.endswith("-*") for key in prop_keys)

if has_wildcards:
wildcards = get_wildcards_r(prop_keys)

# Filter props to remove those we don't want to expose
for item in prop_keys[:]:
Expand All @@ -340,20 +352,21 @@ def write_help_file(name, props, description, prefix):
)

if has_wildcards:
item_text += '\n\n\\item{...}{wildcards: `data-*` or `aria-*`}'
default_argtext += ', ...'
item_text += """
\n\n\\item{{...}}{{wildcards allowed have the form: `{}`}}
""".format(wildcards)

# in R, the online help viewer does not properly wrap lines for
# the usage string -- we will hard wrap at 80 characters using
# textwrap.fill, starting from the beginning of the usage string
argtext = prefix + name + "({})".format(default_argtext)

file_path = os.path.join('man', file_name)
with open(file_path, 'w') as f:
f.write(help_string.format(
funcname=format_fn_name(prefix, name),
name=name,
default_argtext=textwrap.fill(argtext,
default_argtext=textwrap.fill(default_argtext,
width=80,
break_long_words=False),
item_text=item_text,
Expand Down Expand Up @@ -425,6 +438,7 @@ def write_js_metadata(pkg_data, project_shortname):
file_path = os.path.join("R", file_name)
with open(file_path, "w") as f:
f.write(function_string)
f.write(component_helpers)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if has_wildcards?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Will fix.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in c6a013a

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow this change (only adding dash_assert_valid_wildcards to internal.R if needed by a component) was reverted - was that intended?

Copy link
Copy Markdown
Contributor Author

@rpkyle rpkyle Jun 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I am uncertain about how to check for wildcards without passing props to methods that ordinarily would not require them; would making has_wildcards a global variable be appropriate in this case? I would like to avoid refactoring too much this week, I still have a bit to do outside of Python code maintenance.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just looped through the metadata for wildcards again -> 15cbea8


# now copy over all JS dependencies from the (Python) components dir
# the inst/lib directory for the package won't exist on first call
Expand All @@ -445,6 +459,7 @@ def write_js_metadata(pkg_data, project_shortname):
# pylint: disable=R0914, R0913, R0912, R0915
def generate_rpkg(
pkg_data,
rpkg_data,
project_shortname,
export_string,
package_depends,
Expand All @@ -457,8 +472,12 @@ def generate_rpkg(
Parameters
----------
pkg_data
rpkg_data
project_shortname
export_string
package_depends
package_imports
package_suggests

Returns
-------
Expand All @@ -478,7 +497,7 @@ def generate_rpkg(
package_depends = ", " + package_depends.strip(",").lstrip()

if package_imports:
package_imports = ", " + package_imports.strip(",").lstrip()
package_imports = package_imports.strip(",").lstrip()

if package_suggests:
package_suggests = package_suggests.strip(",").lstrip()
Expand Down Expand Up @@ -506,6 +525,8 @@ def generate_rpkg(

package_author_no_email = package_author.split(" <")[0] + " [aut]"

maintainer = pkg_data.get("maintainer", pkg_data.get("author"))

if not (os.path.isfile("LICENSE") or os.path.isfile("LICENSE.txt")):
package_license = pkg_data.get("license", "")
else:
Expand All @@ -531,36 +552,14 @@ def generate_rpkg(
# this avoids having to generate an RData file from within Python.
write_js_metadata(pkg_data=pkg_data, project_shortname=project_shortname)

with open("NAMESPACE", "w") as f:
with open("NAMESPACE", "w+") as f:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐮 minor it's personal preference, but I would avoid f with numbers, maybe for example fp_ns, fp_desr

f.write(import_string)
f.write(export_string)
f.write(packages_string)

with open(".Rbuildignore", "w") as f2:
with open(".Rbuildignore", "w+") as f2:
f2.write(rbuild_ignore_string)

# Write package stub files for R online help, generate if
# dashHtmlComponents or dashCoreComponents; makes it easy
# for R users to bring up main package help page
pkg_help_header = ""

if package_name in ["dashHtmlComponents"]:
pkg_help_header = "Vanilla HTML Components for Dash"
pkg_help_desc = "Dash is a web application framework that\n\
provides pure Python and R abstraction around HTML, CSS, and\n\
JavaScript. Instead of writing HTML or using an HTML\n\
templating engine, you compose your layout using R\n\
functions within the dashHtmlComponents package. The\n\
source for this package is on GitHub:\n\
plotly/dash-html-components."
if package_name in ["dashCoreComponents"]:
pkg_help_header = "Core Interactive UI Components for Dash"
pkg_help_desc = "Dash ships with supercharged components for\n\
interactive user interfaces. A core set of components,\n\
written and maintained by the Dash team, is available in\n\
the dashCoreComponents package. The source for this package\n\
is on GitHub: plotly/dash-core-components."

description_string = description_template.format(
package_name=package_name,
package_description=package_description,
Expand All @@ -573,18 +572,19 @@ def generate_rpkg(
package_url=package_url,
package_issues=package_issues,
package_author_no_email=package_author_no_email,
maintainer=maintainer,
)

with open("DESCRIPTION", "w") as f3:
with open("DESCRIPTION", "w+") as f3:
f3.write(description_string)

if pkg_help_header != "":
if rpkg_data.get("pkg_help_description"):
pkghelp = pkghelp_stub.format(
package_name=package_name,
pkg_help_header=pkg_help_header,
pkg_help_desc=pkg_help_desc,
pkg_help_title=rpkg_data.get("pkg_help_title"),
pkg_help_description=rpkg_data.get("pkg_help_description"),
lib_name=lib_name,
package_author=package_author,
maintainer=maintainer,
)
with open(pkghelp_stub_path, "w") as f4:
f4.write(pkghelp)
Expand Down Expand Up @@ -614,6 +614,7 @@ def generate_exports(
components,
metadata,
pkg_data,
rpkg_data,
prefix,
package_depends,
package_imports,
Expand Down Expand Up @@ -664,6 +665,7 @@ def generate_exports(
# locally or directly from GitHub
generate_rpkg(
pkg_data,
rpkg_data,
project_shortname,
export_string,
package_depends,
Expand Down Expand Up @@ -821,3 +823,13 @@ def create_prop_docstring_r(prop_name, type_object, required, description,
': {}'.format(description) if description != '' else ''
),
is_required='required' if required else 'optional')


def get_wildcards_r(prop_keys):
wildcards = ""
wildcards += ", ".join("'{}'".format(p)
for p in prop_keys if p.endswith("-*"))

if wildcards == "":
wildcards = "NULL"
return wildcards
16 changes: 14 additions & 2 deletions dash/development/component_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections import OrderedDict

import json
import yaml
import sys
import subprocess
import shlex
Expand Down Expand Up @@ -103,6 +104,14 @@ def generate_components(
os.makedirs('man')
if not os.path.exists('R'):
os.makedirs('R')
if os.path.isfile("dash-info.yaml"):
yamldata = open("dash-info.yaml", 'r')
else:
print(
"Error: dash-info.yaml missing; R package metadata not loaded",
file=sys.stderr
)
sys.exit(1)
generator_methods.append(
functools.partial(write_class_file, prefix=prefix))

Expand All @@ -120,21 +129,24 @@ def generate_components(
if rprefix is not None:
with open('package.json', 'r') as f:
jsondata_unicode = json.load(f, object_pairs_hook=OrderedDict)
rpkg_data = yaml.safe_load(yamldata)
if sys.version_info[0] >= 3:
pkg_data = jsondata_unicode
else:
pkg_data = byteify(jsondata_unicode)

generate_exports(
project_shortname,
components,
metadata,
pkg_data,
rpkg_data,
prefix,
rdepends,
rimports,
rsuggests,
)
)
else:
rpkgdata = None


def cli():
Expand Down