I've my scripts setup in the following way -
a.py (Newly added script)
from b import B
import c
class A(B):
def process(self):
super().method()
c.method1()
c.method2()
b.py (Existing script in Prod)
import c
class B(Exception):
def method(self):
c.method1()
c.method2()
c.py (Existing script in Prod)
def method1()...
def method2()...
The dir's that hold b.py & c.py are all in PATH in PROD host.
When I invoke a.py from a scheduler, i get 'module' object has no attribute method() error. Also method1() & method2() in b.py don't get executed.
a.py is in the same dir as b.py, so i'm assuming nothing need to be updated in PATH.
I searched here in SO and found this could be circular dependency issue, however few of the solutions that were suggested didn't work in my case.
Any suggestions on how i can fix this issue? Also what is the best way to resolve these issues if i were to create more scripts in the same dir as existing ones.
what do you expect super() to return? It's meant to be called within a method of a class, not directly within a module.
I'd expect to see a definition of class or function B in b.py, since it's what you import in a.py.
First off - don't use super().method() to access a parent's method. This isn't necessary. the process method of class A should be:
def process(self):
self.testmethod()
c.method1()
c.method2()
You haven't posted enough code to actually show the error.
When I redefine process as above, and instantiate an object of class A:
tester = A()
tester.process()
the process method works as expected.
Updating my previous response (which was about indenation)
So I've been trying to reproduce your problem. This is what I have:
a.py
from b import B
import c
class A(B):
def __init__(self):
super().__init__()
print('a init')
def process(self):
super().method()
c.method1()
c.method2()
a = A()
a.process()
b.py
import c
class B(Exception):
def __init__(self):
print('b init')
def method(self):
print('b start')
c.method1()
c.method2()
print('b done')
c.py
def method1():
print(1)
def method2():
print(2)
Invoking a.py from the commandline: python3.4 a.py my output is:
b init
a init
b start
1
2
b done
1
2
(which is what you would expect)
tl;dr: could not reproduce.
Related
I need to have this import specifically in the class to prevent my tests having to include the imports for each test (they can't be declared outside because of circular dependency issues with the entire codebase).
I am trying to declare my imports in a class, and access what I need with functions but not too sure how to make this work so that any test can call the functions and get what we need.
I have this at the moment:
class KTestHelper:
""" Small helper class for retrieving our hook and constants"""
from fd.fdee import (
IMOLDER,
KDER,
)
p3 = P3Hook()
#staticmethod
def get_imolder(self) -> str:
return self.IMOLDER
#staticmethod
def get_kder(self) -> str:
return self.KDER
#staticmethod
def get_p3_hook(self) -> P3Hook:
return self.p3
self obviously no longer exists as i added #staticmethod but now i'm not sure how to get it to work properly.
I need to be able to do KTestHelper.get_imolder() on every test function / some test functions that need it.
This strategy isn't enough to prevent the module import. The class will be constructed during the module load and the class attributes get evaluated. Example:
test/
__init__.py
a.py
b.py
c.py
a.py
print("loading module a")
class A:
pass
b.py
print("loading module b")
class B:
from .a import A
c.py
from test.b import B
This will output:
loading module b
loading module a
If you want this to work you'll need to put the import line in the class methods themselves.
print("loading module b")
class B:
#classmethod
def a(cls):
from .a import A
return A
I have two modules and each module contains a class. In each module I want to reference methods from the class in the other module. I have setup a small class to help wrap my head around the concept. I imported one module into the other module, but I am still getting a circular import error. To my understanding this is the proper way to do it, but I am still getting an error.
Here are my example classes:
a.py module:
import b
class A():
def __init__(self):
print("A has run")
def aa():
print("aa has run")
b.B.bb()
b.py module:
import a
class B():
def __init__(self):
print("B has run")
def bb():
print("bb has run")
# Run method from class in seperate module
a.A.aa()
Here is my error:
AttributeError: partially initialized module 'b' has no attribute 'B' (most likely due to a circular import)
If a.py is importing b.py and b.py is importing a.py, that's a circular import. Typically you want to restructure your code so that your modules do not need to import each other. Without seeing more about your actual use case, it's hard to give advice, but it could be something like this:
a.py module:
class A():
def __init__(self):
print("A has run")
def aa():
print("aa has run")
b.py module:
import a
class B():
def __init__(self):
print("B has run")
def bb():
print("bb has run")
# Run method from class in seperate module
a.A.aa()
c.py module
import b
b.B.bb()
You are right, that's due to circular import, and the reason is your import is at module level, instead if you exclude the module level import, you will not run in an issue, for example:
Modify your code like this:
class A():
def __init__(self):
print("A has run")
def aa():
print("aa has run")
if __name__ == '__main__':
import b
b.B.bb()
OUTPUT
bb has run
aa has run
Now, you'll no longer get the AttributeError because now the module b is not being imported at module level in module a.
Bit of a hack but the easiest (if hacky) way to get out of this is
class B():
def __init__(self):
print("B has run")
def bb():
print("bb has run")
import a # šimport on demand (each time)
# Run method from class in seperate module
a.A.aa()
A slightly better approach is to remember your import of a:
import b
class A():
def __init__(self):
print("A has run")
def aa():
print("aa has run")
print("running A")
b.B.bb()
b.B.bb()
class B():
def __init__(self):
print("B has run")
def bb():
print("bb has run")
#remember your import of a
a = getattr(B, "a", None)
if not a:
import a
B.a = a
# Run method from class in seperate module
a.A.aa()
You'll notice that "running A" only happens twice: once when you python a.py and then the first time b.B.bb() gets called and imports on demand.
I am wondering if there is a way to do what I am trying, best explained with an example:
Contents of a.py:
class A(object):
def run(self):
print('Original')
class Runner(object):
def run(self):
a = A()
a.run()
Contents of b.py:
import a
class A(a.A):
def run(self):
# Do something project-specific
print('new class')
class Runner(a.Runner):
def other_fcn_to_do_things(self):
pass
Basically, I have a file with some base classes that I would like to use for a few different projects. What I would like would be for b.Runner.run() to use the class A in b.py, without needing to override the run method. In the example above, I would like to code
import b
r = b.Runner()
print(r.run())
to print "new class". Is there any way to do that?
This seems a little convoluted. The Runner classes are probably unnecessary, unless there's something else more complex going on that was left out of your example. If you're set on not overriding the original run(), you could call it in another method in B. Please take a look at this post and this post on super().
It would probably make more sense to do something like this:
a.py:
class A(object):
def run(self):
# stuff
print ('Original')
b.py:
import a
class B(A):
def run(self):
return super(A, self).run()
# can also do: return A.run()
def run_more(self):
super(A, self).run()
# other stuff
print('new class')
Let's say I have this situation:
module2.py
class Bar:
def bar():
a = 5
# do stuff
Messages.show("Done")
module1.py
import module2
class Foo:
def __init__(self):
self.bar = module2.Bar()
def foo(self):
self.bar.bar()
I want to test the method Foo.foo(), but I want to ignore Messages.show("Done), ie I want calls to the Messages.show function to be done on a mock object. If foo was calling Messages.show directly, I could use monkeypatch on foo to mock the Messages class. But now, I'm calling a class from another module and I don't know how to specify that Messages.show calls should not be done ( the reason being that they access the Gui and that doesn't work in a test environment). Let's assume I cannot modify module2.py.
Just override what module2 thinks Messages is:
import module2
module2.Messages = ...
Classes B and C both derive from base class A, and neither override A's method test(). B is defined in the same module as A; C is defined in a separate module. How is it that calling B.test() prints "hello", but calling C.test() fails? Shouldn't either invocation end up executing A.test() and therefore be able to resolve the symbol "message" in mod1's namespace?
I'd also gratefully receive hints on where this behaviour is documented as I've been unable to turn up anything. How are names resolved when C.test() is called, and can "message" be injected into one of the namespaces somehow?
FWIW, the reason I haven't used an instance variable (e.g. set A.message = "hello") is because I'm wanting to access a "global" singleton object and don't want to have an explicit referent to it in every other object.
mod1.py:
import mod2
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
message = "hello"
A().test()
B().test()
mod2.C().test()
mod2.py:
import mod1
class C(mod1.A):
pass
output is:
$ python mod1.py
hello
hello
Traceback (most recent call last):
File "mod1.py", line 14, in <module>
mod2.C().test()
File "mod1.py", line 5, in test
print message
NameError: global name 'message' is not defined
Many thanks!
EOL is correct, moving the "main" part of the program into a new file mod3.py does indeed make things work.
http://bytebaker.com/2008/07/30/python-namespaces/ further clarifies the issue.
In my original question, it turns out that the variable message ist stored in the __main__ module namespace because mod1.py is being run as a script. mod2 imports mod1, but it gets a separate mod1 namespace, where the variable message does not exist. The following code snippet demonstrates more clearly as it writes message into mod1's namespace (not that I'd recommend this be done in real life), causing the expected behaviour.
import sys
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
import mod2
message = "hello"
sys.modules["mod1"].message = message
A().test()
B().test()
mod2.C().test()
I think the best real-world fix is to move the "main" part of the program into a separate module, as EOL implies, or do:
class A(object):
def test(self):
print message
class B(A):
pass
def main():
global message
message = "hello"
A().test()
B().test()
# resolve circular import by importing in local scope
import mod2
mod2.C().test()
if __name__ == "__main__":
# break into mod1 namespace from __main__ namespace
import mod1
mod1.main()
Could you use a class attribute instead of a global? The following works
import mod2
class A(object):
message = "Hello" # Class attribute (not duplicated in instances)
def test(self):
print self.message # Class A attribute can be overridden by subclasses
class B(A):
pass
if __name__ == "__main__":
A().test()
B().test()
mod2.C().test()
Not using globals is cleaner: in the code above, message is explicitly attached to the class it is used in.
That said, I am also very curious as to why the global message is not found by mod2.C().test().
Things work as expected, though, if the cross-importing is removed (no main program in mod1.py, and no import mod2): importing mod1 and mod2 from mod3.py, doing mod1.message = "Hello" there and mod2.C().test() works. I am therefore wondering if the problem is not related to cross-importingā¦