./setup.py check now uses strict checking

No need to maintain two ruff configs. And strict checking happens in
editors/CI automatically, making my life a bit easier.

Remove the no longer needed --strict and --pep8 options from the check
command.
This commit is contained in:
Kovid Goyal 2025-01-26 10:35:19 +05:30
parent c6c7ed4dbe
commit f172c67706
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 62 additions and 151 deletions

View File

@ -15,18 +15,55 @@ exclude = [
"src/templite/*", "src/templite/*",
"src/tinycss/*", "src/tinycss/*",
] ]
preview = true
[tool.ruff.format] [tool.ruff.format]
quote-style = 'single' quote-style = 'single'
[tool.ruff.lint] [tool.ruff.lint]
ignore = ['E402', 'E722', 'E741'] explicit-preview-rules = true
select = ['E', 'F', 'I', 'W', 'INT', 'PIE794'] ignore = [
unfixable = ['PIE794'] 'E402', 'E722', 'E741',
'UP012', 'UP030', 'UP032', 'UP038', 'C413', 'C420', 'PIE790', 'ISC003',
'RUF001', 'RUF002', 'RUF003', 'RUF005', 'RUF012', 'RUF013', 'RUF015', 'RUF031', 'RUF100',
'F841', # because in preview, unused tuple unpacking variable that not use dummy syntax (prefix '_' underscore)
# raise error 'unused-variable', sigh (https://github.com/astral-sh/ruff/issues/8884)
]
select = [
'E', 'F', 'I', 'W', 'INT',
'Q', 'UP', 'YTT', 'TID', 'C4', 'COM818', 'PIE', 'RET501', 'ISC',
'RUF', # nota: RUF can flag many unsolicited errors
# preview rules
'RUF051', 'RUF056', # useless dict operation
'RUF055', # unnecessary regex
'RUF039', # always use raw-string for regex
'RUF047', # needless else
'E302', 'E303', 'E304', 'E305', 'W391', # blank-line standard
'E111', 'E112', 'E113', 'E117', # code indentation
'E114', 'E115', 'E116', 'E261', 'E262', 'E265', # comment formating
'E201', 'E202', 'E211', 'E251', 'E275', # various whitespace
]
unfixable = ['PIE794', 'ISC001']
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"recipes/*" = ['UP']
"manual/plugin_examples/*" = ['UP']
"setup/changelog.py" = ['ISC001']
"setup/commands.py" = ['RUF022']
"src/calibre/*" = ['UP031']
"src/calibre/customize/__init__.py" = ['RET501']
"src/calibre/devices/interface.py" = ['RET501']
"src/calibre/devices/kobo/driver.py" = ['E116']
"src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501'] "src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501']
"src/qt/*.py" = ['I'] "src/calibre/ebooks/metadata/sources/*" = ['UP']
"src/calibre/ebooks/metadata/sources/base.py" = ['RET501']
"src/calibre/ebooks/pdf/reflow.py" = ['E114']
"src/calibre/gui2/store/stores/*" = ['UP']
"src/calibre/gui2/tts/manager.py" = ['UP037']
"src/calibre/utils/copy_files.py" = ['UP037']
"src/calibre/utils/smartypants.py" = ['RUF039', 'RUF055']
"src/calibre/web/feeds/news.py" = ['RET501']
"src/qt/*.py" = ['I', 'E302']
"src/qt/*.pyi" = ['I'] "src/qt/*.pyi" = ['I']
[tool.ruff.lint.isort] [tool.ruff.lint.isort]
@ -41,5 +78,14 @@ section-order = ['__python__', "future", "standard-library", "third-party", "fir
[tool.ruff.lint.isort.sections] [tool.ruff.lint.isort.sections]
'__python__' = ['__python__'] '__python__' = ['__python__']
[tool.ruff.lint.flake8-comprehensions]
allow-dict-calls-with-keyword-arguments = true
[tool.ruff.lint.flake8-quotes]
avoid-escape = true
docstring-quotes = 'single'
inline-quotes = 'single'
multiline-quotes = 'single'
[tool.pylsp-mypy] [tool.pylsp-mypy]
enabled = false enabled = false

View File

@ -1,87 +0,0 @@
line-length = 160
target-version = 'py310'
builtins = ['_', 'I', 'P']
include = ['*.py', '*.recipe']
exclude = [
"*_ui.py",
"bypy/*",
"setup/polib.py",
"setup/linux-installer.py",
"src/css_selectors/*",
"src/polyglot/*",
"src/templite/*",
"src/tinycss/*",
]
preview = true
[format]
quote-style = 'single'
[lint]
explicit-preview-rules = true
ignore = [
'E402', 'E722', 'E741',
'UP012', 'UP030', 'UP032', 'UP038', 'C413', 'C420', 'PIE790', 'ISC003',
'RUF001', 'RUF002', 'RUF003', 'RUF005', 'RUF012', 'RUF013', 'RUF015', 'RUF031', 'RUF100',
'F841', # because in preview, unused tuple unpacking variable that not use dummy syntax (prefix '_' underscore)
# raise error 'unused-variable', sigh (https://github.com/astral-sh/ruff/issues/8884)
]
select = [
'E', 'F', 'I', 'W', 'INT',
'Q', 'UP', 'YTT', 'TID', 'C4', 'COM818', 'PIE', 'RET501', 'ISC',
'RUF', # nota: RUF can flag many unsolicited errors
# preview rules
'RUF051', 'RUF056', # useless dict operation
'RUF055', # unnecessary regex
'RUF039', # always use raw-string for regex
'RUF047', # needless else
'E302', 'E303', 'E304', 'E305', 'W391', # blank-line standard
'E111', 'E112', 'E113', 'E117', # code indentation
'E114', 'E115', 'E116', 'E261', 'E262', 'E265', # comment formating
'E201', 'E202', 'E211', 'E251', 'E275', # various whitespace
]
unfixable = ['PIE794', 'ISC001']
[lint.per-file-ignores]
"recipes/*" = ['UP']
"manual/plugin_examples/*" = ['UP']
"setup/changelog.py" = ['ISC001']
"setup/commands.py" = ['RUF022']
"src/calibre/*" = ['UP031']
"src/calibre/customize/__init__.py" = ['RET501']
"src/calibre/devices/interface.py" = ['RET501']
"src/calibre/devices/kobo/driver.py" = ['E116']
"src/calibre/ebooks/unihandecode/*codepoints.py" = ['E501']
"src/calibre/ebooks/metadata/sources/*" = ['UP']
"src/calibre/ebooks/metadata/sources/base.py" = ['RET501']
"src/calibre/ebooks/pdf/reflow.py" = ['E114']
"src/calibre/gui2/store/stores/*" = ['UP']
"src/calibre/gui2/tts/manager.py" = ['UP037']
"src/calibre/utils/copy_files.py" = ['UP037']
"src/calibre/utils/smartypants.py" = ['RUF039', 'RUF055']
"src/calibre/web/feeds/news.py" = ['RET501']
"src/qt/*.py" = ['I', 'E302']
"src/qt/*.pyi" = ['I']
[lint.isort]
detect-same-package = true
extra-standard-library = ["aes", "elementmaker", "encodings"]
known-first-party = ["calibre_extensions", "calibre_plugins", "polyglot"]
known-third-party = ["odf", "qt", "templite", "tinycss", "css_selectors"]
relative-imports-order = "closest-to-furthest"
split-on-trailing-comma = false
section-order = ['__python__', "future", "standard-library", "third-party", "first-party", "local-folder"]
[lint.isort.sections]
'__python__' = ['__python__']
[lint.flake8-comprehensions]
allow-dict-calls-with-keyword-arguments = true
[lint.flake8-quotes]
avoid-escape = true
docstring-quotes = 'single'
inline-quotes = 'single'
multiline-quotes = 'single'

View File

@ -23,13 +23,6 @@ class Message:
return f'{self.filename}:{self.lineno}: {self.msg}' return f'{self.filename}:{self.lineno}: {self.msg}'
def get_ruff_config(is_strict_check):
if is_strict_check:
return ['--config=ruff-strict-pep8.toml']
else:
return []
def files_walker(root_path, ext): def files_walker(root_path, ext):
for x in os.walk(root_path): for x in os.walk(root_path):
for f in x[-1]: for f in x[-1]:
@ -50,15 +43,10 @@ class Check(Command):
description = 'Check for errors in the calibre source code' description = 'Check for errors in the calibre source code'
CACHE = 'check.json' CACHE = 'check.json'
CACHE_STRICT = 'check-strict.json'
def add_options(self, parser): def add_options(self, parser):
parser.add_option('--fix', '--auto-fix', default=False, action='store_true', parser.add_option('--fix', '--auto-fix', default=False, action='store_true',
help='Try to automatically fix some of the smallest errors') help='Try to automatically fix some of the smallest errors instead of opening an editor for bad files.')
parser.add_option('--pep8', '--pep8-commit', default=False, action='store_true',
help='Try to automatically fix some of the smallest errors, then perform a pep8 commit')
parser.add_option('--strict', '--strict-pep8', default=False, action='store_true',
help='Perform the checking more strictely. See the file "strict-pep8.toml"')
def get_files(self): def get_files(self):
yield from checkable_python_files(self.SRC) yield from checkable_python_files(self.SRC)
@ -86,7 +74,7 @@ class Check(Command):
@property @property
def cache_file(self): def cache_file(self):
return self.j(build_cache_dir(), self.CACHE_STRICT if self.is_strict_check else self.CACHE) return self.j(build_cache_dir(), self.CACHE)
def save_cache(self, cache): def save_cache(self, cache):
dump_json(cache, self.cache_file) dump_json(cache, self.cache_file)
@ -94,7 +82,10 @@ class Check(Command):
def file_has_errors(self, f): def file_has_errors(self, f):
ext = os.path.splitext(f)[1] ext = os.path.splitext(f)[1]
if ext in {'.py', '.recipe'}: if ext in {'.py', '.recipe'}:
p = subprocess.Popen(['ruff', 'check', f] + get_ruff_config(self.is_strict_check)) if self.auto_fix:
p = subprocess.Popen(['ruff', 'check', '--fix', f])
else:
p = subprocess.Popen(['ruff', 'check', f])
return p.wait() != 0 return p.wait() != 0
if ext == '.pyj': if ext == '.pyj':
p = subprocess.Popen(['rapydscript', 'lint', f]) p = subprocess.Popen(['rapydscript', 'lint', f])
@ -103,35 +94,12 @@ class Check(Command):
p = subprocess.Popen(['python', self.j(self.wn_path, 'whats_new.py'), f]) p = subprocess.Popen(['python', self.j(self.wn_path, 'whats_new.py'), f])
return p.wait() != 0 return p.wait() != 0
def perform_auto_fix(self):
cp = subprocess.run(['ruff', 'check', '--fix-only'], stdout=subprocess.PIPE)
if cp.returncode != 0:
raise SystemExit('ruff fixing failed')
return cp.stdout.decode('utf-8') or 'Fixed 0 errors.'
def perform_pep8_git_commit(self):
return subprocess.run(['git', 'commit', '--all', '-m', 'pep8']).returncode != 0
def check_errors_remain(self):
return subprocess.run(['ruff', 'check', '--statistics']).returncode != 0
def run(self, opts): def run(self, opts):
if opts.fix and opts.pep8:
self.info('setup.py check: error: options --fix and --pep8 are mutually exclusive')
raise SystemExit(2)
if opts.strict and opts.pep8:
self.info('setup.py check: error: options --strict and --pep8 are mutually exclusive')
raise SystemExit(2)
self.fhash_cache = {} self.fhash_cache = {}
self.wn_path = os.path.expanduser('~/work/srv/main/static') self.wn_path = os.path.expanduser('~/work/srv/main/static')
self.has_changelog_check = os.path.exists(self.wn_path) self.has_changelog_check = os.path.exists(self.wn_path)
self.auto_fix = opts.fix self.auto_fix = opts.fix
self.is_strict_check = opts.strict self.run_check_files()
if opts.pep8:
self.run_pep8_commit()
else:
self.run_check_files()
def run_check_files(self): def run_check_files(self):
cache = {} cache = {}
@ -141,10 +109,6 @@ class Check(Command):
except OSError as err: except OSError as err:
if err.errno != errno.ENOENT: if err.errno != errno.ENOENT:
raise raise
if self.auto_fix:
self.info('\tAuto-fixing')
msg = self.perform_auto_fix()
self.info(msg+'\n')
dirty_files = tuple(f for f in self.get_files() if not self.is_cache_valid(f, cache)) dirty_files = tuple(f for f in self.get_files() if not self.is_cache_valid(f, cache))
try: try:
for i, f in enumerate(dirty_files): for i, f in enumerate(dirty_files):
@ -161,28 +125,16 @@ class Check(Command):
finally: finally:
self.save_cache(cache) self.save_cache(cache)
def run_pep8_commit(self):
require_git_master()
require_clean_git()
msg = self.perform_auto_fix()
self.info(msg+'\n')
self.info('Commit the pep8 change...')
self.perform_pep8_git_commit()
self.info()
if self.check_errors_remain():
self.info('There are remaining errors. Execute "setup.py check" without options to locate them.')
def report_errors(self, errors): def report_errors(self, errors):
for err in errors: for err in errors:
self.info('\t\t', str(err)) self.info('\t\t', str(err))
def clean(self): def clean(self):
for cache_file in [self.j(build_cache_dir(), self.CACHE), self.j(build_cache_dir(), self.CACHE_STRICT)]: try:
try: os.remove(self.cache_file)
os.remove(cache_file) except OSError as err:
except OSError as err: if err.errno != errno.ENOENT:
if err.errno != errno.ENOENT: raise
raise
class UpgradeSourceCode(Command): class UpgradeSourceCode(Command):