@@ -238,6 +238,10 @@ def test_myfunc(xp):
238238 raw_attr = getattr_static (cls , method_name )
239239 method = getattr (cls , method_name )
240240 cloned_method = _clone_function (method )
241+ # Update the ``__qualname__`` because this will be used later to check
242+ # whether something is a method defined in the class of interest, or just
243+ # a reference to a function that's stored in a class.
244+ cloned_method .__qualname__ = f"{ cls .__name__ } .{ method_name } "
241245
242246 method_to_set : Any
243247 if isinstance (raw_attr , staticmethod ):
@@ -378,6 +382,19 @@ def iter_tagged() -> Iterator[
378382 with contextlib .suppress (KeyError , TypeError ):
379383 tags = _ufuncs_tags [func ]
380384 if tags is not None :
385+ if isinstance (target , type ):
386+ # There's a common pattern to wrap functions in namespace
387+ # classes to bypass lazy_xp_function like this:
388+ #
389+ # class naked:
390+ # myfunc = mymodule.myfunc
391+ #
392+ # To ensure this still works when checking for tags in
393+ # attributes of classes, use ``__qualname__`` to check whether
394+ # or not ``func`` was originally defined within ``target``.
395+ qn = getattr (func , "__qualname__" , "" )
396+ if not qn .startswith (f"{ target .__name__ } ." ):
397+ continue
381398 # put attr, and func in the outputs so we can later tell
382399 # if this was a staticmethod or classmethod.
383400 yield target , name , attr , func , tags
0 commit comments