How do I iterate through all Gtk children in PyGtk recursively? - pygtk

I would like to get a recursive list of all Gtk children object of the main window with pygtk. How do I do that?

Noting these:
Python GTK+ widget name
Python recursion and return statements
... here is a function, which is a port of a PHP one from Getting a descendant (child) widget by name | PHP-GTK Community:
# http://cdn.php-gtk.eu/cdn/farfuture/riUt0TzlozMVQuwGBNNJsaPujRQ4uIYXc8SWdgbgiYY/mtime:1368022411/sites/php-gtk.eu/files/gtk-php-get-child-widget-by-name.php__0.txt
# note get_name() vs gtk.Buildable.get_name(): https://stackoverflow.com/questions/3489520/python-gtk-widget-name
def get_descendant(widget, child_name, level, doPrint=False):
if widget is not None:
if doPrint: print("-"*level + gtk.Buildable.get_name(widget) + " :: " + widget.get_name())
else:
if doPrint: print("-"*level + "None")
return None
#/*** If it is what we are looking for ***/
if(gtk.Buildable.get_name(widget) == child_name): # not widget.get_name() !
return widget;
#/*** If this widget has one child only search its child ***/
if (hasattr(widget, 'get_child') and callable(getattr(widget, 'get_child')) and child_name != ""):
child = widget.get_child()
if child is not None:
return get_descendant(child, child_name,level+1,doPrint)
# /*** Ity might have many children, so search them ***/
elif (hasattr(widget, 'get_children') and callable(getattr(widget, 'get_children')) and child_name !=""):
children = widget.get_children()
# /*** For each child ***/
found = None
for child in children:
if child is not None:
found = get_descendant(child, child_name,level+1,doPrint) # //search the child
if found: return found
if (window):
window.connect("destroy", gtk.main_quit)
#pprint(inspect.getmembers(window.get_children()[0]))
print "E: " + str( get_descendant(window, "nofind", level=0, doPrint=True) )
Usage:
print "E: " + str( get_descendant(window, "nofind", level=0, doPrint=True) )
# output:
# window1 :: GtkWindow
# -scrolledwindow1 :: GtkScrolledWindow
# --viewport1 :: GtkViewport
# ---vbox1 :: GtkVBox
# ----handlebox1 :: GtkHandleBox
# -----drawingarea1 :: GtkDrawingArea
# ----handlebox2 :: GtkHandleBox
# ----handlebox3 :: GtkHandleBox
# E: None
print "E: " + str( get_descendant(window, "viewport1", level=0, doPrint=True) )
# output:
# window1 :: GtkWindow
# -scrolledwindow1 :: GtkScrolledWindow
# --viewport1 :: GtkViewport
# E: <gtk.Viewport object at 0x96cadc4 (GtkViewport at 0x95278d0)>

Related

Leetcode 126: Word Ladder 2 in Python code optimization

I have the solution for the Word Ladder 2 (Leetcode problem 126: Word Ladder 2 ) in Python 3.6, and I notice that one of the very last testcases times out for me on the platform. Funnily, the test passes when run on PyCharm or as an individual test case on the site, but it takes about 5 seconds for it to complete. My solution uses BFS with some optimizations, but can someone tell me if there is a way to make it faster. Thank you! (P.S: Apologies for the additional test cases included in the commented out section!)
import math
import queue
from typing import List
class WordLadder2(object):
#staticmethod
def is_one_hop_away(s1: str, s2: str) -> int:
"""
Uses the distance between strings to return True if string s2 is one character away from s1
:param s1: Base string
:param s2: Comparison string
:return: True if it the difference between the strings is one character
"""
matrix = [[0] * (len(s1) + 1) for i in range(len(s1) + 1)]
for r, row in enumerate(matrix):
for c, entry in enumerate(row):
if not r:
matrix[r][c] = c
elif not c:
matrix[r][c] = r
else:
if s1[r - 1] == s2[c - 1]:
matrix[r][c] = matrix[r - 1][c - 1]
else:
matrix[r][c] = 1 + min(matrix[r - 1][c - 1], matrix[r - 1][c], matrix[r][c - 1])
if matrix[-1][-1] == 1:
return True
else:
return False
def get_next_words(self, s1: str, wordList: List[str]) -> List[str]:
"""
For a given string in the list, return a set of strings that are one hop away
:param s1: String whose neighbors one hop away are needed
:param wordList: Array of words to choose from
:return: List of words that are one character away from given string s1
"""
words = []
for word in wordList:
if self.is_one_hop_away(s1, word):
words.append(word)
return words
def find_ladders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
"""
Main method to determine shortest paths between a beginning word and an ending word, in a given list of words
:param beginWord: Word to begin the ladder
:param endWord: Word to end the ladder
:param wordList: List of words to choose from
:return: List of list of word ladders, if they are found. Empty list, if endWord not in wordList or path not
found from beginWord to endWord
"""
q = queue.Queue()
paths = list()
current = [beginWord]
q.put((beginWord, current))
# Set to track words we have already processed
visited = set()
# Dictionary to keep track of the shortest path lengths to each word from beginWord
shortest_paths = {beginWord: 1}
min_length = math.inf
# Use BFS to find the shortest path in the graph
while q.qsize():
word, path = q.get()
# If endWord is found, add the current path to the list of paths and compute minimum path
# length found so far
if word == endWord:
paths.append(path)
min_length = min(min_length, len(path))
continue
for hop in self.get_next_words(word, wordList):
# If the hop is already processed or in the queue for processing, skip
if hop in visited or hop in q.queue:
continue
# If the shortest path to the hop has not been determined or the current path length is lesser
# than or equal to the known shortest path to the hop, add it to the queue and update the shortest
# path to the hop.
if (hop not in shortest_paths) or (hop in shortest_paths and len(path + [hop]) <= shortest_paths[hop]):
q.put((hop, path + [hop]))
shortest_paths[hop] = len(path + [hop])
visited.add(word)
return [s for s in paths if len(s) == min_length]
if __name__ == "__main__":
# beginword = 'qa'
# endword = 'sq'
# wordlist = ["si","go","se","cm","so","ph","mt","db","mb","sb","kr","ln","tm","le","av","sm","ar","ci","ca","br","ti","ba","to","ra","fa","yo","ow","sn","ya","cr","po","fe","ho","ma","re","or","rn","au","ur","rh","sr","tc","lt","lo","as","fr","nb","yb","if","pb","ge","th","pm","rb","sh","co","ga","li","ha","hz","no","bi","di","hi","qa","pi","os","uh","wm","an","me","mo","na","la","st","er","sc","ne","mn","mi","am","ex","pt","io","be","fm","ta","tb","ni","mr","pa","he","lr","sq","ye"]
# beginword = 'hit'
# endword = 'cog'
# wordlist = ['hot', 'dot', 'dog', 'lot', 'log', 'cog']
# beginword = 'red'
# endword = 'tax'
# wordlist = ['ted', 'tex', 'red', 'tax', 'tad', 'den', 'rex', 'pee']
beginword = 'cet'
endword = 'ism'
wordlist = ["kid","tag","pup","ail","tun","woo","erg","luz","brr","gay","sip","kay","per","val","mes","ohs","now","boa","cet","pal","bar","die","war","hay","eco","pub","lob","rue","fry","lit","rex","jan","cot","bid","ali","pay","col","gum","ger","row","won","dan","rum","fad","tut","sag","yip","sui","ark","has","zip","fez","own","ump","dis","ads","max","jaw","out","btu","ana","gap","cry","led","abe","box","ore","pig","fie","toy","fat","cal","lie","noh","sew","ono","tam","flu","mgm","ply","awe","pry","tit","tie","yet","too","tax","jim","san","pan","map","ski","ova","wed","non","wac","nut","why","bye","lye","oct","old","fin","feb","chi","sap","owl","log","tod","dot","bow","fob","for","joe","ivy","fan","age","fax","hip","jib","mel","hus","sob","ifs","tab","ara","dab","jag","jar","arm","lot","tom","sax","tex","yum","pei","wen","wry","ire","irk","far","mew","wit","doe","gas","rte","ian","pot","ask","wag","hag","amy","nag","ron","soy","gin","don","tug","fay","vic","boo","nam","ave","buy","sop","but","orb","fen","paw","his","sub","bob","yea","oft","inn","rod","yam","pew","web","hod","hun","gyp","wei","wis","rob","gad","pie","mon","dog","bib","rub","ere","dig","era","cat","fox","bee","mod","day","apr","vie","nev","jam","pam","new","aye","ani","and","ibm","yap","can","pyx","tar","kin","fog","hum","pip","cup","dye","lyx","jog","nun","par","wan","fey","bus","oak","bad","ats","set","qom","vat","eat","pus","rev","axe","ion","six","ila","lao","mom","mas","pro","few","opt","poe","art","ash","oar","cap","lop","may","shy","rid","bat","sum","rim","fee","bmw","sky","maj","hue","thy","ava","rap","den","fla","auk","cox","ibo","hey","saw","vim","sec","ltd","you","its","tat","dew","eva","tog","ram","let","see","zit","maw","nix","ate","gig","rep","owe","ind","hog","eve","sam","zoo","any","dow","cod","bed","vet","ham","sis","hex","via","fir","nod","mao","aug","mum","hoe","bah","hal","keg","hew","zed","tow","gog","ass","dem","who","bet","gos","son","ear","spy","kit","boy","due","sen","oaf","mix","hep","fur","ada","bin","nil","mia","ewe","hit","fix","sad","rib","eye","hop","haw","wax","mid","tad","ken","wad","rye","pap","bog","gut","ito","woe","our","ado","sin","mad","ray","hon","roy","dip","hen","iva","lug","asp","hui","yak","bay","poi","yep","bun","try","lad","elm","nat","wyo","gym","dug","toe","dee","wig","sly","rip","geo","cog","pas","zen","odd","nan","lay","pod","fit","hem","joy","bum","rio","yon","dec","leg","put","sue","dim","pet","yaw","nub","bit","bur","sid","sun","oil","red","doc","moe","caw","eel","dix","cub","end","gem","off","yew","hug","pop","tub","sgt","lid","pun","ton","sol","din","yup","jab","pea","bug","gag","mil","jig","hub","low","did","tin","get","gte","sox","lei","mig","fig","lon","use","ban","flo","nov","jut","bag","mir","sty","lap","two","ins","con","ant","net","tux","ode","stu","mug","cad","nap","gun","fop","tot","sow","sal","sic","ted","wot","del","imp","cob","way","ann","tan","mci","job","wet","ism","err","him","all","pad","hah","hie","aim"]
wl = WordLadder2()
# beginword = 'hot'
# endword = 'dog'
# wordlist = ['hot', 'dog', 'dot']
print(wl.find_ladders(beginword, endword, wordlist))
The part that slows down your solution is is_one_hop_away, which is a costly function. This is called repeatedly during the actual BFS. Instead you should aim to first create a graph structure -- an adjacency list -- so that complexity of calculating which words are neighbors is dealt with before actually peforming the BFS search.
Here is one way to do it:
from collections import defaultdict
class Solution:
def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:
def createAdjacencyList(wordList):
adj = defaultdict(set)
d = defaultdict(set)
for word in wordList:
for i in range(len(word)):
derived = word[:i] + "*" + word[i+1:]
for neighbor in d[derived]:
adj[word].add(neighbor)
adj[neighbor].add(word)
d[derived].add(word)
return adj
def edgesOnShortestPaths(adj, beginWord, endWord):
frontier = [beginWord]
edges = defaultdict(list)
edges[beginWord] = []
while endWord not in frontier:
nextfrontier = set(neighbor
for word in frontier
for neighbor in adj[word]
if neighbor not in edges
)
if not nextfrontier: # endNode is not reachable
return
for word in frontier:
for neighbor in adj[word]:
if neighbor in nextfrontier:
edges[neighbor].append(word)
frontier = nextfrontier
return edges
def generatePaths(edges, word):
if not edges[word]:
yield [word]
else:
for neighbor in edges[word]:
for path in generatePaths(edges, neighbor):
yield path + [word]
if endWord not in wordList: # shortcut exit
return []
adj = createAdjacencyList([beginWord] + wordList)
edges = edgesOnShortestPaths(adj, beginWord, endWord)
if not edges: # endNode is not reachable
return []
return list(generatePaths(edges, endWord))

issue with update shown values in a pyqt5-applet (using qtableview)

I want to make a qtableview widget correctly updating. I'm working on a calibration applet, where i wanna fill cell by cell of an (e. g.) 100 x 100 x 4 array.
If my hardware reaches position 1, 2, 3, and so on, I will trigger a voltage measurement and gather those values with an i2c-read out-function.
So issues a la "my qtableview is not updating" are omnipresent.
But so far, I'm not able to adapt examples I have read, to make my code behaving as I want.
So if you look at my screenshot:
the problem is:
when I'm clicking on row or col +/-, the yellow highlighting is not changing instantly
when I'm clicking on store i²c, which is meant to put a dummy 0.0 in/on selected cell, this is also not changing instantly
Several methods like telling the model that data has changed, I was not able to implement correctly so far.
Could some of you help me to add a few lines just to force applet to update correctly?
fillCSV_forum.py:
### libraries:
import sys # to use e. g. exit function
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
import pandas as pd # to use pandas tables
import numpy as np # to use numpy arrays
### user-defined header files / modules:
from uLib_coloredWidget import Color # import user-defined functions..
from rndGen import i2c_read # .. see folder
### initial settings:
# general
np.random.seed(4) # if using np.random, then pseudo random values will occure
### globals:
nRow = 5; nCol = 5; nSht = 4 # table dimensions
rowIdx = colIdx = shtIdx = 0 # aux vars to index array
rndArray = np.random.rand(nSht, nRow, nCol) * 4.3 # auxilliary before integrating i2c
tabNames = ["A4", "A5","A6","A7"] # array (list) with tab names
rowIdx = 1; colIdx = 1 # aux vars to index selected cell
### declarations / definitions:
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.BackgroundRole and index.column() == colIdx and index.row() == rowIdx:
# See below for the data structure.
return QtGui.QColor('yellow')
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
if isinstance(value, float): # to set fixed DISPLAYED precision of floats
return "%.4f" % value
return str(value)
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class App(QtWidgets.QMainWindow):
# local variable's declarations
# init
def __init__(self):
super().__init__() # default one
self.setup_main_window() # using helper function to increase readability (function call within self scope)
# setup main window
self.createLayout() # function call to create layouts with widgets
self.post_main_window() # pass edited layouts to main window
# declaration / methods / helper functions
def setup_main_window(self): # to set window's / applet's properties
self.centralwidget = QtWidgets.QWidget()
self.setCentralWidget(self.centralwidget)
self.resize( 800, 400 )
self.setWindowTitle( "# disposition calibration #" )
def post_main_window(self): # to publish edited layouts in app window
self.centralwidget.setLayout(self.lyoOut)
def createLayout(self): # to create layouts with widgets
self.lyoOut = QtWidgets.QVBoxLayout() # declare different layouts
self.lyoIn1 = QtWidgets.QHBoxLayout()
self.lyoIn2 = QtWidgets.QGridLayout()
self.createWidgets() # function call pass widgets to sub-layouts
self.lyoOut.addLayout(self.lyoIn1) # inner layouts got widgets by self.createWidgets()
self.lyoOut.addLayout(self.lyoIn2) # merge edited inner layout in/to outside layout here
def createWidgets(self): # create master-layout's widgets (function calls)
# fill 1st row of ouside layout
self.lyoIn1 = self.createNestedTabs(self.lyoIn1) # function call to create master-tabs
# fill 2nd row of outside layout
self.lyoIn2 = self.createButtons(self.lyoIn2) # function call to create buttons
def createNestedTabs(self, layout2modify): # create 1st tab layer
self.MstTabs = QtWidgets.QTabWidget() # create tabs-widget
self.MstTabs.setTabPosition(QtWidgets.QTabWidget.North) # set it's location
self.MstTabs.addTab(self.createChildTabs(), "data") # add several sub-tab layouts to that widget
self.MstTabs.addTab(Color("orange"), "plot") #
stylesheet = """
QTabBar::tab:selected {background: lightgreen;}
QTabBar::tab:!selected {background: lightyellow;}
"""
self.MstTabs.setStyleSheet(stylesheet)
layout2modify.addWidget(self.MstTabs) # add this tabs-widget to passed-in layout
return layout2modify # return edited layout
def createChildTabs(self): # create 2nd tab layer
self.ChdTabs = QtWidgets.QTabWidget() # create tabs-widget
self.ChdTabs.setTabPosition(QtWidgets.QTabWidget.West) # set it's location
self.ChdTabs.addTab(self.createPandasTables(0), "A4")
self.ChdTabs.addTab(self.createPandasTables(1), "A5")
self.ChdTabs.addTab(self.createPandasTables(2), "A6")
self.ChdTabs.addTab(self.createPandasTables(3), "A7")
return self.ChdTabs # return created widgets
def createPandasTables(self, shtIdx): # to creating and editing pandas tables-widgets
# use indexed (pandas)dataframe sheet values
Lbl = ["a","b","c","d","e"]
self.df = pd.DataFrame(rndArray[shtIdx], columns = Lbl, index = Lbl)
# .. to create a widget
self.table_widget = QtWidgets.QTableView() # create QTableView-Widget
self.model = TableModel(self.df) # make df to user defined table model to use in widgets
self.table_widget.setModel(self.model) # pass created model to created widget
# certain formatings
self.table_widget.resizeColumnsToContents() # set column width to content
self.table_widget.horizontalHeader().setStretchLastSection(True) # strech last column to frame width
self.table_widget.verticalHeader().setStretchLastSection(True) # strech last row to frame height
self.table_widget.setAlternatingRowColors(True) # switch on alternating row highlighting
return self.table_widget # return created widgets
def createButtons(self, layout2modify): # helper function - to create layout's buttons
bStoreI2C = QtWidgets.QPushButton("Store i²c")
bStoreI2C.clicked.connect(lambda:self.storeVal())
bStoreI2C.setStyleSheet("QPushButton::hover"
"{"
"background-color : yellow;"
"}")
layout2modify.addWidget(bStoreI2C, 1, 3, 2, 1)
self.lbl_1 = QtWidgets.QLabel()
self.lbl_1.setText(str(rowIdx))
self.lbl_1.setAlignment(QtCore.Qt.AlignCenter)
layout2modify.addWidget(self.lbl_1, 1, 5, 2, 1)
bRowAdd = QtWidgets.QPushButton("row +")
bRowAdd.clicked.connect(lambda:self.rowAdd())
layout2modify.addWidget(bRowAdd, 2, 6)
bRowSub = QtWidgets.QPushButton("row -")
bRowSub.clicked.connect(lambda:self.rowSub())
layout2modify.addWidget(bRowSub, 1, 6)
return layout2modify # return edited layout
def storeVal(self):
#i2c_vals = get_i2c_values(i2c_addrs)
for i in range (0,4):
#self.tbData[i, rowIdx, colIdx] = i2c_vals[i] # change cell entries with imported value
rndArray[i, rowIdx, colIdx] = 0
#self.tbData[sht, row, col] = 99 # change cell entry with imported value
# try 1
#self.table_widget.update()
#self.table_widget.repaint()
#self.model.select()
#self.table_widget.select()
# try 2
# self.refreshModel() # not working so far
#self.model = TableModel(self.df) # make df to user defined table model to use in widgets
#self.table_widget.setModel(self.model)
# print(rndArray)
print('i²c-value(s) stored')
def rowAdd(self):
global rowIdx
rowIdx = (rowIdx + 1) % nRow # increment and modulo to cycle
self.lbl_1.setText(str(rowIdx)) # update label's text
print('row is ', rowIdx)
def rowSub(self):
global rowIdx
rowIdx = (rowIdx - 1) % nRow # increment and modulo to cycle
self.lbl_1.setText(str(rowIdx)) # update label's text
print('row is ', rowIdx)
### main:
def main():
app = QtWidgets.QApplication(sys.argv) # instanciate app
window = App() # instanciate window
window.show() # show window
app.exec_() # stuck here 'til window is closed
print('# window will be terminated.. #')
time.sleep(2)
print('# ..app execution closed #')
# make file executable
if __name__ == '__main__':
main()
rndGen.py: (is called in fillCSV_forum.py)
import numpy as np
def i2c_read():
floats = np.random.rand(4,1,1) * 4.3
return floats
uLib_coloredWidget.py: (is called in fillCSV_forum.py)
from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtWidgets import QWidget
class Color(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.Window, QColor(color))
self.setPalette(palette)
pip freeze --local-output of virtual enviroment:
numpy==1.23.0
pandas==1.4.3
PyQt5==5.15.7
PyQt5-Qt5==5.15.2
PyQt5-sip==12.11.0
python-dateutil==2.8.2
pytz==2022.1
six==1.16.0
[... additionally many hours of trial and error....]
i think i finally got a dirty solution / work around..
the problem i could determining, was e. g. if i am clicking the col+/- or store button, the focus of recently selected tab is vanishing.
first when click again into any tab region or select another tabs those values are updating.
so i tried to look for programmatically tab swap and did this as a dirty work around because i could not find a method like "reactivate tab again"
i added ... :
def storeVal(self):
#i2c_vals = get_i2c_values(i2c_addrs)
for i in range (0,nSht):
self.df[i].iat[rowIdx, colIdx] = 99
print('i²c-value(s) stored')
self.show_data()
def show_data(self):
x = self.ChdTabs.currentIndex()
print(x) # debugging
self.ChdTabs.setCurrentIndex(1)
self.ChdTabs.setCurrentIndex(x)
... a show method and called it at the end of the store-method.
in this show method i programmatically swap the active tab back and forth. this is so fast, that i cannot see it
now my values are correctly shown
another tiny if else code is necessary to also swap if tab 1 is selected, but this is cosmetic thing

Numba "LLVM IR parsing error" seemingly because numba.complex128[:,:].shape has a default type?

Please bear with the long question.
Numba encounters "LLVM IR parsing error" in my code seemingly due to defualt typing of np.complex128.shape, but I could not find any documentations saying that np.complex128.shape, numba.complex128.shape or prange have default types.
Minimal workable reproduction:
import numpy as np
from numba import jit, njit, prange
from numba import complex128, int32 # import jit value types
# Invert an (n,n) submatrix of a (m>n,n) rectangular matrix by taking the first
# n rows. "Taking the first n rows" is motivated by the RHS being rank n.
#
# -- Input --
# (m,n) matrix A
#
# -- Return --
# (m,m) matrix A_inv
#njit(complex128[:,:](complex128[:,:]))
def inv_square_jit(in_matrix):
if in_matrix.ndim != 2:
raise ValueError("Input should be 2d array")
n_row = in_matrix.shape[0]
n_col = in_matrix.shape[1]
if n_row<=n_col:
raise ValueError("Input should have more rows than cols")
# Remove specfied column (slightly faster than delete)
# and remove extra rows
sqinv = np.linalg.inv(in_matrix[:n_col, :])
padded = np.zeros((n_row, n_row), dtype = np.complex128)
padded[:len(sqinv), :len(sqinv)] = sqinv
return(padded)
# Solve degenerate underdetermined equation system
# -- Input --
# (m,n+1), (m,n), rank-n 2d np arrays A, B
# n-dim np array-like vb
# or
# (m,n+1), rank n+1 A,
# m-dim np array-like v_rhs
#
# vb can be any array-like item, and is not necessarily 1d.
# Implemented with ChiPhiFunc in mind.
#
# -- Return --
# n+1 np array-like va
#
# -- Note --
# For recursion relations with ChiPhiFunc's, A and B should come from
# convolution matrices. That still needs implementation.
#njit(complex128[:](complex128[:,:], complex128[:]))
def solve_degenerate_jit(A, v_rhs):
n_dim = A.shape[1]
if A.shape[0] != v_rhs.shape[0]:
raise ValueError("solve_underdetermined: A, v_rhs must have the same number of rows")
A_inv = np.ascontiguousarray(inv_square_jit(A))
# This vector is actually m-dim, with m-n blank elems at the end.
va = (A_inv#np.ascontiguousarray(v_rhs))[:n_dim]
return(va)
# #njit(complex128[:](complex128[:,:], complex128[:,:], complex128[:]))
# def solve_degenerate_jit(A, B, vb):
# B_cont = np.ascontiguousarray(B)
# vb_cont = np.ascontiguousarray(vb)
# return(solve_degenerate_jit(A, B_cont#vb_cont))
# Generate convolution operator from a for an n_dim vector.
#njit(complex128[:,:](complex128[:], int32))
def conv_matrix(vec, n_dim):
out_transposed = np.zeros((n_dim,len(vec)+n_dim-1), dtype = np.complex128)
for i in prange(n_dim):
out_transposed[i, i:i+len(vec)] = vec
return(out_transposed.T)
# For solving a*va = v_rhs, where va, vb have the same number of dimensions.
# In the context below, "#dim" represents number of chi mode components.
#
# -- Input --
# v_source_A: 2d matrix, content of ChiPhiFuncGrid, #dim = a
# v_rhs: 2d matrix, content of ChiPhiFuncGrid, #dim = m
# rank_rhs: int, rank of v_rhs (and correct answer)
# -- Output --
# va: 2d matrix, content of ChiPhiFuncGrid. Has #dim = rank_rhs
#njit(complex128[:,:](complex128[:,:], complex128[:,:], int32), parallel=True)
def batch_degen_jit(v_source_A, v_rhs, rank_rhs):
# if type(v_source_A) is not ChiPhiFuncGrid or type(v_source_B) is not ChiPhiFuncGrid:
# raise TypeError('batch_underdetermined_deconv: input should be ChiPhiFuncGrid.')
A_slices = np.ascontiguousarray(v_source_A.T) # now the axis 0 is phi grid
v_rhs_slices = np.ascontiguousarray(v_rhs.T) # now the axis 0 is phi grid
# axis 0 is phi grid, axis 1 is chi mode
va_transposed = np.zeros((len(A_slices), rank_rhs), dtype = np.complex128)
if len(A_slices) != len(v_rhs_slices):
raise ValueError('batch_underdetermined_deconv: A, v_rhs must have the same number of phi grids.')
if len(v_source_A) + rank_rhs - 1 != len(v_rhs):
raise ValueError('batch_underdetermined_deconv: #dim_A + rank_rhs - 1 = #dim_v_rhs must hold.')
for i in prange(len(A_slices)):
A_conv_matrix_i = conv_matrix(A_slices[i], rank_rhs)
# ********** Removing this line somehow makes it compile **********
va_transposed[i, :] = solve_degenerate_jit(A_conv_matrix_i,v_rhs_slices[i])
# ********** Removing this line somehow makes it compile **********
return va_transposed.T
The code compiles fine with parallel=False for the last method. However, with parallel=True, error occurs in for i in prange(len(A_slices)): of def batch_degen_jit(v_source_A, v_rhs, rank_rhs):, seemingly because solve_degenerate_jit(complex128[:,:], complex128[:], int32) accepts int32, but the prange(len(A_slices)) produces int64. Replacing all int32 with int64 solves the problem. Removing the *-marked line also makes it compile.
Error:
LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)
Failed in nopython mode pipeline (step: nopython mode backend)
LLVM IR parsing error
<string>:1278:34: error: '%.777' defined with type 'i64' but expected 'i32'
%".778" = icmp eq i32 %".776", %".777"
^
File "<ipython-input-24-fa65c2d527fa>", line 104:
def batch_degen_jit(v_source_A, v_rhs, rank_rhs):
<source elided>
raise ValueError('batch_underdetermined_deconv: #dim_A + rank_rhs - 1 = #dim_v_rhs must hold.')
for i in prange(len(A_slices)):
^
During: lowering "id=17[LoopNest(index_variable = parfor_index.1805, range = (0, $10call_method.4_size0.1767, 1))]{120: <ir.Block at <ipython-input-24-fa65c2d527fa> (104)>}Var(parfor_index.1805, <ipython-input-24-fa65c2d527fa>:104)" at <ipython-input-24-fa65c2d527fa> (104)
Why is this the case?
Thank you!
(P.S. here's a test case for the methods:
convolver = np.random.rand(10,3)
correct_answer = np.random.rand(10,5)
rhs = np.zeros((10,7))
for i in range(10):
rhs[i] = np.convolve(convolver[i], correct_answer[i])
print(batch_degen_jit(np.complex128(convolver).T, np.complex128(rhs).T, 5))
)

how to get tornadoredis listen value

I want to write a chat demo with tornado and redis. I use redis subscribe , but what I wrote is not work . when I run the code , iterm output
listening 8000
GroupChat here
getMsg here
None
None
And I PUBLISH testc helloword in redis-cli, iterm output:
[I 150401 18:30:57 web:1825] 304 GET /groupchat?key=testc (127.0.0.1) 2.40ms
Message(kind=u'message', channel=u'testc', body=u'helloword', pattern=u'testc')
I just want to get the Message in GroupChat.get , but I get None. anyone help me?
GroupChat code is here :
class GroupChat(tornado.web.RequestHandler):
def initialize(self):
print 'GroupChat here'
self.c = tornadoredis.Client(host=CONFIG['REDIS_HOST'], port=CONFIG['REDIS_PORT'], password=CONFIG['REDIS_AUTH'])
self.channelMsgModel = channelMsgModel(self.c)
#tornado.gen.coroutine
def get(self):
try:
key = self.get_argument('key')
info = yield self.channelMsgModel.getMsg(key)
print info
self.finish(info)
except Exception, e:
print e
pass
channelMsgModel code is here :
import tornado.gen
class channelMsgModel :
timeout = 10
def __init__(self, redisobj):
self.redisobj = redisobj
#tornado.gen.coroutine
def getMsg(self, key):
print 'getMsg here'
yield tornado.gen.Task(self.redisobj.subscribe, key)
info = self.redisobj.listen(self.on_message)
print info
raise tornado.gen.Return(info)
def on_message(self, msg):
if (msg.kind == 'message'):
print msg
return msg
elif (msg.kind == 'unsubscribe'):
self.redisobj.disconnect()
# raise tornado.gen.Return(False)
Use a toro.Queue (which will be included in Tornado itself in the upcoming version 4.2):
class channelMsgModel:
def __init__(self, redisobj):
self.redisobj = redisobj
self.queue = toro.Queue()
#gen.coroutine
def getMsg(self, key):
yield gen.Task(self.redisobj.subscribe, key)
self.redisobj.listen(self.on_message)
info = yield self.queue.get()
raise tornado.gen.Return(info)
def on_message(self, msg):
if (msg.kind == 'message'):
self.queue.put_nowait(msg)
elif (msg.kind == 'unsubscribe'):
self.redisobj.disconnect()

Python Lex-Yacc (PLY) Error recovery at the end of input

Problem
I am trying to implement an error tolerant parser using Python Lex-Yacc (PLY), but I have trouble using error recovery rules at the end of my input string.
How can I recover from an unexpected end of input?
Example
This example grammar produces strings of the form A END A END A END A END ...
Statement : Expressions
Expressions : Expression Expressions
|
Expression : A END
I want to perform an error recovery if the END Token was omitted, so stings like A A A END or A A A will be recognized by the parser.
My approach
I added an error recovery rule, which allows me to accept input like A A A END
Expression : A END
| A error
Which allows me to accept the following input:
A A A END
But if the last END token is omitted (A A A), I still get a syntax error and cannot recover.
Sample PLY code
from __future__ import print_function
# Tokens
tokens = ('A', 'END')
t_A = r'A'
t_END = r'END'
t_ignore = " "
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lex.lex()
# Rules
def p_statement_expr(p):
'''statement : expressions'''
print("parsed:", p[1])
def p_expressions(p):
'''expressions : expression expressions'''
p[0] = [p[1]] + p[2]
def p_expressions_empty(p):
'''expressions : '''
p[0] = list()
def p_expression_pharse(p):
'''expression : A END
| A error'''
p[0] = 'A'
def p_error(p):
if p:
print("Syntax error at '%s'" % p.value)
else:
print("Syntax error at EOI")
import ply.yacc as yacc
yacc.yacc()
while 1:
try:
s = raw_input('query > ') # use input() on Python 3
except EOFError:
break
yacc.parse(s)
I add it as a new answer (and do know it is too late for the bounty :-( ) because it is a very different approach. If we used flex, it would be much easier, since it has the notion of the <<EOF>> token that matches only at end of file. After thinking about that, I realized that it was very simple to add that functionality to PLY without any change to the original module by using a proxy around the lexer. And Python allows easy implementation of proxies thanks the the __getattr__ special method.
I just add
a new token EOF that will be send at end of file
a proxy around the token method of the lexer that on end of file returns the special EOF token on first pass and then the normal None
the eof token to end statement rule
And still reverse the rule expressions : expressions expression instead of expressions : expression expressions to allow immediate reduce
The code becomes :
from __future__ import print_function
# Tokens
tokens = ('A', 'END', 'EOF')
t_A = r'A'
t_END = r'END'
t_ignore = " "
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
orig_lexer = lex.lex()
class ProxyLexer(object):
def __init__(self, lexer, eoftoken):
self.end = False
self.lexer = lexer
self.eof = eoftoken
def token(self):
tok = self.lexer.token()
if tok is None:
if self.end :
self.end = False
else:
self.end = True
tok = lex.LexToken()
tok.type = self.eof
tok.value = None
tok.lexpos = self.lexer.lexpos
tok.lineno = self.lexer.lineno
# print ('custom', tok)
return tok
def __getattr__(self, name):
return getattr(self.lexer, name)
lexer = ProxyLexer(orig_lexer, 'EOF')
# Rules
def p_statement_expr(p):
'''statement : expressions EOF'''
print("parsed:", p[1])
def p_expressions(p):
'''expressions : expressions expression'''
p[0] = p[1] + [p[2]]
def p_expressions_empty(p):
'''expressions : '''
p[0] = list()
def p_expression_pharse(p):
'''expression : A END
| A error'''
p[0] = 'A'
def p_error(p):
if p:
print("Syntax error at '%s'" % p.value)
else:
print("Syntax error at EOI")
import ply.yacc as yacc
parser = yacc.yacc()
while 1:
try:
s = raw_input('query > ') # use input() on Python 3
except EOFError:
break
parser.parse(s, lexer = lexer)
That way :
the original grammar is unchanged
the error recovery method remains stupidly simple and has no dependance on the remaining of the grammar
it can be easily extended to complex parsers
As you want to accept all elements, you can explicitely declare a rule for a A not followed by a END and use the fact that yacc and PLY friendly deal with ambiguous rules.
You can simply have a normal rule :
Expression : A END
and below a lower priority rule (as it comes later) that will issue a warning
Expression : A
That way, all A will be accepted, there won't be any syntax error, and the warning will be issued for any A not followed by a END including one at the end of the flow. In order to more easily find the offending A, I have added in the warning the position of the symbol in the flow.
Edit:
The script is modified to correctly deal with other syntax error (such as AENDENDAEND), and also to immediately reduce expressions by replacing expressions : expression expressions with expressions : expressions expression
Here is the modified script (tested in python 3.4 simply replacing raw_input with input):
from __future__ import print_function
# Tokens
tokens = ('A', 'END')
t_A = r'A'
t_END = r'END'
t_ignore = " "
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lex.lex()
# Rules
def p_statement_expr(p):
'''statement : expressions'''
print("parsed:", p[1])
def p_expressions(p):
'''expressions : expressions expression'''
p[0] = p[1] + [p[2]]
def p_expressions_err(p):
'''expressions : expressions error'''
p[0] = p[1]
def p_expressions_empty(p):
'''expressions : '''
p[0] = list()
def p_expression_pharse(p):
'''expression : A END'''
p[0] = 'A'
# add a separate rule BELOW previous one to display a warning
def p_expression_pharse_warn(p):
'''expression : A'''
print("Warning at absolute position %d (line %d)" % (p.lexpos(1), p.lineno(1)))
p[0] = 'A'
def p_error(p):
if p:
print("Syntax error at '%s'" % p.value)
else:
print("Syntax error at EOI")
import ply.yacc as yacc
yacc.yacc()
while 1:
try:
s = raw_input('query > ') # use input() on Python 3
except EOFError:
break
yacc.parse(s)
Edit : the following is an incorrect attempt to avoid an additional rule : it is more complex and less efficient than the above version. Please see my conclusion below
Edit per comment :
I understand your point that you do not want to multiply grammar rules. It is possible to be fault tolerant, except for last token. If your last token is in error, it will not be followed by anything and will never be caught in rule expression : A error.
But here is a fault tolerant parser that keeps everything except last token if case of error on that one :
from __future__ import print_function
# Tokens
tokens = ('A', 'END')
t_A = r'A'
t_END = r'END'
t_ignore = " "
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lex.lex()
# Rules
def p_statement_expr(p):
'''statement : expressions'''
# print("parsed:", p[1])
def p_expressions(p):
'''expressions : expressions expression'''
p[0] = p[1] + [p[2]]
result.append(p[2])
def p_expressions_empty(p):
'''expressions : '''
p[0] = list()
def p_expression_pharse(p):
'''expression : A END
| A error'''
p[0] = 'A'
def p_error(p):
if p:
global lasterr
print("Syntax error at '%s' (%d)" % (p.value, p.lexpos))
else:
print("Syntax error at EOI")
import ply.yacc as yacc
yacc.yacc()
while 1:
try:
s = input('query > ') # use input() on Python 3
except EOFError:
break
result = []
yacc.parse(s)
print('Result', result)
The princip is to collate by expressions : expressions expression instead of expressions : expression expressions, and to keep all in a global variable.
With an input of A END A A END A A A END it gives
Result ['A', 'A', 'A', 'A', 'A', 'A']
and with : A END A A END A A A END , it gives
Result ['A', 'A', 'A', 'A', 'A']
(all tokens but the last)
With a true flex - bison solution, it would be possible to make use of the special <<EOF>> token that matches at end of input, to always have another token after the last one. Unfortunately, it is not implemented in PLY, and the only real solution is to introduce a rule that accepts alone A token. For a real parser, it also guarantees that you are actually processing the correct token : I used
def p_expression_pharse(p):
'''expression : A END'''
p[0] = 1 + p.lexpos(1)
# add a separate rule BELOW previous one to display a warning
def p_expression_pharse_warn(p):
'''expression : A'''
print("Warning at absolute position %d (line %d)" % (p.lexpos(1), p.lineno(1)))
p[0] = -1 - p.lexpos(1)
to uniquely identify tokens in resul string, and I get correct positions.
And ... the error processing is very simple ...
Discussion TL/DR :
I admit I missed the point of last token error recovery. It is because in all parsers I've seen in real use cases, the error recovery consisted in rejecting the part that was syntactically incorrect (and thus not directly useable) and re-synchonizing the parser on next correct group of token. In all what I have seen, if a partial sentence can be used, it must not be processed by the error recovery mechanizme but by a grammar rule, in which it is easy to describe the appropriate action.
If you just want to keep the offending input for later processing, I think it is not a problem of action depending of a syntax, and I would simply note the position of offending token, or at most note the position of last correctly analysed token (the end of a complete element), the begin of first error recovery token and say that what is between is incorrect.
But it would be much different than what is asked here ...
This works for all examples I could imagine
from __future__ import print_function
# Tokens
tokens = ('A', 'END')
t_A = r'A'
t_END = r'END'
t_ignore = " "
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lex.lex()
# Rules
def p_statement_expr(p):
'''statement : expressions'''
#
print("parsed:", p[1])
def p_expressions(p):
'''expressions : expression expressions'''
p[0] = p[1] + p[2]
def p_expressions_empty(p):
'''expressions : '''
p[0] = list()
def p_expression_pharse(p):
'''expression : A END'''
p[0] = ['A']
def p_expression_error(p):
'''expression : A error'''
p[0] = ['A']
if p[2] is not None:
p[0] += p[2]
def p_error(p):
if p is None:
print("Syntax error at EOI")
e = yacc.YaccSymbol()
e.type = 'error'
e.value = None
yacc.errok()
return e
elif p.type == 'error':
yacc.errok()
return
elif hasattr(p, 'value'):
print("Syntax error at '%s'" % p.value)
e = yacc.YaccSymbol()
e.type = 'error'
e.value = p.value
yacc.errok()
return e
import ply.yacc as yacc
yacc.yacc()
while 1:
try:
s = raw_input('query > ') # use input() on Python 3
except EOFError:
break
yacc.parse(s)