How to resolve Pytest: Cell Var From Loop

How to resolve Pytest: Cell Var From Loop

Recently, I came across following pylint warning:

$ pylint test.py
************* Module test
test.py:29:28: W0640: Cell variable sort_key defined in loop (cell-var-from-loop)

------------------------------------------------------------------
Your code has been rated at 9.41/10 (previous run: 9.41/10, +0.00)

Here’s an example code snippet that triggered such warning:

"""Replace lambda with operator.itemgetter to sort List[dict] by dict key."""
import itertools
from typing import Dict, List


def parse_raw_message(msg: str) -> Dict[str, str]:
    """Parse a pipe-delimited message into a key:value pairs & skip empty keys."""
    split_messages = msg.split("|")
    iter_chunk_size = 2
    args = [iter(split_messages)] * iter_chunk_size
    return {
        key: value
        for key, value in itertools.zip_longest(*args, fillvalue=None)
        if key
    }


def parse_and_group(
    raw_messages: Dict[str, List[Dict[str, str]]], sorting_keys: Dict[str, str]
) -> Dict[str, List[Dict[str, str]]]:
    """Parse raw messages into a dict and sort them by a specific key."""
    parsed_messages: Dict[str, List[Dict[str, str]]] = {}

    for message_type, messages in raw_messages.items():
        sort_key = sorting_keys[message_type]

        parsed_messages[message_type] = sorted(
            (parse_raw_message(msg) for msg in messages),
            key=lambda d: d[sort_key]
        )

    return parsed_messages


if __name__ == "__main__":
    some_raw_messages = {
        "a": [
            "key_a|3|b|2|c|o",
            "key_a|2|b|3|c|f",
            "key_a|1|b|4|c|g",
        ],
        "b": [
            "key_b|2|b|2|c|o",
            "key_b|3|b|1|c|h",
            "key_b|1|b|0|c|j",
        ],
    }
    some_sorting_keys = {
        "a": "key_a",
        "b": "key_b",
    }
    print(parse_and_group(some_raw_messages, some_sorting_keys))

The warning is triggered for the nested lambda function:

        parsed_messages[message_type] = sorted(
            (parse_raw_message(msg) for msg in messages),
            key=lambda d: d[sort_key]
        )

which looks up variables from the parent scope when executed, not when defined. This is nicely explained by Martijn Pieters .

As it turned out, it’s a false positive warning, which I reported .
It was also reported in the past .

To get rid of this warning, we have to replace lambda with operator.itemgetter():

import operator

...
        parsed_messages[message_type] = sorted(
            (parse_raw_message(msg) for msg in messages),
            key=operator.itemgetter(sort_key)
        )
...