Browse Source

[cleanup] Add more ruff rules (#10149)

Authored by: seproDev

Reviewed-by: bashonly <88596187+bashonly@users.noreply.github.com>
Reviewed-by: Simon Sawicki <contact@grub4k.xyz>
sepro 9 months ago
parent
commit
add96eb9f8

+ 1 - 1
CONTRIBUTING.md

@@ -266,7 +266,7 @@ After you have ensured this site is distributing its content legally, you can fo
     $ hatch fmt --check
     ```
 
-    You can use `hatch fmt` to automatically fix problems.
+    You can use `hatch fmt` to automatically fix problems. Rules that the linter/formatter enforces should not be disabled with `# noqa` unless a maintainer requests it. The only exception allowed is for old/printf-style string formatting in GraphQL query templates (use `# noqa: UP031`).
 
 1. Make sure your code works under all [Python](https://www.python.org/) versions supported by yt-dlp, namely CPython and PyPy for Python 3.8 and above. Backward compatibility is not required for even older versions of Python.
 1. When the tests pass, [add](https://git-scm.com/docs/git-add) the new files, [commit](https://git-scm.com/docs/git-commit) them and [push](https://git-scm.com/docs/git-push) the result, like this:

+ 1 - 1
bundle/py2exe.py

@@ -44,7 +44,7 @@ def main():
                 'Cryptodome',
                 # requests >=2.32.0 breaks py2exe builds due to certifi dependency
                 'requests',
-                'urllib3'
+                'urllib3',
             ],
             'dll_excludes': ['w9xpopen.exe', 'crypt32.dll'],
             # Modules that are only imported dynamically must be added here

+ 4 - 4
bundle/pyinstaller.py

@@ -68,7 +68,7 @@ def exe(onedir):
         'dist/',
         onedir and f'{name}/',
         name,
-        OS_NAME == 'win32' and '.exe'
+        OS_NAME == 'win32' and '.exe',
     )))
 
 
@@ -113,7 +113,7 @@ def windows_set_version(exe, version):
         ),
         kids=[
             StringFileInfo([StringTable('040904B0', [
-                StringStruct('Comments', 'yt-dlp%s Command Line Interface' % suffix),
+                StringStruct('Comments', f'yt-dlp{suffix} Command Line Interface'),
                 StringStruct('CompanyName', 'https://github.com/yt-dlp'),
                 StringStruct('FileDescription', 'yt-dlp%s' % (MACHINE and f' ({MACHINE})')),
                 StringStruct('FileVersion', version),
@@ -123,8 +123,8 @@ def windows_set_version(exe, version):
                 StringStruct('ProductName', f'yt-dlp{suffix}'),
                 StringStruct(
                     'ProductVersion', f'{version}{suffix} on Python {platform.python_version()}'),
-            ])]), VarFileInfo([VarStruct('Translation', [0, 1200])])
-        ]
+            ])]), VarFileInfo([VarStruct('Translation', [0, 1200])]),
+        ],
     ))
 
 

+ 4 - 4
devscripts/bash-completion.py

@@ -9,8 +9,8 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 import yt_dlp
 
-BASH_COMPLETION_FILE = "completions/bash/yt-dlp"
-BASH_COMPLETION_TEMPLATE = "devscripts/bash-completion.in"
+BASH_COMPLETION_FILE = 'completions/bash/yt-dlp'
+BASH_COMPLETION_TEMPLATE = 'devscripts/bash-completion.in'
 
 
 def build_completion(opt_parser):
@@ -21,9 +21,9 @@ def build_completion(opt_parser):
             opts_flag.append(option.get_opt_string())
     with open(BASH_COMPLETION_TEMPLATE) as f:
         template = f.read()
-    with open(BASH_COMPLETION_FILE, "w") as f:
+    with open(BASH_COMPLETION_FILE, 'w') as f:
         # just using the special char
-        filled_template = template.replace("{{flags}}", " ".join(opts_flag))
+        filled_template = template.replace('{{flags}}', ' '.join(opts_flag))
         f.write(filled_template)
 
 

+ 5 - 5
devscripts/make_changelog.py

@@ -223,10 +223,10 @@ class Changelog:
 
         return message if not sep else f'{message}{sep}{rest}'
 
-    def _format_message_link(self, message, hash):
-        assert message or hash, 'Improperly defined commit message or override'
-        message = message if message else hash[:HASH_LENGTH]
-        return f'[{message}]({self.repo_url}/commit/{hash})' if hash else message
+    def _format_message_link(self, message, commit_hash):
+        assert message or commit_hash, 'Improperly defined commit message or override'
+        message = message if message else commit_hash[:HASH_LENGTH]
+        return f'[{message}]({self.repo_url}/commit/{commit_hash})' if commit_hash else message
 
     def _format_issues(self, issues):
         return ', '.join(f'[#{issue}]({self.repo_url}/issues/{issue})' for issue in issues)
@@ -356,7 +356,7 @@ class CommitRange:
                 logger.info(f'CHANGE {self._commits[commit.hash]} -> {commit}')
                 self._commits[commit.hash] = commit
 
-        self._commits = {key: value for key, value in reversed(self._commits.items())}
+        self._commits = dict(reversed(self._commits.items()))
 
     def groups(self):
         group_dict = defaultdict(list)

+ 6 - 6
devscripts/make_readme.py

@@ -51,7 +51,7 @@ PATCHES = (
     ),
     (   # Headings
         r'(?m)^  (\w.+\n)(    (?=\w))?',
-        r'## \1'
+        r'## \1',
     ),
     (   # Fixup `--date` formatting
         rf'(?m)(    --date DATE.+({delim}[^\[]+)*)\[.+({delim}.+)*$',
@@ -61,26 +61,26 @@ PATCHES = (
     ),
     (   # Do not split URLs
         rf'({delim[:-1]})? (?P<label>\[\S+\] )?(?P<url>https?({delim})?:({delim})?/({delim})?/(({delim})?\S+)+)\s',
-        lambda mobj: ''.join((delim, mobj.group('label') or '', re.sub(r'\s+', '', mobj.group('url')), '\n'))
+        lambda mobj: ''.join((delim, mobj.group('label') or '', re.sub(r'\s+', '', mobj.group('url')), '\n')),
     ),
     (   # Do not split "words"
         rf'(?m)({delim}\S+)+$',
-        lambda mobj: ''.join((delim, mobj.group(0).replace(delim, '')))
+        lambda mobj: ''.join((delim, mobj.group(0).replace(delim, ''))),
     ),
     (   # Allow overshooting last line
         rf'(?m)^(?P<prev>.+)${delim}(?P<current>.+)$(?!{delim})',
         lambda mobj: (mobj.group().replace(delim, ' ')
                       if len(mobj.group()) - len(delim) + 1 <= max_width + ALLOWED_OVERSHOOT
-                      else mobj.group())
+                      else mobj.group()),
     ),
     (   # Avoid newline when a space is available b/w switch and description
         DISABLE_PATCH,  # This creates issues with prepare_manpage
         r'(?m)^(\s{4}-.{%d})(%s)' % (switch_col_width - 6, delim),
-        r'\1 '
+        r'\1 ',
     ),
     (   # Replace brackets with a Markdown link
         r'SponsorBlock API \((http.+)\)',
-        r'[SponsorBlock API](\1)'
+        r'[SponsorBlock API](\1)',
     ),
 )
 

+ 1 - 1
devscripts/set-variant.py

@@ -30,7 +30,7 @@ def property_setter(name, value):
 opts = parse_options()
 transform = compose_functions(
     property_setter('VARIANT', opts.variant),
-    property_setter('UPDATE_HINT', opts.update_message)
+    property_setter('UPDATE_HINT', opts.update_message),
 )
 
 write_file(VERSION_FILE, transform(read_file(VERSION_FILE)))

+ 1 - 1
devscripts/update-version.py

@@ -24,7 +24,7 @@ def get_new_version(version, revision):
     else:
         old_version = read_version().split('.')
         if version.split('.') == old_version[:3]:
-            revision = str(int((old_version + [0])[3]) + 1)
+            revision = str(int(([*old_version, 0])[3]) + 1)
 
     return f'{version}.{revision}' if revision else version
 

+ 8 - 8
devscripts/zsh-completion.py

@@ -9,15 +9,15 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
 import yt_dlp
 
-ZSH_COMPLETION_FILE = "completions/zsh/_yt-dlp"
-ZSH_COMPLETION_TEMPLATE = "devscripts/zsh-completion.in"
+ZSH_COMPLETION_FILE = 'completions/zsh/_yt-dlp'
+ZSH_COMPLETION_TEMPLATE = 'devscripts/zsh-completion.in'
 
 
 def build_completion(opt_parser):
     opts = [opt for group in opt_parser.option_groups
             for opt in group.option_list]
-    opts_file = [opt for opt in opts if opt.metavar == "FILE"]
-    opts_dir = [opt for opt in opts if opt.metavar == "DIR"]
+    opts_file = [opt for opt in opts if opt.metavar == 'FILE']
+    opts_dir = [opt for opt in opts if opt.metavar == 'DIR']
 
     fileopts = []
     for opt in opts_file:
@@ -38,11 +38,11 @@ def build_completion(opt_parser):
     with open(ZSH_COMPLETION_TEMPLATE) as f:
         template = f.read()
 
-    template = template.replace("{{fileopts}}", "|".join(fileopts))
-    template = template.replace("{{diropts}}", "|".join(diropts))
-    template = template.replace("{{flags}}", " ".join(flags))
+    template = template.replace('{{fileopts}}', '|'.join(fileopts))
+    template = template.replace('{{diropts}}', '|'.join(diropts))
+    template = template.replace('{{flags}}', ' '.join(flags))
 
-    with open(ZSH_COMPLETION_FILE, "w") as f:
+    with open(ZSH_COMPLETION_FILE, 'w') as f:
         f.write(template)
 
 

+ 117 - 10
pyproject.toml

@@ -183,21 +183,84 @@ line-length = 120
 
 [tool.ruff.lint]
 ignore = [
-    "E402",  # module level import not at top of file
-    "E501",  # line too long
-    "E731",  # do not assign a lambda expression, use a def
-    "E741",  # ambiguous variable name
+    "E402",    # module-import-not-at-top-of-file
+    "E501",    # line-too-long
+    "E731",    # lambda-assignment
+    "E741",    # ambiguous-variable-name
+    "UP036",   # outdated-version-block
+    "B006",    # mutable-argument-default
+    "B008",    # function-call-in-default-argument
+    "B011",    # assert-false
+    "B017",    # assert-raises-exception
+    "B023",    # function-uses-loop-variable (false positives)
+    "B028",    # no-explicit-stacklevel
+    "B904",    # raise-without-from-inside-except
+    "C401",    # unnecessary-generator-set
+    "C402",    # unnecessary-generator-dict
+    "PIE790",  # unnecessary-placeholder
+    "SIM102",  # collapsible-if
+    "SIM108",  # if-else-block-instead-of-if-exp
+    "SIM112",  # uncapitalized-environment-variables
+    "SIM113",  # enumerate-for-loop
+    "SIM114",  # if-with-same-arms
+    "SIM115",  # open-file-with-context-handler
+    "SIM117",  # multiple-with-statements
+    "SIM223",  # expr-and-false
+    "SIM300",  # yoda-conditions
+    "TD001",   # invalid-todo-tag
+    "TD002",   # missing-todo-author
+    "TD003",   # missing-todo-link
+    "PLE0604", # invalid-all-object (false positives)
+    "PLW0603", # global-statement
+    "PLW1510", # subprocess-run-without-check
+    "PLW2901", # redefined-loop-name
+    "RUF001",  # ambiguous-unicode-character-string
+    "RUF012",  # mutable-class-default
+    "RUF100",  # unused-noqa (flake8 has slightly different behavior)
 ]
 select = [
-    "E",  # pycodestyle errors
-    "W",  # pycodestyle warnings
-    "F",  # pyflakes
-    "I",  # import order
+    "E",      # pycodestyle Error
+    "W",      # pycodestyle Warning
+    "F",      # Pyflakes
+    "I",      # isort
+    "Q",      # flake8-quotes
+    "N803",   # invalid-argument-name
+    "N804",   # invalid-first-argument-name-for-class-method
+    "UP",     # pyupgrade
+    "B",      # flake8-bugbear
+    "A",      # flake8-builtins
+    "COM",    # flake8-commas
+    "C4",     # flake8-comprehensions
+    "FA",     # flake8-future-annotations
+    "ISC",    # flake8-implicit-str-concat
+    "ICN003", # banned-import-from
+    "PIE",    # flake8-pie
+    "T20",    # flake8-print
+    "RSE",    # flake8-raise
+    "RET504", # unnecessary-assign
+    "SIM",    # flake8-simplify
+    "TID251", # banned-api
+    "TD",     # flake8-todos
+    "PLC",    # Pylint Convention
+    "PLE",    # Pylint Error
+    "PLW",    # Pylint Warning
+    "RUF",    # Ruff-specific rules
 ]
 
 [tool.ruff.lint.per-file-ignores]
-"devscripts/lazy_load_template.py" = ["F401"]
-"!yt_dlp/extractor/**.py" = ["I"]
+"devscripts/lazy_load_template.py" = [
+    "F401",   # unused-import
+]
+"!yt_dlp/extractor/**.py" = [
+    "I",      # isort
+    "ICN003", # banned-import-from
+    "T20",    # flake8-print
+    "A002",   # builtin-argument-shadowing
+    "C408",   # unnecessary-collection-call
+]
+"yt_dlp/jsinterp.py" = [
+    "UP031",  # printf-string-formatting
+]
 
 [tool.ruff.lint.isort]
 known-first-party = [
@@ -207,6 +270,50 @@ known-first-party = [
 ]
 relative-imports-order = "closest-to-furthest"
 
+[tool.ruff.lint.flake8-quotes]
+docstring-quotes = "double"
+multiline-quotes = "single"
+inline-quotes = "single"
+avoid-escape = false
+
+[tool.ruff.lint.pep8-naming]
+classmethod-decorators = [
+    "yt_dlp.utils.classproperty",
+]
+
+[tool.ruff.lint.flake8-import-conventions]
+banned-from = [
+    "base64",
+    "datetime",
+    "functools",
+    "glob",
+    "hashlib",
+    "itertools",
+    "json",
+    "math",
+    "os",
+    "pathlib",
+    "random",
+    "re",
+    "string",
+    "sys",
+    "time",
+    "urllib",
+    "uuid",
+    "xml",
+]
+
+[tool.ruff.lint.flake8-tidy-imports.banned-api]
+"yt_dlp.compat.compat_str".msg = "Use `str` instead."
+"yt_dlp.compat.compat_b64decode".msg = "Use `base64.b64decode` instead."
+"yt_dlp.compat.compat_urlparse".msg = "Use `urllib.parse` instead."
+"yt_dlp.compat.compat_parse_qs".msg = "Use `urllib.parse.parse_qs` instead."
+"yt_dlp.compat.compat_urllib_parse_unquote".msg = "Use `urllib.parse.unquote` instead."
+"yt_dlp.compat.compat_urllib_parse_urlencode".msg = "Use `urllib.parse.urlencode` instead."
+"yt_dlp.compat.compat_urllib_parse_urlparse".msg = "Use `urllib.parse.urlparse` instead."
+"yt_dlp.compat.compat_shlex_quote".msg = "Use `yt_dlp.utils.shell_quote` instead."
+"yt_dlp.utils.error_to_compat_str".msg = "Use `str` instead."
+
 [tool.autopep8]
 max_line_length = 120
 recursive = true

Some files were not shown because too many files changed in this diff