对一个目录和所有子目录中的所有 Python 文件运行 Pylint

是的

find . -iname "*.py" -exec pylint -E {} ;\

还有

FILES=$(find . -iname "*.py")
pylint -E $FILES

如果我理解正确,第一个命令将对每个 Python 文件运行 pylint,第二个命令将对所有文件运行 pylint 一次。我希望这两个命令返回相同的输出,但是它们返回不同的结果。我认为这种区别在某种程度上与导入和 F (故障) pylint 消息有关,这在导入失败时发生,是 pylint-E 的 没有输出。

是否有人已经经历过这种情况,并能够解释为什么会出现这种差异,以及运行 pylint 的最佳方式是什么?

110423 次浏览

My one cent

find . -type f -name "*.py" | xargs pylint

How does it work?

find finds all files ends with py and pass to xargs, xargs runs pylint command on each file.

NOTE: You can give any argument to pylint command as well.

EDIT:

According to doc we can use

  1. pylint mymodule.py

  2. pylint directory/mymodule.py

  3. pylint ./module

number 2 will work if the directory is a python package (i.e. has an __init__.py file or it is an implicit namespace package) or if the “directory” is in the python path.

Just pass the directory name to the pylint command. To lint all files in ./server:

pylint ./server

Note that this requires the __init__.py file to exist in the target directory.

[UPDATED based on helpful additions in the comments]

If you don't have an __init__.py file in the directory, and you don't want to for various reasons, my approach is

touch __init__.py; pylint $(pwd); rm __init__.py

If you already have a __init__.py file in that directory, it will be deleted.

If you find yourself needing this functionality often, you should make a function that does this in a safer way that preserves a pre-existing __init__.py file. For example, you could put the following pylint_all_the_things function in your ~/.bashrc file. (The last line exports the function so it can be called from any subshell.) If you don't want to edit .bashrc, you could put the function body in an executable shell script file.

This function defaults to running pylint in your current directory, but you can specify the directory to use as the 1st function argument.

# Run pylint in a given directory, defaulting to the working directory
pylint_all_the_things() {
local d=${1:-$(pwd)}


# Abort if called with a non-directory argument.
if [ ! -d "${d}" ]; then
echo "Not a directory: ${d}"
echo "If ${d} is a module or package name, call pylint directly"
exit 1
fi


local module_marker="${d}/__init__.py"


# Cleanup function to later remove __init__.py if it doesn't currently exist
[[ ! -f ${module_marker} ]] && local not_a_module=1
cleanup() {
(( ${not_a_module:-0} == 1 )) && rm "${module_marker}"
}
trap cleanup EXIT


# Create __init__.py if it doesn't exist
touch "${module_marker}"
pylint "${d}"
cleanup
}
export -f pylint_all_the_things

The trap utility is used to ensure the cleanup happens even if the call to pylint fails and you have set -e enabled, which causes the function to exit before reaching the cleanup line.

If you want to call pylint recursively on the current working directory and all subfolders, you could do something like

for dir in ./**/ ; do pylint_all_the_things "$dir"; done

Which will require globstar to be enabled in bash (shopt -s globstar).

If your goal is to run pylint on all files in the current working directory and subfolders, here is one workaround. This script runs pylint on the current directory. If __init__.py does not exist, it creates it, runs pylint, then removes it.

#! /bin/bash -
if [[ ! -e __init__.py ]]; then
touch __init__.py
pylint `pwd`
rm __init__.py
else
pylint `pwd`
fi

And if you want to run your custom configuration file use below command

pylint --rcfile=.pylintrc <directory_name>

To run pylint on all *.py files in a directory and its subdirectories, you can run:

shopt -s globstar  # for Bash
pylint ./**/*.py
  1. touch __init__.py in the current directory
  2. touch __init__.py in every subdirectory that you want pylint to look at
  3. pylint $(pwd) (or equivalently pylint /absolute/path/to/current/directory)

There is already an issue for this and hopefully gets fixed soon.

If you do not prefer to use xargs you can just do a plain find-exec:

find . -type f -name "*.py" -exec pylint -j 0 --exit-zero {} \;


The problem I had with pylint Project-Dir is that all the absolute imports were not working.

Im using the "pylint_runner" in order to run pylint on all files in the directory and the subdirectories. Python 3.7.4

pylint_runner 0.54

pylint 2.4.1

https://pypi.org/project/pylint_runner/

Here is the command to run it from the Docker container:

docker run -i --rm --name my_container \
-v "$PWD":"$PWD" -w "$PWD" \
python:3.7 \
/bin/sh -c "pip3 install -r requirements.txt; pylint_runner -v"

requirements.txt - should exist in the "$PWD" directory and contain "pylint_runner" entry.

pytest with pytest-pylint can trivially run pylint on all Python files:

In your setup.cfg file in the root directory of your project, ensure you have at minimum:

[tool:pytest]
addopts = --pylint

Next, run pytest on the command line.

To run Pylint in all subdirectories,

pylint $(find [a-z]* -type d)

This solution is simpler and more direct than others. It works with no setup, including on macOS which doesn't have Bash 4.

The reason for the [a-z]* is because most projects have Git or other magic subdirectories, which would pollute the results. If you have subdirectories starting with Capital Letters, use this variant:

pylint $(find [a-zA-Z]* -type d)

To run Pylint on all code in Git version control,

pylint $(git ls-files '*.py')

This is very fast, as Git already knows the names of all of your files. It also works on macOS which lacks Bash 4, and also Windows. However it won't lint files that are very new and haven't been git added to the repos yet.

My thanks to national treasure Julia Evans for the git ls-files trick -- here's her original use case, automating workflows with "entr": https://jvns.ca/blog/2020/06/28/entr/

I used in the root directory:

pylint *

using ./server or something similar only works if there is a __init__.py in all subdirs, it will not match all python files in all subdirs. using find and xargs is an option, but this also works:

pylint **py

The pylint docs makes mention of a --recursive flag, which tells pylint to find all modules and packages by itself without needing the user to manually specify all of them.

Therefore, all you need to do is:

pylint --recursive=y .

Looks like the flag was introduced in pylint v2.13.0, but it seems that from v2.15.0 onwards, you won't need the --recursive flag, and that will be pylint's default behaviour.