From be8f05d3672794a6dfa7b4f09f5efaa35df9e7e2 Mon Sep 17 00:00:00 2001 From: Jendrik Seipp Date: Fri, 6 Oct 2023 17:49:12 +0200 Subject: [PATCH] Add support for Python 3.12. (#332) * Use ast.Constant instead of ast.Str. * Bump pytest. --- .github/workflows/main.yml | 2 +- setup.py | 1 + tox.ini | 4 ++-- vulture/core.py | 16 ++++++++-------- vulture/utils.py | 4 ++++ 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ab4a511a..301e24e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] steps: - name: Checkout code diff --git a/setup.py b/setup.py index e3a3fbcc..3914be97 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ def find_version(*parts): "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Quality Assurance", diff --git a/tox.ini b/tox.ini index 05b75b83..af2c7f34 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = cleanup, py{38,310,311}, style # Skip py39 since it chokes on distutils. +envlist = cleanup, py{38,310,311,312}, style # Skip py39 since it chokes on distutils. skip_missing_interpreters = true # Erase old coverage results, then accumulate them during this tox run. @@ -13,7 +13,7 @@ commands = deps = coverage==7.0.5 pint # Use latest version to catch API changes. - pytest==7.2.1 + pytest==7.4.2 pytest-cov==4.0.0 commands = pytest {posargs} diff --git a/vulture/core.py b/vulture/core.py index 9278f0ee..b3c845cd 100644 --- a/vulture/core.py +++ b/vulture/core.py @@ -518,11 +518,11 @@ def visit_BinOp(self, node): "%(my_var)s" % locals() """ if ( - isinstance(node.left, ast.Str) + utils.is_ast_string(node.left) and isinstance(node.op, ast.Mod) and self._is_locals_call(node.right) ): - self.used_names |= set(re.findall(r"%\((\w+)\)", node.left.s)) + self.used_names |= set(re.findall(r"%\((\w+)\)", node.left.value)) def visit_Call(self, node): # Count getattr/hasattr(x, "some_attr", ...) as usage of some_attr. @@ -531,21 +531,21 @@ def visit_Call(self, node): or (node.func.id == "hasattr" and len(node.args) == 2) ): attr_name_arg = node.args[1] - if isinstance(attr_name_arg, ast.Str): - self.used_names.add(attr_name_arg.s) + if utils.is_ast_string(attr_name_arg): + self.used_names.add(attr_name_arg.value) # Parse variable names in new format strings: # "{my_var}".format(**locals()) if ( isinstance(node.func, ast.Attribute) - and isinstance(node.func.value, ast.Str) + and utils.is_ast_string(node.func.value) and node.func.attr == "format" and any( kw.arg is None and self._is_locals_call(kw.value) for kw in node.keywords ) ): - self._handle_new_format_string(node.func.value.s) + self._handle_new_format_string(node.func.value.value) def _handle_new_format_string(self, s): def is_identifier(name): @@ -651,8 +651,8 @@ def visit_Assign(self, node): if _assigns_special_variable__all__(node): assert isinstance(node.value, (ast.List, ast.Tuple)) for elt in node.value.elts: - if isinstance(elt, ast.Str): - self.used_names.add(elt.s) + if utils.is_ast_string(elt): + self.used_names.add(elt.value) def visit_While(self, node): self._handle_conditional_node(node, "while") diff --git a/vulture/utils.py b/vulture/utils.py index 4cec77fe..011bb130 100644 --- a/vulture/utils.py +++ b/vulture/utils.py @@ -53,6 +53,10 @@ def condition_is_always_true(condition): return _safe_eval(condition, False) +def is_ast_string(node): + return isinstance(node, ast.Constant) and isinstance(node.value, str) + + def format_path(path): try: return path.relative_to(pathlib.Path.cwd())