# HG changeset patch
# User Arun Kulshreshtha <akulshreshtha@janestreet.com>
# Date 1740163612 18000
#      Fri Feb 21 13:46:52 2025 -0500
# Node ID 69c0a605ed829e4b6fef2522283dca91acc38f26
# Parent  a232522d377076537ae60536fe868b1623e9515d
# EXP-Topic ipython-debugshell
debugcommands: bring over functionality from debugshell extension

There are currently two `hg debugshell` commands: a minimalist, built-in one in
debugcommands.py, and a more featureful extension in contrib/debugshell.py.

This change attempts to consolidate them by bringing over the functionality
from the extension, most notably the ability to use an IPython REPL.

diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -42,6 +42,7 @@
     context,
     copies,
     dagparser,
+    demandimport,
     dirstateutils,
     encoding,
     error,
@@ -3803,7 +3804,7 @@
 
 
 @command(
-    b'debugshell',
+    b'debugshell|dbsh',
     [
         (
             b'c',
@@ -3819,16 +3820,54 @@
 def debugshell(ui, repo, **opts):
     """run an interactive Python interpreter
 
-    The local namespace is provided with a reference to the ui and
-    the repo instance (if available).
+    The local namespace is provided with a reference to the ui and the repo
+    instance (if available). If `ui.debugger` is configured to be `ipdb`, an
+    IPython shell will be used instead of the standard Python shell.
     """
     import code
 
     imported_objects = {
         'ui': ui,
-        'repo': repo,
+        # Import some useful utilities.
+        'hex': hex,
+        'bin': bin,
     }
 
+    if repo:
+        imported_objects.update(
+            {
+                'repo': repo,
+                'cl': repo.changelog,
+                'mf': repo.manifestlog,
+            }
+        )
+
+    # Import the `mercurial` module.
+    srcpath = "(unknown)"
+    if __package__:
+        mercurial = __import__(__package__)
+        imported_objects['m'] = mercurial
+        if mercurial.__path__:
+            srcpath = mercurial.__path__[0]
+
+    # Import native extensions (if available).
+    try:
+        from . import cext
+
+        imported_objects['cext'] = cext
+    except ImportError:
+        cext = None
+    try:
+        from . import pyo3_rustext as rustext
+
+        imported_objects['rustext'] = rustext
+    except ImportError:
+        rustext = None
+
+    # Import a few handy standard library modules.
+    for name in ['os', 'sys', 'subprocess', 're']:
+        imported_objects[name] = __import__(name)
+
     # py2exe disables initialization of the site module, which is responsible
     # for arranging for ``quit()`` to exit the interpreter.  Manually initialize
     # the stuff that site normally does here, so that the interpreter can be
@@ -3850,7 +3889,48 @@
         code.InteractiveInterpreter(locals=imported_objects).runcode(compiled)
         return
 
-    code.interact(local=imported_objects)
+    reporoot = (
+        encoding.strfromlocal(repo.root) if repo and repo.root else "(none)"
+    )
+    bannermsg = (
+        f"Loaded repo: {reporoot}\n"
+        f"Using source: {srcpath}\n"
+        "\nModules:\n"
+        "  m: the mercurial module\n"
+    )
+    if cext:
+        bannermsg += "  cext: native C extensions\n"
+    if rustext:
+        bannermsg += "  rustext: native Rust extensions\n"
+    bannermsg += "\nObjects:\n  ui: the ui object\n"
+    if repo:
+        bannermsg += (
+            "  repo: the repo object\n"
+            "  cl: repo.changelog\n"
+            "  mf: repo.manifestlog\n"
+        )
+    bannermsg += (
+        "\nUtilities:\n"
+        "  hex: binascii.hexlify\n"
+        "  bin: binascii.unhexlify\n"
+    )
+
+    if ui.config(b"ui", b"debugger") == b"ipdb":
+        try:
+            # IPython is incompatible with demandimport.
+            with demandimport.deactivated():
+                import IPython
+
+            globals().update(imported_objects)
+            IPython.embed(header=bannermsg)
+            return
+        except ImportError:
+            ui.warnnoi18n(
+                b"IPython module not found, "
+                b"falling back to standard Python shell\n\n"
+            )
+
+    code.interact(bannermsg, local=imported_objects)
 
 
 @command(
# HG changeset patch
# User Arun Kulshreshtha <akulshreshtha@janestreet.com>
# Date 1740355344 18000
#      Sun Feb 23 19:02:24 2025 -0500
# Node ID c7a4fb11acacc134967b1b7861d30fe4ef3beb45
# Parent  69c0a605ed829e4b6fef2522283dca91acc38f26
# EXP-Topic ipython-debugshell
contrib: delete debugshell extension

The extension's functionality has been moved into core Mercurial. It has
been replaced with a placeholder rather than deleted outright to avoid
breaking existing user configs.

diff --git a/contrib/debugshell.py b/contrib/debugshell.py
--- a/contrib/debugshell.py
+++ b/contrib/debugshell.py
@@ -1,64 +1,5 @@
-# debugshell extension
-"""a python shell with repo, changelog & manifest objects"""
-
-import code
-import mercurial
-import sys
-from mercurial import (
-    demandimport,
-    pycompat,
-    registrar,
-)
-
-cmdtable = {}
-command = registrar.command(cmdtable)
-
-
-def pdb(ui, repo, msg, **opts):
-    objects = {
-        'mercurial': mercurial,
-        'repo': repo,
-        'cl': repo.changelog,
-        'mf': repo.manifestlog,
-    }
-
-    code.interact(msg, local=objects)
-
-
-def ipdb(ui, repo, msg, **opts):
-    import IPython
+"""a python shell with repo, changelog & manifest objects (DEPRECATED)
 
-    cl = repo.changelog
-    mf = repo.manifestlog
-    cl, mf  # use variables to appease pyflakes
-
-    IPython.embed()
-
-
-@command(b'debugshell|dbsh', [])
-def debugshell(ui, repo, **opts):
-    bannermsg = "loaded repo : %s\n" "using source: %s" % (
-        pycompat.sysstr(repo.root),
-        mercurial.__path__[0],
-    )
-
-    pdbmap = {'pdb': 'code', 'ipdb': 'IPython'}
-
-    debugger = ui.config(b"ui", b"debugger")
-    if not debugger:
-        debugger = 'pdb'
-    else:
-        debugger = pycompat.sysstr(debugger)
-
-    # if IPython doesn't exist, fallback to code.interact
-    try:
-        with demandimport.deactivated():
-            __import__(pdbmap[debugger])
-    except ImportError:
-        ui.warnnoi18n(
-            b"%s debugger specified but %s module was not found\n"
-            % (debugger, pdbmap[debugger])
-        )
-        debugger = b'pdb'
-
-    getattr(sys.modules[__name__], debugger)(ui, repo, bannermsg, **opts)
+The functionality of this extension has been included in core Mercurial since
+version 7.1. Please use the core :hg:`debugshell` command instead.
+"""