예제 #1
0
파일: menu.py 프로젝트: daasara/riba
class Interaction(propertied.Propertied):
    """Base class for user-interaction operations"""
    ALL_DIGITS = '0123456789*#'
    timeout = common.FloatProperty(
        "timeout",
        """Duration to wait for response before repeating message""",
        defaultValue=5,
    )
    maxRepetitions = common.IntegerProperty(
        "maxRepetitions",
        """Maximum number of times to play before failure""",
        defaultValue=5,
    )
    onSuccess = basic.BasicProperty(
        "onSuccess",
        """Optional callback for success with signature method( result, runner )""",
    )
    onFailure = basic.BasicProperty(
        "onFailure",
        """Optional callback for failure with signature method( result, runner )""",
    )
    runnerClass = None

    def __call__(self, agi, *args, **named):
        """Initiate AGI-based interaction with the user"""
        return self.runnerClass(model=self, agi=agi)(*args, **named)
예제 #2
0
class Contact( ContactObject ):
	names = basic.BasicProperty(
		"names", "List of names for the contact",
		boundaries = [
			boundary.ForEach( boundary.Type( Name ))
		],
		defaultFunction = lambda x,y: [],
	)
	phones = basic.BasicProperty(
		"phones", "List of phone numbers for the contact",
		boundaries = [
			boundary.ForEach( boundary.Type( PhoneNumber ))
		],
		defaultFunction = lambda x,y: [],
	)
	emails = basic.BasicProperty(
		"emails", "List of email addresses for the contact",
		boundaries = [
			boundary.ForEach( boundary.Type( EmailAddress ))
		],
		defaultFunction = lambda x,y: [],
	)
	deliveries = basic.BasicProperty(
		"deliveries", "List of delivery addresses for the contact",
		boundaries = [
			boundary.ForEach( boundary.Type( DeliveryAddress ))
		],
		defaultFunction = lambda x,y: [],
	)
예제 #3
0
class TestClass(object):
    col = wxtypes.ColourProperty(
        "col",
        "Colour property",
    )
    pen = wxtypes.PenProperty(
        "pen",
        "Pen property",
    )

    penStyle = basic.BasicProperty(
        "penStyle",
        "Pen style property",
        baseType=pen_module.PenStyle,
    )
    penCap = basic.BasicProperty(
        "penCap",
        "Pen cap property",
        baseType=pen_module.PenCap,
    )
    penJoin = basic.BasicProperty(
        "penJoin",
        "Pen Join property",
        baseType=pen_module.PenJoin,
    )
예제 #4
0
class TestClass(object):
    arg = basic.BasicProperty(
        "arg",
        "Single Argument",
        baseType=callable.Argument,
    )
    args = basic.BasicProperty(
        "args",
        "Multiple Arguments",
        baseType=callable.listof_Arguments,
    )
    call = basic.BasicProperty(
        'call',
        'single callable',
        baseType=callable.Callable,
    )
예제 #5
0
파일: menu.py 프로젝트: russellb/starpy
class Prompt(propertied.Propertied):
    """A Prompt to be read to the user"""
    value = basic.BasicProperty(
        "value", """Filename to be read to the user""",
    )
    def __init__(self, value, **named):
        named['value'] = value
        super(Prompt, self).__init__(**named)
예제 #6
0
파일: menu.py 프로젝트: russellb/starpy
class DateTimePrompt(Prompt):
    """Prompt that reads a date/time as a date"""
    format = basic.BasicProperty(
        "format", """Format in which to read the date to the user""",
        defaultValue = None
    )
    def read(self, agi, escapeDigits):
        """Read the audio prompt to the user"""
        return agi.sayDateTime(self.value, escapeDigits, format=self.format)
예제 #7
0
 def testRepr(self):
     """Test representation code with real objects"""
     property = basic.BasicProperty("this")
     bound = boundary.Type(str)
     client = self
     value = "some value"
     object = boundary.BoundaryError(property, bound, client, value,
                                     "somemessage")
     repr(object)
예제 #8
0
 def testStr(self):
     """Test string-conversion code with real objects"""
     property = basic.BasicProperty("this")
     bound = boundary.Type(str)
     client = self
     value = "some value"
     object = boundary.BoundaryError(property, bound, client, value,
                                     "somemessage")
     str(object)
예제 #9
0
class TestClass(object):
    simple = basic.BasicProperty("simple", "documentation")
    withBound = basic.BasicProperty("withBound",
                                    "documentation",
                                    boundaries=(boundary.Type(str), ))
    withDefaultValue = basic.BasicProperty(
        "withDefaultValue",
        "documentation",
        defaultValue='this',
    )
    withDefaultFunction = basic.BasicProperty(
        "withDefaultFunction",
        "documentation",
        defaultFunction=lambda x, y: [],
    )
    withDefaultValueNoSet = basic.BasicProperty(
        "withDefaultValueNoSet",
        "documentation",
        defaultValue='this',
        setDefaultOnGet=0,
    )
    withDefaultFunctionNoSet = basic.BasicProperty(
        "withDefaultFunctionNoSet",
        "documentation",
        defaultFunction=lambda x, y: [],
        setDefaultOnGet=0,
    )
예제 #10
0
class EnumerationChoice(propertied.Propertied):
    """A particular choice within an enumeration set

	The enumeration choice is a particular choice
	stored within the enumeration set.  Its name
	is used to index the choice within its set, while
	its value is the actual value being enumerated.
	"""
    name = basic.BasicProperty(
        "name",
        """The internal name/key used to identify the choice""",
        baseType=basic_types.String_DT,
    )
    value = basic.BasicProperty(
        "value",
        """The data value associated with this choice""",
    )
    friendlyName = basic.BasicProperty(
        "friendlyName",
        """Friendly name used to describe this choice to users""",
        setDefaultOnGet=0,
        defaultFunction=defaultFriendly,
        baseType=basic_types.String_DT,
    )

    def __repr__(self):
        """Get a code-like representation of this choice"""
        if self.friendlyName != self.name:
            return """%s( name=%r, value=%r, friendlyName=%r)""" % (
                self.__class__.__name__,
                self.name,
                self.value,
                self.friendlyName,
            )
        else:
            return """%s( name=%r, value=%r)""" % (
                self.__class__.__name__,
                self.name,
                self.value,
            )
예제 #11
0
	class ContactObject( propertied.Propertied, Persistence.Persistent):
		"""A basic object within the contact database

		Each object within the database supports some number of note fields
		"""
		notes = basic.BasicProperty(
			"notes", "Notes about the given value",
			boundaries = [
				boundary.Type( list ),
				boundary.ForEach( boundary.Type( unicode )),
			],
			defaultFunction = lambda x,y: [],
		)
예제 #12
0
 def _testError(self, bound, badValue):
     """Test that the error object is properly configured"""
     property = basic.BasicProperty("this")
     client = self
     try:
         bound(badValue, property, client)
     except boundary.BoundaryError, error:
         assert error.property is property, """Improper error attribute %s""" % (
             property, )
         assert error.boundary is bound, """Improper error attribute %s""" % (
             bound, )
         assert error.client is client, """Improper error attribute %s""" % (
             client, )
         assert error.value is badValue, """Improper error attribute %s""" % (
             badValue, )
예제 #13
0
 def testInit(self):
     """Test initialisation of the property objects"""
     basic.BasicProperty("name")
     basic.BasicProperty("name", "documentation")
     basic.BasicProperty(
         "name",
         "documentation",
     )
     basic.BasicProperty("name", "documentation", defaultValue=[1, 2, 3])
     basic.BasicProperty("name",
                         "documentation",
                         defaultFunction=lambda x, y: [])
     basic.BasicProperty("name", "documentation", baseType=str)
예제 #14
0
파일: menu.py 프로젝트: russellb/starpy
class SubMenu(Option):
    """A menu-holding option, just forwards call to the held menu"""
    menu = basic.BasicProperty(
        "menu", """The sub-menu we are presenting to the user""",
    )
    def __call__(self, pressed, parent):
        """Get result from the sub-menu, add ourselves into the result"""
        def onResult(result):
            log.debug("""Child menu %s result: %s""", self.menu, result)
            result.insert(0, (self,pressed))
            return result

        def onFailure(reason):
            """Trap voluntary exit and re-start the parent menu"""
            reason.trap(error.MenuExit)
            log.warn("""Restarting parent menu: %s""", parent)
            return parent.model(parent.agi)

        return self.menu(parent.agi).addCallbacks(onResult, onFailure)
예제 #15
0
파일: menu.py 프로젝트: daasara/riba
class PromptRunner(propertied.Propertied):
    """Prompt formed from list of sub-prompts
	"""
    elements = common.ListProperty(
        "elements",
        """Sub-elements of the prompt to be presented""",
    )
    agi = basic.BasicProperty(
        "agi",
        """The FastAGI instance we're controlling""",
    )
    escapeDigits = common.StringLocaleProperty(
        "escapeDigits",
        """Set of digits which escape from playing the prompt""",
    )
    timeout = common.FloatProperty(
        "timeout",
        """Timeout on data-entry after completed reading""",
    )

    def __call__(self):
        """Return a deferred that chains all of the sub-prompts in order
		
		Returns from the first of the sub-prompts that recevies a selection
		
		returns str(digit) for the key the user pressed
		"""
        return self.onNext(None)

    def onNext(self, result, index=0):
        """Process the next operation"""
        if result is not None:
            return result
        try:
            element = self.elements[index]
        except IndexError, err:
            # okay, do a waitForDigit from timeout seconds...
            return self.agi.waitForDigit(self.timeout).addCallback(
                self.processKey).addCallback(self.processLast)
        else:
예제 #16
0
class UtilApplication(propertied.Propertied):
    """Utility class providing simple application-level operations

    FastAGI entry points are waitForCallOn and handleCallsFor, which allow
    for one-shot and permanant handling of calls for an extension
    (respectively), and agiSpecifier, which is loaded from configuration file
    (as specified in self.configFiles).
    """
    amiSpecifier = basic.BasicProperty(
        "amiSpecifier",
        """AMI connection specifier for the application see AMISpecifier""",
        defaultFunction=lambda prop, client: AMISpecifier())
    agiSpecifier = basic.BasicProperty(
        "agiSpecifier",
        """FastAGI server specifier for the application see AGISpecifier""",
        defaultFunction=lambda prop, client: AGISpecifier())
    extensionWaiters = common.DictionaryProperty(
        "extensionWaiters",
        """Set of deferreds waiting for incoming extensions""",
    )
    extensionHandlers = common.DictionaryProperty(
        "extensionHandlers",
        """Set of permanant callbacks waiting for incoming extensions""",
    )
    configFiles = ('starpy.conf', '~/.starpy.conf')

    def __init__(self):
        """Initialise the application from options in configFile"""
        self.loadConfigurations()

    def loadConfigurations(self):
        parser = self._loadConfigFiles(self.configFiles)
        self._copyPropertiesFrom(parser, 'AMI', self.amiSpecifier)
        self._copyPropertiesFrom(parser, 'FastAGI', self.agiSpecifier)
        return parser

    def _loadConfigFiles(self, configFiles):
        """Load options from configuration files given (if present)"""
        parser = ConfigParser()
        filenames = [
            os.path.abspath(os.path.expandvars(os.path.expanduser(file)))
            for file in configFiles
        ]
        log.info("Possible configuration files:\n\t%s", "\n\t".join(filenames)
                 or None)
        filenames = [file for file in filenames if os.path.isfile(file)]
        log.info("Actual configuration files:\n\t%s", "\n\t".join(filenames)
                 or None)
        parser.read(filenames)
        return parser

    def _copyPropertiesFrom(self, parser, section, client, properties=None):
        """Copy properties from the config-parser's given section into client"""
        if properties is None:
            properties = client.getProperties()
        for property in properties:
            if parser.has_option(section, property.name):
                try:
                    value = parser.get(section, property.name)
                    setattr(client, property.name, value)
                except (TypeError, ValueError, AttributeError,
                        NameError) as err:
                    log('Unable to set property %r of %r to config-file value %r: %s'
                        % (
                            property.name,
                            client,
                            parser.get(section, property.name, 1),
                            err,
                        ))
        return client

    def dispatchIncomingCall(self, agi):
        """Handle an incoming call (dispatch to the appropriate registered handler)"""
        extension = agi.variables['agi_extension']
        log.info("""AGI connection with extension: %r""", extension)
        try:
            df = self.extensionWaiters.pop(extension)
        except KeyError as err:
            try:
                callback = self.extensionHandlers[extension]
            except KeyError as err:
                try:
                    callback = self.extensionHandlers[None]
                except KeyError as err:
                    log.warn("""Unexpected connection to extension %r: %s""",
                             extension, agi.variables)
                    agi.finish()
                    return
            try:
                return callback(agi)
            except Exception as err:
                log.error("""Failure during callback %s for agi %s: %s""",
                          callback, agi.variables, err)
                # XXX return a -1 here
        else:
            if not df.called:
                df.callback(agi)

    def waitForCallOn(self, extension, timeout=15):
        """Wait for an AGI call on extension given

        extension -- string extension for which to wait
        timeout -- duration in seconds to wait before defer.TimeoutError is
            returned to the deferred.

        Note that waiting callback overrides any registered handler; that is,
        if you register one callback with waitForCallOn and another with
        handleCallsFor, the first incoming call will trigger the waitForCallOn
        handler.

        returns deferred returning connected FastAGIProtocol or an error
        """
        extension = str(extension)
        log.info('Waiting for extension %r for %s seconds', extension, timeout)
        df = defer.Deferred()
        self.extensionWaiters[extension] = df

        def onTimeout():
            if not df.called:
                df.errback(
                    defer.TimeoutError(
                        """Timeout waiting for call on extension: %r""" %
                        (extension, )))

        reactor.callLater(timeout, onTimeout)
        return df

    def handleCallsFor(self, extension, callback):
        """Register permanant handler for given extension

        extension -- string extension for which to wait or None to define
            a default handler (that chosen if there is not explicit handler
            or waiter)
        callback -- callback function to be called for each incoming channel
            to the given extension.

        Note that waiting callback overrides any registered handler; that is,
        if you register one callback with waitForCallOn and another with
        handleCallsFor, the first incoming call will trigger the waitForCallOn
        handler.

        returns None
        """
        if extension is not None:
            extension = str(extension)
        self.extensionHandlers[extension] = callback
예제 #17
0
class Survey(propertied.Propertied):
    """Models a single survey to be completed"""
    surveyId = common.IntegerProperty(
        "surveyId",
        """Unique identifier for this survey""",
    )
    owner = basic.BasicProperty(
        "owner",
        """Owner's phone number to which to connect""",
    )
    questions = common.ListProperty(
        "questions",
        """Set of questions which make up the survey""",
    )
    YOU_CURRENTLY_HAVE = 'vm-youhave'
    QUESTIONS_IN_YOUR_SURVEY = 'vm-messages'
    QUESTION_IN_YOUR_SURVEY = 'vm-message'
    TO_LISTEN_TO_SURVEY_QUESTION = 'to-listen-to-it'
    TO_RECORD_A_NEW_SURVEY_QUESTION = 'to-rerecord-it'
    TO_FINISH_SURVEY_SETUP = 'vm-helpexit'

    def setupSurvey(self, agi):
        """AGI application to allow the user to set up the survey

        Screen 1:
            You have # questions.
            To listen to a question, press the number of the question.
            To record a new question, press pound.
            To finish setup, press star.
        """
        seq = fastagi.InSequence()
        seq.append(agi.wait, 2)
        base = """You currently have %s question%s.
        To listen to a question press the number of the question.
        To record a new question, press pound.
        To finish survey setup, press star.
        """ % (
            len(self.questions),
            ['', 's'][len(self.questions) == 1],
        )
        if len(base) != 1:
            base += 's'
        base = " ".join(base.split())
        seq.append(agi.execute, 'Festival', base)
        seq.append(agi.finish, )
        return seq()
        seq.append(agi.streamFile, self.YOU_CURRENTLY_HAVE)
        seq.append(agi.sayNumber, len(self.questions))
        if len(self.questions) == 1:
            seq.append(agi.streamFile, self.QUESTION_IN_YOUR_SURVEY)
        else:
            seq.append(agi.streamFile, self.QUESTIONS_IN_YOUR_SURVEY)
        seq.append(agi.streamFile, self.TO_LISTEN_TO_SURVEY_QUESTION)
        seq.append(agi.streamFile, self.TO_RECORD_A_NEW_SURVEY_QUESTION)
        seq.append(agi.streamFile, self.TO_FINISH_SURVEY_SETUP)
        seq.append(agi.finish, )
        return seq()

    def newQuestionId(self):
        """Return a new, unique, question id"""
        bad = True
        while bad:
            bad = False
            id = random.randint(0, sys.maxint)
            for question in self.questions:
                if id == question.__dict__.get('questionId'):
                    bad = True
        return id
예제 #18
0
class UtilApplication(propertied.Propertied):
    """Utility class providing simple application-level operations
	
	FastAGI entry points are waitForCallOn and handleCallsFor, which allow
	for one-shot and permanant handling of calls for an extension 
	(respectively), and agiSpecifier, which is loaded from configuration file 
	(as specified in self.configFiles).
	"""
    amiSpecifier = basic.BasicProperty(
        "amiSpecifier",
        """AMI connection specifier for the application see AMISpecifier""",
        defaultFunction=lambda prop, client: AMISpecifier())
    agiSpecifier = basic.BasicProperty(
        "agiSpecifier",
        """FastAGI server specifier for the application see AGISpecifier""",
        defaultFunction=lambda prop, client: AGISpecifier())
    extensionWaiters = common.DictionaryProperty(
        "extensionWaiters",
        """Set of deferreds waiting for incoming extensions""",
    )
    extensionHandlers = common.DictionaryProperty(
        "extensionHandlers",
        """Set of permanant callbacks waiting for incoming extensions""",
    )
    configFiles = configFiles = ('starpy.conf', '~/.starpy.conf')

    def __init__(self):
        """Initialise the application from options in configFile"""
        self.loadConfigurations()

    def loadConfigurations(self):
        parser = self._loadConfigFiles(self.configFiles)
        self._copyPropertiesFrom(parser, 'AMI', self.amiSpecifier)
        self._copyPropertiesFrom(parser, 'FastAGI', self.agiSpecifier)
        return parser

    def _loadConfigFiles(self, configFiles):
        """Load options from configuration files given (if present)"""
        parser = ConfigParser()
        filenames = [
            os.path.abspath(os.path.expandvars(os.path.expanduser(file)))
            for file in configFiles
        ]
        log.info("Possible configuration files:\n\t%s", "\n\t".join(filenames)
                 or None)
        filenames = [file for file in filenames if os.path.isfile(file)]
        log.info("Actual configuration files:\n\t%s", "\n\t".join(filenames)
                 or None)
        parser.read(filenames)
        return parser

    def _copyPropertiesFrom(self, parser, section, client, properties=None):
        """Copy properties from the config-parser's given section into client"""
        if properties is None:
            properties = client.getProperties()
        for property in properties:
            if parser.has_option(section, property.name):
                try:
                    value = parser.get(section, property.name)
                    setattr(client, property.name, value)
                except (TypeError, ValueError, AttributeError, NameError), err:
                    log("""Unable to set property %r of %r to config-file value %r: %s"""
                        % (
                            property.name,
                            client,
                            parser.get(section, property.name, 1),
                            err,
                        ))
        return client
예제 #19
0
		defaultFunction = lambda x,y: [],
	)
	deliveries = basic.BasicProperty(
		"deliveries", "List of delivery addresses for the contact",
		boundaries = [
			boundary.ForEach( boundary.Type( DeliveryAddress ))
		],
		defaultFunction = lambda x,y: [],
	)



class OrganisationMember( ContactObject ):
	title = common.StringProperty(
		"title", "The title of this position",
		defaultValue = "",
	)
_members = basic.BasicProperty(
	"members", "The members/contacts in the position",
	boundaries = [
		boundary.ForEach( boundary.Type( (Contact, OrganisationMember) ))
	],
	defaultFunction = lambda x,y: [],
)
OrganisationMember.members = _members

class Organisation( Contact ):
	members = _members

	
예제 #20
0
class Enumeration(propertied.Propertied):
    """A choice from an enumerated set of data values

	This class also operates as the base-type for the
	enumeration properties, via the data-type-definition
	API.
	"""
    dataType = "enumeration"
    ## set must be class-data, not just instance data
    ## should probably be a metaclass property of EnumerationSet type
    set = None
    name = basic.BasicProperty(
        "name",
        """Data-value choice within one of our sets""",
        defaultValue="",
        baseType=unicode,
    )

    def __init__(self, name="", *arguments, **named):
        if not isinstance(name, (str, unicode)):
            name = self.__class__.set.getName(name)
        super(Enumeration, self).__init__(name=name, *arguments, **named)
        if not self.choice():
            raise ValueError("""Name %r is not part of %s""" %
                             (self.name, self.__class__.__name__))

    def choice(self):
        """Get the choice object associated with this value or None"""
        return self.set.get(self.name)

    def value(self):
        """Get the value associated with this choice"""
        choice = self.choice()
        if choice is not None:
            return choice.value
        raise ValueError("""Could not get value for name %r for %s""" %
                         (self.name, self.__class__.__name__))

    def __cmp__(self, other):
        """Compare this value to another value"""
        if isinstance(other, Enumeration):
            return cmp(self.value(), other.value())
        else:
            return cmp(self.value(), other)

    def __repr__(self):
        """Return a code-like representation of this object"""
        return """%s( name=%r)""" % (self.__class__.__name__, self.name)

    def __str__(self):
        """Return the enumeration value as a name"""
        return self.name or self.value()

    ### Data-type-definition API
    def check(cls, value):
        """Check whether value is of cls type, and has the same set"""
        return isinstance(value, cls) and cls.set == value.set

    check = classmethod(check)

    def coerce(cls, value):
        """Coerce a value into an Enumeration value

		Accepted types:
			Enumeration objects
			integers/longs
			([name,name,name],remainder) tuples
			[name,name,name,value] lists (values are |'d together)
		"""
        if cls.check(value):
            return value
        elif isinstance(value, (str, unicode)):
            return cls.parse(value)
        else:
            return cls.fromValue(value)

    coerce = classmethod(coerce)

    def fromValue(cls, value):
        """Create from an integer value"""
        name = cls.set.getName(value)
        if name is None:
            raise ValueError("""Value %r is not part of %s""" %
                             (value, cls.__name__))
        else:
            return cls(name=name)

    fromValue = classmethod(fromValue)

    def parse(cls, value):
        """Create from a string value

		Possible formats:
			"coreName"
			"23"
			"friendlyName"
		"""
        value = value.strip()
        current = cls.set.get(value)
        if current is not None:
            return cls(name=value)
        else:
            return cls.fromValue(value)

    parse = classmethod(parse)

    def allInstances(cls):
        """Return cls instances for each of this class's set"""
        items = [(choice.friendlyName, cls(name=choice.name))
                 for choice in cls.set.values()]
        items.sort()
        items = [v[1] for v in items]
        return items

    allInstances = classmethod(allInstances)
예제 #21
0
파일: menu.py 프로젝트: russellb/starpy
class Runner(propertied.Propertied):
    """User's interaction with a given Interaction-type"""
    agi = basic.BasicProperty(
        "agi", """The AGI instance we use to communicate with the user""",
    )
    def defaultFinalDF(prop, client):
        """Produce the default finalDF with onSuccess/onFailure support"""
        df = defer.Deferred()
        model = client.model
        if hasattr(model, 'onSuccess'):
            log.debug('register onSuccess: %s', model.onSuccess)
            df.addCallback(model.onSuccess, runner=client)
        if hasattr(model, 'onFailure'):
            log.debug('register onFailure: %s', model.onFailure)
            df.addErrback(model.onFailure, runner=client)
        return df
    finalDF = basic.BasicProperty(
        "finalDF", """Final deferred we will callback/errback on success/failure""",
        defaultFunction = defaultFinalDF,
    )
    del defaultFinalDF

    alreadyRepeated = common.IntegerProperty(
        "alreadyRepeated", """Number of times we've repeated the message...""",
        defaultValue = 0,
    )
    model = basic.BasicProperty(
        "model", """The data-model that we are presenting to the user (e.g. Menu)""",
    )

    def returnResult(self, result):
        """Return result of deferred to our original caller"""
        log.debug('returnResult: %s %s', self.model,result)
        if not self.finalDF.called:
            self.finalDF.debug = True
            self.finalDF.callback(result)
        else:
            log.debug('finalDF already called, ignoring %s', result)
        return result

    def returnError(self, reason):
        """Return failure of deferred to our original caller"""
        log.debug('returnError: %s', self.model)
        if not isinstance(reason.value, error.MenuExit):
            log.warn("""Failure during menu: %s""", reason.getTraceback())
        if not self.finalDF.called:
            self.finalDF.debug = True
            self.finalDF.errback(reason)
        else:
            log.debug('finalDF already called, ignoring %s', reason.getTraceback())

    def promptAsRunner(self, prompt):
        """Take set of prompt-compatible objects and produce a PromptRunner for them"""
        realPrompt = []
        for p in prompt:
            if isinstance(p, (str, unicode)):
                p = AudioPrompt(p)
            elif isinstance(p, int):
                p = NumberPrompt(p)
            elif not isinstance(p, Prompt):
                raise TypeError( """Unknown prompt element type on %r: %s"""%(
                    p, p.__class__,
                ))
            realPrompt.append(p)
        return PromptRunner(
            elements = realPrompt,
            escapeDigits = self.escapeDigits,
            agi = self.agi,
            timeout = self.model.timeout,
        )
예제 #22
0
 class PTest(propertied.Propertied):
     value = basic.BasicProperty(
         "value",
         """Testing value""",
         baseType=Test2,
     )
예제 #23
0
class Argument(propertied.Propertied):
    """Representation of a single argument on a callable object"""
    name = common.StringLocaleProperty(
        'name',
        """The argument's name, as a simple string""",
    )
    default = basic.BasicProperty(
        'default',
        """Default-value for the argument, may be NULL/unavailable""",
    )
    baseType = basic.BasicProperty(
        'baseType',
        """Base data-type for the argument, may be NULL/unavailable""",
    )

    def __init__(self, name, default=__NULL__, baseType=__NULL__, **named):
        """Initialize the Callable object

		name -- the argument name
		default -- if provided, will provide the default value
			for the argument
		baseType -- if provided, will allow for type checking
			and coercion of arguments before calling the callable
			object.
		"""
        if default is not __NULL__:
            named["default"] = default
        if baseType is not __NULL__:
            named["baseType"] = baseType
        super(Argument, self).__init__(name=name, **named)

    def __str__(self, ):
        """Create a friendly string representation"""
        fragments = [repr(self.name)]
        if hasattr(self, "default"):
            fragments.append(repr(self.default))
        if hasattr(self, "baseType"):
            fragments.append(repr(self.baseType))
        return """%s(%s)""" % (
            self.__class__.__name__,
            ", ".join(fragments),
        )

    __repr__ = __str__

    def __eq__(self, other):
        """Determine whether other is our equivalent

		returns true if other is of the same class, with
		the same primary attributes
		"""
        if self.__class__ is not other.__class__:
            return 0
        NULL = []
        for nm in ['name', 'default', 'baseType']:
            if hasattr(self, nm) and not hasattr(other, nm):
                return 0
            elif not hasattr(self, nm) and hasattr(other, nm):
                return 0
            elif hasattr(self, nm):
                if getattr(self, nm) != getattr(other, nm):
                    return 0
        return 1

    ### Data-type API
    def check(cls, value):
        """Strict check to see if value is an instance of cls"""
        return isinstance(value, cls)

    check = classmethod(check)

    def coerce(cls, value):
        """Coerce value to a cls instance

		Accepted forms:
			("name",)
			("name",default)
			("name",default,baseType)
			"name"
			{ ** } # passed to the initialiser
		"""
        if cls.check(value):
            return value
        if isinstance(value, (tuple, list)) and value and len(value) < 4:
            items = {}
            for item, name in zip(value, ['name', 'default',
                                          'baseType'][:len(value)]):
                items[name] = item
            return cls(**items)
        elif isinstance(value, str):
            return cls(name=value)
        elif isinstance(value, dict):
            return cls(**value)
        raise TypeError("""Don't know how to convert %r to a %s object""" %
                        (value, cls.__name__))

    coerce = classmethod(coerce)
예제 #24
0
class Callable(propertied.Propertied):
    """Modelling of a callable Python object"""
    name = common.StringProperty(
        'name',
        """The callable object's-name (may be different from underlying object)""",
    )
    implementation = basic.BasicProperty(
        "implementation",
        """The underlying implementation (callable Python object)""",
    )
    arguments = common.ListProperty(
        'arguments',
        """Argument-list for the callable object""",
        baseType=listof_Arguments,
    )
    shortHelp = common.StringProperty(
        'shortHelp',
        """Short help-string suitable for tooltips/status-bars""",
    )
    longHelp = common.StringProperty(
        'longHelp',
        """Longer help-string suitable for context-sensitive help""",
    )
    coerce = common.BooleanProperty(
        "coerce",
        """Whether to coerce arguments if possible""",
        defaultValue=0,
    )

    def __init__(self,
                 implementation,
                 name=__NULL__,
                 arguments=__NULL__,
                 shortHelp=__NULL__,
                 longHelp=__NULL__,
                 **named):
        """Initialize the Callable object

		implementation -- a callable python object
		name -- if provided, will override the given name
		arguments -- if provided, will override calculated arguments
		shortHelp -- short help-string, first line of __doc__ if not given
		longHelp -- long help-string, entire __doc__ string if not given
		"""
        if name is __NULL__:
            name = self._name(implementation)
        if arguments is __NULL__:
            arguments = self._arguments(implementation)
        if shortHelp is __NULL__:
            shortHelp = self._shortHelp(implementation)
        if longHelp is __NULL__:
            longHelp = self._longHelp(implementation)
        super(Callable, self).__init__(implementation=implementation,
                                       name=name,
                                       arguments=arguments,
                                       **named)

    def __str__(self):
        """Return a friendly string representation"""
        return """%s( %s )""" % (self.__class__.__name__, self.implementation)

    def __call__(self, *arguments, **named):
        """Do the actual calling of the callable object"""
        set = {}
        for argument, value in zip(arguments, self.arguments):
            set[argument.name] = (argument, value)
        # XXX potentially there are missing positional arguments!
        if named:
            nameSet = dict([(arg.name, arg) for arg in self.arguments])
            for key, value in named.items():
                if set.has_key(key):
                    raise ValueError(
                        """Redefinition of argument order for argument %s""" %
                        (set.get(key)))
                else:
                    # note that argument may be None
                    set[key] = nameSet.get(key), value
        for key, (argument, value) in set.items():
            if self.coerce and argument and argument.baseType and hasattr(
                    argument.baseType, "coerce"):
                value = argument.baseType.coerce(argument)
            set[key] = value
        # XXX Should keep arguments in order to allow for *args set :(
        return self.implementation(**set)

    def getArgument(self, name):
        """Retieve an argument by name"""
        for argument in self.arguments:
            if argument.name == name:
                return argument
        raise KeyError("""%r object doesn't have a %s argument""" %
                       (self, name))

    def _name(self, value):
        """Try to find a decent name for a callable object"""
        name = "<unknown>"
        for attribute in [
                '__name__', 'name', 'func_name', 'co_name', '__file__',
                "friendlyName"
        ]:
            if hasattr(value, attribute):
                v = getattr(value, attribute)
                if isinstance(v, (str, unicode)):
                    name = v
        if '.' in name:
            return name.split('.')[-1]
        return name

    def _shortHelp(self, value):
        """Try to find the short-docstring for an object"""
        if hasattr(value, '__doc__') and value.__doc__:
            return value.__doc__.split('\n')[0]
        else:
            return ""

    def _longHelp(self, value):
        """Try to find the short-docstring for an object"""
        if hasattr(value, '__doc__') and value.__doc__:
            return value.__doc__
        else:
            return ""

    def _useCall(self, value):
        """Can we use __call__ to call this object?

		returns true if we should be able to use it
		"""
        return (
            # must have __call__
            hasattr(value, '__call__') and (
                # call should be a function or method...
                hasattr(value.__call__, 'im_func')
                or hasattr(value.__call__, 'im_code')))

    def _arguments(self, value):
        """Get a list of arguments for a callable object"""
        if self._useCall(value):
            value = value.__call__
        if hasattr(value, 'im_func'):
            # receiver is a method. Drop the first argument, usually 'self'.
            func = value.im_func
            arguments = inspect.getargspec(func)
            if value.im_self is not None:
                # a bound instance or class method
                arguments = inspect.getargspec(func)
                del arguments[0][0]
            else:
                # an un-bound method
                pass
        elif hasattr(value, 'func_code') or hasattr(value, 'im_code'):
            # receiver is a function.
            func = value
            arguments = inspect.getargspec(func)
        else:
            raise ValueError('unknown reciever type %s %s' %
                             (receiver, type(receiver)))
        names, vararg, varnamed, defaults = arguments
        defaults = defaults or ()
        result = [Argument(name=name) for name in names]
        for name, default in zip(names[-len(defaults):], defaults):
            for item in result:
                if item.name == name:
                    item.default = default
        return result

    def check(cls, value):
        """Strict check to see if value is an instance of cls"""
        return isinstance(value, cls)

    check = classmethod(check)

    def coerce(cls, value):
        """Coerce value to a Callable-object"""
        if cls.check(value):
            return value
        if callable(value):
            return cls(implementation=value, )
        else:
            raise TypeError("Don't know how to convert %r to a %s object" % (
                value,
                cls.__name__,
            ))

    coerce = classmethod(coerce)

    def __eq__(self, other):
        """Determine whether other is our equivalent

		returns true if other is of the same class, with
		the same primary attributes
		"""
        if self.__class__ is not other.__class__:
            return 0
        NULL = []
        for nm in ['name', 'implementation', 'arguments']:
            if hasattr(self, nm) and not hasattr(other, nm):
                return 0
            elif not hasattr(self, nm) and hasattr(other, nm):
                return 0
            elif hasattr(self, nm):
                if getattr(self, nm) != getattr(other, nm):
                    return 0
        return 1
예제 #25
0
파일: menu.py 프로젝트: vitimm144/starpy
class PromptRunner(propertied.Propertied):
    """Prompt formed from list of sub-prompts
    """
    elements = common.ListProperty(
        "elements",
        """Sub-elements of the prompt to be presented""",
    )
    agi = basic.BasicProperty(
        "agi",
        """The FastAGI instance we're controlling""",
    )
    escapeDigits = common.StringLocaleProperty(
        "escapeDigits",
        """Set of digits which escape from playing the prompt""",
    )
    timeout = common.FloatProperty(
        "timeout",
        """Timeout on data-entry after completed reading""",
    )

    def __call__(self):
        """Return a deferred that chains all of the sub-prompts in order

        Returns from the first of the sub-prompts that recevies a selection

        returns str(digit) for the key the user pressed
        """
        return self.onNext(None)

    def onNext(self, result, index=0):
        """Process the next operation"""
        if result is not None:
            return result
        try:
            element = self.elements[index]
        except IndexError as err:
            # okay, do a waitForDigit from timeout seconds...
            return self.agi.waitForDigit(self.timeout).addCallback(
                self.processKey).addCallback(self.processLast)
        else:
            df = element.read(self.agi, self.escapeDigits)
            df.addCallback(self.processKey)
            df.addCallback(self.onNext, index=index + 1)
            return df

    def processKey(self, result):
        """Does the pressed key belong to escapeDigits?"""
        if isinstance(result, tuple):
            # getOption result...
            if result[1] == 0:
                # failure during load of the file...
                log.warn("Apparent failure during load of audio file: %s",
                         self.value)
                result = 0
            else:
                result = result[0]
            if isinstance(result, str):
                if result:
                    result = ord(result)
                else:
                    result = 0
        if result:  # None or 0
            # User pressed a key during the reading...
            key = chr(result)
            if key in self.escapeDigits:
                log.info('Exiting early due to user press of: %r', key)
                return key
            else:
                # we don't warn user in this menu if they press an unrecognised key!
                log.info(
                    'Ignoring user keypress because not in escapeDigits: %r',
                    key)
            # completed reading without any escape digits, continue reading
        return None

    def processLast(self, result):
        if result is None:
            result = ''
        return result