How to get Class diagram from Python source code? - python

I try to get a class diagram from Python source code in Client folder with pyreverse but it requires __init__.py
(venv) C:\Users\User\Desktop\project> pyreverse Client
parsing Client\__init__.py...
Failed to import module Client\__init__.py with error:
No module named Client\__init__.py.
I don't find any solution for this. Is there a way to get the diagram?
Update:
There are many files in Client folder:
Client.py
GUI.py
script.py
...
This is a part of the Client.py code:
import threading
class Client:
def __init__(self):
self.socket = None
self.listen_socket = None
self.buff_dict = {}
self.message_list_dict = {}
self.lock = threading.Lock()
self.target = None
self.listen_flag = True
This is a part of the GUI.py code:
import tkinter as tk
class Window(object):
def __init__(self, title, font, client):
self.title = title
self.font = font
self.client = client
self.root = tk.Tk()
self.root.title(title)
self.build_window()
def build_window(self):
pass
class LoginWindow(Window):
def __init__(self, client, font):
super(LoginWindow, self).__init__('Login', font, client)
self.build_window()

Thanks to #Anwarvic and #bruno, I came up with the solution for this.
Firstly, create empty __init__.py file inside Client folder:
(venv) C:\Users\User\Desktop\project\Client> type NUL > __init__.py
Then go to the parent folder of the Client folder where I want to get the class diagram:
(venv) C:\Users\User\Desktop\project> pyreverse Client -o png
But I got this error:
The output format 'png' is currently not available.
Please install 'Graphviz' to have other output formats than 'dot' or 'vcg'.
After some findings, I found this solution. Then I can run the pyreverse without any error.
This is the class diagram I got using pyreverse:

It seems like you don't have an __init__.py in the folder that contains Client.py. You should be able to just create the file without putting anything in it, as its main purpose is to indicate that the folder is a package.
See this SO question about __init__.py for a more in-depth explanation about the file.

The most convenient approach should be to have a Jupyter notebook extension that dynamically generates diagrams of the classes defined in a notebook somehow like the variable inspector - possibly with the option to specify an alternative root like datetime in the first answer to
creating UML charts by Pylint/pyreverse within Jupyter labs / console.
Note: This would remove the restriction of pyreverse requiring the displayed classes to be part of a module.

Related

How to build a good registration mechanism in python?

I want to build a well-modularized python project, where all alternative modules should be registed and acessed via a function named xxx_builder.
Taking data class as an example:
register.py:
def register(key, module, module_dict):
"""Register and maintain the data classes
"""
if key in module_dict:
logger.warning(
'Key {} is already pre-defined, overwritten.'.format(key))
module_dict[key] = module
data_dict = {}
def register_data(key, module):
register(key, module, data_dict)
data.py:
from register import register_data
import ABCDEF
class MyData:
"""An alternative data class
"""
pass
def call_my_data(data_type):
if data_type == 'mydata'
return MyData
register_data('mydata', call_my_data)
builder.py:
import register
def get_data(type):
"""Obtain the corresponding data class
"""
for func in register.data_dict.values():
data = func(type)
if data is not None:
return data
main.py:
from data import MyData
from builder import get_data
if __name__ == '__main__':
data_type = 'mydata'
data = get_data(type=data_type)
My problem
In main.py, to register MyData class into register.data_dict before calling the function get_data, I need to import data.py in advance to execute register_data('mydata', call_my_data).
It's okay when the project is small, and all the data-related classes are placed according to some rules (e.g. all data-related class should be placed under the directory data) so that I can import them in advance.
However, this registeration mechanism means that all data-related classes will be imported, and I need to install all packages even if I won't use it actually. For example, when the indicator data_type in main.py is not mydata I still need to install ABCDEF package for the class MyData.
So is there any good idea to avoid importing all the packages?
Python's packaging tools come with a solution for this: entry points. There's even a tutorial about how to use entry points for plugins (which seems like what you're doing) (in conjunction with this Setuptools tutorial).
IOW, something like this (nb. untested), if you have a plugin package that has defined
[options.entry_points]
myapp.data_class =
someplugindata = my_plugin.data_classes:SomePluginData
in setup.cfg (or pypackage.toml or setup.py, with their respective syntaxes), you could register all of these plugin classes (here shown with an example with a locally registered class too).
from importlib.metadata import entry_points
data_class_registry = {}
def register(key):
def decorator(func):
data_class_registry[key] = func
return func
return decorator
#register("mydata")
class MyData:
...
def register_from_entrypoints():
for entrypoint in entry_points(group="myapp.data_class"):
register(entrypoint.name)(entrypoint.load())
def get_constructor(type):
return data_class_registry[type]
def main():
register_from_entrypoints()
get_constructor("mydata")(...)
get_constructor("someplugindata")(...)

'AttributeError: module 'usbiss' has no attribute Aclass' when creating class instance

Trying to write a python package and I cant create an instance of a class in one of my source files.
package layout is:
-packagedir
----README.md
----setup.py
----packagename
--------__init__.py
--------package.py
--------modules
------------file1.py
------------file2.py
in init.py within packagename i have:
from . modules import file1
from . modules import file2
The file file1.py contains a class:
class File1():
def __init__(self):
self.val = 0
# Other methods and such
The file file2.py contains a class:
class File2():
def __init__(self):
self.type = 0
# Other methods and such
and in package.py I have a class as thus:
class Aclass(file1.File1, file2.File2):
def __init__(self):
# nothing important in here yet
I have build and installed my package like this:
python3 setup.py sdist
sudo pip3 install dist/package-0.1.tar.gz
Now I create a file called test.py and put in it the following:
import package
iss = package.Aclass()
when I run the test file i get the following error:
AttributeError: module 'usbiss' has no attribute 'Aclass'
I do not understand why it is that python is not letting me create an instance of class Aclass and thinks I am accessing an attribute. I am sure there is something fundamentally wrong with my import statements or something but i am at a loss as to what it is. How do I correct this so that I can create an instance of Aclass and use its methods?
Thanks.
The problem here was that I was importing the package itself but not a module within that package. I changed my import in test.py to:
from package import package
and this fixed my issue.
Are you sure you are handling your import properly and not introducing any circular dependencies?
Also:
def __init__(file1.File1, file2.File2):
def __init__():
Your init methods are lacking self. They should be:
def __init__(self, file1.File1, file2.File2):
def __init__(self):

How do I create a module/class that calls all single-classed modules in a directory?

I'm pretty new to the python scene, but not to programming. I'm looking to create a class that calls all modules in a directory (each module will only have a single class) and can't quite yet wrap my head around class-to-class implementation. To roughly illustrate, I have the following directory structure
-__init__.py
-addon.py
-lib/
-__init__.py
-sources/
-__init__.py
-source1scraper.py
-source2scraper.py
-source3scraper.py
I want to have a class I can call that will run each result (lists)
A rough idea of the Sources class would go like so
import threading
class Sources():
def __init__(self):
self.Sources = []
def getSources(self):
sourceDict = []
for package, name, is_pkg in pkgutil.walk_packages(__path__): sourceDict.append((name, is_pkg))
sourceDict = [i[0] for i in sourceDict if i[1] == False]
threads = []
for source in sourceDict:
t = threading.Thread(target=self.getLinks(source))
threads.append(t)
t.start()
def getLinks(self, source):
self.Links = []
Problem 1 here is I don't know where I'd place this class. Maybe it would go in the __init__.py of the sources directory?
Problem 2 here is I'm unsure how to call each source, get back the links, and append to the existing list of links from the other sources that have already been called.
Problem 3 here is I don't know how to correctly thread the processes, so the scrapers run concurrently.
Then comes the scraper.py files. Here's a rough idea of the scraper.py files' structure
import requests
import re
class Source(Sources.Source):
def __init__(self):
self.full_link = 'http://www.blahblah.com/path/to/content'
self.base_link = 'http://www.blahblah.com' #for appending symbolic links
self.regex = '<a href="([^"]+)" rel="nofollow"'
def links(self):
try:
links = []
source_html = client.request(self.full_link)
match = re.compile(self.regex).findall(source_html)
return links
except:
return links
Problem 4 is I don't know what needs to be specified between the class' parenthesis where I currently have class Source(Sources.Source):.
Any advice on how to correctly implement what I'm looking to do would be greatly appreciated. Am I even close?

Imports break moving from python 3.4 to 3.5

I have a module that works fine in python 3.5+ but not in 3.4. The only possible change that may be effecting it is how circular imports are handled in 3.5+. I cannot find any circular imports though so there may be something else going on.
module/
module/
__init__.py
file_a.py
from module import settings
from module.file_b import SomeBClass
def stuff():
settings.init()
stuff = SomeBClass()
def run():
stuff()
def main():
run()
file_b.py
from module.settings import config, properties
class SomeBClass():
....
file_c.py
class SomeClass():
connect to db...
settings.py
from module.file_c import SomeClass
def init():
global config
global properties
config = SomeClass()
properties = config.get_it()
when running I get the following error:
File "/home/somewhere/module/module/file_b.py", line 11, in <module>
from module.settings import config, properties
ImportError: cannot import name 'config'
I have tried running the module with python -mv to see if something gets imported more than once but I cannot see anything alarming.
Anyone have experience dealing with the differences between 3.4 and 3.5+? Does trying to access attributes from globals in the settings.init cause issues?

Dynamic loading of classes

My goal is to load dynamically my different subclasses and execute them. To call my script, I am using this:
python Plugin.py Nmap execute google.com
Or
python Plugin.py Dig execute google.com
Here it's the code
Parent Class: Plugin.py
class Plugin(object)
def __init__(self):
self.sName = ''
def execPlugin(self):
return 'something'
def main():
# get the name of the plugin
sPlugin = sys.argv[1]
# create the object
mMod = __import__(sPlugin, fromlist=[sPlugin])
mInstance = getattr(mMod, sPlugin)
oPlugin = mInstance()
print oPlugin
print type(oPlugin)
if (sys.argv[2] == 'execute'):
# then execute it
return oPlugin.execPlugin(sys.argv[3])
if __name__ == '__main__':
main()
Sub Class located in Nmap/Nmap.py
class Nmap(Plugin):
def __init__(self):
self.sName = 'Nmap'
def execPlugin(self):
return 'something else'
Sub Class located in Dig/Dig.py
class Dig(Plugin):
def __init__(self):
self.sName = 'Dig'
def execPlugin(self):
return 'yeahhh'
My problem is located in
oPlugin = mInstance()
With the following error
TypeError: 'module' object is not callable
I tried so many things but nothing worked. How can I solve my problem?
You have a structure like:
Plugin.py
/Nmap
__init__.py
Nmap.py
# class Nmap(Plugin): ...
In Plugin.py, when you do mMod = __import__(sPlugin, fromlist=[sPlugin]) where sPlugin == 'Nmap', this makes mMod refer to the directory /Nmap, not the file Nmap.py (note that both files and directories can be modules in Python). Hence mInstance = getattr(mMod, sPlugin) makes mInstance the file Nmap.py rather than the class Nmap.
There are two ways to fix this, either:
Use the __init__.py in /Nmap to bring the class "up" one level, i.e. include from Nmap import Nmap; or
Add an extra level of getattr into Plugin.py.
Additionally, you should follow the style guide's naming conventions, which might have helped you track the issue down faster.

Categories