From d2475b42e3f4b17ca39eedd1ac65a929f70ca02e Mon Sep 17 00:00:00 2001 From: Kound Date: Mon, 10 Jan 2022 22:48:46 +0100 Subject: Improve non ascii checker (#5643) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * split ``non-ascii-name`` into 3 different msgs - non-ascii-identifier (replaces non-ascii-name) - non-ascii-file-name (a warning) - non-ascii-module-import (only considering the namespace the import is imported in) Co-authored-by: Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> Co-authored-by: Pierre Sassoulas --- CONTRIBUTORS.txt | 3 + ChangeLog | 9 + doc/whatsnew/2.13.rst | 9 + pylint/checkers/__init__.py | 9 +- pylint/checkers/base.py | 27 +- pylint/checkers/non_ascii_names.py | 201 +++++++++++++++ pylint/constants.py | 1 + pylint/testutils/checker_test_case.py | 15 +- tests/checkers/unittest_non_ascii_name.py | 279 +++++++++++++++++++++ tests/functional/n/non/non_ascii_name.rc | 3 - tests/functional/n/non/non_ascii_name.txt | 4 +- .../n/non/non_ascii_name_backward_test_code.py | 7 + .../n/non/non_ascii_name_backward_test_msg.py | 7 + tests/functional/n/non_ascii_import/__init__.py | 0 .../n/non_ascii_import/non_ascii_import.py | 13 + .../n/non_ascii_import/non_ascii_import_as_bad.py | 6 + .../n/non_ascii_import/non_ascii_import_as_bad.txt | 1 + .../n/non_ascii_import/non_ascii_import_as_okay.py | 12 + .../n/non_ascii_import/non_ascii_import_from_as.py | 6 + .../non_ascii_import/non_ascii_import_from_as.txt | 1 + .../non_ascii_import/non_ascii_name_lo\305\202.py" | 1 + .../non_ascii_name_lo\305\202.txt" | 1 + tests/functional/n/non_ascii_name/__init__.py | 0 .../non_ascii_name_assignment_expressions.py | 4 + .../non_ascii_name_assignment_expressions.rc | 2 + .../non_ascii_name_assignment_expressions.txt | 1 + .../n/non_ascii_name/non_ascii_name_decorator.py | 19 ++ .../n/non_ascii_name/non_ascii_name_decorator.rc | 2 + .../n/non_ascii_name/non_ascii_name_decorator.txt | 1 + .../n/non_ascii_name/non_ascii_name_dict_kwargs.py | 13 + .../n/non_ascii_name/non_ascii_name_for_loop.py | 14 ++ .../n/non_ascii_name/non_ascii_name_for_loop.txt | 1 + .../n/non_ascii_name/non_ascii_name_function.py | 19 ++ .../n/non_ascii_name/non_ascii_name_function.txt | 1 + .../non_ascii_name_function_argument_py38.py | 24 ++ .../non_ascii_name_function_argument_py38.rc | 3 + .../non_ascii_name_function_argument_py38.txt | 2 + .../non_ascii_name_function_argument_py39plus.py | 25 ++ .../non_ascii_name_function_argument_py39plus.rc | 2 + .../non_ascii_name_function_argument_py39plus.txt | 2 + .../n/non_ascii_name/non_ascii_name_inline_var.py | 8 + .../n/non_ascii_name/non_ascii_name_inline_var.txt | 1 + .../n/non_ascii_name/non_ascii_name_kwargs_py38.py | 17 ++ .../n/non_ascii_name/non_ascii_name_kwargs_py38.rc | 3 + .../non_ascii_name/non_ascii_name_kwargs_py38.txt | 1 + .../non_ascii_name_kwargs_py39plus.py | 18 ++ .../non_ascii_name_kwargs_py39plus.rc | 2 + .../non_ascii_name_kwargs_py39plus.txt | 1 + .../n/non_ascii_name/non_ascii_name_local.py | 9 + .../n/non_ascii_name/non_ascii_name_local.txt | 1 + .../non_ascii_name_pos_and_kwonly_function.py | 24 ++ .../non_ascii_name_pos_and_kwonly_function.rc | 2 + .../non_ascii_name_pos_and_kwonly_function.txt | 4 + .../non_ascii_name/non_ascii_name_staticmethod.py | 17 ++ .../non_ascii_name/non_ascii_name_staticmethod.txt | 1 + .../n/non_ascii_name/non_ascii_name_try_except.py | 11 + .../n/non_ascii_name/non_ascii_name_try_except.txt | 1 + .../n/non_ascii_name/non_ascii_name_variable.py | 9 + .../n/non_ascii_name/non_ascii_name_variable.txt | 2 + .../functional/n/non_ascii_name_class/__init__.py | 0 .../n/non_ascii_name_class/non_ascii_name_class.py | 23 ++ .../non_ascii_name_class/non_ascii_name_class.txt | 1 + .../non_ascii_name_class_attribute.py | 25 ++ .../non_ascii_name_class_attribute.txt | 1 + .../non_ascii_name_class_constant.py | 23 ++ .../non_ascii_name_class_constant.txt | 1 + .../non_ascii_name_class_method.py | 18 ++ .../non_ascii_name_class_method.txt | 1 + 68 files changed, 945 insertions(+), 30 deletions(-) create mode 100644 pylint/checkers/non_ascii_names.py create mode 100644 tests/checkers/unittest_non_ascii_name.py delete mode 100644 tests/functional/n/non/non_ascii_name.rc create mode 100644 tests/functional/n/non/non_ascii_name_backward_test_code.py create mode 100644 tests/functional/n/non/non_ascii_name_backward_test_msg.py create mode 100644 tests/functional/n/non_ascii_import/__init__.py create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import.py create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import_as_bad.py create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import_as_bad.txt create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import_as_okay.py create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import_from_as.py create mode 100644 tests/functional/n/non_ascii_import/non_ascii_import_from_as.txt create mode 100644 "tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.py" create mode 100644 "tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.txt" create mode 100644 tests/functional/n/non_ascii_name/__init__.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_decorator.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_decorator.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_decorator.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_dict_kwargs.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_for_loop.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_for_loop.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_inline_var.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_inline_var.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_local.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_local.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.rc create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_try_except.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_try_except.txt create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_variable.py create mode 100644 tests/functional/n/non_ascii_name/non_ascii_name_variable.txt create mode 100644 tests/functional/n/non_ascii_name_class/__init__.py create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class.py create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class.txt create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.py create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.txt create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.py create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.txt create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.py create mode 100644 tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.txt diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index eee4da9a4..aa4fb2e24 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -595,3 +595,6 @@ contributors: * Eero Vuojolahti: contributor * Kian-Meng, Ang: contributor + +* Carli* Freudenberg (CarliJoy): contributor + - Improve non-ascii-name checker diff --git a/ChangeLog b/ChangeLog index df0a7313b..424d8b064 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,15 @@ Release date: TBA Closes #5588 +* Rewrote checker for ``non-ascii-name``. + It now ensures __all__ Python names are ASCII and also properly + checks the names of imports (``non-ascii-module-import``) as + well as file names (``non-ascii-file-name``) and emits their respective new warnings. + + Non ASCII characters could be homoglyphs (look alike characters) and hard to + enter on a non specialized keyboard. + See `Confusable Characters in PEP 672 `_ + * When run in parallel mode ``pylint`` now pickles the data passed to subprocesses with the ``dill`` package. The ``dill`` package has therefore been added as a dependency. diff --git a/doc/whatsnew/2.13.rst b/doc/whatsnew/2.13.rst index 10970a1dd..854fef618 100644 --- a/doc/whatsnew/2.13.rst +++ b/doc/whatsnew/2.13.rst @@ -14,6 +14,15 @@ New checkers Closes #5460 +* Rewrote Checker of ``non-ascii-name``. + It now ensures __all__ Python names are ASCII and also properly + checks the names of imports (``non-ascii-module-import``) as + well as file names (``non-ascii-file-name``) and emits their respective new warnings. + + Non ASCII characters could be homoglyphs (look alike characters) and hard to + enter on a non specialized keyboard. + See `Confusable Characters in PEP 672 `_ + Removed checkers ================ diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py index 273b085a0..914425313 100644 --- a/pylint/checkers/__init__.py +++ b/pylint/checkers/__init__.py @@ -39,7 +39,14 @@ Base id of standard checkers (used in msg and report ids): 15: stdlib 16: python3 17: refactoring -18-50: not yet used: reserved for future internal checkers. +. +. +. +24: non-ascii-names +25-50: not yet used: reserved for future internal checkers. +This file is not updated. Use + script/get_unused_message_id_category.py +to get the next free checker id. 51-99: perhaps used: reserved for external checkers The raw_metrics checker has no number associated since it doesn't emit any diff --git a/pylint/checkers/base.py b/pylint/checkers/base.py index 2fe6123f1..2f79f852e 100644 --- a/pylint/checkers/base.py +++ b/pylint/checkers/base.py @@ -1737,11 +1737,6 @@ class NameChecker(_BasicChecker): ] }, ), - "C0144": ( - '%s name "%s" contains a non-ASCII unicode character', - "non-ascii-name", - "Used when the name contains at least one non-ASCII unicode character.", - ), "W0111": ( "Name %s will become a keyword in Python %s", "assign-to-new-keyword", @@ -1838,7 +1833,6 @@ class NameChecker(_BasicChecker): self._name_hints = {} self._good_names_rgxs_compiled = [] self._bad_names_rgxs_compiled = [] - self._non_ascii_rgx_compiled = re.compile("[^\u0000-\u007F]") def open(self): self.linter.stats.reset_bad_names() @@ -1878,7 +1872,7 @@ class NameChecker(_BasicChecker): return regexps, hints - @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name") + @utils.check_messages("disallowed-name", "invalid-name") def visit_module(self, node: nodes.Module) -> None: self._check_name("module", node.name.split(".")[-1], node) self._bad_names = {} @@ -1904,9 +1898,7 @@ class NameChecker(_BasicChecker): for args in warnings: self._raise_name_warning(prevalent_group, *args) - @utils.check_messages( - "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" - ) + @utils.check_messages("disallowed-name", "invalid-name", "assign-to-new-keyword") def visit_classdef(self, node: nodes.ClassDef) -> None: self._check_assign_to_new_keyword_violation(node.name, node) self._check_name("class", node.name, node) @@ -1914,9 +1906,7 @@ class NameChecker(_BasicChecker): if not any(node.instance_attr_ancestors(attr)): self._check_name("attr", attr, anodes[0]) - @utils.check_messages( - "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" - ) + @utils.check_messages("disallowed-name", "invalid-name", "assign-to-new-keyword") def visit_functiondef(self, node: nodes.FunctionDef) -> None: # Do not emit any warnings if the method is just an implementation # of a base class method. @@ -1944,14 +1934,12 @@ class NameChecker(_BasicChecker): visit_asyncfunctiondef = visit_functiondef - @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name") + @utils.check_messages("disallowed-name", "invalid-name") def visit_global(self, node: nodes.Global) -> None: for name in node.names: self._check_name("const", name, node) - @utils.check_messages( - "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name" - ) + @utils.check_messages("disallowed-name", "invalid-name", "assign-to-new-keyword") def visit_assignname(self, node: nodes.AssignName) -> None: """check module level assigned names""" self._check_assign_to_new_keyword_violation(node.name, node) @@ -2041,11 +2029,6 @@ class NameChecker(_BasicChecker): def _check_name(self, node_type, name, node, confidence=interfaces.HIGH): """check for a name using the type's regexp""" - non_ascii_match = self._non_ascii_rgx_compiled.match(name) - if non_ascii_match is not None: - self._raise_name_warning( - None, node, node_type, name, confidence, warning="non-ascii-name" - ) def _should_exempt_from_invalid_name(node): if node_type == "variable": diff --git a/pylint/checkers/non_ascii_names.py b/pylint/checkers/non_ascii_names.py new file mode 100644 index 000000000..6ad0df556 --- /dev/null +++ b/pylint/checkers/non_ascii_names.py @@ -0,0 +1,201 @@ +# Copyright (c) 2021-2022 Carli Freudenberg + +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +"""All alphanumeric unicode character are allowed in Python but due +to similarities in how they look they can be confused. + +See: https://www.python.org/dev/peps/pep-0672/#confusable-characters-in-identifiers + +The following checkers are intended to make users are aware of these issues. +""" + +import sys +from typing import Optional, Union + +from astroid import nodes + +from pylint import constants, interfaces, lint +from pylint.checkers import base_checker, utils + +if sys.version_info[:2] >= (3, 7): + # pylint: disable-next=fixme + # TODO: Remove after 3.6 has been deprecated + Py37Str = str +else: + + class Py37Str(str): + # Allow Python 3.6 compatibility + def isascii(self: str) -> bool: + return all("\u0000" <= x <= "\u007F" for x in self) + + +NON_ASCII_HELP = ( + "Used when the name contains at least one non-ASCII unicode character. " + "See https://www.python.org/dev/peps/pep-0672/#confusable-characters-in-identifiers" + " for a background why this could be bad. \n" + "If your programming guideline defines that you are programming in " + "English, then there should be no need for non ASCII characters in " + "Python Names. If not you can simply disable this check." +) + + +class NonAsciiNameChecker(base_checker.BaseChecker): + """A strict name checker only allowing ASCII + + Note: This check only checks Names, so it ignores the content of + docstrings and comments! + """ + + __implements__ = interfaces.IAstroidChecker + priority = -1 + + msgs = { + "C2401": ( + '%s name "%s" contains a non-ASCII character, consider renaming it.', + "non-ascii-name", + NON_ASCII_HELP, + {"old_names": [("C0144", "old-non-ascii-name")]}, + ), + # First %s will always be "file" + "W2402": ( + ( + '%s name "%s" contains a non-ASCII character. PEP 3131 only allows ' + "non-ascii identifiers, not file names." + ), + "non-ascii-file-name", + ( + # Some = PyCharm at the time of writing didn't display the non_ascii_name_loł + # files and had big troubles with git. + # Probably only a bug shows the problem quite good. + # That's also why this is a warning and not only a convention! + "Some editors don't support non-ASCII file names properly. " + "Even though Python supports UTF-8 files since Python 3.5 this isn't " + "recommended for interoperability. Further reading:\n" + "- https://www.python.org/dev/peps/pep-0489/#export-hook-name\n" + "- https://www.python.org/dev/peps/pep-0672/#confusable-characters-in-identifiers\n" + "- https://bugs.python.org/issue20485\n" + ), + ), + # First %s will always be "module" + "C2403": ( + '%s name "%s" contains a non-ASCII character, use an ASCII-only alias for import.', + "non-ascii-module-import", + NON_ASCII_HELP, + ), + } + + name = "NonASCII-Checker" + + def _check_name( + self, node_type: str, name: Optional[str], node: nodes.NodeNG + ) -> None: + """Check whether a name is using non-ASCII characters.""" + + if name is None: + # For some nodes i.e. *kwargs from a dict, the name will be empty + return + + if not (Py37Str(name).isascii()): + type_label = constants.HUMAN_READABLE_TYPES[node_type] + args = (type_label.capitalize(), name) + + msg = "non-ascii-name" + + # Some node types have customized messages + if node_type == "file": + msg = "non-ascii-file-name" + elif node_type == "module": + msg = "non-ascii-module-import" + + self.add_message(msg, node=node, args=args, confidence=interfaces.HIGH) + + @utils.check_messages("non-ascii-name") + def visit_module(self, node: nodes.Module) -> None: + self._check_name("file", node.name.split(".")[-1], node) + + @utils.check_messages("non-ascii-name") + def visit_functiondef( + self, node: Union[nodes.FunctionDef, nodes.AsyncFunctionDef] + ) -> None: + self._check_name("function", node.name, node) + + # Check argument names + arguments = node.args + + # Check position only arguments + if arguments.posonlyargs: + for pos_only_arg in arguments.posonlyargs: + self._check_name("argument", pos_only_arg.name, pos_only_arg) + + # Check "normal" arguments + if arguments.args: + for arg in arguments.args: + self._check_name("argument", arg.name, arg) + + # Check key word only arguments + if arguments.kwonlyargs: + for kwarg in arguments.kwonlyargs: + self._check_name("argument", kwarg.name, kwarg) + + visit_asyncfunctiondef = visit_functiondef + + @utils.check_messages("non-ascii-name") + def visit_global(self, node: nodes.Global) -> None: + for name in node.names: + self._check_name("const", name, node) + + @utils.check_messages("non-ascii-name") + def visit_assignname(self, node: nodes.AssignName) -> None: + """check module level assigned names""" + # The NameChecker from which this Checker originates knows a lot of different + # versions of variables, i.e. constants, inline variables etc. + # To simplify we use only `variable` here, as we don't need to apply different + # rules to different types of variables. + frame = node.frame() + + if isinstance(frame, nodes.FunctionDef): + if node.parent in frame.body: + # Only perform the check if the assignment was done in within the body + # of the function (and not the function parameter definition + # (will be handled in visit_functiondef) + # or within a decorator (handled in visit_call) + self._check_name("variable", node.name, node) + elif isinstance(frame, nodes.ClassDef): + self._check_name("attr", node.name, node) + else: + # Possibilities here: + # - isinstance(node.assign_type(), nodes.Comprehension) == inlinevar + # - isinstance(frame, nodes.Module) == variable (constant?) + # - some other kind of assigment missed but still most likely a variable + self._check_name("variable", node.name, node) + + @utils.check_messages("non-ascii-name") + def visit_classdef(self, node: nodes.ClassDef) -> None: + self._check_name("class", node.name, node) + for attr, anodes in node.instance_attrs.items(): + if not any(node.instance_attr_ancestors(attr)): + self._check_name("attr", attr, anodes[0]) + + def _check_module_import(self, node: Union[nodes.ImportFrom, nodes.Import]) -> None: + for module_name, alias in node.names: + name = alias or module_name + self._check_name("module", name, node) + + @utils.check_messages("non-ascii-name") + def visit_import(self, node: nodes.Import) -> None: + self._check_module_import(node) + + @utils.check_messages("non-ascii-name") + def visit_importfrom(self, node: nodes.ImportFrom) -> None: + self._check_module_import(node) + + @utils.check_messages("non-ascii-name") + def visit_call(self, node: nodes.Call) -> None: + """Check if the used keyword args are correct.""" + for keyword in node.keywords: + self._check_name("argument", keyword.arg, keyword) + + +def register(linter: lint.PyLinter) -> None: + linter.register_checker(NonAsciiNameChecker(linter)) diff --git a/pylint/constants.py b/pylint/constants.py index eb5b1118a..db54113ee 100644 --- a/pylint/constants.py +++ b/pylint/constants.py @@ -57,6 +57,7 @@ astroid {astroid.__version__} Python {sys.version}""" HUMAN_READABLE_TYPES = { + "file": "file", "module": "module", "const": "constant", "class": "class", diff --git a/pylint/testutils/checker_test_case.py b/pylint/testutils/checker_test_case.py index af72136a9..5fabf97c5 100644 --- a/pylint/testutils/checker_test_case.py +++ b/pylint/testutils/checker_test_case.py @@ -32,12 +32,18 @@ class CheckerTestCase: yield @contextlib.contextmanager - def assertAddsMessages(self, *messages: MessageTest) -> Generator[None, None, None]: + def assertAddsMessages( + self, *messages: MessageTest, ignore_position: bool = False + ) -> Generator[None, None, None]: """Assert that exactly the given method adds the given messages. The list of messages must exactly match *all* the messages added by the method. Additionally, we check to see whether the args in each message can actually be substituted into the message string. + + Using the keyword argument `ignore_position`, all checks for position + arguments (line, col_offset, ...) will be skipped. This can be used to + just test messages for the correct node. """ yield got = self.linter.release_messages() @@ -53,10 +59,15 @@ class CheckerTestCase: for expected_msg, gotten_msg in zip(messages, got): assert expected_msg.msg_id == gotten_msg.msg_id, msg - assert expected_msg.line == gotten_msg.line, msg assert expected_msg.node == gotten_msg.node, msg assert expected_msg.args == gotten_msg.args, msg assert expected_msg.confidence == gotten_msg.confidence, msg + + if ignore_position: + # Do not check for line, col_offset etc... + continue + + assert expected_msg.line == gotten_msg.line, msg assert expected_msg.col_offset == gotten_msg.col_offset, msg if PY38_PLUS: # pylint: disable=fixme diff --git a/tests/checkers/unittest_non_ascii_name.py b/tests/checkers/unittest_non_ascii_name.py new file mode 100644 index 000000000..d0e3a85ab --- /dev/null +++ b/tests/checkers/unittest_non_ascii_name.py @@ -0,0 +1,279 @@ +import sys +from typing import Iterable, Optional + +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): + """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, + ): + """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: Optional[str]): + """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) diff --git a/tests/functional/n/non/non_ascii_name.rc b/tests/functional/n/non/non_ascii_name.rc deleted file mode 100644 index ff09ea1f6..000000000 --- a/tests/functional/n/non/non_ascii_name.rc +++ /dev/null @@ -1,3 +0,0 @@ -[testoptions] -# This test cannot run on Windows due to Unicode error formatting. -exclude_platforms=win32 diff --git a/tests/functional/n/non/non_ascii_name.txt b/tests/functional/n/non/non_ascii_name.txt index ba5338bb9..2cda6962e 100644 --- a/tests/functional/n/non/non_ascii_name.txt +++ b/tests/functional/n/non/non_ascii_name.txt @@ -1,2 +1,2 @@ -non-ascii-name:3:0:3:10::"Constant name ""áéíóú"" contains a non-ASCII unicode character":HIGH -non-ascii-name:5:0:6:12:úóíéá:"Function name ""úóíéá"" contains a non-ASCII unicode character":HIGH +non-ascii-name:3:0:3:10::"Variable name ""áéíóú"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:5:0:6:12:úóíéá:"Function name ""úóíéá"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non/non_ascii_name_backward_test_code.py b/tests/functional/n/non/non_ascii_name_backward_test_code.py new file mode 100644 index 000000000..36a22ca52 --- /dev/null +++ b/tests/functional/n/non/non_ascii_name_backward_test_code.py @@ -0,0 +1,7 @@ +""" Tests for non-ascii-name checker working as expected after a major refactor """ +# pylint: disable=C0144, use-symbolic-message-instead + +áéíóú = 4444 + +def úóíéá(): + """yo""" diff --git a/tests/functional/n/non/non_ascii_name_backward_test_msg.py b/tests/functional/n/non/non_ascii_name_backward_test_msg.py new file mode 100644 index 000000000..e63f3a4db --- /dev/null +++ b/tests/functional/n/non/non_ascii_name_backward_test_msg.py @@ -0,0 +1,7 @@ +""" Tests for non-ascii-name checker working as expected after a major refactor """ +# pylint: disable=non-ascii-name + +áéíóú = 4444 + +def úóíéá(): + """yo""" diff --git a/tests/functional/n/non_ascii_import/__init__.py b/tests/functional/n/non_ascii_import/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/n/non_ascii_import/non_ascii_import.py b/tests/functional/n/non_ascii_import/non_ascii_import.py new file mode 100644 index 000000000..76163b286 --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import.py @@ -0,0 +1,13 @@ +"""Test that invalid module name imports causes correct error""" +# pylint: disable=import-error, wrong-import-position, unused-wildcard-import, wildcard-import, wrong-import-order +import sys +import os + +# allow module imports to test that this is indeed a valid python file +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.dirname(SCRIPT_DIR)) + +import non_ascii_name_loł as ok +from pathlib import * # test that star imports work correctly and give no error + +print(ok) diff --git a/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.py b/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.py new file mode 100644 index 000000000..df392961f --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.py @@ -0,0 +1,6 @@ +"""import non ascii alias""" +import os.path as łos # [non-ascii-module-import] + + +# Usage should not raise a second error +foo = łos.join("a", "b") diff --git a/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.txt b/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.txt new file mode 100644 index 000000000..595d387f7 --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import_as_bad.txt @@ -0,0 +1 @@ +non-ascii-module-import:2:0:2:22::"Module name ""łos"" contains a non-ASCII character, use an ASCII-only alias for import.":HIGH diff --git a/tests/functional/n/non_ascii_import/non_ascii_import_as_okay.py b/tests/functional/n/non_ascii_import/non_ascii_import_as_okay.py new file mode 100644 index 000000000..35f8513fa --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import_as_okay.py @@ -0,0 +1,12 @@ +"""Test that invalid module name do not cause errors when using an alias""" +# pylint: disable=import-error, wrong-import-position +import sys +import os + +# allow module imports to test that this is indeed a valid python file +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(os.path.dirname(SCRIPT_DIR)) + +import non_ascii_name_loł as foobar + +print(foobar) diff --git a/tests/functional/n/non_ascii_import/non_ascii_import_from_as.py b/tests/functional/n/non_ascii_import/non_ascii_import_from_as.py new file mode 100644 index 000000000..68beba0ec --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import_from_as.py @@ -0,0 +1,6 @@ +"""import as non ascii alias""" +from os.path import join as łos # [non-ascii-module-import] + + +# Usage should not raise a second error +foo = łos("a", "b") diff --git a/tests/functional/n/non_ascii_import/non_ascii_import_from_as.txt b/tests/functional/n/non_ascii_import/non_ascii_import_from_as.txt new file mode 100644 index 000000000..58daa481b --- /dev/null +++ b/tests/functional/n/non_ascii_import/non_ascii_import_from_as.txt @@ -0,0 +1 @@ +non-ascii-module-import:2:0:2:32::"Module name ""łos"" contains a non-ASCII character, use an ASCII-only alias for import.":HIGH diff --git "a/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.py" "b/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.py" new file mode 100644 index 000000000..bc59418e1 --- /dev/null +++ "b/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.py" @@ -0,0 +1 @@ +# [non-ascii-file-name] diff --git "a/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.txt" "b/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.txt" new file mode 100644 index 000000000..eefaa197a --- /dev/null +++ "b/tests/functional/n/non_ascii_import/non_ascii_name_lo\305\202.txt" @@ -0,0 +1 @@ +non-ascii-file-name:1:0:None:None::"File name ""non_ascii_name_loł"" contains a non-ASCII character. PEP 3131 only allows non-ascii identifiers, not file names.":HIGH diff --git a/tests/functional/n/non_ascii_name/__init__.py b/tests/functional/n/non_ascii_name/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.py b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.py new file mode 100644 index 000000000..ac51b9c4d --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.py @@ -0,0 +1,4 @@ +"""Assigment Expression as defined in https://www.python.org/dev/peps/pep-0572/""" + +if (loł := __name__) == "__main__": # [non-ascii-name] + print(loł) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.rc b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.rc new file mode 100644 index 000000000..85fc502b3 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.8 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.txt b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.txt new file mode 100644 index 000000000..8a5acceb8 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_assignment_expressions.txt @@ -0,0 +1 @@ +non-ascii-name:3:4:3:8::"Variable name ""loł"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_decorator.py b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.py new file mode 100644 index 000000000..06900ac7c --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.py @@ -0,0 +1,19 @@ +""" +Ensure that also decorator calls caught correctly +Column and line not correctly detected with Python 3.8, so we +skip it and only test versions after that. +""" + + +def decoractor_func(*args, **kwargs): + """A docstring""" + return lambda x: f"Foobar {args} {kwargs}" + + +@decoractor_func( + aaaaaaaaaaaaaaaaaaalllllllooooooooooooonnngggggggggglllline=1, + normal=2, + fåling=3, # [non-ascii-name] +) +def a_function(): + """A docstring""" diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_decorator.rc b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.rc new file mode 100644 index 000000000..16b75eea7 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.9 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_decorator.txt b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.txt new file mode 100644 index 000000000..6c4944009 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_decorator.txt @@ -0,0 +1 @@ +non-ascii-name:16:4:16:13:a_function:"Argument name ""fåling"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_dict_kwargs.py b/tests/functional/n/non_ascii_name/non_ascii_name_dict_kwargs.py new file mode 100644 index 000000000..5d7b4c7f3 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_dict_kwargs.py @@ -0,0 +1,13 @@ +""" +We don't expect this to give any errors! +""" + + +def okay(**kwargs): + """print kwargs""" + print(kwargs) + + +keyword_args = {"łol": "this would be hard to check against"} + +okay(**keyword_args) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.py b/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.py new file mode 100644 index 000000000..59585645a --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.py @@ -0,0 +1,14 @@ +"""invalid ascii char in a for loop""" + +import os + + +def main(): + """main func""" + # +2: [non-ascii-name] + a_variable = "" + for łol in os.listdir("."): + # Usage should not raise a second error + a_variable += ( + f"{łol} " + ) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.txt b/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.txt new file mode 100644 index 000000000..8995dc0eb --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_for_loop.txt @@ -0,0 +1 @@ +non-ascii-name:10:8:10:12:main:"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function.py b/tests/functional/n/non_ascii_name/non_ascii_name_function.py new file mode 100644 index 000000000..e3c558ea6 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function.py @@ -0,0 +1,19 @@ +"""invalid ASCII char in a function definition""" +# pylint: disable=invalid-name + + +def sayHello(): + """Greetings""" + print("Hello, World!") + + +# +3: [non-ascii-name] + + +def sayНello(): + """From Russia with Love""" + print("Goodbye, World!") + + +# Usage should not raise a second error +sayНello() diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function.txt b/tests/functional/n/non_ascii_name/non_ascii_name_function.txt new file mode 100644 index 000000000..949059da9 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function.txt @@ -0,0 +1 @@ +non-ascii-name:13:0:15:28:sayНello:"Function name ""sayНello"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.py b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.py new file mode 100644 index 000000000..a75e1c6b7 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.py @@ -0,0 +1,24 @@ +""" +non ascii variable defined in a function + +This test is only for 3.8 as the starting column is incorrect +""" + + +def okay( + just_some_thing_long_again: str, + lol_very_long_argument: str, + łol: str, # [non-ascii-name] +) -> bool: + """Be okay, yeah?""" + # Usage should not raise a second error + print(just_some_thing_long_again, lol_very_long_argument, łol) + return True + + +# Usage should raise a second error +okay( + "A VVVVVVVEEEERRRRRRRRRRYYYYYYYYYY LONG TIME ", + lol_very_long_argument="a", + łol="b", # [non-ascii-name] +) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.rc b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.rc new file mode 100644 index 000000000..35b185fdb --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.rc @@ -0,0 +1,3 @@ +[testoptions] +min_pyver=3.8 +max_pyver=3.9 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.txt b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.txt new file mode 100644 index 000000000..781336474 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py38.txt @@ -0,0 +1,2 @@ +non-ascii-name:11:4:11:13:okay:"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:23:0:None:None::"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.py b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.py new file mode 100644 index 000000000..a2d87ac4d --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.py @@ -0,0 +1,25 @@ +""" +non ascii variable defined in a function + +This test is 3.9+ and not using 'min_pyver_end_position' +as the starting column is also incorrect on < 3.9 +""" + + +def okay( + just_some_thing_long_again: str, + lol_very_long_argument: str, + łol: str, # [non-ascii-name] +) -> bool: + """Be okay, yeah?""" + # Usage should not raise a second error + print(just_some_thing_long_again, lol_very_long_argument, łol) + return True + + +# Usage should raise a second error +okay( + "A VVVVVVVEEEERRRRRRRRRRYYYYYYYYYY LONG TIME ", + lol_very_long_argument="a", + łol="b", # [non-ascii-name] +) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.rc b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.rc new file mode 100644 index 000000000..16b75eea7 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.9 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.txt b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.txt new file mode 100644 index 000000000..0222308af --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_function_argument_py39plus.txt @@ -0,0 +1,2 @@ +non-ascii-name:12:4:12:13:okay:"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:24:4:24:12::"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.py b/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.py new file mode 100644 index 000000000..2d47e58c2 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.py @@ -0,0 +1,8 @@ +"""inline loop non ascii variable definition""" +import os + + +foo = [ + f"{łol} " + for łol in os.listdir(".") # [non-ascii-name] +] diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.txt b/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.txt new file mode 100644 index 000000000..64515296b --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_inline_var.txt @@ -0,0 +1 @@ +non-ascii-name:7:8:7:12::"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.py b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.py new file mode 100644 index 000000000..3c7b8c683 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.py @@ -0,0 +1,17 @@ +""" +Defining non ASCII variables in a function call + +This test is only for 3.8 as the starting column is incorrect +""" + + +def okay(**kwargs): + """Print kwargs""" + print(kwargs) + + +okay( + a_long_attribute_that_is_very_okay=1, + b_belongs_to_yet_another_okay_attributed=2, + łol=3, # [non-ascii-name] +) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.rc b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.rc new file mode 100644 index 000000000..35b185fdb --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.rc @@ -0,0 +1,3 @@ +[testoptions] +min_pyver=3.8 +max_pyver=3.9 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.txt b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.txt new file mode 100644 index 000000000..b43189658 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py38.txt @@ -0,0 +1 @@ +non-ascii-name:16:0:None:None::"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.py b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.py new file mode 100644 index 000000000..0dfceb38f --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.py @@ -0,0 +1,18 @@ +""" +Defining non ASCII variables in a function call + +This test is 3.9+ and not using 'min_pyver_end_position' +as the starting column is also incorrect on < 3.9 +""" + + +def okay(**kwargs): + """Print kwargs""" + print(kwargs) + + +okay( + a_long_attribute_that_is_very_okay=1, + b_belongs_to_yet_another_okay_attributed=2, + łol=3, # [non-ascii-name] +) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.rc b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.rc new file mode 100644 index 000000000..16b75eea7 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.9 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.txt b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.txt new file mode 100644 index 000000000..7595a9d08 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_kwargs_py39plus.txt @@ -0,0 +1 @@ +non-ascii-name:17:4:17:10::"Argument name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_local.py b/tests/functional/n/non_ascii_name/non_ascii_name_local.py new file mode 100644 index 000000000..99423fdc8 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_local.py @@ -0,0 +1,9 @@ +"""Using non ascii variables in local""" + + +def okay(): + """docstring""" + łol = "foo" # [non-ascii-name] + # Usage should not raise a second error + baring = łol + print(baring) diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_local.txt b/tests/functional/n/non_ascii_name/non_ascii_name_local.txt new file mode 100644 index 000000000..8e5e684fa --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_local.txt @@ -0,0 +1 @@ +non-ascii-name:6:4:6:8:okay:"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.py b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.py new file mode 100644 index 000000000..651d78f89 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.py @@ -0,0 +1,24 @@ +""" +Test for names within keyword and position only function + +This test is 3.8+ as the columns are not correctly identified +by the ast parser < 3.8 +""" +# pylint: disable=unused-argument + + +def name( + some_thing_long_but_okay, + not_okay_łol, # [non-ascii-name] + not_okay_defaułt=None, # [non-ascii-name] + /, + p_or_kw_okay=None, + p_or_kw_not_økay=None, # [non-ascii-name] + *, + kw_arg_okay, + kw_arg_not_økay, # [non-ascii-name] +): + """ + Do something! + """ + return "Foobar" diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.rc b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.rc new file mode 100644 index 000000000..85fc502b3 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.rc @@ -0,0 +1,2 @@ +[testoptions] +min_pyver=3.8 diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.txt b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.txt new file mode 100644 index 000000000..b13a1af3b --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_pos_and_kwonly_function.txt @@ -0,0 +1,4 @@ +non-ascii-name:12:4:12:17:name:"Argument name ""not_okay_łol"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:13:4:13:21:name:"Argument name ""not_okay_defaułt"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:16:4:16:21:name:"Argument name ""p_or_kw_not_økay"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:19:4:19:20:name:"Argument name ""kw_arg_not_økay"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.py b/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.py new file mode 100644 index 000000000..9b10f1cd4 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.py @@ -0,0 +1,17 @@ +"""static method with non ascii characters""" + + +class OkayClass: + """Class Docstring""" + + def public(self): + """Say it load""" + + @staticmethod + def umlaut_ä(): # [non-ascii-name] + """Say ä""" + return "ä" + + +# Usage should not raise a second error +OkayClass.umlaut_ä() diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.txt b/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.txt new file mode 100644 index 000000000..8bc3e8ce6 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_staticmethod.txt @@ -0,0 +1 @@ +non-ascii-name:11:4:13:19:OkayClass.umlaut_ä:"Function name ""umlaut_ä"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_try_except.py b/tests/functional/n/non_ascii_name/non_ascii_name_try_except.py new file mode 100644 index 000000000..beccf4b9a --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_try_except.py @@ -0,0 +1,11 @@ +""" +Also variables defined in except can't contain non ascii chars +""" + + +try: + raise AttributeError("Test") + # +1: [non-ascii-name] +except AttributeError as łol: + # Usage should not raise a second error + foo = łol diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_try_except.txt b/tests/functional/n/non_ascii_name/non_ascii_name_try_except.txt new file mode 100644 index 000000000..f6b0d6c6c --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_try_except.txt @@ -0,0 +1 @@ +non-ascii-name:9:0:11:14::"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_variable.py b/tests/functional/n/non_ascii_name/non_ascii_name_variable.py new file mode 100644 index 000000000..2994dfa47 --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_variable.py @@ -0,0 +1,9 @@ +""" +Simply variable test +""" +# pylint: disable=invalid-name + +# Test invalid variable name +łol = "Foobar" # [non-ascii-name] +# Usage should not raise a second error +łol += "-" # [non-ascii-name] diff --git a/tests/functional/n/non_ascii_name/non_ascii_name_variable.txt b/tests/functional/n/non_ascii_name/non_ascii_name_variable.txt new file mode 100644 index 000000000..5c142ee7e --- /dev/null +++ b/tests/functional/n/non_ascii_name/non_ascii_name_variable.txt @@ -0,0 +1,2 @@ +non-ascii-name:7:0:7:4::"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH +non-ascii-name:9:0:9:4::"Variable name ""łol"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name_class/__init__.py b/tests/functional/n/non_ascii_name_class/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class.py b/tests/functional/n/non_ascii_name_class/non_ascii_name_class.py new file mode 100644 index 000000000..edcdae643 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class.py @@ -0,0 +1,23 @@ +""" Non ASCII char in class name + +Note that the end line parameter here seems to be off. +We would expect it to be the same as the start, as we only refer +to the class Name and not the complete class definition. +But this is not possible atm with pylint. +""" +# pylint: disable=too-few-public-methods + + +class НoldIt: # [non-ascii-name] + """nice classs""" + + def public(self): + """do something""" + print(self) + + +def main(): + """Main function""" + # Usage should not raise a second error + foobar = НoldIt() + print(foobar) diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class.txt b/tests/functional/n/non_ascii_name_class/non_ascii_name_class.txt new file mode 100644 index 000000000..92c4430b3 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class.txt @@ -0,0 +1 @@ +non-ascii-name:11:0:16:19:НoldIt:"Class name ""НoldIt"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.py b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.py new file mode 100644 index 000000000..07d95bba5 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.py @@ -0,0 +1,25 @@ +"""Non ASCII name in class variable""" + + +class OkayIsh: + """Class docstring""" + + def public(self): + """Be public!""" + print(self) + + def __init__(self): + self.łoopback = "invalid" # [non-ascii-name] + + def foobar(self): + """do something""" + # Usage should not raise a second error + return self.łoopback + +def main(): + """main function""" + # Usage should not raise a second error + barrrr = OkayIsh() + barrrr.foobar() + test = barrrr.łoopback + print(test) diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.txt b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.txt new file mode 100644 index 000000000..7e3f5327c --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_attribute.txt @@ -0,0 +1 @@ +non-ascii-name:12:8:12:22:OkayIsh.__init__:"Attribute name ""łoopback"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.py b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.py new file mode 100644 index 000000000..c3f96cadd --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.py @@ -0,0 +1,23 @@ +"""non ASCII name in global class variable/class constant""" + + +class OkayIsh: + """Class docstring""" + ŁOOPBACK = "invalid" # [non-ascii-name] + + + def more_public(self): + """yet another public method""" + print(self) + + def public(self): + """something public""" + print(self) + + + +def main(): + """Main func""" + # Usage should not raise a second error + foobar = OkayIsh.ŁOOPBACK + print(foobar) diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.txt b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.txt new file mode 100644 index 000000000..26cf27ef7 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_constant.txt @@ -0,0 +1 @@ +non-ascii-name:6:4:6:13:OkayIsh:"Attribute name ""ŁOOPBACK"" contains a non-ASCII character, consider renaming it.":HIGH diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.py b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.py new file mode 100644 index 000000000..ec05b4cf9 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.py @@ -0,0 +1,18 @@ +"""Non ASCII char in classmethod""" + + +class OkayClass: + """We need a class docstring?""" + + def public(self): + """Say something""" + print(self) + + @classmethod + def umlaut_ä(cls): # [non-ascii-name] + """do something""" + return "ä" + + +# Usage should not raise a second error +OkayClass.umlaut_ä() diff --git a/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.txt b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.txt new file mode 100644 index 000000000..8d7606e67 --- /dev/null +++ b/tests/functional/n/non_ascii_name_class/non_ascii_name_class_method.txt @@ -0,0 +1 @@ +non-ascii-name:12:4:14:19:OkayClass.umlaut_ä:"Function name ""umlaut_ä"" contains a non-ASCII character, consider renaming it.":HIGH -- cgit v1.2.1