-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBlock.py
More file actions
184 lines (148 loc) · 4.93 KB
/
Copy pathBlock.py
File metadata and controls
184 lines (148 loc) · 4.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import xml.etree.ElementTree as ET
from typing import Any
class Block:
'''
The base class for all Snappy blocks.
Signature: is the name of the block that corresponds
with its signature in XML format.
Inputs: The values you would find given as input. Empty inputs
must be given as some kind of NoneType value
Input Count: The amount of input slots that exist for the block.
Used for throwing errors in the event of having too many inputs
which could be a problem in Python but is not present in Snap! .
'''
def __init__(self, signature : str, inputs : []):
self.signature = signature
self.inputs = inputs
def toXML(self):
return blockToXML(self)
class Variable(Block):
'''
Snappy Equivalent of a Variable Block
Variables are technically blocks, but they're really just
placeholders. They differ as an XML Element because they
contain a var attribute and no 's' or signature attribute.
'''
def __init__(self, name : str):
self.name = name
class Option:
'''
Just a simple wrapper class to indicate that
a particular input for blocks is an option.
An option refers to some kind of drop-down
selection that appears within the Snap Interface
like "set <variablename> to ___" for example.
'''
def __init__(self, text):
self.text = text
self.tag = None
'''
Block -> XML Methods.
Useful for converting Snappy's "block" object and any Python-defined blocks
into Snap blocks.
'''
def blockToXML(block : Block) -> str:
'''
Converts a Block Object into an XML String.
'''
# Create a new block xml element
blockXML = ET.Element('block')
# Set the signature
if type(block) == Variable:
blockXML.attrib['var'] = block.name
else:
blockXML.attrib['s'] = block.signature
# Add the arguments
for i in range(len(block.inputs)):
blockInput = block.inputs[i]
blockXML.append(formatBlockInput(blockInput))
return ET.tostring(blockXML).decode('utf-8')
def formatBlockInput(inp : Any) -> ET.Element:
'''
Converts inputs into XML-ready formats.
All types are implicitly converted into strings since
every non-list datatype is effectively a string.
'''
if inp == None:
return ''
if type(inp) == Option:
valueElement = ET.Element('l')
optionXML = ET.Element('option')
optionXML.text = inp.text
valueElement.append(optionXML)
return valueElement
elif type(inp) == list:
listElement = ET.Element('list')
for elem in inp:
listElement.append(formatBlockInput(elem))
listReporterElement = ET.Element('block')
listReporterElement.attrib['s'] = 'reportNewList'
listReporterElement.append(listElement)
return listReporterElement
else:
valueElement = ET.Element('l')
valueElement.text = str(inp)
return valueElement
'''
XML -> Block Methods
'''
def xmlToBlock(xmlString : str) -> Block:
'''
Converts an XMLString into a Block object.
'''
xml = ET.fromstring(xmlString)
if 'var' in xml.attrib:
return Variable(xml.attrib['var'])
else:
signature = xml.attrib['s']
inputs = formatXMLInputs(xml)
return Block(signature, inputs)
def formatXMLInputs(blockInput : ET.Element):
'''
Given a particular blockInput, if it's not a list it'll try
to convert the input to a float value or int value, then string.
Otherwise, it returns a list value and recursively formats its elements.
'''
value = None
if blockInput.text == None:
return None
elif type(blockInput) == Option or blockInput.tag == 'l':
if '.' in blockInput.text:
try:
value = float(blockInput.text)
except ValueError:
value = blockInput.text
else:
try:
value = int(blockInput.text)
except ValueError:
value = blockInput.text
elif blockInput.attrib and blockInput.attrib['s'] == 'reportNewList':
return formatXMLInputs(blockInput[0])
else:
value = []
for elem in blockInput:
value.append(formatXMLInputs(elem))
return value
class Script:
'''
An ordered collection of Blocks with a scope.
'''
def __init__(self, blocks : [Block], scope : {str : Any}):
self.blocks = blocks
self.scope = scope
def __iter__(self):
return ScriptIterator(self.blocks)
class ScriptIterator:
'''
Iterator class for executing a script.
'''
def __init__(self, blocks):
self.blocks = blocks
self._index = 0
def __next__(self):
if self._index < len(self.blocks):
result = self.blocks[self._index]
self._index += 1
return result
raise StopIteration