prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Latest requirements.txt seems to be over-specifying dependencies
  This is the latest version requirements.txt file:
  
  ```
  black>=19.3b0
  darglint
  hypothesis>=4.4.0
  interrogate
  ipykernel
  isort>=4.3.18
  jupyter_client
  lxml
  natsort
  nbsphinx>=0.4.2
  pandas-flavor
  pandas-vet
  pre-commit
  pyspark
  pytest-azurepipelines
  pytest-cov
  pytest>=3.4.2
  scikit-learn
  seaborn
  setuptools>=38.5.2
  sphinxcontrib-fulltoc==1.2.0
  unyt
  xarray
  ```
  
  Those are inject in the setup.py as mandatory dependencies but a lot in there looks like docs, dev, or optional. Is pyjanitor really require all that at run time?
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `setup.py`
  Content:
  ```
  1 """Setup script."""
  2 
  3 import re
  4 from pathlib import Path
  5 
  6 from setuptools import find_packages, setup
  7 
  8 
  9 def requirements():
  10     """Reader for requirements.txt"""
  11     with open("requirements.txt", "r+") as f:
  12         return f.read()
  13 
  14 
  15 def generate_long_description() -> str:
  16     """
  17     Extra chunks from README for PyPI description.
  18 
  19     Target chunks must be contained within `.. pypi-doc` pair comments,
  20     so there must be an even number of comments in README.
  21 
  22     :returns: Extracted description from README.
  23     :raises Exception: if odd number of ``.. pypi-doc`` comments
  24         in README.
  25     """
  26     # Read the contents of README file
  27     this_directory = Path(__file__).parent
  28     with open(this_directory / "README.rst", encoding="utf-8") as f:
  29         readme = f.read()
  30 
  31     # Find pypi-doc comments in README
  32     indices = [m.start() for m in re.finditer(".. pypi-doc", readme)]
  33     if len(indices) % 2 != 0:
  34         raise Exception("Odd number of `.. pypi-doc` comments in README")
  35 
  36     # Loop through pairs of comments and save text between pairs
  37     long_description = ""
  38     for i in range(0, len(indices), 2):
  39         start_index = indices[i] + 11
  40         end_index = indices[i + 1]
  41         long_description += readme[start_index:end_index]
  42     return long_description
  43 
  44 
  45 extra_spark = ["pyspark"]
  46 extra_biology = ["biopython"]
  47 extra_chemistry = ["rdkit"]
  48 extra_engineering = ["unyt"]
  49 extra_all = extra_biology + extra_engineering + extra_spark
  50 
  51 setup(
  52     name="pyjanitor",
  53     version="0.20.10",
  54     description="Tools for cleaning pandas DataFrames",
  55     author="Eric J. Ma",
  56     author_email="ericmajinglong@gmail.com",
  57     url="https://github.com/ericmjl/pyjanitor",
  58     license="MIT",
  59     # packages=["janitor", "janitor.xarray", "janitor.spark"],
  60     packages=find_packages(),
  61     install_requires=requirements(),
  62     extras_require={
  63         "all": extra_all,
  64         "biology": extra_biology,
  65         # "chemistry": extra_chemistry, should be inserted once rdkit
  66         # fixes https://github.com/rdkit/rdkit/issues/1812
  67         "engineering": extra_engineering,
  68         "spark": extra_spark,
  69     },
  70     python_requires=">=3.6",
  71     long_description=generate_long_description(),
  72     long_description_content_type="text/x-rst",
  73 )
  74
  ```
  --- 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/setup.py b/setup.py
  --- a/setup.py
  +++ b/setup.py
  @@ -1,15 +1,82 @@
   """Setup script."""
  -
  +import codecs
  +import os
   import re
   from pathlib import Path
  +from pprint import pprint
   
   from setuptools import find_packages, setup
   
  +HERE = os.path.abspath(os.path.dirname(__file__))
  +
  +
  +def read(*parts):
  +    # intentionally *not* adding an encoding option to open
  +    return codecs.open(os.path.join(HERE, *parts), "r").read()
  +
   
  -def requirements():
  -    """Reader for requirements.txt"""
  -    with open("requirements.txt", "r+") as f:
  -        return f.read()
  +def read_requirements(*parts):
  +    """
  +    Return requirements from parts.
  +
  +    Given a requirements.txt (or similar style file),
  +    returns a list of requirements.
  +    Assumes anything after a single '#' on a line is a comment, and ignores
  +    empty lines.
  +
  +    :param parts: list of filenames which contain the installation "parts",
  +        i.e. submodule-specific installation requirements
  +    :returns: A compiled list of requirements.
  +    """
  +    requirements = []
  +    for line in read(*parts).splitlines():
  +        new_line = re.sub(  # noqa: PD005
  +            r"(\s*)?#.*$",  # the space immediately before the
  +            # hash mark, the hash mark, and
  +            # anything that follows it
  +            "",  # replace with a blank string
  +            line,
  +        )
  +        new_line = re.sub(  # noqa: PD005
  +            r"-r.*$",  # link to another requirement file
  +            "",  # replace with a blank string
  +            new_line,
  +        )
  +        new_line = re.sub(  # noqa: PD005
  +            r"-e \..*$",  # link to editable install
  +            "",  # replace with a blank string
  +            new_line,
  +        )
  +        # print(line, "-->", new_line)
  +        if new_line:  # i.e. we have a non-zero-length string
  +            requirements.append(new_line)
  +    return requirements
  +
  +
  +# pull from requirements.IN, requirements.TXT is generated from this
  +INSTALL_REQUIRES = read_requirements(".requirements/base.in")
  +
  +EXTRA_REQUIRES = {
  +    "dev": read_requirements(".requirements/dev.in"),
  +    "docs": read_requirements(".requirements/docs.in"),
  +    "test": read_requirements(".requirements/testing.in"),
  +    "biology": read_requirements(".requirements/biology.in"),
  +    "chemistry": read_requirements(".requirements/chemistry.in"),
  +    "engineering": read_requirements(".requirements/engineering.in"),
  +    "spark": read_requirements(".requirements/spark.in"),
  +}
  +
  +# add 'all' key to EXTRA_REQUIRES
  +all_requires = []
  +for k, v in EXTRA_REQUIRES.items():
  +    all_requires.extend(v)
  +EXTRA_REQUIRES["all"] = set(all_requires)
  +
  +for k1 in ["biology", "chemistry", "engineering", "spark"]:
  +    for v2 in EXTRA_REQUIRES[k1]:
  +        EXTRA_REQUIRES["docs"].append(v2)
  +
  +pprint(EXTRA_REQUIRES)
   
   
   def generate_long_description() -> str:
  @@ -42,12 +109,6 @@
       return long_description
   
   
  -extra_spark = ["pyspark"]
  -extra_biology = ["biopython"]
  -extra_chemistry = ["rdkit"]
  -extra_engineering = ["unyt"]
  -extra_all = extra_biology + extra_engineering + extra_spark
  -
   setup(
       name="pyjanitor",
       version="0.20.10",
  @@ -58,15 +119,8 @@
       license="MIT",
       # packages=["janitor", "janitor.xarray", "janitor.spark"],
       packages=find_packages(),
  -    install_requires=requirements(),
  -    extras_require={
  -        "all": extra_all,
  -        "biology": extra_biology,
  -        # "chemistry": extra_chemistry, should be inserted once rdkit
  -        # fixes https://github.com/rdkit/rdkit/issues/1812
  -        "engineering": extra_engineering,
  -        "spark": extra_spark,
  -    },
  +    install_requires=INSTALL_REQUIRES,
  +    extras_require=EXTRA_REQUIRES,
       python_requires=">=3.6",
       long_description=generate_long_description(),
       long_description_content_type="text/x-rst",
  
verification_info: {"golden_diff": "diff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -1,15 +1,82 @@\n \"\"\"Setup script.\"\"\"\n-\n+import codecs\n+import os\n import re\n from pathlib import Path\n+from pprint import pprint\n \n from setuptools import find_packages, setup\n \n+HERE = os.path.abspath(os.path.dirname(__file__))\n+\n+\n+def read(*parts):\n+    # intentionally *not* adding an encoding option to open\n+    return codecs.open(os.path.join(HERE, *parts), \"r\").read()\n+\n \n-def requirements():\n-    \"\"\"Reader for requirements.txt\"\"\"\n-    with open(\"requirements.txt\", \"r+\") as f:\n-        return f.read()\n+def read_requirements(*parts):\n+    \"\"\"\n+    Return requirements from parts.\n+\n+    Given a requirements.txt (or similar style file),\n+    returns a list of requirements.\n+    Assumes anything after a single '#' on a line is a comment, and ignores\n+    empty lines.\n+\n+    :param parts: list of filenames which contain the installation \"parts\",\n+        i.e. submodule-specific installation requirements\n+    :returns: A compiled list of requirements.\n+    \"\"\"\n+    requirements = []\n+    for line in read(*parts).splitlines():\n+        new_line = re.sub(  # noqa: PD005\n+            r\"(\\s*)?#.*$\",  # the space immediately before the\n+            # hash mark, the hash mark, and\n+            # anything that follows it\n+            \"\",  # replace with a blank string\n+            line,\n+        )\n+        new_line = re.sub(  # noqa: PD005\n+            r\"-r.*$\",  # link to another requirement file\n+            \"\",  # replace with a blank string\n+            new_line,\n+        )\n+        new_line = re.sub(  # noqa: PD005\n+            r\"-e \\..*$\",  # link to editable install\n+            \"\",  # replace with a blank string\n+            new_line,\n+        )\n+        # print(line, \"-->\", new_line)\n+        if new_line:  # i.e. we have a non-zero-length string\n+            requirements.append(new_line)\n+    return requirements\n+\n+\n+# pull from requirements.IN, requirements.TXT is generated from this\n+INSTALL_REQUIRES = read_requirements(\".requirements/base.in\")\n+\n+EXTRA_REQUIRES = {\n+    \"dev\": read_requirements(\".requirements/dev.in\"),\n+    \"docs\": read_requirements(\".requirements/docs.in\"),\n+    \"test\": read_requirements(\".requirements/testing.in\"),\n+    \"biology\": read_requirements(\".requirements/biology.in\"),\n+    \"chemistry\": read_requirements(\".requirements/chemistry.in\"),\n+    \"engineering\": read_requirements(\".requirements/engineering.in\"),\n+    \"spark\": read_requirements(\".requirements/spark.in\"),\n+}\n+\n+# add 'all' key to EXTRA_REQUIRES\n+all_requires = []\n+for k, v in EXTRA_REQUIRES.items():\n+    all_requires.extend(v)\n+EXTRA_REQUIRES[\"all\"] = set(all_requires)\n+\n+for k1 in [\"biology\", \"chemistry\", \"engineering\", \"spark\"]:\n+    for v2 in EXTRA_REQUIRES[k1]:\n+        EXTRA_REQUIRES[\"docs\"].append(v2)\n+\n+pprint(EXTRA_REQUIRES)\n \n \n def generate_long_description() -> str:\n@@ -42,12 +109,6 @@\n     return long_description\n \n \n-extra_spark = [\"pyspark\"]\n-extra_biology = [\"biopython\"]\n-extra_chemistry = [\"rdkit\"]\n-extra_engineering = [\"unyt\"]\n-extra_all = extra_biology + extra_engineering + extra_spark\n-\n setup(\n     name=\"pyjanitor\",\n     version=\"0.20.10\",\n@@ -58,15 +119,8 @@\n     license=\"MIT\",\n     # packages=[\"janitor\", \"janitor.xarray\", \"janitor.spark\"],\n     packages=find_packages(),\n-    install_requires=requirements(),\n-    extras_require={\n-        \"all\": extra_all,\n-        \"biology\": extra_biology,\n-        # \"chemistry\": extra_chemistry, should be inserted once rdkit\n-        # fixes https://github.com/rdkit/rdkit/issues/1812\n-        \"engineering\": extra_engineering,\n-        \"spark\": extra_spark,\n-    },\n+    install_requires=INSTALL_REQUIRES,\n+    extras_require=EXTRA_REQUIRES,\n     python_requires=\">=3.6\",\n     long_description=generate_long_description(),\n     long_description_content_type=\"text/x-rst\",\n", "issue": "Latest requirements.txt seems to be over-specifying dependencies\nThis is the latest version requirements.txt file:\r\n\r\n```\r\nblack>=19.3b0\r\ndarglint\r\nhypothesis>=4.4.0\r\ninterrogate\r\nipykernel\r\nisort>=4.3.18\r\njupyter_client\r\nlxml\r\nnatsort\r\nnbsphinx>=0.4.2\r\npandas-flavor\r\npandas-vet\r\npre-commit\r\npyspark\r\npytest-azurepipelines\r\npytest-cov\r\npytest>=3.4.2\r\nscikit-learn\r\nseaborn\r\nsetuptools>=38.5.2\r\nsphinxcontrib-fulltoc==1.2.0\r\nunyt\r\nxarray\r\n```\r\n\r\nThose are inject in the setup.py as mandatory dependencies but a lot in there looks like docs, dev, or optional. Is pyjanitor really require all that at run time?\n", "before_files": [{"content": "\"\"\"Setup script.\"\"\"\n\nimport re\nfrom pathlib import Path\n\nfrom setuptools import find_packages, setup\n\n\ndef requirements():\n    \"\"\"Reader for requirements.txt\"\"\"\n    with open(\"requirements.txt\", \"r+\") as f:\n        return f.read()\n\n\ndef generate_long_description() -> str:\n    \"\"\"\n    Extra chunks from README for PyPI description.\n\n    Target chunks must be contained within `.. pypi-doc` pair comments,\n    so there must be an even number of comments in README.\n\n    :returns: Extracted description from README.\n    :raises Exception: if odd number of ``.. pypi-doc`` comments\n        in README.\n    \"\"\"\n    # Read the contents of README file\n    this_directory = Path(__file__).parent\n    with open(this_directory / \"README.rst\", encoding=\"utf-8\") as f:\n        readme = f.read()\n\n    # Find pypi-doc comments in README\n    indices = [m.start() for m in re.finditer(\".. pypi-doc\", readme)]\n    if len(indices) % 2 != 0:\n        raise Exception(\"Odd number of `.. pypi-doc` comments in README\")\n\n    # Loop through pairs of comments and save text between pairs\n    long_description = \"\"\n    for i in range(0, len(indices), 2):\n        start_index = indices[i] + 11\n        end_index = indices[i + 1]\n        long_description += readme[start_index:end_index]\n    return long_description\n\n\nextra_spark = [\"pyspark\"]\nextra_biology = [\"biopython\"]\nextra_chemistry = [\"rdkit\"]\nextra_engineering = [\"unyt\"]\nextra_all = extra_biology + extra_engineering + extra_spark\n\nsetup(\n    name=\"pyjanitor\",\n    version=\"0.20.10\",\n    description=\"Tools for cleaning pandas DataFrames\",\n    author=\"Eric J. Ma\",\n    author_email=\"ericmajinglong@gmail.com\",\n    url=\"https://github.com/ericmjl/pyjanitor\",\n    license=\"MIT\",\n    # packages=[\"janitor\", \"janitor.xarray\", \"janitor.spark\"],\n    packages=find_packages(),\n    install_requires=requirements(),\n    extras_require={\n        \"all\": extra_all,\n        \"biology\": extra_biology,\n        # \"chemistry\": extra_chemistry, should be inserted once rdkit\n        # fixes https://github.com/rdkit/rdkit/issues/1812\n        \"engineering\": extra_engineering,\n        \"spark\": extra_spark,\n    },\n    python_requires=\">=3.6\",\n    long_description=generate_long_description(),\n    long_description_content_type=\"text/x-rst\",\n)\n", "path": "setup.py"}], "after_files": [{"content": "\"\"\"Setup script.\"\"\"\nimport codecs\nimport os\nimport re\nfrom pathlib import Path\nfrom pprint import pprint\n\nfrom setuptools import find_packages, setup\n\nHERE = os.path.abspath(os.path.dirname(__file__))\n\n\ndef read(*parts):\n    # intentionally *not* adding an encoding option to open\n    return codecs.open(os.path.join(HERE, *parts), \"r\").read()\n\n\ndef read_requirements(*parts):\n    \"\"\"\n    Return requirements from parts.\n\n    Given a requirements.txt (or similar style file),\n    returns a list of requirements.\n    Assumes anything after a single '#' on a line is a comment, and ignores\n    empty lines.\n\n    :param parts: list of filenames which contain the installation \"parts\",\n        i.e. submodule-specific installation requirements\n    :returns: A compiled list of requirements.\n    \"\"\"\n    requirements = []\n    for line in read(*parts).splitlines():\n        new_line = re.sub(  # noqa: PD005\n            r\"(\\s*)?#.*$\",  # the space immediately before the\n            # hash mark, the hash mark, and\n            # anything that follows it\n            \"\",  # replace with a blank string\n            line,\n        )\n        new_line = re.sub(  # noqa: PD005\n            r\"-r.*$\",  # link to another requirement file\n            \"\",  # replace with a blank string\n            new_line,\n        )\n        new_line = re.sub(  # noqa: PD005\n            r\"-e \\..*$\",  # link to editable install\n            \"\",  # replace with a blank string\n            new_line,\n        )\n        # print(line, \"-->\", new_line)\n        if new_line:  # i.e. we have a non-zero-length string\n            requirements.append(new_line)\n    return requirements\n\n\n# pull from requirements.IN, requirements.TXT is generated from this\nINSTALL_REQUIRES = read_requirements(\".requirements/base.in\")\n\nEXTRA_REQUIRES = {\n    \"dev\": read_requirements(\".requirements/dev.in\"),\n    \"docs\": read_requirements(\".requirements/docs.in\"),\n    \"test\": read_requirements(\".requirements/testing.in\"),\n    \"biology\": read_requirements(\".requirements/biology.in\"),\n    \"chemistry\": read_requirements(\".requirements/chemistry.in\"),\n    \"engineering\": read_requirements(\".requirements/engineering.in\"),\n    \"spark\": read_requirements(\".requirements/spark.in\"),\n}\n\n# add 'all' key to EXTRA_REQUIRES\nall_requires = []\nfor k, v in EXTRA_REQUIRES.items():\n    all_requires.extend(v)\nEXTRA_REQUIRES[\"all\"] = set(all_requires)\n\nfor k1 in [\"biology\", \"chemistry\", \"engineering\", \"spark\"]:\n    for v2 in EXTRA_REQUIRES[k1]:\n        EXTRA_REQUIRES[\"docs\"].append(v2)\n\npprint(EXTRA_REQUIRES)\n\n\ndef generate_long_description() -> str:\n    \"\"\"\n    Extra chunks from README for PyPI description.\n\n    Target chunks must be contained within `.. pypi-doc` pair comments,\n    so there must be an even number of comments in README.\n\n    :returns: Extracted description from README.\n    :raises Exception: if odd number of ``.. pypi-doc`` comments\n        in README.\n    \"\"\"\n    # Read the contents of README file\n    this_directory = Path(__file__).parent\n    with open(this_directory / \"README.rst\", encoding=\"utf-8\") as f:\n        readme = f.read()\n\n    # Find pypi-doc comments in README\n    indices = [m.start() for m in re.finditer(\".. pypi-doc\", readme)]\n    if len(indices) % 2 != 0:\n        raise Exception(\"Odd number of `.. pypi-doc` comments in README\")\n\n    # Loop through pairs of comments and save text between pairs\n    long_description = \"\"\n    for i in range(0, len(indices), 2):\n        start_index = indices[i] + 11\n        end_index = indices[i + 1]\n        long_description += readme[start_index:end_index]\n    return long_description\n\n\nsetup(\n    name=\"pyjanitor\",\n    version=\"0.20.10\",\n    description=\"Tools for cleaning pandas DataFrames\",\n    author=\"Eric J. Ma\",\n    author_email=\"ericmajinglong@gmail.com\",\n    url=\"https://github.com/ericmjl/pyjanitor\",\n    license=\"MIT\",\n    # packages=[\"janitor\", \"janitor.xarray\", \"janitor.spark\"],\n    packages=find_packages(),\n    install_requires=INSTALL_REQUIRES,\n    extras_require=EXTRA_REQUIRES,\n    python_requires=\">=3.6\",\n    long_description=generate_long_description(),\n    long_description_content_type=\"text/x-rst\",\n)\n", "path": "setup.py"}]}