Source code for rude.rules.pycodestyle.line_length

"""
Line length rules: E501.

E501: line too long
"""

from __future__ import annotations

from collections.abc import Iterator
from typing import TYPE_CHECKING, Any, ClassVar

from rude.core.node_types import NodeType
from rude.core.rule import Rule
from rude.core.types import Diagnostic

if TYPE_CHECKING:
    from rude.core.node import Node


[docs] class LineTooLong(Rule): """ E501: Line too long. Rationale: PEP 8 recommends a maximum line length of 79 characters for readability and side-by-side diff viewing. Example:: # Bad x = "this is a very long line that exceeds the maximum line length limit" # E501 # Good x = ( "this is a long line that has been" " wrapped for readability" ) Configuration: [tool.rude.rules.E501] max_line_length = 79 # default """ code: ClassVar[str] = "E501" message: ClassVar[str] = "line too long ({length} > {max_length} characters)" node_types = {NodeType.MODULE} max_line_length: int = 79
[docs] def configure(self, options: dict[str, Any]) -> None: self.max_line_length = options.get("max_line_length", 79)
[docs] def check(self, node: Node) -> Iterator[Diagnostic]: # Only run once at module level if node.parent is not None: return ctx = node.ctx max_len = self.max_line_length line_infos = ctx._line_infos if line_infos is not None: # Fast path: use pre-computed byte length from Rust. # byte_len >= char_len for UTF-8, so byte_len <= max skips safely. for lineno_0, info in enumerate(line_infos): byte_len = info.line_len if byte_len <= max_len: continue # Byte length exceeded — decode to get true character length line = ctx.lines[lineno_0].decode("utf-8", errors="replace") length = len(line.rstrip("\r\n")) if length <= max_len: continue lineno = lineno_0 + 1 # Skip lines that are mostly URLs stripped = line.strip() if self._is_url_line(stripped): continue # Skip noqa lines if ctx.has_noqa(lineno, self.code): continue yield self.diagnostic_at( lineno, max_len, self.message.format(length=length, max_length=max_len), ) else: # Fallback: decode every line for lineno, line_bytes in enumerate(ctx.lines, 1): line = line_bytes.decode("utf-8", errors="replace") length = len(line.rstrip("\r\n")) if length > max_len: # Skip lines that are mostly URLs stripped = line.strip() if self._is_url_line(stripped): continue # Skip noqa lines if ctx.has_noqa(lineno, self.code): continue yield self.diagnostic_at( lineno, max_len, self.message.format(length=length, max_length=max_len), )
def _is_url_line(self, line: str) -> bool: """Check if line is mostly a URL (common exception).""" # If line contains a URL and is mostly that URL, skip url_prefixes = ("http://", "https://", "ftp://", "file://") for prefix in url_prefixes: if prefix in line: # URL makes up significant portion of the line url_start = line.find(prefix) url_part = ( line[url_start:].split()[0] if line[url_start:].split() else line[url_start:] ) if len(url_part) > self.max_line_length * 0.5: return True return False
LINE_LENGTH_RULES = [ LineTooLong, ] __all__ = [ "LINE_LENGTH_RULES", "LineTooLong", ]