In a ipython script notebook I want to have something like:
from mod import run
def initialize():
print("In!")
run()
The script makes the assumption the user needs to define a function initialize, and the run() should test if it is defined and invoke it.
How should I define run() to get a reference to a "initialized" function defined in the interactive namespace ?
Here is one possible approach -
Inside the module:
class MyHandler(object):
def __init__(self, *args):
""" Init stuff """
def run(self, *args):
self.initializer(*args)
"""do running stuff"""
pass
def initializer(self):
raise Exception("Initializer must be defined")
def shutdown(self):
"""default cleanup stuff"""
pass
Then the Ipython workflow could be something like:
from mod import MyHandler
new_handler = MyHandler()
def add_method(obj, meth):
obj.__dict__[meth.__name__] = meth.__get__(obj, obj.__class__)
def intializer(self):
print("In!")
add_method(new_handler, initializer)
new_handler.run()
Related
I'm trying to find a way to dynamically add methods to a class through decorator.
The decorator i have look like:
def deco(target):
def decorator(function):
#wraps(function)
def wrapper(self, *args, **kwargs):
return function(*args, id=self.id, **kwargs)
setattr(target, function.__name__, wrapper)
return function
return decorator
class A:
pass
# in another module
#deco(A)
def compute(id: str):
return do_compute(id)
# in another module
#deco(A)
def compute2(id: str):
return do_compute2(id)
# **in another module**
a = A()
a.compute() # this should work
a.compute2() # this should work
My hope is the decorator should add the compute() function to class A, any object of A should have the compute() method.
However, in my test, this only works if i explicitly import compute into where an object of A is created. I think i'm missing something obvious, but don't know how to fix it. appreciate any help!
I think this will be quite simpler using a decorator implemented as a class:
class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
#deco(A)
def compute(a_instance):
print(a_instance.val)
A(1).compute()
A(2).compute()
outputs
1
2
But just because you can do it does not mean you should. This can become a debugging nightmare, and will probably give a hard time to any static code analyser or linter (PyCharm for example "complains" with Unresolved attribute reference 'compute' for class 'A')
Why doesn't it work out of the box when we split it to different modules (more specifically, when compute is defined in another module)?
Assume the following:
a.py
print('importing deco and A')
class deco:
def __init__(self, cls):
self.cls = cls
def __call__(self, f):
setattr(self.cls, f.__name__, f)
return self.cls
class A:
def __init__(self, val):
self.val = val
b.py
print('defining compute')
from a import A, deco
#deco(A)
def compute(a_instance):
print(a_instance.val)
main.py
from a import A
print('running main')
A(1).compute()
A(2).compute()
If we execute main.py we get the following:
importing deco and A
running main
Traceback (most recent call last):
A(1).compute()
AttributeError: 'A' object has no attribute 'compute'
Something is missing. defining compute is not outputted. Even worse, compute is never defined, let alone getting bound to A.
Why? because nothing triggered the execution of b.py. Just because it sits there does not mean it gets executed.
We can force its execution by importing it. Feels kind of abusive to me, but it works because importing a file has a side-effect: it executes every piece of code that is not guarded by if __name__ == '__main__, much like importing a module executes its __init__.py file.
main.py
from a import A
import b
print('running main')
A(1).compute()
A(2).compute()
outputs
importing deco and A
defining compute
running main
1
2
How can I execute code using a class with a "with" statement, but don't execute code until certain function is run?
For example if I have the class:
class Foo:
def __init__(self):
self.bar = "baz"
def __enter__(self):
return self
def __exit__(self, x, y, z):
return None
def run(self):
# Start running code inserted into __enter__?
And I used the class like:
bar = Foo()
with bar as f:
print(f.bar)
# Don't execute this code until run() is called
bar.run()
# Now execute the code that was inserted into the with statement above
Is this possible?
I don't think you can do that, the way your code is currently written. However, you effectively want bar.run() to be called from Foo.__enter__.
class Foo:
def __init__(self, pre=None):
self.bar = "baz"
def __enter__(self):
self.run()
return `self`
def __exit__(self, x, y, z):
return None
def run(self):
# Start running code inserted into __enter__?
bar = Foo()
with bar as f:
print(f.bar)
Nope, the code inside the with block gets executed only during the execution of the with block. It's not, for example, passed as a closure to the context manager.
If you want that behaviour, you could possibly use a decorator and a local function declaration instead of a with block.
class Foo:
def __init__(self, callback):
self.callback = callback
self.bar = 'baz'
def run(self):
# Setup goes here.
try:
self.callback(self)
finally:
# Cleanup goes here.
pass
#Foo
def bar(f):
print(f.bar)
bar.run()
That's as close to your example as I can get, names included. I think perhaps applying the constructor directly as a decorator is a bit ungainly - you might prefer to have a separate decorator function to call and return a class instance, but you get the general idea.
I am writing a class that sends slack messages to users when processes have finished. I thought it would be useful to provide a Jupyter magic so that users can be notified when the cell is executed.
The class already provides a decorator, so I figured I'd just wrap a cell execution in a decorated function.
from IPython.core.magic import register_cell_magic
from IPython import get_ipython
import functools
class MyClass(object):
def decorate(self, f):
#functools.wraps(f)
def wrapped(*args, **kwargs):
r = f(*args, **kwargs)
print('Send a message here!')
return r
return wrapped
#register_cell_magic
def magic(self, line, cell):
ip = get_ipython()
#self.decorate
def f():
return ip.run_cell(cell)
return f()
So then I'd do:
obj = MyClass()
# ----- NEW CELL
%%obj.magic
'''do some stuff'''
But I get
>> UsageError: Cell magic `%%obj.magic` not found.
I found out that the magic is registered under its name (above, magic), so %%magic works. But then the arguments are all messed up because there is no self in the mix.
I want the magic to be an instance method so that config (set in __init__ can be used). Is there any way to do this?
Here are a couple hacky solutions I don't want to implement unless I really have to:
Register a regular function with the instance as an argument. I don't want to add that line of code to the notebook, I want to use an instance method.
Register a regular function that constructs an instance on the fly.
This is the best I can come up with, and it's #1 on the list of the things I didn't want to do.
from IPython.core.magic import register_cell_magic
from IPython import get_ipython
import functools
class MyClass(object):
def decorate(self, f):
#functools.wraps(f)
def wrapped(*args, **kwargs):
r = f(*args, **kwargs)
print('Send a message here!')
return r
return wrapped
def register_magic(self):
#register_cell_magic
def magic(line, cell):
ip = get_ipython()
#self.decorate
def f():
return ip.run_cell(cell)
return f()
Then
obj = MyClass()
obj.register_magic()
# ------
%%magic
...
I want to suppress Eclipse warnings when defining decorators.
For example:
def tool_wrapper(func):
def inner(self):
cmd="test"
cmd+=func(self)
return inner
#tool_wrapper
def list_peer(self):
return "testing "
I get warning on a decorator definition:
"Method 'tool_wrapper' should have self as first parameter
I define the decorator inside a class, so this is the only way it's working properly.
Thanks
Just define your decorator outside the class and pass the instance as an argument, it will work just as fine.
def tool_wrapper(func):
def inner(inst): # inst : instance of the object
cmd="test"
cmd+=func(inst)
return cmd
return inner
class Test():
def __init__(self):
pass
#tool_wrapper
def list_peer(self):
return "testing "
if __name__ == '__main__':
t = Test()
print t.list_peer()
This script prints testtesting
I want to kill a process from another function in the class attending to the fact that it was initiated by another function. Here's an example:
import time
class foo_class:
global foo_global
def foo_start(self):
import subprocess
self.foo_global =subprocess.Popen(['a daemon service'])
def foo_stop(self):
self.foo_start.foo_global.kill()
self.foo_start.foo_global.wait()
foo_class().foo_start()
time.sleep(5)
foo_class().foo_stop()
How should I define foo_stop?
jterrace code works. If you don't want it to start when you initialize, just call Popen in a separate function and pass nothing to the init function
import subprocess
import time
class foo_class(object):
def __init__(self):
pass
def start(self):
self.foo = subprocess.Popen(['a daemon service'])
def stop(self):
self.foo.kill()
self.foo.wait() #don't know if this is necessary?
def restart(self):
self.start()
foo = foo_class()
foo.start()
time.sleep(5)
foo.stop()
I'm guessing you want something like this:
import subprocess
import time
class foo_class(object):
def __init__(self):
self.foo = None
def start(self):
self.stop()
self.foo = subprocess.Popen(['a daemon service'])
self.foo.start()
def stop(self):
if self.foo:
self.foo.kill()
self.foo.wait()
self.foo = None
foo = foo_class()
foo.start()
time.sleep(5)
foo.stop()
Some things I've changed:
Imports should generally go at the top of the file.
Classes should inherit from object.
You want to use an instance variable.
It doesn't make much sense for your class's method names to start with the class name.
You were creating a new instance of foo_class when calling its methods. Instead, you want to create a single instance and calls the methods on it.