We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[BUG] + in version string breaks fingerprint system
**Describe your context**
-  replace the result of `pip list | grep dash` below
```
dash                                  1.5.1                           
dash-core-components                  1.4.0                           
dash-daq                              0.2.2                           
dash-html-components                  1.0.1                           
dash-renderer                         1.2.0                           
dash-table                            4.5.0  
```

**Describe the bug**

When going from `dash==1.4` to `dash==1.5`, we experienced a breaking change in the custom Dash components we use.

It took some hours to debug, but the reason was found to be related to the new "fingerprint" system in Dash. In our project, we use the [setuptools_scm](https://github.com/pypa/setuptools_scm) package (by the Python Packaging Authority) in order to have a versioning system that automatically is linked to the git repo tags. This makes continuous deployment to e.g. Pypi easy and robust wrt. keeping versions consistent.

I.e. instead of
```python
__version__ = package['version']
```
in the component package, we use something like
```
__version__ = get_distribution(__name__).version
```
This worked until `dash==1.5`, then it broke on non-release-versions due to automatic tags of the type
`1.0.0.dev5+af4304c.d20191103`, where the tag includes a `+`. See [the default tag formats](https://github.com/pypa/setuptools_scm#default-versioning-scheme).

Changing the line above to
```
__version__ = get_distribution(__name__).version.replace("+", ".")
```
is one workaround that gets the third party components to also work on `dash==1.5`

**Expected behavior**

`setuptools_scm` provided versions to work also in `dash>=1.5`.

**Suggested solution**

Change [this line](https://github.com/plotly/dash/blob/40b5357f262ac207f94ac980e6cb928d94df65b7/dash/fingerprint.py#L12) in Dash's `build_fingerprint` to also replace `+` with `_`?

--- 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/fingerprint.py`
Content:
```
1 import re
2 
3 cache_regex = re.compile(r"^v[\w-]+m[0-9a-fA-F]+$")
4 
5 
6 def build_fingerprint(path, version, hash_value):
7     path_parts = path.split("/")
8     filename, extension = path_parts[-1].split(".", 1)
9 
10     return "{}.v{}m{}.{}".format(
11         "/".join(path_parts[:-1] + [filename]),
12         str(version).replace(".", "_"),
13         hash_value,
14         extension,
15     )
16 
17 
18 def check_fingerprint(path):
19     path_parts = path.split("/")
20     name_parts = path_parts[-1].split(".")
21 
22     # Check if the resource has a fingerprint
23     if len(name_parts) > 2 and cache_regex.match(name_parts[1]):
24         original_name = ".".join([name_parts[0]] + name_parts[2:])
25         return "/".join(path_parts[:-1] + [original_name]), True
26 
27     return path, False
28
```
--- 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()

```

diff --git a/dash/fingerprint.py b/dash/fingerprint.py
--- a/dash/fingerprint.py
+++ b/dash/fingerprint.py
@@ -1,7 +1,7 @@
 import re
 
 cache_regex = re.compile(r"^v[\w-]+m[0-9a-fA-F]+$")
-
+version_clean = re.compile(r"[^\w-]")
 
 def build_fingerprint(path, version, hash_value):
     path_parts = path.split("/")
@@ -9,7 +9,7 @@
 
     return "{}.v{}m{}.{}".format(
         "/".join(path_parts[:-1] + [filename]),
-        str(version).replace(".", "_"),
+        re.sub(version_clean, "_", str(version)),
         hash_value,
         extension,
     )

{"golden_diff": "diff --git a/dash/fingerprint.py b/dash/fingerprint.py\n--- a/dash/fingerprint.py\n+++ b/dash/fingerprint.py\n@@ -1,7 +1,7 @@\n import re\n \n cache_regex = re.compile(r\"^v[\\w-]+m[0-9a-fA-F]+$\")\n-\n+version_clean = re.compile(r\"[^\\w-]\")\n \n def build_fingerprint(path, version, hash_value):\n     path_parts = path.split(\"/\")\n@@ -9,7 +9,7 @@\n \n     return \"{}.v{}m{}.{}\".format(\n         \"/\".join(path_parts[:-1] + [filename]),\n-        str(version).replace(\".\", \"_\"),\n+        re.sub(version_clean, \"_\", str(version)),\n         hash_value,\n         extension,\n     )\n", "issue": "[BUG] + in version string breaks fingerprint system\n**Describe your context**\r\n-  replace the result of `pip list | grep dash` below\r\n```\r\ndash                                  1.5.1                           \r\ndash-core-components                  1.4.0                           \r\ndash-daq                              0.2.2                           \r\ndash-html-components                  1.0.1                           \r\ndash-renderer                         1.2.0                           \r\ndash-table                            4.5.0  \r\n```\r\n\r\n**Describe the bug**\r\n\r\nWhen going from `dash==1.4` to `dash==1.5`, we experienced a breaking change in the custom Dash components we use.\r\n\r\nIt took some hours to debug, but the reason was found to be related to the new \"fingerprint\" system in Dash. In our project, we use the [setuptools_scm](https://github.com/pypa/setuptools_scm) package (by the Python Packaging Authority) in order to have a versioning system that automatically is linked to the git repo tags. This makes continuous deployment to e.g. Pypi easy and robust wrt. keeping versions consistent.\r\n\r\nI.e. instead of\r\n```python\r\n__version__ = package['version']\r\n```\r\nin the component package, we use something like\r\n```\r\n__version__ = get_distribution(__name__).version\r\n```\r\nThis worked until `dash==1.5`, then it broke on non-release-versions due to automatic tags of the type\r\n`1.0.0.dev5+af4304c.d20191103`, where the tag includes a `+`. See [the default tag formats](https://github.com/pypa/setuptools_scm#default-versioning-scheme).\r\n\r\nChanging the line above to\r\n```\r\n__version__ = get_distribution(__name__).version.replace(\"+\", \".\")\r\n```\r\nis one workaround that gets the third party components to also work on `dash==1.5`\r\n\r\n**Expected behavior**\r\n\r\n`setuptools_scm` provided versions to work also in `dash>=1.5`.\r\n\r\n**Suggested solution**\r\n\r\nChange [this line](https://github.com/plotly/dash/blob/40b5357f262ac207f94ac980e6cb928d94df65b7/dash/fingerprint.py#L12) in Dash's `build_fingerprint` to also replace `+` with `_`?\n", "before_files": [{"content": "import re\n\ncache_regex = re.compile(r\"^v[\\w-]+m[0-9a-fA-F]+$\")\n\n\ndef build_fingerprint(path, version, hash_value):\n    path_parts = path.split(\"/\")\n    filename, extension = path_parts[-1].split(\".\", 1)\n\n    return \"{}.v{}m{}.{}\".format(\n        \"/\".join(path_parts[:-1] + [filename]),\n        str(version).replace(\".\", \"_\"),\n        hash_value,\n        extension,\n    )\n\n\ndef check_fingerprint(path):\n    path_parts = path.split(\"/\")\n    name_parts = path_parts[-1].split(\".\")\n\n    # Check if the resource has a fingerprint\n    if len(name_parts) > 2 and cache_regex.match(name_parts[1]):\n        original_name = \".\".join([name_parts[0]] + name_parts[2:])\n        return \"/\".join(path_parts[:-1] + [original_name]), True\n\n    return path, False\n", "path": "dash/fingerprint.py"}], "after_files": [{"content": "import re\n\ncache_regex = re.compile(r\"^v[\\w-]+m[0-9a-fA-F]+$\")\nversion_clean = re.compile(r\"[^\\w-]\")\n\ndef build_fingerprint(path, version, hash_value):\n    path_parts = path.split(\"/\")\n    filename, extension = path_parts[-1].split(\".\", 1)\n\n    return \"{}.v{}m{}.{}\".format(\n        \"/\".join(path_parts[:-1] + [filename]),\n        re.sub(version_clean, \"_\", str(version)),\n        hash_value,\n        extension,\n    )\n\n\ndef check_fingerprint(path):\n    path_parts = path.split(\"/\")\n    name_parts = path_parts[-1].split(\".\")\n\n    # Check if the resource has a fingerprint\n    if len(name_parts) > 2 and cache_regex.match(name_parts[1]):\n        original_name = \".\".join([name_parts[0]] + name_parts[2:])\n        return \"/\".join(path_parts[:-1] + [original_name]), True\n\n    return path, False\n", "path": "dash/fingerprint.py"}]}