Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
env:
UV_PYTHON: ${{ matrix.python-version }}

Expand Down
46 changes: 24 additions & 22 deletions dessert/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ def get_data(self, pathname):
if sys.version_info >= (3, 12):
from importlib.resources.abc import TraversableResources
else:
from importlib.abc import TraversableResources
# Disabling in pylint, because it doesn't recognize it is for old versions only
from importlib.abc import TraversableResources # pylint: disable=deprecated-class

def get_resource_reader(self, name: str) -> TraversableResources:
if sys.version_info < (3, 11):
Expand Down Expand Up @@ -567,7 +568,8 @@ def run(self, mod: ast.Module) -> None:
if (
expect_docstring
and isinstance(item, ast.Expr)
and isinstance(item.value, ast.Str)
and isinstance(item.value, ast.Constant)
and isinstance(item.value.value, str)
):
doc = item.value.s
if self.is_rewrite_disabled(doc):
Expand Down Expand Up @@ -685,7 +687,7 @@ def push_format_context(self):
def pop_format_context(self, expl_expr):
"""Format the %-formatted string with current format context.

The expl_expr should be an ast.Str instance constructed from
The expl_expr should be an ast.Constant instance constructed from
the %-placeholders created by .explanation_param(). This will
add the required code to format said string to .expl_stmts and
return the ast.Name instance of the formatted string.
Expand All @@ -694,7 +696,7 @@ def pop_format_context(self, expl_expr):
current = self.stack.pop()
if self.stack:
self.explanation_specifiers = self.stack[-1]
keys = [ast.Str(key) for key in current.keys()]
keys = [ast.Constant(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter))
Expand Down Expand Up @@ -754,16 +756,16 @@ def visit_Assert(self, assert_):

if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook
negation = ast.UnaryOp(ast.Not(), top_condition)
msg = self.pop_format_context(ast.Str(explanation))
msg = self.pop_format_context(ast.Constant(explanation))

# Failed
if assert_.msg:
assertmsg = self.helper("_format_assertmsg", assert_.msg)
gluestr = "\n>assert "
else:
assertmsg = ast.Str("")
assertmsg = ast.Constant("")
gluestr = "assert "
err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg)
err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg)
err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation)
err_name = ast.Name("AssertionError", ast.Load())
fmt = self.helper("_format_explanation", err_msg)
Expand All @@ -779,8 +781,8 @@ def visit_Assert(self, assert_):
hook_call_pass = ast.Expr(
self.helper(
"_call_assertion_pass",
ast.Num(assert_.lineno),
ast.Str(orig),
ast.Constant(assert_.lineno),
ast.Constant(orig),
fmt_pass,
)
)
Expand All @@ -799,7 +801,7 @@ def visit_Assert(self, assert_):
variables = [
ast.Name(name, ast.Store()) for name in self.format_variables
]
clear_format = ast.Assign(variables, ast.NameConstant(None))
clear_format = ast.Assign(variables, ast.Constant(None))
self.statements.append(clear_format)

else: # Original assertion rewriting
Expand All @@ -811,13 +813,13 @@ def visit_Assert(self, assert_):
assertmsg = self.helper("_format_assertmsg", assert_.msg)
explanation = "\n>assert " + explanation
else:
assertmsg = ast.Str("")
assertmsg = ast.Constant("")
explanation = "assert " + explanation

if _MARK_ASSERTION_INTROSPECTION:
explanation = "dessert* " + explanation

template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation))
template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation))
msg = self.pop_format_context(template)
fmt = self.helper("_format_explanation", msg, assertmsg)
err_name = ast.Name("AssertionError", ast.Load())
Expand All @@ -829,7 +831,7 @@ def visit_Assert(self, assert_):
# Clear temporary variables by setting them to None.
if self.variables:
variables = [ast.Name(name, ast.Store()) for name in self.variables]
clear = ast.Assign(variables, ast.NameConstant(None))
clear = ast.Assign(variables, ast.Constant(None))
self.statements.append(clear)
# Fix line numbers.
for stmt in self.statements:
Expand All @@ -843,16 +845,16 @@ def warn_about_none_ast(self, node, module_path, lineno):
internally already.
See issue #3191 for more details.
"""
val_is_none = ast.Compare(node, [ast.Is()], [ast.NameConstant(None)])
val_is_none = ast.Compare(node, [ast.Is()], [ast.Constant(None)])

exc_call = ast.Call(
func=ast.Name("DessertAssertRewriteWarning", ast.Load()),
args=[ast.Str('asserting the value None, please use "assert is None"')],
args=[ast.Constant('asserting the value None, please use "assert is None"')],
keywords=[]
)
warn_call = ast.Call(
func=ast.Name("warn_explicit", ast.Load()),
args=[exc_call, ast.NameConstant(None), ast.Str(module_path), ast.Num(lineno)],
args=[exc_call, ast.Constant(None), ast.Constant(module_path), ast.Constant(lineno)],
keywords=[]
)
body = [
Expand All @@ -867,10 +869,10 @@ def visit_Name(self, name):
# Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
expr = ast.IfExp(test, self.display(name), ast.Constant(name.id))
return name, self.explanation_param(expr)

def visit_BoolOp(self, boolop):
Expand All @@ -892,7 +894,7 @@ def visit_BoolOp(self, boolop):
self.push_format_context()
res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl))
expl_format = self.pop_format_context(ast.Constant(expl))
call = ast.Call(app, [expl_format], [])
self.expl_stmts.append(ast.Expr(call))
if i < levels:
Expand All @@ -904,7 +906,7 @@ def visit_BoolOp(self, boolop):
self.statements = body = inner
self.statements = save
self.expl_stmts = fail_save
expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or))
expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or))
expl = self.pop_format_context(expl_template)
return ast.Name(res_var, ast.Load()), self.explanation_param(expl)

Expand Down Expand Up @@ -983,9 +985,9 @@ def visit_Compare(self, comp: ast.Compare):
next_expl = "({})".format(next_expl)
results.append(next_res)
sym = BINOP_MAP[op.__class__]
syms.append(ast.Str(sym))
syms.append(ast.Constant(sym))
expl = "{} {} {}".format(left_expl, sym, next_expl)
expls.append(ast.Str(expl))
expls.append(ast.Constant(expl))
res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr))
left_res, left_expl = next_res, next_expl
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ classifiers = [
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]

dependencies = ["munch", "py", "atomicwrites>=1.0", "attrs"]
Expand Down