Feedback on pypi package `enhanced_dir`

I created my first pypi package.
Was working on a modified version of dir, and decided to make a package.
This is how to use it,

pip install enhanced_dir
from enhanced_dir import enhanced_dir
enhanced_dir(list(), categorize=True, show_types=True, checks=True, collections_abc_list=True)

(have four keyword arguments)
categorize - it would display the class name of the method
show_types - it would show the type of the method
checks - certain checks, such as could the argument be a key to a dictionary, is it iterable, inheritable
collections_abc_list - would give the list of interfaces in the collections.abc module that are implemented by the argument

so, for the above example,
specifying categorize=True gives,

[{'passed': defaultdict(<function enhanced_dir.<locals>.<lambda> at 0x7fb4e2096b90>,
                        {'[]': defaultdict(<class 'set'>,
                                           {'list': {'__add__', '__contains__', '__delitem__', '__dir__', '__eq__',
                                                     '__format__', '__ge__', '__getattribute__', '__getitem__',
                                                     '__gt__', '__iadd__', '__imul__', '__init__', '__init_subclass__',
                                                     '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
                                                     '__new__', '__reduce__', '__reduce_ex__', '__repr__',
                                                     '__reversed__', '__rmul__', '__setitem__', '__sizeof__',
                                                     '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend',
                                                     'index', 'insert', 'pop', 'remove', 'reverse', 'sort'},
                                            'object': {'__str__', '__setattr__', '__delattr__'}})})},
 {'failed': defaultdict(<class 'set'>, {'[]': {'__doc__', '__hash__', '__class__'}})}

specifying show_types=True gives,

 {'passed_types': defaultdict(<function enhanced_dir.<locals>.<lambda> at 0x7fb4e2096dd0>,
                              {'[]': defaultdict(<class 'set'>,
                                                 {<class 'method-wrapper'>: {'__add__', '__contains__', '__delattr__',
                                                                             '__delitem__', '__eq__', '__ge__',
                                                                             '__getattribute__', '__gt__', '__iadd__',
                                                                             '__imul__', '__init__', '__iter__',
                                                                             '__le__', '__len__', '__lt__', '__mul__',
                                                                             '__ne__', '__repr__', '__rmul__',
                                                                             '__setattr__', '__setitem__', '__str__'},
                                                  <class 'builtin_function_or_method'>: {'__dir__', '__format__',
                                                                                         '__getitem__',
                                                                                         '__init_subclass__', '__new__',
                                                                                         '__reduce__', '__reduce_ex__',
                                                                                         '__reversed__', '__sizeof__',
                                                                                         '__subclasshook__', 'append',
                                                                                         'clear', 'copy', 'count',
                                                                                         'extend', 'index', 'insert',
                                                                                         'pop', 'remove', 'reverse',
                                                                                         'sort'}})})},
 {'failed_types': defaultdict(<function enhanced_dir.<locals>.<lambda> at 0x7fb4e20e03b0>,
                              {'[]': defaultdict(<class 'set'>,
                                                 {<class 'type'>: {'__class__'},
                                                  <class 'NoneType'>: {'__hash__'},
                                                  <class 'str'>: {'__doc__'}})})}

specifying collections_abc_list=True gives,

[{'Collection', 'Sequence', 'Iterable', 'Reversible', 'Sized', 'MutableSequence', 'Container'}]

specifying checks=True gives,

[{'defaultdict_arg': False, 'dict_key': False, 'inheritable': False, 'iterable': True}]]

(I used from pprint import pprint to print the output)

Is there something I could change here, either add or remove or modify a functionality?

I added one more function to this package, have named it two_way, this is how it would work,

import enhanced_dir
from pprint import pprint
pprint(enhanced_dir.two_way('+'), compact=True)

gives,

[{'succeeded': defaultdict(<class 'set'>,
                           {'bool': {'complex', 'float', 'bool', 'int'},
                            'bytearray': {'bytes', 'bytearray'},
                            'bytes': {'bytes', 'bytearray'},
                            'complex': {'complex', 'float', 'bool', 'int'},
                            'dir': {'list', 'dir'},
                            'float': {'complex', 'float', 'bool', 'int'},
                            'int': {'complex', 'float', 'bool', 'int'},
                            'list': {'list', 'dir'},
                            'str': {'str'},
                            'tuple': {'tuple'}})}]

it is the builtins, for which '+' would work, it mentions dir also, as dir is also a builtin.
the arguments to this function are,

  1. operator like '+', '-', '*' and so on
  2. success, by default it is set to True, which would give the defaultdict of builtins for which the operator works
  3. fail, by default it is set to False, which would give the defaultdict of builtins for which the operator does not work.

update #1:

have made a few changes to it,

  1. now it does not take success, fail as arguments
  2. it would only return defaultdict of builtins for which the operator works
  3. have added an opposite keyword which works like this,
    if
a operator b

succeeds and opposite=True, then,

b: a

would be mentioned in our defaultdict, whereas if opposite=False, then, a: b would be mentioned.
by default, opposite=False

  1. have added support for dict_keys, dict_values, dict_items
  2. have added support for callable vs non callable, so would see output like this,
'False': {'bool()'}
'int()': {'float()'}

where () indicates the operator works on the callable builtin.

update #2:

have made a few more changes to it,

  1. it takes iterators as an argument also, by default iterators=False, if we specify iterators=True, then it would consider the iterators (like list_iterator, set_iterator, …) also. have skipped longrange_iterator as it was causing memory issues.
    one example of output is,
'(iter(set()))': {'dict().items()', 'dict().keys()'}

added generator to it also, am investigating mappingproxy, but there is some issue with mappingproxy, maybe because of its repr, currently mappingproxy would not be displayed.

have added one more function to this package, it is the reverse of the above function, it would return the list of operators that work for two input arguments.

have named it operator_check, this is how it works,

import enhanced_dir
from pprint import pprint
pprint(enhanced_dir.operator_check(1, 2), compact=True)

gives,

{'succeeded': {'!=', '%', '&', '*', '**', '+', ',', '-', '/', '//', '<', '<<',
               '<=', '==', '>', '>=', '>>', '^', '|'}}

all these operators would work for the two arguments, that is 1 + 2, 1 - 2, 1 / 2, … is valid.

one optional argument is show_failed, by default, it is set to False.
if we set show_failed=True in the above case, that is,

pprint(enhanced_dir.operator_check(1, 2, show_failed=True), compact=True)

then we get output as,

{'failed': {'%=', '&=', '**=', '*=', '+=', '-=', '->', '.', '...', '//=', '/=',
            ':', ':=', ';', '<<=', '=', '>>=', '@', '@=', '^=', '|=', '~'},
 'succeeded': {'!=', '%', '&', '*', '**', '+', ',', '-', '/', '//', '<', '<<',
               '<=', '==', '>', '>=', '>>', '^', '|'}}

have made a few changes to it,
it would show and, or, is, in keywords also, as they appear to be valid when in between two arguments in a lot of cases.

there is one more change I made to the function enhanced_dir, let me describe it, let us take a class as example, the standard dir would give output,

class A:
  pass
dir(A)

gives,

{'__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
 '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__',
 '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
 '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
 '__sizeof__', '__str__', '__subclasshook__', '__weakref__'}

but there are a few more methods which are not listed here, which could be obtained by, dir(type(A))

dir(type(A))

gives,

{'__abstractmethods__', '__annotations__', '__base__', '__bases__',
 '__basicsize__', '__call__', '__class__', '__delattr__', '__dict__',
 '__dictoffset__', '__dir__', '__doc__', '__eq__', '__flags__', '__format__',
 '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__',
 '__init_subclass__', '__instancecheck__', '__itemsize__', '__le__', '__lt__',
 '__module__', '__mro__', '__name__', '__ne__', '__new__', '__or__',
 '__prepare__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__',
 '__ror__', '__setattr__', '__sizeof__', '__str__', '__subclasscheck__',
 '__subclasses__', '__subclasshook__', '__text_signature__',
 '__weakrefoffset__', 'mro'}

and these work on class A, for example, A.__text_signature__, A.__base__, … are all valid.

so, I added these methods also to the enhanced_dir function, now our output when we run enhanced_dir on class A would be,

from pprint import pprint
import enhanced_dir
class A:
  pass
pprint(enhanced_dir.enhanced_dir(A), compact=True, width=120)

gives,

[{'passed': defaultdict(<function enhanced_dir.<locals>.<lambda> at 0x7f4b42b89e60>,
                        {"<class '__main__.A'>": defaultdict(<class 'set'>,
                                                             {'A': {'__init_subclass__', '__instancecheck__',
                                                                    '__subclasscheck__', '__subclasses__',
                                                                    '__subclasshook__', '__weakref__', 'mro'},
                                                              'object': {'__delattr__', '__dir__', '__eq__',
                                                                         '__format__', '__ge__', '__getattribute__',
                                                                         '__gt__', '__hash__', '__init__', '__le__',
                                                                         '__lt__', '__ne__', '__new__', '__reduce__',
                                                                         '__reduce_ex__', '__repr__', '__setattr__',
                                                                         '__sizeof__', '__str__'},
                                                              'type': {'__call__', '__prepare__'}})})},
 {'failed': defaultdict(<class 'set'>,
                        {"<class '__main__.A'>": {'__abstractmethods__', '__base__', '__bases__', '__basicsize__',
                                                  '__class__', '__dict__', '__dictoffset__', '__doc__', '__flags__',
                                                  '__itemsize__', '__module__', '__mro__', '__name__', '__qualname__',
                                                  '__text_signature__', '__weakrefoffset__'}})}]

/Ok bro, I’ll try_ :grin:

1 Like

decided to add an extended_builtins list to this package, currently this list looks something like this,

import enhanced_dir
from pprint import pprint
pprint(enhanced_dir.extended_builtins, compact=True, width=120)

gives,

['Ellipsis', 'False', 'None', 'NotImplemented', 'True', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint',
 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr',
 'dict', 'dir', 'display', 'divmod', 'dreload', 'enumerate', 'eval', 'exec', 'execfile', 'filter', 'float', 'format',
 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord',
 'pow', 'print', 'property', 'range', 'repr', 'reversed', 'round', 'runfile', 'set', 'setattr', 'slice', 'sorted',
 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip', "(iter(b''))", '(iter(bytearray()))',
 '(iter({}.keys()))', '(iter({}.values()))', '(iter({}.items()))', '(iter([]))', '(iter(reversed([])))',
 '(iter(range(0)))', '(iter(set()))', "(iter(''))", '(iter(()))', '(iter(zip()))', '(lambda x: 1).__code__.co_lines',
 '(lambda x: 1).__code__.co_positions', '(type.__dict__)', '((lambda: (yield))())', 'dict().keys', 'dict().values',
 'dict().items']

some of these are not builtins, and I have not included some of the existing builtins to it, but this list makes it a bit easier to carry inspection.
one example is I was testing what all of these could be used after case in a match case block,

{'bool', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 'enumerate',
 'filter', 'float', 'frozenset', 'int', 'list', 'map', 'memoryview', 'object',
 'property', 'range', 'reversed', 'set', 'slice', 'staticmethod', 'str',
 'super', 'tuple', 'type', 'zip'}
{'((lambda: (yield))())', "(iter(''))", '(iter(()))', '(iter([]))',
 "(iter(b''))", '(iter(bytearray()))', '(iter(range(0)))',
 '(iter(reversed([])))', '(iter(set()))', '(iter(zip()))', '(iter({}.items()))',
 '(iter({}.keys()))', '(iter({}.values()))', '(lambda x: 1).__code__.co_lines',
 '(lambda x: 1).__code__.co_positions', '(type.__dict__)', 'Ellipsis', 'False',
 'None', 'NotImplemented', 'True', 'abs', 'all', 'any', 'ascii', 'bin',
 'breakpoint', 'callable', 'chr', 'compile', 'copyright', 'credits', 'delattr',
 'dict().items', 'dict().keys', 'dict().values', 'dir', 'display', 'divmod',
 'dreload', 'eval', 'exec', 'execfile', 'format', 'getattr', 'globals',
 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'locals', 'max', 'min', 'next', 'oct', 'open', 'ord',
 'pow', 'print', 'repr', 'round', 'runfile', 'setattr', 'sorted', 'sum',
 'vars'}

the first set includes the ones that could be used after case (in callable form), like, tuple(), list() is all valid.
the second set includes ones which could not be used after case (again in callable form), like, round(), iter() are all invalid.

update #1 -

  1. made a few changes to it, have not included some more builtins
  2. have added an external_checks dictionary, this is how it works,
pprint(enhanced_dir.external_checks, compact=True)

gives,

{'imports': 'from collections import Counter, namedtuple, defaultdict, '
            'OrderedDict;                               from types import '
            'SimpleNamespace;                               from fractions '
            'import Fraction;                               from decimal '
            'import Decimal;',
 'modules': ['Counter', 'Fraction', 'Decimal', 'defaultdict', 'OrderedDict',
             'namedtuple', 'SimpleNamespace']}

(it does not print well)
the purpose of including imports is to directly import them at once, like,

exec(enhanced_dir.external_checks['imports'])

the main purpose is to perform checks on the modules also, carrying the same, use after case check, and including these would give,

{'Counter', 'Decimal', 'Fraction', 'OrderedDict', 'SimpleNamespace', 'bool',
 'bytearray', 'bytes', 'classmethod', 'complex', 'defaultdict', 'dict',
 'enumerate', 'filter', 'float', 'frozenset', 'int', 'list', 'map',
 'memoryview', 'object', 'property', 'range', 'reversed', 'set', 'slice',
 'staticmethod', 'str', 'super', 'tuple', 'type', 'zip'}
{'((lambda: (yield))())', "(iter(''))", '(iter(()))', '(iter([]))',
 "(iter(b''))", '(iter(bytearray()))', '(iter(range(0)))',
 '(iter(reversed([])))', '(iter(set()))', '(iter(zip()))', '(iter({}.items()))',
 '(iter({}.keys()))', '(iter({}.values()))', '(lambda x: 1).__code__.co_lines',
 '(lambda x: 1).__code__.co_positions', '(type.__dict__)', 'Ellipsis', 'False',
 'None', 'NotImplemented', 'True', 'abs', 'all', 'any', 'ascii', 'bin',
 'breakpoint', 'callable', 'chr', 'compile', 'copyright', 'credits', 'delattr',
 'dict().items', 'dict().keys', 'dict().values', 'dir', 'display', 'divmod',
 'dreload', 'eval', 'exec', 'execfile', 'format', 'getattr', 'globals',
 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'locals', 'max', 'min', 'namedtuple', 'next', 'oct',
 'open', 'ord', 'pow', 'print', 'repr', 'round', 'runfile', 'setattr', 'sorted',
 'sum', 'vars'}

If you are packaging a Python project to share with others, please make sure to follow at least the basic steps in the official packaging tutorial, such as defining your build system in your pyproject.toml, using declarative metadata (e.g. in a setup.cfg or pyrpoject.toml), filling out the requisite metadata fields, and building a wheel for distribution, not just a sdist.

You do actually place your package source code in a src subdirectory, which is a good idea, but it only has a single file module (which is uncommon); and your project might soon outgrow if you keep working on it. You might want to make it an import package rather than a single module, which also gives you the benefit of setuptools or other packaging tools autodetecting your package automatically, and you might also want to choose a non-legacy build system, like flit or hatch, which makes all this much easier for you.

1 Like

thanks for the steps. am looking into it.

made one change to it, instead of specifying collections_abc_list, have renamed it to, interfaces_and_types, this is how it will work now,

import enhanced_dir
from pprint import pprint
pprint(enhanced_dir.enhanced_dir(type.__dict__, \
categorize=False, interfaces_and_types=True), \
compact=True, width=120)

[{'collections_abc': {'Mapping', 'Sized', 'Iterable', 'Container', \
'Collection'}}, {'types': {'MappingProxyType'}}]

one more example,

pprint(enhanced_dir.enhanced_dir(lambda: 1, \
categorize=False, interfaces_and_types=True), \
compact=True, width=120)

[{'collections_abc': {'Hashable', 'Callable'}}, \
{'types': {'LambdaType', 'FunctionType'}}]

where 'collections_abc' is from import collections.abc and 'types' is from import types

update #1 -

  1. have changed it to now pprint from default instead of returning a list/dict, this applies for all the three functions, enhanced_dir, operator_check, two_way. so, no need to manually do from pprint import pprint from now onwards.
  2. have added an argument print_width to all the three functions, if one wants to change the width, for example,
import enhanced_dir
enhanced_dir.enhanced_dir([], print_width=200)

in the background, it is equivalent to,

pprint(enhanced_dir.enhanced_dir([]), compact=True, width=200)

by default compact=True, width=120.

Really good job. I have tried it. :wink:

Unfortunately, I don’t see this(^) as an enhancement. It just makes things, eh, harder(for some cases). Your users can not directly use the information they get from the function. It doesn’t return anything! I am sure that there will be users who will want to process or use the data ,they get from your function, in/with different ways. So, you should let users do what they want with the result. That is the recommended thing(i assume not just by me) in most cases.

Why not simply add a default arg to “ask” if user wants to print the result or not (maybe print=False -i think it must be set to False by default-) and also make your function returning the result in all cases?

1 Like

yes, printing instead of returning is a bit controversial.
I have added a pprint argument to all three functions, but by default I have set it to True, as I dont want to keep importing pprint everytime I use these functions, or keep specifying pprint=True manually everytime I use it.
if I get a returned list/dict by default, then will have to manually import pprint everytime.

as of now, if one wants a returned list/dict, then they could set pprint=False.

update #1 -

  1. after thinking about it a bit more, I decided to make pprint off by default
  2. now, it would return a list/dict by default
  3. but since I would like to put the least effort possible to pprint the output, I have set the argument responsible to pretty print the output name to p, by default, p=False, if one wants to enable pprint, then set, p=True, or even less effort would be, p=1.

one example use is,

enhanced_dir.enhanced_dir([])

output would be a list, which is not pretty printed.
so, x = enhanced_dir.enhanced_dir([]) would make x a list

enhanced_dir.enhanced_dir([], p=1)

output would be a list, which is pretty printed, with compact=True, width=120.
x = enhanced_dir.enhanced_dir([], p=1) would not make x anything, it would be NoneType

enhanced_dir.enhanced_dir([], p=1, print_width=200)

output would be a list, which is pretty printed, with compact=True, width=200.
x = enhanced_dir.enhanced_dir([], p=1, print_width=200) would not make x anything, it would be NoneType

1 Like

I was working on one more function which I think would be useful for inspection purpose.
So, the other day I was using Fraction and it appears that there are different ways to specify arguments for Fraction.
for example,

from fractions import Fraction
Fraction('1/2')
Fraction(1, 2)
Fraction(1)

all are valid.
so, is there a way to get all possible ways to specify arguments.
so, I decided to make a function, have named it argument_inspector, this is how it will work.

import enhanced_dir
enhanced_dir.argument_inspector('int', no_of_arguments=2)

defaultdict(<class 'set'>,
            {0: {True},
             1: {1, 1.2, '1.2', 'float()', '1/2', '1', 'int()', 'False', b'2', 'bool()', 'True'},
             2: {(b'2', 'int()'), (b'2', 'False'), (b'2', 'bool()')}})

which means,

  1. 0: {True} means that it is callable, that is int() is valid
  2. 1: ... means the arguments that are valid, that is int(1), int(1.2), … are all valid
  3. similarly 2: ... means two arguments, that is int(b'2', int()), int(b'2', False), … are all valid

it would work on external imports also, but would have to specify it in the following way,

enhanced_dir.argument_inspector('Fraction', lib='fractions', no_of_arguments=2)

(in the background it does, from fractions import Fraction)

defaultdict(<class 'set'>,
            {0: {True},
             1: {1, 1.2, '1.2', 'float()', '1/2', '1', 'int()', 'False', 'bool()', 'True'},
             2: {(1, '1'), ('1', '1'), ('1', 'None'), ('1', 'True'), ('1', 1), ('int()', '1'), (1, 1), (1, 'None'),
                 (1, 'True'), (1.2, 'None'), ('1.2', 'None'), ('1/2', 'None'), ('False', 1), ('False', '1'),
                 ('False', 'None'), ('False', 'True'), ('True', '1'), ('True', 1), ('True', 'None'), ('True', 'True'),
                 ('bool()', '1'), ('bool()', 'None'), ('bool()', 1), ('bool()', 'True'), ('float()', 'None'),
                 ('int()', 'None'), ('int()', 1), ('int()', 'True')}})

by default no_of_arguments is set to 2, but one could increase it to 3 also.
although it takes around 2 min 30 seconds to run this function if no_of_arguments=3 as the time complexity goes, len(builtins)**no_of_arguments
maximum no_of_arguments it works for is 3 for now, it would not return >3 argument output.

note -

  1. there is one bug in this, that is sometimes it would return a string, but that argument would work in a non string way, for example, it returns 2: (b'2', 'int()'), but int(b'2', int()) works, this is because I use f-strings in the background, and both, f"{'1'}", f'{1}', give '1' as output, am working on fixing this.
  2. the same p=1 to pretty print is valid for this function also.
  3. I use a list named inspection_arguments for this function, that currently looks like,
[1, 1.2, 'abc', '1', '1.2', '1/2', {1, 2, 3}, {'a': 1, 'b': 2}, (1, 2, 3),
 [1, 2, 3], {'c', 'a', 'b'}, ('a', 'b', 'c'), ['a', 'b', 'c'], (1+2j), b'2',
 b'a', bytearray(b'\x00'), frozenset({1, 2, 3}), frozenset({'c', 'a', 'b'})]

the inspection is carried on the concatenation of two lists, that is,
enhanced_dir.extended_builtins + enhanced_dir.inspection_arguments

It appears that there are three ways to get these interfaces and types,

import collections.abc
import typing
import types

plus a lot of the classes in the typing module are there in collections.abc also, that is there are a lot of aliases.
for now I have added the interfaces described in typing module also.
although you would see repetition, for example,

enhanced_dir.enhanced_dir([], p=1, interfaces_and_types=True, categorize=False)

[{'collections_abc': {'Sequence', 'Collection', 'MutableSequence', 'Sized', 'Reversible', 'Container', 'Iterable'}},
 {'types': set()},
 {'typing': {'Sequence', 'Collection', 'MutableSequence', 'Sized', 'Reversible', 'Container', 'Iterable', 'List'}}]

again there is repetition, and for this example, only List is a new entry, but have decided to specify the interfaces implemented from both collections.abc and typing modules as of now.

TL;DR: There is no overlap between types and collections.abc, while most of the builtin types, Abstract Base Classes (ABCs) in collections.abc and types in types are duplicated in typing for historical reasons, but as of PEP 585 in Python 3.9 are mostly redundant. So, at least until >=3.9, they aren’t aliases.

In a little more detail:

  • types contains various utility functions for creating types, as well as a few miscellaneous types for singletons (NoneType, EllipsisType, NotImplementedType, the types of None, ... and NotImplemented, respectively) and types that are not exposed as top-level builtins, like ModuleType, FunctionType and MethodType, the types of modules, functions and methods respectively.
  • collections.abc contains various abstract base classes (ABCs) for bulit-in/standard library classes, like Collection, Sequence, Mapping, etc. These types can’t be instantiated directly, but describe the methods/attributes of an object at an abstract level, e.g. a list, tuple, set and dict are all Collections, since they have a length and can be iterated, but only lists and tuples are Sequence, since they have a defined order and can be accessed by index.
  • typing includes a wide variety of objects useful for static typing and type annotations, used by type checkers like MyPy, rather than for instantiation at runtime. These include a parallel hierarchy of types matching the builtins (list, set, dict, etc), those in collections.abc and a few in types, among others, because before Python 3.9, the builtin types could not be subscripted (e.g. list[str] for a list of strings, or tuple[str, float, float] for a 3-tuple containing a string, then two floats. However, PEP 585 allows using the standard types identically to the typing types for typing purposes, so these duplicate types are now redundant and will be eventually removed (at some point, though the deprecation is likely to be long).
2 Likes

thanks for the details, for now, have kept the output the same as I described in my previous post.

I was working on the function argument_inspector a bit more, decided to add a few more objects to inspection_arguments, currently it looks like this,

[1, 0, 1.2, 'abc', '1', '0', '1.2', '1/2', {1, 2, 3}, {'a': 1, 'b': 2},
 (1, 2, 3), [(2, 5), (3, 2)], [1, 2, 3], [1, 0, 1, 0], {'c', 'b', 'a'},
 ('a', 'b', 'c'), ['a', 'b', 'c'], (1+2j), b'2', b'a', bytearray(b'\x00'),
 frozenset({1, 2, 3}), frozenset({'c', 'b', 'a'}),
 (lambda x: x < 5), range(0, 2)]

one change I made to the function is to include the output also, one example is,

enhanced_dir.argument_inspector('int', p=1)


(the value in key-value pair is the output)

defaultdict(<class 'dict'>,
            {0: {True: 0},
             1: {b'2': 2,
                 0: 0,
                 1: 1,
                 1.2: 1,
                 '0': 0,
                 '1': 1,
                 '1.2': 1,
                 '1/2': 0,
                 'False': 0,
                 'True': 1,
                 'bool()': 0,
                 'float()': 0,
                 'int()': 0},
             2: {(b'2', 0): 2, (b'2', '0'): 2, (b'2', 'False'): 2, (b'2', 'bool()'): 2, (b'2', 'int()'): 2}})

one more example is,

enhanced_dir.argument_inspector('Fraction', lib='fractions', p=1)

defaultdict(<class 'dict'>,
            {0: {True: Fraction(0, 1)},
             1: {0: Fraction(0, 1),
                 1: Fraction(1, 1),
                 1.2: Fraction(5404319552844595, 4503599627370496),
                 '0': Fraction(0, 1),
                 '1': Fraction(1, 1),
                 '1.2': Fraction(5404319552844595, 4503599627370496),
                 '1/2': Fraction(1, 2),
                 'False': Fraction(0, 1),
                 'True': Fraction(1, 1),
                 'bool()': Fraction(0, 1),
                 'float()': Fraction(0, 1),
                 'int()': Fraction(0, 1)},
             2: {('0', 'True'): Fraction(0, 1),
                 ('1', '1'): Fraction(1, 1),
                 ('1', 'None'): Fraction(1, 1),
                 ('False', 'None'): Fraction(0, 1),
                 ('False', 1): Fraction(0, 1),
                 (0, '1'): Fraction(0, 1),
                 (0, 'True'): Fraction(0, 1),
                 (1, 'True'): Fraction(1, 1),
                 (1.2, 'None'): Fraction(5404319552844595, 4503599627370496),
                 ('0', 1): Fraction(0, 1),
                 ('0', '1'): Fraction(0, 1),
                 ('0', 'None'): Fraction(0, 1),
                 ('1', 'True'): Fraction(1, 1),
                 ('1', 1): Fraction(1, 1),
                 ('1.2', 'None'): Fraction(5404319552844595, 4503599627370496),
                 ('1/2', 'None'): Fraction(1, 2),
                 ('False', '1'): Fraction(0, 1),
                 ('False', 'True'): Fraction(0, 1),
                 ('True', '1'): Fraction(1, 1),
                 ('True', 'None'): Fraction(1, 1),
                 (1, 1): Fraction(1, 1),
                 ('True', 'True'): Fraction(1, 1),
                 ('True', 1): Fraction(1, 1),
                 ('bool()', 1): Fraction(0, 1),
                 ('bool()', '1'): Fraction(0, 1),
                 ('bool()', 'None'): Fraction(0, 1),
                 ('bool()', 'True'): Fraction(0, 1),
                 ('float()', 'None'): Fraction(0, 1),
                 ('int()', 1): Fraction(0, 1),
                 (0, 'None'): Fraction(0, 1),
                 ('int()', '1'): Fraction(0, 1),
                 ('int()', 'None'): Fraction(0, 1),
                 ('int()', 'True'): Fraction(0, 1),
                 (0, 1): Fraction(0, 1),
                 (1, '1'): Fraction(1, 1),
                 (1, 'None'): Fraction(1, 1)}})

update #1 -

  1. I was running this function on the builtin next and found out there was an issue with the iterators, so, have decided to add the iterators also to inspection_arguments, earlier,
enhanced_dir.argument_inspector('next', p=1, no_of_arguments=1)

would give,

defaultdict(<class 'dict'>, {1: {'((lambda: (yield))())': None}})

now, it would give,

            {1: {'((lambda: (yield))())': None,
                 "(iter('abc'))": 'a',
                 '(iter((1, 2, 3)))': 1,
                 '(iter([1, 2, 3]))': 1,
                 "(iter(b'2'))": 50,
                 "(iter(b'a'))": 97,
                 '(iter(bytearray(1)))': 0,
                 '(iter(range(5)))': 0,
                 '(iter(reversed([1, 2, 3])))': 3,
                 "(iter(zip((0, 1, 2), 'abc')))": (0, 'a'),
                 "(iter({'a': 1, 'b': 2}.items()))": ('a', 1),
                 "(iter({'a': 1, 'b': 2}.keys()))": 'a',
                 "(iter({'a': 1, 'b': 2}.values()))": 1,
                 '(iter({1, 2, 3}))': 1,
                 "enumerate('a')": (0, 'a')}})
  1. decided to make the output display optional, the argument is show_output, by default show_output=True, if one wants to not display the output, then set show_output=False

have made one change, in the enhanced_dir function, it shows failed, have added a way to display the output for the methods that are in the failed category, what failed means, is that __qualname__ is not applicable for them.
one could get the output using show_failed_output=1, for example,

enhanced_dir.enhanced_dir([1, 2, 3], p=1, show_failed_output=1)

[{'passed': defaultdict(<function enhanced_dir.<locals>.<lambda> at 0x7f251ad005f0>,
                        {'[1, 2, 3]': defaultdict(<class 'set'>,
                                                  {'list': {'__add__', '__contains__', '__delitem__', '__dir__',
                                                            '__eq__', '__format__', '__ge__', '__getattribute__',
                                                            '__getitem__', '__gt__', '__iadd__', '__imul__', '__init__',
                                                            '__init_subclass__', '__iter__', '__le__', '__len__',
                                                            '__lt__', '__mul__', '__ne__', '__new__', '__reduce__',
                                                            '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
                                                            '__setitem__', '__sizeof__', '__subclasshook__', 'append',
                                                            'clear', 'copy', 'count', 'extend', 'index', 'insert',
                                                            'pop', 'remove', 'reverse', 'sort'},
                                                   'object': {'__delattr__', '__setattr__', '__str__'}})})},
 {'failed': defaultdict(<class 'set'>, {'[1, 2, 3]': {'__doc__', '__hash__', '__class__'}})},
 {'failed_output': defaultdict(<class 'dict'>,
                               {'[1, 2, 3]': {'__class__': <class 'list'>,
                                              '__doc__': 'Built-in mutable sequence.\n'
                                                         '\n'
                                                         'If no argument is given, the constructor creates a new empty '
                                                         'list.\n'
                                                         'The argument must be an iterable if specified.',
                                              '__hash__': None}})}]

one thing here is to get the arguments that could be provided to the methods in the passed category, one could use the function argument_inspector for it, for example,

enhanced_dir.argument_inspector('[1, 2, 3].index', p=1, no_of_arguments=2)

defaultdict(<class 'dict'>,
            {1: {1: 0, '1': 0, 'True': 0},
             2: {('1', '0'): 0,
                 ('1', 0): 0,
                 ('True', 'False'): 0,
                 ('True', 'bool()'): 0,
                 ('True', 0): 0,
                 (1, '0'): 0,
                 (1, 'False'): 0,
                 (1, 'bool()'): 0,
                 (1, 'int()'): 0,
                 (1, 0): 0,
                 ('1', 'False'): 0,
                 ('1', 'bool()'): 0,
                 ('1', 'int()'): 0,
                 ('True', '0'): 0,
                 ('True', 'int()'): 0}})

so, here, one could provide two arguments also to the index method for a list, where the second argument is like the position from where the first argument needs to be found in the list.

if we specify, no_of_arguments=3, then it would take some more time to run (it took 5 min 43 sec for this case),

enhanced_dir.argument_inspector('[1, 2, 3].index', p=1, no_of_arguments=3)

defaultdict(<class 'dict'>,
            {1: {1: 0, '1': 0, 'True': 0},
             2: {('1', '0'): 0,
                 ('1', 'False'): 0,
                 ('1', 'int()'): 0,
                 ('True', '0'): 0,
                 ('True', 'False'): 0,
                 ('True', 'bool()'): 0,
                 ('True', 0): 0,
                 (1, '0'): 0,
                 (1, 'False'): 0,
                 (1, 'bool()'): 0,
                 (1, 'int()'): 0,
                 (1, 0): 0,
                 ('1', 'bool()'): 0,
                 ('1', 0): 0,
                 ('True', 'int()'): 0},
             3: {('True', 'False', '1'): 0,
                 ('True', 'False', 1): 0,
                 (1, '0', '1'): 0,
                 (1, 0, '1'): 0,
                 (1, 0, 'True'): 0,
                 ('1', '0', '1'): 0,
                 ('1', '0', 'True'): 0,
                 ('True', 0, '1'): 0,
                 (1, '0', 'True'): 0,
                 ('True', 0, 1): 0,
                 ('True', 0, 'True'): 0,
                 (1, 'bool()', '1'): 0,
                 ('True', '0', 1): 0,
                 (1, '0', 1): 0,
                 (1, 'False', 1): 0,
                 (1, 'False', '1'): 0,
                 (1, 'False', 'True'): 0,
                 (1, 'bool()', 1): 0,
                 (1, 'bool()', 'True'): 0,
                 (1, 'int()', 'True'): 0,
                 ('1', '0', 1): 0,
                 ('1', 'False', 'True'): 0,
                 (1, 0, 1): 0,
                 ('1', 'False', 1): 0,
                 ('1', 'bool()', '1'): 0,
                 ('1', 0, 'True'): 0,
                 ('1', 0, 1): 0,
                 ('1', 0, '1'): 0,
                 ('1', 'bool()', 'True'): 0,
                 ('1', 'bool()', 1): 0,
                 (1, 'int()', 1): 0,
                 ('1', 'False', '1'): 0,
                 ('1', 'int()', '1'): 0,
                 ('1', 'int()', 'True'): 0,
                 ('1', 'int()', 1): 0,
                 ('True', '0', '1'): 0,
                 ('True', '0', 'True'): 0,
                 ('True', 'False', 'True'): 0,
                 ('True', 'bool()', 1): 0,
                 ('True', 'bool()', '1'): 0,
                 ('True', 'bool()', 'True'): 0,
                 (1, 'int()', '1'): 0,
                 ('True', 'int()', '1'): 0,
                 ('True', 'int()', 'True'): 0,
                 ('True', 'int()', 1): 0}})

because the three-argument version is also valid for [1, 2, 3].index, the second and third arguments work like a slice, to look for in between those.
am currently working to combine these two functions, maybe give a list of types that are valid as arguments for a particular method.

have made a couple of updates to it, decided to add a show_graphs argument,

enhanced_dir.enhanced_dir(int, p=1, show_graphs=1, categorize=0)

am working on adding some more graphs which would be useful for inspection.
have added show_arguments and no_of_arguments arguments,

enhanced_dir.enhanced_dir([1, 2], p=1, show_arguments=1, no_of_arguments=1, categorize=0)

it would give output like,

[["[1, 2].remove: defaultdict(<class 'set'>, {1: {'1', 1, 'True'}})"],
 ["[1, 2].index: defaultdict(<class 'set'>, {1: {'1', 1, 'True'}})"],
 ["[1, 2].__reversed__: defaultdict(<class 'set'>, {0: {True}})"],
 ["[1, 2].clear: defaultdict(<class 'set'>, {0: {True}})"],
 ["[1, 2].__sizeof__: defaultdict(<class 'set'>, {0: {True}})"],
 ["[1, 2].__format__: defaultdict(<class 'set'>, {1: {'str()'}})"],
 ["[1, 2].__delitem__: defaultdict(<class 'set'>, {1: {0, 1, 'False', 'True', 'int()', 'bool()', '1', '0'}})"],
 ["[1, 2].__rmul__: defaultdict(<class 'set'>, {1: {0, 1, 'False', 'True', 'int()', 'bool()', '1', '0'}})"],
 ["[1, 2].__add__: defaultdict(<class 'set'>, {1: {'dir()', 'list()'}})"],
 ...]

by default no_of_arguments=2.