From f172c6770670ebc319bf92cc95496caf7368b26e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 26 Jan 2025 10:35:19 +0530 Subject: [PATCH] ./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. --- pyproject.toml | 54 +++++++++++++++++++++++++-- ruff-strict-pep8.toml | 87 ------------------------------------------- setup/check.py | 72 ++++++----------------------------- 3 files changed, 62 insertions(+), 151 deletions(-) delete mode 100644 ruff-strict-pep8.toml diff --git a/pyproject.toml b/pyproject.toml index c6bc3082fb..f313f8576f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,18 +15,55 @@ exclude = [ "src/templite/*", "src/tinycss/*", ] +preview = true [tool.ruff.format] quote-style = 'single' [tool.ruff.lint] -ignore = ['E402', 'E722', 'E741'] -select = ['E', 'F', 'I', 'W', 'INT', 'PIE794'] -unfixable = ['PIE794'] +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'] [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/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'] [tool.ruff.lint.isort] @@ -41,5 +78,14 @@ section-order = ['__python__', "future", "standard-library", "third-party", "fir [tool.ruff.lint.isort.sections] '__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] enabled = false diff --git a/ruff-strict-pep8.toml b/ruff-strict-pep8.toml deleted file mode 100644 index 3ea2b2b3a9..0000000000 --- a/ruff-strict-pep8.toml +++ /dev/null @@ -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' diff --git a/setup/check.py b/setup/check.py index ea069b8582..18a34f0e34 100644 --- a/setup/check.py +++ b/setup/check.py @@ -23,13 +23,6 @@ class Message: 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): for x in os.walk(root_path): for f in x[-1]: @@ -50,15 +43,10 @@ class Check(Command): description = 'Check for errors in the calibre source code' CACHE = 'check.json' - CACHE_STRICT = 'check-strict.json' def add_options(self, parser): parser.add_option('--fix', '--auto-fix', default=False, action='store_true', - help='Try to automatically fix some of the smallest errors') - 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"') + help='Try to automatically fix some of the smallest errors instead of opening an editor for bad files.') def get_files(self): yield from checkable_python_files(self.SRC) @@ -86,7 +74,7 @@ class Check(Command): @property 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): dump_json(cache, self.cache_file) @@ -94,7 +82,10 @@ class Check(Command): def file_has_errors(self, f): ext = os.path.splitext(f)[1] 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 if ext == '.pyj': 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]) 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): - 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.wn_path = os.path.expanduser('~/work/srv/main/static') self.has_changelog_check = os.path.exists(self.wn_path) self.auto_fix = opts.fix - self.is_strict_check = opts.strict - if opts.pep8: - self.run_pep8_commit() - else: - self.run_check_files() + self.run_check_files() def run_check_files(self): cache = {} @@ -141,10 +109,6 @@ class Check(Command): except OSError as err: if err.errno != errno.ENOENT: 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)) try: for i, f in enumerate(dirty_files): @@ -161,28 +125,16 @@ class Check(Command): finally: 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): for err in errors: self.info('\t\t', str(err)) def clean(self): - for cache_file in [self.j(build_cache_dir(), self.CACHE), self.j(build_cache_dir(), self.CACHE_STRICT)]: - try: - os.remove(cache_file) - except OSError as err: - if err.errno != errno.ENOENT: - raise + try: + os.remove(self.cache_file) + except OSError as err: + if err.errno != errno.ENOENT: + raise class UpgradeSourceCode(Command):