|
106 | 106 | } |
107 | 107 |
|
108 | 108 |
|
| 109 | +# Enum values removed or split between v3 and v4. Flagged (not rewritten) |
| 110 | +# because the correct replacement depends on call-site semantics. |
| 111 | +REMOVED_ENUM_VALUES: dict[str, str] = { |
| 112 | + "MediaBuyStatus.pending_activation": ( |
| 113 | + "`pending_activation` split in v4: use `pending_start` if the buy hasn't reached " |
| 114 | + "its scheduled start date, or `pending_creatives` if creatives haven't been " |
| 115 | + "submitted. Check `valid_actions` on the MediaBuy response to confirm which applies." |
| 116 | + ), |
| 117 | +} |
| 118 | + |
| 119 | + |
109 | 120 | # Private-module imports that shouldn't appear in downstream code. |
110 | 121 | PRIVATE_IMPORT_PATHS: dict[str, str] = { |
111 | 122 | "adcp.types.generated_poc": ( |
|
168 | 179 | class Finding: |
169 | 180 | """One migration finding — either an applied rename or a manual TODO.""" |
170 | 181 |
|
171 | | - kind: str # "rename" | "flag_removed" | "flag_private" | "flag_numbered" | "flag_attribute" |
| 182 | + # Valid kind values: "rename" | "flag_removed" | "flag_private" | |
| 183 | + # "flag_numbered" | "flag_attribute" | "flag_enum_value" |
| 184 | + kind: str |
172 | 185 | path: str |
173 | 186 | line: int |
174 | 187 | column: int |
@@ -253,6 +266,12 @@ def _iter_python_files(root: Path) -> list[Path]: |
253 | 266 | attr: re.compile(rf"{re.escape(attr)}\b") for attr in REMOVED_ATTRIBUTE_ACCESSES |
254 | 267 | } |
255 | 268 |
|
| 269 | +# Enum value patterns — re.escape handles the dot so the pattern matches |
| 270 | +# the literal ``MediaBuyStatus.pending_activation``, not a regex wildcard. |
| 271 | +_REMOVED_ENUM_VALUE_PATTERNS = { |
| 272 | + val: re.compile(rf"{re.escape(val)}\b") for val in REMOVED_ENUM_VALUES |
| 273 | +} |
| 274 | + |
256 | 275 |
|
257 | 276 | def scan_file(path: Path, *, apply_changes: bool) -> tuple[list[Finding], str | None]: |
258 | 277 | """Scan one file. Returns (findings, new_contents_or_None). |
@@ -399,6 +418,23 @@ def scan_file(path: Path, *, apply_changes: bool) -> tuple[list[Finding], str | |
399 | 418 | ) |
400 | 419 | ) |
401 | 420 |
|
| 421 | + # Removed enum values (e.g. MediaBuyStatus.pending_activation). The |
| 422 | + # class-qualified form is anchored tightly enough that false positives |
| 423 | + # are unlikely; trailing word boundary prevents suffix matches like |
| 424 | + # ``MediaBuyStatus.pending_activation_v2``. |
| 425 | + for enum_val, hint in REMOVED_ENUM_VALUES.items(): |
| 426 | + for match in _REMOVED_ENUM_VALUE_PATTERNS[enum_val].finditer(line): |
| 427 | + findings.append( |
| 428 | + Finding( |
| 429 | + kind="flag_enum_value", |
| 430 | + path=str(path), |
| 431 | + line=lineno, |
| 432 | + column=match.start() + 1, |
| 433 | + before=enum_val, |
| 434 | + hint=hint, |
| 435 | + ) |
| 436 | + ) |
| 437 | + |
402 | 438 | if apply_changes and rename_hits: |
403 | 439 | for old, new in ASSET_CONTENT_RENAMES.items(): |
404 | 440 | updated = _RENAME_PATTERNS[old].sub(new, updated) |
@@ -513,7 +549,8 @@ def _format_text_report(report: Report, *, apply_changes: bool) -> str: |
513 | 549 | "before": str, "after": str, "hint": null, "migration_anchor": null} |
514 | 550 | ], |
515 | 551 | "flagged": [ |
516 | | - {"kind": "flag_removed" | "flag_numbered" | "flag_private" | "flag_attribute", |
| 552 | + {"kind": "flag_removed" | "flag_numbered" | "flag_private" |
| 553 | + | "flag_attribute" | "flag_enum_value", |
517 | 554 | "path": str, "line": int, "column": int, "before": str, |
518 | 555 | "after": null, "hint": str | null, "migration_anchor": str | null} |
519 | 556 | ] |
|
0 commit comments