Repository: wemake-services/wemake-python-styleguide

Issue:
Add exception to WPS226 to allow commas and dots
# Rule request

I suggest adding comma `,` and dot `.` to the list of allowed exceptions for WPS226

## Thesis

Forcing defining a constant for dot and comma strings worsen the code readability without need.

## Reasoning

It is a [classical](https://realpython.com/python-string-split-concatenate-join/#going-from-a-list-to-a-string-in-python-with-join) pattern to build a sentence with the code like:
```
strings = ['a', 'b', 'c']
','.join(strings)
```
It feels unnatural to define a constant (e.g. DOT, COMMA) for such obvious case.

Besides that the check WPS226 looks good. I don't like disabling it or increasing the number of string occurrences.
This extends the idea in https://github.com/wemake-services/wemake-python-styleguide/issues/1534


Discussion:
@0anton do you want to send a PR with the fix? 🙂 
Yes, sure! Just give me a couple of days for that.

On Mon 4. Oct 2021 at 19:53, Nikita Sobolev ***@***.***>
wrote:

> @0anton <https://github.com/0anton> do you want to send a PR with the
> fix? 🙂
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/wemake-services/wemake-python-styleguide/issues/2209#issuecomment-933716280>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AEECBZEBM3FDPHFRAOIXDYTUFHSYHANCNFSM5FJ4TYBQ>
> .
>


Resolving patch:
diff --git a/wemake_python_styleguide/violations/complexity.py b/wemake_python_styleguide/violations/complexity.py
--- a/wemake_python_styleguide/violations/complexity.py
+++ b/wemake_python_styleguide/violations/complexity.py
@@ -851,12 +851,16 @@ class TooManyExceptCasesViolation(ASTViolation):
 
 @final
 class OverusedStringViolation(MaybeASTViolation):
-    """
-    Forbid the overuse of string constants.
+    r"""
+    Forbid the overuse of string literals.
 
     We allow to use strings without any restrictions as annotations for
     variables, arguments, return values, and class attributes.
 
+    Some common string literals like dot `'.'`, comma `','`, empty string `''`,
+    single space `' '`, new line `'\n'`, `'\r\n'` and tabulator `'\t'`
+    do not count against string literal overuse limit.
+
     Reasoning:
         When some string is used more than several time in your code,
         it probably means that this string is a meaningful constant
@@ -875,7 +879,7 @@ class OverusedStringViolation(MaybeASTViolation):
 
     """
 
-    error_template = 'Found string constant over-use: {0}'
+    error_template = 'Found string literal over-use: {0}'
     code = 226
 
 
diff --git a/wemake_python_styleguide/visitors/ast/complexity/overuses.py b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
--- a/wemake_python_styleguide/visitors/ast/complexity/overuses.py
+++ b/wemake_python_styleguide/visitors/ast/complexity/overuses.py
@@ -36,16 +36,21 @@ class StringOveruseVisitor(base.BaseNodeVisitor):
     Restricts repeated usage of the same string constant.
 
     NB: Some short strings are ignored, as their use is very common and
-    forcing assignment would not make much sense (i.e. newlines or "").
+    forcing assignment would not make much sense (i.e. newlines, "",
+    comma, dot).
     """
 
     _ignored_string_constants: ClassVar[_StringConstants] = frozenset((
         ' ',
+        '.',
+        ',',
         '',
         '\n',
         '\r\n',
         '\t',
         b' ',
+        b'.',
+        b',',
         b'',
         b'\n',
         b'\r\n',


Test patch:
diff --git a/tests/test_visitors/conftest.py b/tests/test_visitors/conftest.py
--- a/tests/test_visitors/conftest.py
+++ b/tests/test_visitors/conftest.py
@@ -1,6 +1,7 @@
 from typing import Optional, Sequence, Tuple, Type, Union
 
 import pytest
+from typing_extensions import Final
 
 from wemake_python_styleguide.violations.base import (
     ASTViolation,
@@ -14,6 +15,7 @@
     Tuple[Type[BaseViolation], ...],
     None,
 ]
+_ERROR_FORMAT: Final = ': {0}'
 
 
 @pytest.fixture(scope='session')
@@ -69,10 +71,9 @@ def factory(
             assert len(real_errors) == 1
 
         violation = real_errors[0]
-        error_format = ': {0}'
 
-        assert error_format in violation.error_template
-        assert violation.error_template.endswith(error_format)
+        assert _ERROR_FORMAT in violation.error_template
+        assert violation.error_template.endswith(_ERROR_FORMAT)
 
         reproduction = violation.__class__(
             node=violation._node,  # noqa: WPS437
diff --git a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
--- a/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
+++ b/tests/test_visitors/test_ast/test_complexity/test_overuses/test_overused_string.py
@@ -200,20 +200,22 @@ def test_string_type_annotations(
     r'"\t"',
     r'"\n"',
     '""',
+    '","',
+    '"."',
 ])
 @pytest.mark.parametrize('prefix', [
     'b',
     'u',
     '',
 ])
-def test_string_overuse_exceptions(
+def test_common_strings_allowed(
     assert_errors,
     parse_ast_tree,
     default_options,
     prefix,
     string_value,
 ):
-    """Ensures that over-used strings raise violations."""
+    """Ensures that common strings do not count against the overuse limit."""
     snippet = string_actions.format(prefix + string_value)
     tree = parse_ast_tree(snippet)