There are some packages which has some methods like subscribe or add which let user adds a customized callback function. For debug purpose and curiosity, I would like to know which function and which class in this package actually called my callback function later. So it would be nice if I have the line number of the class.
Minimal working example
# test.py
import inspect
class Foo: # line 5
def caller(self):
print("Hello from caller")
b=Bar()
b.func()
class Bar:
def func(self):
print("Hello from func")
stack = inspect.stack()
the_class = stack[1][0].f_locals["self"].__class__.__name__
the_method = stack[1][0].f_code.co_name
# Need some help here to get value 5
# To do
# ...
print(f"Called by {the_class}.{the_method}()")
f = Foo()
f.caller()
Output:
Hello from caller
Hello from func
Called by Foo.caller()
Q: Inside func, how to get value 5 which is line number of definition of class Foo? Ideally by inspect or other traceback magic, not by searching the string in file.
Related
I have some code that creates instances from a list of classes that is passed to it. This cannot change as the list of classes passed to it has been designed to be dynamic and chosen at runtime through configuration files). Initialising those classes must be done by the code under test as it depends on factors only the code under test knows how to control (i.e. it will set specific initialisation args). I've tested the code quite extensively through running it and manually trawling through reams of output. Obviously I'm at the point where I need to add some proper unittests as I've proven my concept to myself. The following example demonstrates what I am trying to test:
I would like to test the run method of the Foo class defined below:
# foo.py
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run():
for thing in self._stuff:
stuff = stuff()
stuff.run()
Where one (or more) files would contain the class definitions for stuff to run, for example:
# classes.py
class Abc:
def run(self):
print("Abc.run()", self)
class Ced:
def run(self):
print("Ced.run()", self)
class Def:
def run(self):
print("Def.run()", self)
And finally, an example of how it would tie together:
>>> from foo import Foo
>>> from classes import Abc, Ced, Def
>>> f = Foo([Abc, Ced, Def])
>>> f.run()
Abc.run() <__main__.Abc object at 0x7f7469f9f9a0>
Ced.run() <__main__.Abc object at 0x7f7469f9f9a1>
Def.run() <__main__.Abc object at 0x7f7469f9f9a2>
Where the list of stuff to run defines the object classes (NOT instances), as the instances only have a short lifespan; they're created by Foo.run() and die when (or rather, sometime soon after) the function completes. However, I'm finding it very tricky to come up with a clear method to test this code.
I want to prove that the run method of each of the classes in the list of stuff to run was called. However, from the test, I do not have visibility on the Abc instance which the run method creates, therefore, how can it be verified? I can't patch the import as the code under test does not explicitly import the class (after all, it doesn't care what class it is). For example:
# test.py
from foo import Foo
class FakeStuff:
def run(self):
self.run_called = True
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert <SOMETHING>.run_called, "FakeStuff.run() was not called"
It seems that you correctly realise that you can pass anything into Foo(), so you should be able to log something in FakeStuff.run():
class Foo:
def __init__(self, stuff):
self._stuff = stuff
def run(self):
for thing in self._stuff:
stuff = thing()
stuff.run()
class FakeStuff:
run_called = 0
def run(self):
FakeStuff.run_called += 1
def test_foo_runs_all_stuff():
under_test = Foo([FakeStuff, FakeStuff])
under_test.run()
# How to verify that FakeStuff.run() was called?
assert FakeStuff.run_called == 2, "FakeStuff.run() was not called"
Note that I have modified your original Foo to what I think you meant. Please correct me if I'm wrong.
Can I actually use functions of another class as a parameter and attribute for a class instance/object?
I noticed if I do something like this there are lots of oddities (Note that I use Jupyter lab):
class ObjectClass:
#A class; I will insert a function into generalMethod
def __init__(self, generalMethod):
self.generalMethod = generalMethod
class GeneralMethods():
#Two different methods that I want to call
def method1(self):
add(2)
def method2(self):
print("Hey now, you're an all-star, get your game on, go play" )
return "Hey, I can return stuff, at least!",2
def add(input):
#A simple function that adds 1 to input
print(1 + input)
#Creating two objects with different methods as inputs from GeneralMethods
gm = GeneralMethods()
object1 = ObjectClass(gm.method1())
object2 = ObjectClass(gm.method2())
#Attempting to call anything from generalMethod; a getter method does the same
object1.generalMethod
object2.generalMethod
gm.method1() and gm.method2() does everything inside it, even when I simply declare it as a parameter of the object/instance!
But anyObject.generalMethod doesn't do anything besides return whatever is in the return when I call it, and if a function is in there, it will return None.
So can I actually call a function from the attribute and it perform like gm.method1() does when calling it from the attribute (anyObjectIChoose.generalMethod).
You can pass a function as parameter:
def foo():
print('hello')
def bar(_f):
_f()
bar(_f=foo)
'hello'
Note that when you add () to function's name, you invoke it. To pass as param you need just the name, not invoke it.
The current code I have, allows the function to call the wrapper decorator, and uses the function name in its code. However, I'm looking for a way to give the function a 'alias' in a way as an argument. Here's the current code:
import os, sys
# Take user input
message = input('type command: ')
# Command wrapper
ALLCOMMANDS = {}
def command(function):
ALLCOMMANDS[function.__name__] = function
return function
# Commands
#command
def foo():
print("bar")
#command
def goo():
print('ber')
# Run appropriate command
if message in ALLCOMMANDS:
ALLCOMMANDS[message]()
For example I would want to be able to call the function by a name such as '!foo' from the user input, so maybe the argument would look like #command(name='!foo'), I just don't know where to go from there to use that argument in the decorator since it already has an argument.
I attempted
# Command wrapper
ALLCOMMANDS = {}
def command(name):
ALLCOMMANDS[name] = name
return name
but keep getting errors and I assume I am missing something
You should read up a bit more on python decorators. You're getting an error with:
def command(name):
ALLCOMMANDS[name] = name
return name
Because of the return name.
Decorators are just syntactic sugar. This:
#command
def foo():
print('bar')
Is equivalent to:
def foo():
print('bar')
foo = command(foo)
From this you can see why your original decorator works. At the end you return function.
Things get a little tricker when you have a decorator that takes arguments. Desugared the following:
#command('nickname')
def foo():
print('bar')
Looks like this:
def foo():
print('bar')
foo = command('nickname')(foo)
So, to write a decorator that takes arguments, the decorator needs to return a function that takes the function to decorate as an argument:
def command(nickname):
def wrapped(f):
ALLCOMMANDS[nickname] = f
return f
return wrapped
Also consider making ALLCOMMANDS an attribute on your command instead of a global (UPPER_SNAKE is usually reserved for constants):
def command(nickname):
def wrapped(f):
command._functions[nickname] = f
return f
return wrapped
command._functions = {}
How can I indent the print output on the command line from a class that is called? I can't edit the class file to add tabs to each print().
So I would call the imported class in mypythonthing.py:
print('Calling class')
MyClass()
All the print output would then be indented, or have something prepended to it.
e.g.
$ python mypythonthing.py
$ Running your python script...
$ Calling class
$ > The print output from MyClass is indented
$ > Exiting MyClass
$
Patch the built-in print function to prefix each line with your indentation.
import builtins
def print(*args, **kwargs):
builtins.print(" > ", *args, **kwargs)
If you can put the code that should be indented inside (one or more) functions, then you can use a decorator to wrap these functions.
Then any invocation of print inside these function will be indented.
Also, you will only need to declare this function in your main script, and not anywhere else.
Example -
import builtins
import another # for demo purposes only
# This will override the default `print` function.
# Invoking it as a decorator will automatically perform
# initialisation and cleanup. There is also never a need
# to modify this.
def indent(f):
def closure():
old = builtins.print
builtins.print = lambda x, *args, **kwargs: old("\t>", x, *args, **kwargs)
f()
builtins.print = old
return closure
some_number = "100"
# Example function, note decorator usage.
# This function may **not** take any parameters!
# It may however, use any variables declared before it.
#indent
def indentedStuffGoesHere():
print("Inside `indentedStuffGoesHere`")
print(some_number)
another.Foo().bar()
another.stuff()
print("entering special block")
indentedStuffGoesHere()
print("done")
another.py
def stuff():
print("TESTING stuff")
class Foo:
def bar(self):
print("HELLO FROM FOO")
Output:
entering special block
> Inside `indentedStuffGoesHere`
> 100
> HELLO FROM FOO
> TESTING stuff
done
i think what you might be looking for is textwrap:
textwrap docs
so as an example:
wrapper = textwrap.TextWrapper(width=preferredWidth, subsequent_indent='\t')
message = "asdf" * 50
print wrapper.fill(message)
class TestUM:
#classmethod
def setup_class(will):
""" Setup Class"""
will.var = "TEST"
def setup(this):
""" Setup """
print this.var
def test_number(work):
""" Method """
print work.var
def teardown(orr):
""" Teardown """
print orr.var
#classmethod
def teardown_class(nott):
""" Teardown Class """
print nott.var
Run it as
nosetests -v -s test.py
I am not a Python expert but I cannot figure out why the above code works flawlessly using nose. Every print prints "TEST". What exactly is happening here.
In instance methods, the first argument is the instance itself.
In class methods, the first argument is the class itself.
In your case, rather than name that argument self or cls (the convention), you've named it this, work, orr, and nott. But they're all getting the same argument regardless of the name of the argument.
You've successfully set the attribute var to "TEST", so they all see it correctly.
Example functions without the use of classes:
def test1(attribute):
print attribute
def test2(name):
print name
def test3(cls):
print cls
def test4(self):
print self
Calling those functions:
>>> test1('hello')
hello
>>> test2('hello')
hello
>>> test3('hello')
hello
>>> test4('hello')
hello
The name of the argument doesn't matter. All that matters is what the argument is pointing at, which is always the instance or class