<commit_before>from annotypes import add_call_types, TYPE_CHECKING, stringify_error
from cothread import cothread
from p4p import Value
from p4p.server import Server, DynamicProvider, ServerOperation
from p4p.server.cothread import Handler, SharedPV

from malcolm.core import Subscribe, Error, APublished, Controller, Delta, \
    Return, Response, Put, Post, Unsubscribe, \
    ProcessPublishHook, method_return_unpacked, Method, \
    BlockMeta, MethodModel, RLock
from malcolm.modules import builtin
from .pvaconvert import convert_dict_to_value, update_path, \
    convert_value_to_dict

if TYPE_CHECKING:
    from typing import Optional, Dict, List, Set


class BlockHandler(Handler):
    def __init__(self, controller, field=None):
        # type: (Controller, str) -> None
        self.controller = controller
        # Lock to control access to self.pv
        self._lock = RLock()
        self.field = field
        self.pv = None  # type: Optional[SharedPV]
        self.value = None  # type: Value
        self.put_paths = None  # type: Set[str]

    def rpc(self, pv, op):
        # type: (SharedPV, ServerOperation) -> None
        value = op.value()
        if value.getID() == "epics:nt/NTURI:1.0":
            # We got an NTURI, get path from path and parameters from query
            assert value.scheme == "pva", \
                "Can only handle NTURI with scheme=pva"
            prefix = self.controller.mri + "."
            assert value.path.startswith(prefix), \
                "NTURI path '%s' doesn't start with '%s'" % (value.path, prefix)
            method = value.path[len(prefix):]
            parameters = convert_value_to_dict(value.query)
        else:
            # We got something else, take path from pvRequest method and our mri
            # and parameters from the full value
            if self.field is not None:
                # We already know the method name
                method = self.field
            else:
                # Get the path and string "value" from the put value
                method = op.pvRequest().get("method")
                assert method, "No 'method' in pvRequest:\n%s" % op.pvRequest()
            parameters = convert_value_to_dict(value)
        path = [self.controller.mri, method]
        view = self.controller.block_view()[method]
        assert isinstance(view, Method), \
            "%s.%s is not a Method so cannot do RPC" % tuple(path)
        add_wrapper = method_return_unpacked() in view.meta.tags
        self.controller.log.debug(
            "RPC method {method} called with params: {params}".format(
                method=method,
                params=parameters
            ))
        post = Post(path=path, parameters=parameters)

        def handle_post_response(response):
            # type: (Response) -> None
            if isinstance(response, Return):
                if add_wrapper:
                    # Method gave us return unpacked (bare string or other type)
                    # so we must wrap it in a structure to send it
                    ret = {"return": response.value}
                else:
                    ret = response.value
                v = convert_dict_to_value(ret)
                op.done(v)
            else:
                if isinstance(response, Error):
                    message = stringify_error(response.message)
                else:
                    message = "BadResponse: %s" % response.to_dict()
                op.done(error=message)

        post.set_callback(handle_post_response)
        self.controller.handle_request(post).get()

    def put(self, pv, op):
        # type: (SharedPV, ServerOperation) -> None
        path = [self.controller.mri]
        # We work out what to Put by taking every field that is marked as
        # changed and walking up the tree, adding every dotted field name
        # to the tree on the way up. This set will contain something like:
        #  {"attr.value", "attr"}
        # Or for a table:<commit_msg>Improve logging of RPC calls<commit_after>from annotypes import add_call_types, TYPE_CHECKING, stringify_error
from cothread import cothread
from p4p import Value
from p4p.server import Server, DynamicProvider, ServerOperation
from p4p.server.cothread import Handler, SharedPV

from malcolm.core import Subscribe, Error, APublished, Controller, Delta, \
    Return, Response, Put, Post, Unsubscribe, \
    ProcessPublishHook, method_return_unpacked, Method, \
    BlockMeta, MethodModel, RLock
from malcolm.modules import builtin
from .pvaconvert import convert_dict_to_value, update_path, \
    convert_value_to_dict

if TYPE_CHECKING:
    from typing import Optional, Dict, List, Set


class BlockHandler(Handler):
    def __init__(self, controller, field=None):
        # type: (Controller, str) -> None
        self.controller = controller
        # Lock to control access to self.pv
        self._lock = RLock()
        self.field = field
        self.pv = None  # type: Optional[SharedPV]
        self.value = None  # type: Value
        self.put_paths = None  # type: Set[str]

    def rpc(self, pv, op):
        # type: (SharedPV, ServerOperation) -> None
        value = op.value()
        if value.getID() == "epics:nt/NTURI:1.0":
            # We got an NTURI, get path from path and parameters from query
            assert value.scheme == "pva", \
                "Can only handle NTURI with scheme=pva"
            prefix = self.controller.mri + "."
            assert value.path.startswith(prefix), \
                "NTURI path '%s' doesn't start with '%s'" % (value.path, prefix)
            method = value.path[len(prefix):]
            parameters = convert_value_to_dict(value.query)
        else:
            # We got something else, take path from pvRequest method and our mri
            # and parameters from the full value
            if self.field is not None:
                # We already know the method name
                method = self.field
            else:
                # Get the path and string "value" from the put value
                method = op.pvRequest().get("method")
                assert method, "No 'method' in pvRequest:\n%s" % op.pvRequest()
            parameters = convert_value_to_dict(value)
        path = [self.controller.mri, method]
        view = self.controller.block_view()[method]
        assert isinstance(view, Method), \
            "%s.%s is not a Method so cannot do RPC" % tuple(path)
        add_wrapper = method_return_unpacked() in view.meta.tags
        self.controller.log.debug(
            "%s: RPC method %s called with params %s", self.controller.mri,
            method, parameters)
        post = Post(path=path, parameters=parameters)

        def handle_post_response(response):
            # type: (Response) -> None
            if isinstance(response, Return):
                if add_wrapper:
                    # Method gave us return unpacked (bare string or other type)
                    # so we must wrap it in a structure to send it
                    ret = {"return": response.value}
                else:
                    ret = response.value
                v = convert_dict_to_value(ret)
                op.done(v)
            else:
                if isinstance(response, Error):
                    message = stringify_error(response.message)
                else:
                    message = "BadResponse: %s" % response.to_dict()
                op.done(error=message)

        post.set_callback(handle_post_response)
        self.controller.handle_request(post).get()

    def put(self, pv, op):
        # type: (SharedPV, ServerOperation) -> None
        path = [self.controller.mri]
        # We work out what to Put by taking every field that is marked as
        # changed and walking up the tree, adding every dotted field name
        # to the tree on the way up. This set will contain something like:
        #  {"attr.value", "attr"}
        # Or for a table:
{'fraction_non_alphanumeric': 0.08801404212637914, 'fraction_numerical': 0.0012537612838515546, 'mean_word_length': 2.0434948492941625, 'pattern_counts': {'":': 4, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3011686', 'n_tokens_mistral': 2195, 'n_tokens_neox': 2118, 'n_words': 697}