{"owner":"pylint-dev","repo":"pylint","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# Agent guidelines\n\n## AST-based checking\n\nPylint is an AST-based linter built on [astroid](https://github.com/pylint-dev/astroid).\nWhen writing or modifying checkers, prefer **`isinstance` against concrete astroid node\ntypes** combined with domain knowledge of Python syntax, rather than duck-typing with\n`getattr`/`hasattr`.\n\nThe visitor pattern from `BaseChecker` already narrows the node type for you: a\n`visit_call` method only receives `nodes.Call`, a `visit_assign` only receives\n`nodes.Assign`, and so on. Inside such a method, the node's structure is known — its\nattributes follow from the grammar (e.g. a `nodes.Call` always has `.func` and `.args`).\nWalk and type-check that known structure with `isinstance` instead of probing for\nattributes defensively.\n\nGood:\n\n```python\ndef visit_call(self, node: nodes.Call) -> None:\n    if isinstance(node.func, nodes.Attribute):\n        ...\n```\n\nAvoid:\n\n```python\ndef visit_call(self, node) -> None:\n    if hasattr(node.func, \"attrname\"):  # don't probe — check the type\n        ...\n```\n\nThis keeps checks precise, readable, and aligned with astroid's typed node API.\n\n### Caveat: astroid proxies and `Uninferable`\n\n`isinstance` checks the _static_ node type. Some astroid objects — `bases.Instance`,\n`Generator`, `BoundMethod`/`UnboundMethod`, and `util.Uninferable` — resolve attributes\nthrough `__getattr__`, proxying to a wrapped node. So `hasattr(obj, \"x\")` can be True at\nruntime on an object whose class has no `x`, and a naive `isinstance(obj, ConcreteNode)`\nwill _drop_ cases the old `hasattr` caught (e.g. an exception inferred to an `Instance`\nthat proxies `ancestors`, or an `AsyncGenerator` that proxies `locals`).\n\nWhen replacing such a guard:\n\n- Include the proxy base in the type tuple — e.g. `(nodes.ClassDef, bases.Instance)`, or\n  `(nodes.LocalsDictNodeNG, bases.Proxy)` for anything exposing `qname`.\n- If the check is a behavioral _capability_ spanning heterogeneous nodes with no common\n  base — or the proxied node may legitimately lack the attribute (a `BoundMethod` can\n  wrap a `Lambda`, which has no `.decorators`) — keep `hasattr`. That is honest\n  duck-typing, not a grammar check, and `isinstance` cannot express it safely.\n"},"files":{"AGENTS.md":"# Agent guidelines\n\n## AST-based checking\n\nPylint is an AST-based linter built on [astroid](https://github.com/pylint-dev/astroid).\nWhen writing or modifying checkers, prefer **`isinstance` against concrete astroid node\ntypes** combined with domain knowledge of Python syntax, rather than duck-typing with\n`getattr`/`hasattr`.\n\nThe visitor pattern from `BaseChecker` already narrows the node type for you: a\n`visit_call` method only receives `nodes.Call`, a `visit_assign` only receives\n`nodes.Assign`, and so on. Inside such a method, the node's structure is known — its\nattributes follow from the grammar (e.g. a `nodes.Call` always has `.func` and `.args`).\nWalk and type-check that known structure with `isinstance` instead of probing for\nattributes defensively.\n\nGood:\n\n```python\ndef visit_call(self, node: nodes.Call) -> None:\n    if isinstance(node.func, nodes.Attribute):\n        ...\n```\n\nAvoid:\n\n```python\ndef visit_call(self, node) -> None:\n    if hasattr(node.func, \"attrname\"):  # don't probe — check the type\n        ...\n```\n\nThis keeps checks precise, readable, and aligned with astroid's typed node API.\n\n### Caveat: astroid proxies and `Uninferable`\n\n`isinstance` checks the _static_ node type. Some astroid objects — `bases.Instance`,\n`Generator`, `BoundMethod`/`UnboundMethod`, and `util.Uninferable` — resolve attributes\nthrough `__getattr__`, proxying to a wrapped node. So `hasattr(obj, \"x\")` can be True at\nruntime on an object whose class has no `x`, and a naive `isinstance(obj, ConcreteNode)`\nwill _drop_ cases the old `hasattr` caught (e.g. an exception inferred to an `Instance`\nthat proxies `ancestors`, or an `AsyncGenerator` that proxies `locals`).\n\nWhen replacing such a guard:\n\n- Include the proxy base in the type tuple — e.g. `(nodes.ClassDef, bases.Instance)`, or\n  `(nodes.LocalsDictNodeNG, bases.Proxy)` for anything exposing `qname`.\n- If the check is a behavioral _capability_ spanning heterogeneous nodes with no common\n  base — or the proxied node may legitimately lack the attribute (a `BoundMethod` can\n  wrap a `Lambda`, which has no `.decorators`) — keep `hasattr`. That is honest\n  duck-typing, not a grammar check, and `isinstance` cannot express it safely.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","title":"AGENTS.md","content":"# Agent guidelines\n\n## AST-based checking\n\nPylint is an AST-based linter built on [astroid](https://github.com/pylint-dev/astroid).\nWhen writing or modifying checkers, prefer **`isinstance` against concrete astroid node\ntypes** combined with domain knowledge of Python syntax, rather than duck-typing with\n`getattr`/`hasattr`.\n\nThe visitor pattern from `BaseChecker` already narrows the node type for you: a\n`visit_call` method only receives `nodes.Call`, a `visit_assign` only receives\n`nodes.Assign`, and so on. Inside such a method, the node's structure is known — its\nattributes follow from the grammar (e.g. a `nodes.Call` always has `.func` and `.args`).\nWalk and type-check that known structure with `isinstance` instead of probing for\nattributes defensively.\n\nGood:\n\n```python\ndef visit_call(self, node: nodes.Call) -> None:\n    if isinstance(node.func, nodes.Attribute):\n        ...\n```\n\nAvoid:\n\n```python\ndef visit_call(self, node) -> None:\n    if hasattr(node.func, \"attrname\"):  # don't probe — check the type\n        ...\n```\n\nThis keeps checks precise, readable, and aligned with astroid's typed node API.\n\n### Caveat: astroid proxies and `Uninferable`\n\n`isinstance` checks the _static_ node type. Some astroid objects — `bases.Instance`,\n`Generator`, `BoundMethod`/`UnboundMethod`, and `util.Uninferable` — resolve attributes\nthrough `__getattr__`, proxying to a wrapped node. So `hasattr(obj, \"x\")` can be True at\nruntime on an object whose class has no `x`, and a naive `isinstance(obj, ConcreteNode)`\nwill _drop_ cases the old `hasattr` caught (e.g. an exception inferred to an `Instance`\nthat proxies `ancestors`, or an `AsyncGenerator` that proxies `locals`).\n\nWhen replacing such a guard:\n\n- Include the proxy base in the type tuple — e.g. `(nodes.ClassDef, bases.Instance)`, or\n  `(nodes.LocalsDictNodeNG, bases.Proxy)` for anything exposing `qname`.\n- If the check is a behavioral _capability_ spanning heterogeneous nodes with no common\n  base — or the proxied node may legitimately lack the attribute (a `BoundMethod` can\n  wrap a `Lambda`, which has no `.decorators`) — keep `hasattr`. That is honest\n  duck-typing, not a grammar check, and `isinstance` cannot express it safely.\n","category":"root","tokens":552}]}