Where are user defined functions stored? - python-3.x

I want to make a sort of help() function for my module. My idea is to have something like module.help() that just prints out the __doc__ of my functions. My first approach was just to hardcode them and then iterate over them, but I feel there has to be a better way to do so. I looked through the docs for a reference as to where they are stored but couldn't find any. What I want is the python equivalent to this but for function names. I would appreciate if anyone could help me out. Thanks!
Edit: Ok so as of now the functions I have are:
BoyleGraph
Boyle_Resolve
Boyle_k
Boyle_k_solve
GayLussacGraph
GayLussac_Resolve
`
and what I have tried so far is:
funcs = list()
for f in dir():
funcs.append(f)
def helper():
for f in funcs[:-13]:
print(help(f))
and this returns something like (redacted):
No Python documentation found for 'GayLussac_Resolve'.
Use help() to get the interactive help utility.
Use help(str) for help on the str class.
Now using:
def helper():
for f in funcs[:-13]:
print(f)
will give me:
BoyleGraph
Boyle_Resolve
Boyle_k
Boyle_k_solve
GayLussacGraph
GayLussac_Resolve
but doing:
def helper():
for f in funcs[:-13]:
print(f, '\n', '#' * 50)
print(f.__doc__)
gives me (redacted):
GayLussac_Resolve
##################################################
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.
which is the __doc__ of str() which is not even in the funcs list. I feel I'm so close yet so far.
PS: I know the funcs definition looks sloppy but when I try to assign directly or use list comprehensions I only get the first element of dir()'s output

dir() gets you a list of names, not objects. You could use the values of globals() instead, but you would need to filter out special names like __builtins__ and imports. Instead, just use help(module). It does everything you want, automatically.

Related

finding the caller object given its name only

I want to find the caller callable from within the called object, without explcitely forwarding the caller to the called as an object.
My current code looks something like this:
class Boo:
#classmethod
def foo(cls, aa, b2=2):
_ret = aa + b2
autolog(fn=Boo.foo, values={"input": locals(), "output": _ret}, message="This is what it should look like")
autolog_nameless(values={"input": locals(), "output": _ret}, message="This would be convenient")
return _ret
and yields
DEBUG | Boo.foo with aa=3.14159, b2=2 yields 5.14159. Message: This is what it should look like
DEBUG | cls=<class '__main__.Boo'>, aa=3.14159, b2=2, _ret=5.14159 yields 5.14159. Message: This would be convenient
The method autolog gets the locals() and the caller method fn, and parses them using the signature of the caller. This works nice and provides the desired output, but requires passing the caller as an object - something I'd like to avoid as I'm refractoring to include this feature and have about 1000 places to modify.
What I'd like to achieve is: pass locals() only; get the name of the caller within autolog_nameless, using inspect.stack()[1][3] or rather inspect.currentframe().f_back.f_code.co_name (latter has much less overhead), and using this - an possibly the information in locals() - find the caller object to inspect it for its signature.
The method autolog_nameless gets cls, actually the class as part of locals() (or would get self if the caller was a simple method), but I can't really do anything with it.
I'd think all the information required is given, but I just can't find a solution. Any help is greatly appreciated.
As it turns out it's quite simple: listing the methods of the class object found in locals() and searching by name should do the trick.
Code, without error checking:
# getting all methods of the class
methods = inspect.getmembers(locals()['cls'], predicate=inspect.ismethod)
# finding the callers name; won't work within the list comprehension for scope issues
_name = inspect.currentframe().f_back.f_code.co_name
# methods is a list of tuples, each tuple holds the name and the method object
fn = [x for x in methods if x[0] == _name][0][1]
and fn is the caller object to check the signature.
Note, locals()['cls'] works here as in the example we have a classmethod, but this is just the object that the called method belongs to.

Simplifying Init Method Python

Is there a better way of doing this?
def __init__(self,**kwargs):
self.ServiceNo = kwargs["ServiceNo"]
self.Operator = kwargs["Operator"]
self.NextBus = kwargs["NextBus"]
self.NextBus2 = kwargs["NextBus2"]
self.NextBus3 = kwargs["NextBus3"]
The attributes (ServiceNo,Operator,...) always exist
That depends on what you mean by "simpler".
For example, is what you wrote simpler than what I would write, namely
def __init__(self,ServiceNo, Operator, NextBus, NextBus2, NextBus3):
self.ServiceNo = ServiceNo
self.Operator = Operator
self.NextBus = NextBus
self.NextBus2 = NextBus2
self.NextBus3 = NextBus3
True, I've repeated each attribute name an additional time, but I've made it much clearer which arguments are legal for __init__. The caller is not free to add any additional keyword argument they like, only to see it silently ignored.
Of course, there's a lot of boilerplate here; that's something a dataclass can address:
from dataclasses import dataclass
#dataclass
class Foo:
ServiceNo: int
Operator: str
NextBus: Bus
NextBus2: Bus
NextBus3: Bus
(Adjust the types as necessary.)
Now each attribute is mentioned once, and you get the __init__ method shown above for free.
Better how? You don’t really describe what problem you’re trying to solve.
If it’s error handling, you can use the dictionary .get() method in the event that key doesn’t exist.
If you just want a more succinct way of initializing variables, you could remove the ** and have the dictionary as a variable itself, then use it elsewhere in your code, but that depends on what your other methods are doing.
A hacky solution available since the attributes and the argument names match exactly is to directly copy from the kwargs dict to the instance's dict, then check that you got all the keys you expected, e.g.:
def __init__(self,**kwargs):
vars(self).update(kwargs)
if vars(self).keys() != {"ServiceNo", "Operator", "NextBus", "NextBus2", "NextBus3"}:
raise TypeError(f"{type(self).__name__} missing required arguments")
I don't recommend this; chepner's options are all superior to this sort of hackery, and they're more reliable (for example, this solution fails if you use __slots__ to prevent autovivication of attributes, as the instance won't having a backing dict you can pull with vars).

How can I get a list of variable names from a lark-parser Tree?

I am using python 3.8.5 and lark-parser 0.11.2. I have a question about Visitors.
I have a grammar for my needs and Lark is working great. I have a case where,
under some conditions, I want to evaluate a returned parse tree and scan it to
get a, possibly empty, list of variable names appearing in the tree.
A sample expression is:
count + num_items
The parse tree from the expression is:
Tree('add', [Tree('variable', [Token('VARIABLE', 'count')]), Tree('variable', [Token('VARIABLE', 'num_items')])])
I figured that I would write a Visitor class that would scann the tree for variables and store them in an internal list:
from lark import Visitor, v_args
#v_args(inline=True)
class FindVariables(Visitor):
def __init__(self):
super().__init__()
self.variable_list = []
def variable(self, var):
try:
self.variable_list.append(var)
except Exception as e:
raise
I am trying to use it as:
fv = FindVariables()
fv2 = fv.visit(parse_result)
for var in fv.variable_list:
...
The issue I have is that when fv = FindVariables() is executed I get a
TypeError exception:
f() missing 1 required positional argument: 'self'
If I change the call above to:
fv = FindVariables().visit(parse_result)
the statement runs but fv does not "see" variable_list.
I am probably misusing the Visitor class. Is there a best/better way to approach this?
Well, I am answering my question but I am not sure that it is the answer.
I changed Visitor to Transformer in the code block in the question and it just worked.
I am glad that I have a solution but it feels like Visitor should have been the right tool here. Still happy to find out if I am misusing the lib here and if there is a better way.

Reason for error when using exec() within a function in python

If I make a code like:
lists = ["a='1'", "b='2'", "c=a+b"]
returned_list = []
for x in lists:
exec(x)
print(c)
It works, and It print "12". but, If I use exec() in function:
lists = ["a='1'", "b='2'", "c=a+b"]
def test(lst):
for x in lists:
exec(x)
print(c)
test(lists)
It returns NameError: name 'c' is not defined. How could I use exec() in function?
When you assign a new variable in a function, you are actually assigning a variable in a scope which will be closed after the function is closed.
Imagine it as a bubble with an item inside, which after the bubble blows, the item blows and disappears as well. It means, using exec() in a function would create a temporary local variable. But since functions have a predefined code, adding new variables to them without changing the code directly, would not be possible. in that case we need to use global keyword for each new variable in exec to make the variable save in the main and not in function. Therefor, your list would like this:
lists = ["global a\na='1'"]
also I'm not quite sure if you like the output of a+b be 12, if not, you can just remove the single quotes around each number such as "a=1" to make them integers
for further information check this and this

How to modify the signature of a function dynamically

I am writing a framework in Python. When a user declares a function, they do:
def foo(row, fetch=stuff, query=otherStuff)
def bar(row, query=stuff)
def bar2(row)
When the backend sees query= value, it executes the function with the query argument depending on value. This way the function has access to the result of something done by the backend in its scope.
Currently I build my arguments each time by checking whether query, fetch and the other items are None, and launching it with a set of args that exactly matches what the user asked for. Otherwise I got the "got an unexpected keyword argument" error. This is the code in the backend:
#fetch and query is something computed by the backend
if fetch= None and query==None:
userfunction(row)
elif fetch==None:
userunction (row, query=query)
elif query == None:
userfunction (row, fetch=fetch)
else:
userfunction (row,fetch=fetch,query=query)
This is not good; for each additional "service" the backend offers, I need to write all the combinations with the previous ones.
Instead of that I would like to primarily take the function and manually add a named parameter, before executing it, removing all the unnecessary code that does these checks. Then the user would just use the stuff it really wanted.
I don't want the user to have to modify the function by adding stuff it doesn't want (nor do I want them to specify a kwarg every time).
So I would like an example of this if this is doable, a function addNamedVar(name, function) that adds the variable name to the function function.
I want to do that that way because the users functions are called a lot of times, meaning that it would trigger me to, for example, create a dict of the named var of the function (with inspect) and then using **dict. I would really like to just modify the function once to avoid any kind of overhead.
This is indeed doable in AST and that's what I am gonna do because this solution will suit better for my use case . However you could do what I asked more simply by having a function cloning approach like the code snippet I show. Note that this code return the same functions with different defaults values. You can use this code as example to do whatever you want.
This works for python3
def copyTransform(f, name, **args):
signature=inspect.signature(f)
params= list(signature.parameters)
numberOfParam= len(params)
numberOfDefault= len(f.__defaults__)
listTuple= list(f.__defaults__)
for key,val in args.items():
toChangeIndex = params.index(key, numberOfDefault)
if toChangeIndex:
listTuple[toChangeIndex- numberOfDefault]=val
newTuple= tuple(listTuple)
oldCode=f.__code__
newCode= types.CodeType(
oldCode.co_argcount, # integer
oldCode.co_kwonlyargcount, # integer
oldCode.co_nlocals, # integer
oldCode.co_stacksize, # integer
oldCode.co_flags, # integer
oldCode.co_code, # bytes
oldCode.co_consts, # tuple
oldCode.co_names, # tuple
oldCode.co_varnames, # tuple
oldCode.co_filename, # string
name, # string
oldCode.co_firstlineno, # integer
oldCode.co_lnotab, # bytes
oldCode.co_freevars, # tuple
oldCode.co_cellvars # tuple
)
newFunction=types.FunctionType(newCode, f.__globals__, name, newTuple, f.__closure__)
newFunction.__qualname__=name #also needed for serialization
You need to do that weird stuff with the names if you want to Pickle your clone function.

Resources