@@ -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+
87135def _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
118166def _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
122173def _format_unsafe_installation_message (identity : DistributionIdentity ) -> str :
0 commit comments