Yicong-Huang commented on code in PR #58288:
URL: https://github.com/apache/spark/pull/58288#discussion_r3876097956


##########
dev/merge_spark_pr.py:
##########
@@ -350,6 +350,112 @@ def additional_fix_versions(inferred_versions, 
existing_versions):
     return [version for version in inferred_versions if version not in 
existing]
 
 
+def parse_affects_versions_input(raw, available_versions):
+    """Parse a committer's comma-separated Affects Version entry and validate 
it.
+
+    Whitespace around each entry is stripped and empty segments are dropped, 
so a
+    blank line yields no names. Duplicates are removed while input order is
+    preserved. Returns ``(version_names, is_valid)`` where ``is_valid`` is True
+    only when every parsed name is in ``available_versions``; an empty entry is
+    vacuously valid, letting the caller treat blank as "keep current".
+
+    >>> parse_affects_versions_input("4.4.0", {"4.4.0", "5.0.0"})
+    (['4.4.0'], True)
+    >>> parse_affects_versions_input(" 4.4.0 , 5.0.0 ", {"4.4.0", "5.0.0"})
+    (['4.4.0', '5.0.0'], True)
+    >>> parse_affects_versions_input("9.9.9", {"4.4.0", "5.0.0"})
+    (['9.9.9'], False)
+    >>> parse_affects_versions_input("4.4.0,9.9.9", {"4.4.0", "5.0.0"})
+    (['4.4.0', '9.9.9'], False)
+    >>> parse_affects_versions_input("", {"4.4.0"})
+    ([], True)
+    >>> parse_affects_versions_input("   ", {"4.4.0"})
+    ([], True)
+    >>> parse_affects_versions_input("4.4.0, 4.4.0", {"4.4.0"})
+    (['4.4.0'], True)
+    """
+    names = [n for n in (segment.strip() for segment in raw.split(",")) if n]
+    names = list(dict.fromkeys(names))
+    return names, set(names).issubset(available_versions)
+
+
+def fix_precedes_affects(fix_version_names, affects_version_names):
+    """Whether the earliest fix ships before the earliest recorded Affects 
Version.
+
+    The Affects Version/s should reach down to at least the earliest release 
the fix
+    lands in: a fix in F but an affected floor of A > F leaves a fixed release 
the
+    ticket does not admit to affecting. This flags both a too-high affected 
version on
+    a fresh resolve (fixed 4.4.0, affects only 5.0.0) and a backport that 
extends the
+    fix to an earlier line (affects 4.4.0, backport adds fix 4.3.x). Returns 
True iff
+    min(fix) < min(affects) by numeric semver. Only dotted ``x.y.z`` names are
+    compared; other names are ignored, and the result is False when either 
side has
+    no comparable version (so an empty Affects Version/s never triggers a 
prompt).
+
+    >>> fix_precedes_affects(["4.4.0"], ["5.0.0"])
+    True
+    >>> fix_precedes_affects(["4.4.0"], ["4.3.0"])
+    False
+    >>> fix_precedes_affects(["4.4.0"], ["4.4.0"])
+    False
+    >>> fix_precedes_affects(["4.4.0", "4.3.1"], ["4.4.0"])
+    True
+    >>> fix_precedes_affects(["4.4.0"], ["4.10.0"])
+    True
+    >>> fix_precedes_affects(["5.0.0"], [])
+    False
+    >>> fix_precedes_affects([], ["5.0.0"])
+    False
+    """
+
+    def tuples(names):
+        return [
+            tuple(int(p) for p in n.split(".")) for n in names if 
re.match(r"^\d+\.\d+\.\d+$", n)
+        ]
+
+    fix = tuples(fix_version_names)
+    affects = tuples(affects_version_names)
+    if not fix or not affects:
+        return False
+    return min(fix) < min(affects)
+
+
+def suggest_affects_versions(fix_version_names, affects_version_names):
+    """Suggest Affects Version/s from the fix version(s) and the current ones.
+
+    The suggestion keeps every recorded affected version that is not later 
than the
+    highest fix version (dropping only the anomalously-high ones) and adds all 
fix
+    versions, so a fresh resolve replaces a too-high affected version with the 
fix
+    version while a backport extends the affected versions down to the newly 
fixed
+    line. The result is de-duplicated and sorted high-to-low by numeric semver.
+    Non-``x.y.z`` names are ignored.
+
+    >>> suggest_affects_versions(["4.4.0"], ["5.0.0"])
+    ['4.4.0']
+    >>> suggest_affects_versions(["4.4.0", "4.3.1"], ["4.4.0"])
+    ['4.4.0', '4.3.1']
+    >>> suggest_affects_versions(["4.4.0"], ["4.3.0"])
+    ['4.4.0', '4.3.0']
+    >>> suggest_affects_versions(["4.4.0"], [])
+    ['4.4.0']
+    >>> suggest_affects_versions([], ["5.0.0"])
+    []
+    """
+
+    def semver(name):
+        m = re.match(r"^(\d+)\.(\d+)\.(\d+)$", name)
+        return tuple(int(g) for g in m.groups()) if m else None
+
+    fix_tuples = [t for t in (semver(n) for n in fix_version_names) if t is 
not None]
+    if not fix_tuples:
+        return []
+    max_fix = max(fix_tuples)
+    kept_affects = [
+        n for n in affects_version_names if semver(n) is not None and 
semver(n) <= max_fix
+    ]
+    merged = list(dict.fromkeys(list(fix_version_names) + kept_affects))

Review Comment:
   Good point, agreed. I removed the pre-filled default entirely -- since the 
merge target cannot tell us when the bug was introduced, we cannot reliably 
guess the affected version, so there is no default to Enter-through anymore. 
When the recorded Affects Version/s sit above the fix, the script now just 
flags the inconsistency and asks the committer to type the correct version(s) 
explicitly (blank leaves them untouched). That is the deliberate difference 
from the Fix Version prompt, which can safely pre-fill because the merged 
branches tell us exactly which releases contain the fix.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to