Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

radon hal yields Halstead complexity measures of zero for complex files #266

Open
fmv1992 opened this issue Jul 4, 2024 · 2 comments
Open

Comments

@fmv1992
Copy link

fmv1992 commented Jul 4, 2024

Are these metrics expected for these cases?

f1.py:
    h1: 0
    h2: 0
    N1: 0
    N2: 0
    vocabulary: 0
    length: 0
    calculated_length: 0
    volume: 0
    difficulty: 0
    effort: 0
    time: 0.0
    bugs: 0.0
f2.py:
    h1: 1
    h2: 2
    N1: 1
    N2: 2
    vocabulary: 3
    length: 3
    calculated_length: 2.0
    volume: 4.754887502163469
    difficulty: 0.5
    effort: 2.3774437510817346
    time: 0.1320802083934297
    bugs: 0.0015849625007211565
f3.py:
    h1: 2
    h2: 4
    N1: 2
    N2: 4
    vocabulary: 6
    length: 6
    calculated_length: 10.0
    volume: 15.509775004326936
    difficulty: 1.0
    effort: 15.509775004326936
    time: 0.861654166907052
    bugs: 0.005169925001442312

Cases:

./f1.py
-------------------------------------------------------------------------------
class A:
    a = 1
    b = 2
-------------------------------------------------------------------------------
./f2.py
-------------------------------------------------------------------------------
class A:
    a = 1
    b = 2


def main():
    pass


if __name__ == "__main__":
    main()
-------------------------------------------------------------------------------
./f3.py
-------------------------------------------------------------------------------
class A:
    a = 1
    b = 2

    def add(self):
        return self.a + self.b


def main():
    pass


if __name__ == "__main__":
    main()

I guess I don't understand why I'm getting zeros there. An actual pretty complex code that I checked yielded hal metrics with values as 0 and that was surprising to me.

I understand that the code that gets 0 actually does not run anything, but still, it's surprising to me.

@rubik
Copy link
Owner

rubik commented Oct 21, 2024

@fmv1992 Hi Felipe, yes, for those simple cases those results are expected. Halstead metrics are based on the presence of operators, and the first case for example doesn't contain any operators (assignment isn't considered as such). Regarding the more complex cases you mentioned, if you could share a snippet I could take a look.

@mwchase
Copy link

mwchase commented Dec 1, 2024

I can give an example of a file from one of my projects that radon measures as all zeros, despite the fact that there's definitely a bunch of stuff happening in it. Like, if it makes sense that the following code gets a 0 from radon, I'm not totally sure what radon is supposed to be measuring. This probably goes back to #160 (and #161 as a corollary).

"""The runtime logic module."""

from __future__ import annotations

import collections
import typing

import attr
import trio

from motr.core import exc as _exc
from motr.core import result as _result
from motr.core import stream_multiplexer as _stream_multiplexer

if typing.TYPE_CHECKING:
    from motr.core import compendium as _compendium
    from motr.core import datum as _datum

Task = typing.Callable[
    [_stream_multiplexer.Multiplexer], typing.Awaitable[_result.Result]
]


@attr.dataclass(frozen=True)
class CallStepOnce:
    """Wrapper class that calls a step at most once."""

    started: trio.Event = attr.Factory(trio.Event)
    finished: trio.Event = attr.Factory(trio.Event)

    async def __call__(self, step: Step) -> None:
        """Perform the given step no more than once."""
        if self.started.is_set():
            await self.finished.wait()
            return
        self.started.set()
        try:
            await step()
        finally:
            self.finished.set()


@attr.dataclass(frozen=True)
class Execution:
    """Container that matches requests for a datum to the relevant task."""

    compendium: _compendium.Compendium
    reporter: typing.Callable[[Task], typing.ContextManager[None]]
    results: dict[_result.Result, list[Task]]
    multiplexer: _stream_multiplexer.Multiplexer = attr.Factory(
        _stream_multiplexer.Multiplexer
    )
    steps: collections.defaultdict[Task, CallStepOnce] = attr.Factory(
        lambda: collections.defaultdict(CallStepOnce)
    )

    async def __call__(self, datum: _datum.Datum) -> None:
        """Find and perform the task associated with the given datum."""
        task = self.compendium.parent(datum)
        await self.steps[task](Step(self, task))


@attr.dataclass(frozen=True)
class Step:
    """Wrapper that adapts a task to the runner code."""

    execution: Execution
    task: Task

    async def __call__(self) -> None:
        """Queue up all requirements, then carry out the task and report."""
        with self.execution.reporter(self.task):
            async with trio.open_nursery() as nursery:
                for datum in self.execution.compendium.inputs(self.task):
                    nursery.start_soon(self.execution, datum)
            result = await self.task(
                self.execution.multiplexer.with_name(str(self.task))
            )
            self.execution.results[result].append(self.task)
            if result.aborted:
                raise _exc.MOTRTaskError(f"Task {self.task!r} failed")


async def run_all(
    request: typing.Callable[[_datum.Datum], typing.Awaitable[None]],
    data: typing.Iterable[_datum.Datum],
) -> None:
    """Map the given callable over all data, asynchronously."""
    async with trio.open_nursery() as nursery:
        for datum in data:
            nursery.start_soon(request, datum)

Radon output:

↪ radon hal src/motr/core/runner.py
src/motr/core/runner.py:
    h1: 0
    h2: 0
    N1: 0
    N2: 0
    vocabulary: 0
    length: 0
    calculated_length: 0
    volume: 0
    difficulty: 0
    effort: 0
    time: 0.0
    bugs: 0.0

And

↪ radon --version
6.0.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants