@@ -192,6 +192,8 @@ def _combine_group_contents(
192192
193193H = TypeVar ("H" , bound = "Histogram" )
194194
195+ NO_METADATA = object ()
196+
195197
196198# We currently do not cast *to* a histogram, but this is consistent
197199# and could be used later.
@@ -203,7 +205,8 @@ class Histogram:
203205 "_hist" ,
204206 "axes" ,
205207 )
206- # .metadata and ._variance_known are part of the dict
208+ # .metadata and ._variance_known are part of the dict.
209+ # .metadata will not be placed in the dict if not passed.
207210
208211 _family : ClassVar [object ] = boost_histogram
209212
@@ -243,7 +246,7 @@ def __init__(
243246 self ,
244247 * axes : Axis | CppAxis | Histogram | CppHistogram | dict [str , Any ],
245248 storage : Storage | None = None ,
246- metadata : Any = None ,
249+ metadata : Any = NO_METADATA ,
247250 ) -> None :
248251 """
249252 Construct a new histogram.
@@ -259,7 +262,8 @@ def __init__(
259262 storage : Storage = bh.storage.Double()
260263 Select a storage to use in the histogram
261264 metadata : Any = None
262- Data that is passed along if a new histogram is created
265+ Data that is passed along if a new histogram is created. No not use
266+ in new code; set properties in __dict__ directly instead.
263267 """
264268 self ._variance_known = True
265269 storage_err_msg = "storage= is not allowed with conversion constructor"
@@ -269,7 +273,7 @@ def __init__(
269273 if storage is not None :
270274 raise TypeError (storage_err_msg )
271275 cpp_hist : CppHistogram = axes [0 ] # type: ignore[assignment]
272- self ._from_histogram_cpp (cpp_hist , metadata = None )
276+ self ._from_histogram_cpp (cpp_hist , metadata = metadata )
273277 return
274278
275279 # If we construct with another Histogram as the only positional argument,
@@ -301,7 +305,8 @@ def __init__(
301305 if storage is None :
302306 storage = Double ()
303307
304- self .metadata = metadata
308+ if metadata is not NO_METADATA :
309+ self .metadata = metadata
305310
306311 # Check for missed parenthesis or incorrect types
307312 if not isinstance (storage , Storage ):
@@ -342,7 +347,7 @@ def _clone(
342347
343348 self = cls .__new__ (cls )
344349 if isinstance (_hist , tuple (_histograms )):
345- self ._from_histogram_cpp (_hist ) # type: ignore[arg-type]
350+ self ._from_histogram_cpp (_hist , metadata = NO_METADATA ) # type: ignore[arg-type]
346351 if other is not None :
347352 return cls ._clone (self , other = other , memo = memo )
348353 return self
@@ -352,7 +357,7 @@ def _clone(
352357 if other is None :
353358 other = _hist
354359
355- self ._from_histogram_object (_hist )
360+ self ._from_histogram_object (_hist , metadata = NO_METADATA )
356361
357362 if memo is NOTHING :
358363 self .__dict__ = copy .copy (other .__dict__ )
@@ -372,16 +377,17 @@ def _new_hist(self, _hist: CppHistogram, memo: Any = NOTHING) -> Self:
372377 """
373378 return self .__class__ ._clone (_hist , other = self , memo = memo )
374379
375- def _from_histogram_cpp (self , other : CppHistogram , * , metadata : Any = None ) -> None :
380+ def _from_histogram_cpp (self , other : CppHistogram , * , metadata : Any ) -> None :
376381 """
377382 Import a Cpp histogram.
378383 """
379384 self ._variance_known = True
380385 self ._hist = other
381- self .metadata = metadata
386+ if metadata is not NO_METADATA :
387+ self .metadata = metadata
382388 self .axes = self ._generate_axes_ ()
383389
384- def _from_histogram_object (self , other : Histogram , * , metadata : Any = None ) -> None :
390+ def _from_histogram_object (self , other : Histogram , * , metadata : Any ) -> None :
385391 """
386392 Convert self into a new histogram object based on another, possibly
387393 converting from a different subclass.
@@ -391,7 +397,10 @@ def _from_histogram_object(self, other: Histogram, *, metadata: Any = None) -> N
391397 self .axes = self ._generate_axes_ ()
392398 for ax in self .axes :
393399 ax .__dict__ = copy .copy (ax ._ax .raw_metadata )
394- self .metadata = other .metadata if metadata is None else metadata
400+ if metadata is not NO_METADATA :
401+ self .metadata = metadata
402+ elif "metadata" in other .__dict__ :
403+ self .metadata = other .metadata
395404
396405 # Allow custom behavior on either "from" or "to"
397406 other ._export_bh_ (self )
@@ -420,6 +429,14 @@ def _generate_axes_(self) -> AxesTuple:
420429
421430 return AxesTuple (self ._axis (i ) for i in range (self .ndim ))
422431
432+ # Backward compat for metadata default
433+ def __getattr__ (self , name : str ) -> Any :
434+ if name == "metadata" :
435+ msg = ".metadata was not set, returning None instead of Attribute error, boost-histogram 1.7+ will error."
436+ warnings .warn (msg , FutureWarning , stacklevel = 2 )
437+ return None
438+ return super ().__getattribute__ (name )
439+
423440 def _to_uhi_ (self ) -> dict [str , Any ]:
424441 """
425442 Convert to a UHI histogram.
@@ -695,7 +712,7 @@ def storage_type(self) -> type[Storage]:
695712 def _storage_type (self ) -> type [Storage ]:
696713 warnings .warn (
697714 "Accessing storage type has changed from _storage_type to storage_type, and will be removed in future." ,
698- DeprecationWarning ,
715+ FutureWarning ,
699716 stacklevel = 2 ,
700717 )
701718 return cast (self , self ._hist ._storage_type , Storage ) # type: ignore[return-value]
0 commit comments