Repository: pandas-dev/pandas

Issue:
Make header_style a Property of ExcelFormatter
Right now the header style of excel exports is defined as global to the `pandas/io/formats/excel.py` module:

https://github.com/pandas-dev/pandas/blob/1a12c41d201f56439510e683fadfed1218ea9067/pandas/io/formats/excel.py#L37

Because this exists outside of the `ExcelFormatter` class where it is used it is way more difficult to override the default than it needs to be. If this was moved from global scope to instance scope it would allow users to theoretically subclass the `ExcelFormatter` and easily define their own style for the header

This could also eliminate the need for any work on #19386


Discussion:

Resolving patch:
diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py
--- a/pandas/io/formats/excel.py
+++ b/pandas/io/formats/excel.py
@@ -34,15 +34,6 @@ def __init__(self, row, col, val, style=None, mergestart=None,
         self.mergeend = mergeend
 
 
-header_style = {"font": {"bold": True},
-                "borders": {"top": "thin",
-                            "right": "thin",
-                            "bottom": "thin",
-                            "left": "thin"},
-                "alignment": {"horizontal": "center",
-                              "vertical": "top"}}
-
-
 class CSSToExcelConverter(object):
     """A callable for converting CSS declarations to ExcelWriter styles
 
@@ -389,6 +380,16 @@ def __init__(self, df, na_rep='', float_format=None, cols=None,
         self.merge_cells = merge_cells
         self.inf_rep = inf_rep
 
+    @property
+    def header_style(self):
+        return {"font": {"bold": True},
+                "borders": {"top": "thin",
+                            "right": "thin",
+                            "bottom": "thin",
+                            "left": "thin"},
+                "alignment": {"horizontal": "center",
+                              "vertical": "top"}}
+
     def _format_value(self, val):
         if is_scalar(val) and missing.isna(val):
             val = self.na_rep
@@ -427,7 +428,7 @@ def _format_header_mi(self):
             # Format multi-index as a merged cells.
             for lnum in range(len(level_lengths)):
                 name = columns.names[lnum]
-                yield ExcelCell(lnum, coloffset, name, header_style)
+                yield ExcelCell(lnum, coloffset, name, self.header_style)
 
             for lnum, (spans, levels, labels) in enumerate(zip(
                     level_lengths, columns.levels, columns.labels)):
@@ -435,16 +436,16 @@ def _format_header_mi(self):
                 for i in spans:
                     if spans[i] > 1:
                         yield ExcelCell(lnum, coloffset + i + 1, values[i],
-                                        header_style, lnum,
+                                        self.header_style, lnum,
                                         coloffset + i + spans[i])
                     else:
                         yield ExcelCell(lnum, coloffset + i + 1, values[i],
-                                        header_style)
+                                        self.header_style)
         else:
             # Format in legacy format with dots to indicate levels.
             for i, values in enumerate(zip(*level_strs)):
                 v = ".".join(map(pprint_thing, values))
-                yield ExcelCell(lnum, coloffset + i + 1, v, header_style)
+                yield ExcelCell(lnum, coloffset + i + 1, v, self.header_style)
 
         self.rowcounter = lnum
 
@@ -469,7 +470,7 @@ def _format_header_regular(self):
 
             for colindex, colname in enumerate(colnames):
                 yield ExcelCell(self.rowcounter, colindex + coloffset, colname,
-                                header_style)
+                                self.header_style)
 
     def _format_header(self):
         if isinstance(self.columns, ABCMultiIndex):
@@ -482,7 +483,8 @@ def _format_header(self):
             row = [x if x is not None else ''
                    for x in self.df.index.names] + [''] * len(self.columns)
             if reduce(lambda x, y: x and y, map(lambda x: x != '', row)):
-                gen2 = (ExcelCell(self.rowcounter, colindex, val, header_style)
+                gen2 = (ExcelCell(self.rowcounter, colindex, val,
+                                  self.header_style)
                         for colindex, val in enumerate(row))
                 self.rowcounter += 1
         return itertools.chain(gen, gen2)
@@ -518,7 +520,7 @@ def _format_regular_rows(self):
 
             if index_label and self.header is not False:
                 yield ExcelCell(self.rowcounter - 1, 0, index_label,
-                                header_style)
+                                self.header_style)
 
             # write index_values
             index_values = self.df.index
@@ -526,7 +528,8 @@ def _format_regular_rows(self):
                 index_values = self.df.index.to_timestamp()
 
             for idx, idxval in enumerate(index_values):
-                yield ExcelCell(self.rowcounter + idx, 0, idxval, header_style)
+                yield ExcelCell(self.rowcounter + idx, 0, idxval,
+                                self.header_style)
 
             coloffset = 1
         else:
@@ -562,7 +565,7 @@ def _format_hierarchical_rows(self):
 
                 for cidx, name in enumerate(index_labels):
                     yield ExcelCell(self.rowcounter - 1, cidx, name,
-                                    header_style)
+                                    self.header_style)
 
             if self.merge_cells:
                 # Format hierarchical rows as merged cells.
@@ -581,12 +584,12 @@ def _format_hierarchical_rows(self):
                     for i in spans:
                         if spans[i] > 1:
                             yield ExcelCell(self.rowcounter + i, gcolidx,
-                                            values[i], header_style,
+                                            values[i], self.header_style,
                                             self.rowcounter + i + spans[i] - 1,
                                             gcolidx)
                         else:
                             yield ExcelCell(self.rowcounter + i, gcolidx,
-                                            values[i], header_style)
+                                            values[i], self.header_style)
                     gcolidx += 1
 
             else:
@@ -594,7 +597,7 @@ def _format_hierarchical_rows(self):
                 for indexcolvals in zip(*self.df.index):
                     for idx, indexcolval in enumerate(indexcolvals):
                         yield ExcelCell(self.rowcounter + idx, gcolidx,
-                                        indexcolval, header_style)
+                                        indexcolval, self.header_style)
                     gcolidx += 1
 
         for cell in self._generate_body(gcolidx):


Test patch: