wemake-services / wemake-python-styleguide

The strictest and most opinionated python linter ever!
https://wemake-python-styleguide.rtfd.io
MIT License
2.53k stars 378 forks source link

Incorrect detection of string constant overuse (WPS226) #2392

Closed smac89 closed 1 year ago

smac89 commented 2 years ago

What's wrong

String usage is detecting strings used inside list of dictionaries as being overused. For example in a django project, the following configuration will trigger the rule violation on NAME:

AUTH_PASSWORD_VALIDATORS = (
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
)

Additionally, there is a documentation mismatch. The docs for max-string-usages here says the default is 5, but the documentation for the rule says it is 3 (which is also what I saw)

How it should be

Dictionary keys should not count towards string overuse

Flake8 version and plugins

{
  "dependencies": [],
  "platform": {
    "python_implementation": "CPython",
    "python_version": "3.10.0",
    "system": "Linux"
  },
  "plugins": [
    {
      "is_local": false,
      "plugin": "flake8-bandit",
      "version": "2.1.2"
    },
    {
      "is_local": false,
      "plugin": "flake8-broken-line",
      "version": "0.4.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-comprehensions",
      "version": "3.8.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-darglint",
      "version": "1.8.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-debugger",
      "version": "4.0.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-docstrings",
      "version": "1.6.0, pydocstyle: 6.1.1"
    },
    {
      "is_local": false,
      "plugin": "flake8-eradicate",
      "version": "1.2.0"
    },
    {
      "is_local": false,
      "plugin": "flake8-string-format",
      "version": "0.3.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_commas",
      "version": "2.1.0"
    },
    {
      "is_local": false,
      "plugin": "flake8_isort",
      "version": "4.1.1"
    },
    {
      "is_local": false,
      "plugin": "flake8_quotes",
      "version": "3.3.1"
    },
    {
      "is_local": false,
      "plugin": "mccabe",
      "version": "0.7.0"
    },
    {
      "is_local": false,
      "plugin": "naming",
      "version": "0.12.1"
    },
    {
      "is_local": false,
      "plugin": "pycodestyle",
      "version": "2.8.0"
    },
    {
      "is_local": false,
      "plugin": "pyflakes",
      "version": "2.4.0"
    },
    {
      "is_local": false,
      "plugin": "rst-docstrings",
      "version": "0.2.5"
    },
    {
      "is_local": false,
      "plugin": "wemake_python_styleguide",
      "version": "0.16.0"
    }
  ],
  "version": "4.0.1"
}

pip information

Hope you don't mind pipenv graph, as I don't use pip directly.

boto3==1.20.54
  - botocore [required: >=1.23.54,<1.24.0, installed: 1.23.54]
    - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
    - python-dateutil [required: >=2.1,<3.0.0, installed: ?]
    - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.9]
  - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
  - s3transfer [required: >=0.5.0,<0.6.0, installed: 0.5.2]
    - botocore [required: >=1.12.36,<2.0a.0, installed: 1.23.54]
      - jmespath [required: >=0.7.1,<1.0.0, installed: 0.10.0]
      - python-dateutil [required: >=2.1,<3.0.0, installed: ?]
      - urllib3 [required: >=1.25.4,<1.27, installed: 1.26.9]
celery==5.2.5
  - billiard [required: >=3.6.4.0,<4.0, installed: 3.6.4.0]
  - click [required: >=8.0.3,<9.0, installed: 8.1.2]
  - click-didyoumean [required: >=0.0.3, installed: ?]
  - click-plugins [required: >=1.1.1, installed: ?]
  - click-repl [required: >=0.2.0, installed: ?]
  - kombu [required: >=5.2.3,<6.0, installed: 5.2.4]
    - amqp [required: >=5.0.9,<6.0.0, installed: 5.1.0]
      - vine [required: >=5.0.0, installed: 5.0.0]
    - vine [required: Any, installed: 5.0.0]
  - pytz [required: >=2021.3, installed: 2022.1]
  - vine [required: >=5.0.0,<6.0, installed: 5.0.0]
coverage==6.3.2
cryptography==36.0.2
  - cffi [required: >=1.12, installed: 1.15.0]
django-admin-interface==0.19.0
  - django-colorfield [required: >=0.2,<1.0, installed: ?]
  - django-flat-responsive [required: >=1.0,<3.0, installed: ?]
  - django-flat-theme [required: >=1.0,<2.0, installed: ?]
  - six [required: >=1.9.0,<2.0.0, installed: 1.16.0]
django-object-actions==4.0.0
django-phonenumber-field==6.0.0
  - Django [required: >=2.2, installed: 3.2.12]
    - asgiref [required: >=3.3.2,<4, installed: 3.5.0]
    - pytz [required: Any, installed: 2022.1]
    - sqlparse [required: >=0.2.2, installed: 0.4.2]
django-timezone-field==4.2.3
  - django [required: >=2.2, installed: 3.2.12]
    - asgiref [required: >=3.3.2,<4, installed: 3.5.0]
    - pytz [required: Any, installed: 2022.1]
    - sqlparse [required: >=0.2.2, installed: 0.4.2]
  - pytz [required: Any, installed: 2022.1]
flakeheaven==0.11.1
  - colorama [required: Any, installed: 0.4.4]
  - entrypoints [required: Any, installed: 0.4]
  - flake8 [required: >=4.0.1,<5.0.0, installed: 4.0.1]
  - pygments [required: Any, installed: 2.11.2]
  - toml [required: Any, installed: 0.10.2]
  - urllib3 [required: Any, installed: 1.26.9]
google-api-python-client==2.42.0
  - google-api-core [required: >=1.31.5,<3.0.0dev,!=2.3.0,!=2.2.*,!=2.1.*,!=2.0.*, installed: 2.7.1]
    - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
      - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
      - pyasn1-modules [required: >=0.2.1, installed: ?]
      - rsa [required: >=3.1.4,<5, installed: 4.8]
      - six [required: >=1.9.0, installed: 1.16.0]
    - googleapis-common-protos [required: >=1.52.0,<2.0dev, installed: 1.56.0]
      - protobuf [required: >=3.12.0, installed: 3.20.0]
    - protobuf [required: >=3.12.0, installed: 3.20.0]
    - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
  - google-auth [required: >=1.16.0,<3.0.0dev, installed: 2.6.2]
    - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
    - pyasn1-modules [required: >=0.2.1, installed: ?]
    - rsa [required: >=3.1.4,<5, installed: 4.8]
    - six [required: >=1.9.0, installed: 1.16.0]
  - google-auth-httplib2 [required: >=0.1.0, installed: ?]
  - httplib2 [required: >=0.15.0,<1dev, installed: 0.20.4]
  - uritemplate [required: >=3.0.1,<5, installed: 4.1.1]
google-cloud-firestore==2.4.0
  - google-api-core [required: >=1.31.5,<3.0.0dev,!=2.3.0,!=2.2.*,!=2.1.*,!=2.0.*, installed: 2.7.1]
    - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
      - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
      - pyasn1-modules [required: >=0.2.1, installed: ?]
      - rsa [required: >=3.1.4,<5, installed: 4.8]
      - six [required: >=1.9.0, installed: 1.16.0]
    - googleapis-common-protos [required: >=1.52.0,<2.0dev, installed: 1.56.0]
      - protobuf [required: >=3.12.0, installed: 3.20.0]
    - protobuf [required: >=3.12.0, installed: 3.20.0]
    - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
  - google-cloud-core [required: >=1.4.1,<3.0.0dev, installed: 2.2.3]
    - google-api-core [required: >=1.31.5,<3.0.0dev,!=2.3.0,!=2.2.*,!=2.1.*,!=2.0.*, installed: 2.7.1]
      - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
        - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
        - pyasn1-modules [required: >=0.2.1, installed: ?]
        - rsa [required: >=3.1.4,<5, installed: 4.8]
        - six [required: >=1.9.0, installed: 1.16.0]
      - googleapis-common-protos [required: >=1.52.0,<2.0dev, installed: 1.56.0]
        - protobuf [required: >=3.12.0, installed: 3.20.0]
      - protobuf [required: >=3.12.0, installed: 3.20.0]
      - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
    - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
      - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
      - pyasn1-modules [required: >=0.2.1, installed: ?]
      - rsa [required: >=3.1.4,<5, installed: 4.8]
      - six [required: >=1.9.0, installed: 1.16.0]
  - proto-plus [required: >=1.10.0, installed: 1.20.3]
    - protobuf [required: >=3.19.0, installed: 3.20.0]
google-cloud-storage==2.2.1
  - google-api-core [required: >=1.31.5,<3.0.0dev,!=2.3.0,!=2.2.*,!=2.1.*,!=2.0.*, installed: 2.7.1]
    - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
      - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
      - pyasn1-modules [required: >=0.2.1, installed: ?]
      - rsa [required: >=3.1.4,<5, installed: 4.8]
      - six [required: >=1.9.0, installed: 1.16.0]
    - googleapis-common-protos [required: >=1.52.0,<2.0dev, installed: 1.56.0]
      - protobuf [required: >=3.12.0, installed: 3.20.0]
    - protobuf [required: >=3.12.0, installed: 3.20.0]
    - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
  - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
    - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
    - pyasn1-modules [required: >=0.2.1, installed: ?]
    - rsa [required: >=3.1.4,<5, installed: 4.8]
    - six [required: >=1.9.0, installed: 1.16.0]
  - google-cloud-core [required: >=1.6.0,<3.0dev, installed: 2.2.3]
    - google-api-core [required: >=1.31.5,<3.0.0dev,!=2.3.0,!=2.2.*,!=2.1.*,!=2.0.*, installed: 2.7.1]
      - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
        - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
        - pyasn1-modules [required: >=0.2.1, installed: ?]
        - rsa [required: >=3.1.4,<5, installed: 4.8]
        - six [required: >=1.9.0, installed: 1.16.0]
      - googleapis-common-protos [required: >=1.52.0,<2.0dev, installed: 1.56.0]
        - protobuf [required: >=3.12.0, installed: 3.20.0]
      - protobuf [required: >=3.12.0, installed: 3.20.0]
      - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
    - google-auth [required: >=1.25.0,<3.0dev, installed: 2.6.2]
      - cachetools [required: >=2.0.0,<6.0, installed: 5.0.0]
      - pyasn1-modules [required: >=0.2.1, installed: ?]
      - rsa [required: >=3.1.4,<5, installed: 4.8]
      - six [required: >=1.9.0, installed: 1.16.0]
  - google-resumable-media [required: >=2.3.2, installed: 2.3.2]
    - google-crc32c [required: >=1.0,<2.0dev, installed: ?]
  - protobuf [required: Any, installed: 3.20.0]
  - requests [required: >=2.18.0,<3.0.0dev, installed: 2.26.0]
graphene==2.1.9
  - aniso8601 [required: >=3,<=7, installed: 7.0.0]
  - graphql-core [required: >=2.1,<3, installed: 2.3.2]
    - promise [required: >=2.3,<3, installed: 2.3]
    - rx [required: >=1.6,<2, installed: 1.6.1]
    - six [required: >=1.10.0, installed: 1.16.0]
  - graphql-relay [required: >=2,<3, installed: 2.0.1]
    - graphql-core [required: >=2.2,<3, installed: 2.3.2]
      - promise [required: >=2.3,<3, installed: 2.3]
      - rx [required: >=1.6,<2, installed: 1.6.1]
      - six [required: >=1.10.0, installed: 1.16.0]
    - promise [required: >=2.2,<3, installed: 2.3]
    - six [required: >=1.12, installed: 1.16.0]
  - six [required: >=1.10.0,<2, installed: 1.16.0]
grpcio-status==1.45.0
  - googleapis-common-protos [required: >=1.5.5, installed: 1.56.0]
    - protobuf [required: >=3.12.0, installed: 3.20.0]
  - grpcio [required: >=1.45.0, installed: 1.45.0]
    - six [required: >=1.5.2, installed: 1.16.0]
  - protobuf [required: >=3.12.0, installed: 3.20.0]
mccabe==0.6.1
phonenumbers==8.12.46
Pillow==9.1.0
pip==22.0.4
platformdirs==2.5.1
prompt-toolkit==3.0.29
  - wcwidth [required: Any, installed: 0.2.5]
py-moneyed==1.2
  - babel [required: >=2.8.0, installed: 2.9.1]
pycountry==22.3.5
  - setuptools [required: Any, installed: 62.0.0]
pywatchman==1.4.2.dev1
redis==4.1.4
  - deprecated [required: >=1.2.3, installed: 1.2.13]
  - packaging [required: >=20.4, installed: 21.0]
restructuredtext-lint==1.4.0
  - docutils [required: >=0.11,<1.0, installed: 0.18.1]
sentry-sdk==1.5.8
  - certifi [required: Any, installed: 2021.05.30]
  - urllib3 [required: >=1.10.0, installed: 1.26.9]
slack-sdk==3.13.0
testfixtures==6.18.5
twilio==7.5.1
  - PyJWT [required: >=2.0.0,<3.0.0, installed: ?]
  - pytz [required: Any, installed: 2022.1]
  - requests [required: >=2.0.0, installed: 2.26.0]
tzdata==2022.1
tzlocal==4.2
  - pytz-deprecation-shim [required: Any, installed: ?]
wemake-python-styleguide==0.16.1
  - astor [required: >=0.8,<0.9, installed: 0.8.1]
  - attrs [required: Any, installed: ?]
  - darglint [required: >=1.2,<2.0, installed: ?]
  - flake8 [required: >=3.7,<5, installed: 4.0.1]
  - flake8-bandit [required: >=2.1,<4, installed: 3.0.0]
    - bandit [required: >=1.7.3, installed: 1.7.4]
      - GitPython [required: >=1.0.1, installed: 3.1.27]
        - gitdb [required: >=4.0.1,<5, installed: 4.0.9]
      - PyYAML [required: >=5.3.1, installed: ?]
      - stevedore [required: >=1.20.0, installed: ?]
    - flake8 [required: Any, installed: 4.0.1]
    - flake8-polyfill [required: Any, installed: ?]
    - pycodestyle [required: Any, installed: 2.8.0]
  - flake8-broken-line [required: >=0.3,<0.5, installed: ?]
  - flake8-bugbear [required: >=20.1,<23.0, installed: 22.3.23]
    - attrs [required: >=19.2.0, installed: ?]
    - flake8 [required: >=3.0.0, installed: 4.0.1]
  - flake8-commas [required: >=2.0,<3.0, installed: ?]
  - flake8-comprehensions [required: >=3.1,<4.0, installed: ?]
  - flake8-debugger [required: >=4.0,<5.0, installed: ?]
  - flake8-docstrings [required: >=1.3,<2.0, installed: ?]
  - flake8-eradicate [required: >=1.0,<2.0, installed: ?]
  - flake8-isort [required: >=4.0,<5.0, installed: ?]
  - flake8-quotes [required: >=3.0,<4.0, installed: ?]
  - flake8-rst-docstrings [required: >=0.2,<0.3, installed: ?]
  - flake8-string-format [required: >=0.3,<0.4, installed: ?]
  - pep8-naming [required: >=0.11,<0.13, installed: ?]
  - pygments [required: >=2.4,<3.0, installed: 2.11.2]
  - typing-extensions [required: >=3.6,<5.0, installed: ?]
wheel==0.37.1
wrapt==1.14.0
wsproto==1.1.0
  - h11 [required: >=0.9.0,<1, installed: 0.13.0]

OS information

Distributor ID: Ubuntu
Description:    Ubuntu 21.10
Release:        21.10
Codename:       impish
sobolevn commented 2 years ago

Oh, sorry!

Can you please make a PR with the fix? It should be 3 indeed.

sobolevn commented 2 years ago

Yeap. Here's what's wrong:

https://github.com/wemake-services/wemake-python-styleguide/blob/1b654fbe027efd3a6522a799ad76c1efe8279dea/wemake_python_styleguide/options/config.py#L102-L104