Skip to content

Commit 7b4a8c2

Browse files
committed
Add more API functions, prep 1.12.1 for release
1 parent 6703b04 commit 7b4a8c2

3 files changed

Lines changed: 196 additions & 2 deletions

File tree

Changes.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
Revision history for the Python package RiveScript.
22

3+
1.12.1 May 31 2016
4+
- Added API functions: `get_global(name)`, `get_variable(name)`, and
5+
`set_uservars(user || dict[, dict])` -- the latter is for setting many
6+
variables for a user at once, or for setting many variables for many users.
7+
Refer to the API documentation for details.
8+
39
1.12.0 May 10 2016
410
- Add support for nested arrays, like `!array colors = @rgb white black`
511
(PR #22)

rivescript/rivescript.py

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,7 @@ def call(self, rs, name, fields):
13711371
# name = the name of the object being called
13721372
# fields = array of arguments passed to the object
13731373
return reply
1374-
"""
1374+
"""
13751375

13761376
# Allow them to delete a handler too.
13771377
if obj is None:
@@ -1417,6 +1417,14 @@ def set_global(self, name, value):
14171417
del self._gvars[name]
14181418
self._gvars[name] = value
14191419

1420+
def get_global(self, name):
1421+
"""Retrieve the current value of a global variable.
1422+
1423+
:param str name: The name of the variable to get.
1424+
:return str: The value of the variable or ``"undefined"``.
1425+
"""
1426+
return self._gvars.get(name, "undefined")
1427+
14201428
def set_variable(self, name, value):
14211429
"""Set a bot variable.
14221430
@@ -1432,6 +1440,14 @@ def set_variable(self, name, value):
14321440
del self._bvars[name]
14331441
self._bvars[name] = value
14341442

1443+
def get_variable(self, name):
1444+
"""Retrieve the current value of a bot variable.
1445+
1446+
:param str name: The name of the variable to get.
1447+
:return str: The value of the variable or ``"undefined"``.
1448+
"""
1449+
return self._bvars.get(name, "undefined")
1450+
14351451
def set_substitution(self, what, rep):
14361452
"""Set a substitution.
14371453
@@ -1477,6 +1493,75 @@ def set_uservar(self, user, name, value):
14771493

14781494
self._users[user][name] = value
14791495

1496+
def set_uservars(self, user, data=None):
1497+
"""Set many variables for a user, or set many variables for many users.
1498+
1499+
This function can be called in two ways::
1500+
1501+
# Set a dict of variables for a single user.
1502+
rs.set_uservars(username, vars)
1503+
1504+
# Set a nested dict of variables for many users.
1505+
rs.set_uservars(many_vars)
1506+
1507+
In the first syntax, ``vars`` is a simple dict of key/value string
1508+
pairs. In the second syntax, ``many_vars`` is a structure like this::
1509+
1510+
{
1511+
"username1": {
1512+
"key": "value",
1513+
},
1514+
"username2": {
1515+
"key": "value",
1516+
},
1517+
}
1518+
1519+
This way you can export *all* user variables via ``get_uservars()``
1520+
and then re-import them all at once, instead of setting them once per
1521+
user.
1522+
1523+
:param optional str user: The user ID to set many variables for.
1524+
Skip this parameter to set many variables for many users instead.
1525+
:param dict data: The dictionary of key/value pairs for user variables,
1526+
or else a dict of dicts mapping usernames to key/value pairs.
1527+
1528+
This may raise a ``TypeError`` exception if you pass it invalid data
1529+
types. Note that only the standard ``dict`` type is accepted, but not
1530+
variants like ``OrderedDict``, so if you have a dict-like type you
1531+
should cast it to ``dict`` first.
1532+
"""
1533+
1534+
# Check the parameters to see how we're being used.
1535+
if type(user) is dict and data is None:
1536+
# Setting many variables for many users.
1537+
for uid, uservars in user.items():
1538+
if type(uservars) is not dict:
1539+
raise TypeError(
1540+
"In set_uservars(many_vars) syntax, the many_vars dict "
1541+
"must be in the format of `many_vars['username'] = "
1542+
"dict(key=value)`, but the contents of many_vars['{}']"
1543+
" is not a dict.".format(uid)
1544+
)
1545+
1546+
if not uid in self._users:
1547+
self._users[uid] = dict(topic="random")
1548+
1549+
for key, value in uservars.items():
1550+
self._users[uid][key] = value
1551+
1552+
elif type(user) in [text_type] and type(data) is dict:
1553+
# Setting variables for a single user.
1554+
for key, value in data.items():
1555+
self._users[user][key] = value
1556+
1557+
else:
1558+
raise TypeError(
1559+
"set_uservars() may only be called with types (str, dict) or "
1560+
"(dict<str, dict>) but you called it with types ({}, {})".format(
1561+
type(user), type(data),
1562+
),
1563+
)
1564+
14801565
def get_uservar(self, user, name):
14811566
"""Get a variable about a user.
14821567
@@ -1899,7 +1984,7 @@ def _getreply(self, user, msg, context='normal', step=0, ignore_object_errors=Tr
18991984
for trig in self._sorted["topics"][topic]:
19001985
# Process the triggers.
19011986
regexp = self._reply_regexp(user, trig)
1902-
self._say("Try to match %r against %r (%r)" % (msg, trig, regexp))
1987+
self._say("Try to match %r against %r (%r)" % (msg, trig, regexp.pattern))
19031988

19041989
# Python's regular expression engine is slow. Try a verbatim
19051990
# match if this is an atomic trigger.

tests/test_rivescript.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import re
77
import unittest
8+
from collections import OrderedDict
89

910
from rivescript.rivescript import RS_ERR_MATCH
1011

@@ -90,12 +91,22 @@ def test_bot_variables(self):
9091
9192
+ happy birthday
9293
- <bot age=6>Thanks!
94+
95+
+ who is your master
96+
- My master is <bot master>.
9397
""")
98+
self.rs.set_variable("master", "kirsle")
99+
94100
self.reply("What is your name?", "My name is Aiden.")
95101
self.reply("How old are you?", "I am 5.")
96102
self.reply("What are you?", "I'm undefined.")
97103
self.reply("Happy birthday!", "Thanks!")
98104
self.reply("How old are you?", "I am 6.")
105+
self.reply("Who is your master?", "My master is kirsle.")
106+
107+
self.assertEqual(self.rs.get_variable("age"), "6")
108+
self.assertEqual(self.rs.get_variable("master"), "kirsle")
109+
self.assertEqual(self.rs.get_variable("fake"), "undefined")
99110

100111

101112
def test_global_variables(self):
@@ -107,10 +118,20 @@ def test_global_variables(self):
107118
108119
+ set debug mode *
109120
- <env debug=<star>>Switched to <star>.
121+
122+
+ are you testing
123+
- Testing: <env testing>
110124
""")
125+
self.rs.set_global("testing", "true")
126+
111127
self.reply("Debug mode.", "Debug mode is: false")
112128
self.reply("Set debug mode true", "Switched to true.")
113129
self.reply("Debug mode?", "Debug mode is: true")
130+
self.reply("Are you testing?", "Testing: true")
131+
132+
self.assertEqual(self.rs.get_global("debug"), "true")
133+
self.assertEqual(self.rs.get_global("testing"), "true")
134+
self.assertEqual(self.rs.get_global("fake"), "undefined")
114135

115136

116137
class SubstitutionTests(RiveScriptTestCase):
@@ -680,6 +701,88 @@ def test_concat(self):
680701
self.reply("test concat second file", "Helloworld!")
681702

682703

704+
class APITest(RiveScriptTestCase):
705+
"""Miscellaneous API tests."""
706+
707+
def test_set_uservars(self):
708+
self.new("""
709+
+ who am i
710+
- You are <get name>, seeker!
711+
712+
+ how old am i
713+
- You are <get age> years old.
714+
""")
715+
716+
# Test the base case, with no vars set.
717+
self.reply("Who am I?", "You are undefined, seeker!")
718+
self.reply("How old am I?", "You are undefined years old.")
719+
720+
# Set setting one variable at a time.
721+
self.rs.set_uservar("localuser", "name", "Alice")
722+
self.rs.set_uservar("localuser", "age", "10")
723+
self.reply("Who am I?", "You are Alice, seeker!")
724+
self.reply("How old am I?", "You are 10 years old.")
725+
726+
# Test setting a dict of variables for one user.
727+
self.rs.set_uservars("localuser", {
728+
"name": "Eliza",
729+
"age": "20",
730+
})
731+
self.reply("Who am I?", "You are Eliza, seeker!")
732+
self.reply("How old am I?", "You are 20 years old.")
733+
734+
# Test setting a partial dict which only updates named keys.
735+
self.rs.set_uservars("localuser", dict(name="UltraHAL"))
736+
self.reply("Who am I?", "You are UltraHAL, seeker!")
737+
self.reply("How old am I?", "You are 20 years old.")
738+
739+
# Test setting a dict of many users to many keys.
740+
self.rs.set_uservars({
741+
"localuser": {
742+
"age": "22",
743+
},
744+
"testuser": {
745+
"name": "Bob",
746+
}
747+
})
748+
self.reply("Who am I?", "You are UltraHAL, seeker!")
749+
self.reply("How old am I?", "You are 22 years old.")
750+
self.assertEqual(self.rs.get_uservar("testuser", "name"), "Bob")
751+
self.assertEqual(self.rs.get_uservar("testuser", "age"), "undefined")
752+
753+
# Non-existing users return None, not "undefined"
754+
self.assertEqual(self.rs.get_uservar("fake", "name"), None)
755+
756+
# Test calling with (str, None)
757+
with self.assertRaises(TypeError):
758+
self.rs.set_uservars("alice")
759+
760+
# Test calling with (str, str)
761+
with self.assertRaises(TypeError):
762+
self.rs.set_uservars("alice", "name")
763+
764+
# Test calling with (dict, dict)
765+
with self.assertRaises(TypeError):
766+
self.rs.set_uservars({"localuser": "hi"}, {"name": "Alice"})
767+
768+
# Test calling it in many-users mode, where one of the users isn't
769+
# a dict.
770+
with self.assertRaises(TypeError):
771+
self.rs.set_uservars({
772+
"localuser": {
773+
"name": "Mary",
774+
},
775+
"testuser": "not a dict",
776+
})
777+
778+
# Test calling with dict-like objects.
779+
with self.assertRaises(TypeError):
780+
self.rs.set_uservars("localuser", OrderedDict(name="Alice"))
781+
782+
# But dict-like objects can be cast as dicts.
783+
testdict = OrderedDict(name="Joe")
784+
self.assertEqual(self.rs.set_uservars("localuser", dict(testdict)), None)
785+
683786
class UnicodeTest(RiveScriptTestCase):
684787
"""UTF-8 Tests."""
685788

0 commit comments

Comments
 (0)