def as_lockfile_entry(self, project_root: Path) -> dict[str, Any]:
        """Build a lockfile entry dictionary for the candidate."""
        version = str(self.version)
        if not self.req.is_pinned:
            try:
                version = str(comparable_version(version))
            except InvalidVersion as e:
                raise RequirementError(f"Invalid version for {self.req.as_line()}: {e}") from None
        result = {
            "name": normalize_name(self.name),
            "version": version,
            "extras": sorted(self.req.extras or ()),
            "requires_python": str(self.requires_python),
            "editable": self.req.editable,
            "subdirectory": getattr(self.req, "subdirectory", None),
        }
        if self.req.is_vcs:
            result.update(
                {
                    self.req.vcs: self.req.repo,
                    "ref": self.req.ref,
                }
            )
            if not self.req.editable:
                result.update(revision=self.get_revision())
        elif not self.req.is_named:
            if self.req.is_file_or_url and self.req.is_local:
                self.req._root = project_root
                result.update(path=self.req.str_path)
            else:
                result.update(url=self.req.url)
        return {k: v for k, v in result.items() if v}
Build a lockfile entry dictionary for the candidate.
src/pdm/models/candidates.py