Module sverchok.utils.snlite_importhelper
Expand source code
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import re
import bpy
import ast
UNPARSABLE = None, None, None, None
sock_dict = {
'v': 'SvVerticesSocket',
's': 'SvStringsSocket',
'm': 'SvMatrixSocket',
'o': 'SvObjectSocket',
'c': 'SvColorSocket',
'C': 'SvCurveSocket',
'D': 'SvDictionarySocket',
'T': 'SvTextSocket',
'S': 'SvSurfaceSocket',
'So': 'SvSolidSocket',
'SF': 'SvScalarFieldSocket',
'VF': 'SvVectorFieldSocket',
'FP': 'SvFilePathSocket',
}
def set_autocolor(node, use_me, color_me):
node.use_custom_color = use_me
node.color = color_me
def processed(str_in):
_, b = str_in.split('=')
return ast.literal_eval(b)
def parse_socket_line(node, line):
lsp = line.strip().split()
if not len(lsp) in {3, 5}:
node.error(
f'directive: (socket line) "{line}" -> is malformed '
f'(too little information, probably forgot to specify the socket-kind'
f': {sock_dict.keys()}'
)
return UNPARSABLE
else:
socket_type = sock_dict.get(lsp[2])
socket_name = lsp[1]
if not socket_type:
return UNPARSABLE
elif len(lsp) == 3:
return socket_type, socket_name, None, None
else:
default = processed(lsp[3])
nested = processed(lsp[4])
return socket_type, socket_name, default, nested
def trim_comment(line):
idx = line.find("#")
if idx < 0:
return line
return line[:idx]
def parse_required_socket_line(node, line):
# required input sockets do not accept defaults or nested info, what would be the point?
# receives a line like
# >in socketname sockettype
line = trim_comment(line)
lsp = line.strip().split()
if len(lsp) > 3:
lsp = lsp[:3]
if len(lsp) == 3:
socket_type = sock_dict.get(lsp[2])
socket_name = lsp[1]
if not socket_type:
return UNPARSABLE
return socket_type, socket_name, None, None
node.error(f'directive: (socket line) "{line}" -> is malformed, missing socket type? {lsp}')
return UNPARSABLE
def parse_extended_socket_line(node, line):
"""
returns socket_info:
socket_type, socket_name, default, nested (display name)
"""
socket_info = [None, None, None, None, None]
pattern = """
\+in\s+ # valid for inputs
(\w+)\s+ # list name to use
(\w+)\s+ # socket type
d=(.+)\s+ # default value
n=(\d) # nested value
(.+name="(.+)")? # optional socket label
"""
try:
p = re.compile(pattern, re.VERBOSE)
g = p.search(line.strip())
matches = g.groups()
for idx, m in enumerate(matches):
if m:
if idx == 0:
# the socket name used by user passing info to lists.
socket_info[1] = m
elif idx == 1:
# remap to the bl_idname of a sockettype
socket_info[0] = sock_dict.get(m.strip())
elif idx in (2, 3):
# defaults or nested, are still a string at this point
socket_info[idx] = ast.literal_eval(m)
elif idx == 4:
# if 4 is a match, then 5 contains the desired label
socket_info[idx] = matches[5]
break
# print(f"socket_info:{socket_info}")
return socket_info
except Exception as err:
print("SNLITE ERROR:", err)
def extract_directive_as_multiline_string(lines):
pattern = """
\"{3}(.*?)\"{3} # double quote enclosure
| # or
\'{3}(.*?)\'{3} # single quote enclosure
"""
try:
p = re.compile(pattern, re.MULTILINE|re.DOTALL|re.VERBOSE)
g = p.search(lines.strip())
matches = g.groups()
for idx, m in enumerate(matches):
if m:
return m
except Exception as err:
print("SNLITE ERROR:", err)
return
return
def print_node_script(node):
err_message = 'failed to find a directive in this script: SNLITE Error 1 (see docs for more info)'
print(node.script_name, err_message)
print("start --->")
print(node.script_str)
print("<--- end")
def parse_sockets(node):
if hasattr(node, 'inject_params'):
node.inject_params = False
snlite_info = {
'inputs': [], 'outputs': [],
'snlite_ui': [], 'includes': {},
'custom_enum': [], 'custom_enum_2': [],
'callbacks': {}, 'inputs_required': [],
}
directive = extract_directive_as_multiline_string(node.script_str)
if not directive:
print_node_script(node)
return snlite_info
for line in directive.split('\n'):
L = line.strip()
if L.startswith('in ') or L.startswith('out '):
socket_dir = L.split(' ')[0] + 'puts'
snlite_info[socket_dir].append(parse_socket_line(node, L))
if L.startswith('>in '):
# one or more inputs can be required before processing/showing errors
input_info = parse_required_socket_line(node, L)
snlite_info['inputs'].append(input_info)
snlite_info['inputs_required'].append(input_info[1])
if L.startswith('+in '):
# this is extended :regex: parsing of socket info line.
input_info = parse_extended_socket_line(node, L)
snlite_info['inputs'].append(input_info)
elif L.startswith('inject'):
if hasattr(node, 'inject_params'):
node.inject_params = True
elif L.startswith('enum ='):
snlite_info['custom_enum'] = L[6:].strip().split(' ')
elif L.startswith('enum2 ='):
snlite_info['custom_enum_2'] = L[7:].strip().split(' ')
elif L.startswith('include <') and L.endswith('>'):
filename = L[9:-1]
file = bpy.data.texts.get(filename)
if file:
snlite_info['includes'][filename] = file.as_string()
elif L in {'fh', 'filehandler'}:
snlite_info['display_file_handler'] = True
return snlite_info
def are_matched(sock_, socket_description):
return (sock_.bl_idname, sock_.name) == socket_description[:2]
Functions
def are_matched(sock_, socket_description)
-
Expand source code
def are_matched(sock_, socket_description): return (sock_.bl_idname, sock_.name) == socket_description[:2]
def extract_directive_as_multiline_string(lines)
-
Expand source code
def extract_directive_as_multiline_string(lines): pattern = """ \"{3}(.*?)\"{3} # double quote enclosure | # or \'{3}(.*?)\'{3} # single quote enclosure """ try: p = re.compile(pattern, re.MULTILINE|re.DOTALL|re.VERBOSE) g = p.search(lines.strip()) matches = g.groups() for idx, m in enumerate(matches): if m: return m except Exception as err: print("SNLITE ERROR:", err) return return
def parse_extended_socket_line(node, line)
-
returns socket_info: socket_type, socket_name, default, nested (display name)
Expand source code
def parse_extended_socket_line(node, line): """ returns socket_info: socket_type, socket_name, default, nested (display name) """ socket_info = [None, None, None, None, None] pattern = """ \+in\s+ # valid for inputs (\w+)\s+ # list name to use (\w+)\s+ # socket type d=(.+)\s+ # default value n=(\d) # nested value (.+name="(.+)")? # optional socket label """ try: p = re.compile(pattern, re.VERBOSE) g = p.search(line.strip()) matches = g.groups() for idx, m in enumerate(matches): if m: if idx == 0: # the socket name used by user passing info to lists. socket_info[1] = m elif idx == 1: # remap to the bl_idname of a sockettype socket_info[0] = sock_dict.get(m.strip()) elif idx in (2, 3): # defaults or nested, are still a string at this point socket_info[idx] = ast.literal_eval(m) elif idx == 4: # if 4 is a match, then 5 contains the desired label socket_info[idx] = matches[5] break # print(f"socket_info:{socket_info}") return socket_info except Exception as err: print("SNLITE ERROR:", err)
def parse_required_socket_line(node, line)
-
Expand source code
def parse_required_socket_line(node, line): # required input sockets do not accept defaults or nested info, what would be the point? # receives a line like # >in socketname sockettype line = trim_comment(line) lsp = line.strip().split() if len(lsp) > 3: lsp = lsp[:3] if len(lsp) == 3: socket_type = sock_dict.get(lsp[2]) socket_name = lsp[1] if not socket_type: return UNPARSABLE return socket_type, socket_name, None, None node.error(f'directive: (socket line) "{line}" -> is malformed, missing socket type? {lsp}') return UNPARSABLE
def parse_socket_line(node, line)
-
Expand source code
def parse_socket_line(node, line): lsp = line.strip().split() if not len(lsp) in {3, 5}: node.error( f'directive: (socket line) "{line}" -> is malformed ' f'(too little information, probably forgot to specify the socket-kind' f': {sock_dict.keys()}' ) return UNPARSABLE else: socket_type = sock_dict.get(lsp[2]) socket_name = lsp[1] if not socket_type: return UNPARSABLE elif len(lsp) == 3: return socket_type, socket_name, None, None else: default = processed(lsp[3]) nested = processed(lsp[4]) return socket_type, socket_name, default, nested
def parse_sockets(node)
-
Expand source code
def parse_sockets(node): if hasattr(node, 'inject_params'): node.inject_params = False snlite_info = { 'inputs': [], 'outputs': [], 'snlite_ui': [], 'includes': {}, 'custom_enum': [], 'custom_enum_2': [], 'callbacks': {}, 'inputs_required': [], } directive = extract_directive_as_multiline_string(node.script_str) if not directive: print_node_script(node) return snlite_info for line in directive.split('\n'): L = line.strip() if L.startswith('in ') or L.startswith('out '): socket_dir = L.split(' ')[0] + 'puts' snlite_info[socket_dir].append(parse_socket_line(node, L)) if L.startswith('>in '): # one or more inputs can be required before processing/showing errors input_info = parse_required_socket_line(node, L) snlite_info['inputs'].append(input_info) snlite_info['inputs_required'].append(input_info[1]) if L.startswith('+in '): # this is extended :regex: parsing of socket info line. input_info = parse_extended_socket_line(node, L) snlite_info['inputs'].append(input_info) elif L.startswith('inject'): if hasattr(node, 'inject_params'): node.inject_params = True elif L.startswith('enum ='): snlite_info['custom_enum'] = L[6:].strip().split(' ') elif L.startswith('enum2 ='): snlite_info['custom_enum_2'] = L[7:].strip().split(' ') elif L.startswith('include <') and L.endswith('>'): filename = L[9:-1] file = bpy.data.texts.get(filename) if file: snlite_info['includes'][filename] = file.as_string() elif L in {'fh', 'filehandler'}: snlite_info['display_file_handler'] = True return snlite_info
def print_node_script(node)
-
Expand source code
def print_node_script(node): err_message = 'failed to find a directive in this script: SNLITE Error 1 (see docs for more info)' print(node.script_name, err_message) print("start --->") print(node.script_str) print("<--- end")
def processed(str_in)
-
Expand source code
def processed(str_in): _, b = str_in.split('=') return ast.literal_eval(b)
def set_autocolor(node, use_me, color_me)
-
Expand source code
def set_autocolor(node, use_me, color_me): node.use_custom_color = use_me node.color = color_me
def trim_comment(line)
-
Expand source code
def trim_comment(line): idx = line.find("#") if idx < 0: return line return line[:idx]