[Python] The cognitive complexity of the function is too high

Discussão em 'Python' iniciado por Stack, Maio 29, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to write the code for Moore machine. It is working and the test coverage is 100%. But the cognitive complexity of the function test() is too high - 11 (I need it to be <10). How do I fix this? I've tried several methods, but then test coverage falls.

    class MachineException(Exception):
    def __init__(self, message):
    super().__init__(message)
    self.message = message


    class MooreMachine:
    def __init__(self):
    self.state = "S0"
    self.v = None
    self.visited_methods = set()
    self.visited_states = {self.state}
    self.transitions = set()

    self._init_graph()
    self._init_outputs()

    def _init_graph(self):
    self.graph = {
    "S0": {"sit": ["S1"], "view": ["S3"], "pan": ["S4"]},
    "S1": {"sit": ["S2"]},
    "S2": {},
    "S3": {"sit": ["S5"]},
    "S4": {"pan": ["S3"], "hike": ["S5"]},
    "S5": {"speed": ["S2"]}
    }

    def _init_outputs(self):
    self.outputs = {
    "S0": "m5",
    "S1": "m4",
    "S2": "m2",
    "S3": "m3",
    "S4": "m4",
    "S5": "m3"
    }

    def assign_v(self, value):
    self.v = value

    def get_output(self):
    return self.outputs.get(self.state, "unknown")

    def seen_method(self, method_name):
    return method_name in self.visited_methods

    def _can_transition(self, method_name):
    return method_name in self.graph.get(self.state, {})

    def _method_exists(self, method_name):
    return any(method_name in methods
    for methods in self.graph.values())

    def _execute_transition(self, method_name):
    target = self.graph[self.state][method_name][0]
    self._make_transition(method_name, target)

    def _make_transition(self, method_name, target_state):
    self.transitions.add((self.state, method_name, target_state))
    self.visited_methods.add(method_name)
    self.state = target_state
    self.visited_states.add(target_state)

    def has_path_to(self, target_state):
    if target_state == self.state:
    return True

    visited = set()
    return self._dfs_path_check(self.state, target_state, visited)

    def _dfs_path_check(self, current, target, visited):
    if current == target:
    return True
    if current in visited:
    return False

    visited.add(current)

    for method in self.graph.get(current, {}):
    for neighbor in self.graph[current][method]:
    if self._dfs_path_check(neighbor, target, visited):
    return True
    return False

    def __getattr__(self, name):
    if not name.startswith("select_"):
    raise MachineException("unknown")

    method_name = name[7:]

    if not self._method_exists(method_name):
    raise MachineException("unknown")

    if not self._can_transition(method_name):
    raise MachineException("unsupported")

    return lambda: self._execute_transition(method_name)


    def test():
    obj = MooreMachine()

    # Initial state
    assert obj.state == "S0"
    assert obj.get_output() == "m5"
    assert not obj.seen_method("sit")

    # Basic sit transition
    obj.select_sit()
    assert obj.state == "S1"
    assert obj.get_output() == "m4"
    assert obj.seen_method("sit")

    # Second sit transition
    obj.select_sit()
    assert obj.state == "S2"
    assert obj.get_output() == "m2"

    # Path checking from S2
    assert not obj.has_path_to("S0")
    assert not obj.has_path_to("S1")
    assert obj.has_path_to("S2") # self

    # View transition
    obj.state = "S0"
    obj.select_view()
    assert obj.state == "S3"
    assert obj.get_output() == "m3"

    # Sit from S3
    obj.select_sit()
    assert obj.state == "S5"
    assert obj.get_output() == "m3"

    # Speed transition
    obj.select_speed()
    assert obj.state == "S2"
    assert obj.get_output() == "m2"

    # Pan transition
    obj.state = "S0"
    obj.select_pan()
    assert obj.state == "S4"
    assert obj.get_output() == "m4"

    # Hike transition with v=0
    obj.assign_v(0)
    obj.select_hike()
    assert obj.state == "S5"
    assert obj.get_output() == "m3"

    # Path checking from S4
    obj.state = "S4"
    assert obj.has_path_to("S2")
    assert obj.has_path_to("S3")
    assert obj.has_path_to("S5")
    assert not obj.has_path_to("S0")
    assert not obj.has_path_to("S1")

    # Pan from S4
    obj.select_pan()
    assert obj.state == "S3"

    # Invalid method
    exception_raised = False
    try:
    obj.select_invalid()
    except MachineException as e:
    assert e.message == "unknown"
    exception_raised = True
    assert exception_raised

    # Unsupported method
    exception_raised = False
    try:
    obj.state = "S2"
    obj.select_speed()
    except MachineException as e:
    assert e.message == "unsupported"
    exception_raised = True
    assert exception_raised

    # Check transitions recording
    obj.state = "S0"
    obj.select_sit()
    obj.select_sit()
    assert ("S0", "sit", "S1") in obj.transitions
    assert ("S1", "sit", "S2") in obj.transitions

    # Check visited states
    assert "S0" in obj.visited_states
    assert "S1" in obj.visited_states
    assert "S2" in obj.visited_states

    # Check method existence
    assert any("sit" in methods for methods in obj.graph.values())
    assert not any("invalid" in methods
    for methods in obj.graph.values())

    # Check can_transition
    obj.state = "S0"
    assert obj._can_transition("sit")
    assert not obj._can_transition("speed")

    # Check getattr with non-select
    exception_raised = False
    try:
    obj.non_select_method()
    except MachineException as e:
    assert e.message == "unknown"
    exception_raised = True
    assert exception_raised

    # Check assign_v
    obj.assign_v(1)
    assert obj.v == 1

    # Full cycle coverage
    obj.state = "S0"
    obj.select_sit() # S0 -> S1
    obj.select_sit() # S1 -> S2
    obj.state = "S0"
    obj.select_view() # S0 -> S3
    obj.select_sit() # S3 -> S5
    obj.select_speed() # S5 -> S2
    obj.state = "S0"
    obj.select_pan() # S0 -> S4
    obj.select_hike() # S4 -> S5 (with v=1)

    # Check main function
    machine = main()
    assert isinstance(machine, MooreMachine)
    assert machine.state == "S0"

    return True


    def main():
    return MooreMachine()



    I've tried adding additional functions, such as:

    def assert_raises(exception_type, message, func, *args):
    try:
    func(*args)
    except exception_type as e:
    assert e.message == message
    return True
    return False


    Firstly, I added this function - the test coverage fell. Then, my friend adviced adding other functions inside test() - they didn't work because of some troubles with pytest (I don't know what happened). Then, I added these same functions outside test() and tests stopped working.

    Continue reading...

Compartilhe esta Página