Module sverchok.core.socket_data

For internal usage of the sockets module

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 #####

"""For internal usage of the sockets module"""
import logging
from collections import UserDict
from itertools import chain
from traceback import format_list, extract_stack
from typing import NewType, Optional, Literal

from bpy.types import NodeSocket
from sverchok.core.sv_custom_exceptions import SvNoDataError
from sverchok.utils.handle_blender_data import BlTrees


SockId = NewType('SockId', str)
sv_logger = logging.getLogger('sverchok')


class DebugMemory(UserDict):
    _last_printed = dict()

    def __init__(self, data, print_all=True, print_trace=False):
        self.data = data
        self._print_all = print_all
        self._print_trace = print_trace

        self._id_sock: dict[SockId, NodeSocket] = dict()

        self._tree_len = 0
        self._node_len = 0
        self._sock_len = 0

        self._data_len = 100

    def __setitem__(self, key, value):
        if self._print_trace:
            for line in format_list(extract_stack()[4:-3]):
                print(line, end='')
        if key not in self.data:
            self.data[key] = value
            (self._pprint if self._print_all else self._pprint_id)(key, 'NEW')
        else:
            self.data[key] = value
            (self._pprint if self._print_all else self._pprint_id)(key, 'VALUE')

    def __delitem__(self, key):
        if self._print_trace:
            for line in format_list(extract_stack()[4:-3]):
                print(line, end='')
        (self._pprint if self._print_all else self._pprint_id)(key, 'DELETE')
        del self.data[key]

    def _pprint(self, changed_id, type_: Literal['NEW', 'DELETE', 'VALUE']):
        self._update_sockets()
        self._update_limits()

        print("SOCKETS DATA CACHE:")
        for id_, data in self.data.items():
            data = self._cut_text(str(data), self._data_len)
            if id_ == changed_id:
                if type_ == 'VALUE':
                    data = self._colorize(str(data), "GREEN")
                text = f"\t{self._to_address(id_, type_ != 'DELETE')}: {data},"
                if type_ == 'NEW':
                    print(self._colorize(text, "GREEN"))
                elif type_ == 'DELETE':
                    print(self._colorize(text, "RED"))
                else:
                    print(text)
            else:
                text = f"\t{self._to_address(id_)}: {data},"
                print(text)

    def _pprint_id(self, id_, type_: Literal['NEW', 'DELETE', 'VALUE']):
        self._update_sockets()
        self._update_limits()

        data = self.data[id_]
        data = self._cut_text(str(data), self._data_len)
        if type_ == 'VALUE':
            data = self._colorize(str(data), "GREEN")
        text = f"\t{self._to_address(id_, type_ != 'DELETE')}: {data},"
        if type_ == 'NEW':
            print(self._colorize(text, 'GREEN'))
        elif type_ == 'DELETE':
            print(self._colorize(text, 'RED'))
        else:
            print(text)

    def _update_sockets(self):
        self._id_sock.clear()
        for tree in BlTrees().sv_trees:
            for node in tree.nodes:
                for sock in chain(node.inputs, node.outputs):
                    if sock.bl_idname in {'NodeSocketVirtual', 'NodeSocketColor'}:
                        continue
                    if sock.socket_id in self._id_sock:
                        ds = self._id_sock[sock.socket_id]
                        sv_logger.debug(f"SOCKET ID DUPLICATION: "
                              f"1 - {ds.id_data.name} {ds.node.name=} {ds.name=}"
                              f"2 - {sock.id_data.name} {node.name=} {sock.name=}")
                    self._id_sock[sock.socket_id] = sock

    def _to_address(self, id_: SockId, colorize=True) -> str:
        if sock := self._id_sock.get(id_):
            return f"{sock.id_data.name:<{self._tree_len}}" \
                   f"|{sock.node.name:<{self._node_len}}" \
                   f"|{'out' if sock.is_output else 'in':<3}" \
                   f"|{sock.name:<{self._sock_len}}"
        else:
            return self._colorize(f"NOT FOUND ID({id_})", "YELLOW" if colorize else None)

    def _update_limits(self):
        for sock in self._id_sock.values():
            self._tree_len = max(self._tree_len, len(sock.id_data.name))
            self._node_len = max(self._node_len, len(sock.node.name))
            self._sock_len = max(self._sock_len, len(sock.name))

    @staticmethod
    def _colorize(text, color: Optional[Literal['GREEN', 'RED', 'YELLOW']] = None):
        if not color:
            return text
        elif color == 'GREEN':
            return f"\033[32m{text}\033[0m"
        elif color == 'RED':
            return f"\033[31m{text}\033[0m"
        elif color == 'YELLOW':
            return f"\033[33m{text}\033[0m"

    @staticmethod
    def _cut_text(text, max_size):
        if len(text) < max_size:
            return text
        else:
            start = text[:max_size//2-2]
            end = text[len(text) - (max_size//2-1):]
            return f"{start}...{end}"


socket_data_cache: dict[SockId, list] = dict()
# socket_data_cache = DebugMemory(socket_data_cache)


def sv_deep_copy(lst):
    """return deep copied data of list/tuple structure"""
    # faster than builtin deep copy for us.
    # useful for our limited case
    # we should be able to specify vectors here to get them create
    # or stop destroying them when in vector socket.
    if isinstance(lst, (list, tuple)):
        if lst and not isinstance(lst[0], (list, tuple)):
            return lst[:]
        return [sv_deep_copy(l) for l in lst]
    return lst


def sv_forget_socket(socket):
    """deletes socket data from cache"""
    try:
        del socket_data_cache[socket.socket_id]
    except KeyError:
        pass


def sv_set_socket(socket, data):
    """sets socket data for socket"""
    socket_data_cache[socket.socket_id] = data


def sv_get_socket(socket, deepcopy=True):
    """gets socket data from socket,
    if deep copy is True a deep copy is make_dep_dict,
    to increase performance if the node doesn't mutate input
    set to False and increase performance substanstilly
    """
    data = socket_data_cache.get(socket.socket_id)
    if data is not None:
        return sv_deep_copy(data) if deepcopy else data
    else:
        raise SvNoDataError(socket)


def get_output_socket_data(node, output_socket_name):
    """
    This method is intended to usage in internal tests mainly.
    Get data that the node has written to the output socket.
    Raises SvNoDataError if it hasn't written any.
    """
    socket = node.outputs[output_socket_name]
    sock_address = socket.socket_id
    if sock_address in socket_data_cache:
        return socket_data_cache[sock_address]
    else:
        raise SvNoDataError(socket)


def clear_all_socket_cache():
    """
    Reset socket cache for all node-trees.
    """
    socket_data_cache.clear()


def unregister():
    clear_all_socket_cache()

Functions

def clear_all_socket_cache()

Reset socket cache for all node-trees.

Expand source code
def clear_all_socket_cache():
    """
    Reset socket cache for all node-trees.
    """
    socket_data_cache.clear()
def get_output_socket_data(node, output_socket_name)

This method is intended to usage in internal tests mainly. Get data that the node has written to the output socket. Raises SvNoDataError if it hasn't written any.

Expand source code
def get_output_socket_data(node, output_socket_name):
    """
    This method is intended to usage in internal tests mainly.
    Get data that the node has written to the output socket.
    Raises SvNoDataError if it hasn't written any.
    """
    socket = node.outputs[output_socket_name]
    sock_address = socket.socket_id
    if sock_address in socket_data_cache:
        return socket_data_cache[sock_address]
    else:
        raise SvNoDataError(socket)
def sv_deep_copy(lst)

return deep copied data of list/tuple structure

Expand source code
def sv_deep_copy(lst):
    """return deep copied data of list/tuple structure"""
    # faster than builtin deep copy for us.
    # useful for our limited case
    # we should be able to specify vectors here to get them create
    # or stop destroying them when in vector socket.
    if isinstance(lst, (list, tuple)):
        if lst and not isinstance(lst[0], (list, tuple)):
            return lst[:]
        return [sv_deep_copy(l) for l in lst]
    return lst
def sv_forget_socket(socket)

deletes socket data from cache

Expand source code
def sv_forget_socket(socket):
    """deletes socket data from cache"""
    try:
        del socket_data_cache[socket.socket_id]
    except KeyError:
        pass
def sv_get_socket(socket, deepcopy=True)

gets socket data from socket, if deep copy is True a deep copy is make_dep_dict, to increase performance if the node doesn't mutate input set to False and increase performance substanstilly

Expand source code
def sv_get_socket(socket, deepcopy=True):
    """gets socket data from socket,
    if deep copy is True a deep copy is make_dep_dict,
    to increase performance if the node doesn't mutate input
    set to False and increase performance substanstilly
    """
    data = socket_data_cache.get(socket.socket_id)
    if data is not None:
        return sv_deep_copy(data) if deepcopy else data
    else:
        raise SvNoDataError(socket)
def sv_set_socket(socket, data)

sets socket data for socket

Expand source code
def sv_set_socket(socket, data):
    """sets socket data for socket"""
    socket_data_cache[socket.socket_id] = data
def unregister()
Expand source code
def unregister():
    clear_all_socket_cache()

Classes

class DebugMemory (data, print_all=True, print_trace=False)
Expand source code
class DebugMemory(UserDict):
    _last_printed = dict()

    def __init__(self, data, print_all=True, print_trace=False):
        self.data = data
        self._print_all = print_all
        self._print_trace = print_trace

        self._id_sock: dict[SockId, NodeSocket] = dict()

        self._tree_len = 0
        self._node_len = 0
        self._sock_len = 0

        self._data_len = 100

    def __setitem__(self, key, value):
        if self._print_trace:
            for line in format_list(extract_stack()[4:-3]):
                print(line, end='')
        if key not in self.data:
            self.data[key] = value
            (self._pprint if self._print_all else self._pprint_id)(key, 'NEW')
        else:
            self.data[key] = value
            (self._pprint if self._print_all else self._pprint_id)(key, 'VALUE')

    def __delitem__(self, key):
        if self._print_trace:
            for line in format_list(extract_stack()[4:-3]):
                print(line, end='')
        (self._pprint if self._print_all else self._pprint_id)(key, 'DELETE')
        del self.data[key]

    def _pprint(self, changed_id, type_: Literal['NEW', 'DELETE', 'VALUE']):
        self._update_sockets()
        self._update_limits()

        print("SOCKETS DATA CACHE:")
        for id_, data in self.data.items():
            data = self._cut_text(str(data), self._data_len)
            if id_ == changed_id:
                if type_ == 'VALUE':
                    data = self._colorize(str(data), "GREEN")
                text = f"\t{self._to_address(id_, type_ != 'DELETE')}: {data},"
                if type_ == 'NEW':
                    print(self._colorize(text, "GREEN"))
                elif type_ == 'DELETE':
                    print(self._colorize(text, "RED"))
                else:
                    print(text)
            else:
                text = f"\t{self._to_address(id_)}: {data},"
                print(text)

    def _pprint_id(self, id_, type_: Literal['NEW', 'DELETE', 'VALUE']):
        self._update_sockets()
        self._update_limits()

        data = self.data[id_]
        data = self._cut_text(str(data), self._data_len)
        if type_ == 'VALUE':
            data = self._colorize(str(data), "GREEN")
        text = f"\t{self._to_address(id_, type_ != 'DELETE')}: {data},"
        if type_ == 'NEW':
            print(self._colorize(text, 'GREEN'))
        elif type_ == 'DELETE':
            print(self._colorize(text, 'RED'))
        else:
            print(text)

    def _update_sockets(self):
        self._id_sock.clear()
        for tree in BlTrees().sv_trees:
            for node in tree.nodes:
                for sock in chain(node.inputs, node.outputs):
                    if sock.bl_idname in {'NodeSocketVirtual', 'NodeSocketColor'}:
                        continue
                    if sock.socket_id in self._id_sock:
                        ds = self._id_sock[sock.socket_id]
                        sv_logger.debug(f"SOCKET ID DUPLICATION: "
                              f"1 - {ds.id_data.name} {ds.node.name=} {ds.name=}"
                              f"2 - {sock.id_data.name} {node.name=} {sock.name=}")
                    self._id_sock[sock.socket_id] = sock

    def _to_address(self, id_: SockId, colorize=True) -> str:
        if sock := self._id_sock.get(id_):
            return f"{sock.id_data.name:<{self._tree_len}}" \
                   f"|{sock.node.name:<{self._node_len}}" \
                   f"|{'out' if sock.is_output else 'in':<3}" \
                   f"|{sock.name:<{self._sock_len}}"
        else:
            return self._colorize(f"NOT FOUND ID({id_})", "YELLOW" if colorize else None)

    def _update_limits(self):
        for sock in self._id_sock.values():
            self._tree_len = max(self._tree_len, len(sock.id_data.name))
            self._node_len = max(self._node_len, len(sock.node.name))
            self._sock_len = max(self._sock_len, len(sock.name))

    @staticmethod
    def _colorize(text, color: Optional[Literal['GREEN', 'RED', 'YELLOW']] = None):
        if not color:
            return text
        elif color == 'GREEN':
            return f"\033[32m{text}\033[0m"
        elif color == 'RED':
            return f"\033[31m{text}\033[0m"
        elif color == 'YELLOW':
            return f"\033[33m{text}\033[0m"

    @staticmethod
    def _cut_text(text, max_size):
        if len(text) < max_size:
            return text
        else:
            start = text[:max_size//2-2]
            end = text[len(text) - (max_size//2-1):]
            return f"{start}...{end}"

Ancestors

  • collections.UserDict
  • collections.abc.MutableMapping
  • collections.abc.Mapping
  • collections.abc.Collection
  • collections.abc.Sized
  • collections.abc.Iterable
  • collections.abc.Container