From e67e4c01a8cc479057070d15654eda832f4639df Mon Sep 17 00:00:00 2001 From: Daniel Brown <ddb@star.sr.bham.ac.uk> Date: Thu, 15 Jan 2015 17:18:13 +0000 Subject: [PATCH] Attempt at fixing referencing issue when deep copying the various pykat objects. It was an issue because they create dynamic properties for classes on the fly depending on which components are added, e.g. so that you can do kat.m1. However on copying the class definitions also need to be deep-copied by creating a new class and copying over the properties. --- examples/length_tuning.py | 2 +- pykat/components.py | 32 ++++++++++++++++++------ pykat/detectors.py | 33 ++++++++++++++++++++++--- pykat/finesse.py | 51 ++++++++++++++++++++++++++++++++------- pykat/node_network.py | 14 ++++++++--- 5 files changed, 107 insertions(+), 25 deletions(-) diff --git a/examples/length_tuning.py b/examples/length_tuning.py index 8a1c524..2729774 100644 --- a/examples/length_tuning.py +++ b/examples/length_tuning.py @@ -38,7 +38,7 @@ xaxis b1 phi lin 0 180 100 yaxis deg % plotting the phase of the results """ -kat = finesse.kat(tempdir = "/home/sleavey/Desktop/") +kat = finesse.kat() kat.parseCommands(code) maxtem = np.arange(0, 2, 2) diff --git a/pykat/components.py b/pykat/components.py index 99c8cdb..7193049 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -13,6 +13,7 @@ import pykat from pykat.node_network import * from pykat.exceptions import * import abc +import copy from pykat.SIfloat import * from pykat.param import Param, AttrParam @@ -70,8 +71,15 @@ class NodeGaussSetter(object): class Component(object): __metaclass__ = abc.ABCMeta - - def __init__(self, name): + + 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) + + def __init__(self, name=None): + self.__name = name self._svgItem = None self._requested_node_names = [] @@ -86,12 +94,20 @@ class Component(object): self.__id = next_component_id next_component_id += 1 - # This creates an instance specific class for the component - # this enables us to add properties to instances rather than - # all classes - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - + + + def __deepcopy__(self, memo): + """ + 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 + + return result + def _register_param(self, param): self._params.append(param) diff --git a/pykat/detectors.py b/pykat/detectors.py index 612b01c..e6c5e93 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -75,6 +75,8 @@ class BaseDetector(object) : else: raise pkex.BasePyKatException("Nodes should be a list or tuple of node names or a singular node name as a string.") + + def _register_param(self, param): self._params.append(param) @@ -349,7 +351,13 @@ class bp(Detector1): class pd(Detector1): - def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, pdtype=None, **kwargs): + 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) + + def __init__(self, name=None, num_demods=1, node_name=None, senstype=None, alternate_beam=False, pdtype=None, **kwargs): BaseDetector.__init__(self, name, node_name) self.__num_demods = num_demods @@ -394,11 +402,28 @@ class pd(Detector1): elif i<num_demods-1: raise pkex.BasePyKatException("Missing demodulation phase {0} (phi{0})".format(i+1)) - # define new class for assigning new attributes - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) self.__set_demod_attrs() + + + def __deepcopy__(self, memo): + """ + When deep copying a kat object we need to take into account + the instance specific properties. + """ + + result = pd(self.name, self.num_demods, self.node.name) + + memo[id(self)] = result + 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]) + + return result @property def senstype(self): return self.__senstype diff --git a/pykat/finesse.py b/pykat/finesse.py index e32e785..bc335e2 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -183,10 +183,9 @@ class katRun(object): def __getitem__(self, value): idx = [i for i in range(len(self.ylabels)) if self.ylabels[i].split()[0] == str(value)] - + out = None + if len(idx) > 0: - out = self.y[:, idx] - if len(idx) == 1: if self.yaxis == "abs:deg": out = self.y[:, idx[0]] @@ -198,6 +197,9 @@ class katRun(object): elif self.yaxis == "re:im": out = self.y[:, idx[0]] + 1j*self.y[:, idx[1]] + if out == None: + out = self.y[:, idx] + if out.size == 1: return out[0].squeeze() else: @@ -375,10 +377,18 @@ Constant = namedtuple('Constant', 'name, value, usedBy') class kat(object): + def __new__(cls, *args, **kwargs): + # This may seem like an arbitrary step but here we are creating a + # new class that is a base class of itself. 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. + return object.__new__(type(pykat.finesse.kat.__name__, (pykat.finesse.kat,), {}), *args, **kwargs) + def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): - - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) self.scene = None # scene object for GUI self.verbose = True @@ -424,7 +434,30 @@ class kat(object): if kat_file != None: self.loadKatFile(kat_file) - + + def __deepcopy__(self, memo): + """ + 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 + 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]) + + return result + @property def signals(self): return self.__signals @@ -1229,7 +1262,7 @@ class kat(object): # need to parse 2D outputs slightly different as they are effectively 2D matrices # written in linear form x = data[0::(1+self.x2axis.steps),0].squeeze() - y = data[0:(1+self.x2axis.steps),1].squeeze() + y = data[0:(1+self.x2axis.steps),1] # get rows and columns lined up so that we can reshape a single column of all x/y data # into a matrix z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps).squeeze() @@ -1242,7 +1275,7 @@ class kat(object): rows,cols = data.shape x = data[:,0].squeeze() - y = data[:,1:cols].squeeze() + y = data[:,1:cols] return [x, y, hdr] diff --git a/pykat/node_network.py b/pykat/node_network.py index 973d42c..e5db683 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -15,9 +15,18 @@ import pykat.exceptions as pkex from pykat.components import Component, NodeGaussSetter from pykat.detectors import BaseDetector as Detector from pykat.optics.gaussian_beams import beam_param +from copy import deepcopy 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) + def __init__(self, kat): + self.__nodes = {} self.__kat = kat self.__nodeComponents = {} # dictionary of tuples containing which components are connected to a node @@ -25,9 +34,8 @@ class NodeNetwork(object): self.__componentCallback = {} self.__node_id = 1 - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - + + @property def kat(self): return self.__kat -- GitLab