follow up on config file

Hi. Well, not sure anyone is interested in following up

    > on the config file issue, but if so, attached is a
    > complete version (sorry for the big size!). One class and
    > two functions were moved from __init__.py, but besides
    > that it's all new (and thus the non-patch). But at least
    > my quick tests shows it to be backwards compatible
    > (i.e. it can read the .matplotlibrc files, and rc(...)
    > was rewritten to work with the new system).

    > A very quick synopsis: config['text']['color'] = 'r'
    > config['text.color'] = 'g' config['text.c'] = 'b'
    > rc('text', color=(100, 100, 100))

I'm interested. I haven't had time to follow it closely yet, but I do
plan on taking a close look soon. One thing I would like to see is a
syntax like

rc.text.color = 'red'

I think by creating a proper class for the rc instance and text
attribute and overriding setattr and getattr appropriately, you can
achieve this.

FYI, I'll include some code I worked on over holiday investigating
using enthought traits for rc files which does support a syntax like
this. Note the usage of

  rc.lines.color = 'r'

Thanks for chugging along on this. When time permits, I know Fernando
is interested in joining in this discussion because we previously
discussed adopting a python based config file that allowed recursive
includes that might be usable both for ipython and matplotlib,

JDH

import sys, os, re
from traits import *
from matplotlib.cbook import is_string_like
from matplotlib.artist import Artist

doprint = True
flexible_true_trait = Trait(
    True,
    { 'true': True, 't': True, 'yes': True, 'y': True, 'on': True, True: True,
      'false': False, 'f': False, 'no': False, 'n': False, 'off': False, False: False
                               } )
flexible_false_trait = Trait( False, flexible_true_trait )

colors = {
    'c' : '#00bfbf',
    'b' : '#0000ff',
    'g' : '#008000',
    'k' : '#000000',
    'm' : '#bf00bf',
    'r' : '#ff0000',
    'w' : '#ffffff',
    'y' : '#bfbf00',
    'gold' : '#FFD700',
    'peachpuff' : '#FFDAB9',
    'navajowhite' : '#FFDEAD',
    }

def hex2color(s):
    "Convert hex string (like html uses, eg, #efefef) to a r,g,b tuple"
    return tuple([int(n, 16)/255.0 for n in (s[1:3], s[3:5], s[5:7])])

class RGBA(HasTraits):
    # r,g,b,a in the range 0-1 with default color 0,0,0,1 (black)
    r = Range(0., 1., 0.)
    g = Range(0., 1., 0.)
    b = Range(0., 1., 0.)
    a = Range(0., 1., 1.)
    def __init__(self, r=0., g=0., b=0., a=1.):
        self.r = r
        self.g = g
        self.b = b
        self.a = a
    def __repr__(self):
        return 'r,g,b,a = (%1.2f, %1.2f, %1.2f, %1.2f)'%\
               (self.r, self.g, self.b, self.a)

def tuple_to_rgba(ob, name, val):
    tup = [float(x) for x in val]
    if len(tup)==3:
        r,g,b = tup
        return RGBA(r,g,b)
    elif len(tup)==4:
        r,g,b,a = tup
        return RGBA(r,g,b,a)
    else:
        raise ValueError
tuple_to_rgba.info = 'a RGB or RGBA tuple of floats'

def hex_to_rgba(ob, name, val):
    rgx = re.compile('^#[0-9A-Fa-f]{6}$')

    if not is_string_like(val):
        raise TypeError
    if rgx.match(val) is None:
        raise ValueError
    r,g,b = hex2color(val)
    return RGBA(r,g,b,1.0)
hex_to_rgba.info = 'a hex color string'

def colorname_to_rgba(ob, name, val):
    hex = colors[val.lower()]
    r,g,b = hex2color(hex)
    return RGBA(r,g,b,1.0)
colorname_to_rgba.info = 'a named color'

def float_to_rgba(ob, name, val):
    val = float(val)
    return RGBA(val, val, val, 1.)
float_to_rgba.info = 'a grayscale intensity'

Color = Trait(RGBA(), float_to_rgba, colorname_to_rgba, RGBA,
              hex_to_rgba, tuple_to_rgba)

def file_exists(ob, name, val):
    fh = file(val, 'r')
    return val

def path_exists(ob, name, val):
    os.path.exists(val)
linestyles = ('-', '--', '-.', ':', 'steps', 'None')
TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN = range(4)
linemarkers = (None, '.', ',', 'o', '^', 'v', '<', '>', 's',
                  '+', 'x', 'd', 'D', '|', '_', 'h', 'H',
                  'p', '1', '2', '3', '4',
                  TICKLEFT,
                  TICKRIGHT,
                  TICKUP,
                  TICKDOWN,
                  'None'
               )

class LineRC(HasTraits):
    linewidth = Float(0.5)
    linestyle = Trait(*linestyles)
    color = Color
    marker = Trait(*linemarkers)
    markerfacecolor = Color
    markeredgecolor = Color
    markeredgewidth = Float(0.5)
    markersize = Float(6)
    antialiased = flexible_true_trait
    data_clipping = flexible_false_trait

class PatchRC(HasTraits):
    linewidth = Float(1.0)
    facecolor = Color
    edgecolor = Color
    antialiased = flexible_true_trait

timezones = 'UTC', 'US/Central', 'ES/Eastern' # fixme: and many more
backends = ('GTKAgg', 'Cairo', 'FltkAgg', 'GD', 'GDK', 'GTK', 'Agg',
            'GTKCairo', 'Paint', 'PS', 'SVG', 'Template', 'TkAgg',
            'WX')
class RC(HasTraits):
    backend = Trait(*backends)
    numerix = Trait('Numeric', 'numarray')
    interactive = flexible_false_trait
    toolbar = Trait('toolbar2', 'classic', None)
    timezone = Trait(*timezones)
    lines = Trait(LineRC())
    patch = Trait(PatchRC())

rc = RC()
rc.lines.color = 'r'
if doprint:
    print 'RC'
    rc.print_traits()
    print 'RC lines'
    rc.lines.print_traits()
    print 'RC patches'
    rc.patch.print_traits()

class Patch(Artist, HasTraits):
    linewidth = Float(0.5)
    facecolor = Color
    fc = facecolor
    edgecolor = Color
    fill = flexible_true_trait
    def __init__(self,
                 edgecolor=None,
                 facecolor=None,
                 linewidth=None,
                 antialiased = None,
                 fill=1,
                 **kwargs
                 ):
        Artist.__init__(self)

        if edgecolor is None: edgecolor = rc.patch.edgecolor
        if facecolor is None: facecolor = rc.patch.facecolor
        if linewidth is None: linewidth = rc.patch.linewidth
        if antialiased is None: antialiased = rc.patch.antialiased

        self.edgecolor = edgecolor
        self.facecolor = facecolor
        self.linewidth = linewidth
        self.antialiased = antialiased
        self.fill = fill

p = Patch()
p.facecolor = '#bfbf00'
p.edgecolor = 'gold'
p.facecolor = (1,.5,.5,.25)
p.facecolor = 0.25
p.fill = 'f'
print 'p.facecolor', type(p.facecolor), p.facecolor
print 'p.fill', type(p.fill), p.fill
if p.fill_: print 'fill'
else: print 'no fill'
if doprint:
    print
    print 'Patch'
    print_traits()

John Hunter wrote:

Thanks for chugging along on this. When time permits, I know Fernando
is interested in joining in this discussion because we previously
discussed adopting a python based config file that allowed recursive
includes that might be usable both for ipython and matplotlib,

I can't really work on this right now (I'll be fixing the mpl-related %run things first), but I have a generic comment to make. I want ipython to be usable as a standalone package, without traits or any other very fancy things. So I'd like to have a simple module whose job is only to load a config file written in pure python, with the only unusual feature of allowing recursive inclusions with an intelligent path search. This is quite simple to write (if it weren't for recursion/path, it would be a trivial execfile() call).

I would then layer on top of this, perhaps a traits-based system which would allow for example gui editing of parameters. But ipython will always run in a plain terminal without traits or any kind of gui.

I am also not terribly interested in keeping backwards compatibility. The main reason I don't call ipython 1.0 is API changes: I still consider its design to be in flux, so even though I try hard not to break things just for the fun of it, I'm willing to in order to have a clean design for the future. At some point when I implement the new python-based rc system, I'll simply have to tell users to edit their config files to the new format. Not particularly pleasant, but it's a one-time pain. And the time it would take to write and maintain a solid, bulletproof format cross-reader is IMHO better spent on other tasks.

Regards,

f