6Migration Script: Migrate a project from embedded diagnostics to oc_diagnostics
7================================================================================
11Rewrite Python import paths and Markdown references in an existing project so
12that all usage of the old embedded ``diagnostics/`` package is replaced by the
13new standalone ``oc_diagnostics`` package.
17- Walk the project tree, skipping common non-project directories
19- Rewrite Python import statements in ``.py`` files using a curated list of
20 ``(pattern, replacement)`` pairs.
21- Rewrite documentation references in ``.md`` files.
22- Create a ``.bak`` file next to every modified file so changes are reversible.
23- Handle the old ``diagnostics/`` directory in one of two ways:
24 - **Default (backup):** rename ``diagnostics/`` to ``diagnostics.bak/``.
25 The original content is preserved; the active project no longer uses it.
26 - **``--delete-original``:** permanently delete ``diagnostics/`` with no
27 backup copy. Use only when a backup already exists.
28- Support ``--dry-run`` to preview all actions without modifying the filesystem.
40- Must not modify files outside the target project root.
41- Must skip directories in ``EXCLUDE_DIRS`` to avoid corrupting virtual
42 environments, VCS metadata, caches, and third-party packages.
43- In ``--dry-run`` mode no files must be written, renamed, or deleted.
44- Every modified file must have a ``.bak`` copy written before the rewrite.
45- The ``diagnostics/`` backup copy is created by default; ``--delete-original``
46 skips the backup and deletes instead.
48Run this script from the root of the project being migrated:
50 python tools/migrate_to_oc_diagnostics.py [--dry-run] [--delete-original]
85PYTHON_REPLACEMENTS = [
87 (
r"from\s+diagnostics\.unified_debug\s+import\s+_dbg",
88 r"from oc_diagnostics import _dbg"),
90 (
r"from\s+diagnostics\.unified_debug\s+import\s+DEBUG_LEVELS",
91 r"from oc_diagnostics import DEBUG_LEVELS"),
94 (
r"from\s+\.\s*diagnostics\.unified_debug\s+import\s+_dbg",
95 r"from oc_diagnostics import _dbg"),
97 (
r"from\s+\.\s*diagnostics\.unified_debug\s+import\s+DEBUG_LEVELS",
98 r"from oc_diagnostics import DEBUG_LEVELS"),
101 (
r"from\s+diagnostics\.instrumentation_middleware\s+import\s+apply_instrumentation",
102 r"from oc_diagnostics.instrumentation_middleware import apply_instrumentation"),
104 (
r"from\s+diagnostics\.instrumentation_middleware\s+import\s+enable_instrumentation_middleware",
105 r"from oc_diagnostics.instrumentation_middleware import enable_instrumentation_middleware"),
108 (
r"from\s+\.\s*diagnostics\.instrumentation_middleware\s+import\s+apply_instrumentation",
109 r"from oc_diagnostics.instrumentation_middleware import apply_instrumentation"),
111 (
r"from\s+\.\s*diagnostics\.instrumentation_middleware\s+import\s+enable_instrumentation_middleware",
112 r"from oc_diagnostics.instrumentation_middleware import enable_instrumentation_middleware"),
115 (
r"from\s+diagnostics\.id_instrumentation\s+import\s+instrument_ids",
116 r"from oc_diagnostics.id_instrumentation import instrument_ids"),
118 (
r"from\s+\.\s*diagnostics\.id_instrumentation\s+import\s+instrument_ids",
119 r"from oc_diagnostics.id_instrumentation import instrument_ids"),
122 (
r"from\s+diagnostics\.ui_event_logger\s+import\s+ui_event_logger",
123 r"from oc_diagnostics.ui_event_logger import ui_event_logger"),
125 (
r"from\s+\.\s*diagnostics\.ui_event_logger\s+import\s+ui_event_logger",
126 r"from oc_diagnostics.ui_event_logger import ui_event_logger"),
135 (
r"\bdiagnostics/",
r"oc_diagnostics/"),
136 (
r"DIAGNOSTICS_README\.md",
r"REFERENCE.md"),
137 (
r"DIAGNOSTICS_INTEGRATION_GUIDE_README\.md",
r"INTEGRATION_GUIDE.md"),
138 (
r"from diagnostics\.",
r"from oc_diagnostics."),
139 (
r"`diagnostics/`",
r"`oc_diagnostics/`"),
148 """Return True if this directory should be skipped during the tree walk."""
149 parts = set(os.path.normpath(dirpath).split(os.sep))
150 return bool(parts & EXCLUDE_DIRS)
154 """Rewrite a file using a list of (pattern, replacement) pairs.
156 Returns True if the file would be (or was) modified.
157 Creates a ``.bak`` backup before writing when not in dry-run mode.
160 with open(path,
"r", encoding=
"utf-8")
as f:
162 except (OSError, UnicodeDecodeError):
166 for pattern, repl
in replacements:
167 updated = re.sub(pattern, repl, updated)
169 if updated == original:
173 backup = path +
".bak"
174 with open(backup,
"w", encoding=
"utf-8")
as f:
176 with open(path,
"w", encoding=
"utf-8")
as f:
183 """Walk *root* and rewrite all Python and Markdown files.
185 Returns the list of files that were (or would be) modified.
189 for dirpath, dirnames, filenames
in os.walk(root):
193 if d
not in EXCLUDE_DIRS
and not d.endswith(
".egg-info")
199 for filename
in filenames:
200 full = os.path.join(dirpath, filename)
202 if filename.endswith(
".py"):
204 modified.append(full)
206 elif filename.endswith(
".md"):
208 modified.append(full)
218 """Handle the old ``diagnostics/`` directory.
220 Default (backup): rename ``diagnostics/`` → ``diagnostics.bak/``.
221 With ``--delete-original``: permanently delete ``diagnostics/``.
223 Returns a human-readable status string.
225 old_path = os.path.join(
".",
"diagnostics")
226 backup_path = os.path.join(
".",
"diagnostics.bak")
228 if not os.path.exists(old_path):
229 return "No diagnostics/ folder found (already removed or never present)."
232 action = f
"Deleted diagnostics/ (no backup created)."
234 shutil.rmtree(old_path)
236 action = f
"Renamed diagnostics/ → diagnostics.bak/ (backup preserved)."
238 if os.path.exists(backup_path):
239 shutil.rmtree(backup_path)
240 shutil.move(old_path, backup_path)
249if __name__ ==
"__main__":
250 parser = argparse.ArgumentParser(
251 description=
"Migrate a project from embedded diagnostics/ to oc_diagnostics.",
256 help=
"Preview all changes without modifying the filesystem.",
262 "Delete diagnostics/ without creating a backup. "
263 "Default behavior renames it to diagnostics.bak/ instead."
266 args = parser.parse_args()
268 dry_prefix =
"[DRY RUN] " if args.dry_run
else ""
270 print(f
"\n{dry_prefix}Migrating project to oc_diagnostics...\n")
272 print(
" (No files will be modified.)\n")
277 print(f
"{dry_prefix}Would update:" if args.dry_run
else "Updated the following files:\n")
278 for f
in modified_files:
281 print(
"\n (.bak backups created alongside each modified file)\n")
283 print(
"No Python or Markdown files required changes.\n")
287 delete_original=args.delete_original,
288 dry_run=args.dry_run,
290 print(f
"{dry_prefix}{result}\n")
292 print(f
"{dry_prefix}Migration {'preview' if args.dry_run else 'complete'}.\n")
bool rewrite_file(str path, list replacements, bool dry_run)
list[str] migrate_project(str root=".", bool dry_run=False)
str handle_old_diagnostics(bool delete_original, bool dry_run)
bool _should_exclude(str dirpath)