python dragonfly to recognize similar words - python

I am doing a program with dragon fly using wsr,where it has to analyse a word,any voice matching that word should output 'yes it matches'
If i say 'czechoslovakia' then it must print true even for all the similar matches of this world ,like words for 'circle slovakia, cat on slavia,seko vakia...'
What specific methods,should i use for this?
My program
from dragonfly.all import *
import pythoncom
import time
# Voice command rule combining spoken form and recognition processing.
class ExampleRule(CompoundRule):
spec = "czechoslovakia|circle slovalia|sceko bakia|cat on ania" # Spoken form of command.
def _process_recognition(self, node, extras): # Callback when command is spoken.
print "Voice command spoken."
# Create a grammar which contains and loads the command rule.
grammar = Grammar("example grammar") # Create a grammar to contain the command rule.
grammar.add_rule(ExampleRule()) # Add the command rule to the grammar.
grammar.load() # Load the grammar.
while True:
pythoncom.PumpWaitingMessages()
time.sleep(.1)

There is nothing built into Dragonfly to allow you to do this, but you have some other options.
If you're looking to dynamically generate the spec, you might want
to look at Fuzzy. You could give it a word and use it to generate
other similar sounding words from that word. Then you could create
the spec from them.
Here is the WSR engine class in Dragonfly.
I don't know much about SAPI5, but you might be able to ask it for
alternatives. If you can, you might be able to extend the
Dragonfly GrammarWrapper to expose the alternatives, and then use a
catchall grammar to save all utterances and then filter out what you
want (possibly using Fuzzy).
If you were using Natlink, I would recommend
looking at the results object. As you can see here, the results
object has access to all of Dragon's different hypotheses for what
you said in a given utterance. Just as with my second suggestion,
you could catch everything and then filter what you wanted:
.
from natlinkutils import GrammarBase
class CatchAll(GrammarBase):
# this spec will catch everything
gramSpec = """
<start> exported = {emptyList};
"""
def initialize(self):
self.load(self.gramSpec, allResults=1)
self.activateAll()
def gotResultsObject(self, recogType, resObj):
for x in range(0, 100):
try:
possible_interpretation = resObj.getWords(x)
# do whatever sort of filtering you want here
except Exception:
break
c = CatchAll()
c.initialize()
def unload():
global c
if c:
c.unload()
c = None

Related

Unicode to Kruti Dev 010

How to convert unicode to Kruti Dev 010?
I'm using python speech recognition library to convert speech to text and
displaying it using tkinter Text widget but it's not displaying in Kruti Dev.
# class variable
__thisTextArea = Text(__root)
Font_tuple = font.Font(family='Kruti Dev 010', size=16)
# inside constructor
self.__thisTextArea.config(font = self.Font_tuple, yscrollcommand=self.__thisScrollBar.set)
# inside speech recognition function
self.__thisTextArea.insert(INSERT, text_obtained)
self.__thisTextArea.insert(END,' ')
Also, on saving the file speech recognized text is not saving however typed text is saving.
KrutiDev to Unicode Converter.
I'm not familiar enough with either tkinter or devanagari to tell whether this is at all helpful, but I found another project with a pair of functions which map both ways; is this what you are looking for?
The code at https://github.com/jmcmanu2/python_practice/blob/master/Unicode%20KrutiDev%20converter.py doesn't have an explicit license, so I am not comfortable publishing it here, but I have a slightly updated version for Python 3 in a gist at https://gist.github.com/tripleee/b82a79f5b3e57dc6a487ae45077cdbd3 for the time being. (The original was almost Python 3 already so the tweaking required was minimal, though I ripped out unrelated parts which dealt with Excel files.)
With that code available as an import, does this do what you are asking?
from unicode2krutidev import Unicode_to_KrutiDev
# ...
class something:
__thisTextArea = Text(__root)
Font_tuple = font.Font(family='Kruti Dev 010', size=16)
def __init__(self, ...):
# ...
self.__thisTextArea.config(font = self.Font_tuple, yscrollcommand=self.__thisScrollBar.set)
# ...
def recognize_speech(self, ...):
# ...
text_converted = Unicode_to_KrutiDev(text_obtained)
self.__thisTextArea.insert(INSERT, text_converted)
self.__thisTextArea.insert(END,' ')
# ...
self.save_to_database(text_obtained)

Pymel: How do I close For loop after deleting null groups?

As I continue to study For Loops: I've run into some annoying errors. The problem is the script does exactly what I want it to. It deletes the null groups under the demo joints: but unlike other loops I've made for renaming which can be closed with a transform flag in the cmds.ls command: cmds.listRelatives doesn't allow a transform flag to close out the loop. You run the script by simply clicking Build Examples then hitting Delete Waste Groups
I've tried every flag according to the Maya documentation: but nothing seems to be closing the loop. I dont know if I need another variable, or a combination of some flags: or if I am using the wrong type of wording: but ideally what I would like this script to do is simply close out the loop so I dont get the error Error: No object matches name: curve
'''
import DS_wasteGroup_cleanerDemo
reload (DS_wasteGroup_cleanerDemo)
DS_wasteGroup_cleanerDemo.gui()
'''
import re
import maya.cmds as cmds
import maya.mel as mel
if cmds.window("renameWin", exists =True):
cmds.deleteUI("renameWin", window = True)
myWindow = cmds.window("renameWin",t='DS_wasteGroup_cleanerDemo',w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
def gui():
cmds.button( label="Build Examples", c = buildExamples)
cmds.separator( w=200, h=3)
cmds.button( label="Delete Waste Groups", c = deleteWasteGrp)
cmds.separator( w=200, h=9)
cmds.setParent('..')
cmds.showWindow(myWindow)
def buildExamples(*args):
cmds.group(n='exampleGroup1',world=True,empty=True)
cmds.joint(n='demoJoint1')
cmds.group(n='curve1',world=True,empty=True)
cmds.parent('curve1','demoJoint1')
cmds.joint(n='demoJoint2')
cmds.parent('demoJoint2','exampleGroup1')
cmds.group(n='curve2',world=True,empty=True)
cmds.parent('curve2','demoJoint2')
cmds.joint(n='demoJoint3')
cmds.parent('demoJoint3','exampleGroup1')
cmds.group(n='curve3',world=True,empty=True)
cmds.parent('curve3','demoJoint3')
cmds.joint(n='demoJoint4')
cmds.parent('demoJoint4','exampleGroup1')
cmds.group(n='curve4',world=True,empty=True)
cmds.parent('curve4','demoJoint4')
cmds.joint(n='demoJoint5')
cmds.parent('demoJoint5','exampleGroup1')
cmds.group(n='curve5',world=True,empty=True)
cmds.parent('curve5','demoJoint5')
def deleteWasteGrp(*args):
grpList = cmds.listRelatives('demoJoint*',p=True,f=True)
for name in grpList:
print(grpList)
cmds.delete('curve*')
My apologies if I'm posting simple questions. I do write Python scripts to automate the most tedious tasks in rigging: but my knowledge is only intermediate. I want to learn more python so my scripts arent so clunky and brute forced: as well as the fact that I need them to be more adaptable to various types of characters: so any resources that dumb all this down would also be appreciated. Thank you for your help.
The error is correct, because the very first time the for loop executes, all "curve" obects are deleted, then in the next iteration, the same command does not find any curve objects because they are already deleted. If you place the delete command outside the for loop, the error should disappear.
Honestly I would take a whole different approach as you're hard-coding everything which could easily lead to disaster. When I mean hard-code, I mean you're trying to parent, let's say, "demoJoint2" to an object. This is bad because why are you assuming that "demoJoint2" even exists? If you create an object with a specific name that already exists, Maya will auto-rename the new object, and now you're referencing the wrong one right off the bat! Instead when you create your objects, capture their names in a variable then work with that, otherwise you'll be constantly shooting yourself in the foot.
Here's the same script with an approach I would try instead:
import maya.cmds as cmds
def gui():
if cmds.window("renameWin", exists=True):
cmds.deleteUI("renameWin", window=True)
myWindow = cmds.window("renameWin", t="DS_wasteGroup_cleanerDemo", w=200, h=500, toolbox=True)
column = cmds.columnLayout(adj=True)
cmds.button(label="Build Examples", c=buildExamples)
cmds.separator(w=200, h=3)
cmds.button(label="Delete Waste Groups", c=deleteWasteGrp)
cmds.separator(w=200, h=9)
cmds.setParent("..")
cmds.showWindow(myWindow)
def buildExamples(*args):
root = cmds.group(n="exampleGroup1", world=True, empty=True)
for i in range(5): # Loop to the amount of joints you want to create.
jnt = cmds.createNode("joint", name="demoJoint" + str(i + 1)) # Use `i` to help name the object.
jnt = cmds.parent(jnt, root)[0] # Parenting changes its long name, so recapture the joint in a variable.
crv = cmds.group(n="curve" + str(i + 1), world=True, empty=True) # Create empty group.
cmds.parent(crv, jnt) # Parent curve to joint.
def deleteWasteGrp(*args):
jnts = cmds.ls("demoJoint*", long=True, type="joint") # Get all `demoJoints`.
children = cmds.listRelatives(jnts, f=True, children=True, type="transform") or [] # Get all of their children, and only get transform types.
curves = [obj for obj in children if obj.split("|")[-1].startswith("curve")] # Don't assume we got the right objects. Run a final loop to collect any object that starts with `curve`. Need to use split as we're looping through long names but need to check its short name.
if curves: # `cmds.delete` will error if this list is empty, so don't assume.
cmds.delete(curves) # Delete all curves at once.
gui()
Now I can hit the build button as much as I want with no issues, and delete all the curves when pressing the delete button.
A few more notes:
Notice in buildExamples I'm using a loop to create all the objects instead of reusing redundant code that does the same thing. You could even have a spinbox in your gui that defines how many joints it creates now, where as before it wasn't possible because the count was hard-coded.
cmds.listRelatives does have a way to filter objects by transforms by setting parameter type="transform". In fact you'll see many commands have this same parameter (again start checking docs).
cmds.listRelatives('demoJoint*',p=True,f=True) was grabbing the joint's parent, not its children. The docs clearly state this.
Running cmds.delete('curve*') is going to delete ALL objects with names that start with curve, and since you're running this in a loop it's trying to do this multiple times.
maya.cmds is not pymel. There's a whole separate module called pymel.
If you're unsure with any parts of the code try adding in a print statement to see what it's doing.
I feel like you're going about this whole process a bit wrong, and I would love to elaborate if you're interested, but for now here is a fix for your loop situation:
def deleteWasteGrp(*args):
curveList = cmds.ls('curve*',transforms=True)
try:
cmds.delete(curveList)
print('Deleted the following objects: {}'.format(curveList))
except Exception as e:
cmds.warning('Cleanup failed: {}'.format(e))
The cmds.delete method accepts a list parameter, which in your case is the easiest way to get the job done. Keep in mind that when you delete a parent object, you also delete its children, so depending on your circumstances deleting objects can be order-specific.
Throwing any "likely to fail" calls in a try/except clause is generally a good idea, as it lets you handle the error gracefully. Be careful, however, to not suppress it and just move on -- you at the very least need to alert the user adequately.
Lastly, your buildExamples method will most likely fail if you run it more than once. Because you are addressing objects by string literals (hard coded names) instead of keeping track of their actual names (and full path). You will likely see this error eventually:
# Error: ValueError: file <maya console> line ??: More than one object matches name: demoJoint1 #
Edit: Some elaborations as requested
The commands cmds.group and cmds.joint return a string value indicating the actual name of the object created (in create mode). It's usually a good idea of storing this value in case Maya decides to name your object slightly differently than what you are expecting, usually when there is a naming clash. Eg:
print cmds.group(name='test', world=True, empty=True)
# Returns: test
print cmds.group(name='test', world=True, empty=True)
# Returns: test1
Example of how to capture object names as you create them. I've concatenated your five identical(ish) calls to create joints and curves in this loop:
import maya.cmds as cmds
topGroupName = 'exampleGroup'
actualTopGroupName = None
# Create top level group
actualTopGroupName = cmds.group(n=topGroupName, world=True, empty=True)
# Loop through 5 times and do the following:
for i in range(5):
# PS: hash character in name indicates "next available number"
cmds.select(clear=True)
jnt = cmds.joint(n='demoJoint#')
crv = cmds.group(n='curve#',world=True,empty=True)
cmds.parent(crv, jnt)
cmds.parent(jnt, actualTopGroupName)
Example of how to narrow down which objects to search for with cmds.ls:
topGroupName = 'exampleGroup'
print cmds.ls('|{}*|*|curve*'.format(topGroupName))
# Returns: [u'curve1', u'curve2', u'curve3', u'curve4', u'curve5']
# The string .format() expression is just a dynamic way of writing this:
# |exampleGroup*|*|curve*
Vertical pipes (|) indicate levels in a hierarchy, similar to how slashes (/) work in URLs. And asterisks/wildcards (*) indicate "any character, or none".
Hope this helps you along your way a little bit.

How to reuse completions from PathCompleter in prompt_toolkit

I am creating a REPL tool for my project that (simplified for clarity) either directly executes entered commands or (if a command ".x some/path/to/file" is entered) reads and executes them from file. My question is related to auto-completing the user input (using prompt_toolkit).
I have something like (minimum executable example):
import prompt_toolkit
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.document import Document
from prompt_toolkit.contrib.completers import PathCompleter
class CommandCompleter(Completer):
def __init__(self):
self.path_completer = PathCompleter()
self.commands = [".x", "command1", "command2"]
def get_completions(self, document, complete_event):
if document.text.startswith(".x "):
sub_doc = Document(document.text[3:])
yield from (Completion(cmd.text, -document.cursor_position)
# ???????? ?????????????????????????
for cmd
in self.path_completer.get_completions(sub_doc, complete_event))
# ???????
else:
yield from (Completion(cmd, -document.cursor_position)
for cmd in self.commands
if cmd.startswith(document.text))
if __name__ == "__main__":
while True:
other_args = {}
input = prompt_toolkit.prompt(">>> ", completer=CommandCompleter(), **other_args)
# Do something with input (omitted)
The second if-branch (for commands) works correctly but I don't know how to properly call the PathCompleter.get_completions() method and reconstruct the Completion objects from its result (where the ???'s are) in the first branch. The trick is that I am using the completion only for a part of the input and various sub-stringing, position calculations etc. did not (yet) lead to the satisfactory behaviour (i.e. offering the paths and constructing the correct input line).
I will definitely go on searching but if anyone knows how to rewrite this, it would be very useful.
Note: yield from self.path_completer.get_completions(document, complete_event) would be used if the whole input would be just the path (and this works correctly).
Probably the following should fix it:
sub_doc = Document(document.text[3:])
yield from (Completion(completion.text, completion.start_position, display=completion.display)
for completion
in self.path_completer.get_completions(sub_doc, complete_event))
completion.text contains the text that is going to be inserted;
completion.start_position contains the place where the text is going to be inserted, relative to the cursor position (in this particular example we can take the value from the nested completer).
completion.display is the value displayed in the pop-up menu. (In this case, the whole filename, rather than only the inserted string.
Feel free to open a GitHub issue if you have any more questions.

Is there a way to really pickle compiled regular expressions in python?

I have a python console application that contains 300+ regular expressions. The set of regular expressions is fixed for each release. When users run the app, the entire set of regular expressions will be applied anywhere from once (a very short job) to thousands of times (a long job).
I would like to speed up the shorter jobs by compiling the regular expressions up front, pickle the compiled regular expressions to a file, and then load that file when the application is run.
The python re module is efficient and the regex compilation overhead is quite acceptable for long jobs. For short jobs, however, it is a large proportion of the overall run-time. Some users will want to run many small jobs to fit into their existing workflows. Compiling the regular expressions takes about 80ms. A short job might take 20ms-100ms excluding regular expression compilation. So for short jobs, the overhead can be 100% or more. This is with Python27 under both Windows and Linux.
The regular expressions must be applied with the DOTALL flag, so need to be compiled prior to use. A large compilation cache clearly doesn't help in this instances. As some have pointed out, the default method to serialise the compiled regular expression doesn't actually do much.
The re and sre modules compile the patterns into a little custom language with its own opcodes and some auxiliary data structures (e.g., for charsets used in an expression). The pickle function in re.py takes the easy way out. It is:
def _pickle(p):
return _compile, (p.pattern, p.flags)
copy_reg.pickle(_pattern_type, _pickle, _compile)
I think that a good solution to the problem would be an update to the definition of _pickle in re.py that actually pickled the compiled pattern object. Unfortunately, this goes beyond my python skills. I bet, however, that someone here knows how to do it.
I realise that I am not the first person to ask this question - but perhaps you can be the first person to give an accurate and useful response to it!
Your advice would be greatly appreciated.
OK, this isn't pretty, but it might be what you want. I looked at the sre_compile.py module from Python 2.6, and ripped out a bit of it, chopped it in half, and used the two pieces to pickle and unpickle compiled regexes:
import re, sre_compile, sre_parse, _sre
import cPickle as pickle
# the first half of sre_compile.compile
def raw_compile(p, flags=0):
# internal: convert pattern list to internal format
if sre_compile.isstring(p):
pattern = p
p = sre_parse.parse(p, flags)
else:
pattern = None
code = sre_compile._code(p, flags)
return p, code
# the second half of sre_compile.compile
def build_compiled(pattern, p, flags, code):
# print code
# XXX: <fl> get rid of this limitation!
if p.pattern.groups > 100:
raise AssertionError(
"sorry, but this version only supports 100 named groups"
)
# map in either direction
groupindex = p.pattern.groupdict
indexgroup = [None] * p.pattern.groups
for k, i in groupindex.items():
indexgroup[i] = k
return _sre.compile(
pattern, flags | p.pattern.flags, code,
p.pattern.groups-1,
groupindex, indexgroup
)
def pickle_regexes(regexes):
picklable = []
for r in regexes:
p, code = raw_compile(r, re.DOTALL)
picklable.append((r, p, code))
return pickle.dumps(picklable)
def unpickle_regexes(pkl):
regexes = []
for r, p, code in pickle.loads(pkl):
regexes.append(build_compiled(r, p, re.DOTALL, code))
return regexes
regexes = [
r"^$",
r"a*b+c*d+e*f+",
]
pkl = pickle_regexes(regexes)
print pkl
print unpickle_regexes(pkl)
I don't really know if this works, or if it speeds things up. I know it prints a list of regexes when I try it. It might be very specific to version 2.6, I also don't know that.
As others have mentioned, you can simply pickle the compiled regex. They will pickle and unpickle just fine, and be usable. However, it doesn't look like the pickle actually contains the result of compilation. I suspect you will incur the compilation overhead again when you use the result of the unpickling.
>>> p.dumps(re.compile("a*b+c*"))
"cre\n_compile\np1\n(S'a*b+c*'\np2\nI0\ntRp3\n."
>>> p.dumps(re.compile("a*b+c*x+y*"))
"cre\n_compile\np1\n(S'a*b+c*x+y*'\np2\nI0\ntRp3\n."
In these two tests, you can see the only difference between the two pickles is in the string. Apparently compiled regexes don't pickle the compiled bits, just the string needed to compile it again.
But I'm wondering about your application overall: compiling a regex is a fast operation, how short are your jobs that compiling the regex is significant? One possibility is that you are compiling all 300 regexes, and then only using one for a short job. In that case, don't compile them all up front. The re module is very good at using cached copies of compiled regexes, so you generally don't have to compile them yourself, just use the string form. The re module will lookup the string in a dictionary of compiled regexes, so grabbing the compiled form yourself only saves you a dictionary look up. I may be totally off-base, sorry if so.
Just compile as you go - re module will cache the compiled re's even if you dont. Bump the re._MAXCACHE up to 400 or 500, the short jobs will only compile the re's they need, and the long jobs benefit from a big fat cache of compiled expressions - everybody's happy!
Some observations and musings:
You don't need to compile to get the effect of the re.DOTALL flag (or any other flag)-- all you need to do is insert (?s) at the start of the pattern string ... re.DOTALL -> re.S -> the s in (?s). Do a Ctrl-F search for sux (sic) in the re syntax docs.
80ms seems a very short time, even when multiplied by "many" (how many??) short jobs.
Does each job require a new Python process to be started? If so, isn't 80ms small compared with process startup and shutdown overhead? Otherwise, please explain why it is not possible, when a user wants to run "many" small jobs, to do the re.compiles once per batch of jobs.
In a similar case (where every time some input needs to be run through ALL of the regexes), I had to split the Python script in a master-slave setup using *nix sockets; the first time the script is called, the master —doing all time-expensive regex compilations— starts up and the slave for that and all subsequent invokations exchanges data with the master. The master stays idle maximum N seconds.
In my case, this master/slave setup was found to be faster in all occasions than the straightforward way (many invokations against relatively little data every time; also, it had to be a script because it is called from an external application without any Python bindings). I don't know whether this would apply to your situation.
I had the same problem and instead of patching python's re module I opted to create a long running regex "service" instead. Basic code appended below. Please note: It is not designed to handle multiple clients in parallel, i.e. the server is only available once a client has closed the connection.
server
from multiprocessing.connection import Client
from multiprocessing.connection import Listener
import re
class RegexService(object):
patternsByRegex = None
def __init__(self):
self.patternsByRegex = {}
def processMessage(self, message):
regex = message.get('regex')
result = {"error": None}
if regex == None:
result["error"] = "no regex in message - something is wrong with your client"
return result
text = message.get('text')
pattern = self.patternsByRegex.get(regex)
if pattern == None:
print "compiling previously unseen regex: %s" %(regex)
pattern = re.compile(regex, re.IGNORECASE)
self.patternsByRegex[regex] = pattern
if text == None:
result["error"] = "no match"
return result
match = pattern.match(text)
result["matchgroups"] = None
if match == None:
return result
result["matchgroups"] = match.groups()
return result
workAddress = ('localhost', 6000)
resultAddress = ('localhost', 6001)
listener = Listener(workAddress, authkey='secret password')
service = RegexService()
patterns = {}
while True:
connection = listener.accept()
resultClient = Client(resultAddress, authkey='secret password')
while True:
try:
message = connection.recv()
resultClient.send(service.processMessage(message))
except EOFError:
resultClient.close()
connection.close()
break
listener.close()
testclient
from multiprocessing.connection import Client
from multiprocessing.connection import Listener
workAddress = ('localhost', 6000)
resultAddress = ('localhost', 6001)
regexClient = Client(workAddress, authkey='secret password')
resultListener = Listener(resultAddress, authkey='secret password')
resultConnection = None
def getResult():
global resultConnection
if resultConnection == None:
resultConnection = resultListener.accept()
return resultConnection.recv()
regexClient.send({
"regex": r'.*'
})
print str(getResult())
regexClient.send({
"regex": r'.*',
"text": "blub"
})
print str(getResult())
regexClient.send({
"regex": r'(.*)',
"text": "blub"
})
print str(getResult())
resultConnection.close()
regexClient.close()
output of test client run 2 times
$ python ./regexTest.py
{'error': 'no match'}
{'matchgroups': (), 'error': None}
{'matchgroups': ('blub',), 'error': None}
$ python ./regexTest.py
{'error': 'no match'}
{'matchgroups': (), 'error': None}
{'matchgroups': ('blub',), 'error': None}
output of service process during both test runs
$ python ./regexService.py
compiling previously unseen regex: .*
compiling previously unseen regex: (.*)
As long as you create them on program start, the pyc file will cache them. You don't need to result to pickling.

Python: Networked IDLE/Redo IDLE front-end while using the same back-end?

Is there any existing web app that lets multiple users work with an interactive IDLE type session at once?
Something like:
IDLE 2.6.4
Morgan: >>> letters = list("abcdefg")
Morgan: >>> # now, how would you iterate over letters?
Jack: >>> for char in letters:
print "char %s" % char
char a
char b
char c
char d
char e
char f
char g
Morgan: >>> # nice nice
If not, I would like to create one. Is there some module I can use that simulates an interactive session? I'd want an interface like this:
def class InteractiveSession():
''' An interactive Python session '''
def putLine(line):
''' Evaluates line '''
pass
def outputLines():
''' A list of all lines that have been output by the session '''
pass
def currentVars():
''' A dictionary of currently defined variables and their values '''
pass
(Although that last function would be more of an extra feature.)
To formulate my problem another way: I'd like to create a new front end for IDLE. How can I do this?
UPDATE: Or maybe I can simulate IDLE through eval()?
UPDATE 2: What if I did something like this:
I already have a simple GAE Python chat app set up, that allows users to sign in, make chat rooms, and chat with each other.
Instead of just saving incoming messages to the datastore, I could do something like this:
def putLine(line, user, chat_room):
''' Evaluates line for the session used by chat_room '''
# get the interactive session for this chat room
curr_vars = InteractiveSession.objects.where("chatRoom = %s" % chat_room).get()
result = eval(prepared_line, curr_vars.state, {})
curr_vars.state = curr_globals
curr_vars.lines.append((user, line))
if result:
curr_vars.lines.append(('SELF', result.__str__()))
curr_vars.put()
The InteractiveSession model:
def class InteractiveSession(db.Model):
# a dictionary mapping variables to values
# it looks like GAE doesn't actually have a dictionary field, so what would be best to use here?
state = db.DictionaryProperty()
# a transcript of the session
#
# a list of tuples of the form (user, line_entered)
#
# looks something like:
#
# [('Morgan', '# hello'),
# ('Jack', 'x = []'),
# ('Morgan', 'x.append(1)'),
# ('Jack', 'x'),
# ('SELF', '[1]')]
lines = db.ListProperty()
Could this work, or am I way off/this approach is infeasible/I'm duplicating work when I should use something already built?
UPDATE 3: Also, assuming I get everything else working, I'd like syntax highlighting. Ideally, I'd have some API or service I could use that would parse the code and style it appropriately.
for c in "characters":
would become:
<span class="keyword">for</span> <span class="var">c</span> <span class="keyword">in</span> <span class="string>"characters"</span><span class="punctuation">:</span>
Is there a good existing Python tool to do this?
I could implement something like this pretty quickly in Nevow. Obviously, access would need to be pretty restricted since doing something like this involves allowing access to a Python console to someone via HTTP.
What I'd do is create an Athena widget for the console, that used an instance of a custom subclass of code.InteractiveInterpreter that is common to all users logged in.
UPDATE: Okay, so you have something chat-like in GAE. If you just submit lines to a code.InteractiveInterpreter subclass that looks like this, it should work for you. Note that the interface is pretty similar to the InteractiveSession class you describe:
class SharedConsole(code.InteractiveInterpreter):
def __init__(self):
self.users = []
def write(self, data):
# broadcast output to connected clients here
for user in self.users:
user.addOutput(data)
class ConnectedUser(object):
def __init__(self, sharedConsole):
self.sharedConsole = sharedConsole
sharedConsole.users.append(self) # reference look, should use weak refs
def addOutput(self, data):
pass # do GAE magic to send data to connected client
# this is a hook for submitted code lines; call it from GAE when a user submits code
def gotCommand(self, command):
needsMore = self.sharedConsole.runsource(command)
if needsMore:
pass # tell the client to change the command line to a textarea
# or otherwise add more lines of code to complete the statement
The closest Python interpreter I know of to what you are looking for, in terms of interface, is DreamPie. It has separate input and output areas, much like a chat interface. Also, DreamPie runs all of the code in a subprocess. DreamPie also does completion and syntax coloring, much like IDLE, which means it doesn't just pipe input and output to/from the subprocess -- it has implemented the abstractions which you are looking for.
If you wish to develop a desktop application (not a web-app), I recommend basing your work on DreamPie and just adding the multiple-frontend functionality.
Update: For syntax highlighting (including HTML) see the Pygments project. But that is a completely different question; please ask one question at a time here.
As a proof of concept, you may be able to put something together using sockets and a command-line session.
this is likely possible with the upcoming implimentation of IPython using a 0MQ backend.
I would use ipython and screen. With this method, you would have to create a shared login, but you could both connect to the shared screen session. One downside would be that you would both appear as the same user.

Categories

Resources