Ticket #58 (closed enhancement: fixed)

Opened 8 months ago

Last modified 2 months ago

Grammar merging

Reported by: dcooper Assigned to: athomas
Priority: major Component: cly
Severity: normal Keywords:
Cc:

Description

The ability to merge grammar tress together would be very useful. An example is where a base grammar is loaded but when the user authenticates they get an extended set of commands by merging a privileged grammar set on top of the original grammar set.

Attachments

Change History

01/04/08 18:53:29 changed by athomas

  • status changed from new to assigned.

This is usually better handled by the valid=<predicate> option which allows you to use custom callbacks to hide branches until the predicate is satisfied.

Still though, it might be useful for loading grammars from multiple sources.

06/10/08 10:13:55 changed by athomas

Preliminary support in r549.

06/10/08 10:48:21 changed by athomas

The problem with the scenario you've portrayed is that grammar merging is a one-way operation, so if you subsequently want to drop privileges you won't be able to prune the Grammar.

A more flexible option might be to subclass Node and implement your own conditional grammar merging:

from cly import *
from getpass import getpass
import sys


class Session(object):
    def __init__(self):
        self.username = None
        self.authenticated = None

    def authenticate(self, password):
        self.authenticated = password == 'foo'
        if self.authenticated:
            print 'Authenticated successfully'
        else:
            print 'Password incorrect'


class Merge(Node):
    def __init__(self, session, other):
        super(Merge, self).__init__()
        self._session = session
        self._other = other

    def selected(self, context, match):
        raise Error('Merge nodes should never be selected')

    def follow(self, context):
        if not self._session.authenticated:
            return []
        return self._other.children(context, follow=True)

    def visible(self, context):
        return self._session.authenticated

    def valid(self, context):
        if not self._session.authenticated:
            return False
        for node in self.follow(context):
            if node.valid(context):
                return True
        return False


def login(context):
    session = context.user_context['session']
    password = getpass()
    session.authenticate(password)


def shutdown():
    print 'Shutting down.'
    sys.exit(0)


session = Session()
privileged = Grammar(
    shutdown=Node(Action(shutdown)),
    )

grammar = Grammar(
    login=Node(Action(login, with_context=True)),
    priv=Merge(session, privileged),
    )

interact(grammar, user_context=locals())

I think this is a more robust solution, so I'll clean up the interface and commit it soon.

06/10/08 21:54:20 changed by athomas

Fixed in r551. Use the Conditional node:

from cly import *
from getpass import getpass
import sys


class Session(object):
    def __init__(self):
        self.username = None
        self.authenticated = None

    def authenticate(self):
        self.authenticated = getpass() == 'foo'
        if self.authenticated:
            print 'Authenticated successfully'
        else:
            print 'Password incorrect'


def shutdown():
    print 'Shutting down.'
    sys.exit(0)

session = Session()

privileged = Conditional(
    lambda c: session.authenticated,
    shutdown=Node(Action(shutdown)),
    )

grammar = Grammar(
    login=Node(Action(session.authenticate)),
    privileged=privileged,
    )

interact(grammar, user_context=locals())

06/11/08 09:11:12 changed by athomas

  • status changed from assigned to closed.
  • resolution set to fixed.

06/29/08 21:24:31 changed by athomas

Grammar.update(grammar) is also useful. It merges the children of grammar into the LHS grammar.


Add/Change #58 (Grammar merging)




Change Properties
Action