Combine extract-meta and component generation in a cli.#451
Conversation
|
This seems like a good step forward. A few questions:
|
Yes, the old code was not modified.
Coming up soon.
Just need to add |
|
|
||
|
|
||
| # pylint: disable=too-many-locals | ||
| def generate_components(component_src, output_dir): |
There was a problem hiding this comment.
I would rename output_dir to project_shortname and not treat it as a path.
| cmd = shlex.split('node {} {}'.format(extract_path, component_src), | ||
| posix=not is_windows) | ||
|
|
||
| namespace = os.path.basename(output_dir) |
There was a problem hiding this comment.
I would just remove namespace as a variable here.
| ) | ||
| sys.exit(1) | ||
| # pylint: disable=unbalanced-tuple-unpacking | ||
| src, out = sys.argv[1:] |
There was a problem hiding this comment.
again, I would rename this project_shortname instead of out... because the R code won't output there.
| component_data['description'], | ||
| namespace | ||
| ) | ||
| print('Generated {}/{}.py'.format(namespace, name)) |
There was a problem hiding this comment.
for the Python piece, if it turns out that we do need package.json in the Python module directory next to __init__.py then I would do the copying here
|
💃 once the tests pass and the internal logic of the program are little bit more language-agnostic :) |
04f7761 to
82e22c3
Compare
| component_data['description'], | ||
| project_shortname | ||
| ) | ||
| print('Generated {}/{}.py'.format(project_shortname, name)) |
There was a problem hiding this comment.
can we move this line into the generate_class_file function? It's Python-specific and there will likely be an R-specific version as well and i don't think it's good to duplicate the path-as-function-of-project_shortname logic here as well :)
There was a problem hiding this comment.
i don't think it's good to duplicate the path-as-function-of-project_shortname logic here as well
I don't understand what you mean by that, just print the component name ?
There was a problem hiding this comment.
I'm saying that in the case of Python, the file path happens to be project_shortname/name`` but for R it'll be camelCase(project_shortname)/name.R` and I don't want to duplicate that logic here...
We could just print Generated {lang} version of {component_name} instead and bypass the language-specific path details :)
| components = [] | ||
|
|
||
| for component_path, component_data in metadata.items(): | ||
| name = component_path.split('/')[-1].split('.')[0] |
There was a problem hiding this comment.
maybe name this component_name just for extra clarity/readability
| with open(os.path.join(project_shortname, 'metadata.json'), 'w') as f: | ||
| json.dump(metadata, f) | ||
|
|
||
| with open(os.path.join(project_shortname, '_imports_.py'), 'w') as f: |
There was a problem hiding this comment.
can we move this Python-specific stuff into a Python-specific place? doesn't have to be in this PR but just in general :)
There was a problem hiding this comment.
It's actually duplicate code so let's refactor that for the old method too.
| import os | ||
| import textwrap | ||
|
|
||
| from dash.development.component_loader import generate_imports |
There was a problem hiding this comment.
should this also be a relative import? :)
|
|
||
| components = [] | ||
|
|
||
| for component_path, component_data in metadata.items(): |
There was a problem hiding this comment.
as a general comment, this seems to be repeated code with
dash/dash/development/component_loader.py
Line 68 in d892b9e
|
@nicolaskruchten Tests are in plotly/dash-component-boilerplate#40 @rmarren1 Please review |
rmarren1
left a comment
There was a problem hiding this comment.
This is great! I think we should fully commit to one method of generating class files.
With this change it looks like you can either
- Run the
extract-meta.jscommand manually to getmetadata.json - Run
dash.development.component_loader.generate_classespointing to the newmetadata.json
OR - Run
dash-generate-componentsto do all that in one function.
So we can either
- In
generate_components, we can writemetadata.jsonfirst, then use a call todash.development.component_loader.generate_classesfrom withingenerate_componentsto generate the components. - Just delete the
dash.development.component_loader.generate_classesfunction and rely on the CLI.
I think 2 might cause some minor headaches since we would need to update the toolchains in dash-*-components to use the CLI rather than dash.development.component_loader.generate_classes, but also dash.development.component_loader.generate_classes isn't really doing much. I'm pro deleting un-needed code, so I would go with #2 unless there is something I am not thinking of.
Also, I think base_component is getting a bit busy, I would be in favor of moving all the I/O functions to component_generator.py, e.g. generate_class_file, generate_imports, generate_classes_files.
Other than the method to get the path to extract-meta.js is the only critical change here.
dash/development/base_component.py
Outdated
| import inspect | ||
| import abc | ||
| import sys | ||
| import textwrap |
dash/development/base_component.py
Outdated
| f.write(import_string) | ||
| f.write(class_string) | ||
|
|
||
| print('Generated {}'.format(file_name)) |
dash/development/base_component.py
Outdated
| ).lstrip()) | ||
|
|
||
|
|
||
| def generate_classes_files(project_shortname, metadata, *component_generators): |
There was a problem hiding this comment.
Can this be generate_class_files to match generate_class and generate_class_file method names?
There was a problem hiding this comment.
Also, can it be moved up to be next to those methods? Just to keep them together spatially in the code
There was a problem hiding this comment.
Actually, perhaps we can just move these into components_generator.py
| package_info_filename='package.json'): | ||
| is_windows = sys.platform == 'win32' | ||
|
|
||
| extract_path = os.path.abspath(os.path.join( |
There was a problem hiding this comment.
This pattern for accessing the included files has given me problems in the past, from what I researched to fix it we should use https://stackoverflow.com/a/20885799
| )) | ||
|
|
||
| os.environ['NODE_PATH'] = 'node_modules' | ||
| cmd = shlex.split('node {} {}'.format(extract_path, components_source), |
There was a problem hiding this comment.
Is this different from ['node', extract_path, component_source]? Never used shlex not sure what this does.
There was a problem hiding this comment.
I use shlex to make sure it works across platforms, had problems on windows passing arguments without the posix=False argument.
| # Add an import statement for this component | ||
| with open(imports_path, 'a') as f: | ||
| f.write('from .{0:s} import {0:s}\n'.format(name)) | ||
| components = generate_classes_files(namespace, data, generate_class_file) |
There was a problem hiding this comment.
Four lines up is not relevant anymore, write append not used.
|
@rmarren1 I think we need to keep supporting both methods for backwards-compatibility, otherwise older components may break, no? |
Writing just to read back seems like a bad pattern, I initially did not write the
I think they should stay there for backward compatibility. Someone working on a component library should be able to update dash without breaking their build system. |
|
I moved all python class generation code to |
|
💃 Deleting the non-cli method of generating component class files would only break build systems for component authors and shouldn't affect things for end users (as long as we keep the |
|
@rmarren1 FWIW here's the terminology I use when thinking about our stakeholders:
In general our commitment should be not to break things for any of these folks :) |
|
@T4rk1n I'm a bit confused about what the |
|
@nicolaskruchten The component_loader is the old method for generating the components, it contains the methods for generating components at runtime and the method we were using in |
|
Some history: In the first dash iteration, component libraries would call #276 changed this, so that that component classes were generated once at build time using Now this CLI will replace the |
|
thanks for the clarifications! 💃 on my end |
extract-meta.jsextract-meta.jscall and component generation.dash-generate-componentsUsage:
$ dash-generate-components src/lib/components my_components_namespacecc @plotly/dash
Closes plotly/dash-component-boilerplate#17