Skip to content

Commit ba63fe8

Browse files
authored
Merge pull request #122 from chigwell/121-fix-bug-generate-a-session-string-refusing-to-start
fix: enhance installation guard to support editable source checkouts
2 parents 74f99f5 + 7d75941 commit ba63fe8

2 files changed

Lines changed: 86 additions & 2 deletions

File tree

telegram_mcp/install_guard.py

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DistributionIdentity:
3131
urls: tuple[str, ...] = ()
3232
summary: str = ""
3333
direct_url: str = ""
34+
source_root: Path | None = None
3435

3536
@classmethod
3637
def from_distribution(cls, dist: metadata.Distribution) -> "DistributionIdentity":
@@ -67,7 +68,8 @@ def from_distribution(cls, dist: metadata.Distribution) -> "DistributionIdentity
6768
maintainers=maintainers,
6869
urls=urls,
6970
summary=package_metadata.get("Summary", ""),
70-
direct_url=direct_url,
71+
direct_url=direct_url or "",
72+
source_root=_distribution_source_root(dist),
7173
)
7274

7375

@@ -84,6 +86,52 @@ def _project_root_declares_distribution_name(path: Path) -> bool:
8486
return f'name = "{DISTRIBUTION_NAME}"' in pyproject_text
8587

8688

89+
def _resolve_path(path: Path) -> Path:
90+
try:
91+
return path.resolve()
92+
except OSError:
93+
return path
94+
95+
96+
def _candidate_is_project_root(path: Path) -> bool:
97+
return _project_root_declares_distribution_name(_resolve_path(path))
98+
99+
100+
def _distribution_source_root(dist: metadata.Distribution) -> Path | None:
101+
"""Return an editable/source-checkout root for installer metadata.
102+
103+
``uv sync`` installs the project editably. In that mode ``importlib.metadata``
104+
can resolve the active distribution to ``telegram_mcp.egg-info`` in the
105+
checkout, while the PEP 610 ``direct_url.json`` file lives in the
106+
environment's ``.dist-info`` directory. Treating the adjacent project root as
107+
source provenance keeps the PyPI-collision guard strict for normal installs
108+
without blocking cloned checkouts.
109+
"""
110+
111+
dist_path = getattr(dist, "_path", None)
112+
if dist_path is not None:
113+
metadata_path = Path(dist_path)
114+
if metadata_path.name.endswith(".egg-info"):
115+
candidate = metadata_path.parent
116+
if _candidate_is_project_root(candidate):
117+
return _resolve_path(candidate)
118+
119+
files = getattr(dist, "files", None)
120+
locate_file = getattr(dist, "locate_file", None)
121+
if not files or not callable(locate_file):
122+
return None
123+
124+
for package_file in files:
125+
if Path(str(package_file)) != Path("pyproject.toml"):
126+
continue
127+
128+
candidate = Path(locate_file(package_file)).parent
129+
if _candidate_is_project_root(candidate):
130+
return _resolve_path(candidate)
131+
132+
return None
133+
134+
87135
def _direct_url_json(direct_url: str) -> dict:
88136
if not direct_url:
89137
return {}
@@ -116,7 +164,10 @@ def _direct_url_is_explicit_source_install(direct_url: str) -> bool:
116164

117165

118166
def _looks_like_explicit_source_install(identity: DistributionIdentity) -> bool:
119-
return _direct_url_is_explicit_source_install(identity.direct_url)
167+
return (
168+
_direct_url_is_explicit_source_install(identity.direct_url)
169+
or identity.source_root is not None
170+
)
120171

121172

122173
def _format_unsafe_installation_message(identity: DistributionIdentity) -> str:

tests/test_install_guard.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,39 @@ def raise_missing(_distribution_name):
101101
install_guard.assert_safe_distribution()
102102

103103

104+
def test_install_guard_allows_uv_editable_source_checkout_without_direct_url(
105+
monkeypatch, tmp_path
106+
):
107+
(tmp_path / "pyproject.toml").write_text(
108+
'[project]\nname = "telegram-mcp"\n',
109+
encoding="utf-8",
110+
)
111+
(tmp_path / "telegram_mcp.egg-info").mkdir()
112+
113+
class FakeDistribution:
114+
version = "source-version"
115+
116+
def __init__(self):
117+
self._path = tmp_path / "telegram_mcp.egg-info"
118+
self.files = []
119+
self.metadata = Message()
120+
self.metadata["Name"] = "telegram-mcp"
121+
self.metadata["Version"] = "source-version"
122+
self.metadata["Author"] = "chigwell, l1v0n1"
123+
self.metadata["Project-URL"] = "Homepage, https://github.com/chigwell/telegram-mcp"
124+
125+
def read_text(self, _filename):
126+
return None
127+
128+
monkeypatch.setattr(
129+
install_guard.metadata,
130+
"distribution",
131+
lambda _distribution_name: FakeDistribution(),
132+
)
133+
134+
install_guard.assert_safe_distribution()
135+
136+
104137
def test_install_guard_raises_for_untrusted_installed_distribution(monkeypatch):
105138
class FakeDistribution:
106139
version = "0.6.3"

0 commit comments

Comments
 (0)