Skip to content

Disable consider-using-join for non-empty separator #8701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
vmeurisse opened this issue May 18, 2023 · 7 comments · Fixed by #9129 or #9461
Closed

Disable consider-using-join for non-empty separator #8701

vmeurisse opened this issue May 18, 2023 · 7 comments · Fixed by #9129 or #9461
Assignees
Labels
Enhancement ✨ Improvement to a component Good first issue Friendly and approachable by new contributors Help wanted 🙏 Outside help would be appreciated, good for new contributors Needs PR This issue is accepted, sufficiently specified and now needs an implementation
Milestone

Comments

@vmeurisse
Copy link

Current problem

In 2.16.0, consider-using-join was extended to include non-empty separators. This makes pylint complains about the following code:

def test(values: List[str]):
    data = ""
    for value in values:
        data += f"prefix{value}suffix"
    return data

However I don't think the suggested refactor is an improvement: prefix and suffix are duplicated and the empty list case need to be handled specifically

def test(values: List[str]):
    if not values:
        return ""
    return "prefix" + "suffixprefix".join(values) + "suffix"

The current options are:

  • disable the rule completely. This has the drawback of loosing the previous rule for empty separators.
  • use a "pylint: disable" comment. However having to use this on regular basis creates bad habits and reduce the value of linting

Desired solution

Would it be possible to have an option to disable the rule only for the non-empty separator case?

Additional context

No response

@vmeurisse vmeurisse added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label May 18, 2023
@Pierre-Sassoulas Pierre-Sassoulas added Enhancement ✨ Improvement to a component Needs PR This issue is accepted, sufficiently specified and now needs an implementation and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels May 19, 2023
@Pierre-Sassoulas
Copy link
Member

I remember having the same sentiment during initial implementation too. I think adding an option make sense.

@Pierre-Sassoulas Pierre-Sassoulas added Help wanted 🙏 Outside help would be appreciated, good for new contributors Good first issue Friendly and approachable by new contributors labels May 19, 2023
@vivekthedev
Copy link

I am interested in solving this one can you guide me through it?

@Pierre-Sassoulas
Copy link
Member

The checking code is here:

def _check_consider_using_join(self, aug_assign: nodes.AugAssign) -> None:
"""We start with the augmented assignment and work our way upwards.
Names of variables for nodes if match successful:
result = '' # assign
for number in ['1', '2', '3'] # for_loop
result += number # aug_assign
"""
for_loop = aug_assign.parent
if not isinstance(for_loop, nodes.For) or len(for_loop.body) > 1:
return
assign = for_loop.previous_sibling()
if not isinstance(assign, nodes.Assign):
return
result_assign_names = {
target.name
for target in assign.targets
if isinstance(target, nodes.AssignName)
}
is_concat_loop = (
aug_assign.op == "+="
and isinstance(aug_assign.target, nodes.AssignName)
and len(for_loop.body) == 1
and aug_assign.target.name in result_assign_names
and isinstance(assign.value, nodes.Const)
and isinstance(assign.value.value, str)
and self._name_to_concatenate(aug_assign.value) == for_loop.target.name
)
if is_concat_loop:
self.add_message("consider-using-join", node=aug_assign)
@utils.only_required_for_messages("consider-using-join")
def visit_augassign(self, node: nodes.AugAssign) -> None:
self._check_consider_using_join(node)

You can add option here:

options = (
(
"max-nested-blocks",
{
"default": 5,
"type": "int",
"metavar": "<int>",
"help": "Maximum number of nested blocks for function / method body",
},
),
(
"never-returning-functions",
{
"default": ("sys.exit", "argparse.parse_error"),
"type": "csv",
"metavar": "<members names>",
"help": "Complete name of functions that never returns. When checking "
"for inconsistent-return-statements if a never returning function is "
"called then it will be considered as an explicit return statement "
"and no message will be printed.",
},
),
)

And the functional tests are in this file. (To launch only the functional tests you can do pytest -k consider_join).

Let me know if there's anything else I can do to help :)

@vivekthedev
Copy link

Hey @Pierre-Sassoulas Thanks for providing the code sections. I have created the option but how should I go about modifying the checking. Should I write a new function or detect non-empty separator in the same function and return the function if found. I am new to contributing to large codebases.

@Pierre-Sassoulas
Copy link
Member

I think the _check_consider_using_join function can be tweaked a little to detect the non empty separator we want to avoid raising for if the suggest-join-with-non-empty-separator is true.

@vivekthedev
Copy link

Hey @Pierre-Sassoulas Apologies for late response. In this problem do we have to check the code for non-empty separators with astroid or just adding the the if statement like this is enough:

 if self.linter.config.suggest_join_with_non_empty_separator:
            return

@theirix
Copy link
Contributor

theirix commented Oct 8, 2023

Hello. I have tried to implement this feature, please take a look.

@Pierre-Sassoulas Pierre-Sassoulas added this to the 3.1.0 milestone Oct 8, 2023
netbsd-srcmastr referenced this issue in NetBSD/pkgsrc Mar 3, 2024
New Features
------------

- Skip ``consider-using-join`` check for non-empty separators if an ``suggest-join-with-non-empty-separator`` option is set to ``no``.

  Closes #8701 (`#8701 <https://github.com/pylint-dev/pylint/issues/8701>`_)

- Discover ``.pyi`` files when linting.

  These can be ignored with the ``ignore-patterns`` setting.

  Closes #9097 (`#9097 <https://github.com/pylint-dev/pylint/issues/9097>`_)

- Check ``TypeAlias`` and ``TypeVar`` (PEP 695) nodes for ``invalid-name``.

  Refs #9196 (`#9196 <https://github.com/pylint-dev/pylint/issues/9196>`_)

- Support for resolving external toml files named pylintrc.toml and .pylintrc.toml.

  Closes #9228 (`#9228 <https://github.com/pylint-dev/pylint/issues/9228>`_)

- Check for `.clear`, `.discard`, `.pop` and `remove` methods being called on a set while it is being iterated over.

  Closes #9334 (`#9334 <https://github.com/pylint-dev/pylint/issues/9334>`_)



New Checks
----------

- New message `use-yield-from` added to the refactoring checker. This message is emitted when yielding from a loop can be replaced by `yield from`.

  Closes #9229. (`#9229 <https://github.com/pylint-dev/pylint/issues/9229>`_)

- Added a ``deprecated-attribute`` message to check deprecated attributes in the stdlib.

  Closes #8855 (`#8855 <https://github.com/pylint-dev/pylint/issues/8855>`_)


False Positives Fixed
---------------------

- Fixed false positive for ``inherit-non-class`` for generic Protocols.

  Closes #9106 (`#9106 <https://github.com/pylint-dev/pylint/issues/9106>`_)

- Exempt ``TypedDict`` from ``typing_extensions`` from ``too-many-ancestor`` checks.

  Refs #9167 (`#9167 <https://github.com/pylint-dev/pylint/issues/9167>`_)



False Negatives Fixed
---------------------

- Extend broad-exception-raised and broad-exception-caught to except*.

  Closes #8827 (`#8827 <https://github.com/pylint-dev/pylint/issues/8827>`_)

- Fix a false-negative for unnecessary if blocks using a different than expected ordering of arguments.

  Closes #8947. (`#8947 <https://github.com/pylint-dev/pylint/issues/8947>`_)



Other Bug Fixes
---------------

- Improve the message provided for wrong-import-order check.  Instead of the import statement ("import x"), the message now specifies the import that is out of order and which imports should come after it.  As reported in the issue, this is particularly helpful if there are multiple imports on a single line that do not follow the PEP8 convention.

  The message will report imports as follows:
  For "import X", it will report "(standard/third party/first party/local) import X"
  For "import X.Y" and "from X import Y", it will report "(standard/third party/first party/local) import X.Y"
  The import category is specified to provide explanation as to why pylint has issued the message and guidence to the developer on how to fix the problem.

  Closes #8808 (`#8808 <https://github.com/pylint-dev/pylint/issues/8808>`_)



Other Changes
-------------

- Print how many files were checked in verbose mode.

  Closes #8935 (`#8935 <https://github.com/pylint-dev/pylint/issues/8935>`_)

- Fix a crash when an enum class which is also decorated with a ``dataclasses.dataclass`` decorator is defined.

  Closes #9100 (`#9100 <https://github.com/pylint-dev/pylint/issues/9100>`_)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement ✨ Improvement to a component Good first issue Friendly and approachable by new contributors Help wanted 🙏 Outside help would be appreciated, good for new contributors Needs PR This issue is accepted, sufficiently specified and now needs an implementation
Projects
None yet
4 participants