openrewrite-migrate-python 0.19.0__py3-none-any.whl → 0.20.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openrewrite_code_quality_python/cleanup/boolean_simplification.py +153 -51
- openrewrite_code_quality_python/cleanup/pandas_cleanup.py +10 -2
- openrewrite_code_quality_python/codequality/_grouping.py +31 -0
- openrewrite_code_quality_python/codequality/collapsible_if_statements.py +6 -1
- openrewrite_code_quality_python/codequality/merge_identical_branches.py +6 -1
- openrewrite_migrate_python/migrate/_method_renames.py +13 -5
- openrewrite_migrate_python/migrate/backoff_to_tenacity.py +74 -33
- {openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/METADATA +2 -2
- {openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/RECORD +12 -11
- {openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/WHEEL +0 -0
- {openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/entry_points.txt +0 -0
- {openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,8 @@ unnecessary operators, literals, or indirection:
|
|
|
14
14
|
See: https://peps.python.org/pep-0008/#programming-recommendations
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from typing import Any, List
|
|
17
|
+
from typing import AbstractSet, Any, Dict, List, NamedTuple, Tuple
|
|
18
|
+
from uuid import UUID
|
|
18
19
|
|
|
19
20
|
from rewrite import ExecutionContext, Preconditions, Recipe, TreeVisitor
|
|
20
21
|
from rewrite.markers import Markers
|
|
@@ -28,11 +29,11 @@ from rewrite.python.preconditions import uses_method
|
|
|
28
29
|
from rewrite.python.scope_utils import scope_of
|
|
29
30
|
from rewrite.python.type_utils import is_of_class_type
|
|
30
31
|
from rewrite.python.visitor import PythonVisitor
|
|
31
|
-
from rewrite.python.template import pattern, template, capture
|
|
32
|
+
from rewrite.python.template import Capture, Template, pattern, template, capture
|
|
32
33
|
from rewrite.python.template.pattern import MatchResult
|
|
33
34
|
from rewrite.java.tree import (
|
|
34
35
|
Binary as JBinary, Block, ControlParentheses, Empty, Expression, If as JIf, J,
|
|
35
|
-
Literal, MethodInvocation, Return, Ternary as JTernary, Unary,
|
|
36
|
+
Literal, MethodInvocation, Parentheses, Return, Ternary as JTernary, Unary,
|
|
36
37
|
WhileLoop as JWhileLoop,
|
|
37
38
|
)
|
|
38
39
|
from rewrite.java.support_types import JavaType, Space
|
|
@@ -80,22 +81,43 @@ _lc_not_x_template = template("not {lc_x}", lc_x=_lc_x)
|
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
# --- DeMorgan patterns/templates ---
|
|
84
|
+
class _Distributed(NamedTuple):
|
|
85
|
+
"""A De Morgan result, written alone or continuing a chain of its own operator."""
|
|
86
|
+
|
|
87
|
+
alone: Template
|
|
88
|
+
chained: Template
|
|
89
|
+
"""``{dm_z} and`` the result, which the parser nests to the left as it does ``z and a and b``."""
|
|
90
|
+
operator: JBinary.Type
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
_dm_z = capture('dm_z')
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _distributed(operator: str, left: str, right: str, captures: Dict[str, Capture]) -> _Distributed:
|
|
97
|
+
return _Distributed(
|
|
98
|
+
Template(f"{left} {operator} {right}", captures),
|
|
99
|
+
Template(f"{{dm_z}} {operator} {left} {operator} {right}", {'dm_z': _dm_z, **captures}),
|
|
100
|
+
JBinary.Type.And if operator == "and" else JBinary.Type.Or,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
83
104
|
_dm_x = capture('dm_x')
|
|
84
105
|
_dm_y = capture('dm_y')
|
|
106
|
+
_dm_xy = {'dm_x': _dm_x, 'dm_y': _dm_y}
|
|
85
107
|
_not_and_pattern = pattern("not ({dm_x} and {dm_y})", dm_x=_dm_x, dm_y=_dm_y)
|
|
86
108
|
_not_or_pattern = pattern("not ({dm_x} or {dm_y})", dm_x=_dm_x, dm_y=_dm_y)
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
_not_x_or_not_y = _distributed("or", "not {dm_x}", "not {dm_y}", _dm_xy)
|
|
110
|
+
_not_x_and_not_y = _distributed("and", "not {dm_x}", "not {dm_y}", _dm_xy)
|
|
89
111
|
# Partial-negation templates: one operand already negated by operator flip (in→not in)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
112
|
+
_not_x_or_y = _distributed("or", "not {dm_x}", "{dm_y}", _dm_xy)
|
|
113
|
+
_x_or_not_y = _distributed("or", "{dm_x}", "not {dm_y}", _dm_xy)
|
|
114
|
+
_not_x_and_y = _distributed("and", "not {dm_x}", "{dm_y}", _dm_xy)
|
|
115
|
+
_x_and_not_y = _distributed("and", "{dm_x}", "not {dm_y}", _dm_xy)
|
|
94
116
|
# Fused DeMorgan + double-negation: not (not x and not y) -> x or y
|
|
95
117
|
_not_notx_and_noty_pattern = pattern("not (not {dm_x} and not {dm_y})", dm_x=_dm_x, dm_y=_dm_y)
|
|
96
|
-
|
|
118
|
+
_x_or_y = _distributed("or", "{dm_x}", "{dm_y}", _dm_xy)
|
|
97
119
|
_not_notx_or_noty_pattern = pattern("not (not {dm_x} or not {dm_y})", dm_x=_dm_x, dm_y=_dm_y)
|
|
98
|
-
|
|
120
|
+
_x_and_y = _distributed("and", "{dm_x}", "{dm_y}", _dm_xy)
|
|
99
121
|
|
|
100
122
|
# DeMorgan with comparison operators: not (p == q and r == s) -> p != q or r != s
|
|
101
123
|
_dm_p = capture('dm_p')
|
|
@@ -105,8 +127,8 @@ _dm_s = capture('dm_s')
|
|
|
105
127
|
_not_eq_and_eq_pattern = pattern(
|
|
106
128
|
"not ({dm_p} == {dm_q} and {dm_r} == {dm_s})", dm_p=_dm_p, dm_q=_dm_q, dm_r=_dm_r, dm_s=_dm_s
|
|
107
129
|
)
|
|
108
|
-
|
|
109
|
-
"{dm_p} != {dm_q}
|
|
130
|
+
_neq_or_neq = _distributed(
|
|
131
|
+
"or", "{dm_p} != {dm_q}", "{dm_r} != {dm_s}", {'dm_p': _dm_p, 'dm_q': _dm_q, 'dm_r': _dm_r, 'dm_s': _dm_s}
|
|
110
132
|
)
|
|
111
133
|
|
|
112
134
|
def _is_bool_literal(node, expected_source: str) -> bool:
|
|
@@ -299,8 +321,11 @@ class RemoveRedundantBoolean(Recipe):
|
|
|
299
321
|
return Visitor()
|
|
300
322
|
|
|
301
323
|
|
|
302
|
-
|
|
303
|
-
|
|
324
|
+
_Distribution = Tuple[_Distributed, MatchResult]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _choose_demorgan(match, truth_value, both_not, left_not, right_not, neither_not) -> _Distribution | None:
|
|
328
|
+
"""Choose a DeMorgan template and its values, folding negations where possible.
|
|
304
329
|
|
|
305
330
|
For each operand, tries to absorb the ``not`` introduced by DeMorgan:
|
|
306
331
|
|
|
@@ -319,19 +344,58 @@ def _apply_demorgan(cursor, match, truth_value, both_not, left_not, right_not, n
|
|
|
319
344
|
y_negated = negated_without_not(dm_y_val, truth_value)
|
|
320
345
|
|
|
321
346
|
if x_negated and y_negated:
|
|
322
|
-
|
|
323
|
-
return neither_not.apply(cursor, values=values)
|
|
347
|
+
return neither_not, MatchResult({'dm_x': x_negated, 'dm_y': y_negated})
|
|
324
348
|
if x_negated:
|
|
325
|
-
|
|
326
|
-
return right_not.apply(cursor, values=values)
|
|
349
|
+
return right_not, MatchResult({'dm_x': x_negated, 'dm_y': dm_y_val})
|
|
327
350
|
if y_negated:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
351
|
+
return left_not, MatchResult({'dm_x': dm_x_val, 'dm_y': y_negated})
|
|
352
|
+
return both_not, match
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def _negated_chain(unary: Unary, operator: JBinary.Type) -> JBinary | None:
|
|
356
|
+
"""The parenthesized chain of three or more ``operator`` operands that ``unary`` negates, or None."""
|
|
357
|
+
inner = unary.expression
|
|
358
|
+
if isinstance(inner, Parentheses):
|
|
359
|
+
inner = inner.tree
|
|
360
|
+
if isinstance(inner, JBinary) and inner.operator == operator \
|
|
361
|
+
and isinstance(inner.left, JBinary) and inner.left.operator == operator:
|
|
362
|
+
return inner
|
|
363
|
+
return None
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _operands(chain: Expression, operator: JBinary.Type) -> List[Expression]:
|
|
367
|
+
"""The operands of ``chain``, a chain of ``operator`` nested to the left."""
|
|
368
|
+
operands = []
|
|
369
|
+
while isinstance(chain, JBinary) and chain.operator == operator:
|
|
370
|
+
operands.append(chain.right)
|
|
371
|
+
chain = chain.left
|
|
372
|
+
return [chain, *operands]
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _distributes(unary: Unary, written: Unary, made: AbstractSet[UUID], operator: JBinary.Type,
|
|
376
|
+
truth_value: bool) -> bool:
|
|
377
|
+
"""Whether ``unary``'s ``not`` distributes over its ``operator``: never over a chain ``written`` spells, unless
|
|
378
|
+
``made`` holds it, and over a chain a rewrite made only where each operand can be negated."""
|
|
379
|
+
spelled = _negated_chain(written, operator)
|
|
380
|
+
if spelled is not None and spelled.id not in made:
|
|
381
|
+
return False
|
|
382
|
+
chain = _negated_chain(unary, operator)
|
|
383
|
+
return chain is None or truth_value or not any(is_not(operand) for operand in _operands(chain, operator))
|
|
384
|
+
|
|
331
385
|
|
|
386
|
+
def _and_head(made: AbstractSet[UUID], unary: Unary, distributed: _Distributed) -> AbstractSet[UUID]:
|
|
387
|
+
"""``made`` and the left part of the chain ``unary`` negates, which ``distributed`` negates whole."""
|
|
388
|
+
negated = JBinary.Type.And if distributed.operator == JBinary.Type.Or else JBinary.Type.Or
|
|
389
|
+
chain = _negated_chain(unary, negated)
|
|
390
|
+
return made if chain is None else made | {chain.left.id}
|
|
332
391
|
|
|
333
|
-
|
|
334
|
-
|
|
392
|
+
|
|
393
|
+
def _distribution(unary: Unary, cursor: Cursor, written: Unary, made: AbstractSet[UUID]) -> _Distribution | None:
|
|
394
|
+
"""How ``unary``'s ``not`` distributes one level in, or None where it does not.
|
|
395
|
+
|
|
396
|
+
``written`` is ``unary`` as the code spells it, before its operands were rewritten, and ``made`` holds the ids
|
|
397
|
+
of the chains that rewrites made.
|
|
398
|
+
"""
|
|
335
399
|
truth_value = is_read_as_truth_value(cursor)
|
|
336
400
|
|
|
337
401
|
# not (p == q and r == s) -> p != q or r != s
|
|
@@ -340,46 +404,52 @@ def _distribute_not(unary: Unary, cursor: Cursor) -> J:
|
|
|
340
404
|
isinstance(operand, Expression) and is_comparison(operand)
|
|
341
405
|
for operand in (match[k] for k in ('dm_p', 'dm_q', 'dm_r', 'dm_s'))
|
|
342
406
|
):
|
|
343
|
-
if
|
|
344
|
-
return unary
|
|
345
|
-
return _neq_or_neq_template.apply(cursor, values=match) or unary
|
|
407
|
+
return (_neq_or_neq, match) if truth_value else None
|
|
346
408
|
|
|
347
409
|
if truth_value:
|
|
348
410
|
# Fused: not (not x and not y) -> x or y
|
|
349
411
|
match = _not_notx_and_noty_pattern.match(unary, cursor)
|
|
350
412
|
if match:
|
|
351
|
-
return
|
|
413
|
+
return _x_or_y, match
|
|
352
414
|
|
|
353
415
|
# Fused: not (not x or not y) -> x and y
|
|
354
416
|
match = _not_notx_or_noty_pattern.match(unary, cursor)
|
|
355
417
|
if match:
|
|
356
|
-
return
|
|
418
|
+
return _x_and_y, match
|
|
357
419
|
|
|
358
420
|
# not (x and y) -> not x or not y
|
|
359
|
-
#
|
|
360
|
-
#
|
|
421
|
+
# A chain the code spells, as not (a and b and c), is left alone, but one that rewriting an operand
|
|
422
|
+
# made distributes: not (a or not (b and c)) has become not (a or not b or not c) by now.
|
|
361
423
|
match = _not_and_pattern.match(unary, cursor)
|
|
362
|
-
if match:
|
|
363
|
-
|
|
364
|
-
if not (isinstance(dm_x_val, JBinary) and dm_x_val.operator == JBinary.Type.And):
|
|
365
|
-
return _apply_demorgan(
|
|
366
|
-
cursor, match, truth_value, _not_x_or_not_y_template,
|
|
367
|
-
_not_x_or_y_template, _x_or_not_y_template,
|
|
368
|
-
_x_or_y_template,
|
|
369
|
-
) or unary
|
|
424
|
+
if match and _distributes(unary, written, made, JBinary.Type.And, truth_value):
|
|
425
|
+
return _choose_demorgan(match, truth_value, _not_x_or_not_y, _not_x_or_y, _x_or_not_y, _x_or_y)
|
|
370
426
|
|
|
371
427
|
# not (x or y) -> not x and not y
|
|
372
428
|
match = _not_or_pattern.match(unary, cursor)
|
|
373
|
-
if match:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
429
|
+
if match and _distributes(unary, written, made, JBinary.Type.Or, truth_value):
|
|
430
|
+
return _choose_demorgan(match, truth_value, _not_x_and_not_y, _not_x_and_y, _x_and_not_y, _x_and_y)
|
|
431
|
+
|
|
432
|
+
return None
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def _ends_chain(cursor: Cursor, operator: JBinary.Type) -> bool:
|
|
436
|
+
"""Whether the value at ``cursor`` is the right operand of an ``and``/``or`` of ``operator``."""
|
|
437
|
+
node, parent = cursor.value, cursor.parent_tree_cursor().value
|
|
438
|
+
return isinstance(node, J) and isinstance(parent, JBinary) and parent.operator == operator \
|
|
439
|
+
and parent.right.id == node.id
|
|
440
|
+
|
|
381
441
|
|
|
382
|
-
|
|
442
|
+
def _respaced(chain: J, binary: JBinary) -> J:
|
|
443
|
+
"""``chain``, written by a template in place of ``binary``, with the spaces and comments ``binary`` had
|
|
444
|
+
around its left operand and its operator."""
|
|
445
|
+
head = chain.left if isinstance(chain, JBinary) else None
|
|
446
|
+
if not isinstance(head, JBinary):
|
|
447
|
+
return chain
|
|
448
|
+
return chain.replace(_left=head.replace(
|
|
449
|
+
_left=head.left.replace(_prefix=binary.left.prefix),
|
|
450
|
+
_operator=binary.padding.operator,
|
|
451
|
+
_right=head.right.replace(_prefix=binary.right.prefix),
|
|
452
|
+
))
|
|
383
453
|
|
|
384
454
|
|
|
385
455
|
@categorize(_Cleanup)
|
|
@@ -423,6 +493,11 @@ class DeMorgan(Recipe):
|
|
|
423
493
|
|
|
424
494
|
def editor(self) -> TreeVisitor[Any, ExecutionContext]:
|
|
425
495
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
496
|
+
def __init__(self, made: AbstractSet[UUID] = frozenset()):
|
|
497
|
+
super().__init__()
|
|
498
|
+
# The ids of chains that rewrites made, which this visit meets again in what a template wrote.
|
|
499
|
+
self._made = made
|
|
500
|
+
|
|
426
501
|
def visit_unary(
|
|
427
502
|
self, unary: Unary, p: ExecutionContext
|
|
428
503
|
) -> J:
|
|
@@ -431,11 +506,38 @@ class DeMorgan(Recipe):
|
|
|
431
506
|
visited.replace(_prefix=Space.EMPTY)
|
|
432
507
|
):
|
|
433
508
|
return visited
|
|
434
|
-
|
|
435
|
-
|
|
509
|
+
cursor = Cursor(self.cursor.parent, visited)
|
|
510
|
+
distribution = _distribution(visited, cursor, unary, self._made)
|
|
511
|
+
# The `and` holding `x and not (a or b)` rewrites it whole, in visit_binary.
|
|
512
|
+
if distribution is None or _ends_chain(cursor, distribution[0].operator):
|
|
513
|
+
return visited
|
|
514
|
+
distributed, values = distribution
|
|
515
|
+
result = distributed.alone.apply(cursor, values=values)
|
|
516
|
+
if result is None:
|
|
436
517
|
return visited
|
|
437
518
|
# The negations a rewrite creates may distribute in turn.
|
|
438
|
-
|
|
519
|
+
made = _and_head(self._made, visited, distributed)
|
|
520
|
+
return Visitor(made).visit(result, p, self.cursor.parent_or_throw) or result
|
|
521
|
+
|
|
522
|
+
def visit_binary(self, binary: JBinary, p: ExecutionContext) -> J:
|
|
523
|
+
visited = super().visit_binary(binary, p)
|
|
524
|
+
if not isinstance(visited, JBinary) or not isinstance(visited.right, Unary) or has_comments(
|
|
525
|
+
visited.right.replace(_prefix=Space.EMPTY)
|
|
526
|
+
):
|
|
527
|
+
return visited
|
|
528
|
+
cursor = Cursor(self.cursor.parent, visited)
|
|
529
|
+
# Read under the unvisited `and`/`or`, as visit_unary read it before leaving it here.
|
|
530
|
+
written = binary.right if isinstance(binary.right, Unary) else visited.right
|
|
531
|
+
distribution = _distribution(visited.right, Cursor(self.cursor, visited.right), written, self._made)
|
|
532
|
+
if distribution is None or distribution[0].operator != visited.operator:
|
|
533
|
+
return visited
|
|
534
|
+
distributed, values = distribution
|
|
535
|
+
result = distributed.chained.apply(cursor, values={**values.as_dict(), 'dm_z': visited.left})
|
|
536
|
+
if result is None:
|
|
537
|
+
return visited
|
|
538
|
+
result = _respaced(result, visited)
|
|
539
|
+
made = _and_head(self._made, visited.right, distributed)
|
|
540
|
+
return Visitor(made).visit(result, p, self.cursor.parent_or_throw) or result
|
|
439
541
|
|
|
440
542
|
return Visitor()
|
|
441
543
|
|
|
@@ -9,6 +9,7 @@ Both fire only where the receiver's pandas type resolves.
|
|
|
9
9
|
See: https://pandas.pydata.org/docs/whatsnew/v1.4.0.html#deprecations
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
import dataclasses
|
|
12
13
|
from typing import Any, List
|
|
13
14
|
|
|
14
15
|
from rewrite import ExecutionContext, Preconditions, Recipe, TreeVisitor
|
|
@@ -103,12 +104,19 @@ class DataframeAppendToConcat(Recipe):
|
|
|
103
104
|
):
|
|
104
105
|
return m
|
|
105
106
|
try:
|
|
106
|
-
|
|
107
|
+
concat = _concat_template.apply(
|
|
107
108
|
self.cursor, visitor=self, values={'df': select, 'x': args[0]}
|
|
108
|
-
)
|
|
109
|
+
)
|
|
109
110
|
except ValueError:
|
|
110
111
|
# The template refuses a file where `pd` is bound to something else.
|
|
111
112
|
return m
|
|
113
|
+
if not isinstance(concat, MethodInvocation):
|
|
114
|
+
return concat or m
|
|
115
|
+
# Typed as the receiver, so that an `append` on this result or taking it is migrated too.
|
|
116
|
+
method_type = concat.method_type
|
|
117
|
+
if method_type is None:
|
|
118
|
+
return concat
|
|
119
|
+
return concat.replace(_method_type=dataclasses.replace(method_type, _return_type=select.type))
|
|
112
120
|
|
|
113
121
|
# `append` does not resolve under pandas 2, so the gate names the receiver.
|
|
114
122
|
return Preconditions.check(uses_type(_DATAFRAME), Visitor())
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Grouping an ``and`` or ``or`` chain the way the parser groups it."""
|
|
2
|
+
|
|
3
|
+
from rewrite.java.support_types import Space
|
|
4
|
+
from rewrite.java.tree import Binary
|
|
5
|
+
from rewrite.utils import random_id
|
|
6
|
+
|
|
7
|
+
from openrewrite_code_quality_python.codequality._trivia import concat
|
|
8
|
+
|
|
9
|
+
_CHAINED = (Binary.Type.And, Binary.Type.Or)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def grouped_left(binary: Binary) -> Binary:
|
|
13
|
+
"""``binary`` with a chain of its own operator on its right, unparenthesized, regrouped to the left.
|
|
14
|
+
|
|
15
|
+
``a and (b and c)`` without the parentheses prints as ``a and b and c``, which the parser
|
|
16
|
+
groups as ``(a and b) and c``. Both evaluate the same, but a recipe matching the tree sees
|
|
17
|
+
other operands in each.
|
|
18
|
+
"""
|
|
19
|
+
right = binary.right
|
|
20
|
+
if binary.operator not in _CHAINED or not isinstance(right, Binary) or right.operator != binary.operator:
|
|
21
|
+
return binary
|
|
22
|
+
left = binary.replace(
|
|
23
|
+
_id=random_id(),
|
|
24
|
+
_prefix=Space.EMPTY,
|
|
25
|
+
_right=right.left.replace(_prefix=concat(right.prefix, right.left.prefix)),
|
|
26
|
+
)
|
|
27
|
+
return grouped_left(binary.replace(
|
|
28
|
+
_left=grouped_left(left),
|
|
29
|
+
_operator=right.padding.operator,
|
|
30
|
+
_right=right.right,
|
|
31
|
+
))
|
|
@@ -28,6 +28,7 @@ from rewrite.java.tree import (
|
|
|
28
28
|
)
|
|
29
29
|
from rewrite.utils import random_id
|
|
30
30
|
|
|
31
|
+
from openrewrite_code_quality_python.codequality._grouping import grouped_left
|
|
31
32
|
from openrewrite_code_quality_python.codequality._reindent import (
|
|
32
33
|
line_indent,
|
|
33
34
|
reindent,
|
|
@@ -190,8 +191,12 @@ class CollapsibleIfStatements(Recipe):
|
|
|
190
191
|
),
|
|
191
192
|
_type=None,
|
|
192
193
|
)
|
|
193
|
-
|
|
194
|
+
# An `and` chain in the inner condition continues the merged one, nested to the left
|
|
195
|
+
# as the parser nests `a and b and c`, so only its last operand is left to parenthesize.
|
|
196
|
+
combined = grouped_left(combined.replace(
|
|
194
197
|
_left=maybe_parenthesize(combined, combined.left.id, combined.left),
|
|
198
|
+
))
|
|
199
|
+
combined = combined.replace(
|
|
195
200
|
_right=maybe_parenthesize(combined, combined.right.id, combined.right),
|
|
196
201
|
)
|
|
197
202
|
|
|
@@ -27,6 +27,7 @@ from rewrite.java.tree import (
|
|
|
27
27
|
If,
|
|
28
28
|
Statement,
|
|
29
29
|
)
|
|
30
|
+
from openrewrite_code_quality_python.codequality._grouping import grouped_left
|
|
30
31
|
from openrewrite_code_quality_python.codequality._semantically_equal import SemanticallyEqual
|
|
31
32
|
from rewrite.utils import random_id
|
|
32
33
|
|
|
@@ -180,8 +181,12 @@ def _merge_conditions(first: If, second: If) -> If:
|
|
|
180
181
|
_right=_or_operand(second_cond, Space.SINGLE_SPACE),
|
|
181
182
|
_type=None,
|
|
182
183
|
)
|
|
183
|
-
|
|
184
|
+
# An `or` chain in the second condition continues the merged one, nested to the left as the
|
|
185
|
+
# parser nests `a or b or c`, so only its last operand is left to parenthesize.
|
|
186
|
+
combined = grouped_left(combined.replace(
|
|
184
187
|
_left=maybe_parenthesize(combined, combined.left.id, combined.left),
|
|
188
|
+
))
|
|
189
|
+
combined = combined.replace(
|
|
185
190
|
_right=maybe_parenthesize(combined, combined.right.id, combined.right),
|
|
186
191
|
)
|
|
187
192
|
|
|
@@ -19,16 +19,22 @@ def rename_methods(
|
|
|
19
19
|
method gives it. The patterns match overrides, since a call through a subclass to a member
|
|
20
20
|
typeshed no longer declares resolves to the subclass.
|
|
21
21
|
"""
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
# Where Python still declares `old` as an alias, `old = new`, the call resolves to `new`.
|
|
23
|
+
patterns = {
|
|
24
|
+
old: [f"{declaring_type} {old}(..)", f"{declaring_type} {new}(..)"] for old, new in renames.items()
|
|
25
|
+
}
|
|
26
|
+
calls = {
|
|
27
|
+
old: [MethodMatcher.create(pattern, match_overrides=True) for pattern in alternatives]
|
|
28
|
+
for old, alternatives in patterns.items()
|
|
29
|
+
}
|
|
24
30
|
|
|
25
31
|
class Visitor(PythonVisitor[ExecutionContext]):
|
|
26
32
|
def visit_method_invocation(self, method: MethodInvocation, p: ExecutionContext) -> J:
|
|
27
33
|
m = super().visit_method_invocation(method, p)
|
|
28
34
|
if not isinstance(m, MethodInvocation):
|
|
29
35
|
return m
|
|
30
|
-
|
|
31
|
-
if
|
|
36
|
+
matchers = calls.get(m.name.simple_name, [])
|
|
37
|
+
if not any(matcher.matches(m, cursor=self.cursor) for matcher in matchers):
|
|
32
38
|
return m
|
|
33
39
|
# The old method type names the old method, which no longer describes the call.
|
|
34
40
|
m = m.replace(
|
|
@@ -41,7 +47,9 @@ def rename_methods(
|
|
|
41
47
|
]
|
|
42
48
|
return m.padding.replace(_arguments=arguments.padding.replace(elements=elements))
|
|
43
49
|
|
|
44
|
-
gates = [
|
|
50
|
+
gates = [
|
|
51
|
+
uses_method(pattern, match_overrides=True) for alternatives in patterns.values() for pattern in alternatives
|
|
52
|
+
]
|
|
45
53
|
return Preconditions.check(gates[0] if len(gates) == 1 else Preconditions.or_(*gates), Visitor())
|
|
46
54
|
|
|
47
55
|
|
|
@@ -55,7 +55,7 @@ from rewrite.java.tree import (
|
|
|
55
55
|
Annotation, Assignment, Binary, ClassDeclaration, Empty, Expression, FieldAccess,
|
|
56
56
|
Identifier, Import, J,
|
|
57
57
|
JLeftPadded, JRightPadded, Literal, Lambda, MethodDeclaration, MethodInvocation, Modifier,
|
|
58
|
-
Space, Statement,
|
|
58
|
+
Parentheses, Space, Statement,
|
|
59
59
|
)
|
|
60
60
|
from rewrite.rpc import RpcRecipe
|
|
61
61
|
|
|
@@ -485,18 +485,18 @@ class _Settings:
|
|
|
485
485
|
dict in another module, so nothing can be read off the decorator alone.
|
|
486
486
|
"""
|
|
487
487
|
|
|
488
|
-
# module name -> dict name ->
|
|
489
|
-
dicts: Dict[str, Dict[str,
|
|
488
|
+
# module name -> dict name -> the dict literal's text, which a lookup parses again.
|
|
489
|
+
dicts: Dict[str, Dict[str, str]] = field(default_factory=dict)
|
|
490
490
|
# Names assigned more than once, or mutated, whose contents nothing can rely on.
|
|
491
491
|
unsafe: Set[Tuple[str, str]] = field(default_factory=set)
|
|
492
492
|
|
|
493
|
-
def record(self, module: str, name: str,
|
|
493
|
+
def record(self, module: str, name: str, source: str | None) -> None:
|
|
494
494
|
known = self.dicts.setdefault(module, {})
|
|
495
|
-
if name in known or
|
|
495
|
+
if name in known or source is None:
|
|
496
496
|
# A second binding of the name, or one whose value is not a plain literal dict.
|
|
497
497
|
self.unsafe.add((module, name))
|
|
498
498
|
return
|
|
499
|
-
known[name] =
|
|
499
|
+
known[name] = source
|
|
500
500
|
|
|
501
501
|
def mutated(self, module: str, name: str) -> None:
|
|
502
502
|
self.unsafe.add((module, name))
|
|
@@ -504,7 +504,9 @@ class _Settings:
|
|
|
504
504
|
def lookup(self, module: str, name: str) -> Dict[str, Expression] | None:
|
|
505
505
|
if (module, name) in self.unsafe:
|
|
506
506
|
return None
|
|
507
|
-
|
|
507
|
+
source = self.dicts.get(module, {}).get(name)
|
|
508
|
+
parsed = _parsed_expression(source) if source is not None else None
|
|
509
|
+
return _dict_entries(parsed) if parsed is not None else None
|
|
508
510
|
|
|
509
511
|
|
|
510
512
|
def _module_name(path: PurePosixPath) -> str:
|
|
@@ -543,15 +545,13 @@ class _Compat:
|
|
|
543
545
|
package_dirs: Set[str] = field(default_factory=set)
|
|
544
546
|
# Files already holding a compat module, so a second run does not write it again.
|
|
545
547
|
existing: Set[str] = field(default_factory=set)
|
|
546
|
-
# The helpers each file's converted decorators call,
|
|
547
|
-
#
|
|
548
|
+
# The helpers each file's converted decorators call, read off each decorator in the scan
|
|
549
|
+
# except one splatting a dict, which `generate` reads once every module's dicts are in.
|
|
548
550
|
needed: Dict[str, Set[str]] = field(default_factory=dict)
|
|
549
|
-
#
|
|
550
|
-
|
|
551
|
+
# Those splatting decorators, each as the member invoked, the text of its call, what the
|
|
552
|
+
# file's imports bound and whether it decorates an `async def`.
|
|
553
|
+
splatted: Dict[str, List[Tuple[str | None, str, "_Bindings", bool]]] = field(
|
|
551
554
|
default_factory=dict)
|
|
552
|
-
# The same for the ones written as a call rather than with an `@`, with the member invoked.
|
|
553
|
-
pending_calls: Dict[str, List[Tuple[str, MethodInvocation, "_Bindings", bool]]] \
|
|
554
|
-
= field(default_factory=dict)
|
|
555
555
|
settings: _Settings = field(default_factory=_Settings)
|
|
556
556
|
# Filled in by `generate`, read by the editor: the module a file imports its helpers from.
|
|
557
557
|
module_of: Dict[str, str] = field(default_factory=dict)
|
|
@@ -584,6 +584,32 @@ def _parsed_module(source: str, path: PurePosixPath) -> SourceFile:
|
|
|
584
584
|
return cu.replace(_source_path=Path(str(path)))
|
|
585
585
|
|
|
586
586
|
|
|
587
|
+
def _source_of(tree: J, cursor: Cursor) -> str:
|
|
588
|
+
"""``tree``'s text without its prefix or the markup of its markers, which an accumulator keeps
|
|
589
|
+
in place of the tree."""
|
|
590
|
+
return tree.replace(prefix=Space.EMPTY).print(cursor, PrintOutputCapture(
|
|
591
|
+
0, PrintOutputCapture.MarkerPrinter.SANITIZED)) # ty: ignore[too-many-positional-arguments]
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def _call_source(call: Annotation | MethodInvocation, cursor: Cursor) -> str:
|
|
595
|
+
"""The text of the call a decorator makes, ``@`` or not, which parses as a call either way."""
|
|
596
|
+
source = _source_of(call, cursor)
|
|
597
|
+
return source.removeprefix("@").lstrip() if isinstance(call, Annotation) else source
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _parsed_expression(source: str) -> Expression | None:
|
|
601
|
+
"""The expression ``source`` spells, parsed on its own in parentheses, which let it span lines
|
|
602
|
+
as the brackets around it in its file did."""
|
|
603
|
+
from rewrite.python._parser_visitor import ParserVisitor
|
|
604
|
+
|
|
605
|
+
text = f"({source})"
|
|
606
|
+
statements = ParserVisitor(text, None, None).visit_Module(ast.parse(text)).statements
|
|
607
|
+
statement = statements[0] if len(statements) == 1 else None
|
|
608
|
+
expression = statement.expression if isinstance(statement, ExpressionStatement) else statement
|
|
609
|
+
inner = expression.tree if isinstance(expression, Parentheses) else None
|
|
610
|
+
return inner if isinstance(inner, Expression) else None
|
|
611
|
+
|
|
612
|
+
|
|
587
613
|
_COMPAT_DOCSTRING = """The parts of backoff that tenacity has no equivalent for.
|
|
588
614
|
|
|
589
615
|
Written by org.openrewrite.python.migrate.MigrateBackoffToTenacity.
|
|
@@ -819,6 +845,11 @@ def _split_arguments(
|
|
|
819
845
|
return positional, named
|
|
820
846
|
|
|
821
847
|
|
|
848
|
+
def _splats(call: Annotation | MethodInvocation) -> bool:
|
|
849
|
+
"""Whether ``call`` has a ``**`` argument, which only the run's settings dicts can read."""
|
|
850
|
+
return any(isinstance(a, Star) and a.kind == Star.Kind.DICT for a in call.arguments or [])
|
|
851
|
+
|
|
852
|
+
|
|
822
853
|
def _splat_entries(
|
|
823
854
|
expression: Expression, bindings: "_Bindings", settings: _Settings,
|
|
824
855
|
) -> Dict[str, Expression] | None:
|
|
@@ -1300,6 +1331,7 @@ class _VendorMembers(PythonVisitor[None]):
|
|
|
1300
1331
|
|
|
1301
1332
|
def _collect_settings(cu: CompilationUnit, settings: _Settings, module: str) -> None:
|
|
1302
1333
|
"""Record this module's top-level dict literals, and what it rules out relying on."""
|
|
1334
|
+
cursor = Cursor(Cursor(None, Cursor.ROOT_VALUE), cu)
|
|
1303
1335
|
for statement in cu.statements:
|
|
1304
1336
|
expression = statement.expression if isinstance(statement, ExpressionStatement) \
|
|
1305
1337
|
else statement
|
|
@@ -1312,7 +1344,9 @@ def _collect_settings(cu: CompilationUnit, settings: _Settings, module: str) ->
|
|
|
1312
1344
|
# Only a module-level constant is worth reading: anything else is far likelier
|
|
1313
1345
|
# to be built up or rebound somewhere this never looks.
|
|
1314
1346
|
continue
|
|
1315
|
-
|
|
1347
|
+
value = expression.assignment
|
|
1348
|
+
settings.record(module, name,
|
|
1349
|
+
_source_of(value, cursor) if _dict_entries(value) is not None else None)
|
|
1316
1350
|
|
|
1317
1351
|
# A mutation anywhere in the file, or a rebinding below the top level, means the dict
|
|
1318
1352
|
# a decorator splats is not the dict seen here.
|
|
@@ -1670,11 +1704,12 @@ class ReplaceBackoffDecoratorsWithTenacity(ScanningRecipe[_Compat]):
|
|
|
1670
1704
|
visited = super().visit_annotation(annotation, p)
|
|
1671
1705
|
if not isinstance(visited, Annotation):
|
|
1672
1706
|
return visited
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1707
|
+
outcome = self.outcome(visited)
|
|
1708
|
+
if outcome is not None and _splats(visited):
|
|
1709
|
+
self._defer(self._bindings.resolve(visited.annotation_type), visited,
|
|
1710
|
+
_decorates_async(self.cursor))
|
|
1711
|
+
else:
|
|
1712
|
+
ReplaceBackoffDecoratorsWithTenacity._record_helpers(acc, self._path, outcome)
|
|
1678
1713
|
return visited
|
|
1679
1714
|
|
|
1680
1715
|
# A member used as a value or a type is rewritten without any analysis, but the
|
|
@@ -1692,28 +1727,34 @@ class ReplaceBackoffDecoratorsWithTenacity(ScanningRecipe[_Compat]):
|
|
|
1692
1727
|
self, method: MethodInvocation, p: ExecutionContext,
|
|
1693
1728
|
) -> J:
|
|
1694
1729
|
self._record(self.vendored_call(method))
|
|
1695
|
-
|
|
1696
|
-
if
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1730
|
+
outcome = self.call_outcome(method)
|
|
1731
|
+
if outcome is not None and _splats(method):
|
|
1732
|
+
self._defer(_invoked_member(method, self._bindings), method, self._wraps_async())
|
|
1733
|
+
else:
|
|
1734
|
+
ReplaceBackoffDecoratorsWithTenacity._record_helpers(acc, self._path, outcome)
|
|
1700
1735
|
return super().visit_method_invocation(method, p)
|
|
1701
1736
|
|
|
1702
1737
|
def _record(self, found: Tuple[str, str, bool] | None) -> None:
|
|
1703
1738
|
if found is not None:
|
|
1704
1739
|
acc.needed.setdefault(self._path, set()).add(found[0])
|
|
1705
1740
|
|
|
1741
|
+
def _defer(
|
|
1742
|
+
self, decorator: str | None, call: Annotation | MethodInvocation, is_async: bool,
|
|
1743
|
+
) -> None:
|
|
1744
|
+
# The dict a `**` names may be in a module the scan has not reached yet, so the
|
|
1745
|
+
# decorator is read in `generate`, from its text.
|
|
1746
|
+
acc.splatted.setdefault(self._path, []).append(
|
|
1747
|
+
(decorator, _call_source(call, self.cursor), self._bindings, is_async))
|
|
1748
|
+
|
|
1706
1749
|
return Scanner()
|
|
1707
1750
|
|
|
1708
1751
|
def generate(self, acc: _Compat, ctx: ExecutionContext) -> List[SourceFile]:
|
|
1709
|
-
for path,
|
|
1710
|
-
for
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
self._record_helpers(acc, path, _analyze_call(
|
|
1716
|
-
decorator, call, bindings, is_async, acc.settings))
|
|
1752
|
+
for path, calls in acc.splatted.items():
|
|
1753
|
+
for decorator, source, bindings, is_async in calls:
|
|
1754
|
+
call = _parsed_expression(source)
|
|
1755
|
+
if isinstance(call, MethodInvocation):
|
|
1756
|
+
self._record_helpers(acc, path, _analyze_call(
|
|
1757
|
+
decorator, call, bindings, is_async, acc.settings))
|
|
1717
1758
|
|
|
1718
1759
|
helpers: Dict[PurePosixPath, Set[str]] = {}
|
|
1719
1760
|
for path, needed in sorted(acc.needed.items()):
|
{openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrewrite-migrate-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.0
|
|
4
4
|
Summary: OpenRewrite recipes for migrating Python codebases to newer Python versions.
|
|
5
5
|
Author-email: "Moderne Inc." <support@moderne.io>
|
|
6
6
|
License: Moderne Proprietary
|
|
@@ -19,7 +19,7 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Provides-Extra: dev
|
|
22
|
-
Requires-Dist: openrewrite>=8.
|
|
22
|
+
Requires-Dist: openrewrite>=8.96.3; extra == "dev"
|
|
23
23
|
Requires-Dist: pydantic>=2.11; extra == "dev"
|
|
24
24
|
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
25
25
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
{openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/RECORD
RENAMED
|
@@ -4,7 +4,7 @@ openrewrite_code_quality_python/cleanup/_literals.py,sha256=bv-dN0v-FOLBIHEqxNHw
|
|
|
4
4
|
openrewrite_code_quality_python/cleanup/_negation.py,sha256=dMIzjStSrDKK80txkClEklWBMr5a1-boT5WjWRwtQp4,1704
|
|
5
5
|
openrewrite_code_quality_python/cleanup/assign_return.py,sha256=y10YG2MkcZoE0Ded_0UPBLt-mg7omkRS4D3AP67v06I,5352
|
|
6
6
|
openrewrite_code_quality_python/cleanup/attr_access.py,sha256=V3gRlZkXac34q4t2aAKIRc8xRzl3t8GAqPpY69883cg,8491
|
|
7
|
-
openrewrite_code_quality_python/cleanup/boolean_simplification.py,sha256=
|
|
7
|
+
openrewrite_code_quality_python/cleanup/boolean_simplification.py,sha256=8CpM8CPUDvy2BfilgHL7BkeJ7bbhvoDR6m6EpeWNoF4,39167
|
|
8
8
|
openrewrite_code_quality_python/cleanup/collection_literals.py,sha256=uFFIOZ_PFqRXkyesrXf368xVGKuGb7hK94Zf24rH3wo,15866
|
|
9
9
|
openrewrite_code_quality_python/cleanup/comparison_simplification.py,sha256=lhXIy_UMWeDRGyBZJnLFzN0em_ij6cRuWFgm6dS050A,13114
|
|
10
10
|
openrewrite_code_quality_python/cleanup/complex_transforms.py,sha256=oEKdO9bzvvJipFzfVDANtGqnYoFSs7J_vuZd5TZjUHo,10690
|
|
@@ -22,7 +22,7 @@ openrewrite_code_quality_python/cleanup/naming_conventions.py,sha256=qqZuF5b8lVr
|
|
|
22
22
|
openrewrite_code_quality_python/cleanup/negated_operators.py,sha256=DOXtB249IlPJovRNpCJPYHcv79u8bnNlwesqWRfJePg,5097
|
|
23
23
|
openrewrite_code_quality_python/cleanup/none_and_identity.py,sha256=QVdFo9MdsF1RHGWLmR4179qAeYCEn99a-OJ8rEn9PBM,12092
|
|
24
24
|
openrewrite_code_quality_python/cleanup/object_inheritance.py,sha256=7hy16CBEUNTRoXYuWqe0_ZZS5Ig4Yft6VTErkWpIfkc,3099
|
|
25
|
-
openrewrite_code_quality_python/cleanup/pandas_cleanup.py,sha256
|
|
25
|
+
openrewrite_code_quality_python/cleanup/pandas_cleanup.py,sha256=2eq7R5Cux9RTFCChaclSr0KIfAdn_SmW1wwB0Bmqj7g,8536
|
|
26
26
|
openrewrite_code_quality_python/cleanup/python_best_practices.py,sha256=AkJa4Wr02cqKhlAsfr7yDKOUpSHkw8ljuj6uxLrF9-A,9309
|
|
27
27
|
openrewrite_code_quality_python/cleanup/range_cleanup.py,sha256=9KFNSiiSc2pl_S6KdpUW_Hhtw9zweOAT393Zth72ids,5287
|
|
28
28
|
openrewrite_code_quality_python/cleanup/redundant_code.py,sha256=aMPAAOO5uhBZy1j5o2LvX3lKD6Q3KuDKx1G0b_Mv5uk,6812
|
|
@@ -33,6 +33,7 @@ openrewrite_code_quality_python/cleanup/use_items.py,sha256=_ifgqhSga2uXhOBYcGgi
|
|
|
33
33
|
openrewrite_code_quality_python/cleanup/use_list_clear.py,sha256=XXfAbF6suxfvby4QbqWtiCtw6yMIH__6mSleNomoiXQ,4294
|
|
34
34
|
openrewrite_code_quality_python/cleanup/yoda_conditions.py,sha256=hk3nNoUPSJK4NoX18F5wggvmHbrVjHUNUL9WMqqJR8Y,4551
|
|
35
35
|
openrewrite_code_quality_python/codequality/__init__.py,sha256=ctQ0xAuLLSuLZoVajxaW2zn0RPm3VD-iCrLGx0XFZZY,1124
|
|
36
|
+
openrewrite_code_quality_python/codequality/_grouping.py,sha256=9WPdgX_9RELDoz4wRw0uO-yB6cO2snb6RTlBermYWUQ,1165
|
|
36
37
|
openrewrite_code_quality_python/codequality/_reindent.py,sha256=ZeD2DOqQtYAKc0v21JtBg_jqxKpg39ur9kcVV8gWtt4,3514
|
|
37
38
|
openrewrite_code_quality_python/codequality/_semantically_equal.py,sha256=tdkYMvtxoOQuoqY1Jx1we5NiCCPFV_5oKtEDpSJejkI,1649
|
|
38
39
|
openrewrite_code_quality_python/codequality/_side_effects.py,sha256=p-xvunfN8bUGXpTmt1DHOA1P2moPSkR1aVgqg52hUEc,1227
|
|
@@ -41,9 +42,9 @@ openrewrite_code_quality_python/codequality/_truth_value.py,sha256=T52Aznr8ZUnL7
|
|
|
41
42
|
openrewrite_code_quality_python/codequality/all_branches_identical.py,sha256=Qx4oBBE_MJvCpZVIlmMmP_9USL8SLUznnFheqEoZdbY,7943
|
|
42
43
|
openrewrite_code_quality_python/codequality/boolean_checks_not_inverted.py,sha256=cGqCcP1e5FChahREpJ6YBHhwEVwlQDWWE9ukOzaKTi4,6582
|
|
43
44
|
openrewrite_code_quality_python/codequality/code_quality.py,sha256=qO_et2buzzQi2yVZtA2ncIaZ129PuaruPOWyB3GgwMQ,2284
|
|
44
|
-
openrewrite_code_quality_python/codequality/collapsible_if_statements.py,sha256=
|
|
45
|
+
openrewrite_code_quality_python/codequality/collapsible_if_statements.py,sha256=Dc2U5jxxwb38Er7GbVMKd8iJ__eT1K3Agu2-F7q4wvM,9559
|
|
45
46
|
openrewrite_code_quality_python/codequality/find_suspicious_chained_comparison.py,sha256=oe3YEtM6FgiHl9nX2jq0rmZzQ_M7BcOaD13PHlSCGM8,3720
|
|
46
|
-
openrewrite_code_quality_python/codequality/merge_identical_branches.py,sha256=
|
|
47
|
+
openrewrite_code_quality_python/codequality/merge_identical_branches.py,sha256=fIVc1EHEo6tCIfF4YyU_CdBOaE-QMyhj_OfUd0vHM1I,7159
|
|
47
48
|
openrewrite_code_quality_python/codequality/remove_duplicate_conditions.py,sha256=DgoD3w3zqEIrcHukYEsXOlp5ksG5EhVxa1zW0LeczdM,6588
|
|
48
49
|
openrewrite_code_quality_python/codequality/remove_self_assignment.py,sha256=8jyrmh5P9kXwaR9f8Nb0ZapwrksWSXXlhMpMQY_kabE,7519
|
|
49
50
|
openrewrite_code_quality_python/codequality/remove_unconditional_value_overwrite.py,sha256=t9AUeuJr--iQnfKOv65ewxTFXPQxPsjA7YOcV7cAGRo,11982
|
|
@@ -55,7 +56,7 @@ openrewrite_migrate_python/migrate/_decorators.py,sha256=03DZoAa3LQ9fXZx3FFm_RSm
|
|
|
55
56
|
openrewrite_migrate_python/migrate/_finders.py,sha256=RVmkMUOWLxxCWx4Ig8SUEbqPTVGqrAP2VVhiazBsCyM,4960
|
|
56
57
|
openrewrite_migrate_python/migrate/_langchain_tables.py,sha256=TF2A2iwkULa5uybo1rBxp_o2weziCxAmn5NLUcSAXqs,55153
|
|
57
58
|
openrewrite_migrate_python/migrate/_markers.py,sha256=EggMUANYXPh0Q6GbKlhWpthC0ac5VtgEbyHVLGyeH4Y,1126
|
|
58
|
-
openrewrite_migrate_python/migrate/_method_renames.py,sha256
|
|
59
|
+
openrewrite_migrate_python/migrate/_method_renames.py,sha256=-6xpdU5u8xkvAO65TjVnPXblqYi8jA-Pl2_Mm6f6480,2869
|
|
59
60
|
openrewrite_migrate_python/migrate/_module_imports.py,sha256=kSuccPhiCcrVYhGelQOWJ2UZkN2YHNDiMP-QDK5En98,2844
|
|
60
61
|
openrewrite_migrate_python/migrate/_slots.py,sha256=WYiAMYa7_tZE3vI_r55kMBojHXdxE58CzvhyAOJSZ48,634
|
|
61
62
|
openrewrite_migrate_python/migrate/aifc_migrations.py,sha256=FxEUdhNFGm-7oNTrvk76eDy6wTjA9uRSjdOrwQ5oT-I,8900
|
|
@@ -64,7 +65,7 @@ openrewrite_migrate_python/migrate/ast_deprecations.py,sha256=fudnnAwQfBU5YCOe37
|
|
|
64
65
|
openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py,sha256=l4HOVWAaVY_7Deunjr8cGLWQ07CLyZqclFjiPDJSz_I,9471
|
|
65
66
|
openrewrite_migrate_python/migrate/asyncio_deprecations.py,sha256=6lliBj-lvRthCs1CuN4A-Z5iQlkQQ9C6vKxi59ErCwg,11327
|
|
66
67
|
openrewrite_migrate_python/migrate/avro_migrations.py,sha256=6wb2_zFYTXEiK_DZ_Jl3awsMMG6GpZ6PJWZuer0DqG4,2331
|
|
67
|
-
openrewrite_migrate_python/migrate/backoff_to_tenacity.py,sha256=
|
|
68
|
+
openrewrite_migrate_python/migrate/backoff_to_tenacity.py,sha256=Ob6s32hwxL5XU4_yidyX0PlOp_z-mJWUAdBwIeos4uA,94033
|
|
68
69
|
openrewrite_migrate_python/migrate/builtins_deprecations.py,sha256=cWFmqa2GPUTZgk6O_sDDDMVfT1PfBCU615FkTQmtAQw,5625
|
|
69
70
|
openrewrite_migrate_python/migrate/calendar_deprecations.py,sha256=EPDRqN7Uf6U41CjfZu6BiJDZ4ehaNlBgzZD4YxGFzWs,2417
|
|
70
71
|
openrewrite_migrate_python/migrate/cgi_escape_deprecations.py,sha256=Alevj5BrJ59wOd2dQRlorKypLZSS4b1FG9YV8FteCR4,8595
|
|
@@ -144,8 +145,8 @@ openrewrite_migrate_python/migrate/wave_deprecations.py,sha256=QcjcFNpb2mQ4v8VdY
|
|
|
144
145
|
openrewrite_migrate_python/migrate/webbrowser_deprecations.py,sha256=2ENvVuz5fcKAxnoMQVWIC62ipFeRy2_Ah_WEUushASM,2205
|
|
145
146
|
openrewrite_migrate_python/migrate/xdrlib_migrations.py,sha256=khHPyP1TctExRMl1w_sR9d2fh5_QD04WQDNCqN3wUh4,2140
|
|
146
147
|
openrewrite_migrate_python/migrate/xml_deprecations.py,sha256=mvyEUhVGPQTsjE81OkxBqKQCmis5xqTNg6LcUX1ipp4,7613
|
|
147
|
-
openrewrite_migrate_python-0.
|
|
148
|
-
openrewrite_migrate_python-0.
|
|
149
|
-
openrewrite_migrate_python-0.
|
|
150
|
-
openrewrite_migrate_python-0.
|
|
151
|
-
openrewrite_migrate_python-0.
|
|
148
|
+
openrewrite_migrate_python-0.20.0.dist-info/METADATA,sha256=DAE-EZVWsrzQUdG46PKrWloODLgLzRMjsHoVt7L-Nzk,1450
|
|
149
|
+
openrewrite_migrate_python-0.20.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
150
|
+
openrewrite_migrate_python-0.20.0.dist-info/entry_points.txt,sha256=SuygzlMJiKIW19FYJVxx0hhRyzW6HTDCweY7584XpXw,138
|
|
151
|
+
openrewrite_migrate_python-0.20.0.dist-info/top_level.txt,sha256=y8dKeC9kb5GUzFraB-OhJGxjPemM3GaPdrAygz2RP0U,59
|
|
152
|
+
openrewrite_migrate_python-0.20.0.dist-info/RECORD,,
|
{openrewrite_migrate_python-0.19.0.dist-info → openrewrite_migrate_python-0.20.0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|