@@ -1273,6 +1273,149 @@ def _format_axis(self, s: str, config: _AxisFormatConfig, fixticks: bool):
12731273 # Ensure ticks are within axis bounds
12741274 self ._fix_ticks (s , fixticks = fixticks )
12751275
1276+ def _resolve_axis_format (self , axis , params , rc_kw ):
1277+ """
1278+ Resolve formatting parameters for a single axis (x or y).
1279+ """
1280+ p = params
1281+
1282+ # Color resolution
1283+ color = p .get ("color" )
1284+ axis_color = _not_none (p .get (f"{ axis } color" ), color )
1285+
1286+ # Helper to get axis-specific or generic param
1287+ def get (name ):
1288+ return p .get (f"{ axis } { name } " )
1289+
1290+ # Resolve colors
1291+ tickcolor = get ("tickcolor" )
1292+ if "tick.color" not in rc_kw :
1293+ tickcolor = _not_none (tickcolor , axis_color )
1294+
1295+ ticklabelcolor = get ("ticklabelcolor" )
1296+ if "tick.labelcolor" not in rc_kw :
1297+ ticklabelcolor = _not_none (ticklabelcolor , axis_color )
1298+
1299+ labelcolor = get ("labelcolor" )
1300+ if "label.color" not in rc_kw :
1301+ labelcolor = _not_none (labelcolor , axis_color )
1302+
1303+ # Flexible keyword args
1304+ margin = _not_none (
1305+ get ("margin" ), p .get ("margin" ), rc .find (f"axes.{ axis } margin" , context = True )
1306+ )
1307+
1308+ tickdir = _not_none (
1309+ get ("tickdir" ), rc .find (f"{ axis } tick.direction" , context = True )
1310+ )
1311+
1312+ locator = _not_none (get ("locator" ), p .get (f"{ axis } ticks" ))
1313+ minorlocator = _not_none (get ("minorlocator" ), p .get (f"{ axis } minorticks" ))
1314+
1315+ formatter = _not_none (get ("formatter" ), p .get (f"{ axis } ticklabels" ))
1316+
1317+ # Tick minor default logic
1318+ tickminor = get ("tickminor" )
1319+ tickminor_default = None
1320+ if (
1321+ isinstance (formatter , mticker .FixedFormatter )
1322+ or np .iterable (formatter )
1323+ and not isinstance (formatter , str )
1324+ ):
1325+ tickminor_default = False
1326+
1327+ tickminor = _not_none (
1328+ tickminor ,
1329+ tickminor_default ,
1330+ rc .find (f"{ axis } tick.minor.visible" , context = True ),
1331+ )
1332+
1333+ # Tick label dir logic
1334+ ticklabeldir = p .get ("ticklabeldir" )
1335+ axis_ticklabeldir = _not_none (get ("ticklabeldir" ), ticklabeldir )
1336+ tickdir = _not_none (tickdir , axis_ticklabeldir )
1337+
1338+ # Spine locations
1339+ loc = get ("loc" )
1340+ spineloc = get ("spineloc" )
1341+ spineloc = _not_none (loc , spineloc )
1342+
1343+ # Spine side inference
1344+ side = self ._get_spine_side (axis , spineloc )
1345+
1346+ tickloc = get ("tickloc" )
1347+ if side is not None and side not in ("zero" , "center" , "both" ):
1348+ tickloc = _not_none (tickloc , side )
1349+
1350+ # Infer other locations
1351+ ticklabelloc = get ("ticklabelloc" )
1352+ labelloc = get ("labelloc" )
1353+ offsetloc = get ("offsetloc" )
1354+
1355+ if tickloc != "both" :
1356+ ticklabelloc = _not_none (ticklabelloc , tickloc )
1357+ valid_sides = ("bottom" , "top" ) if axis == "x" else ("left" , "right" )
1358+
1359+ if ticklabelloc in valid_sides :
1360+ labelloc = _not_none (labelloc , ticklabelloc )
1361+ # Note: original code likely had typo relating xoffset to yticklabels
1362+ # We assume standard behavior here: follow ticklabelloc
1363+ offsetloc = _not_none (offsetloc , ticklabelloc )
1364+
1365+ tickloc = _not_none (tickloc , rc ._get_loc_string (axis , f"{ axis } tick" ))
1366+ spineloc = _not_none (spineloc , rc ._get_loc_string (axis , "axes.spines" ))
1367+
1368+ # Map to config fields
1369+ # Note: min_/max_ map to xmin/xmax etc
1370+ config_kwargs = {}
1371+ for field in _AxisFormatConfig .__dataclass_fields__ :
1372+ val = None
1373+ match field :
1374+ case "min_" :
1375+ val = p .get (f"{ axis } min" )
1376+ case "max_" :
1377+ val = p .get (f"{ axis } max" )
1378+ case "color" :
1379+ val = axis_color
1380+ case "tickcolor" :
1381+ val = tickcolor
1382+ case "ticklabelcolor" :
1383+ val = ticklabelcolor
1384+ case "labelcolor" :
1385+ val = labelcolor
1386+ case "margin" :
1387+ val = margin
1388+ case "tickdir" :
1389+ val = tickdir
1390+ case "locator" :
1391+ val = locator
1392+ case "minorlocator" :
1393+ val = minorlocator
1394+ case "formatter" :
1395+ val = formatter
1396+ case "tickminor" :
1397+ val = tickminor
1398+ case "ticklabeldir" :
1399+ val = axis_ticklabeldir
1400+ case "spineloc" :
1401+ val = spineloc
1402+ case "tickloc" :
1403+ val = tickloc
1404+ case "ticklabelloc" :
1405+ val = ticklabelloc
1406+ case "labelloc" :
1407+ val = labelloc
1408+ case "offsetloc" :
1409+ val = offsetloc
1410+ case _:
1411+ # Direct mapping (e.g. xlinewidth -> linewidth)
1412+ val = get (field )
1413+
1414+ if val is not None :
1415+ config_kwargs [field ] = val
1416+
1417+ return _AxisFormatConfig (** config_kwargs )
1418+
12761419 @docstring ._snippet_manager
12771420 def format (
12781421 self ,
@@ -1409,120 +1552,13 @@ def format(
14091552 """
14101553 rc_kw , rc_mode = _pop_rc (kwargs )
14111554 with rc .context (rc_kw , mode = rc_mode ):
1412- # No mutable default args
1413- xlabel_kw = xlabel_kw or {}
1414- ylabel_kw = ylabel_kw or {}
1415- xscale_kw = xscale_kw or {}
1416- yscale_kw = yscale_kw or {}
1417- xlocator_kw = xlocator_kw or {}
1418- ylocator_kw = ylocator_kw or {}
1419- xformatter_kw = xformatter_kw or {}
1420- yformatter_kw = yformatter_kw or {}
1421- xminorlocator_kw = xminorlocator_kw or {}
1422- yminorlocator_kw = yminorlocator_kw or {}
1423-
1424- # Color keyword arguments. Inherit from 'color' when necessary
1425- color = kwargs .pop ("color" , None )
1426- xcolor = _not_none (xcolor , color )
1427- ycolor = _not_none (ycolor , color )
1428- if "tick.color" not in rc_kw :
1429- xtickcolor = _not_none (xtickcolor , xcolor )
1430- ytickcolor = _not_none (ytickcolor , ycolor )
1431- if "tick.labelcolor" not in rc_kw :
1432- xticklabelcolor = _not_none (xticklabelcolor , xcolor )
1433- yticklabelcolor = _not_none (yticklabelcolor , ycolor )
1434- if "label.color" not in rc_kw :
1435- xlabelcolor = _not_none (xlabelcolor , xcolor )
1436- ylabelcolor = _not_none (ylabelcolor , ycolor )
1437-
1438- # Flexible keyword args, declare defaults
1439- # NOTE: 'xtickdir' and 'ytickdir' read from 'tickdir' arguments here
1440- xmargin = _not_none (xmargin , rc .find ("axes.xmargin" , context = True ))
1441- ymargin = _not_none (ymargin , rc .find ("axes.ymargin" , context = True ))
1442- xtickdir = _not_none (xtickdir , rc .find ("xtick.direction" , context = True ))
1443- ytickdir = _not_none (ytickdir , rc .find ("ytick.direction" , context = True ))
1444- xlocator = _not_none (xlocator = xlocator , xticks = xticks )
1445- ylocator = _not_none (ylocator = ylocator , yticks = yticks )
1446- xminorlocator = _not_none (
1447- xminorlocator = xminorlocator , xminorticks = xminorticks
1448- ) # noqa: E501
1449- yminorlocator = _not_none (
1450- yminorlocator = yminorlocator , yminorticks = yminorticks
1451- ) # noqa: E501
1452- xformatter = _not_none (xformatter = xformatter , xticklabels = xticklabels )
1453- yformatter = _not_none (yformatter = yformatter , yticklabels = yticklabels )
1454- xtickminor_default = ytickminor_default = None
1455- if (
1456- isinstance (xformatter , mticker .FixedFormatter )
1457- or np .iterable (xformatter )
1458- and not isinstance (xformatter , str )
1459- ): # noqa: E501
1460- xtickminor_default = False
1461- if (
1462- isinstance (yformatter , mticker .FixedFormatter )
1463- or np .iterable (yformatter )
1464- and not isinstance (yformatter , str )
1465- ): # noqa: E501
1466- ytickminor_default = False
1467- xtickminor = _not_none (
1468- xtickminor ,
1469- xtickminor_default ,
1470- rc .find ("xtick.minor.visible" , context = True ),
1471- ) # noqa: E501
1472- ytickminor = _not_none (
1473- ytickminor ,
1474- ytickminor_default ,
1475- rc .find ("ytick.minor.visible" , context = True ),
1476- ) # noqa: E501
1477- ticklabeldir = kwargs .pop ("ticklabeldir" , None )
1478- xticklabeldir = _not_none (xticklabeldir , ticklabeldir )
1479- yticklabeldir = _not_none (yticklabeldir , ticklabeldir )
1480- xtickdir = _not_none (xtickdir , xticklabeldir )
1481- ytickdir = _not_none (ytickdir , yticklabeldir )
1482-
1483- # Sensible defaults for spine, tick, tick label, and label locs
1484- # NOTE: Allow tick labels to be present without ticks! User may
1485- # want this sometimes! Same goes for spines!
1486- xspineloc = _not_none (xloc = xloc , xspineloc = xspineloc )
1487- yspineloc = _not_none (yloc = yloc , yspineloc = yspineloc )
1488- xside = self ._get_spine_side ("x" , xspineloc )
1489- yside = self ._get_spine_side ("y" , yspineloc )
1490- if xside is not None and xside not in ("zero" , "center" , "both" ):
1491- xtickloc = _not_none (xtickloc , xside )
1492- if yside is not None and yside not in ("zero" , "center" , "both" ):
1493- ytickloc = _not_none (ytickloc , yside )
1494- if xtickloc != "both" : # then infer others
1495- xticklabelloc = _not_none (xticklabelloc , xtickloc )
1496- if xticklabelloc in ("bottom" , "top" ):
1497- xlabelloc = _not_none (xlabelloc , xticklabelloc )
1498- xoffsetloc = _not_none (xoffsetloc , yticklabelloc )
1499- if ytickloc != "both" : # then infer others
1500- yticklabelloc = _not_none (yticklabelloc , ytickloc )
1501- if yticklabelloc in ("left" , "right" ):
1502- ylabelloc = _not_none (ylabelloc , yticklabelloc )
1503- yoffsetloc = _not_none (yoffsetloc , yticklabelloc )
1504- xtickloc = _not_none (xtickloc , rc ._get_loc_string ("x" , "xtick" ))
1505- ytickloc = _not_none (ytickloc , rc ._get_loc_string ("y" , "ytick" ))
1506- xspineloc = _not_none (xspineloc , rc ._get_loc_string ("x" , "axes.spines" ))
1507- yspineloc = _not_none (yspineloc , rc ._get_loc_string ("y" , "axes.spines" ))
1508-
1509- # Create config objects dynamically by introspecting the dataclass fields
1510- x_kwargs , y_kwargs = {}, {}
1511- l_vars = locals ()
1512- for name in _AxisFormatConfig .__dataclass_fields__ :
1513- # Handle exceptions to the "x" + name pattern for local variables
1514- if name == "min_" :
1515- x_var , y_var = "xmin" , "ymin"
1516- elif name == "max_" :
1517- x_var , y_var = "xmax" , "ymax"
1518- else :
1519- x_var = "x" + name
1520- y_var = "y" + name
1521- x_kwargs [name ] = l_vars .get (x_var , None )
1522- y_kwargs [name ] = l_vars .get (y_var , None )
1523-
1524- x_config = _AxisFormatConfig (** x_kwargs )
1525- y_config = _AxisFormatConfig (** y_kwargs )
1555+ # Resolve parameters for x and y axes
1556+ # We capture locals() to pass all named arguments to the helper
1557+ params = locals ()
1558+ params .update (kwargs ) # Include any extras in kwargs
1559+
1560+ x_config = self ._resolve_axis_format ("x" , params , rc_kw )
1561+ y_config = self ._resolve_axis_format ("y" , params , rc_kw )
15261562
15271563 # Format axes
15281564 self ._format_axis ("x" , x_config , fixticks = fixticks )
0 commit comments