summaryrefslogtreecommitdiff
path: root/cmd2/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'cmd2/utils.py')
-rw-r--r--cmd2/utils.py31
1 files changed, 30 insertions, 1 deletions
diff --git a/cmd2/utils.py b/cmd2/utils.py
index 5a4fdbf7..39dc6e2b 100644
--- a/cmd2/utils.py
+++ b/cmd2/utils.py
@@ -3,7 +3,9 @@
import collections
import collections.abc as collections_abc
+import functools
import glob
+import inspect
import os
import re
import subprocess
@@ -11,7 +13,7 @@ import sys
import threading
import unicodedata
from enum import Enum
-from typing import Any, Callable, Dict, Iterable, List, Optional, TextIO, Union
+from typing import Any, Callable, Dict, Iterable, List, Optional, TextIO, Type, Union
from . import constants
@@ -1037,3 +1039,30 @@ def categorize(func: Union[Callable, Iterable[Callable]], category: str) -> None
setattr(item, constants.CMD_ATTR_HELP_CATEGORY, category)
else:
setattr(func, constants.CMD_ATTR_HELP_CATEGORY, category)
+
+
+def get_defining_class(meth: Callable) -> Optional[Type]:
+ """
+ Attempts to resolve the class that defined a method.
+
+ Inspired by implementation published here:
+ https://stackoverflow.com/a/25959545/1956611
+
+ :param meth: method to inspect
+ :return: class type in which the supplied method was defined. None if it couldn't be resolved.
+ """
+ if isinstance(meth, functools.partial):
+ return get_defining_class(meth.func)
+ if inspect.ismethod(meth) or (inspect.isbuiltin(meth)
+ and getattr(meth, '__self__') is not None
+ and getattr(meth.__self__, '__class__')):
+ for cls in inspect.getmro(meth.__self__.__class__):
+ if meth.__name__ in cls.__dict__:
+ return cls
+ meth = getattr(meth, '__func__', meth) # fallback to __qualname__ parsing
+ if inspect.isfunction(meth):
+ cls = getattr(inspect.getmodule(meth),
+ meth.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
+ if isinstance(cls, type):
+ return cls
+ return getattr(meth, '__objclass__', None) # handle special descriptor objects