Lets say I have a class as follows:
class MyClass:
def __init__(self):
pass
def my_func_1(self):
print("This class has been invoked from another code")
def my_func_2(self):
print("This class has been called from the command prompt")
if __name__ == "__main__":
MyClass()
Is there a way to have the class run my_func_1 if it is invoked from a code and my_func_2 if from command line? Also, by from command line I mean by the if __name__ == "__main__": part.
The context in which I wanted to use this was to have the main class' init read the access level of the command line or ask user to login to an admin user. I ended up passing a value which can be true only if the code is ran from command line.
class MyClass:
def __init__(self, from_command_line: bool = False):
if from_command_line:
my_func_2()
else:
my_func_1()
def my_func_1(self):
print("This class has been invoked from another code")
def my_func_2(self):
print("This class has been called from the command line")
if __name__ == "__main__":
MyClass(from_command_line = True)
Like this, if the execution comes from command line, the variable from_command_line will be true and in any other case it will be false. Unless someone makes a mistake. Which an exception handling can fix. If you have a better way to do this I would very much like to learn about it.
I've written some python code that needs to read a config file at /etc/myapp/config.conf . I want to write a unit test for what happens if that file isn't there, or contains bad values, the usual stuff. Lets say it looks like this...
""" myapp.py
"""
def readconf()
""" Returns string of values read from file
"""
s = ''
with open('/etc/myapp/config.conf', 'r') as f:
s = f.read()
return s
And then I have other code that parses s for its values.
Can I, through some magic Python functionality, make any calls that readconf makes to open redirect to custom locations that I set as part of my test environment?
Example would be:
main.py
def _open_file(path):
with open(path, 'r') as f:
return f.read()
def foo():
return _open_file("/sys/conf")
test.py
from unittest.mock import patch
from main import foo
def test_when_file_not_found():
with patch('main._open_file') as mopen_file:
# Setup mock to raise the error u want
mopen_file.side_effect = FileNotFoundError()
# Run actual function
result = foo()
# Assert if result is expected
assert result == "Sorry, missing file"
Instead of hard-coding the config file, you can externalize it or parameterize it. There are 2 ways to do it:
Environment variables: Use a $CONFIG environment variable that contains the location of the config file. You can run the test with an environment variable that can be set using os.environ['CONFIG'].
CLI params: Initialize the module with commandline params. For tests, you can set sys.argv and let the config property be set by that.
In order to mock just calls to open in your function, while not replacing the call with a helper function, as in Nf4r's answer, you can use a custom patch context manager:
from contextlib import contextmanager
from types import CodeType
#contextmanager
def patch_call(func, call, replacement):
fn_code = func.__code__
try:
func.__code__ = CodeType(
fn_code.co_argcount,
fn_code.co_kwonlyargcount,
fn_code.co_nlocals,
fn_code.co_stacksize,
fn_code.co_flags,
fn_code.co_code,
fn_code.co_consts,
tuple(
replacement if call == name else name
for name in fn_code.co_names
),
fn_code.co_varnames,
fn_code.co_filename,
fn_code.co_name,
fn_code.co_firstlineno,
fn_code.co_lnotab,
fn_code.co_freevars,
fn_code.co_cellvars,
)
yield
finally:
func.__code__ = fn_code
Now you can patch your function:
def patched_open(*args):
raise FileNotFoundError
with patch_call(readconf, "open", "patched_open"):
...
You can use mock to patch a module's instance of the 'open' built-in to redirect to a custom function.
""" myapp.py
"""
def readconf():
s = ''
with open('./config.conf', 'r') as f:
s = f.read()
return s
""" test_myapp.py
"""
import unittest
from unittest import mock
import myapp
def my_open(path, mode):
return open('asdf', mode)
class TestSystem(unittest.TestCase):
#mock.patch('myapp.open', my_open)
def test_config_not_found(self):
try:
result = myapp.readconf()
assert(False)
except FileNotFoundError as e:
assert(True)
if __name__ == '__main__':
unittest.main()
You could also do it with a lambda like this, if you wanted to avoid declaring another function.
#mock.patch('myapp.open', lambda path, mode: open('asdf', mode))
def test_config_not_found(self):
...
I have a simple luigi pipeline.
import luigi
import subprocess
import row_count_test
class TaskOne(luigi.Task):
def requires(self):
return None
def run(self):
output = row_count_test()
if output:
with self.output().open('w') as open_file:
open_file.write('{}'.format(output))
def output(self):
return luigi.LocalTarget('TaskOne.txt')
class TaskTwo(luigi.Task):
def requires(self):
return TaskOne()
def run(self):
subprocess.call('rm *.txt', shell = True)
if __name__ == "__main__":
luigi.run()
I run the following code through command line:
python luigi_demo.py --scheduler-host localhost TaskTwo
I want to be able to save the terminal output to a log file. I also want to be able to add a time stamp to the log file name. I know there's a way to do it through bash commands. Is there a way to do this using luigi? I looked at the luigi.cfg documentation and it wasn't too helpful. A simple example would be greatly appreciated.
You just have to changes the following to your TaskTwo.
import datetime as dt
class TaskTwo(luigi.Task):
date= luigi.DateSecondParameter(default=dt.datetime.now())
def output(self):
# Here you create a file with your date in it.
return luigi.LocalTarget('path/to/your/log/file%s.txt' % self.date)
def requires(self):
return TaskOne()
def run(self):
self.output().open('w') as f:
subprocess.call('rm *.txt', shell = True,stdout=f)
Also, on a side note if you want to delete the file created in the Taskone then you can remove all the code in the run() then just add self.input().remove()
class TaskTwo(luigi.Task):
date= luigi.DateSecondParameter(default=dt.datetime.now())
def output():
return luigi.LocalTarget('path/to/your/log/file%s.txt' % self.date)
def requires(self):
return TaskOne()
def run(self):
# this should remove the file created in the Task one.
self.input().remove()
I want to create the test case for check the same function in many files. Each file has the same function name but different algorithm.
I tried to create a loop for test each file in unit-test but it didnt't work.
__import__('name') will return a module. So here we can find a solution:
lib1.py, lib2.py, lib3.py:
def func():
return 123
test.py:
import unittest
class TestFoo(unittest.TestCase):
def test_bar(self):
for name in ['lib1','lib2','lib3']:
result=__import__(name).func()
self.assertEqual(result, 123)
if __name__ == '__main__':
unittest.main()
File 1
#file1
from tkinter import *
class UI:
def __init__(self):
self.main_window = Tk()
sam = 'TestWord'
import file2
def run(self):
self.main_window.mainloop()
def main():
ui = UI()
ui.run()
main()
File 2
#file2
from file1 import sam
print(sam)
Output
from file1 import sam
builtins.ImportError: cannot import name 'sam'
How can I import sam when it is in a class? I know how to do this without the class present. Also, this code opens two GUI's which must be part of the problem, but I'm completely lost at this point. Do both variables have to be self.sam? So, self.sam = 'TestWord' and import self.sam. I have tried this and it didn't work, however, I might be missing a detail here. Any help will be appreciated.
problem is import file2 will not run the code for you. It happens only on python REPL.
you could do something like below,
File1:
from tkinter import *
class UI:
def __init__(self):
self.main_window = Tk()
sam = 'TestWord'
from file2 import foo
foo(sam)
def run(self):
self.main_window.mainloop()
def main():
ui = UI()
ui.run()
main()
File2:
def foo(sam):
print(sam)
So your main() doesn't run when you import file1, only when you run file1 use
if __name__ == '__main__':
main()
Additionally you can't access sam inside the class, this isn't a matter of files in this case, to access it from outside the class you either need to make it global, or use self.sam = ... and have an instance of your class.
Even better is to have file2 contain methods that use the sam value which you import into your file1.
Without seeing your actual use case I can't provide any other methods of achieving what you want, but you really should not make it global to access from another file.