diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index 1eaf1cc5d9a..892f5ba9a76 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -374,6 +374,13 @@ attributes (see :ref:`import-mod-attrs` for module attributes): Return ``True`` if the object is a bound method written in Python. +.. function:: ispackage(object) + + Return ``True`` if the object is a :term:`package`. + + .. versionadded:: 3.14 + + .. function:: isfunction(object) Return ``True`` if the object is a Python function, which includes functions diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index d95f1848ad6..1ccfa329d55 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -326,6 +326,10 @@ inspect If true, string :term:`annotations ` are displayed without surrounding quotes. (Contributed by Jelle Zijlstra in :gh:`101552`.) +* Add function :func:`inspect.ispackage` to determine whether an object is a + :term:`package` or not. + (Contributed by Zhikang Yan in :gh:`125634`.) + json ---- diff --git a/Lib/inspect.py b/Lib/inspect.py index 0c33c6cc995..ea0d992436e 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -6,9 +6,9 @@ It also provides some help for examining source code and class layout. Here are some of the useful functions provided by this module: - ismodule(), isclass(), ismethod(), isfunction(), isgeneratorfunction(), - isgenerator(), istraceback(), isframe(), iscode(), isbuiltin(), - isroutine() - check object types + ismodule(), isclass(), ismethod(), ispackage(), isfunction(), + isgeneratorfunction(), isgenerator(), istraceback(), isframe(), + iscode(), isbuiltin(), isroutine() - check object types getmembers() - get members of an object that satisfy a given condition getfile(), getsourcefile(), getsource() - find an object's source code @@ -128,6 +128,7 @@ __all__ = [ "ismethoddescriptor", "ismethodwrapper", "ismodule", + "ispackage", "isroutine", "istraceback", "markcoroutinefunction", @@ -186,6 +187,10 @@ def ismethod(object): """Return true if the object is an instance method.""" return isinstance(object, types.MethodType) +def ispackage(object): + """Return true if the object is a package.""" + return ismodule(object) and hasattr(object, "__path__") + def ismethoddescriptor(object): """Return true if the object is a method descriptor. diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 77fdc6f2384..a4857dadec2 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -51,7 +51,7 @@ from test.test_inspect import inspect_deferred_annotations # Functions tested in this suite: # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, -# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, +# isbuiltin, isroutine, isgenerator, ispackage, isgeneratorfunction, getmembers, # getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, # getclasstree, getargvalues, formatargvalues, currentframe, # stack, trace, ismethoddescriptor, isdatadescriptor, ismethodwrapper @@ -105,7 +105,7 @@ unsorted_keyword_only_parameters = 'throw out the baby with_ the_ bathwater'.spl class IsTestBase(unittest.TestCase): predicates = set([inspect.isbuiltin, inspect.isclass, inspect.iscode, inspect.isframe, inspect.isfunction, inspect.ismethod, - inspect.ismodule, inspect.istraceback, + inspect.ismodule, inspect.istraceback, inspect.ispackage, inspect.isgenerator, inspect.isgeneratorfunction, inspect.iscoroutine, inspect.iscoroutinefunction, inspect.isasyncgen, inspect.isasyncgenfunction, @@ -121,7 +121,10 @@ class IsTestBase(unittest.TestCase): predicate == inspect.iscoroutinefunction) and \ other == inspect.isfunction: continue - self.assertFalse(other(obj), 'not %s(%s)' % (other.__name__, exp)) + if predicate == inspect.ispackage and other == inspect.ismodule: + self.assertTrue(predicate(obj), '%s(%s)' % (predicate.__name__, exp)) + else: + self.assertFalse(other(obj), 'not %s(%s)' % (other.__name__, exp)) def test__all__(self): support.check__all__(self, inspect, not_exported=("modulesbyfile",), extra=("get_annotations",)) @@ -201,7 +204,17 @@ class TestPredicates(IsTestBase): self.assertFalse(inspect.ismethodwrapper(int)) self.assertFalse(inspect.ismethodwrapper(type("AnyClass", (), {}))) + def test_ispackage(self): + self.istest(inspect.ispackage, 'asyncio') + self.istest(inspect.ispackage, 'importlib') + self.assertFalse(inspect.ispackage(inspect)) + self.assertFalse(inspect.ispackage(mod)) + self.assertFalse(inspect.ispackage(':)')) + class FakePackage: + __path__ = None + + self.assertFalse(inspect.ispackage(FakePackage())) def test_iscoroutine(self): async_gen_coro = async_generator_function_example(1) diff --git a/Misc/NEWS.d/next/Library/2024-10-17-04-52-00.gh-issue-125633.lMck06.rst b/Misc/NEWS.d/next/Library/2024-10-17-04-52-00.gh-issue-125633.lMck06.rst new file mode 100644 index 00000000000..e816a13b75e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-17-04-52-00.gh-issue-125633.lMck06.rst @@ -0,0 +1,2 @@ +Add function :func:`inspect.ispackage` to determine whether an object is a +:term:`package` or not.