mirror of
https://github.com/JoelBender/bacpypes
synced 2025-09-28 22:15:23 +08:00
add an IPRouter and tests
This commit is contained in:
parent
f1d4626527
commit
2fa62dfe5f
|
@ -5,6 +5,8 @@ Virtual Local Area Network
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from .errors import ConfigurationError
|
from .errors import ConfigurationError
|
||||||
|
@ -12,7 +14,7 @@ from .debugging import ModuleLogger, bacpypes_debugging
|
||||||
|
|
||||||
from .core import deferred
|
from .core import deferred
|
||||||
from .pdu import Address
|
from .pdu import Address
|
||||||
from .comm import Server
|
from .comm import Client, Server, bind
|
||||||
|
|
||||||
# some debugging
|
# some debugging
|
||||||
_debug = 0
|
_debug = 0
|
||||||
|
@ -61,10 +63,12 @@ class Network:
|
||||||
if pdu.pduDestination == self.broadcast_address:
|
if pdu.pduDestination == self.broadcast_address:
|
||||||
for n in self.nodes:
|
for n in self.nodes:
|
||||||
if (pdu.pduSource != n.address):
|
if (pdu.pduSource != n.address):
|
||||||
|
if _debug: Network._debug(" - match: %r", n)
|
||||||
n.response(deepcopy(pdu))
|
n.response(deepcopy(pdu))
|
||||||
else:
|
else:
|
||||||
for n in self.nodes:
|
for n in self.nodes:
|
||||||
if n.promiscuous or (pdu.pduDestination == n.address):
|
if n.promiscuous or (pdu.pduDestination == n.address):
|
||||||
|
if _debug: Network._debug(" - match: %r", n)
|
||||||
n.response(deepcopy(pdu))
|
n.response(deepcopy(pdu))
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
|
@ -165,7 +169,7 @@ class IPNode(Node):
|
||||||
tuple and a broadcast tuple that would be used for socket communications.
|
tuple and a broadcast tuple that would be used for socket communications.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, addr, lan=None):
|
def __init__(self, addr, lan=None, promiscuous=False, spoofing=False, sid=None):
|
||||||
if _debug: IPNode._debug("__init__ %r lan=%r", addr, lan)
|
if _debug: IPNode._debug("__init__ %r lan=%r", addr, lan)
|
||||||
|
|
||||||
# make sure it's an Address that has appropriate pieces
|
# make sure it's an Address that has appropriate pieces
|
||||||
|
@ -178,5 +182,74 @@ class IPNode(Node):
|
||||||
self.addrBroadcastTuple = addr.addrBroadcastTuple
|
self.addrBroadcastTuple = addr.addrBroadcastTuple
|
||||||
|
|
||||||
# continue initializing
|
# continue initializing
|
||||||
Node.__init__(self, addr.addrTuple, lan=lan)
|
Node.__init__(self, addr.addrTuple, lan=lan, promiscuous=promiscuous, spoofing=spoofing, sid=sid)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# IPRouterNode
|
||||||
|
#
|
||||||
|
|
||||||
|
@bacpypes_debugging
|
||||||
|
class IPRouterNode(Client):
|
||||||
|
|
||||||
|
def __init__(self, router, addr, lan=None):
|
||||||
|
if _debug: IPRouterNode._debug("__init__ %r %r lan=%r", router, addr, lan)
|
||||||
|
|
||||||
|
# save the reference to the router
|
||||||
|
self.router = router
|
||||||
|
|
||||||
|
# make ourselves an IPNode and bind to it
|
||||||
|
self.node = IPNode(addr, lan=lan, promiscuous=True, spoofing=True)
|
||||||
|
bind(self, self.node)
|
||||||
|
|
||||||
|
# save our mask and subnet
|
||||||
|
self.addrMask = addr.addrMask
|
||||||
|
self.addrSubnet = addr.addrSubnet
|
||||||
|
|
||||||
|
def confirmation(self, pdu):
|
||||||
|
if _debug: IPRouterNode._debug("confirmation %r", pdu)
|
||||||
|
|
||||||
|
self.router.process_pdu(self, pdu)
|
||||||
|
|
||||||
|
def process_pdu(self, pdu):
|
||||||
|
if _debug: IPRouterNode._debug("process_pdu %r", pdu)
|
||||||
|
|
||||||
|
# pass it downstream
|
||||||
|
self.request(pdu)
|
||||||
|
|
||||||
|
#
|
||||||
|
# IPRouter
|
||||||
|
#
|
||||||
|
|
||||||
|
@bacpypes_debugging
|
||||||
|
class IPRouter:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if _debug: IPRouter._debug("__init__")
|
||||||
|
|
||||||
|
# connected network nodes
|
||||||
|
self.nodes = []
|
||||||
|
|
||||||
|
def add_network(self, addr, lan):
|
||||||
|
if _debug: IPRouter._debug("add_network %r %r", addr, lan)
|
||||||
|
|
||||||
|
node = IPRouterNode(self, addr, lan)
|
||||||
|
if _debug: IPRouter._debug(" - node: %r", node)
|
||||||
|
|
||||||
|
self.nodes.append(node)
|
||||||
|
|
||||||
|
def process_pdu(self, node, pdu):
|
||||||
|
if _debug: IPRouter._debug("process_pdu %r %r", node, pdu)
|
||||||
|
|
||||||
|
# unpack the address part of the destination
|
||||||
|
addrstr = socket.inet_aton(pdu.pduDestination[0])
|
||||||
|
ipaddr = struct.unpack('!L', addrstr)[0]
|
||||||
|
if _debug: IPRouter._debug(" - ipaddr: %r", ipaddr)
|
||||||
|
|
||||||
|
# loop through the other nodes
|
||||||
|
for inode in self.nodes:
|
||||||
|
if inode is not node:
|
||||||
|
if (ipaddr & inode.addrMask) == inode.addrSubnet:
|
||||||
|
if _debug: IPRouter._debug(" - inode: %r", inode)
|
||||||
|
inode.process_pdu(pdu)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ from bacpypes.debugging import bacpypes_debugging, ModuleLogger
|
||||||
|
|
||||||
from bacpypes.pdu import Address, LocalBroadcast, PDU
|
from bacpypes.pdu import Address, LocalBroadcast, PDU
|
||||||
from bacpypes.comm import bind
|
from bacpypes.comm import bind
|
||||||
from bacpypes.vlan import IPNetwork, IPNode
|
from bacpypes.vlan import IPNetwork, IPNode, IPRouter
|
||||||
|
|
||||||
from ..state_machine import ClientStateMachine, StateMachineGroup
|
from ..state_machine import ClientStateMachine, StateMachineGroup
|
||||||
from ..time_machine import reset_time_machine, run_time_machine
|
from ..time_machine import reset_time_machine, run_time_machine
|
||||||
|
@ -58,14 +58,14 @@ class ZPDU():
|
||||||
@bacpypes_debugging
|
@bacpypes_debugging
|
||||||
class TNetwork(StateMachineGroup):
|
class TNetwork(StateMachineGroup):
|
||||||
|
|
||||||
def __init__(self, node_count):
|
def __init__(self, node_count, address_pattern):
|
||||||
if _debug: TNetwork._debug("__init__ %r", node_count)
|
if _debug: TNetwork._debug("__init__ %r", node_count)
|
||||||
StateMachineGroup.__init__(self)
|
StateMachineGroup.__init__(self)
|
||||||
|
|
||||||
self.vlan = IPNetwork()
|
self.vlan = IPNetwork()
|
||||||
|
|
||||||
for i in range(node_count):
|
for i in range(node_count):
|
||||||
node_address = Address("192.168.0.{}/24".format(i + 1))
|
node_address = Address(address_pattern.format(i + 1))
|
||||||
node = IPNode(node_address, self.vlan)
|
node = IPNode(node_address, self.vlan)
|
||||||
if _debug: TNetwork._debug(" - node: %r", node)
|
if _debug: TNetwork._debug(" - node: %r", node)
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_idle")
|
if _debug: TestVLAN._debug("test_idle")
|
||||||
|
|
||||||
# two element network
|
# two element network
|
||||||
tnet = TNetwork(2)
|
tnet = TNetwork(2, "192.168.1.{}/24")
|
||||||
|
|
||||||
# set the start states of both machines to success
|
# set the start states of both machines to success
|
||||||
tnet[1].start_state.success()
|
tnet[1].start_state.success()
|
||||||
|
@ -130,19 +130,19 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_send_receive")
|
if _debug: TestVLAN._debug("test_send_receive")
|
||||||
|
|
||||||
# two element network
|
# two element network
|
||||||
tnet = TNetwork(2)
|
tnet = TNetwork(2, "192.168.2.{}/24")
|
||||||
|
|
||||||
# make a PDU from node 1 to node 2
|
# make a PDU from node 1 to node 2
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.1', 47808),
|
source=('192.168.2.1', 47808),
|
||||||
destination=('192.168.0.2', 47808),
|
destination=('192.168.2.2', 47808),
|
||||||
)
|
)
|
||||||
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
||||||
|
|
||||||
# node 1 sends the pdu, mode 2 gets it
|
# node 1 sends the pdu, mode 2 gets it
|
||||||
tnet[1].start_state.send(pdu).success()
|
tnet[1].start_state.send(pdu).success()
|
||||||
tnet[2].start_state.receive(ZPDU(
|
tnet[2].start_state.receive(ZPDU(
|
||||||
pduSource=('192.168.0.1', 47808),
|
pduSource=('192.168.2.1', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
|
|
||||||
# run the group
|
# run the group
|
||||||
|
@ -155,22 +155,22 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_broadcast")
|
if _debug: TestVLAN._debug("test_broadcast")
|
||||||
|
|
||||||
# three element network
|
# three element network
|
||||||
tnet = TNetwork(3)
|
tnet = TNetwork(3, "192.168.3.{}/24")
|
||||||
|
|
||||||
# make a broadcast PDU
|
# make a broadcast PDU
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.1', 47808),
|
source=('192.168.3.1', 47808),
|
||||||
destination=('192.168.0.255', 47808),
|
destination=('192.168.3.255', 47808),
|
||||||
)
|
)
|
||||||
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
||||||
|
|
||||||
# node 1 sends the pdu, node 2 and 3 each get it
|
# node 1 sends the pdu, node 2 and 3 each get it
|
||||||
tnet[1].start_state.send(pdu).success()
|
tnet[1].start_state.send(pdu).success()
|
||||||
tnet[2].start_state.receive(ZPDU(
|
tnet[2].start_state.receive(ZPDU(
|
||||||
pduSource=('192.168.0.1', 47808),
|
pduSource=('192.168.3.1', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
tnet[3].start_state.receive(ZPDU(
|
tnet[3].start_state.receive(ZPDU(
|
||||||
pduSource=('192.168.0.1', 47808)
|
pduSource=('192.168.3.1', 47808)
|
||||||
)).success()
|
)).success()
|
||||||
|
|
||||||
# run the group
|
# run the group
|
||||||
|
@ -183,12 +183,12 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_spoof_fail")
|
if _debug: TestVLAN._debug("test_spoof_fail")
|
||||||
|
|
||||||
# two element network
|
# two element network
|
||||||
tnet = TNetwork(1)
|
tnet = TNetwork(1, "192.168.4.{}/24")
|
||||||
|
|
||||||
# make a unicast PDU with the wrong source
|
# make a unicast PDU with the wrong source
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.2', 47808),
|
source=('192.168.4.2', 47808),
|
||||||
destination=('192.168.0.3', 47808),
|
destination=('192.168.4.3', 47808),
|
||||||
)
|
)
|
||||||
|
|
||||||
# the node sends the pdu and would be a success but...
|
# the node sends the pdu and would be a success but...
|
||||||
|
@ -205,20 +205,20 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_spoof_pass")
|
if _debug: TestVLAN._debug("test_spoof_pass")
|
||||||
|
|
||||||
# one node network
|
# one node network
|
||||||
tnet = TNetwork(1)
|
tnet = TNetwork(1, "192.168.5.{}/24")
|
||||||
|
|
||||||
# reach into the network and enable spoofing for the node
|
# reach into the network and enable spoofing for the node
|
||||||
tnet.vlan.nodes[0].spoofing = True
|
tnet.vlan.nodes[0].spoofing = True
|
||||||
|
|
||||||
# make a unicast PDU from a fictitious node
|
# make a unicast PDU from a fictitious node
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.3', 47808),
|
source=('192.168.5.3', 47808),
|
||||||
destination=('192.168.0.1', 47808),
|
destination=('192.168.5.1', 47808),
|
||||||
)
|
)
|
||||||
|
|
||||||
# node 1 sends the pdu, but gets it back as if it was from node 3
|
# node 1 sends the pdu, but gets it back as if it was from node 3
|
||||||
tnet[1].start_state.send(pdu).receive(ZPDU(
|
tnet[1].start_state.send(pdu).receive(ZPDU(
|
||||||
pduSource=('192.168.0.3', 47808),
|
pduSource=('192.168.5.3', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
|
|
||||||
# run the group
|
# run the group
|
||||||
|
@ -232,24 +232,24 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_promiscuous_pass")
|
if _debug: TestVLAN._debug("test_promiscuous_pass")
|
||||||
|
|
||||||
# three element network
|
# three element network
|
||||||
tnet = TNetwork(3)
|
tnet = TNetwork(3, "192.168.6.{}/24")
|
||||||
|
|
||||||
# reach into the network and enable promiscuous mode
|
# reach into the network and enable promiscuous mode
|
||||||
tnet.vlan.nodes[2].promiscuous = True
|
tnet.vlan.nodes[2].promiscuous = True
|
||||||
|
|
||||||
# make a PDU from node 1 to node 2
|
# make a PDU from node 1 to node 2
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.1', 47808),
|
source=('192.168.6.1', 47808),
|
||||||
destination=('192.168.0.2', 47808),
|
destination=('192.168.6.2', 47808),
|
||||||
)
|
)
|
||||||
|
|
||||||
# node 1 sends the pdu to node 2, node 3 also gets a copy
|
# node 1 sends the pdu to node 2, node 3 also gets a copy
|
||||||
tnet[1].start_state.send(pdu).success()
|
tnet[1].start_state.send(pdu).success()
|
||||||
tnet[2].start_state.receive(ZPDU(
|
tnet[2].start_state.receive(ZPDU(
|
||||||
pduSource=('192.168.0.1', 47808),
|
pduSource=('192.168.6.1', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
tnet[3].start_state.receive(ZPDU(
|
tnet[3].start_state.receive(ZPDU(
|
||||||
pduDestination=('192.168.0.2', 47808),
|
pduDestination=('192.168.6.2', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
|
|
||||||
# run the group
|
# run the group
|
||||||
|
@ -259,18 +259,18 @@ class TestVLAN(unittest.TestCase):
|
||||||
if _debug: TestVLAN._debug("test_promiscuous_fail")
|
if _debug: TestVLAN._debug("test_promiscuous_fail")
|
||||||
|
|
||||||
# three element network
|
# three element network
|
||||||
tnet = TNetwork(3)
|
tnet = TNetwork(3, "192.168.7.{}/24")
|
||||||
|
|
||||||
# make a PDU from node 1 to node 2
|
# make a PDU from node 1 to node 2
|
||||||
pdu = PDU(b'data',
|
pdu = PDU(b'data',
|
||||||
source=('192.168.0.1', 47808),
|
source=('192.168.7.1', 47808),
|
||||||
destination=('192.168.0.2', 47808),
|
destination=('192.168.7.2', 47808),
|
||||||
)
|
)
|
||||||
|
|
||||||
# node 1 sends the pdu to node 2, node 3 waits and gets nothing
|
# node 1 sends the pdu to node 2, node 3 waits and gets nothing
|
||||||
tnet[1].start_state.send(pdu).success()
|
tnet[1].start_state.send(pdu).success()
|
||||||
tnet[2].start_state.receive(ZPDU(
|
tnet[2].start_state.receive(ZPDU(
|
||||||
pduSource=('192.168.0.1', 47808),
|
pduSource=('192.168.7.1', 47808),
|
||||||
)).success()
|
)).success()
|
||||||
|
|
||||||
# if node 3 receives anything it will trigger unexpected receive and fail
|
# if node 3 receives anything it will trigger unexpected receive and fail
|
||||||
|
@ -279,3 +279,124 @@ class TestVLAN(unittest.TestCase):
|
||||||
# run the group
|
# run the group
|
||||||
tnet.run()
|
tnet.run()
|
||||||
|
|
||||||
|
|
||||||
|
@bacpypes_debugging
|
||||||
|
class TestRouter(unittest.TestCase):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if _debug: TestRouter._debug("__init__ %r %r", args, kwargs)
|
||||||
|
super(TestRouter, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def setup_method(self, method):
|
||||||
|
"""This function is called before each test method is called as is
|
||||||
|
given a reference to the test method."""
|
||||||
|
if _debug: TestRouter._debug("setup_method %r", method)
|
||||||
|
|
||||||
|
# create a state machine group that has all nodes on all networks
|
||||||
|
self.smg = StateMachineGroup()
|
||||||
|
|
||||||
|
# make some networks
|
||||||
|
vlan10 = IPNetwork()
|
||||||
|
vlan20 = IPNetwork()
|
||||||
|
|
||||||
|
# make a router and add the networks
|
||||||
|
trouter = IPRouter()
|
||||||
|
trouter.add_network(Address("192.168.10.1/24"), vlan10)
|
||||||
|
trouter.add_network(Address("192.168.20.1/24"), vlan20)
|
||||||
|
|
||||||
|
# add nodes to the networks
|
||||||
|
for pattern, lan in (
|
||||||
|
("192.168.10.{}/24", vlan10),
|
||||||
|
("192.168.20.{}/24", vlan20),
|
||||||
|
):
|
||||||
|
for i in range(2):
|
||||||
|
node_address = Address(pattern.format(i + 2))
|
||||||
|
node = IPNode(node_address, lan)
|
||||||
|
if _debug: TestRouter._debug(" - node: %r", node)
|
||||||
|
|
||||||
|
# bind a client state machine to the node
|
||||||
|
csm = ClientStateMachine()
|
||||||
|
bind(csm, node)
|
||||||
|
|
||||||
|
# add it to the group
|
||||||
|
self.smg.append(csm)
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
"""This function is called after each test method has been called and
|
||||||
|
is given a reference to the test method."""
|
||||||
|
if _debug: TestRouter._debug("teardown_method %r", method)
|
||||||
|
|
||||||
|
# reset the time machine
|
||||||
|
reset_time_machine()
|
||||||
|
if _debug: TestRouter._debug(" - time machine reset")
|
||||||
|
|
||||||
|
# run the group
|
||||||
|
self.smg.run()
|
||||||
|
|
||||||
|
# run it for some time
|
||||||
|
run_time_machine(60.0)
|
||||||
|
if _debug: TestRouter._debug(" - time machine finished")
|
||||||
|
|
||||||
|
# check for success
|
||||||
|
all_success, some_failed = self.smg.check_for_success()
|
||||||
|
assert all_success
|
||||||
|
|
||||||
|
def test_idle(self):
|
||||||
|
if _debug: TestRouter._debug("test_idle")
|
||||||
|
|
||||||
|
# all successful
|
||||||
|
for csm in self.smg.state_machines:
|
||||||
|
csm.start_state.success()
|
||||||
|
|
||||||
|
def test_send_receive(self):
|
||||||
|
"""Test that a node can send a message to another node on
|
||||||
|
a different network.
|
||||||
|
"""
|
||||||
|
if _debug: TestRouter._debug("test_send_receive")
|
||||||
|
|
||||||
|
# unpack the state machines
|
||||||
|
csm_10_2, csm_10_3, csm_20_2, csm_20_3 = self.smg.state_machines
|
||||||
|
|
||||||
|
# make a PDU from network 10 node 1 to network 20 node 2
|
||||||
|
pdu = PDU(b'data',
|
||||||
|
source=('192.168.10.2', 47808),
|
||||||
|
destination=('192.168.20.3', 47808),
|
||||||
|
)
|
||||||
|
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
||||||
|
|
||||||
|
# node 1 sends the pdu, mode 2 gets it
|
||||||
|
csm_10_2.start_state.send(pdu).success()
|
||||||
|
csm_20_3.start_state.receive(ZPDU(
|
||||||
|
pduSource=('192.168.10.2', 47808),
|
||||||
|
)).success()
|
||||||
|
|
||||||
|
# other nodes get nothing
|
||||||
|
csm_10_3.start_state.success()
|
||||||
|
csm_20_2.start_state.success()
|
||||||
|
|
||||||
|
def test_remote_broadcast(self):
|
||||||
|
"""Test that a node can send a message to all of the other nodes on
|
||||||
|
a different network.
|
||||||
|
"""
|
||||||
|
if _debug: TestRouter._debug("test_remote_broadcast")
|
||||||
|
|
||||||
|
# unpack the state machines
|
||||||
|
csm_10_2, csm_10_3, csm_20_2, csm_20_3 = self.smg.state_machines
|
||||||
|
|
||||||
|
# make a PDU from network 10 node 1 to network 20 node 2
|
||||||
|
pdu = PDU(b'data',
|
||||||
|
source=('192.168.10.2', 47808),
|
||||||
|
destination=('192.168.20.255', 47808),
|
||||||
|
)
|
||||||
|
if _debug: TestVLAN._debug(" - pdu: %r", pdu)
|
||||||
|
|
||||||
|
# node 10-2 sends the pdu, nodes 20-2 and 20-3 get it
|
||||||
|
csm_10_2.start_state.send(pdu).success()
|
||||||
|
csm_10_3.start_state.success()
|
||||||
|
csm_20_2.start_state.receive(ZPDU(
|
||||||
|
pduSource=('192.168.10.2', 47808),
|
||||||
|
)).success()
|
||||||
|
csm_20_3.start_state.receive(ZPDU(
|
||||||
|
pduSource=('192.168.10.2', 47808),
|
||||||
|
)).success()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ _log = ModuleLogger(globals())
|
||||||
class ZPDU():
|
class ZPDU():
|
||||||
|
|
||||||
def __init__(self, cls=None, **kwargs):
|
def __init__(self, cls=None, **kwargs):
|
||||||
if _debug: ZPDU._debug("__init__ %r", kwargs)
|
if _debug: ZPDU._debug("__init__ %r %r", cls, kwargs)
|
||||||
|
|
||||||
self.cls = cls
|
self.cls = cls
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
Loading…
Reference in New Issue
Block a user