EPICS Controls 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  2020  2021  2022  2023  2024  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  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: RE: subclassing Pyepics epics.motor doesn't work as expected
From: Terry Cornall <[email protected]>
To: Matt Newville <[email protected]>
Cc: "[email protected] \([email protected]\)" <[email protected]>
Date: Sun, 1 Jun 2014 23:43:42 +0000

Thanks Mark, I figured it would be to do with this dynamic behaviour. I got around it by ‘side-classing’ rather than subclassing. That is, made a new class that had an epics.Motor object as an attribute rather than inheriting from it.

 

Terry

 

From: [email protected] [mailto:[email protected]] On Behalf Of Matt Newville
Sent: Thursday, 29 May 2014 12:47 PM
To: Terry Cornall
Cc: [email protected] ([email protected])
Subject: Re: subclassing Pyepics epics.motor doesn't work as expected

 

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 <[email protected]> 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


References:
subclassing Pyepics epics.motor doesn't work as expected Terry Cornall
Re: subclassing Pyepics epics.motor doesn't work as expected Matt Newville

Navigate by Date:
Prev: Re: Tech-Talk register Ralph Lange
Next: TD20 classic : Turbo molecular pump controller "최숙"
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  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: subclassing Pyepics epics.motor doesn't work as expected Matt Newville
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  2020  2021  2022  2023  2024 
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 ·