Is there a simple way to check in python if a class is defined? Even if you know the location of the module file .py where the class should be defined.
Let's say I have these files, file1.py where I try to check if Class1 is defined in file2.py. And file3.py where I have a second class Class2 defined.
in file1.py, I have this code:
try:
modulePath = os.sep.join([cwd,os.sep.join(factory.split(".")[0:-1])]) + ".py"
moduleName = factory.split(".")[-2]
className = factory.split(".")[-1]
m = imp.load_source(moduleName, modulePath)
c = getattr(m, className)
except:
raise ValueError('Factory Path not correctly specified')
where
factory = <string as path to the class 1> # for example com.Class1
cwd = os.getcwd() # i.e. current working directory
in file2.py
```
from . import Class2
Class1(object):
def __init__(self):
self.object2 = Class2()
in file3.py
```
Class2(object):
def __init__(self):
pass
Basically, as the modules file2.py and file3.py are not installed, the code in file1 raise an error as imp can't find the Class2. I tried with and without relative import, without success...
If you know where the class resides and assuming the module containing the class is in the python path, then you can just wrap an import of that class in a try block
try:
import MyClass
#or
from my_module import MyClass
except ImportError:
#raise an exception or log a warning of some sort
Related
In Python I have 3 files in the same folder: file1.py, file2.py and test.py
file1.py contains a simple class definition
class MyClass:
def __init__(self, a, b):
self.a = a
self.b = b
print(f"a={a} and b={b}")
file2.py instantiate previous class
from file1 import MyClass
def instantiate_class():
o = MyClass(1,2)
In test.py I'm trying to mock the file1.MyClass from instantiate_class() function
from unittest.mock import patch
from file2 import instantiate_class
with patch("file1.MyClass") as mock_check:
instantiate_class()
print(mock_check.call_args)
Problem is that class MyClass from file2.py is not mocked as the print from the MyClass constructor is called.
Only solution I found to solve this is to change file2.py with:
import file1
def instantiate_class():
o = file1.MyClass(1,2)
Is there a solution to obtain mocking that does not involve changing file1.py and file2.py? I only want to change the test.py file (writing unit tests for existing classes).
I need to mock the file2.mock instead of file1.mock. Like below:
from unittest.mock import patch
from file2 import instantiate_class
with patch("file2.MyClass") as mock_check:
instantiate_class()
print(mock_check.call_args)
reference
where-to-patch
I have the following bizarre set up. Consider 3 scripts in different directories:
root1/folderA/scriptA.py
root2/folderB/scriptB.py
root2/folderC/scriptC.py
The first file and it's location are fully modifiable. The second and third are completely fixed.
scriptA.py contains a parent class:
class A:
def get_path(self):
# ...
# code to determine "my_path"
# ...
return my_path
scriptB.py contains a child class:
from root1.folderA.scriptA import A
class B(A):
pass
scriptC.py contains the code to execute:
from root2.folderB.scriptB import B
if __name__ == "__main__":
b = B()
print(b.get_path()) # want print root/folderB/scriptB.py
In scriptC.py what I want is the behaviour to get the path of the child class's declaration file (without any hard-coding). Is there any way to program the A.get_path() to have this behavoir?
I've tried looking into the inspect, os.path and pathlib modules but I haven't had any luck.
It looks like the trick is to use __init_subclass__ which runs whenever a class is sub-classed, in conjunction with a class's __module__ attribute to retrieve its containing module, and __file__ to retrieve the absolute path to the python script or module.
For example, in script_a.py:
import sys
from pathlib import Path
class PathInfo:
__slots__ = ()
path: Path
def __init_subclass__(cls, **kwargs):
mod = sys.modules[cls.__module__]
# `__file__` is a string, so we want to convert to a `Path` so it's easier to work with
cls.path = Path(mod.__file__)
class A(PathInfo):
__slots__ = ()
script_b.py:
from script_a import A
class B(A):
pass
# prints: /Users/<name>/PycharmProjects/my-project/script_a.py
print(A().path)
# prints: /Users/<name>/PycharmProjects/my-project/script_b.py
print(B.path)
How to find class parent and subclasses across different modules without running the code (static analysis)
Module Contains __init__.py and 4 files as below
Example first_file.py
class Parent(object):
def __init__(self):
pass
def method1(self):
print('parent')
Example second_file.py
from first_file import Parent
class Child(Parent):
def __init__(self):
pass
def method1(self):
print('child')
Example third_file.py
from first_file import Parent
class Child1(Parent):
def __init__(self):
pass
def method1(self):
print('child1')
Example fourth_file.py
from second_file import Child
class Child2(Child):
def __init__(self):
pass
def method1(self):
print('child2')
I want the way to list down the subclasses of parent given filename and class
example
>>> findclasshierchay first_file.py --class Parent
and it will list down all subclasses with filenames
Here's a stab at using cls.mro().
Disclaimer, I don't know if you consider this to be running the code or not. It's not static analysis, but if you have no great side effects upon module load, it really doesn't do that much either.
file1.py
class File1:
pass
file2.py
from file1 import File1
class File2(File1):
pass
listhier.py
import sys
import copy
import os
from importlib import import_module
from importlib.util import find_spec as importlib_find
#shamelessly copied from django 👇
def import_string(dotted_path):
"""
Import a dotted module path and return the attribute/class designated by the
last name in the path. Raise ImportError if the import failed.
"""
try:
module_path, class_name = dotted_path.rsplit('.', 1)
except ValueError as err:
raise ImportError("%s doesn't look like a module path" % dotted_path) from err
module = import_module(module_path)
try:
return getattr(module, class_name)
except AttributeError as err:
raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
module_path, class_name)
) from err
toload = sys.argv[1]
cls = import_string(toload)
for cls_ in cls.mro():
print("%s.%s" % (cls_.__module__, cls_.__name__))
python listhier.py file2.File2
output:
file2.File2
file1.File1
builtins.object
Here is is the directory structure:
->src/dir/classes.py
->src/run.py
# classes.py
class A():
def methA():
# class A
class B():
def MethB():
# class B
class C():
def methC():
# class C
then i need to import Class A in run.py file.
from dir.classes import A
A.methA()
i already tried with using from dir.classes import A but it gives me
ModuleNotFoundError: No module named 'dir.classes'; 'classes' is not a package error
So how can i do that?
You need to put__init__.py file on your dir folder.
This way dir will be recognized as python package.
First, you must have __init__.py in each directory for Python to recognize them as packages.
Then, you should use from dir.classes import A. A is the name of the class, you shouldn't use Class A
I have 3 files a.py, b.py, c.py
I am trying to dynamically import a class called "C" defined in c.py from within a.py
and have the evaluated name available in b.py
python a.py is currently catching the NameError. I'm trying to avoid this and create an
instance in b.py which calls C.do_int(10)
a.py
import b
#older
#services = __import__('services')
#interface = eval('services.MyRestInterface')
# python2.7
import importlib
module = importlib.import_module('c')
interface = eval('module.C')
# will work
i = interface()
print i.do_int(10)
# interface isn't defined in b.py after call to eval
try:
print b.call_eval('interface')
except NameError:
print "b.call_eval('interface'): interface is not defined in b.py"
b.py
def call_eval(name):
interface = eval(name)
i = interface()
return i.do_int(10)
c.py
class C(object):
my_int = 32
def do_int(self, number):
self.my_int += number
return self.my_int
How can I achieve this?
interface only exists in a's namespace. You can put a reference to the interface into b's namespace like this
b.interface = interface
try:
print b.call_eval('interface')
except NameError:
print "b.call_eval('interface'): interface is not defined in b.py"
I'm not sure why you're not just passing the interface to call_eval though
I'm sure there should be a better solution by totally avoiding this.
But this could do the trick:
a.py:
shared_variables = {}
import b
import c
shared_variables['C'] = c.C
b.do_something_with('C')
b.py:
from __main__ import shared_variables
def do_something_with(name):
print(shared_variables[name])
If a.py already loads the class, I fail to see the reason to pass it by name. Instead, do
# b.py
def call_eval(klass):
j = klass()
return i.do_int(10)
and, in a.py, do
import importlib
module = importlib.import_module('c')
interface = getattr(module, 'C')
b.call_eval(interface)