Commit 755a17bf authored by Daniel Brown's avatar Daniel Brown
Browse files

fixing more deepcopying issues, probably still some left with detectors

parent f98056f0
......@@ -84,6 +84,8 @@ class NodeGaussSetter(object):
def qy(self, value):
self.__node().setGauss(self.__comp(), self.qx, complex(value))
id___ = 0
class Component(object):
__metaclass__ = abc.ABCMeta
......@@ -91,7 +93,13 @@ class Component(object):
# This creates an instance specific class for the component
# this enables us to add properties to instances rather than
# all classes
return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs)
global id___
id___ += 1
cnew_name = str("%s.%s_%i" % (cls.__module__, cls.__name__, id___))
cnew = type(cnew_name, (cls,), {})
return object.__new__(cnew, *args, **kwargs)
def __init__(self, name=None):
......@@ -116,10 +124,11 @@ class Component(object):
When deep copying a kat object we need to take into account
the instance specific properties.
"""
result = self.__class__.__new__(self.__class__)
result.__dict__ = copy.deepcopy(self.__dict__, memo)
result.__update_node_setters
# Here we create a copy of this object based of the base class
# of this one, otherwise we're making a copy of a copy of a copy...
result = self.__class__.__new__(self.__class__.__base__)
result.__dict__ = copy.deepcopy(self.__dict__, memo)
return result
......@@ -163,13 +172,19 @@ class Component(object):
# need to remove them. This function should get called if the nodes
# are updated, either by some function call or the GUI
key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)]
# now we have a list of which to remove
for key in key_rm:
ns = self.__dict__[key]
delattr(self, '__nodesetter_' + ns.node.name)
delattr(self.__class__, ns.node.name)
name = str(ns.node.name)
if '__nodesetter_' + name in self.__dict__:
delattr(self, '__nodesetter_' + name)
if name in self.__class__.__dict__:
delattr(self.__class__, name)
# Now re-add them pointing to the recent nodes
for node in self.nodes:
if type(node) != pykat.node_network.DumpNode:
ns = NodeGaussSetter(self, node)
......@@ -180,9 +195,12 @@ class Component(object):
if not isinstance(ns, NodeGaussSetter):
raise exceptions.ValueError("Argument is not of type NodeGaussSetter")
name = ns.node.name
name = str(ns.node.name)
fget = lambda self: self.__get_node_setter(name)
if name == "nITM1":
print(self.__class__)
setattr(self.__class__, name, property(fget))
setattr(self, '__nodesetter_' + name, ns)
......
......@@ -416,7 +416,9 @@ class Block:
def name(self): return self.__name
Constant = namedtuple('Constant', 'name, value, usedBy')
id___ = 0
class kat(object):
def __new__(cls, *args, **kwargs):
......@@ -428,7 +430,10 @@ class kat(object):
# kat objects share the same class definition they also have the
# same properties regardless of whether they have the actual
# object added to it. So we create an instance specific class.
return object.__new__(type(pykat.finesse.kat.__name__, (pykat.finesse.kat,), {}), *args, **kwargs)
global id___
id___ += 1
cnew = type(pykat.finesse.kat.__name__ + str("_") + str(id___), (pykat.finesse.kat,), {})
return object.__new__(cnew, *args, **kwargs)
def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None):
self.scene = None # scene object for GUI
......@@ -477,17 +482,32 @@ class kat(object):
self.loadKatFile(kat_file)
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
"""
When deep copying a kat object we need to take into account
the instance specific properties. This is because when
the kat object adds new components it also adds properties for
each of these. There properties are unique to each kat object,
but properties are part of the class definition. Thus if two
kat objects share the same class definition they also have the
same properties regardless of whether they have the actual
object added to it. So we create an instance specific class.
"""
result = self.__class__.__new__(self.__class__)
memo[id(self)] = result
for k, v in self.__dict__.items():
setattr(result, k, copy.deepcopy(v, memo))
result.__dict__ = copy.deepcopy(self.__dict__, memo)
# Find all properties in class we are copying
# and deep copy these to the new class instance
for x in self.__class__.__dict__.items():
if isinstance(x[1], property):
setattr(result.__class__, x[0], x[1])
result.nodes._NodeNetwork__update_nodes_properties()
# Update any weakrefs
for c in result.components:
result.components[c]._Component__update_node_setters()
return result
@property
......
......@@ -22,13 +22,21 @@ from pykat.detectors import BaseDetector as Detector
from pykat.optics.gaussian_beams import beam_param
from copy import deepcopy
id___ = 0
class NodeNetwork(object):
def __new__(cls, *args, **kwargs):
# This creates an instance specific class for the component
# this enables us to add properties to instances rather than
# all classes
return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs)
# This creates an instance specific class for the component
# this enables us to add properties to instances rather than
# all classes
global id___
id___ += 1
cnew_name = str("%s.%s_%i" % (cls.__module__, cls.__name__, id___))
cnew = type(cnew_name, (cls,), {})
return object.__new__(cnew, *args, **kwargs)
def __init__(self, kat):
self.__nodes = {}
......@@ -37,7 +45,20 @@ class NodeNetwork(object):
self.__componentNodes = {} # dictionary of tuples containing which nodes are connected to a given component
self.__componentCallback = {}
self.__node_id = 1
def __deepcopy__(self, memo):
"""
When deep copying a kat object we need to take into account
the instance specific properties.
"""
# Here we create a copy of this object based of the base class
# of this one, otherwise we're making a copy of a copy of a copy...
result = self.__class__.__new__(self.__class__.__base__)
result.__dict__ = deepcopy(self.__dict__, memo)
return result
@property
def kat(self): return self.__kat
......@@ -151,7 +172,29 @@ class NodeNetwork(object):
self.__nodeComponents[node.id] = tuple(l)
if do_callback: self.__componentCallback[comp.id]()
def __update_nodes_properties(self):
# check if any node setters have already been added. If so we
# need to remove them. This function should get called if the nodes
# are updated, either by some function call or the GUI
key_rm = [k for k in self.__dict__ if k.startswith("__node_", 0, 7)]
# now we have a list of which to remove
for key in key_rm:
ns = self.__dict__[key]
name = str(ns.name)
if '__node_' + name in self.__dict__:
delattr(self, '__node_' + name)
if name in self.__class__.__dict__:
delattr(self.__class__, name)
# Now re-add them pointing to the recent nodes
for node in self.__nodes:
if not self.__nodes[node].isDump:
self.__add_node_attr(self.__nodes[node])
def createNode(self, node_name):
"""
This creates a new node object. It won't be connected to anything or added to a
......@@ -285,6 +328,7 @@ class NodeNetwork(object):
kat.nodes.replaceNode(kat.bs1, "n1", kat.nodes.createNode("test1"))
name = node.name
delattr(self, '__node_' + name)
delattr(self.__class__, name)
......
"""
Test file to ensure that references between deep copied objects are handled properly
"""
import pykat
from copy import deepcopy
kat0 = pykat.finesse.kat()
kat0.parseCommands("m m1 1 0 0 n0 n1")
kat1 = deepcopy(kat0)
assert(kat0 != kat1)
assert(kat0.__class__ != kat1.__class__)
assert(kat0.m1 != kat1.m1)
assert(kat0.m1.__class__ != kat1.m1.__class__)
assert(kat0.m1.n0 != kat1.m1.n0)
assert(kat0.m1.n0.node != kat1.m1.n0.node)
assert(kat0.nodes.n0 != kat1.nodes.n0)
new = kat1.nodes.createNode("n4")
kat1.nodes.replaceNode(kat1.m1, kat1.m1.n0, new)
assert(not hasattr(kat1.nodes, "n0"))
assert(hasattr(kat1.nodes, "n4"))
assert(hasattr(kat0.nodes, "n0"))
assert(not hasattr(kat0.nodes, "n4"))
assert(hasattr(kat1.m1, "n4"))
assert(not hasattr(kat1.m1, "n0"))
assert(hasattr(kat0.m1, "n0"))
assert(not hasattr(kat0.m1, "n4"))
assert(kat0.nodes.n0 == kat0.m1.n0.node)
assert(kat0.nodes.n1 == kat0.m1.n1.node)
assert(kat1.nodes.n4 == kat1.m1.n4.node)
assert(kat1.nodes.n1 == kat1.m1.n1.node)
print("PASSED")
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment