Skip to content

Commit b4f113d

Browse files
authored
Merge pull request #130 from PytorchConnectomics/chore/bump-pytorch-connectomics-latest
chore: bump pytorch_connectomics to latest upstream and align backend with v2 CLI
2 parents 3721817 + 9466d72 commit b4f113d

8 files changed

Lines changed: 790 additions & 1260 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ dependencies = [
3131
"torchvision>=0.15.0",
3232
"uvicorn==0.38.0",
3333
"zarr>=2.18",
34-
"connectomics",
34+
"pytorch-connectomics",
3535
]
3636

3737
[tool.uv.sources]
38-
connectomics = { path = "pytorch_connectomics", editable = true }
38+
"pytorch-connectomics" = { path = "pytorch_connectomics", editable = true }
3939

4040
[dependency-groups]
4141
dev = [

pytorch_connectomics

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit 20ccfde6f85868351b00d7b795d4cf89a251d6be
1+
Subproject commit 0a0dceb8bc03e0afbb843aef17181c9f854209da

scripts/bootstrap.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ if (Test-Path (Join-Path $pytcDir '.git')) {
2020
Push-Location $pytcDir
2121
git fetch origin *> $null
2222
$currentCommit = (git rev-parse HEAD) -replace '\s', ''
23-
$targetCommit = '20ccfde'
23+
$targetCommit = '0a0dceb'
2424
if ($currentCommit -ne $targetCommit) {
2525
git checkout $targetCommit
2626
}
2727
Pop-Location
2828
} else {
29-
git clone 'https://github.com/zudi-lin/pytorch_connectomics.git' $pytcDir
29+
git clone 'https://github.com/PytorchConnectomics/pytorch_connectomics.git' $pytcDir
3030
Push-Location $pytcDir
31-
git checkout '20ccfde'
31+
git checkout '0a0dceb'
3232
Pop-Location
3333
}
3434

scripts/setup_pytorch_connectomics.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ if ! command -v git >/dev/null 2>&1; then
77
exit 1
88
fi
99

10-
PYTORCH_CONNECTOMICS_COMMIT="20ccfde"
11-
REPO_URL="https://github.com/zudi-lin/pytorch_connectomics.git"
10+
PYTORCH_CONNECTOMICS_COMMIT="0a0dceb"
11+
REPO_URL="https://github.com/PytorchConnectomics/pytorch_connectomics.git"
1212
PYTORCH_CONNECTOMICS_DIR="pytorch_connectomics"
1313

1414
if [ -d "${PYTORCH_CONNECTOMICS_DIR}/.git" ]; then

server_api/main.py

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,33 +90,88 @@ def health():
9090

9191

9292
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
93-
PYTC_CONFIG_ROOT = BASE_DIR / "pytorch_connectomics" / "configs"
94-
PYTC_BUILD_FILE = (
95-
BASE_DIR / "pytorch_connectomics" / "connectomics" / "model" / "build.py"
93+
PYTC_ROOT = BASE_DIR / "pytorch_connectomics"
94+
PYTC_CONFIG_ROOTS = (
95+
PYTC_ROOT / "tutorials",
96+
PYTC_ROOT / "configs",
9697
)
98+
PYTC_CONFIG_SUFFIXES = (".yaml", ".yml")
99+
100+
101+
def _iter_existing_config_roots():
102+
for root in PYTC_CONFIG_ROOTS:
103+
if root.exists() and root.is_dir():
104+
yield root
97105

98106

99107
def _list_pytc_configs() -> List[str]:
100-
if not PYTC_CONFIG_ROOT.exists():
101-
return []
102-
return sorted(
103-
[
104-
str(path.relative_to(PYTC_CONFIG_ROOT)).replace("\\", "/")
105-
for path in PYTC_CONFIG_ROOT.rglob("*.yaml")
106-
]
108+
configs = []
109+
for root in _iter_existing_config_roots():
110+
for suffix in PYTC_CONFIG_SUFFIXES:
111+
for path in root.rglob(f"*{suffix}"):
112+
configs.append(str(path.relative_to(PYTC_ROOT)).replace("\\", "/"))
113+
return sorted(set(configs))
114+
115+
116+
def _is_relative_to(path: pathlib.Path, root: pathlib.Path) -> bool:
117+
try:
118+
path.relative_to(root)
119+
return True
120+
except ValueError:
121+
return False
122+
123+
124+
def _is_valid_config_path(path: pathlib.Path) -> bool:
125+
if not path.is_file():
126+
return False
127+
if path.suffix.lower() not in PYTC_CONFIG_SUFFIXES:
128+
return False
129+
if not _is_relative_to(path, PYTC_ROOT.resolve()):
130+
return False
131+
return any(
132+
_is_relative_to(path, root.resolve()) for root in _iter_existing_config_roots()
107133
)
108134

109135

136+
def _resolve_requested_config(path: str) -> Optional[pathlib.Path]:
137+
if not path:
138+
return None
139+
140+
normalized = path.replace("\\", "/").strip()
141+
if not normalized or normalized.startswith("/"):
142+
return None
143+
if ".." in pathlib.PurePosixPath(normalized).parts:
144+
return None
145+
146+
candidates = [(PYTC_ROOT / normalized).resolve()]
147+
for root in _iter_existing_config_roots():
148+
candidates.append((root / normalized).resolve())
149+
150+
for candidate in candidates:
151+
if _is_valid_config_path(candidate):
152+
return candidate
153+
return None
154+
155+
110156
def _read_model_architectures() -> List[str]:
111-
if not PYTC_BUILD_FILE.exists():
112-
return []
113-
text = PYTC_BUILD_FILE.read_text(encoding="utf-8", errors="ignore")
114-
match = re.search(r"MODEL_MAP\s*=\s*{(.*?)}", text, re.S)
115-
if not match:
116-
return []
117-
block = match.group(1)
118-
keys = re.findall(r"'([^']+)'\s*:", block)
119-
return sorted(set(keys))
157+
# Prefer runtime registry from the installed connectomics package.
158+
try:
159+
from connectomics.models.arch import list_architectures
160+
161+
architectures = list_architectures()
162+
if architectures:
163+
return sorted(set(architectures))
164+
except Exception:
165+
pass
166+
167+
# Fallback: parse decorator registrations from source files.
168+
pattern = re.compile(r"""@register_architecture\(\s*['"]([^'"]+)['"]\s*\)""")
169+
architectures = []
170+
arch_root = PYTC_ROOT / "connectomics" / "models" / "arch"
171+
for py_file in arch_root.rglob("*.py"):
172+
text = py_file.read_text(encoding="utf-8", errors="ignore")
173+
architectures.extend(pattern.findall(text))
174+
return sorted(set(architectures))
120175

121176

122177
@app.get("/pytc/configs")
@@ -131,17 +186,12 @@ def list_pytc_configs():
131186
def get_pytc_config(path: str):
132187
if not path:
133188
raise HTTPException(status_code=400, detail="Config path is required.")
134-
if ".." in path or path.startswith("/"):
135-
raise HTTPException(status_code=400, detail="Invalid config path.")
136-
requested = (PYTC_CONFIG_ROOT / path).resolve()
137-
if not str(requested).startswith(str(PYTC_CONFIG_ROOT.resolve())):
138-
raise HTTPException(status_code=400, detail="Invalid config path.")
139-
if not requested.is_file():
189+
requested = _resolve_requested_config(path)
190+
if requested is None:
140191
raise HTTPException(status_code=404, detail="Config not found.")
141-
if requested.suffix.lower() != ".yaml":
142-
raise HTTPException(status_code=400, detail="Config must be a YAML file.")
143192
content = requested.read_text(encoding="utf-8", errors="ignore")
144-
return {"path": path, "content": content}
193+
canonical_path = str(requested.relative_to(PYTC_ROOT)).replace("\\", "/")
194+
return {"path": canonical_path, "content": content}
145195

146196

147197
@app.get("/pytc/architectures")

server_pytc/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ async def get_tensorboard_url():
9898
async def start_model_inference(req: Request):
9999
req = await req.json()
100100
print("start model inference")
101-
# log_dir = req["log_dir"]
102-
start_inference(req)
101+
return start_inference(req)
103102

104103

105104
@app.post("/stop_model_inference")

0 commit comments

Comments
 (0)