Factory Design Pattern in Python - python

Situation: I have implemented a factory design pattern in Python as below. There are different screens which have different configurations. One can register the screen in the factory and get the screen in case correct configuration is passed to respective factory class method.
Question: I am wondering if this is a clean solution as each screen class (Screen1, Screen2, ..) has a main-method which calls in a specific order the respective private class methods. Is there a better/cleaner way to handle the main methods in each class?
Highly appreciate any comments and improvements!!
Code:
import numpy as np
import pandas as pd
from abc import ABCMeta, abstractmethod
df = pd.DataFrame({"ident": ["A1", "A2", "B3", "B4"], "other_col": np.random.randint(1, 6, 4)})
class IScreens(metaclass=ABCMeta):
#abstractmethod
def main(self):
pass
class Screen1(IScreens):
def __init__(self, data, config):
self.data = data
self.batch = config["cfg1"]
def __get_letter(self):
self.data["campaign"] = self.data[self.batch].str[:1]
return self.data
def __get_number(self):
self.data["num"] = self.data[self.batch].str[1:]
return self.data
def __some_other_stuff(self):
self.data["other_stuff"] = self.data["num"].astype(int) * 100 / 2
return self.data
def main(self):
self.data = self.__get_letter()
self.data = self.__get_number()
self.data = self.__some_other_stuff()
# some more processing steps follow
return self.data
class Screen2(IScreens):
def __init__(self, data, config):
self.data = data
self.batch = config["cfg1"]
def __some_cool_stuff(self):
self.data["cool_other_stuff"] = self.data[self.batch] + "_COOOOL"
return self.data
def main(self):
self.data = self.__some_cool_stuff()
# some more processing steps follow
return self.data
class ScreenFactory:
def __init__(self):
self._screens = {}
def register_screen(self, screen_name, screen_class):
self._screens[screen_name] = screen_class
def get_screen(self, data, config):
if "screen_indicator" not in config:
raise AssertionError("Your config file does not include 'screen_indicator' key")
screen = self._screens.get(config["screen_indicator"])
if not screen:
raise AssertionError("screen not implemented")
return screen(data=data, config=config)
factory = ScreenFactory()
factory.register_screen(screen_name="s1", screen_class=Screen1)
config = {"screen_indicator": "s1", "cfg1": "ident"}
factory.get_screen(data=df, config=config).main()
factory.register_screen(screen_name="s2", screen_class=Screen2)
config = {"screen_indicator": "s2", "cfg1": "ident"}
factory.get_screen(data=df, config=config).main()

Related

I want to access the text of an MDTextField directly from the custom class with MyTextField.text but I'm getting <AliasProperty name=text>

python
Class MyTextField(MyTextField):
pass
class CustomIconItem(OneLineAvatarIconListItem):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.venus = []
def create_playlist(self, txt):
app = MDApp.get_running_app()
if MyTextField.text == "":
pass
else:
self.venus.append(MyTextField.text)
data = [{"text": str(self.venus[i]), "size_hint_x": 1}
for i in range(len(self.venus))]
app.root.ids.rp.data = data

Parametrized RuleBasedStateMachine

After watching https://www.youtube.com/watch?v=zi0rHwfiX1Q I tried to port the example from C (implementation) and Erlang (testing) to Python and Hypothesis. Given this implementation (the rem function emulates %'s C behavior):
import math
def rem(x, y):
res = x % y
return int(math.copysign(res,x))
class Queue:
def __init__(self, capacity: int):
self.capacity = capacity + 1
self.data = [None] * self.capacity
self.inp = 0
self.outp = 0
def put(self, n: int):
self.data[self.inp] = n
self.inp = (self.inp + 1) % self.capacity
def get(self):
ans = self.data[self.outp]
self.outp = (self.outp + 1) % self.capacity
return ans
def size(self):
return rem((self.inp - self.outp), self.capacity)
and this test code
import unittest
from hypothesis.stateful import rule, precondition, RuleBasedStateMachine
from hypothesis.strategies import integers
from myqueue import Queue
class QueueMachine(RuleBasedStateMachine):
cap = 1
def __init__(self):
super().__init__()
self.queue = Queue(self.cap)
self.model = []
#rule(value=integers())
#precondition(lambda self: len(self.model) < self.cap)
def put(self, value):
self.queue.put(value)
self.model.append(value)
#rule()
def size(self):
expected = len(self.model)
actual = self.queue.size()
assert actual == expected
#rule()
#precondition(lambda self: self.model)
def get(self):
actual = self.queue.get()
expected = self.model[0]
self.model = self.model[1:]
assert actual == expected
TestQueue = QueueMachine.TestCase
if __name__ == "__main__":
unittest.main()
The actual question is how to use Hypothesis to also parametrize over QueueMachine.cap instead of setting it manually in the test class.
You can set self.queue in an initialize method rather than __init__, using a suitable integers strategy for the capacity.

Attempt to pickle unknown type while creating a deepcopy

I have the following two classes:
class QPolygonModel(QtGui.QPolygon):
_idx = None
_selected = None
def __init__(self, idx, polygon: QtGui.QPolygon = None):
# Call default constructor
if polygon is None:
super().__init__()
# Call copy constructor
else:
super().__init__(polygon)
self._idx = idx
self._selected = False
#property
def idx(self):
return self._idx
#property
def is_selected(self):
return self._selected
#is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
This is a custom polygon class that inherits from QPolygon. Objects of this class are stored in a list in the "Scene" class:
class ImageAnnotatorState:
points = None
radius = None
image = None
polygons = None
_finished = None
multiselect = None
def __init__(self, image):
super().__init__()
self.points = QtGui.QPolygon()
self.radius = 8
self.image = image
self.polygons = self._init_polygons()
self.is_finished = False
self.multiselect = False
def _init_polygons(self):
result = []
for annotation in self.image.annotations:
polyline = QPolygonModel(annotation.get_id())
for point in annotation.points:
q_point = QPoint(point.x, point.y)
polyline.append(q_point)
result.append(polyline)
return result
#property
def is_finished(self):
return self._finished
#is_finished.setter
def is_finished(self, flag):
self._finished = flag
Now for the purpose of creating an undo function, I need to create a deepcopy of this scene class so I can store the state that was active before a scene change was made.
So in a QDialog form, I try to do the following:
class ImageAnnotator(QDialog):
_state = None
_previous_state = None
def __init__(self, image):
super().__init__()
self._state = ImageAnnotatorState(image)
self._previous_state = copy.deepcopy(self._state)
self.show()
The deepcopy call here fails with the following exception:
SystemError: attempt to pickle unknown type 'QPolygonModel'
What am I doing wrong?
EDIT:
Reproducible example:
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QApplication
import copy
import sys
class Test(QtGui.QPolygon):
idx = None
def __init__(self, z = None):
if z is None:
super().__init__()
else:
super().__init__(z)
class State:
test = None
def __init__(self):
self.test = [Test(), Test()]
print(self.test)
class Main(QDialog):
state = None
prev = None
def __init__(self):
super().__init__()
self.state = State()
prev = copy.deepcopy(self.state)
print(prev)
app = QApplication(sys.argv)
Main()
It seems that it is a bug similar to the one that ekhumoro points out in this answer. A workaround is to implement the __deepcopy__ method.
On the other hand if you want to set the default value in the case of QPolygon do not use None but an empty QPolygon.
With the above, I have implemented the following:
import copy
import random
from PyQt5 import QtCore, QtGui
class QPolygonModel(QtGui.QPolygon):
def __init__(self, idx, polygon=QtGui.QPolygon()):
super().__init__(polygon)
self._idx = idx
self._selected = False
#property
def idx(self):
return self._idx
#property
def is_selected(self):
return self._selected
#is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
# https://stackoverflow.com/a/10622689
def __deepcopy__(self, memo):
o = QPolygonModel(self.idx)
o.__dict__.update(self.__dict__)
ba = QtCore.QByteArray()
stream_w = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
stream_w << self
stream_r = QtCore.QDataStream(ba, QtCore.QIODevice.ReadOnly)
stream_r >> o
return o
class State:
def __init__(self):
self.polylines = []
for _ in range(4):
poly = QtGui.QPolygon(
[QtCore.QPoint(*random.sample(range(10), 2)) for _ in range(4)]
)
polyline = QPolygonModel(random.randint(0, 10), poly)
self.polylines.append(polyline)
if __name__ == "__main__":
curr = State()
prev = copy.deepcopy(curr)
assert len(curr.polylines) == len(prev.polylines)
for polyline1, polyline2 in zip(curr.polylines, prev.polylines):
assert id(polyline1) != id(polyline2)
assert polyline1.size() == polyline2.size()
assert polyline1.is_selected == polyline2.is_selected
assert polyline1.idx == polyline2.idx
for i, j in zip(range(polyline1.size()), range(polyline2.size())):
assert polyline1.point(i) == polyline2.point(j)

Bluez example-advertisement interval linux

I am using Bluez 5.50 example-advertisement to work with a custom server that I edited from the example-gatt-server. It works just fine however I want to reduce the advertising interval i.e. increase the advertisement frequency.
Most help available online is using the hcitool for example here. I read the details about the advertisement packet and it should be possible to set the interval to a described vale as per the supplement to Bluetooth core specifications described here. However, when I try to add it to the properties of the advertisement there is a parsing error. I am unsure how to go about this because of my unfamiliarity with python and dbus.
I am attaching my code for the advertisement below. I added the set_advInterval section where it was needed along the code.
`#!/usr/bin/python
from __future__ import print_function
import dbus
import dbus.exceptions
import dbus.mainloop.glib
import dbus.service
import array
try:
from gi.repository import GObject # python3
except ImportError:
import gobject as GObject # python2
from random import randint
mainloop = None
BLUEZ_SERVICE_NAME = 'org.bluez'
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
class InvalidArgsException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
class NotSupportedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.NotSupported'
class NotPermittedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.NotPermitted'
class InvalidValueLengthException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.InvalidValueLength'
class FailedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.Failed'
class Advertisement(dbus.service.Object):
PATH_BASE = '/org/bluez/example/advertisement'
def __init__(self, bus, index, advertising_type):
self.path = self.PATH_BASE + str(index)
self.bus = bus
self.ad_type = advertising_type
self.service_uuids = None
self.manufacturer_data = None
self.solicit_uuids = None
self.service_data = None
self.local_name = None
self.include_tx_power = None
self.data = None
self.advInterval=None
dbus.service.Object.__init__(self, bus, self.path)
def get_properties(self):
properties = dict()
properties['Type'] = self.ad_type
if self.service_uuids is not None:
properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
signature='s')
if self.solicit_uuids is not None:
properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
signature='s')
if self.manufacturer_data is not None:
properties['ManufacturerData'] = dbus.Dictionary(
self.manufacturer_data, signature='qv')
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
signature='sv')
if self.local_name is not None:
properties['LocalName'] = dbus.String(self.local_name)
if self.include_tx_power is not None:
properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
if self.advInterval is not None:
properties['Interval']=dbus.Uint16(self.advInterval)
if self.data is not None:
properties['Data'] = dbus.Dictionary(
self.data, signature='yv')
return {LE_ADVERTISEMENT_IFACE: properties}
def get_path(self):
return dbus.ObjectPath(self.path)
def set_advInterval(self,interval):
self.advInterval=dbus.UInt16(interval)
def add_service_uuid(self, uuid):
if not self.service_uuids:
self.service_uuids = []
self.service_uuids.append(uuid)
def add_solicit_uuid(self, uuid):
if not self.solicit_uuids:
self.solicit_uuids = []
self.solicit_uuids.append(uuid)
def add_manufacturer_data(self, manuf_code, data):
if not self.manufacturer_data:
self.manufacturer_data = dbus.Dictionary({}, signature='qv')
self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
def add_service_data(self, uuid, data):
if not self.service_data:
self.service_data = dbus.Dictionary({}, signature='sv')
self.service_data[uuid] = dbus.Array(data, signature='y')
def add_local_name(self, name):
if not self.local_name:
self.local_name = ""
self.local_name = dbus.String(name)
def add_data(self, ad_type, data):
if not self.data:
self.data = dbus.Dictionary({}, signature='yv')
self.data[ad_type] = dbus.Array(data, signature='y')
#dbus.service.method(DBUS_PROP_IFACE,
in_signature='s',
out_signature='a{sv}')
def GetAll(self, interface):
print('GetAll')
if interface != LE_ADVERTISEMENT_IFACE:
raise InvalidArgsException()
print('returning props')
return self.get_properties()[LE_ADVERTISEMENT_IFACE]
#dbus.service.method(LE_ADVERTISEMENT_IFACE,
in_signature='',
out_signature='')
def Release(self):
print('%s: Released!' % self.path)
class TestAdvertisement(Advertisement):
def __init__(self, bus, index):
Advertisement.__init__(self, bus, index, 'per
ipheral')
self.add_service_uuid('180F')
self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
self.add_service_data('9999', [0x00, 0x01, 0x02, 0x03, 0x04])
self.add_local_name('TestAdvertisement')
self.set_advInterval(0.2)
self.include_tx_power = True
self.add_data(0x26, [0x01, 0x01, 0x00])
def register_ad_cb():
print('Advertisement registered')
def register_ad_error_cb(error):
print('Failed to register advertisement: ' + str(error))
mainloop.quit()
def find_adapter(bus):
remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
DBUS_OM_IFACE)
objects = remote_om.GetManagedObjects()
for o, props in objects.items():
if LE_ADVERTISING_MANAGER_IFACE in props:
return o
return None
def main():
global mainloop
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
adapter = find_adapter(bus)
if not adapter:
print('LEAdvertisingManager1 interface not found')
return
adapter_props = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
"org.freedesktop.DBus.Properties");
adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(1))
ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
LE_ADVERTISING_MANAGER_IFACE)
test_advertisement = TestAdvertisement(bus, 0)
mainloop = GObject.MainLoop()
ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
reply_handler=register_ad_cb,
error_handler=register_ad_error_cb)
mainloop.run()
if __name__ == '__main__':
main()
`
According to https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.50/doc/advertising-api.txt the advertising interface api does not have property "Interval". Therefore we cant set the adv interval via this interface.

Get all subclasses defined in several python modules

I got 2 python files implement test repository factory pattern:
testrepository.py
class TestRepository(object):
def __init__(self):
pass
def generate(self, username, password):
pass
def update(self):
pass
class RepositoryFactory(object):
factories = {}
def add_repo(identification, repo_factory):
RepositoryFactory.factories[identification] = repo_factory
add_repo = staticmethod(add_repo)
def create_repo(identification, server, dirpath, reponame):
if identification not in RepositoryFactory.factories:
RepositoryFactory.factories[identification] = \
eval(identification + '.Factory()')
return RepositoryFactory.factories[identification].create(server, dirpath, reponame)
create_repo = staticmethod(create_repo)
gitrepository.py
from testrepository import TestRepository, RepositoryFactory
class GitRepository(TestRepository):
def __init__(self, server, dirpath, reponame):
super(GitRepository, self).__init__()
self.server = server
self.dirpath = dirpath
self.reponame = reponame
self.username = None
self.password = None
self.fullPath = path.join(self.dirpath, self.reponame)
def generate(self, username, password):
pass
def update(self):
pass
class Factory(object):
#staticmethod
def create(server, dirpath, reponame):
return GitRepository(server=server, dirpath=dirpath, reponame=reponame)
if __name__ == '__main__':
repo_type = TestRepository.__subclasses__().pop().__name__
repo = RepositoryFactory.create_repo(repo_type,
server='localhost',
dirpath='/home/user/git',
reponame='repo')
repo.generate(username='test', password='test')
repo.update()
Problem is repo_type = TestRepository.__subclasses__() in example above is empty, if I put all classes in one file it works:
>>> print TestRepository.__subclasses__()
[<class '__main__.GitRepository'>]
so question is how to fetch all subclasses from several python modules?

Categories