# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt from __future__ import annotations import sys from collections.abc import Iterable import astroid import pytest from astroid import nodes import pylint.checkers.non_ascii_names import pylint.interfaces import pylint.testutils class TestNonAsciiChecker(pylint.testutils.CheckerTestCase): CHECKER_CLASS = pylint.checkers.non_ascii_names.NonAsciiNameChecker checker: pylint.checkers.non_ascii_names.NonAsciiNameChecker @pytest.mark.skipif( sys.version_info < (3, 8), reason="requires python3.8 or higher" ) def test_kwargs_and_position_only(self) -> None: """Even the new position only and keyword only should be found.""" node = astroid.extract_node( """ def name( ok, not_økay, not_okay_defaułt=None, /, p_or_kw_okay=None, p_or_kw_not_økay=None, *, kw_arg_okay, kw_arg_not_økay, ): ... """ ) assert isinstance(node, nodes.FunctionDef) arguments = node.args posargs = list(arguments.posonlyargs) args = list(arguments.args) kwargs = list(arguments.kwonlyargs) with self.assertAddsMessages( pylint.testutils.MessageTest( msg_id="non-ascii-name", node=posargs[1], args=("Argument", "not_økay"), confidence=pylint.interfaces.HIGH, ), pylint.testutils.MessageTest( msg_id="non-ascii-name", node=posargs[2], args=("Argument", "not_okay_defaułt"), confidence=pylint.interfaces.HIGH, ), pylint.testutils.MessageTest( msg_id="non-ascii-name", node=args[1], args=("Argument", "p_or_kw_not_økay"), confidence=pylint.interfaces.HIGH, ), pylint.testutils.MessageTest( msg_id="non-ascii-name", node=kwargs[1], args=("Argument", "kw_arg_not_økay"), confidence=pylint.interfaces.HIGH, ), ignore_position=True, ): self.checker.visit_functiondef(node) @pytest.mark.parametrize( "code, assign_type", [ pytest.param( """ try: ... except ValueError as łol: #@ ... """, "Variable", id="try-except", ), pytest.param( """ class FooBar: łol = "test" #@ """, "Attribute", id="class_attribute", ), pytest.param( """ łol = "test" #@ """, "Variable", id="global_assign", ), pytest.param( """ def foobar(): łol="test" #@ """, "Variable", id="function_variable", ), pytest.param( """ for łol in os.listdir("."): #@ ... """, "Variable", id="for_loop_variable", ), pytest.param( """ [łoł for łol in os.listdir(".") #@ ] """, "Variable", id="inline_for_loop_variable", ), ], ) def test_assignname( self, code: str, assign_type: str, ) -> None: """Variables defined no matter where, should be checked for non ascii.""" assign_node = astroid.extract_node(code) if not isinstance(assign_node, nodes.AssignName): # For some elements we can't directly extract the assign # node, so we have to manually look in the children for it for child in assign_node.get_children(): if isinstance(child, nodes.AssignName): assign_node = child break # Just to make sure we found the correct node assert isinstance(assign_node, nodes.AssignName) with self.assertAddsMessages( pylint.testutils.MessageTest( msg_id="non-ascii-name", node=assign_node, args=(assign_type, "łol"), confidence=pylint.interfaces.HIGH, ), ignore_position=True, ): self.checker.visit_assignname(assign_node) @pytest.mark.parametrize( "import_statement, wrong_name", [ pytest.param("import fürimma", "fürimma", id="bad_single_main_module"), pytest.param( "import fürimma as okay", None, id="bad_single_main_module_with_okay_alias", ), pytest.param( "import fürimma, pathlib", "fürimma", id="bad_single_main_module_with_stdlib_import", ), pytest.param( "import pathlib, os, foobar, fürimma", "fürimma", id="stdlib_with_bad_single_main_module", ), pytest.param( "import pathlib, os, foobar, sys as systëm", "systëm", id="stdlib_with_bad_alias", ), pytest.param( "import fürimma as okay, pathlib", None, id="bad_single_main_module_with_okay_alias_with_stdlib_import", ), pytest.param( "import fürimma.submodule", "fürimma.submodule", id="bad_main_module" ), pytest.param( "import fürimma.submodule as submodule", None, id="bad_main_module_with_okay_alias", ), pytest.param( "import main_module.fürimma", "main_module.fürimma", id="bad_submodule" ), pytest.param( "import main_module.fürimma as okay", None, id="bad_submodule_with_okay_alias", ), pytest.param( "import main_module.fürimma as not_økay", "not_økay", id="bad_submodule_with_bad_alias", ), pytest.param( "from foo.bar import function", None, id="from_okay_module_import_okay" ), pytest.param( "from foo.bär import function", None, id="from_bad_module_import_okay" ), pytest.param( "from foo.bar import functiøn", "functiøn", id="from_okay_module_import_bad", ), pytest.param( "from foo.bar import functiøn as function", None, id="from_okay_module_import_bad_as_good", ), pytest.param( "from foo.bär import functiøn as function", None, id="from_bad_module_import_bad_as_good", ), pytest.param( "from foo.bar import functiøn as føl", "føl", id="from_okay_module_import_bad_as_bad", ), pytest.param( "from foo.bar import functiøn as good, bäd", "bäd", id="from_okay_module_import_bad_as_good_and_bad", ), pytest.param( "from foo.bar import functiøn as good, bäd", "bäd", id="from_okay_module_import_bad_as_good_and_bad", ), pytest.param( "from foo.bar import functiøn as good, *", # We still have functiøn within our namespace and could detect this # But to do this properly we would need to check all `*` imports # -> Too much effort! "functiøn", id="from_okay_module_import_bad_as_good_and_star", marks=pytest.mark.xfail( reason="We don't know what is imported when using star" ), ), ], ) def test_check_import(self, import_statement: str, wrong_name: str | None) -> None: """We expect that for everything that user can change there is a message.""" node = astroid.extract_node(f"{import_statement} #@") expected_msgs: Iterable[pylint.testutils.MessageTest] = tuple() if wrong_name: expected_msgs = ( pylint.testutils.MessageTest( msg_id="non-ascii-module-import", node=node, args=("Module", wrong_name), confidence=pylint.interfaces.HIGH, ), ) with self.assertAddsMessages(*expected_msgs, ignore_position=True): if import_statement.startswith("from"): assert isinstance(node, nodes.ImportFrom) self.checker.visit_importfrom(node) else: assert isinstance(node, nodes.Import) self.checker.visit_import(node)