Skip to content

gh-125633: Add function ispackage to stdlib inspect #125634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Oct 27, 2024
7 changes: 7 additions & 0 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ inspect
If true, string :term:`annotations <annotation>` 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
----
Expand Down
11 changes: 8 additions & 3 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

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
Expand Down Expand Up @@ -128,6 +128,7 @@
"ismethoddescriptor",
"ismethodwrapper",
"ismodule",
"ispackage",
"isroutine",
"istraceback",
"markcoroutinefunction",
Expand Down Expand Up @@ -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.

Expand Down
19 changes: 16 additions & 3 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

# 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
Expand Down Expand Up @@ -105,7 +105,7 @@ def unsorted_keyword_only_parameters_fn(*, throw, out, the, baby, with_,
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,
Expand All @@ -121,7 +121,10 @@ def istest(self, predicate, exp):
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",))
Expand Down Expand Up @@ -201,7 +204,17 @@ def test_excluding_predicates(self):
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(':)'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This last test genuinely made me laugh :)


class FakePackage:
__path__ = None

self.assertFalse(inspect.ispackage(FakePackage()))

def test_iscoroutine(self):
async_gen_coro = async_generator_function_example(1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add function :func:`inspect.ispackage` to determine whether an object is a
:term:`package` or not.
Loading