You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
The reason will be displayed to describe this comment to others.Learn more.
That makes sense semantically — None is the canonical way to disable hatching. However, in practice, there are scenarios where hatch can be constructed programmatically (e.g., through loops, Giles, or data pipelines). In such cases, it may naturally or unintentionally evaluate to an empty list — for example, via something like config.get("hatch", []). Raising an error in these situations could make otherwise valid automation brittle. Would it be acceptable to issue a warning and treat an empty list as None instead, to preserve robustness? This would align with Matplotlib’s general philosophy of being tolerant and user-friendly toward benign input while still informing users of the preferred approach.
if not hatch_list: _api.warn_external( "'hatch' was an empty sequence; treating as no hatch. " "Use None explicitly to suppress this warning." ) hatches = itertools.cycle([None])
The reason will be displayed to describe this comment to others.Learn more.
I would rather argument, that it's likely an error in the programmatic code if it returns an empty list. I don't see a practical case in which one would naturally end up with an empty list if one has a finite set of groups and want to generate hatches.config.get("hatch", []) seems contrieved. Why would one want to write this instead ofconfig.get("hatch") which will work correctly.
The reason will be displayed to describe this comment to others.Learn more.
However, in practice, empty sequences can occur naturally in data-driven workflows. For instance, configuration files often deserialize [] for optional arrays (e.g., JSON/YAML style templates), and plotting utilities may use defensive defaults like config.get("hatch", []) to avoid KeyError. Similarly, dynamically generating hatches from metadata — e.g. [meta.get("hatch") for meta in datasets] — can yield an empty list when all datasets omit that field. In such cases, treating [] as “no hatch” (with a warning) keeps things robust and user-friendly for automated or programmatic pipelines, while still guiding users to prefer None explicitly.
The reason will be displayed to describe this comment to others.Learn more.
Even if these use cases may be rare or not widely observed at the moment, such scenarios could easily emerge in evolving data-driven or cloud-integrated visualization systems in the immediate future — often unexpectedly.
The reason will be displayed to describe this comment to others.Learn more.
Dynamic data aggregation / filtering workflows:
Pipelines may dynamically build visual encodings (colors, hatches, etc.) from dataset metadata:
hatches = [meta.get("hatch") for meta in datasets if meta.get("include", True)]
Such code is outright dangerous. Depending on the distribution of "include" in your datasets the list can have an arbitrary length between 0 andlen(datasets), and all the in-between values result in undesired behavior. When infering from datasets you definitively want to have exactly one property per dataset:
hatches = [meta.get("hatch") if meta.get("include", True) else None for meta in datasets]
All other cases are just hand-waving "but someone might end up with an empty list that they want to pass", which I don't buy. (btw. are these cases AI-genereated?) Until someone comes up with a concrete example, I have the firm believe that empty lists are rather an error in the generating code and not intended input.
Note that we also raise forplt.stackplot([1, 2, 3], [[1, 2 ,3]]*2, hatch=[]) - though admittedly in a less helpful way.
The reason will be displayed to describe this comment to others.Learn more.
Thanks,@timhoffm — I appreciate your detailed perspective. I’d like to clarify why I still believe that gracefully handling an empty sequence (with a warning) can be beneficial in the long run, both pragmatically and philosophically.
1. Defensive and user-friendly design
Matplotlib’s strength has always been its tolerance toward benign irregularities. Users rely on it as a stable visualization backend even when inputs are indirectly produced — from templates, configs, or pipelines — not always hand-written by developers.
Treating [] as equivalent to None (with a warning) preserves this spirit of robustness and continuity. It avoids brittle failures in higher-level tools that integrate Matplotlib as a rendering engine, especially when such inputs originate from sources beyond direct user control.
2. Real-world automation and serialization
Even though empty lists may seem unlikely in manual usage, they appear quite often in automated systems: YAML/JSON serialization — missing or optional fields frequently deserialize to empty arrays rather than None. Dynamic workflows — templated plotting pipelines or report generators (e.g., Airflow, Prefect, or CI visualizations) often construct keyword dictionaries dynamically. When an aesthetic parameter is unused, it may remain as an empty list placeholder.
Interoperability layers — frameworks like seaborn, pandas, or holoviews may forward arguments programmatically, sometimes defaulting to empty sequences when certain aesthetics are unused. These cases are not errors per se — they are side effects of flexible, data-driven design where “no hatch” may naturally translate to an empty sequence.
3. Design philosophy — protecting the fragile parts
From a design standpoint, the absence of current breakage does not mean the absence of potential breakage. If a construct can occur naturally in evolving ecosystems, it’s prudent to make the library resilient to it — especially when doing so is low-risk and consistent with existing semantics (None already means “no hatch”). Designing defensively means protecting things that could break, even without hard proof that they already do. In software ecosystems, “possible” often becomes “inevitable” over time as integration layers grow.
4. Minimal cost, maximal stability
Issuing a warning — rather than raising — strikes a balanced compromise: It informs users of the preferred canonical input (None). It preserves existing behavior for programmatic systems that may unintentionally produce []. It avoids introducing regressions in automated pipelines where errors may not be easy to trace back to Matplotlib.
So, while I agree that hatch=[] might not be common in hand-written code, its graceful handling can future-proof Matplotlib against subtle integration issues in complex or automated visualization systems — where inputs are often constructed indirectly rather than typed explicitly.
In essence, this isn’t about accommodating an artificial case — it’s about maintaining the robustness and user-centered flexibility that have always defined Matplotlib’s design philosophy.
I’d like to open a separate issue to discuss this more broadly, since the handling of empty sequences (like hatch=[]) may have implications beyond this specific function.
This could be an opportunity to evaluate whether Matplotlib should adopt a consistent, system-wide approach toward benign empty inputs — for example, treating them as “no-op” equivalents with a warning, rather than raising outright.
Such a discussion could help clarify the library’s general stance on defensive input handling versus strict validation, especially in the context of data-driven or programmatic pipelines that interface with Matplotlib.
Personally, as a user, it feels more natural and intuitive to assign hatch = [], since it allows me to dynamically add or remove patterns later
The reason will be displayed to describe this comment to others.Learn more.
I recognize you are trying to make your point. However, you are not responding to my points in#30726 (comment).In particular, please stop posting AI-generated content. A generic AI argument falls short of considering the nuances and context of API design here. I am not spending time on rebutting AI gibberish. (I'm sorry if my AI assumption is wrong, but the text feels very AI-generated and scanners say with 99% likelihood it is.)
These principles found my decision:
In the face of ambiguity, refuse the temptation to guess.
Errors should never pass silently.
Consistency - comparestackplot(..., hatch)
If in doubt, keep the API minimal. - It's much simpler to later extend the API than to narrow it.
The reason will be displayed to describe this comment to others.Learn more.
I just wanted to point out a small consistency angle that might be worth considering. If None is accepted to mean “no hatch,” then treating an empty list [] the same way actually keeps the API simple rather than expanding it — both essentially express “no pattern.”
For example:
config = {"color": ["red", "blue"], "hatch": []}ax.bar(x, y, **config)
In a lot of configuration-driven setups (like JSON or YAML), optional arrays often deserialize as empty lists by default instead of None. Handling [] like None would make the behavior more predictable and tolerant in those cases, without changing the meaning or adding any new semantics.
This isn’t about guessing what the user wants — it’s more about keeping the two representations of “no hatch” consistent in data-driven workflows, where either form might appear naturally.
For instance, in dynamic plotting code, users might deliberately pass [] when they’re building visual attributes incrementally or conditionally:
hatches = []if use_patterns: hatches.append('//')ax.bar(x, y, hatch=hatches)
In this kind of logic, hatches starts as an empty list by design — it’s not an error or a missing value, just a placeholder that may or may not be populated later depending on conditions. Passing [] deliberately keeps the code clean and avoids special-case checks like:
ax.bar(x, y, hatch=None if not hatches else hatches)
So, supporting [] as equivalent to None helps in situations where users or frameworks construct arguments dynamically, keeping code simpler and more consistent without altering semantics.
elif not all(h is None or isinstance(h, str) for h in hatch_list):
raise TypeError("All entries in 'hatch' must be strings or None")
else:
# Sequence of hatch patterns: cycle through them as needed.
# Example: hatch=['//', 'xx', '..'] → patterns repeat across datasets.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.