Repository: sunpy/sunpy

Issue:
Turn `sunpy.map.header_helper.meta_keywords()` into a dictionary
Currently, the function `sunpy.map.header_helper.meta_keywords()` does one thing: it returns a (non-copied) dictionary.  If that's all it's going to do, with no apparent need to protect the returned dictionary, it should simply be a dictionary rather than a function.  Why force people to type the parentheses and cause confusion if they accidentally omit them?


Discussion:
~~I guess this way stops users accidentally modifying the dictionary in-place?~~ ha I tried this and it definitely doesn't 😄 . But maybe we could change that by making a copy of the dict in the `meta_keywords()` function?
Yeah, that was my point, but I worded it poorly.  Since the dictionary wasn't being copied, either it doesn't need to be protected and can be exposed directly as a variable, or the lack of a copy is a bug.

Even if the dictionary does need to be protected, I think this should be made into a (now-possible) module-level attribute (that returns a copy) rather than a function that takes no arguments.
[PEP 562](https://peps.python.org/pep-0562/) describes module-level attrs which it sounds like may be useful here. I was not aware of these.
> [PEP 562](https://peps.python.org/pep-0562/) describes module-level attrs which it sounds like may be useful here. I was not aware of these.

You say that, but you reviewed my PR for downloading sample data on demand (https://github.com/sunpy/sunpy/pull/6426#pullrequestreview-1132149882), for which module-level attributes are fundamental.  I'm now particularly amused by your request there (https://github.com/sunpy/sunpy/pull/6426#discussion_r988438026) to add a comment pointing to PEP 562 in order to help "future readers", seemingly foreshadowing your own amnesia. =P
_Originally posted by @ayshih in https://github.com/sunpy/sunpy/issues/6722#issuecomment-1404575334_
> Um...  What is the purpose for this dictionary?  Our codebase doesn't appear to use it; does any user?  Can we just remove it?  If it has a purpose, why does it live in `sunpy.map.header_helper`?  All questions I should pose in the original issue, I suppose.
The only reference I can find is in the map.rst:

Any of the keywords in the dictionary returned by :func:`~sunpy.map.header_helper.meta_keywords` can be passed to the :func:`~sunpy.map.header_helper.make_fitswcs_header` and will then populate the returned MetaDict header.

> > [PEP 562](https://peps.python.org/pep-0562/) describes module-level attrs which it sounds like may be useful here. I was not aware of these.
> 
> You say that, but you reviewed my PR for downloading sample data on demand ([#6426 (review)](https://github.com/sunpy/sunpy/pull/6426#pullrequestreview-1132149882)), for which module-level attributes are fundamental. I'm now particularly amused by your request there ([#6426 (comment)](https://github.com/sunpy/sunpy/pull/6426#discussion_r988438026)) to add a comment pointing to PEP 562 in order to help "future readers", seemingly foreshadowing your own amnesia. =P

"future readers" apparently code for "Will who can't seem to remember anything".
 > Um...  What is the purpose for this dictionary?  Our codebase doesn't appear to use it; does any user?  Can we just remove it? 

Well either way it sounds like the *function* should be deprecated. From a user perspective, I can see it being useful to know which metadata keywrods Map deems to be the important ones. If for some reason a user wanted to create a map by hand, they could use this to make sure they are populating all of the relevant keys.

Also, I think to answer to "why is this a function" lies in the fact that it did not used to return a `dict` at all and just printed things to the screen https://github.com/sunpy/sunpy/blame/175fbba229d6192dc2f6285b23295279502cc031/sunpy/map/maphelper.py#L18-L49. You could of course then ask "why did this function exist at all?!" but I do not have an answer to that.
> The only reference I can find is in the map.rst:
> 
> Any of the keywords in the dictionary returned by :func:`~sunpy.map.header_helper.meta_keywords` can be passed to the :func:`~sunpy.map.header_helper.make_fitswcs_header` and will then populate the returned MetaDict header.

I see that sentence in our docs, but it's not even true, right?  `make_fitswcs_header()` (currently) has no capacity for accepting arbitrary keyword arguments.

Resolving patch:
diff --git a/sunpy/map/header_helper.py b/sunpy/map/header_helper.py
--- a/sunpy/map/header_helper.py
+++ b/sunpy/map/header_helper.py
@@ -7,10 +7,12 @@
 from sunpy import log
 from sunpy.coordinates import frames, sun
 from sunpy.util import MetaDict
+from sunpy.util.decorators import deprecated
 
 __all__ = ['meta_keywords', 'make_fitswcs_header', 'get_observer_meta', 'make_heliographic_header']
 
 
+@deprecated(since="5.0", message="Unused and will be removed in 6.0")
 def meta_keywords():
     """
     Returns the metadata keywords that are used when creating a `sunpy.map.GenericMap`.
@@ -19,12 +21,12 @@ def meta_keywords():
     --------
     Returns a dictionary of all meta keywords that are used in a `sunpy.map.GenericMap` header:
         >>> import sunpy.map
-        >>> sunpy.map.meta_keywords()
+        >>> sunpy.map.meta_keywords() # doctest: +SKIP
         {'cunit1': 'Units of the coordinate increments along naxis1 e.g. arcsec **required',
          'cunit2': 'Units of the coordinate increments along naxis2 e.g. arcsec **required',
          ...
     """
-    return _map_meta_keywords
+    return _map_meta_keywords.copy()
 
 
 @u.quantity_input(equivalencies=u.spectral())


Test patch:
diff --git a/sunpy/map/tests/test_header_helper.py b/sunpy/map/tests/test_header_helper.py
--- a/sunpy/map/tests/test_header_helper.py
+++ b/sunpy/map/tests/test_header_helper.py
@@ -9,6 +9,7 @@
 from sunpy.coordinates import frames, sun
 from sunpy.map import make_fitswcs_header
 from sunpy.map.header_helper import make_heliographic_header
+from sunpy.util.exceptions import SunpyDeprecationWarning
 from sunpy.util.metadata import MetaDict
 
 
@@ -62,8 +63,9 @@ def hpc_coord_notime():
 
 
 def test_metakeywords():
-    meta = sunpy.map.meta_keywords()
-    assert isinstance(meta, dict)
+    with pytest.warns(SunpyDeprecationWarning):
+        meta = sunpy.map.meta_keywords()
+        assert isinstance(meta, dict)
 
 
 def test_scale_conversion(map_data, hpc_coord):