From ac3a4b2c363902ef833353701308325c062469b7 Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Fri, 23 Jan 2026 22:33:18 -0600 Subject: [PATCH 1/2] feat: remove dj.kill and dj.kill_quick Remove database connection management functions that don't belong in a data pipeline library. Closes #1347 Reasoning: - Outside DataJoint's scope (data pipelines, not DB administration) - Better tools exist (native SQL, CLI tools, GUIs, cloud consoles) - Implementation had issues (interactive prompts, SQL injection risk) - MySQL-only, wouldn't work with PostgreSQL backend Users can use native database tools instead: - MySQL: SHOW PROCESSLIST; KILL ; - PostgreSQL: SELECT * FROM pg_stat_activity; SELECT pg_terminate_backend(pid); Co-Authored-By: Claude Opus 4.5 --- src/datajoint/__init__.py | 3 - src/datajoint/admin.py | 101 -------------------------------- tests/unit/test_lazy_imports.py | 20 ------- 3 files changed, 124 deletions(-) delete mode 100644 src/datajoint/admin.py diff --git a/src/datajoint/__init__.py b/src/datajoint/__init__.py index 7e07977f3..7f809487d 100644 --- a/src/datajoint/__init__.py +++ b/src/datajoint/__init__.py @@ -39,7 +39,6 @@ "Top", "U", "Diagram", - "kill", "MatCell", "MatStruct", # Codec API @@ -94,8 +93,6 @@ # Diagram imports networkx and matplotlib "Diagram": (".diagram", "Diagram"), "diagram": (".diagram", None), # Return the module itself - # kill imports pymysql via connection - "kill": (".admin", "kill"), # cli imports click "cli": (".cli", "cli"), } diff --git a/src/datajoint/admin.py b/src/datajoint/admin.py deleted file mode 100644 index 275e9a823..000000000 --- a/src/datajoint/admin.py +++ /dev/null @@ -1,101 +0,0 @@ -import logging - -import pymysql - -from .connection import conn - -logger = logging.getLogger(__name__.split(".")[0]) - - -def kill(restriction=None, connection=None, order_by=None): - """ - View and kill database connections interactively. - - Displays a list of active connections and prompts for connections to kill. - - Parameters - ---------- - restriction : str, optional - SQL WHERE clause to filter connections. Can use any attribute from - information_schema.processlist: ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO. - connection : Connection, optional - A datajoint.Connection object. Defaults to datajoint.conn(). - order_by : str or list[str], optional - Attribute(s) to order results by. Defaults to 'id'. - - Examples - -------- - >>> dj.kill('HOST LIKE "%compute%"') # List connections from hosts containing "compute" - >>> dj.kill('TIME > 600') # List connections idle for more than 10 minutes - """ - - if connection is None: - connection = conn() - - if order_by is not None and not isinstance(order_by, str): - order_by = ",".join(order_by) - - query = ( - "SELECT * FROM information_schema.processlist WHERE id <> CONNECTION_ID()" - + ("" if restriction is None else " AND (%s)" % restriction) - + (" ORDER BY %s" % (order_by or "id")) - ) - - while True: - print(" ID USER HOST STATE TIME INFO") - print("+--+ +----------+ +-----------+ +-----------+ +-----+") - cur = ({k.lower(): v for k, v in elem.items()} for elem in connection.query(query, as_dict=True)) - for process in cur: - try: - print("{id:>4d} {user:<12s} {host:<12s} {state:<12s} {time:>7d} {info}".format(**process)) - except TypeError: - print(process) - response = input('process to kill or "q" to quit > ') - if response == "q": - break - if response: - try: - pid = int(response) - except ValueError: - pass # ignore non-numeric input - else: - try: - connection.query("kill %d" % pid) - except pymysql.err.InternalError: - logger.warn("Process not found") - - -def kill_quick(restriction=None, connection=None): - """ - Kill database connections without prompting. - - Parameters - ---------- - restriction : str, optional - SQL WHERE clause to filter connections. Can use any attribute from - information_schema.processlist: ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO. - connection : Connection, optional - A datajoint.Connection object. Defaults to datajoint.conn(). - - Returns - ------- - int - Number of terminated connections. - - Examples - -------- - >>> dj.kill_quick('HOST LIKE "%compute%"') # Kill connections from hosts with "compute" - """ - if connection is None: - connection = conn() - - query = "SELECT * FROM information_schema.processlist WHERE id <> CONNECTION_ID()" + ( - "" if restriction is None else " AND (%s)" % restriction - ) - - cur = ({k.lower(): v for k, v in elem.items()} for elem in connection.query(query, as_dict=True)) - nkill = 0 - for process in cur: - connection.query("kill %d" % process["id"]) - nkill += 1 - return nkill diff --git a/tests/unit/test_lazy_imports.py b/tests/unit/test_lazy_imports.py index f5142516e..a87412151 100644 --- a/tests/unit/test_lazy_imports.py +++ b/tests/unit/test_lazy_imports.py @@ -27,25 +27,6 @@ def test_lazy_diagram_import(): assert Diagram.__name__ == "Diagram" -def test_lazy_admin_import(): - """Admin module should not be loaded until dj.kill is accessed.""" - # Remove datajoint from sys.modules to get fresh import - modules_to_remove = [key for key in sys.modules if key.startswith("datajoint")] - for mod in modules_to_remove: - del sys.modules[mod] - - # Import datajoint - import datajoint as dj - - # Admin module should not be loaded yet - assert "datajoint.admin" not in sys.modules, "admin module loaded eagerly" - - # Access kill - should trigger lazy load - kill = dj.kill - assert "datajoint.admin" in sys.modules, "admin module not loaded after access" - assert callable(kill) - - def test_lazy_cli_import(): """CLI module should not be loaded until dj.cli is accessed.""" # Remove datajoint from sys.modules to get fresh import @@ -103,5 +84,4 @@ def test_core_imports_available(): # Heavy modules should still not be loaded assert "datajoint.diagram" not in sys.modules - assert "datajoint.admin" not in sys.modules assert "datajoint.cli" not in sys.modules From 6d4e3902e26386c068774df95a957d5afef7c68c Mon Sep 17 00:00:00 2001 From: Dimitri Yatsenko Date: Sat, 24 Jan 2026 10:21:10 -0600 Subject: [PATCH 2/2] chore: remove unused _RenameMap class Dead code - class was defined but never instantiated anywhere. Co-Authored-By: Claude Opus 4.5 --- src/datajoint/table.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/datajoint/table.py b/src/datajoint/table.py index 8c672f41e..16cb51b6d 100644 --- a/src/datajoint/table.py +++ b/src/datajoint/table.py @@ -50,12 +50,6 @@ ) -class _RenameMap(tuple): - """for internal use""" - - pass - - @dataclass class ValidationResult: """