Get number of args for function - python

I have a module, ui. I'd like to get a list of all the classes that ui defines which are a subclass of ui.View, and that can be called without arguments.
I've tried the following code:
ui_elements=[d for d in ui.__dict__.values() if type(d)==type and inspect.getargspec(d.__init__)[0]==1 and isinstance(d(),ui.View)]
But I get the following error from inside the inspect module:
TypeError: <slot wrapper '__init__' of 'object' objects> is not a Python function
I'm using inspect instead of a try: except: just because checking beforehand seems like a better solution than trying it to see if it works.
So, how can I get a list of all the classes that subclass ui.View and do not require arguments to create? I'd like to do this inline if possible, and I'd also like to avoid using try:/except: statements.

To get a list comprehension with try/except you can use a local function:
def tryCreate(class_):
try:
return class_()
except:
return None
ui_elements = set(tryCreate(d) for d in ui.__dict__.values()
if issubclass(d, ui.View)) - set(None)
Okay, it's a set comprehension in the end ;-)

As said in comments, you should use try/catch, but if you really want to do it that way...
First, use inspect to check if your symbol is a class, use issubclass as suggested in comments and you want the number of arguments to be 1, not the first argument to be 1. So, something like this:
ui_elements = [ d for d in ui.__dict__.values()
if (inspect.isclass(d) and
issubclass(d, ui.View) and
inspect.ismethod(d.__init__) and
len(inspect.getargspec(d.__init__)[0]) == 1) ]

Related

Is it possible to access builtins or any other useful functions through an Ellipsis object?

I have a challenge where I'm given a function where I can pass only a single argument which must be a builtin (no modules of any kind), for example chr or IndexError and use its attributes and call its functions to get access to other builtin types.
For example, if I choose the getattr function, I can access the builtins like this:
def main(a):
builtins = a(a, '__self__')
main(getattr)
Most other functions aren't of much help for my challenge. I know that the attributes are deep and a lot of information can be extracted.
This is a good reference: https://book.hacktricks.xyz/misc/basic-python/bypass-python-sandboxes
What can I get access to using an Ellipsis object, in Python written as ... ?
Subclasses can be accessed using ....__class__.__base__.__subclasses__() which returns a list and eventually get access back using a for loop to find which of those classes's __name__ attribute is catch-warnings, and that class's _module attribute has all the builtins (Code). I cannot use that because the index at which it will appear is always different
The python version I target is 3.9.

Two Objects Created from the Same Class, isinstance = False

I'm trying to create some unit tests for some code here at work.
The code takes in an object and based on information within that object imports a specific module then creates an instance of it.
The test I am trying to write creates the object and then I check that it is an instance of the class I expect it to import. The issue is the isinstance check is failing.
Here is what my test looks like.
import importlib
from path.to.imported_api import SomeApi
api = importlib.import_module("path.to.imported_api").create_instance() # create_instance() is a function that returns SomeApi().
assert isinstance(api, SomeApi) # This returns false, but I am not sure why.
The reason for the difference is, that whereas both objects refer to the same module, they get different identifiers as you load a new module and bypass sys.modules. See also the explanation here: https://bugs.python.org/issue40427
A hack might be to compare the name:
assert isinstance(api.__class__.__name__, SomeApi.__name__)
There are a few things that could cause that:
So first, it could be that the api is just returning something that looks like SomeApi(). Also it coud is be that SomeApi is overwriting isinstance behaviour.

Use isinstance with an undefined class

Assume that class MyClass is sometimes, but not always, defined. I have a function foo(a=None) in which argument a can be None, a string, or an object of MyClass.
My question is: If MyClass is not defined in my Python session, how can I check the type of argument a in a fashion similar to isinstance without getting a NameError?
Note on duck-typing: I am deliberately limiting the function.
I'm using Python 2.6.x and Updating is not an option. A forward-compatible solution (especially for 2.7.x) is highly appreciated.
I would suggest a different approach: polyfill the class so all code that wants to refer to it can simply do so:
try:
from foo import Bar # load the native class
except ImportError:
class Bar:
pass # implement necessary parts here
You can put this into your own module and then from mymodule import Bar everywhere it's needed. That allows all your code to use Bar regardless of whether it's defined natively or not.
Even if redefining the class isn't your preferred way to handle this, handling the ImportError is still the way to handle this situation, since you will have to import the class either way and that's where the error will occur. Instead of defining the class, you may instead want to set a class_exists = False flag or something.
If MyClass isn't defined then you have no way to reference its type.
Therefore you can have no way to verify that type(a) has the correct value.
I workarounded the problem by overriding a method in MyClass and doing nothing in it (pass). After that I no longer needed to check its type.
Different workarounds may exist for different cases. Catching the NameError could be another one.
t = 'asdfas'
print(isinstance(t, str))
try:
print(isinstance(t, MyClass))
except NameError:
print(False)
Seems to me, that such a construct may appear in future python. Like typed python, which is quite new. And in typed python we have a possibility to use future types, in apos.

Difference in how functions are called in Python

I've noticed some functions are called using the var.func() format as in var1.split() while other functions are called using the func(var) format as in sorted(list1).
Here's a very simple program to illustrate the question. I've also noticed the same behavior with open and read functions.
str1 = "This is a string"
list1 = str1.split()
print str1.split(' ')
print sorted(list1)
I'm very new to programming so any help would be greatly appreciated!
Everything in python is an object. Thus when doing something like this:
s = "some string"
s is an str object and you can call all the str methods on it. You can also do things like this:
"some string".split()
and it will give you a list of splitted strings.
This difference has to do with issues of scope. Functions which can be called directly, such as sorted(list1) in your example above, are either builtin functions, or else defined at the top level of one of your imported libraries (for example when using from simpy import *, you can call test() directly to run the built in test suite for the simpy library). Functions which are accessed through the dot operator are functions which are defined for the particular data type that you are applying them to. Remember that each data type in python is an object, and therefore an instance of a class. Those functions, such as split() are defined in that data type's class definition. Additionally, to use the example of test() from the simpy library again, if you were to import a library with only import simpy, you would have to use simpy.test() to call that method.
from simpy import *
test()
vs
import simpy
simpy.test()
The first works because you've imported all methods and classes from the top level of the simpy library, whereas the second works because you've explicitly dived into the scope of the simpy library.
var.func() just means that the function belongs to the object.
For instance, x.sort(). lists (like x) have a function sort.
When you call func(var), func is not a function of lists.
For instance, sorted(x).
This isn't Python specific. You will see the same idea in other languages (e.g. Java).
In var.func() the func() is meant to be used with the var object.
e.g. split() on a string object but cannot use on a list
But func(var) is not confined to a single var object type. you can use it with any appropriate var object.
e.g. sorted() can be used with any iterable like lists, tuples, dicts...
Following TraxusIV's line of thought, I tried the following and it worked!
from string import split
str1 = "This is a string"
list1 = str1.split()
print split(str1)
print sorted(list1)

Python Functions - how can I dynamically call a function when I don't know how many arguments it expects?

I'd like to write a method in python which dynamically reads a module and creates a list of all the functions in that module. Then I'd like to loop through this list and call each function. So far I have the following code:
import mymodule
from inspect import getmembers, isfunction
def call_the_functions():
functions_list = [f for f in getmembers(mymodule) if isfunction(f[1])]
for f in functions_list:
result = f()
My problem is that my program is crashing because some of the functions require arguments. I'd like to do something like the following, but don't know how:
for f in functions_list:
args = [""] * f.expectedNumberOfArguments()
result = f(*args)
Am I going about this the right way? (I'm basically writing a unit test, and the first check is simply that the functions return an object of the right type, regardless of the arguments they are called with.)
Your approach is fundamentally flawed. If written carefully, the functions will reject arguments of invalid type by raising TypeError or asserting. Failing that, they will try to access an attribute or method on the parameter and promptly get an AttributeError.
It is futile to try to avoid writing unit tests that know something about the functions being tested.
You could use inspect.getargspec():
In [17]: def f(x, z=2, *args, **kwargs): pass
In [18]: inspect.getargspec(f)
Out[18]: ArgSpec(args=['x', 'z'], varargs='args', keywords='kwargs', defaults=(2,))
Whether it's meaningful to call functions that you know nothing about with arbitrary arguments is a different question...
You can use the inspect module and getargspec. Here is a simple example:
import inspect
def myfunc(x):
return x * 2
print inspect.getargspec(myfunc);
gives:
ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
Some functions might be generators, so your test strategy of calling them might not give you what you expect, inspect.isgeneratorfunction() will allow you to test for that.

Categories