prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Allow excluding file patterns when generating component definitions 
  Thanks a lot for making Dash -- we have found it very useful for building dashboards.
  
  We ran into an issue migrating to the new `dash-generate-components` utility.
  
  In [dash-bootstrap-components](https://github.com/ASIDataScience/dash-bootstrap-components), we keep the components in `src/components`. Besides components files, this also includes a few unit test files called, e.g. `src/components/__tests__/DropdownMenu.test.js`. When we run `dash-generate-components ./src/components <output>`, these test files also get picked up. This leads to a traceback in the build logs: 
  
  ```
  Error with path src/components/__tests__/DropdownMenu.test.jsError: No suitable component definition found.
  Error: No suitable component definition found.
  at parse (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/parse.js:84:9)
  at Object.defaultParse [as parse] (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/main.js:66:30)
  at parseFile (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:64:51)
  at dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:84:17)
  at Array.forEach (<anonymous>)
  at collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)
  at dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:82:17)
  at Array.forEach (<anonymous>)
  at collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)
  at componentPaths.forEach.componentPath (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:15:5)
  ```
  
  While the error is, of course, legitimate, it would be nice to be able to suppress these tracebacks. I can see three routes:
  - do nothing -- after all, this doesn't stop us from building `metadata.json`, it just makes it slightly more confusing.
  - in `dash/extract-meta.js`, we could explicitly blacklist `__tests__` directories in the same way that files that don't end in `jsx?` are black-listed. AFAICT, the `__tests__` directory structure is the default with [jest](https://jestjs.io/docs/en/configuration.html#testmatch-array-string). [react-docgen](https://github.com/reactjs/react-docgen#cli) ignores `node_modules`, `__tests__` and `__mocks__` by default, so there is definitely a precedent.
  - add a `--ignore` argument to `dash-generate-components` that allows passing file globs to be excluded. 
  
  Very happy to submit a PR if you decide on what the best course of action is.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `dash/development/component_generator.py`
  Content:
  ```
  1 from __future__ import print_function
  2 
  3 import json
  4 import sys
  5 import subprocess
  6 import shlex
  7 import os
  8 import argparse
  9 import shutil
  10 
  11 import pkg_resources
  12 
  13 from ._py_components_generation import generate_class_file
  14 from ._py_components_generation import generate_imports
  15 from ._py_components_generation import generate_classes_files
  16 
  17 
  18 class _CombinedFormatter(argparse.ArgumentDefaultsHelpFormatter,
  19                          argparse.RawDescriptionHelpFormatter):
  20     pass
  21 
  22 
  23 # pylint: disable=too-many-locals
  24 def generate_components(components_source, project_shortname,
  25                         package_info_filename='package.json'):
  26     is_windows = sys.platform == 'win32'
  27 
  28     extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')
  29 
  30     os.environ['NODE_PATH'] = 'node_modules'
  31     cmd = shlex.split('node {} {}'.format(extract_path, components_source),
  32                       posix=not is_windows)
  33 
  34     shutil.copyfile('package.json',
  35                     os.path.join(project_shortname, package_info_filename))
  36 
  37     proc = subprocess.Popen(cmd,
  38                             stdout=subprocess.PIPE,
  39                             stderr=subprocess.PIPE,
  40                             shell=is_windows)
  41     out, err = proc.communicate()
  42     status = proc.poll()
  43 
  44     if err:
  45         print(err.decode(), file=sys.stderr)
  46 
  47     if not out:
  48         print(
  49             'Error generating metadata in {} (status={})'.format(
  50                 project_shortname, status),
  51             file=sys.stderr)
  52         sys.exit(1)
  53 
  54     metadata = json.loads(out.decode())
  55 
  56     components = generate_classes_files(
  57         project_shortname,
  58         metadata,
  59         generate_class_file
  60     )
  61 
  62     with open(os.path.join(project_shortname, 'metadata.json'), 'w') as f:
  63         json.dump(metadata, f)
  64 
  65     generate_imports(project_shortname, components)
  66 
  67 
  68 def cli():
  69     parser = argparse.ArgumentParser(
  70         prog='dash-generate-components',
  71         formatter_class=_CombinedFormatter,
  72         description='Generate dash components by extracting the metadata '
  73         'using react-docgen. Then map the metadata to python classes.'
  74     )
  75     parser.add_argument('components_source',
  76                         help='React components source directory.')
  77     parser.add_argument(
  78         'project_shortname',
  79         help='Name of the project to export the classes files.'
  80     )
  81     parser.add_argument(
  82         '-p', '--package-info-filename',
  83         default='package.json',
  84         help='The filename of the copied `package.json` to `project_shortname`'
  85     )
  86 
  87     args = parser.parse_args()
  88     generate_components(args.components_source, args.project_shortname,
  89                         package_info_filename=args.package_info_filename)
  90 
  91 
  92 if __name__ == '__main__':
  93     cli()
  94
  ```
  
  Path: `dash/version.py`
  Content:
  ```
  1 __version__ = '0.33.0'
  2
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py
  --- a/dash/development/component_generator.py
  +++ b/dash/development/component_generator.py
  @@ -22,14 +22,17 @@
   
   # pylint: disable=too-many-locals
   def generate_components(components_source, project_shortname,
  -                        package_info_filename='package.json'):
  +                        package_info_filename='package.json',
  +                        ignore='^_'):
       is_windows = sys.platform == 'win32'
   
       extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')
   
       os.environ['NODE_PATH'] = 'node_modules'
  -    cmd = shlex.split('node {} {}'.format(extract_path, components_source),
  -                      posix=not is_windows)
  +    cmd = shlex.split(
  +        'node {} {} {}'.format(extract_path, ignore, components_source),
  +        posix=not is_windows
  +    )
   
       shutil.copyfile('package.json',
                       os.path.join(project_shortname, package_info_filename))
  @@ -83,10 +86,18 @@
           default='package.json',
           help='The filename of the copied `package.json` to `project_shortname`'
       )
  +    parser.add_argument(
  +        '-i', '--ignore',
  +        default='^_',
  +        help='Files/directories matching the pattern will be ignored'
  +    )
   
       args = parser.parse_args()
  -    generate_components(args.components_source, args.project_shortname,
  -                        package_info_filename=args.package_info_filename)
  +    generate_components(
  +        args.components_source, args.project_shortname,
  +        package_info_filename=args.package_info_filename,
  +        ignore=args.ignore
  +    )
   
   
   if __name__ == '__main__':
  diff --git a/dash/version.py b/dash/version.py
  --- a/dash/version.py
  +++ b/dash/version.py
  @@ -1 +1 @@
  -__version__ = '0.33.0'
  +__version__ = '0.34.0'
  
verification_info: {"golden_diff": "diff --git a/dash/development/component_generator.py b/dash/development/component_generator.py\n--- a/dash/development/component_generator.py\n+++ b/dash/development/component_generator.py\n@@ -22,14 +22,17 @@\n \n # pylint: disable=too-many-locals\n def generate_components(components_source, project_shortname,\n-                        package_info_filename='package.json'):\n+                        package_info_filename='package.json',\n+                        ignore='^_'):\n     is_windows = sys.platform == 'win32'\n \n     extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')\n \n     os.environ['NODE_PATH'] = 'node_modules'\n-    cmd = shlex.split('node {} {}'.format(extract_path, components_source),\n-                      posix=not is_windows)\n+    cmd = shlex.split(\n+        'node {} {} {}'.format(extract_path, ignore, components_source),\n+        posix=not is_windows\n+    )\n \n     shutil.copyfile('package.json',\n                     os.path.join(project_shortname, package_info_filename))\n@@ -83,10 +86,18 @@\n         default='package.json',\n         help='The filename of the copied `package.json` to `project_shortname`'\n     )\n+    parser.add_argument(\n+        '-i', '--ignore',\n+        default='^_',\n+        help='Files/directories matching the pattern will be ignored'\n+    )\n \n     args = parser.parse_args()\n-    generate_components(args.components_source, args.project_shortname,\n-                        package_info_filename=args.package_info_filename)\n+    generate_components(\n+        args.components_source, args.project_shortname,\n+        package_info_filename=args.package_info_filename,\n+        ignore=args.ignore\n+    )\n \n \n if __name__ == '__main__':\ndiff --git a/dash/version.py b/dash/version.py\n--- a/dash/version.py\n+++ b/dash/version.py\n@@ -1 +1 @@\n-__version__ = '0.33.0'\n+__version__ = '0.34.0'\n", "issue": "Allow excluding file patterns when generating component definitions \nThanks a lot for making Dash -- we have found it very useful for building dashboards.\r\n\r\nWe ran into an issue migrating to the new `dash-generate-components` utility.\r\n\r\nIn [dash-bootstrap-components](https://github.com/ASIDataScience/dash-bootstrap-components), we keep the components in `src/components`. Besides components files, this also includes a few unit test files called, e.g. `src/components/__tests__/DropdownMenu.test.js`. When we run `dash-generate-components ./src/components <output>`, these test files also get picked up. This leads to a traceback in the build logs: \r\n\r\n```\r\nError with path src/components/__tests__/DropdownMenu.test.jsError: No suitable component definition found.\r\nError: No suitable component definition found.\r\nat parse (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/parse.js:84:9)\r\nat Object.defaultParse [as parse] (/project/pascal/dash-bootstrap-components/node_modules/react-docgen/dist/main.js:66:30)\r\nat parseFile (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:64:51)\r\nat dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:84:17)\r\nat Array.forEach (<anonymous>)\r\nat collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)\r\nat dirs.forEach.filename (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:82:17)\r\nat Array.forEach (<anonymous>)\r\nat collectMetadataRecursively (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:79:14)\r\nat componentPaths.forEach.componentPath (/opt/anaconda/envs/Python3/lib/python3.6/site-packages/dash/extract-meta.js:15:5)\r\n```\r\n\r\nWhile the error is, of course, legitimate, it would be nice to be able to suppress these tracebacks. I can see three routes:\r\n- do nothing -- after all, this doesn't stop us from building `metadata.json`, it just makes it slightly more confusing.\r\n- in `dash/extract-meta.js`, we could explicitly blacklist `__tests__` directories in the same way that files that don't end in `jsx?` are black-listed. AFAICT, the `__tests__` directory structure is the default with [jest](https://jestjs.io/docs/en/configuration.html#testmatch-array-string). [react-docgen](https://github.com/reactjs/react-docgen#cli) ignores `node_modules`, `__tests__` and `__mocks__` by default, so there is definitely a precedent.\r\n- add a `--ignore` argument to `dash-generate-components` that allows passing file globs to be excluded. \r\n\r\nVery happy to submit a PR if you decide on what the best course of action is.\n", "before_files": [{"content": "from __future__ import print_function\n\nimport json\nimport sys\nimport subprocess\nimport shlex\nimport os\nimport argparse\nimport shutil\n\nimport pkg_resources\n\nfrom ._py_components_generation import generate_class_file\nfrom ._py_components_generation import generate_imports\nfrom ._py_components_generation import generate_classes_files\n\n\nclass _CombinedFormatter(argparse.ArgumentDefaultsHelpFormatter,\n                         argparse.RawDescriptionHelpFormatter):\n    pass\n\n\n# pylint: disable=too-many-locals\ndef generate_components(components_source, project_shortname,\n                        package_info_filename='package.json'):\n    is_windows = sys.platform == 'win32'\n\n    extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')\n\n    os.environ['NODE_PATH'] = 'node_modules'\n    cmd = shlex.split('node {} {}'.format(extract_path, components_source),\n                      posix=not is_windows)\n\n    shutil.copyfile('package.json',\n                    os.path.join(project_shortname, package_info_filename))\n\n    proc = subprocess.Popen(cmd,\n                            stdout=subprocess.PIPE,\n                            stderr=subprocess.PIPE,\n                            shell=is_windows)\n    out, err = proc.communicate()\n    status = proc.poll()\n\n    if err:\n        print(err.decode(), file=sys.stderr)\n\n    if not out:\n        print(\n            'Error generating metadata in {} (status={})'.format(\n                project_shortname, status),\n            file=sys.stderr)\n        sys.exit(1)\n\n    metadata = json.loads(out.decode())\n\n    components = generate_classes_files(\n        project_shortname,\n        metadata,\n        generate_class_file\n    )\n\n    with open(os.path.join(project_shortname, 'metadata.json'), 'w') as f:\n        json.dump(metadata, f)\n\n    generate_imports(project_shortname, components)\n\n\ndef cli():\n    parser = argparse.ArgumentParser(\n        prog='dash-generate-components',\n        formatter_class=_CombinedFormatter,\n        description='Generate dash components by extracting the metadata '\n        'using react-docgen. Then map the metadata to python classes.'\n    )\n    parser.add_argument('components_source',\n                        help='React components source directory.')\n    parser.add_argument(\n        'project_shortname',\n        help='Name of the project to export the classes files.'\n    )\n    parser.add_argument(\n        '-p', '--package-info-filename',\n        default='package.json',\n        help='The filename of the copied `package.json` to `project_shortname`'\n    )\n\n    args = parser.parse_args()\n    generate_components(args.components_source, args.project_shortname,\n                        package_info_filename=args.package_info_filename)\n\n\nif __name__ == '__main__':\n    cli()\n", "path": "dash/development/component_generator.py"}, {"content": "__version__ = '0.33.0'\n", "path": "dash/version.py"}], "after_files": [{"content": "from __future__ import print_function\n\nimport json\nimport sys\nimport subprocess\nimport shlex\nimport os\nimport argparse\nimport shutil\n\nimport pkg_resources\n\nfrom ._py_components_generation import generate_class_file\nfrom ._py_components_generation import generate_imports\nfrom ._py_components_generation import generate_classes_files\n\n\nclass _CombinedFormatter(argparse.ArgumentDefaultsHelpFormatter,\n                         argparse.RawDescriptionHelpFormatter):\n    pass\n\n\n# pylint: disable=too-many-locals\ndef generate_components(components_source, project_shortname,\n                        package_info_filename='package.json',\n                        ignore='^_'):\n    is_windows = sys.platform == 'win32'\n\n    extract_path = pkg_resources.resource_filename('dash', 'extract-meta.js')\n\n    os.environ['NODE_PATH'] = 'node_modules'\n    cmd = shlex.split(\n        'node {} {} {}'.format(extract_path, ignore, components_source),\n        posix=not is_windows\n    )\n\n    shutil.copyfile('package.json',\n                    os.path.join(project_shortname, package_info_filename))\n\n    proc = subprocess.Popen(cmd,\n                            stdout=subprocess.PIPE,\n                            stderr=subprocess.PIPE,\n                            shell=is_windows)\n    out, err = proc.communicate()\n    status = proc.poll()\n\n    if err:\n        print(err.decode(), file=sys.stderr)\n\n    if not out:\n        print(\n            'Error generating metadata in {} (status={})'.format(\n                project_shortname, status),\n            file=sys.stderr)\n        sys.exit(1)\n\n    metadata = json.loads(out.decode())\n\n    components = generate_classes_files(\n        project_shortname,\n        metadata,\n        generate_class_file\n    )\n\n    with open(os.path.join(project_shortname, 'metadata.json'), 'w') as f:\n        json.dump(metadata, f)\n\n    generate_imports(project_shortname, components)\n\n\ndef cli():\n    parser = argparse.ArgumentParser(\n        prog='dash-generate-components',\n        formatter_class=_CombinedFormatter,\n        description='Generate dash components by extracting the metadata '\n        'using react-docgen. Then map the metadata to python classes.'\n    )\n    parser.add_argument('components_source',\n                        help='React components source directory.')\n    parser.add_argument(\n        'project_shortname',\n        help='Name of the project to export the classes files.'\n    )\n    parser.add_argument(\n        '-p', '--package-info-filename',\n        default='package.json',\n        help='The filename of the copied `package.json` to `project_shortname`'\n    )\n    parser.add_argument(\n        '-i', '--ignore',\n        default='^_',\n        help='Files/directories matching the pattern will be ignored'\n    )\n\n    args = parser.parse_args()\n    generate_components(\n        args.components_source, args.project_shortname,\n        package_info_filename=args.package_info_filename,\n        ignore=args.ignore\n    )\n\n\nif __name__ == '__main__':\n    cli()\n", "path": "dash/development/component_generator.py"}, {"content": "__version__ = '0.34.0'\n", "path": "dash/version.py"}]}