Repository: pandas-dev/pandas
Pull request: BUG: shows correct package name when import_optional_dependency is ca…
…lled. fixes #35948

- [x] closes #35948
- [x] tests added / passed
- [x] passes `black pandas`
- [ ] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry


Problem statement:
DOC: "engine=odf" option should ask you to install pyodf instead of odf in pandas.read_excel
#### Location of the documentation

"pandas.read_excel"

#### Documentation problem

When `engine="odf"` is used and `pyodf` is not installed it gives an error that you should install optional dependency odf as `pip or conda install odf`. This pip/conda command fails because there's no module called `odf`. This could also be a security vulnerability if someone puts a malicious "odf" module on pypi.

#### Suggested fix for documentation

The error should ask you to install odf using `pip or conda install pyodf`

Hints from issue discussion:
Do you mean `odfpy` rather than `pyodf`?

We'll need to update https://github.com/pandas-dev/pandas/blame/master/pandas/compat/_optional.py#L48-L50 to take an optional package name argument. Or we can have a dict of import names to package names and do something like

```python
install_name = install_mapping.get(name, name)
```

and include the install name in the error message.
Hello, I am new to the community, and I would like to work on this issue. May I take this issue?
Please review my changes made in the commit.
@hs2361 can you open a pull request and link to this issue

Commit message: formatted according to black

Patch to review:
diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py
index 689c7c889ef66..88fcdca1f881f 100644
--- a/pandas/compat/_optional.py
+++ b/pandas/compat/_optional.py
@@ -33,6 +33,32 @@
     "numba": "0.46.0",
 }
 
+INSTALL_MAPPING = {
+    "bs4": "beautifulsoup4",
+    "bottleneck": "Bottleneck",
+    "fsspec": "fsspec",
+    "botocore": "botocore",
+    "fastparquet": "fastparquet",
+    "gcsfs": "gcsfs",
+    "lxml.etree": "lxml",
+    "matplotlib": "matplotlib",
+    "numexpr": "numexpr",
+    "odf": "odfpy",
+    "openpyxl": "openpyxl",
+    "pandas_gbq": "pandas-gbq",
+    "pyarrow": "pyarrow",
+    "pytest": "pytest",
+    "pyxlsb": "pyxlsb",
+    "scipy": "scipy",
+    "sqlalchemy": "SQLAlchemy",
+    "tables": "tables",
+    "tabulate": "tabulate",
+    "xarray": "xarray",
+    "xlrd": "xlrd",
+    "numba": "numba",
+    "jinja2": "Jinja2",
+}
+
 
 def _get_version(module: types.ModuleType) -> str:
     version = getattr(module, "__version__", None)
@@ -82,9 +108,13 @@ def import_optional_dependency(
         is False, or when the package's version is too old and `on_version`
         is ``'warn'``.
     """
+
+    package_name = INSTALL_MAPPING.get(name)
+    install_name = package_name if package_name is not None else name
+
     msg = (
-        f"Missing optional dependency '{name}'. {extra} "
-        f"Use pip or conda to install {name}."
+        f"Missing optional dependency '{install_name}'. {extra} "
+        f"Use pip or conda to install {install_name}."
     )
     try:
         module = importlib.import_module(name)


Code review comments:
diff_hunk: @@ -33,6 +33,32 @@
     "numba": "0.46.0",
 }
 
+INSTALL_MAPPING = {
path: pandas/compat/_optional.py
text: @user1:
only add the names of packages that are *different* (as we already have all of the names); add a comment to describe this.

@author:
I have made the changes as suggested.