Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,16 @@ pip install -e .
agentdecompile-cli --server-url http://YOUR_SERVER:8080/ tool --list-tools
```

### Optional: .NET/IL decompilation support (ilspycmd)

`pipx install agentdecompile[all]` (or `pip install -e .[all]` from source) installs every optional **Python** feature, but .NET/IL assembly decompilation depends on [ILSpy's](https://github.com/icsharpcode/ILSpy) `ilspycmd`, which is a .NET global tool, not a PyPI package — it cannot be expressed as a pip dependency and is not bundled by any extras group. Install it separately:

```bash
dotnet tool install -g ilspycmd
```

Requires the [.NET SDK](https://dotnet.microsoft.com/download).

### Option 3: Docker (run the server)

**Published image (no build required):**
Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ test = [
"pytest-sugar>=1.0.0",
"ghidra-stubs>=12.0.4",
]
# Bundles every pip-installable optional feature. Does NOT include `ilspycmd`
# (.NET/IL decompilation support) -- that's ILSpy's dotnet global tool, not a
# PyPI package, and cannot be expressed as a pip dependency. Install it
# separately with `dotnet tool install -g ilspycmd`; see README.md.
all = [
"agentdecompile[semantic]",
]

[build-system]
requires = [
Expand Down
30 changes: 22 additions & 8 deletions tests/test_rewrite_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ def _claim_worker(work_dir: str, request_id: str, claimant: str, result_path: st
Path(result_path).write_text("1" if ok else "0", encoding="utf-8")


def _write_request_worker(work_dir: str, name: str) -> None:
rewrite_queue.write_rewrite_request(
Path(work_dir), function_name=name, entry="0x1", candidate_source=f"src-{name}", mismatch_class=None, mismatch_histogram=None
)


# macOS defaults multiprocessing to the "spawn" start method, which re-imports
# the target function in a fresh interpreter rather than fork()ing the
# already-loaded parent. Under pytest, the test module isn't reliably
# importable by that fresh interpreter (no guaranteed `tests` package on
# sys.path), so spawn-based Process creation fails here with
# ModuleNotFoundError/AttributeError on macOS CI even though the exact same
# code passes on Linux (which defaults to fork). Force fork explicitly --
# available on both Linux and macOS (the only two CI platforms) -- since
# these tests only need process-level isolation, not spawn's clean-slate
# import behavior.
_FORK_CONTEXT = multiprocessing.get_context("fork")


def test_write_rewrite_request_creates_pending_entry(tmp_path: Path) -> None:
request_id = rewrite_queue.write_rewrite_request(
tmp_path,
Expand Down Expand Up @@ -241,8 +260,8 @@ def test_concurrent_claims_from_separate_processes_only_one_wins(tmp_path: Path)
)
result_a = tmp_path / "result_a.txt"
result_b = tmp_path / "result_b.txt"
proc_a = multiprocessing.Process(target=_claim_worker, args=(str(tmp_path), request_id, "proc-a", str(result_a)))
proc_b = multiprocessing.Process(target=_claim_worker, args=(str(tmp_path), request_id, "proc-b", str(result_b)))
proc_a = _FORK_CONTEXT.Process(target=_claim_worker, args=(str(tmp_path), request_id, "proc-a", str(result_a)))
proc_b = _FORK_CONTEXT.Process(target=_claim_worker, args=(str(tmp_path), request_id, "proc-b", str(result_b)))
proc_a.start()
proc_b.start()
proc_a.join(timeout=10)
Expand All @@ -264,13 +283,8 @@ def test_write_rewrite_request_survives_concurrent_writes_to_different_entries(t
not lose each other's entries (the lock serializes the whole file, not
just same-entry races)."""

def _writer(work_dir: str, name: str) -> None:
rewrite_queue.write_rewrite_request(
Path(work_dir), function_name=name, entry="0x1", candidate_source=f"src-{name}", mismatch_class=None, mismatch_histogram=None
)

procs = [
multiprocessing.Process(target=_writer, args=(str(tmp_path), f"sub_{i}"))
_FORK_CONTEXT.Process(target=_write_request_worker, args=(str(tmp_path), f"sub_{i}"))
for i in range(6)
]
for proc in procs:
Expand Down
Loading