[llvm] [mlir] [mlir] Add mlir-opt-repl: interactive MLIR pass pipeline explorer and MCP server (PR #203796)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Jun 14 17:36:13 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Maksim Levental (makslevental)
<details>
<summary>Changes</summary>
See the RFC at https://discourse.llvm.org/t/rfc-mlir-opt-repl-interactive-mlir-pass-pipeline-explorer-and-mcp-server/91068
Add a pip-installable Python package (`mlir-opt-repl`) that provides:
- An interactive terminal REPL for applying mlir-opt passes step by step, with history navigation, rewind, and colored unified/side-by-side diffs.
- An MCP (Model Context Protocol) server for use as a Claude Code tool, exposing the same functionality over JSON-RPC stdio.
Note, this PR checks in the tests as `pytest` tests which are launched via lit. The reason for this is I use `coverage=100%` as a regularizer on Claude (hallucinate a fix/feature -> add a test -> better be 100% covered).
By Claude, for Claude :)
---
Patch is 75.44 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203796.diff
17 Files Affected:
- (modified) .ci/all_requirements.txt (+152-2)
- (modified) mlir/python/requirements.txt (+6)
- (added) mlir/test/mlir-opt-repl/conftest.py (+84)
- (added) mlir/test/mlir-opt-repl/lit.local.cfg (+13)
- (added) mlir/test/mlir-opt-repl/run-tests.test (+1)
- (added) mlir/test/mlir-opt-repl/test_cli_and_edge_cases.py (+164)
- (added) mlir/test/mlir-opt-repl/test_mcp.py (+281)
- (added) mlir/test/mlir-opt-repl/test_repl.py (+203)
- (added) mlir/tools/mlir-opt-repl/README.md (+145)
- (added) mlir/tools/mlir-opt-repl/pyproject.toml (+38)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/__init__.py (+3)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/__main__.py (+122)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/diff.py (+91)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/engine.py (+211)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/mcp.py (+201)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/render.py (+7)
- (added) mlir/tools/mlir-opt-repl/src/mlir_opt_repl/repl.py (+213)
``````````diff
diff --git a/.ci/all_requirements.txt b/.ci/all_requirements.txt
index 1ab2a81b1d1db..ce4f34317ebba 100644
--- a/.ci/all_requirements.txt
+++ b/.ci/all_requirements.txt
@@ -181,6 +181,122 @@ charset-normalizer==3.4.3 \
--hash=sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c \
--hash=sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9
# via requests
+click==8.4.1 \
+ --hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \
+ --hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96
+ # via -r mlir/python/requirements.txt
+colorama==0.4.6 \
+ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
+ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
+ # via -r mlir/python/requirements.txt
+coverage[toml]==7.14.1 \
+ --hash=sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86 \
+ --hash=sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd \
+ --hash=sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d \
+ --hash=sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5 \
+ --hash=sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42 \
+ --hash=sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de \
+ --hash=sha256:10274a1fbeb8ec5d72966e17bb198a3104257aca4ac09d98667c5f8aca8c8548 \
+ --hash=sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1 \
+ --hash=sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7 \
+ --hash=sha256:1238cb94638e610e972c60dac68e813f868dc7d6e982535270558443058d9d59 \
+ --hash=sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906 \
+ --hash=sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af \
+ --hash=sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1 \
+ --hash=sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d \
+ --hash=sha256:196a13319ad88d6d8ef5ab489ec4f44ddde2143c0c7d5b27786f6c3ffd56a7e1 \
+ --hash=sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be \
+ --hash=sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02 \
+ --hash=sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42 \
+ --hash=sha256:23bf7fa51ac02e07fc7c96849b82946da47ae862dc8f86d183b2a4864fc38129 \
+ --hash=sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e \
+ --hash=sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be \
+ --hash=sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e \
+ --hash=sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65 \
+ --hash=sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54 \
+ --hash=sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1 \
+ --hash=sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5 \
+ --hash=sha256:3a56abc20a472baf0304c455721bc601477440d28ecfde8a03dde79ede07e0df \
+ --hash=sha256:3c18ebc343e15be53049b3a2dce38fe82d58f37e20ab9094b3a39c0aa4f6bb47 \
+ --hash=sha256:3d452fd08b5c72c5167c93e6867b5c08500bd40f2a21e1e854a500550b6cc36f \
+ --hash=sha256:3e3680291c4a1d0dadfa84a2c459576a4af5133abb617905714339a0c73138cf \
+ --hash=sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37 \
+ --hash=sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4 \
+ --hash=sha256:478b5bcd63c2e1357c5c7e16c070690df7b07f676b1c114d7b93e533c664309f \
+ --hash=sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84 \
+ --hash=sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1 \
+ --hash=sha256:4ea1c034f95c9b056e856b794630b17f9fa3d57e4800ff1e503d3be0f9c9078c \
+ --hash=sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8 \
+ --hash=sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e \
+ --hash=sha256:59baf88468dbc8d63b1887afd92bda52e40bb1561696e5819670601403810cec \
+ --hash=sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d \
+ --hash=sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54 \
+ --hash=sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890 \
+ --hash=sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b \
+ --hash=sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d \
+ --hash=sha256:62a9f70b52e0b5a95cfef4a5c5641b06983cadc5e538a3feeb5c00211f523ac2 \
+ --hash=sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33 \
+ --hash=sha256:6a3cb83d1552c0cd1b4906655b6a33fd4a8473229633a901c6b73bf86914dee9 \
+ --hash=sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e \
+ --hash=sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6 \
+ --hash=sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce \
+ --hash=sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247 \
+ --hash=sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901 \
+ --hash=sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36 \
+ --hash=sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69 \
+ --hash=sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416 \
+ --hash=sha256:7f02d09f70776579b926d889a4c9c235070a1f47c40458aeaca563fae5acfdb5 \
+ --hash=sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500 \
+ --hash=sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad \
+ --hash=sha256:84ac9499e48700399a5dd0ea7085b5091961fec52c68d66b4ec0d3cf7f4441b1 \
+ --hash=sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b \
+ --hash=sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b \
+ --hash=sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a \
+ --hash=sha256:87ebdf787d4888e3f3f2d523eadc6e18c6d18c6d0eb173801a189641627fb37e \
+ --hash=sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee \
+ --hash=sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07 \
+ --hash=sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a \
+ --hash=sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d \
+ --hash=sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c \
+ --hash=sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343 \
+ --hash=sha256:a24a81f9715ee42ef59a316cc11611c98fe23920f7c81861315c9f3ff4a230f4 \
+ --hash=sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2 \
+ --hash=sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8 \
+ --hash=sha256:a5274669f37f2343635a347b91a60777621341ab3378e9c6ac9335eee704bddf \
+ --hash=sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb \
+ --hash=sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c \
+ --hash=sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff \
+ --hash=sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e \
+ --hash=sha256:b84ffdf877644e7096aa936991efeed873f7f3df57b9cd001312b7668ab08550 \
+ --hash=sha256:bcaa50684dcaadfa599ac48f81103c756d791cfd85c97203d2217c593d48b860 \
+ --hash=sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793 \
+ --hash=sha256:c643734307300234fafa36bf2a040a7235f8f177ea1fd6ec1423aea6fb7b929f \
+ --hash=sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851 \
+ --hash=sha256:c7e057326434e441306226fbeb5d1aaf14a2637efe97ba668306635835f32ad7 \
+ --hash=sha256:c912c259304cfb5ee584481cfb7ce1ff932b4d61e6c9140b8f19cb7b5ed82332 \
+ --hash=sha256:ce66d8e46da2bb5ee313a745cbd2e391d319176c1f7a9451bfcd3a2fb920859b \
+ --hash=sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2 \
+ --hash=sha256:cfe5a5fec635799ef33428f1e5e61bafa45a92a96190ba731561ba558ccc214d \
+ --hash=sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a \
+ --hash=sha256:d34d75f892b3ab73ba11cab5442cce7b3e168fd64162b16f0e1e0d09c508edef \
+ --hash=sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474 \
+ --hash=sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee \
+ --hash=sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43 \
+ --hash=sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034 \
+ --hash=sha256:dd34767fa19848d35659ffc0a75314f58c7af3f1cd87ec521e8292a1238398a3 \
+ --hash=sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c \
+ --hash=sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d \
+ --hash=sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7 \
+ --hash=sha256:e854312c4103f2ad4c0dc023b69b77ebfd2c89db5f86c4c94dc2353f9a92167e \
+ --hash=sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d \
+ --hash=sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4 \
+ --hash=sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9 \
+ --hash=sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52 \
+ --hash=sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a \
+ --hash=sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c \
+ --hash=sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253 \
+ --hash=sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c
+ # via pytest-cov
cryptography==46.0.3 \
--hash=sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217 \
--hash=sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d \
@@ -239,6 +355,10 @@ cryptography==46.0.3 \
# via
# -r lldb/test/requirements.txt
# pyjwt
+execnet==2.1.2 \
+ --hash=sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd \
+ --hash=sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec
+ # via pytest-xdist
google-api-core==2.25.1 \
--hash=sha256:8a2a56c1fef82987a524371f99f3bd0143702fecc670c72e600c1cda6bf8dbb7 \
--hash=sha256:d2aaa0b13c78c61cb3f4282c464c046e45fbd75755683c9c525e6e8f7ed0a5e8
@@ -310,6 +430,10 @@ idna==3.10 \
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
# via requests
+iniconfig==2.3.0 \
+ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \
+ --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12
+ # via pytest
junitparser==3.2.0 \
--hash=sha256:b05e89c27e7b74b3c563a078d6e055d95cf397444f8f689b0ca616ebda0b3c65 \
--hash=sha256:e14fdc0a999edfc15889b637390e8ef6ca09a49532416d3bd562857d42d4b96d
@@ -404,11 +528,19 @@ numpy==2.1.2 \
packaging==25.0 \
--hash=sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 \
--hash=sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f
- # via -r lldb/test/requirements.txt
+ # via
+ # -r lldb/test/requirements.txt
+ # pytest
pexpect==4.9.0 ; sys_platform != "win32" \
--hash=sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523 \
--hash=sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f
# via -r lldb/test/requirements.txt
+pluggy==1.6.0 \
+ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \
+ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
+ # via
+ # pytest
+ # pytest-cov
proto-plus==1.26.1 \
--hash=sha256:13285478c2dcf2abb829db158e1047e2f1e8d63a077d94263c2b88b043c75a66 \
--hash=sha256:21a515a4c4c0088a773899e23c7bbade3d18f9c66c73edd4c7ee3816bc96a012
@@ -461,6 +593,10 @@ pygithub==2.8.1 \
--hash=sha256:23a0a5bca93baef082e03411bf0ce27204c32be8bfa7abc92fe4a3e132936df0 \
--hash=sha256:341b7c78521cb07324ff670afd1baa2bf5c286f8d9fd302c1798ba594a5400c9
# via -r .ci/requirements.txt
+pygments==2.20.0 \
+ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
+ --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
+ # via pytest
pyjwt[crypto]==2.10.1 \
--hash=sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953 \
--hash=sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb
@@ -494,6 +630,21 @@ pynacl==1.6.0 \
--hash=sha256:f46386c24a65383a9081d68e9c2de909b1834ec74ff3013271f1bca9c2d233eb \
--hash=sha256:f4b3824920e206b4f52abd7de621ea7a44fd3cb5c8daceb7c3612345dfc54f2e
# via pygithub
+pytest==9.1.0 \
+ --hash=sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c \
+ --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32
+ # via
+ # -r mlir/python/requirements.txt
+ # pytest-cov
+ # pytest-xdist
+pytest-cov==7.1.0 \
+ --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \
+ --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678
+ # via -r mlir/python/requirements.txt
+pytest-xdist==3.8.0 \
+ --hash=sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88 \
+ --hash=sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1
+ # via -r mlir/python/requirements.txt
pyyaml==6.0.1 \
--hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
--hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
@@ -580,7 +731,6 @@ typing-extensions==4.15.0 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via
# -r mlir/python/requirements.txt
- # cryptography
# pygithub
urllib3==2.5.0 \
--hash=sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760 \
diff --git a/mlir/python/requirements.txt b/mlir/python/requirements.txt
index 538929e1bbfd2..11065d7635672 100644
--- a/mlir/python/requirements.txt
+++ b/mlir/python/requirements.txt
@@ -5,3 +5,9 @@ typing_extensions>=4.12.2
# RUN dependencies
numpy>=2.1.0, <=2.1.2
ml_dtypes>=0.5.0, <=0.6.0
+click>=8.2.1
+colorama>=0.4.6
+# TEST dependencies
+pytest>=9.0.3
+pytest-cov>=7.1.0
+pytest-xdist>=3.8.0
diff --git a/mlir/test/mlir-opt-repl/conftest.py b/mlir/test/mlir-opt-repl/conftest.py
new file mode 100644
index 0000000000000..39972267255a4
--- /dev/null
+++ b/mlir/test/mlir-opt-repl/conftest.py
@@ -0,0 +1,84 @@
+import json
+import re
+import sys
+from contextlib import contextmanager
+from io import StringIO
+from pathlib import Path
+
+import pytest
+
+sys.path.insert(
+ 0, str(Path(__file__).parent.parent.parent / "tools" / "mlir-opt-repl" / "src")
+)
+
+import mlir_opt_repl.engine as engine
+from mlir_opt_repl.mcp import mcp_main
+from mlir_opt_repl.repl import interactive_main
+
+SAMPLE_MLIR = "func.func @test(%arg0: f32, %arg1: f32) -> f32 { %0 = arith.addf %arg0, %arg1 : f32 return %0 : f32 }"
+INIT_MSG = {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {}}
+
+
+ at pytest.fixture(autouse=True)
+def reset_engine():
+ engine.current_ir = None
+ engine.ir_history = []
+ yield
+ engine.current_ir = None
+ engine.ir_history = []
+
+
+ at contextmanager
+def capture_stdio(input_text):
+ old_stdin, old_stdout = sys.stdin, sys.stdout
+ sys.stdin = StringIO(input_text)
+ sys.stdout = StringIO()
+ try:
+ yield sys.stdout
+ finally:
+ sys.stdin, sys.stdout = old_stdin, old_stdout
+
+
+def run_mcp(*messages):
+ input_text = ""
+ for m in messages:
+ text = json.dumps(m)
+ input_text += f"Content-Length: {len(text.encode())}\r\n\r\n{text}"
+
+ with capture_stdio(input_text) as stdout:
+ try:
+ mcp_main()
+ except Exception:
+ pass
+ return stdout.getvalue()
+
+
+def run_repl(input_text):
+ with capture_stdio(input_text) as stdout:
+ try:
+ interactive_main()
+ except (EOFError, SystemExit):
+ pass
+ return stdout.getvalue()
+
+
+def parse_responses(output):
+ responses = []
+ for m in re.finditer(r"Content-Length: (\d+)\r?\n\r?\n", output):
+ start = m.end()
+ length = int(m.group(1))
+ body = output[start : start + length]
+ responses.append(json.loads(body))
+ return responses
+
+
+def tool_call(id, name, arguments=None):
+ return {
+ "jsonrpc": "2.0",
+ "id": id,
+ "method": "tools/call",
+ "params": {
+ "name": name,
+ "arguments": arguments or {},
+ },
+ }
diff --git a/mlir/test/mlir-opt-repl/lit.local.cfg b/mlir/test/mlir-opt-repl/lit.local.cfg
new file mode 100644
index 0000000000000..a911833dfd4ad
--- /dev/null
+++ b/mlir/test/mlir-opt-repl/lit.local.cfg
@@ -0,0 +1,13 @@
+import os
+
+src_root = os.path.join(config.mlir_src_root, "tools", "mlir-opt-repl", "src")
+pyproject = os.path.join(config.mlir_src_root, "tools", "mlir-opt-repl", "pyproject.toml")
+config.environment["PYTHONPATH"] = src_root
+config.environment["MLIR_OPT"] = os.path.join(config.mlir_tools_dir, "mlir-opt")
+
+config.substitutions.insert(0, ("%pyproject", pyproject))
+
+config.suffixes = [".test"]
+
+if not config.enable_bindings_python:
+ config.unsupported = True
diff --git a/mlir/test/mlir-opt-repl/run-tests.test b/mlir/test/mlir-opt-repl/run-tests.test
new file mode 100644
index 0000000000000..4500591f9d7f9
--- /dev/null
+++ b/mlir/test/mlir-opt-repl/run-tests.test
@@ -0,0 +1 @@
+// RUN: %python -m pytest %S -n auto -q --cov=mlir_opt_repl --cov-config=%pyproject --cov-report=term-missing --cov-fail-under=100
diff --git a/mlir/test/mlir-opt-repl/test_cli_and_edge_cases.py b/mlir/test/mlir-opt-repl/test_cli_and_edge_cases.py
new file mode 100644
index 0000000000000..42d91ffa3d65e
--- /dev/null
+++ b/mlir/test/mlir-opt-repl/test_cli_and_edge_cases.py
@@ -0,0 +1,164 @@
+"""Tests for the click CLI entry point and edge cases in engine/diff."""
+
+import json
+import subprocess
+from unittest.mock import patch
+
+from click.testing import CliRunner
+
+import mlir_opt_repl.engine as engine
+from conftest import capture_stdio
+from mlir_opt_repl.__main__ import cli
+from mlir_opt_repl.diff import render_side_by_side, render_unified_diff
+from mlir_opt_repl.mcp import mcp_main
+from mlir_opt_repl.repl import interactive_main
+
+
+class TestClickCLI:
+ def test_help(self):
+ result = CliRunner().invoke(cli, ["--help"])
+ assert result.exit_code == 0
+ assert "mcp" in result.output
+ assert "repl" in result.output
+
+ def test_mcp_help(self):
+ result = CliRunner().invoke(cli, ["mcp", "--help"])
+ assert result.exit_code == 0
+ assert "MCP" in result.output
+
+ def test_repl_help(self):
+ result = CliRunner().invoke(cli, ["repl", "--help"])
+ assert result.exit_code == 0
+ assert "load" in result.output
+
+ def test_default_is_repl(self):
+ result = CliRunner().invoke(cli, [], input="quit\n")
+ assert "mlir-opt-repl" in result.output
+
+ def test_mcp_subcommand(self):
+ msg = json.dumps(
+ {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {}}
+ )
+ input_text = f"Content-Length: {len(msg.encode())}\r\n\r\n{msg}"
+ result = CliRunner().invoke(cli, ["mcp"], input=input_text)
+ assert "mlir-opt-repl" in result.output
+
+ def test_config_with_mlir_opt(self):
+ result = CliRunner().invoke(cli, ["mcp-config"])
+ assert result.exit_code == 0
+ assert "mcpServers" in result.output
+ assert "__main__.py" in result.output
+ assert "mcp" in result.output
+
+ def test_config_without_mlir_opt(self, monkeypatch):
+ monkeypatch.delenv...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/203796
More information about the llvm-commits
mailing list