Skip to content

Commit

Permalink
Add ruff Python linter, remove flake8 (#2312)
Browse files Browse the repository at this point in the history
See: https://blog.jerrycodes.com/ruff-the-python-linter/. Advantages:

1) an order of magnitude faster than flake8 + isort (0.1 secs instead of 2.5 secs to lint all .py files):

Before:
```
$ time make flake8 isort 
real	0m2,554s
user	0m15,626s
sys	0m0,436s
```

Now:
```
$ time make ruff 
real	0m0,102s
user	0m0,297s
sys	0m0,025s
```

2) A lot more code quality checks than before.

Previously done also for pyftpdlib: giampaolo/pyftpdlib#611.
  • Loading branch information
giampaolo committed Oct 4, 2023
1 parent e7ba381 commit 84cdeb4
Show file tree
Hide file tree
Showing 74 changed files with 419 additions and 500 deletions.
34 changes: 0 additions & 34 deletions .flake8

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ jobs:
python-version: 3.x
- name: 'Run linters'
run: |
python3 -m pip install isort rstcheck toml-sort flake8 flake8-blind-except flake8-bugbear flake8-debugger flake8-print flake8-quotes sphinx
python3 -m pip install ruff rstcheck toml-sort sphinx
make lint-all
# Check sanity of .tar.gz + wheel files
Expand Down
8 changes: 2 additions & 6 deletions .github/workflows/issues.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Bot triggered by Github Actions every time a new issue, PR or comment
"""Bot triggered by Github Actions every time a new issue, PR or comment
is created. Assign labels, provide replies, closes issues, etc. depending
on the situation.
"""
Expand Down Expand Up @@ -144,10 +143,7 @@ def has_label(issue, label):

def has_os_label(issue):
labels = set([x.name for x in issue.labels])
for label in OS_LABELS:
if label in labels:
return True
return False
return any(x in labels for x in OS_LABELS)


def get_repo():
Expand Down
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ XXXX-XX-XX
- 2246_: drop python 3.4 & 3.5 support. (patch by Matthieu Darbois)
- 2290_: PID reuse is now pre-emptively checked for `Process.ppid()`_ and
`Process.parents()`_.
- 2312_: use ``ruff`` Python linter instead of ``flake8 + isort``. It's an
order of magnitude faster + it adds a ton of new code quality checks.

**Bug fixes**

Expand Down
1 change: 0 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
include .flake8
include .gitignore
include CONTRIBUTING.md
include CREDITS
Expand Down
32 changes: 7 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,9 @@ TSCRIPT = psutil/tests/runner.py

# Internal.
PY3_DEPS = \
autoflake \
autopep8 \
check-manifest \
concurrencytest \
coverage \
flake8 \
flake8-blind-except \
flake8-bugbear \
flake8-debugger \
flake8-print \
flake8-quotes \
isort \
pep8-naming \
pylint \
pyperf \
pypinfo \
Expand Down Expand Up @@ -82,6 +72,7 @@ clean: ## Remove all build files.
.coverage \
.failed-tests.txt \
.pytest_cache \
.ruff_cache/ \
build/ \
dist/ \
docs/_build/ \
Expand Down Expand Up @@ -200,11 +191,8 @@ test-coverage: ## Run test coverage.
# Linters
# ===================================================================

flake8: ## Run flake8 linter.
@git ls-files '*.py' | xargs $(PYTHON) -m flake8 --config=.flake8 --jobs=${NUM_WORKERS}

isort: ## Run isort linter.
@git ls-files '*.py' | xargs $(PYTHON) -m isort --check-only --jobs=${NUM_WORKERS}
ruff: ## Run ruff linter.
@git ls-files '*.py' | xargs $(PYTHON) -m ruff check --config=pyproject.toml --no-cache

_pylint: ## Python pylint (not mandatory, just run it from time to time)
@git ls-files '*.py' | xargs $(PYTHON) -m pylint --rcfile=pyproject.toml --jobs=${NUM_WORKERS}
Expand All @@ -219,8 +207,7 @@ lint-toml: ## Linter for pyproject.toml
@git ls-files '*.toml' | xargs toml-sort --check

lint-all: ## Run all linters
${MAKE} flake8
${MAKE} isort
${MAKE} ruff
${MAKE} lint-c
${MAKE} lint-rst
${MAKE} lint-toml
Expand All @@ -229,12 +216,8 @@ lint-all: ## Run all linters
# Fixers
# ===================================================================

fix-flake8: ## Run autopep8, fix some Python flake8 / pep8 issues.
@git ls-files '*.py' | xargs $(PYTHON) -m autopep8 --in-place --jobs=${NUM_WORKERS} --global-config=.flake8
@git ls-files '*.py' | xargs $(PYTHON) -m autoflake --in-place --jobs=${NUM_WORKERS} --remove-all-unused-imports --remove-unused-variables --remove-duplicate-keys

fix-imports: ## Fix imports with isort.
@git ls-files '*.py' | xargs $(PYTHON) -m isort --jobs=${NUM_WORKERS}
fix-ruff:
@git ls-files '*.py' | xargs $(PYTHON) -m ruff --config=pyproject.toml --no-cache --fix

fix-unittests: ## Fix unittest idioms.
@git ls-files '*test_*.py' | xargs $(PYTHON) -m teyit --show-stats
Expand All @@ -243,8 +226,7 @@ fix-toml: ## Fix pyproject.toml
@git ls-files '*.toml' | xargs toml-sort

fix-all: ## Run all code fixers.
${MAKE} fix-flake8
${MAKE} fix-imports
${MAKE} fix-ruff
${MAKE} fix-unittests
${MAKE} fix-toml

Expand Down
2 changes: 1 addition & 1 deletion docs/DEVGUIDE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Once you have a compiler installed run:
.. code-block:: bash
git clone git@github.com:giampaolo/psutil.git
make setup-dev-env # install useful dev libs (flake8, coverage, ...)
make setup-dev-env # install useful dev libs (ruff, coverage, ...)
make build
make install
make test
Expand Down
15 changes: 8 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,23 @@

# -- General configuration ------------------------------------------------

import ast
import datetime
import os


PROJECT_NAME = "psutil"
AUTHOR = u"Giampaolo Rodola"
AUTHOR = "Giampaolo Rodola"
THIS_YEAR = str(datetime.datetime.now().year)
HERE = os.path.abspath(os.path.dirname(__file__))


def get_version():
INIT = os.path.abspath(os.path.join(HERE, '../psutil/__init__.py'))
with open(INIT, 'r') as f:
with open(INIT) as f:
for line in f:
if line.startswith('__version__'):
ret = eval(line.strip().split(' = ')[1])
ret = ast.literal_eval(line.strip().split(' = ')[1])
assert ret.count('.') == 2, ret
for num in ret.split('.'):
assert num.isdigit(), ret
Expand Down Expand Up @@ -288,7 +289,7 @@ def get_version():
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'psutil.tex', u'psutil Documentation',
(master_doc, 'psutil.tex', 'psutil Documentation',
AUTHOR, 'manual'),
]

Expand All @@ -314,7 +315,7 @@ def get_version():
#
# latex_appendices = []

# It false, will not define \strong, \code, itleref, \crossref ... but only
# It false, will not define \strong, \code, itleref, \crossref ... but only
# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
# packages.
#
Expand All @@ -330,7 +331,7 @@ def get_version():
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'psutil', u'psutil Documentation',
(master_doc, 'psutil', 'psutil Documentation',
[author], 1)
]

Expand All @@ -345,7 +346,7 @@ def get_version():
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'psutil', u'psutil Documentation',
(master_doc, 'psutil', 'psutil Documentation',
author, 'psutil', 'One line description of project.',
'Miscellaneous'),
]
Expand Down
22 changes: 10 additions & 12 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@
# See: https://github.com/giampaolo/psutil/issues/564
if (int(__version__.replace('.', '')) !=
getattr(_psplatform.cext, 'version', None)):
msg = "version conflict: %r C extension module was built for another " \
"version of psutil" % _psplatform.cext.__file__
msg = "version conflict: %r C extension " % _psplatform.cext.__file__
msg += "module was built for another version of psutil"
if hasattr(_psplatform.cext, 'version'):
msg += " (%s instead of %s)" % (
'.'.join([x for x in str(_psplatform.cext.version)]), __version__)
Expand Down Expand Up @@ -267,10 +267,7 @@ def _pprint_secs(secs):
"""Format seconds in a human readable form."""
now = time.time()
secs_ago = int(now - secs)
if secs_ago < 60 * 60 * 24:
fmt = "%H:%M:%S"
else:
fmt = "%Y-%m-%d %H:%M:%S"
fmt = "%H:%M:%S" if secs_ago < 60 * 60 * 24 else "%Y-%m-%d %H:%M:%S"
return datetime.datetime.fromtimestamp(secs).strftime(fmt)


Expand All @@ -279,7 +276,7 @@ def _pprint_secs(secs):
# =====================================================================


class Process(object):
class Process(object): # noqa: UP004
"""Represents an OS process with the given PID.
If PID is omitted current process PID (os.getpid()) is used.
Raise NoSuchProcess if PID does not exist.
Expand Down Expand Up @@ -869,7 +866,8 @@ def cpu_num(self):

def environ(self):
"""The environment variables of the process as a dict. Note: this
might not reflect changes made after the process started. """
might not reflect changes made after the process started.
"""
return self._proc.environ()

if WINDOWS:
Expand Down Expand Up @@ -1333,7 +1331,7 @@ class Popen(Process):
>>> p.username()
'giampaolo'
>>> p.communicate()
('hi\n', None)
('hi', None)
>>> p.terminate()
>>> p.wait(timeout=2)
0
Expand Down Expand Up @@ -1384,7 +1382,7 @@ def __getattribute__(self, name):
def wait(self, timeout=None):
if self.__subproc.returncode is not None:
return self.__subproc.returncode
ret = super(Popen, self).wait(timeout)
ret = super(Popen, self).wait(timeout) # noqa
self.__subproc.returncode = ret
return ret

Expand Down Expand Up @@ -2200,7 +2198,7 @@ def net_if_addrs():
if WINDOWS and fam == -1:
fam = _psplatform.AF_LINK
elif (hasattr(_psplatform, "AF_LINK") and
_psplatform.AF_LINK == fam):
fam == _psplatform.AF_LINK):
# Linux defines AF_LINK as an alias for AF_PACKET.
# We re-set the family here so that repr(family)
# will show AF_LINK rather than AF_PACKET
Expand Down Expand Up @@ -2425,7 +2423,7 @@ def test(): # pragma: no cover

del memoize_when_activated, division
if sys.version_info[0] < 3:
del num, x
del num, x # noqa

if __name__ == "__main__":
test()

0 comments on commit 84cdeb4

Please sign in to comment.