We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Split out Dash CI only requirements into `requires-ci.txt`?
Both `pip install dash[dev]` and `pip install dash[testing]` are extra public Dash features (respectively for building Dash components, and using the shipped Dash app test utilities).

Looking at [`requires-dev.txt`](https://github.com/plotly/dash/blob/dev/requires-dev.txt) and [`requires-testing.txt`](https://github.com/plotly/dash/blob/dev/requires-testing.txt), there appears to have leaked https://github.com/plotly/dash CI/test suite dependencies into both files (i.e. dependencies not used by the features in `dash[dev]` and `dash[testing]`). Latest one introduced is [`flaky`](https://github.com/plotly/dash/pull/1741).

Split out Dash CI only dependencies to a new file?

--- 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/long_callback/managers/diskcache_manager.py`
Content:
```
1 from . import BaseLongCallbackManager
2 
3 _pending_value = "__$pending__"
4 
5 
6 class DiskcacheLongCallbackManager(BaseLongCallbackManager):
7     def __init__(self, cache, cache_by=None, expire=None):
8         """
9         Long callback manager that runs callback logic in a subprocess and stores
10         results on disk using diskcache
11 
12         :param cache:
13             A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache
14             documentation for information on configuration options.
15         :param cache_by:
16             A list of zero-argument functions.  When provided, caching is enabled and
17             the return values of these functions are combined with the callback
18             function's input arguments and source code to generate cache keys.
19         :param expire:
20             If provided, a cache entry will be removed when it has not been accessed
21             for ``expire`` seconds.  If not provided, the lifetime of cache entries
22             is determined by the default behavior of the ``cache`` instance.
23         """
24         try:
25             import diskcache  # pylint: disable=import-outside-toplevel
26             import psutil  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable,import-error
27             import multiprocess  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable
28         except ImportError as missing_imports:
29             raise ImportError(
30                 """\
31 DiskcacheLongCallbackManager requires the multiprocess, diskcache, and psutil packages
32 which can be installed using pip...
33 
34     $ pip install multiprocess diskcache psutil
35 
36 or conda.
37 
38     $ conda install -c conda-forge multiprocess diskcache psutil\n"""
39             ) from missing_imports
40 
41         if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):
42             raise ValueError("First argument must be a diskcache.Cache object")
43         super().__init__(cache_by)
44         self.handle = cache
45         self.expire = expire
46 
47     def terminate_job(self, job):
48         import psutil  # pylint: disable=import-outside-toplevel,import-error
49 
50         if job is None:
51             return
52 
53         # Use diskcache transaction so multiple process don't try to kill the
54         # process at the same time
55         with self.handle.transact():
56             if psutil.pid_exists(job):
57                 process = psutil.Process(job)
58 
59                 for proc in process.children(recursive=True):
60                     try:
61                         proc.kill()
62                     except psutil.NoSuchProcess:
63                         pass
64 
65                 try:
66                     process.kill()
67                 except psutil.NoSuchProcess:
68                     pass
69 
70                 try:
71                     process.wait(0.5)
72                 except (psutil.TimeoutExpired, psutil.NoSuchProcess):
73                     pass
74 
75     def terminate_unhealthy_job(self, job):
76         import psutil  # pylint: disable=import-outside-toplevel,import-error
77 
78         if job and psutil.pid_exists(job):
79             if not self.job_running(job):
80                 self.terminate_job(job)
81                 return True
82 
83         return False
84 
85     def job_running(self, job):
86         import psutil  # pylint: disable=import-outside-toplevel,import-error
87 
88         if job and psutil.pid_exists(job):
89             proc = psutil.Process(job)
90             return proc.status() != psutil.STATUS_ZOMBIE
91         return False
92 
93     def make_job_fn(self, fn, progress, args_deps):
94         return _make_job_fn(fn, self.handle, progress, args_deps)
95 
96     def clear_cache_entry(self, key):
97         self.handle.delete(key)
98 
99     def call_job_fn(self, key, job_fn, args):
100         # pylint: disable-next=import-outside-toplevel,no-name-in-module,import-error
101         from multiprocess import Process
102 
103         # pylint: disable-next=not-callable
104         proc = Process(target=job_fn, args=(key, self._make_progress_key(key), args))
105         proc.start()
106         return proc.pid
107 
108     def get_progress(self, key):
109         progress_key = self._make_progress_key(key)
110         return self.handle.get(progress_key)
111 
112     def result_ready(self, key):
113         return self.handle.get(key) is not None
114 
115     def get_result(self, key, job):
116         # Get result value
117         result = self.handle.get(key)
118         if result is None:
119             return None
120 
121         # Clear result if not caching
122         if self.cache_by is None:
123             self.clear_cache_entry(key)
124         else:
125             if self.expire:
126                 self.handle.touch(key, expire=self.expire)
127 
128         self.clear_cache_entry(self._make_progress_key(key))
129 
130         self.terminate_job(job)
131         return result
132 
133 
134 def _make_job_fn(fn, cache, progress, args_deps):
135     def job_fn(result_key, progress_key, user_callback_args):
136         def _set_progress(progress_value):
137             cache.set(progress_key, progress_value)
138 
139         maybe_progress = [_set_progress] if progress else []
140         if isinstance(args_deps, dict):
141             user_callback_output = fn(*maybe_progress, **user_callback_args)
142         elif isinstance(args_deps, (list, tuple)):
143             user_callback_output = fn(*maybe_progress, *user_callback_args)
144         else:
145             user_callback_output = fn(*maybe_progress, user_callback_args)
146         cache.set(result_key, user_callback_output)
147 
148     return job_fn
149
```

Path: `dash/long_callback/managers/celery_manager.py`
Content:
```
1 import json
2 import inspect
3 import hashlib
4 
5 from _plotly_utils.utils import PlotlyJSONEncoder
6 from dash.long_callback.managers import BaseLongCallbackManager
7 
8 
9 class CeleryLongCallbackManager(BaseLongCallbackManager):
10     def __init__(self, celery_app, cache_by=None, expire=None):
11         """
12         Long callback manager that runs callback logic on a celery task queue,
13         and stores results using a celery result backend.
14 
15         :param celery_app:
16             A celery.Celery application instance that must be configured with a
17             result backend. See the celery documentation for information on
18             configuration options.
19         :param cache_by:
20             A list of zero-argument functions.  When provided, caching is enabled and
21             the return values of these functions are combined with the callback
22             function's input arguments and source code to generate cache keys.
23         :param expire:
24             If provided, a cache entry will be removed when it has not been accessed
25             for ``expire`` seconds.  If not provided, the lifetime of cache entries
26             is determined by the default behavior of the celery result backend.
27         """
28         import celery  # pylint: disable=import-outside-toplevel,import-error
29         from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error
30             DisabledBackend,
31         )
32 
33         if not isinstance(celery_app, celery.Celery):
34             raise ValueError("First argument must be a celery.Celery object")
35 
36         if isinstance(celery_app.backend, DisabledBackend):
37             raise ValueError("Celery instance must be configured with a result backend")
38 
39         super().__init__(cache_by)
40         self.handle = celery_app
41         self.expire = expire
42 
43     def terminate_job(self, job):
44         if job is None:
45             return
46 
47         self.handle.control.terminate(job)
48 
49     def terminate_unhealthy_job(self, job):
50         task = self.get_task(job)
51         if task and task.status in ("FAILURE", "REVOKED"):
52             return self.terminate_job(job)
53         return False
54 
55     def job_running(self, job):
56         future = self.get_task(job)
57         return future and future.status in (
58             "PENDING",
59             "RECEIVED",
60             "STARTED",
61             "RETRY",
62             "PROGRESS",
63         )
64 
65     def make_job_fn(self, fn, progress, args_deps):
66         return _make_job_fn(fn, self.handle, progress, args_deps)
67 
68     def get_task(self, job):
69         if job:
70             return self.handle.AsyncResult(job)
71 
72         return None
73 
74     def clear_cache_entry(self, key):
75         self.handle.backend.delete(key)
76 
77     def call_job_fn(self, key, job_fn, args):
78         task = job_fn.delay(key, self._make_progress_key(key), args)
79         return task.task_id
80 
81     def get_progress(self, key):
82         progress_key = self._make_progress_key(key)
83         progress_data = self.handle.backend.get(progress_key)
84         if progress_data:
85             return json.loads(progress_data)
86 
87         return None
88 
89     def result_ready(self, key):
90         return self.handle.backend.get(key) is not None
91 
92     def get_result(self, key, job):
93         # Get result value
94         result = self.handle.backend.get(key)
95         if result is None:
96             return None
97 
98         result = json.loads(result)
99 
100         # Clear result if not caching
101         if self.cache_by is None:
102             self.clear_cache_entry(key)
103         else:
104             if self.expire:
105                 # Set/update expiration time
106                 self.handle.backend.expire(key, self.expire)
107         self.clear_cache_entry(self._make_progress_key(key))
108 
109         self.terminate_job(job)
110         return result
111 
112 
113 def _make_job_fn(fn, celery_app, progress, args_deps):
114     cache = celery_app.backend
115 
116     # Hash function source and module to create a unique (but stable) celery task name
117     fn_source = inspect.getsource(fn)
118     fn_str = fn_source
119     fn_hash = hashlib.sha1(fn_str.encode("utf-8")).hexdigest()
120 
121     @celery_app.task(name=f"long_callback_{fn_hash}")
122     def job_fn(result_key, progress_key, user_callback_args, fn=fn):
123         def _set_progress(progress_value):
124             cache.set(progress_key, json.dumps(progress_value, cls=PlotlyJSONEncoder))
125 
126         maybe_progress = [_set_progress] if progress else []
127         if isinstance(args_deps, dict):
128             user_callback_output = fn(*maybe_progress, **user_callback_args)
129         elif isinstance(args_deps, (list, tuple)):
130             user_callback_output = fn(*maybe_progress, *user_callback_args)
131         else:
132             user_callback_output = fn(*maybe_progress, user_callback_args)
133 
134         cache.set(result_key, json.dumps(user_callback_output, cls=PlotlyJSONEncoder))
135 
136     return job_fn
137
```

Path: `setup.py`
Content:
```
1 import io
2 from setuptools import setup, find_packages
3 
4 main_ns = {}
5 exec(open("dash/version.py").read(), main_ns)  # pylint: disable=exec-used, consider-using-with
6 
7 
8 def read_req_file(req_type):
9     with open("requires-{}.txt".format(req_type)) as fp:
10         requires = (line.strip() for line in fp)
11         return [req for req in requires if req and not req.startswith("#")]
12 
13 
14 setup(
15     name="dash",
16     version=main_ns["__version__"],
17     author="Chris Parmer",
18     author_email="chris@plotly.com",
19     packages=find_packages(exclude=["tests*"]),
20     include_package_data=True,
21     license="MIT",
22     description=(
23         "A Python framework for building reactive web-apps. "
24         "Developed by Plotly."
25     ),
26     long_description=io.open("README.md", encoding="utf-8").read(),  # pylint: disable=consider-using-with
27     long_description_content_type="text/markdown",
28     install_requires=read_req_file("install"),
29     python_requires=">=3.6",
30     extras_require={
31         "dev": read_req_file("dev"),
32         "testing": read_req_file("testing"),
33     },
34     entry_points={
35         "console_scripts": [
36             "dash-generate-components = "
37             "dash.development.component_generator:cli",
38             "renderer = dash.development.build_process:renderer",
39             "dash-update-components = dash.development.update_components:cli"
40         ],
41         "pytest11": ["dash = dash.testing.plugin"],
42     },
43     url="https://plotly.com/dash",
44     classifiers=[
45         "Development Status :: 5 - Production/Stable",
46         "Environment :: Web Environment",
47         "Framework :: Dash",
48         "Framework :: Flask",
49         "Intended Audience :: Developers",
50         "Intended Audience :: Education",
51         "Intended Audience :: Financial and Insurance Industry",
52         "Intended Audience :: Healthcare Industry",
53         "Intended Audience :: Manufacturing",
54         "Intended Audience :: Science/Research",
55         "License :: OSI Approved :: MIT License",
56         "Programming Language :: Python",
57         "Programming Language :: Python :: 3",
58         "Programming Language :: Python :: 3.6",
59         "Programming Language :: Python :: 3.7",
60         "Programming Language :: Python :: 3.8",
61         "Programming Language :: Python :: 3.9",
62         "Topic :: Database :: Front-Ends",
63         "Topic :: Office/Business :: Financial :: Spreadsheet",
64         "Topic :: Scientific/Engineering :: Visualization",
65         "Topic :: Software Development :: Libraries :: Application Frameworks",
66         "Topic :: Software Development :: Widget Sets",
67     ],
68 )
69
```
--- 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/long_callback/managers/celery_manager.py b/dash/long_callback/managers/celery_manager.py
--- a/dash/long_callback/managers/celery_manager.py
+++ b/dash/long_callback/managers/celery_manager.py
@@ -25,10 +25,18 @@
             for ``expire`` seconds.  If not provided, the lifetime of cache entries
             is determined by the default behavior of the celery result backend.
         """
-        import celery  # pylint: disable=import-outside-toplevel,import-error
-        from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error
-            DisabledBackend,
-        )
+        try:
+            import celery  # pylint: disable=import-outside-toplevel,import-error
+            from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error
+                DisabledBackend,
+            )
+        except ImportError as missing_imports:
+            raise ImportError(
+                """\
+CeleryLongCallbackManager requires extra dependencies which can be installed doing
+
+    $ pip install "dash[celery]"\n"""
+            ) from missing_imports
 
         if not isinstance(celery_app, celery.Celery):
             raise ValueError("First argument must be a celery.Celery object")
diff --git a/dash/long_callback/managers/diskcache_manager.py b/dash/long_callback/managers/diskcache_manager.py
--- a/dash/long_callback/managers/diskcache_manager.py
+++ b/dash/long_callback/managers/diskcache_manager.py
@@ -4,14 +4,15 @@
 
 
 class DiskcacheLongCallbackManager(BaseLongCallbackManager):
-    def __init__(self, cache, cache_by=None, expire=None):
+    def __init__(self, cache=None, cache_by=None, expire=None):
         """
         Long callback manager that runs callback logic in a subprocess and stores
         results on disk using diskcache
 
         :param cache:
             A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache
-            documentation for information on configuration options.
+            documentation for information on configuration options. If not provided,
+            a diskcache.Cache instance will be created with default values.
         :param cache_by:
             A list of zero-argument functions.  When provided, caching is enabled and
             the return values of these functions are combined with the callback
@@ -28,20 +29,22 @@
         except ImportError as missing_imports:
             raise ImportError(
                 """\
-DiskcacheLongCallbackManager requires the multiprocess, diskcache, and psutil packages
-which can be installed using pip...
+DiskcacheLongCallbackManager requires extra dependencies which can be installed doing
 
-    $ pip install multiprocess diskcache psutil
-
-or conda.
-
-    $ conda install -c conda-forge multiprocess diskcache psutil\n"""
+    $ pip install "dash[diskcache]"\n"""
             ) from missing_imports
 
-        if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):
-            raise ValueError("First argument must be a diskcache.Cache object")
+        if cache is None:
+            self.handle = diskcache.Cache()
+        else:
+            if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):
+                raise ValueError(
+                    "First argument must be a diskcache.Cache "
+                    "or diskcache.FanoutCache object"
+                )
+            self.handle = cache
+
         super().__init__(cache_by)
-        self.handle = cache
         self.expire = expire
 
     def terminate_job(self, job):
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -28,8 +28,11 @@
     install_requires=read_req_file("install"),
     python_requires=">=3.6",
     extras_require={
+        "ci": read_req_file("ci"),
         "dev": read_req_file("dev"),
         "testing": read_req_file("testing"),
+        "celery": read_req_file("celery"),
+        "diskcache": read_req_file("diskcache"),
     },
     entry_points={
         "console_scripts": [

{"golden_diff": "diff --git a/dash/long_callback/managers/celery_manager.py b/dash/long_callback/managers/celery_manager.py\n--- a/dash/long_callback/managers/celery_manager.py\n+++ b/dash/long_callback/managers/celery_manager.py\n@@ -25,10 +25,18 @@\n             for ``expire`` seconds.  If not provided, the lifetime of cache entries\n             is determined by the default behavior of the celery result backend.\n         \"\"\"\n-        import celery  # pylint: disable=import-outside-toplevel,import-error\n-        from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error\n-            DisabledBackend,\n-        )\n+        try:\n+            import celery  # pylint: disable=import-outside-toplevel,import-error\n+            from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error\n+                DisabledBackend,\n+            )\n+        except ImportError as missing_imports:\n+            raise ImportError(\n+                \"\"\"\\\n+CeleryLongCallbackManager requires extra dependencies which can be installed doing\n+\n+    $ pip install \"dash[celery]\"\\n\"\"\"\n+            ) from missing_imports\n \n         if not isinstance(celery_app, celery.Celery):\n             raise ValueError(\"First argument must be a celery.Celery object\")\ndiff --git a/dash/long_callback/managers/diskcache_manager.py b/dash/long_callback/managers/diskcache_manager.py\n--- a/dash/long_callback/managers/diskcache_manager.py\n+++ b/dash/long_callback/managers/diskcache_manager.py\n@@ -4,14 +4,15 @@\n \n \n class DiskcacheLongCallbackManager(BaseLongCallbackManager):\n-    def __init__(self, cache, cache_by=None, expire=None):\n+    def __init__(self, cache=None, cache_by=None, expire=None):\n         \"\"\"\n         Long callback manager that runs callback logic in a subprocess and stores\n         results on disk using diskcache\n \n         :param cache:\n             A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache\n-            documentation for information on configuration options.\n+            documentation for information on configuration options. If not provided,\n+            a diskcache.Cache instance will be created with default values.\n         :param cache_by:\n             A list of zero-argument functions.  When provided, caching is enabled and\n             the return values of these functions are combined with the callback\n@@ -28,20 +29,22 @@\n         except ImportError as missing_imports:\n             raise ImportError(\n                 \"\"\"\\\n-DiskcacheLongCallbackManager requires the multiprocess, diskcache, and psutil packages\n-which can be installed using pip...\n+DiskcacheLongCallbackManager requires extra dependencies which can be installed doing\n \n-    $ pip install multiprocess diskcache psutil\n-\n-or conda.\n-\n-    $ conda install -c conda-forge multiprocess diskcache psutil\\n\"\"\"\n+    $ pip install \"dash[diskcache]\"\\n\"\"\"\n             ) from missing_imports\n \n-        if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):\n-            raise ValueError(\"First argument must be a diskcache.Cache object\")\n+        if cache is None:\n+            self.handle = diskcache.Cache()\n+        else:\n+            if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):\n+                raise ValueError(\n+                    \"First argument must be a diskcache.Cache \"\n+                    \"or diskcache.FanoutCache object\"\n+                )\n+            self.handle = cache\n+\n         super().__init__(cache_by)\n-        self.handle = cache\n         self.expire = expire\n \n     def terminate_job(self, job):\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -28,8 +28,11 @@\n     install_requires=read_req_file(\"install\"),\n     python_requires=\">=3.6\",\n     extras_require={\n+        \"ci\": read_req_file(\"ci\"),\n         \"dev\": read_req_file(\"dev\"),\n         \"testing\": read_req_file(\"testing\"),\n+        \"celery\": read_req_file(\"celery\"),\n+        \"diskcache\": read_req_file(\"diskcache\"),\n     },\n     entry_points={\n         \"console_scripts\": [\n", "issue": "Split out Dash CI only requirements into `requires-ci.txt`?\nBoth `pip install dash[dev]` and `pip install dash[testing]` are extra public Dash features (respectively for building Dash components, and using the shipped Dash app test utilities).\r\n\r\nLooking at [`requires-dev.txt`](https://github.com/plotly/dash/blob/dev/requires-dev.txt) and [`requires-testing.txt`](https://github.com/plotly/dash/blob/dev/requires-testing.txt), there appears to have leaked https://github.com/plotly/dash CI/test suite dependencies into both files (i.e. dependencies not used by the features in `dash[dev]` and `dash[testing]`). Latest one introduced is [`flaky`](https://github.com/plotly/dash/pull/1741).\r\n\r\nSplit out Dash CI only dependencies to a new file?\n", "before_files": [{"content": "from . import BaseLongCallbackManager\n\n_pending_value = \"__$pending__\"\n\n\nclass DiskcacheLongCallbackManager(BaseLongCallbackManager):\n    def __init__(self, cache, cache_by=None, expire=None):\n        \"\"\"\n        Long callback manager that runs callback logic in a subprocess and stores\n        results on disk using diskcache\n\n        :param cache:\n            A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache\n            documentation for information on configuration options.\n        :param cache_by:\n            A list of zero-argument functions.  When provided, caching is enabled and\n            the return values of these functions are combined with the callback\n            function's input arguments and source code to generate cache keys.\n        :param expire:\n            If provided, a cache entry will be removed when it has not been accessed\n            for ``expire`` seconds.  If not provided, the lifetime of cache entries\n            is determined by the default behavior of the ``cache`` instance.\n        \"\"\"\n        try:\n            import diskcache  # pylint: disable=import-outside-toplevel\n            import psutil  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable,import-error\n            import multiprocess  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable\n        except ImportError as missing_imports:\n            raise ImportError(\n                \"\"\"\\\nDiskcacheLongCallbackManager requires the multiprocess, diskcache, and psutil packages\nwhich can be installed using pip...\n\n    $ pip install multiprocess diskcache psutil\n\nor conda.\n\n    $ conda install -c conda-forge multiprocess diskcache psutil\\n\"\"\"\n            ) from missing_imports\n\n        if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):\n            raise ValueError(\"First argument must be a diskcache.Cache object\")\n        super().__init__(cache_by)\n        self.handle = cache\n        self.expire = expire\n\n    def terminate_job(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job is None:\n            return\n\n        # Use diskcache transaction so multiple process don't try to kill the\n        # process at the same time\n        with self.handle.transact():\n            if psutil.pid_exists(job):\n                process = psutil.Process(job)\n\n                for proc in process.children(recursive=True):\n                    try:\n                        proc.kill()\n                    except psutil.NoSuchProcess:\n                        pass\n\n                try:\n                    process.kill()\n                except psutil.NoSuchProcess:\n                    pass\n\n                try:\n                    process.wait(0.5)\n                except (psutil.TimeoutExpired, psutil.NoSuchProcess):\n                    pass\n\n    def terminate_unhealthy_job(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job and psutil.pid_exists(job):\n            if not self.job_running(job):\n                self.terminate_job(job)\n                return True\n\n        return False\n\n    def job_running(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job and psutil.pid_exists(job):\n            proc = psutil.Process(job)\n            return proc.status() != psutil.STATUS_ZOMBIE\n        return False\n\n    def make_job_fn(self, fn, progress, args_deps):\n        return _make_job_fn(fn, self.handle, progress, args_deps)\n\n    def clear_cache_entry(self, key):\n        self.handle.delete(key)\n\n    def call_job_fn(self, key, job_fn, args):\n        # pylint: disable-next=import-outside-toplevel,no-name-in-module,import-error\n        from multiprocess import Process\n\n        # pylint: disable-next=not-callable\n        proc = Process(target=job_fn, args=(key, self._make_progress_key(key), args))\n        proc.start()\n        return proc.pid\n\n    def get_progress(self, key):\n        progress_key = self._make_progress_key(key)\n        return self.handle.get(progress_key)\n\n    def result_ready(self, key):\n        return self.handle.get(key) is not None\n\n    def get_result(self, key, job):\n        # Get result value\n        result = self.handle.get(key)\n        if result is None:\n            return None\n\n        # Clear result if not caching\n        if self.cache_by is None:\n            self.clear_cache_entry(key)\n        else:\n            if self.expire:\n                self.handle.touch(key, expire=self.expire)\n\n        self.clear_cache_entry(self._make_progress_key(key))\n\n        self.terminate_job(job)\n        return result\n\n\ndef _make_job_fn(fn, cache, progress, args_deps):\n    def job_fn(result_key, progress_key, user_callback_args):\n        def _set_progress(progress_value):\n            cache.set(progress_key, progress_value)\n\n        maybe_progress = [_set_progress] if progress else []\n        if isinstance(args_deps, dict):\n            user_callback_output = fn(*maybe_progress, **user_callback_args)\n        elif isinstance(args_deps, (list, tuple)):\n            user_callback_output = fn(*maybe_progress, *user_callback_args)\n        else:\n            user_callback_output = fn(*maybe_progress, user_callback_args)\n        cache.set(result_key, user_callback_output)\n\n    return job_fn\n", "path": "dash/long_callback/managers/diskcache_manager.py"}, {"content": "import json\nimport inspect\nimport hashlib\n\nfrom _plotly_utils.utils import PlotlyJSONEncoder\nfrom dash.long_callback.managers import BaseLongCallbackManager\n\n\nclass CeleryLongCallbackManager(BaseLongCallbackManager):\n    def __init__(self, celery_app, cache_by=None, expire=None):\n        \"\"\"\n        Long callback manager that runs callback logic on a celery task queue,\n        and stores results using a celery result backend.\n\n        :param celery_app:\n            A celery.Celery application instance that must be configured with a\n            result backend. See the celery documentation for information on\n            configuration options.\n        :param cache_by:\n            A list of zero-argument functions.  When provided, caching is enabled and\n            the return values of these functions are combined with the callback\n            function's input arguments and source code to generate cache keys.\n        :param expire:\n            If provided, a cache entry will be removed when it has not been accessed\n            for ``expire`` seconds.  If not provided, the lifetime of cache entries\n            is determined by the default behavior of the celery result backend.\n        \"\"\"\n        import celery  # pylint: disable=import-outside-toplevel,import-error\n        from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error\n            DisabledBackend,\n        )\n\n        if not isinstance(celery_app, celery.Celery):\n            raise ValueError(\"First argument must be a celery.Celery object\")\n\n        if isinstance(celery_app.backend, DisabledBackend):\n            raise ValueError(\"Celery instance must be configured with a result backend\")\n\n        super().__init__(cache_by)\n        self.handle = celery_app\n        self.expire = expire\n\n    def terminate_job(self, job):\n        if job is None:\n            return\n\n        self.handle.control.terminate(job)\n\n    def terminate_unhealthy_job(self, job):\n        task = self.get_task(job)\n        if task and task.status in (\"FAILURE\", \"REVOKED\"):\n            return self.terminate_job(job)\n        return False\n\n    def job_running(self, job):\n        future = self.get_task(job)\n        return future and future.status in (\n            \"PENDING\",\n            \"RECEIVED\",\n            \"STARTED\",\n            \"RETRY\",\n            \"PROGRESS\",\n        )\n\n    def make_job_fn(self, fn, progress, args_deps):\n        return _make_job_fn(fn, self.handle, progress, args_deps)\n\n    def get_task(self, job):\n        if job:\n            return self.handle.AsyncResult(job)\n\n        return None\n\n    def clear_cache_entry(self, key):\n        self.handle.backend.delete(key)\n\n    def call_job_fn(self, key, job_fn, args):\n        task = job_fn.delay(key, self._make_progress_key(key), args)\n        return task.task_id\n\n    def get_progress(self, key):\n        progress_key = self._make_progress_key(key)\n        progress_data = self.handle.backend.get(progress_key)\n        if progress_data:\n            return json.loads(progress_data)\n\n        return None\n\n    def result_ready(self, key):\n        return self.handle.backend.get(key) is not None\n\n    def get_result(self, key, job):\n        # Get result value\n        result = self.handle.backend.get(key)\n        if result is None:\n            return None\n\n        result = json.loads(result)\n\n        # Clear result if not caching\n        if self.cache_by is None:\n            self.clear_cache_entry(key)\n        else:\n            if self.expire:\n                # Set/update expiration time\n                self.handle.backend.expire(key, self.expire)\n        self.clear_cache_entry(self._make_progress_key(key))\n\n        self.terminate_job(job)\n        return result\n\n\ndef _make_job_fn(fn, celery_app, progress, args_deps):\n    cache = celery_app.backend\n\n    # Hash function source and module to create a unique (but stable) celery task name\n    fn_source = inspect.getsource(fn)\n    fn_str = fn_source\n    fn_hash = hashlib.sha1(fn_str.encode(\"utf-8\")).hexdigest()\n\n    @celery_app.task(name=f\"long_callback_{fn_hash}\")\n    def job_fn(result_key, progress_key, user_callback_args, fn=fn):\n        def _set_progress(progress_value):\n            cache.set(progress_key, json.dumps(progress_value, cls=PlotlyJSONEncoder))\n\n        maybe_progress = [_set_progress] if progress else []\n        if isinstance(args_deps, dict):\n            user_callback_output = fn(*maybe_progress, **user_callback_args)\n        elif isinstance(args_deps, (list, tuple)):\n            user_callback_output = fn(*maybe_progress, *user_callback_args)\n        else:\n            user_callback_output = fn(*maybe_progress, user_callback_args)\n\n        cache.set(result_key, json.dumps(user_callback_output, cls=PlotlyJSONEncoder))\n\n    return job_fn\n", "path": "dash/long_callback/managers/celery_manager.py"}, {"content": "import io\nfrom setuptools import setup, find_packages\n\nmain_ns = {}\nexec(open(\"dash/version.py\").read(), main_ns)  # pylint: disable=exec-used, consider-using-with\n\n\ndef read_req_file(req_type):\n    with open(\"requires-{}.txt\".format(req_type)) as fp:\n        requires = (line.strip() for line in fp)\n        return [req for req in requires if req and not req.startswith(\"#\")]\n\n\nsetup(\n    name=\"dash\",\n    version=main_ns[\"__version__\"],\n    author=\"Chris Parmer\",\n    author_email=\"chris@plotly.com\",\n    packages=find_packages(exclude=[\"tests*\"]),\n    include_package_data=True,\n    license=\"MIT\",\n    description=(\n        \"A Python framework for building reactive web-apps. \"\n        \"Developed by Plotly.\"\n    ),\n    long_description=io.open(\"README.md\", encoding=\"utf-8\").read(),  # pylint: disable=consider-using-with\n    long_description_content_type=\"text/markdown\",\n    install_requires=read_req_file(\"install\"),\n    python_requires=\">=3.6\",\n    extras_require={\n        \"dev\": read_req_file(\"dev\"),\n        \"testing\": read_req_file(\"testing\"),\n    },\n    entry_points={\n        \"console_scripts\": [\n            \"dash-generate-components = \"\n            \"dash.development.component_generator:cli\",\n            \"renderer = dash.development.build_process:renderer\",\n            \"dash-update-components = dash.development.update_components:cli\"\n        ],\n        \"pytest11\": [\"dash = dash.testing.plugin\"],\n    },\n    url=\"https://plotly.com/dash\",\n    classifiers=[\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Web Environment\",\n        \"Framework :: Dash\",\n        \"Framework :: Flask\",\n        \"Intended Audience :: Developers\",\n        \"Intended Audience :: Education\",\n        \"Intended Audience :: Financial and Insurance Industry\",\n        \"Intended Audience :: Healthcare Industry\",\n        \"Intended Audience :: Manufacturing\",\n        \"Intended Audience :: Science/Research\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Topic :: Database :: Front-Ends\",\n        \"Topic :: Office/Business :: Financial :: Spreadsheet\",\n        \"Topic :: Scientific/Engineering :: Visualization\",\n        \"Topic :: Software Development :: Libraries :: Application Frameworks\",\n        \"Topic :: Software Development :: Widget Sets\",\n    ],\n)\n", "path": "setup.py"}], "after_files": [{"content": "from . import BaseLongCallbackManager\n\n_pending_value = \"__$pending__\"\n\n\nclass DiskcacheLongCallbackManager(BaseLongCallbackManager):\n    def __init__(self, cache=None, cache_by=None, expire=None):\n        \"\"\"\n        Long callback manager that runs callback logic in a subprocess and stores\n        results on disk using diskcache\n\n        :param cache:\n            A diskcache.Cache or diskcache.FanoutCache instance. See the diskcache\n            documentation for information on configuration options. If not provided,\n            a diskcache.Cache instance will be created with default values.\n        :param cache_by:\n            A list of zero-argument functions.  When provided, caching is enabled and\n            the return values of these functions are combined with the callback\n            function's input arguments and source code to generate cache keys.\n        :param expire:\n            If provided, a cache entry will be removed when it has not been accessed\n            for ``expire`` seconds.  If not provided, the lifetime of cache entries\n            is determined by the default behavior of the ``cache`` instance.\n        \"\"\"\n        try:\n            import diskcache  # pylint: disable=import-outside-toplevel\n            import psutil  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable,import-error\n            import multiprocess  # noqa: F401,E402 pylint: disable=import-outside-toplevel,unused-import,unused-variable\n        except ImportError as missing_imports:\n            raise ImportError(\n                \"\"\"\\\nDiskcacheLongCallbackManager requires extra dependencies which can be installed doing\n\n    $ pip install \"dash[diskcache]\"\\n\"\"\"\n            ) from missing_imports\n\n        if cache is None:\n            self.handle = diskcache.Cache()\n        else:\n            if not isinstance(cache, (diskcache.Cache, diskcache.FanoutCache)):\n                raise ValueError(\n                    \"First argument must be a diskcache.Cache \"\n                    \"or diskcache.FanoutCache object\"\n                )\n            self.handle = cache\n\n        super().__init__(cache_by)\n        self.expire = expire\n\n    def terminate_job(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job is None:\n            return\n\n        # Use diskcache transaction so multiple process don't try to kill the\n        # process at the same time\n        with self.handle.transact():\n            if psutil.pid_exists(job):\n                process = psutil.Process(job)\n\n                for proc in process.children(recursive=True):\n                    try:\n                        proc.kill()\n                    except psutil.NoSuchProcess:\n                        pass\n\n                try:\n                    process.kill()\n                except psutil.NoSuchProcess:\n                    pass\n\n                try:\n                    process.wait(0.5)\n                except (psutil.TimeoutExpired, psutil.NoSuchProcess):\n                    pass\n\n    def terminate_unhealthy_job(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job and psutil.pid_exists(job):\n            if not self.job_running(job):\n                self.terminate_job(job)\n                return True\n\n        return False\n\n    def job_running(self, job):\n        import psutil  # pylint: disable=import-outside-toplevel,import-error\n\n        if job and psutil.pid_exists(job):\n            proc = psutil.Process(job)\n            return proc.status() != psutil.STATUS_ZOMBIE\n        return False\n\n    def make_job_fn(self, fn, progress, args_deps):\n        return _make_job_fn(fn, self.handle, progress, args_deps)\n\n    def clear_cache_entry(self, key):\n        self.handle.delete(key)\n\n    def call_job_fn(self, key, job_fn, args):\n        # pylint: disable-next=import-outside-toplevel,no-name-in-module,import-error\n        from multiprocess import Process\n\n        # pylint: disable-next=not-callable\n        proc = Process(target=job_fn, args=(key, self._make_progress_key(key), args))\n        proc.start()\n        return proc.pid\n\n    def get_progress(self, key):\n        progress_key = self._make_progress_key(key)\n        return self.handle.get(progress_key)\n\n    def result_ready(self, key):\n        return self.handle.get(key) is not None\n\n    def get_result(self, key, job):\n        # Get result value\n        result = self.handle.get(key)\n        if result is None:\n            return None\n\n        # Clear result if not caching\n        if self.cache_by is None:\n            self.clear_cache_entry(key)\n        else:\n            if self.expire:\n                self.handle.touch(key, expire=self.expire)\n\n        self.clear_cache_entry(self._make_progress_key(key))\n\n        self.terminate_job(job)\n        return result\n\n\ndef _make_job_fn(fn, cache, progress, args_deps):\n    def job_fn(result_key, progress_key, user_callback_args):\n        def _set_progress(progress_value):\n            cache.set(progress_key, progress_value)\n\n        maybe_progress = [_set_progress] if progress else []\n        if isinstance(args_deps, dict):\n            user_callback_output = fn(*maybe_progress, **user_callback_args)\n        elif isinstance(args_deps, (list, tuple)):\n            user_callback_output = fn(*maybe_progress, *user_callback_args)\n        else:\n            user_callback_output = fn(*maybe_progress, user_callback_args)\n        cache.set(result_key, user_callback_output)\n\n    return job_fn\n", "path": "dash/long_callback/managers/diskcache_manager.py"}, {"content": "import json\nimport inspect\nimport hashlib\n\nfrom _plotly_utils.utils import PlotlyJSONEncoder\nfrom dash.long_callback.managers import BaseLongCallbackManager\n\n\nclass CeleryLongCallbackManager(BaseLongCallbackManager):\n    def __init__(self, celery_app, cache_by=None, expire=None):\n        \"\"\"\n        Long callback manager that runs callback logic on a celery task queue,\n        and stores results using a celery result backend.\n\n        :param celery_app:\n            A celery.Celery application instance that must be configured with a\n            result backend. See the celery documentation for information on\n            configuration options.\n        :param cache_by:\n            A list of zero-argument functions.  When provided, caching is enabled and\n            the return values of these functions are combined with the callback\n            function's input arguments and source code to generate cache keys.\n        :param expire:\n            If provided, a cache entry will be removed when it has not been accessed\n            for ``expire`` seconds.  If not provided, the lifetime of cache entries\n            is determined by the default behavior of the celery result backend.\n        \"\"\"\n        try:\n            import celery  # pylint: disable=import-outside-toplevel,import-error\n            from celery.backends.base import (  # pylint: disable=import-outside-toplevel,import-error\n                DisabledBackend,\n            )\n        except ImportError as missing_imports:\n            raise ImportError(\n                \"\"\"\\\nCeleryLongCallbackManager requires extra dependencies which can be installed doing\n\n    $ pip install \"dash[celery]\"\\n\"\"\"\n            ) from missing_imports\n\n        if not isinstance(celery_app, celery.Celery):\n            raise ValueError(\"First argument must be a celery.Celery object\")\n\n        if isinstance(celery_app.backend, DisabledBackend):\n            raise ValueError(\"Celery instance must be configured with a result backend\")\n\n        super().__init__(cache_by)\n        self.handle = celery_app\n        self.expire = expire\n\n    def terminate_job(self, job):\n        if job is None:\n            return\n\n        self.handle.control.terminate(job)\n\n    def terminate_unhealthy_job(self, job):\n        task = self.get_task(job)\n        if task and task.status in (\"FAILURE\", \"REVOKED\"):\n            return self.terminate_job(job)\n        return False\n\n    def job_running(self, job):\n        future = self.get_task(job)\n        return future and future.status in (\n            \"PENDING\",\n            \"RECEIVED\",\n            \"STARTED\",\n            \"RETRY\",\n            \"PROGRESS\",\n        )\n\n    def make_job_fn(self, fn, progress, args_deps):\n        return _make_job_fn(fn, self.handle, progress, args_deps)\n\n    def get_task(self, job):\n        if job:\n            return self.handle.AsyncResult(job)\n\n        return None\n\n    def clear_cache_entry(self, key):\n        self.handle.backend.delete(key)\n\n    def call_job_fn(self, key, job_fn, args):\n        task = job_fn.delay(key, self._make_progress_key(key), args)\n        return task.task_id\n\n    def get_progress(self, key):\n        progress_key = self._make_progress_key(key)\n        progress_data = self.handle.backend.get(progress_key)\n        if progress_data:\n            return json.loads(progress_data)\n\n        return None\n\n    def result_ready(self, key):\n        return self.handle.backend.get(key) is not None\n\n    def get_result(self, key, job):\n        # Get result value\n        result = self.handle.backend.get(key)\n        if result is None:\n            return None\n\n        result = json.loads(result)\n\n        # Clear result if not caching\n        if self.cache_by is None:\n            self.clear_cache_entry(key)\n        else:\n            if self.expire:\n                # Set/update expiration time\n                self.handle.backend.expire(key, self.expire)\n        self.clear_cache_entry(self._make_progress_key(key))\n\n        self.terminate_job(job)\n        return result\n\n\ndef _make_job_fn(fn, celery_app, progress, args_deps):\n    cache = celery_app.backend\n\n    # Hash function source and module to create a unique (but stable) celery task name\n    fn_source = inspect.getsource(fn)\n    fn_str = fn_source\n    fn_hash = hashlib.sha1(fn_str.encode(\"utf-8\")).hexdigest()\n\n    @celery_app.task(name=f\"long_callback_{fn_hash}\")\n    def job_fn(result_key, progress_key, user_callback_args, fn=fn):\n        def _set_progress(progress_value):\n            cache.set(progress_key, json.dumps(progress_value, cls=PlotlyJSONEncoder))\n\n        maybe_progress = [_set_progress] if progress else []\n        if isinstance(args_deps, dict):\n            user_callback_output = fn(*maybe_progress, **user_callback_args)\n        elif isinstance(args_deps, (list, tuple)):\n            user_callback_output = fn(*maybe_progress, *user_callback_args)\n        else:\n            user_callback_output = fn(*maybe_progress, user_callback_args)\n\n        cache.set(result_key, json.dumps(user_callback_output, cls=PlotlyJSONEncoder))\n\n    return job_fn\n", "path": "dash/long_callback/managers/celery_manager.py"}, {"content": "import io\nfrom setuptools import setup, find_packages\n\nmain_ns = {}\nexec(open(\"dash/version.py\").read(), main_ns)  # pylint: disable=exec-used, consider-using-with\n\n\ndef read_req_file(req_type):\n    with open(\"requires-{}.txt\".format(req_type)) as fp:\n        requires = (line.strip() for line in fp)\n        return [req for req in requires if req and not req.startswith(\"#\")]\n\n\nsetup(\n    name=\"dash\",\n    version=main_ns[\"__version__\"],\n    author=\"Chris Parmer\",\n    author_email=\"chris@plotly.com\",\n    packages=find_packages(exclude=[\"tests*\"]),\n    include_package_data=True,\n    license=\"MIT\",\n    description=(\n        \"A Python framework for building reactive web-apps. \"\n        \"Developed by Plotly.\"\n    ),\n    long_description=io.open(\"README.md\", encoding=\"utf-8\").read(),  # pylint: disable=consider-using-with\n    long_description_content_type=\"text/markdown\",\n    install_requires=read_req_file(\"install\"),\n    python_requires=\">=3.6\",\n    extras_require={\n        \"ci\": read_req_file(\"ci\"),\n        \"dev\": read_req_file(\"dev\"),\n        \"testing\": read_req_file(\"testing\"),\n        \"celery\": read_req_file(\"celery\"),\n        \"diskcache\": read_req_file(\"diskcache\"),\n    },\n    entry_points={\n        \"console_scripts\": [\n            \"dash-generate-components = \"\n            \"dash.development.component_generator:cli\",\n            \"renderer = dash.development.build_process:renderer\",\n            \"dash-update-components = dash.development.update_components:cli\"\n        ],\n        \"pytest11\": [\"dash = dash.testing.plugin\"],\n    },\n    url=\"https://plotly.com/dash\",\n    classifiers=[\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Web Environment\",\n        \"Framework :: Dash\",\n        \"Framework :: Flask\",\n        \"Intended Audience :: Developers\",\n        \"Intended Audience :: Education\",\n        \"Intended Audience :: Financial and Insurance Industry\",\n        \"Intended Audience :: Healthcare Industry\",\n        \"Intended Audience :: Manufacturing\",\n        \"Intended Audience :: Science/Research\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Topic :: Database :: Front-Ends\",\n        \"Topic :: Office/Business :: Financial :: Spreadsheet\",\n        \"Topic :: Scientific/Engineering :: Visualization\",\n        \"Topic :: Software Development :: Libraries :: Application Frameworks\",\n        \"Topic :: Software Development :: Widget Sets\",\n    ],\n)\n", "path": "setup.py"}]}