Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019 
<== Date ==> <== Thread ==>

Subject: Re: subclassing Pyepics epics.motor doesn't work as expected
From: Matt Newville <newville@cars.uchicago.edu>
To: Terry Cornall <Terry.Cornall@synchrotron.org.au>
Cc: "tech-talk@aps.anl.gov \(tech-talk@aps.anl.gov\)" <tech-talk@aps.anl.gov>
Date: Wed, 28 May 2014 21:46:33 -0500
Hi Terry,

Sorry for the trouble.  The Device classes in pyepics, including the Motor class, are a little unusual in that they override
__getattr__() and __setattr__() for getting/setting instance attributes that are internally mapped to fields of the appropriate device.   That is, one can do

    >>> from epics import Motor
    >>> m1  = Motor('XXX:m1')
    >>> print m1.VAL   # gets VAL field
    1.000
    >>> m1.VAL = 2.0   # puts to VAL field
    >>> print m1.RBV
    2.000

Since there are many fields in the Motor record, many of which aren't needed for most use cases, a Motor object does not try to connect to all fields at initialization but instead trie to connect to a particular field only when necessary (ie, as late as possible).   Currently this means that __getattr__() and __setattr__() will treat any unknown attribute as a potential PV, so that

   >>> print m1.FOO

will hang for a while, trying to connect to 'XXX:m1.FOO', eventually returning None.

Right now, the list of "attributes to not convert to PVs" is hardwired into the Motor class (and other Devices too), and I don't think it can be easily altered by a subclass.  At least, I don't see an easy way to do that.

It would certainly be reasonable to change this behavior.  I think it might be preferable to have an explicit list of attributes that will be converted to PVs rather than trying to convert every unknown attribute to a PV.  That would better allow subclassing, and avoid silent hanging on typos. This might take a little effort, but is probably worth it.

--Matt



On Wed, May 28, 2014 at 7:33 PM, Terry Cornall <Terry.Cornall@synchrotron.org.au> wrote:

Hi All.

I’ve been trying to subclass the Motor class in Pyepics, so I can add my own variables and methods to it, and I’ve run into problems.

Adding new methods seems to work just fine, but I find a strange problem when I try to add new ‘instance variables’ to my subclass.

They all end up having value ‘None’ if I add them after doing the __init__ of the base Motor class. Worse, if I try to define
them before I call the epics.Motor.__init__(), I get an exception from motor.py. This is not normal Python behaviour, as far as I know, and I’ve added code to test that I can subclass this way with a ‘simple’ python class, as shown below.

Any ideas what I am doing wrong, or why epics.Motor can’t be subclassed in this way?

Any suggestions as to how I can subclass it and add my own variables, or why I shouldn’t?

I suppose an alternative would be to make a class that contains an epics.Motor object rather than subclass it.

Thanks,  

Terry

 

import epics  #get the Motor class (and everything else) from Pyepics

 

class myBaseClass():   #make a simple base class so I can test that subclassing works as expected

    def __init__(self,*args,**kwargs):

        self.baseVar=kwargs.pop('mssg')  #have some variable just so we aren't empty

 

class myClass(myBaseClass):     #subclass myBaseClass

    def __init__(self,*args,**kwargs):

        self.before_init_base_instance_variable=1               #add an instance variable before I init base class. This works

        myBaseClass.__init__(self,*args,**kwargs)            #init the base class

        self.new_instance_variable=1000                        #here’s a new instance variable that I want in my subclass

        self.another_new_instance_variable=2000      #here’s another new instance variable that I want in my subclass

 

class myMotor(epics.Motor):     #subclass epics.Motor

    def __init__(self,*args,**kwargs):

    #    self.before_init_instance_variable=1                  #This would get an exception from motor.py, so I comment it out

        epics.Motor.__init__(self,*args,**kwargs)          #call the epics.Motor ‘s __init__ to set it up

        self.new_instance_variable=1000                        #here’s a new instance variable that I want in my subclass

        self.another_new_instance_variable=2000      #here’s a new instance variable that I want in my subclass

 

c=myClass(mssg='fred')

print c.before_init_base_instance_variable                   #This works.

print c.baseVar                                               #print the variable in the myBaseClass just to show it worked

print c.new_instance_variable                       #access the variable I added. It works

print c.another_new_instance_variable            #access the second variable I added. It works

c.new_instance_variable=3000

print c.new_instance_variable                    #see if writing to it worked. It does

 

m=myMotor('SR10BM01DMC05:MTR01')            #create an instance of my subclass, based on epics.Motor

# print m.before_init_base_instance_variable              #Just trying to define this causes an exception so it’s commented out

print m.new_instance_variable                                      #try to access the variable I added, get 'None'

print m.another_new_instance_variable                     #try to access the second variable I added, get 'None'

m.new_instance_variable=3000

print m.new_instance_variable                            #see if writing to it worked. Get 'None'

 

This is what I get when I run the code.

1                  #this is from the instance variable I added before calling myBaseClass.__init__ and it works

Fred           #this is the variable in the the myBaseClass and it is initialised as expected.

1000       #The next three are instance variables I added to my sublass based on myBaseClass

2000      #and they work as expected

3000       #This is the instance variable I wrote to and it worked.

               #The next three are from the instance variables I added to the subclass based on epics.Motor

None    #should have been 1000

None    #should have been 2000

None    #should have been 3000




--
--Matt Newville <newville at cars.uchicago.edu> 630-252-0431

Replies:
RE: subclassing Pyepics epics.motor doesn't work as expected Terry Cornall
References:
subclassing Pyepics epics.motor doesn't work as expected Terry Cornall

Navigate by Date:
Prev: subclassing Pyepics epics.motor doesn't work as expected Terry Cornall
Next: Re: subclassing Pyepics epics.motor doesn't work as expected Vigder, Mark
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019 
Navigate by Thread:
Prev: subclassing Pyepics epics.motor doesn't work as expected Terry Cornall
Next: RE: subclassing Pyepics epics.motor doesn't work as expected Terry Cornall
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  <20142015  2016  2017  2018  2019 
ANJ, 17 Dec 2015 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·