Python Class Inheritance with a moving import error - python

My first question on stack! :D
I have a problem when I try to have my cake and eat it too, it seems.
I have stripped out all the code as I do not believe that it is part of the problem.
I have the following dir structure:
master/
- main.py
- modules/
- parent_class.py
- child_class.py
- __init__.py
In parent_class.py:
class Parent:
pass
In child_class.py:
from modules.parent_class import Parent
class Child(Parent)
pass
if __name__ == "__main__":
child = Child()
child.do_stuff()
In main.py:
from modules.child_class import Child
child = Child()
child.do_stuff()
The problem I am having I believe it has to do with me not understanding sys.path properly.
When I run main.py there are no errors.
However, when I try to run child_class.py for testing purposes I get the following error...
Traceback (most recent call last):
File "child_class.py", line 1, in <module>
from modules.parent_class import Parent
ModuleNotFoundError: No module named 'modules'
The error goes away when I change child_class.py to this:
from parent_class import Parent
class Child(Parent)
pass
if __name__ == "__main__":
child = Child()
child.do_stuff()
But now when I run main.py I get this error:
Traceback (most recent call last):
File "c.../main.py", line 1, in <module>
from modules.child_class import Child
File "...\child_class.py", line 1, in <module>
from parent_class import Parent
ModuleNotFoundError: No module named 'parent_class'
How do you do a unit test if you have to change the import line every time?
Thank you in advance for a good explaination. (I have read a lot of docs on importing, and packages and modules, watched like 10 different vids on this topic but still not sure why or how to make this just work.) (I am just saying I have tried to find the answer, but I am now exhausted and need a solution before I really go mad!) thank you, thank you, thank you

TLDR:
Run the python file with the -m flag.
python -m modules.child_class
This issue is a result of misunderstanding the difference between Python Script programs and Python Package programs.
If you are running a Python program as a script (running a Python file directly), then you do direct imports like you have already:
from parent_class import Parent
However, if you are building a Python Package that is designed for importing into other programs (eg. a Library or Framework) then you need to use relative imports. (Sounds like the Unittest is running the program as a package but when you run the program, you are running it as a script.)
from .parent_class import Parent
# or
from modules.parent_class import Parent
If you are making a package then run the program with the -m flag or import into another program (a script or another package)
python -m main
or
python -m modules.child_class

Until someone shows me a better way, I will do the following...
if __name__ == "__main__":
from parent_class import Parent
else:
from modules.parent_class import Parent

Related

How to get Python relative imports right

I have a custom Python library ("common") that is being imported and used from several Python projects.
That central library has the following structure:
/common
/__init__.py
/wrapper.py
/util
/__init__.py
/misc.py
Our custom library resides in a central place /data/Development/Python, so in my Python projects I have an .env file in order to include our lib:
PYTHONPATH="/data/Development/Python"
That works fine, I can for example do something like:
from common.util import misc
However, now I want to make use of a class MyClass within common/wrapper.py from the code in common/util/misc.py. Thus I tried the following import in misc.py:
from ..wrapper import MyClass
But that leads to the following error:
Exception has occurred: ImportError
cannot import name 'MyClass'
Any ideas what I am doing wrong here?
PS: When I do an from .. import wrapper instead and then from code I use wrapper.MyClass, then it works fine. Does that make any sense?
It's finding wrapper, otherwise you'd get a different error:
>>> from wibble import myclass
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'wibble'
So it seems wrapper does not contain MyClass. Typo?
"However, now I want to make use of a class MyClass within common/wrapper.py from the code in common/util/misc.py. Thus I tried the following import in misc.py:"
If you do export PYTHONPATH=~/common
In order to import MyClass you will do:
from wrapper import MyClass
Because you have added common to your root folder, utils is already in your path since it is in you root folder.
Similarly, if you wanted to import from wrapper you would do from utils.misc import ThisClass since utils.misc is already in the root folder common

Importing functions between two Python scripts simultaneously

I am trying to run a chat bot of sorts that is capable of creating new commands whilst the program is running. To do this I am keeping all the commands in a second python script and using the main script to edit the commands.py file whilst the chat bot is still running.
The issue...
I am able to have both scripts access each other using import main and then main.functionName() to call the function. However, when I try to call a function in commands.py from main.py, then use the function called to call another function back in main.py I get an error saying
AttributeError: module 'main' has no attribute 'exampleFunction'
For example the following code;
TESTING.py
import TESTING2
def runme(inp):
print(inp)
startOver()
print("begin")
TESTING2.startOver()
TESTING2.py
import TESTING
def startOver():
userInput = input("Enter text at your own risk... ")
TESTING.runme(userInput)
Produces the following;
begin
Traceback (most recent call last):
File "C:\Users\harry\Desktop\TESTING.py", line 1, in <module>
import TESTING2
File "C:\Users\harry\Desktop\TESTING2.py", line 1, in <module>
import TESTING
File "C:\Users\harry\Desktop\TESTING.py", line 8, in <module>
TESTING2.startOver()
AttributeError: module 'TESTING2' has no attribute 'startOver'
The desired outcome would be a continuous loop of entering an input and then the text being printed as if one seamless script.
Is this possible? If so how do I do it - or is there a better way to achieve the same goal?
Many thanks.
So, I'll have a go at giving you something that might solve your problem. Essentially what you are doing is constructing a circular dependency: commands.py is written by main.py, main.py depends on commands.py for its functions. There is almost certainly a way to solve your problem without introducing such a circular dependency, but I would need to know more in order to suggest something.
If you are sure you want to do it like this, you could use importlib.reload, which tells python to reload a module that you've already imported. In other words, if you've added a new function to commands.py since calling the original import, calling reload will now make this function available.
As a small example, try setting up commands.py and main.py scripts as follows:
#commands.py
def func1():
print(1)
and:
#main.py
import commands
commands.func1()
input("hit enter once you've edited commands.py")
from importlib import reload
commands = reload(commands)
commands.func2()
run main.py and when you get to the input part, open up commands.py and change it to look like this:
#commands.py
def func1():
print(1)
def func2():
print(2)
Now hit "enter" in the running main.py script. You should see the result of func2printed to the terminal.
Note however also that reload doesn't necessarily act the way you would expect and could cause some strange and explainable things to happen. For more info, see this post: https://stackoverflow.com/a/438845/141789

Having trouble with imports in Python

I use Python 2.7. I'm trying to run my UI-automation script, but I got ImportError.
I have at least 30 Classes with methods. I want to have these methods in each and any class that's why I created BaseClass(MainClass) and created objects of all my classes. Please advise what should I do in this case or how I can solve this problem.
Here the example what similar to my code.
test_class/baseclass.py
from test_class.first_class import FirstClass
from test_class.second_class import SecondClass
class MainClass:
def __init__(self):
self.firstclass = FirstClass()
self.secondclass = SecondClass()
test_class/first_class.py
from test_class.baseclass import MainClass
class FirstClass(MainClass):
def __init__(self):
MainClass.__init__(self)
def add_two_number(self):
return 2 + 2
test_class/second_class.py
from test_class.baseclass import MainClass
class SecondClass(MainClass):
def __init__(self):
MainClass.__init__(self)
def minus_number(self):
return self.firstclass.add_two_number() - 10
if __name__ == '__main__':
print(SecondClass().minus_number())
When I run the last file I get this error
Traceback (most recent call last):
File "/Users/nik-edcast/git/ui-automation/test_class/second_class.py", line 1, in <module>
from test_class.baseclass import MainClass
File "/Users/nik-edcast/git/ui-automation/test_class/baseclass.py", line 1, in <module>
from test_class.first_class import FirstClass
File "/Users/nik-edcast/git/ui-automation/test_class/first_class.py", line 1, in <module>
from test_class.baseclass import MainClass
ImportError: cannot import name MainClass
check this line: from test_class.baseclass import MainClass -> it seems like all other imports had a '_' between the names like second_class. So try maybe to write base_class. who knows might work
Are you proabably running your code like python test_class/second_class.py. If you just do this then python will think the base directory to find modules is ./test_class. So when you import the test_class package python will start looking for a folder called ./test_class/test_class to find the sub-modules. This directory doesn't exist and so the import fails. There are several ways that you can tell python how to correctly find your modules.
Using PYTHONPATH
One way to get around this is to set PYTHONPATH before starting python. This is just an environment variable with which you can tell python where to look for your modules.
eg.
export PYTHONPATH=/path/to/your/root/folder
python test_class/second_class.py
Using the -m switch for python
Be default python treats the directory of the main module as the place to look for other modules. However, if you use -m python will fall back to looking in the current directory. But you also need to specify the full name of the module you want to run (rather than as a file). eg.
python -m test_class.second_class
Writing a root entry point
In this we just define your main module at the base level (the directory that contains test_class. Python will treat this folder as the place to look for user modules and will find everything appropriately. eg.
main.py (in /path/to/your/root/folder)
from test_class.second_class import SecondClass
if __name__ == '__main__':
print(SecondClass().minus_number())
at the command line
python main.py
You could try importing the full files instead of using from file import class. Then you would only need to add the file name before referencing something from another file.

Run python script from another python script on linux

I'm using python 2.7 and ubuntu 16.04.
I have a simple REST server on python: file server_run.py in module1 which is importing some scripts from module2.
Now I'm writing an integration test, which is sending POST request to my server and verify that necessary action was taken by my server. Obviously, server should be up and running but I don't want to do it manually, I want to start my server (server_run.py which has also main method) from my test: server_run_test.py file in module3.
So, the task sounds very simple: I need to start one python script from another one, but I spent almost the whole day. I found a couple of solutions here, including:
script_path = "[PATH_TO_MODULE1]/server_run.py"
subprocess.Popen(['python', script_path], cwd=os.path.dirname(script_path))
But my server is not coming up, throwing the error:
Traceback (most recent call last):
File "[PATH_TO_MODULE1]/server_run.py", line 1, in <module>
from configuration.constants import *
File "[PATH_TO_MODULE2]/constants.py", line 1, in <module>
from config import *
ModuleNotFoundError: No module named 'config'
So, it looks like when I'm trying to start my server in subprocess it doesn't see imports anymore.
Do you guys have any idea how can I fix it?
Eventually, the solution was found, 2 steps were taken:
1. In each module I had an empty __init__.py file, it was changed to:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
__version__ = '${version}'
2. Instead of using the following syntax:
from configuration.constants import *
from configuration.config import *
config and constants were imported as:
from configuration import constants,config
and then we are using reference to them when need to get some constant.
Thanks everyone for looking into it.
Rather than running it using os module try using the import function. You will need to save in the same dictionary or a sub folder or the python installation but this seems to be the way to do it. Like this post suggests.

Python Packages?

Ok, I think whatever I'm doing wrong, it's probably blindingly obvious, but I can't figure it out. I've read and re-read the tutorial section on packages and the only thing I can figure is that this won't work because I'm executing it directly. Here's the directory setup:
eulerproject/
__init__.py
euler1.py
euler2.py
...
eulern.py
tests/
__init__.py
testeulern.py
Here are the contents of testeuler12.py (the first test module I've written):
import unittest
from .. import euler12
class Euler12UnitTests(unittest.TestCase):
def testtriangle(self):
"""
Ensure that the triangle number generator returns the first 10
triangle numbers.
"""
self.seq = [1,3,6,10,15,21,28,36,45,55]
self.generator = euler12.trianglegenerator()
self.results = []
while len(self.results) != 10:
self.results.append(self.generator.next())
self.assertEqual(self.seq, self.results)
def testdivisors(self):
"""
Ensure that the divisors function can properly factor the number 28.
"""
self.number = 28
self.answer = [1,2,4,7,14,28]
self.assertEqual(self.answer, euler12.divisors(self.number))
if __name__ == '__main__':
unittest.main()
Now, when I execute this from IDLE and from the command line while in the directory, I get the following error:
Traceback (most recent call last):
File "C:\Documents and Settings\jbennet\My Documents\Python\eulerproject\tests\testeuler12.py", line 2, in <module>
from .. import euler12
ValueError: Attempted relative import in non-package
I think the problem is that since I'm running it directly, I can't do relative imports (because __name__ changes, and my vague understanding of the packages description is that __name__ is part of how it tells what package it's in), but in that case what do you guys suggest for how to import the 'production' code stored 1 level up from the test code?
I had the same problem. I now use nose to run my tests, and relative imports are correctly handled.
Yeah, this whole relative import thing is confusing.
Generally you would have a directory, the name of which is your package name, somewhere on your PYTHONPATH. For example:
eulerproject/
euler/
__init__.py
euler1.py
...
tests/
...
setup.py
Then, you can either install this systemwide, or make sure to set PYTHONPATH=/path/to/eulerproject/:$PYTHONPATH when invoking your script.
An absolute import like this will then work:
from euler import euler1
Edit:
According to the Python docs, "modules intended for use as the main module of a Python application should always use absolute imports." (Cite)
So a test harness like nose, mentioned by the other answer, works because it imports packages rather than running them from the command line.
If you want to do things by hand, your runnable script needs to be outside the package hierarchy, like this:
eulerproject/
runtests.py
euler/
__init__.py
euler1.py
...
tests/
__init__.py
testeulern.py
Now, runtests.py can do from euler.tests.testeulern import TestCase and testeulern.py can do from .. import euler1

Categories