EsoErik

Tuesday, June 16, 2015

 

Making IPython "?" magic show the correct doc string for custom descriptors

Suppose that in some directory in sys.path, there is a file, t.py:

class Property:
    def __init__(self, properties, name, default_value=None, change_callback=None, doc=None):
        self.name = name
        self.var_name = '_' + name
        self.default_value = default_value
        self.change_callback = change_callback
        if doc is not None:
            self.__doc__ = doc
        properties.append(self)

    def instantiate(self, obj_instance):
        setattr(obj_instance, self.var_name, self.default_value)

    def __get__(self, obj_instance, _=None):
        if obj_instance is None:
            return self
        return getattr(obj_instance, self.var_name)

    def __set__(self, obj_instance, v):
        if v != getattr(obj_instance, self.var_name):
            if self.change_callback is not None:
                self.change_callback(obj_instance, v)
            setattr(obj_instance, self.var_name, v)

    def __delete__(self, obj_instance):
        setattr(obj_instance, self.name, self.default_value)

class Foo:
    properties = []
    bar = Property(
        properties,
        'bar',
        change_callback=lambda _, v: print("A Foo's bar changed to {}.".format(v)),
        doc='The bar property of Foo: this is it.  Its default value is None.\n'
            'Doing "del Foo_instance.bar" is equivalent to "Foo_instance.bar = None".')
    bif = Property(
        properties,
        'bif',
        42,
        doc='bif property.  "del Foo_instance.bif" reverts bif to its default value of 42.')

    def __init__(self):
        for property in self.properties:
            property.instantiate(self)

t.py can then be used from IPython.  For example:

$ ipython
Python 3.4.3 (default, May 28 2015, 17:06:52)
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0-dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from t import Foo

In [2]: f = Foo()

In [3]: f.bar

In [4]: f.bar?
Type:        NoneType
String form: None
Docstring:  

In [5]: f.bif
Out[5]: 42

In [6]: f.bif?
Type:        int
String form: 42
Docstring:
int(x=0) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4


IPython's "?" magic is not seeing our descriptor's doc strings.  It does recognize doc strings for run-of-the-mill properties:

In [1]: class C:
   ...:     def get_baz(self):
   ...:         return self.baz
   ...:     baz = property(get_baz, doc='a baz property')
   ...:    

In [2]: c=C()

In [3]: c.baz?
Type:        property
String form:
Docstring:   a baz property


What's the difference?  This is the difference.  IPython treats descriptors that are (or are derived from) "property" specially.  What happens if we make our descriptor inherit from property, then?  Let's try it:


class Property(property):
    def __init__(self, properties, name, default_value=None, change_callback=None, doc=None):
        self.name = name
        self.var_name = '_' + name
        self.default_value = default_value
        self.change_callback = change_callback
        if doc is not None:
            self.__doc__ = doc
        properties.append(self)

    def instantiate(self, obj_instance):
        setattr(obj_instance, self.var_name, self.default_value)

    def __get__(self, obj_instance, _=None):
        if obj_instance is None:
            return self
        return getattr(obj_instance, self.var_name)

    def __set__(self, obj_instance, v):
        if v != getattr(obj_instance, self.var_name):
            if self.change_callback is not None:
                self.change_callback(obj_instance, v)
            setattr(obj_instance, self.var_name, v)

    def __delete__(self, obj_instance):
        setattr(obj_instance, self.name, self.default_value)

class Foo:
    properties = []
    bar = Property(
        properties,
        'bar',
        change_callback=lambda _, v: print("A Foo's bar changed to {}.".format(v)),
        doc='The bar property of Foo: this is it.  Its default value is None.\n'
            'Doing "del Foo_instance.bar" is equivalent to "Foo_instance.bar = None".')
    bif = Property(
        properties,
        'bif',
        42,
        doc='bif property.  "del Foo_instance.bif" reverts bif to its default value of 42.')

    def __init__(self):
        for property in self.properties:
            property.instantiate(self)

$ ipython 
Python 3.4.3 (default, May 28 2015, 17:06:52)
Type "copyright", "credits" or "license" for more information.

IPython 4.0.0-dev -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from t import Foo

In [2]: f = Foo()

In [3]: f.bar

In [4]: f.bar?
Type:        Property
String form:
File:        ~/t.py
Docstring:
The bar property of Foo: this is it.  Its default value is None.
Doing "del Foo_instance.bar" is equivalent to "Foo_instance.bar = None".

In [5]: f.bif
Out[5]: 42

In [6]: f.bif?
Type:        Property
String form:
File:        ~/t.py
Docstring:   bif property.  "del Foo_instance.bif" reverts bif to its default value of 42.


Our doc strings are seen, and things seem to work.  If any reason exists why I should not derive my descriptors from property, I am not aware of it!

Thursday, June 4, 2015

 

xorg.conf for 60Hz 4k Dell UP3214Q with the NVidia Proprietary Driver

It seems that Displayport 1.2 MST will never be properly supported on Linux, but it is possible to make it work perfectly with the some TwinView + Xinerama hackery.  I use the following with a Quadro K6000 and UP3214Q:

Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0"
    Option         "Xinerama" "1"
EndSection

Section "Files"
EndSection

Section "Monitor"
    Identifier     "Monitor0"
    VendorName     "Unknown"
    ModelName      "Unknown"
    HorizSync       24.0 - 140.0
    VertRefresh     20.0 - 77.0
    Option         "DPMS"
    Option         "UseEDIDDpi" "FALSE"
    Option         "DPI" "120x120"
EndSection

Section "Device"
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
EndSection

Section "Screen"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    Option         "metamodes" "DFP-4.9: 1920x2160 { ViewPortIn=1920x2160 },   DFP-4.8: 1920x2160 { ViewPortIn=1920x2160 }; DFP-4.9: 1920x2160 { ViewPortIn=1280x1440 },   DFP-4.8: 1920x2160 { ViewPortIn=1280x1440 }; DFP-4.9: 1920x2160 { ViewPortIn=960x1080 },    DFP-4.8: 1920x2160 { ViewPortIn=960x1080}; DFP-4.9: 1280x1440 { ViewPortIn=1280x1440 },   DFP-4.8: 1280x1440 { ViewPortIn=1280x1440 }; DFP-4.9: 960x1080 { ViewPortIn=960x1080 },     DFP-4.8: 960x1080 { ViewPortIn=960x1080}; DFP-4.9: 2560x1440 { ViewPortIn=2560x1440 },   NULL; DFP-4.9: 1920x1080 { ViewPortIn=1920x1080 },   NULL; DFP-4.9: 1920x1080_120 { ViewPortIn=1920x1080 }, NULL"
    Option     "TwinView"
    Option     "NoTwinViewXineramaInfo" "1"
    Option     "TwinViewXineramaInfoOverride" "3840x2160+0+0"
    Option     "ConnectedMonitor" "DFP-4.9, DFP-4.8"
    Option     "TwinViewOrientation"      "LeftOf"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection

Archives

July 2009   August 2009   September 2009   October 2009   November 2009   December 2009   January 2010   September 2010   December 2010   January 2011   February 2011   April 2011   June 2011   August 2011   February 2012   June 2012   July 2012   August 2012   October 2012   November 2012   January 2014   April 2014   June 2014   August 2014   September 2014   October 2014   January 2015   March 2015   April 2015   June 2015   November 2015   December 2015   January 2016   June 2016   August 2016   January 2017   March 2017   April 2018   April 2019   June 2019   January 2020  

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]