Skip to content
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

feat: Support linting in out-of-source directories #9721

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

gremat
Copy link

@gremat gremat commented Jun 10, 2024

Type of Changes

Type
✨ New feature

Description

Linting files/modules that import external modules works just fine by adding the paths containing those modules to PYTHONPATH. However, there are some well-known situations where a mostly automated inclusion of certain directories is beneficial. E.g., in a python project setup where source files are located in src/, you want to lint the tests located in tests/ without import errors for the main module.
Similar to other python projects, like pytest, this PR introduces the configuration option main.pythonpath where you can specify absolute and, especially, relative paths that will always be included to sys.path when linting.

Notes on related issues:

Refs #9507
Refs #7357
Refs #5644

P.S.: Credits to the authors of 71b6325 which made this implementation straight forward.

EDIT:
In a first version, I've added --pythonpath in Pyreverse, too (though corrupted because unfortunately I missed that Pyreverse does not share config with the linter). But I gave it another thought and I don't see the point of integrating pythonpath there, too. So I will leave it out until someone argues in favor for it.

@gremat gremat requested a review from DudeNr33 as a code owner June 10, 2024 11:17
@gremat gremat force-pushed the feat/support-lintint-in-out-of-source-directories branch from 5646d29 to 451b675 Compare June 10, 2024 12:36
@gremat
Copy link
Author

gremat commented Jun 10, 2024

FYI: Changelog checks fails because of: actions/setup-python#886 .

This comment has been minimized.

@gremat gremat force-pushed the feat/support-lintint-in-out-of-source-directories branch from a3162e1 to 3f62568 Compare June 10, 2024 14:12
Copy link

codecov bot commented Jun 11, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 95.85%. Comparing base (3f1f7b8) to head (3f62568).
Report is 2 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #9721   +/-   ##
=======================================
  Coverage   95.84%   95.85%           
=======================================
  Files         174      174           
  Lines       18865    18868    +3     
=======================================
+ Hits        18082    18085    +3     
  Misses        783      783           
Files Coverage Δ
pylint/lint/__init__.py 91.66% <100.00%> (ø)
pylint/lint/base_options.py 100.00% <ø> (ø)
pylint/lint/parallel.py 92.85% <100.00%> (ø)
pylint/lint/pylinter.py 96.66% <100.00%> (+<0.01%) ⬆️
pylint/lint/utils.py 96.07% <100.00%> (+0.16%) ⬆️

Copy link
Contributor

🤖 According to the primer, this change has no effect on the checked open source code. 🤖🎉

This comment was generated for commit 3f62568

@DudeNr33 DudeNr33 removed their request for review June 15, 2024 10:38
@DanielNoord
Copy link
Collaborator

I don't really understand why we would provide another method to manipulate PYTHONPATH when users have multiple ways to do so.

I understand that we want to make this easier for users, but as far as I understand it setting up PYTHONPATH should be done before you call into your Python executable. The init-hook is the earliest thing that gets executed so that is probably the best place to get this behaviour? What was your reason for not documenting the current way to do this but add another option?

By the way, some of the PRs you link are not really related to this. For example, #5644 is just an issue with the project structure and not really related to any of the issues you mention in this PR.

@gremat
Copy link
Author

gremat commented Jun 28, 2024

By the way, some of the PRs you link are not really related to this. For example, #5644 is just an issue with the project structure and not really related to any of the issues you mention in this PR.

Actually, #5644 provides a very prominent - because it's pylint itself! - example for the benefit of my suggested changes. As far as I can see, and please correct me, if I'm wrong, as soon as pylint's code is moved to the src folder (as suggested in #5644), pylint's own pylintrc will have to do something about the import situation if you do not want to see import errors when editing files in tests. Currently, this would be the init-hook workaround as you mentioned.

I understand that we want to make this easier for users, but as far as I understand it setting up PYTHONPATH should be done before you call into your Python executable. The init-hook is the earliest thing that gets executed so that is probably the best place to get this behaviour? What was your reason for not documenting the current way to do this but add another option?

Yes, init-hook might be one of the first places to hook up into. However, I consider this workaround to be rather hacky and ugly. Compare, e.g., solutions of other parties like pytest, mypy, isort or setuptools which all provide a nice and clean way with a specific argument/configuration option to tackle this standard project layout. (Nowadays, that is; see link from #5644 for older alternatives.)
That said, my reason to add another option is mainly to provide a nice, clean and similarly-looking option in pylint, too. It will get easier for (new) users, in fact, easy enough that you almost don't need to consult the documentation/help output and could easily guesstimate the required option in your pyproject.toml.

I don't really understand why we would provide another method to manipulate PYTHONPATH when users have multiple ways to do so.

For pylint, I am only aware of --source-roots to manipulate PYTHONPATH but it serves a different purpose because it is related to implicit namespace packages and does not help with the init-hook vs. suggested option.
You are of course right that you could set PYTHONPATH externally, e.g., PYTHONPATH=./src pylint. But I see two problems with that. Firstly, you will need to convince your editor/IDE of that which is again error-prone, not out-of-the-box and you have to do that for every editor you use (moreover, might be even required on a per-project basis). Secondly, you might very well get into trouble finding and specifying the right location which depends on your current working directory, the file location, the project root and from where exactly it will be called.
Thus I don't consider the external option to be a viable approach.

@DanielNoord
Copy link
Collaborator

Actually, #5644 provides a very prominent - because it's pylint itself! - example for the benefit of my suggested changes.

I'm not sure if this is correct. Wouldn't this be solved by doing a pip install -e . before calling pylint? In that setup, the tests directory won't be able to import from pylint unless it is installed. Changing the PYTHONPATH to act as if it can seems incorrect.

Compare, e.g., solutions of other parties

I think the mypy example is exactly what frightens me from adding this. The amount of configuration and gotcha's on different platforms seems like a headache. For example, why use globs and not regular expressions like we do for other configuration options.
What I personally like about the init-hook is that we don't need to provide any "support". We can point users to common patterns/commands that might satisfy their needs, but there is no reason to look into our code to see whether we automatically expand home directories (to take the example from mypy). It's just Python code.

For pylint, I am only aware of --source-roots to manipulate PYTHONPATH but it serves a different purpose because it is related to implicit namespace packages and does not help with the init-hook vs. suggested option. You are of course right that you could set PYTHONPATH externally, e.g., PYTHONPATH=./src pylint. But I see two problems with that. Firstly, you will need to convince your editor/IDE of that which is again error-prone, not out-of-the-box and you have to do that for every editor you use (moreover, might be even required on a per-project basis). Secondly, you might very well get into trouble finding and specifying the right location which depends on your current working directory, the file location, the project root and from where exactly it will be called. Thus I don't consider the external option to be a viable approach.

I understand your point, and can see how it is far from optimal. On the other hand, the same argument as I provided above applies here. We can tap into and refer to existing documentation for setting PYTHONPATH for other tools and IDEs. Making our own custom solution increases the scope for bugs and edge cases we didn't consider.

I want to stress that I really do understand where you are coming from and see this as a nice feature for most users. However, from the perspective of a maintainers it seems to invite more edge cases and bugs to our tool without providing any features that the currently available features don't already provide. Perhaps I missed this so let me ask explicitly: is there anything that this solution allows you to do that you can't using the latest version of pylint? If there is then that might convince me to be more open to this change!

@akamat10
Copy link
Contributor

akamat10 commented Sep 29, 2024

#9967 is alternative way of solving this problem. Instead of falling back to legacy __init__.py based walking up the directories when source-roots are specified, it changes the fallback behavior for --source-roots to return the --source-roots list if the linting files don't overlap with any of the --source-roots. This will simplify the behavior of --source-roots and opens it up to more usecases beyond just implicit namespace packages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants