From 60afe4fd53d4b2d0aab2e452022334f47affb5c0 Mon Sep 17 00:00:00 2001 From: Alex Carney Date: Sat, 5 Oct 2024 21:50:45 +0100 Subject: [PATCH] fix: return ``None`` for non ``file:`` URIs --- docs/source/howto/migrate-to-v2.rst | 5 +++++ docs/source/reference/uris.rst | 16 ++++++++++++++++ pygls/uris.py | 10 +++++++--- pygls/workspace/text_document.py | 18 ++++++++++-------- pygls/workspace/workspace.py | 16 +++++++++------- 5 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 docs/source/reference/uris.rst diff --git a/docs/source/howto/migrate-to-v2.rst b/docs/source/howto/migrate-to-v2.rst index 182d9fb9..7a4b7de8 100644 --- a/docs/source/howto/migrate-to-v2.rst +++ b/docs/source/howto/migrate-to-v2.rst @@ -25,6 +25,11 @@ Python Support *pygls v2* removes support for Python 3.8 and adds support for Python 3.13 (with the GIL, you are welcome to try the free-threaded version just note that it has not been tested yet!) +URI Handling +------------ + +The :func:`pygls.uris.to_fs_path` will now return ``None`` for URIs that do not have a ``file:`` scheme. + Removed Deprecated Functions ---------------------------- diff --git a/docs/source/reference/uris.rst b/docs/source/reference/uris.rst new file mode 100644 index 00000000..98f94966 --- /dev/null +++ b/docs/source/reference/uris.rst @@ -0,0 +1,16 @@ +URIs +==== + +.. currentmodule:: pygls.uris + +.. autofunction:: from_fs_path + +.. autofunction:: to_fs_path + +.. autofunction:: uri_scheme + +.. autofunction:: uri_with + +.. autofunction:: urlparse + +.. autofunction:: urlunparse diff --git a/pygls/uris.py b/pygls/uris.py index 8c40f70b..b0c34f1d 100644 --- a/pygls/uris.py +++ b/pygls/uris.py @@ -21,6 +21,8 @@ https://github.com/Microsoft/vscode-uri/blob/e59cab84f5df6265aed18ae5f43552d3eef13bb9/lib/index.ts """ +from __future__ import annotations + from typing import Optional, Tuple import re @@ -75,20 +77,22 @@ def from_fs_path(path: str): return None -def to_fs_path(uri: str): +def to_fs_path(uri: str) -> str | None: """ Returns the filesystem path of the given URI. Will handle UNC paths and normalize windows drive letters to lower-case. Also uses the platform specific path separator. Will *not* validate the path for invalid characters and semantics. - Will *not* look at the scheme of this URI. """ try: # scheme://netloc/path;parameters?query#fragment scheme, netloc, path, _, _, _ = urlparse(uri) - if netloc and path and scheme == "file": + if scheme != "file": + return None + + if netloc and path: # unc path: file://shares/c$/far/boo value = f"//{netloc}{path}" diff --git a/pygls/workspace/text_document.py b/pygls/workspace/text_document.py index 7beb6ca4..023569a3 100644 --- a/pygls/workspace/text_document.py +++ b/pygls/workspace/text_document.py @@ -19,12 +19,13 @@ import io import logging import os +import pathlib import re from typing import List, Optional, Pattern from lsprotocol import types -from pygls.uris import to_fs_path +from pygls.uris import urlparse, to_fs_path from .position_codec import PositionCodec # TODO: this is not the best e.g. we capture numbers @@ -47,9 +48,10 @@ def __init__( ): self.uri = uri self.version = version - path = to_fs_path(uri) - if path is None: - raise Exception("`path` cannot be None") + + if (path := to_fs_path(uri)) is None: + _, _, path, *_ = urlparse(uri) + self.path = path self.language_id = language_id self.filename: Optional[str] = os.path.basename(self.path) @@ -177,10 +179,10 @@ def offset_at_position(self, client_position: types.Position) -> int: @property def source(self) -> str: - if self._source is None: - with io.open(self.path, "r", encoding="utf-8") as f: - return f.read() - return self._source + if self._source is None and self.path is not None: + return pathlib.Path(self.path).read_text(encoding="utf-8") + + return self._source or "" def word_at_position( self, diff --git a/pygls/workspace/workspace.py b/pygls/workspace/workspace.py index 0bd68e5b..246af1d2 100644 --- a/pygls/workspace/workspace.py +++ b/pygls/workspace/workspace.py @@ -47,10 +47,7 @@ def __init__( self._root_uri = root_uri if self._root_uri is not None: self._root_uri_scheme = uri_scheme(self._root_uri) - root_path = to_fs_path(self._root_uri) - if root_path is None: - raise Exception("Couldn't get `root_path` from `root_uri`") - self._root_path = root_path + self._root_path = to_fs_path(self._root_uri) else: self._root_path = None self._sync_kind = sync_kind @@ -151,9 +148,14 @@ def get_text_document(self, doc_uri: str) -> TextDocument: return self._text_documents.get(doc_uri) or self._create_text_document(doc_uri) def is_local(self): - return ( - self._root_uri_scheme == "" or self._root_uri_scheme == "file" - ) and os.path.exists(self._root_path) + + if self._root_uri_scheme not in {"", "file"}: + return False + + if (path := self._root_path) is None: + return False + + return os.path.exists(path) def put_notebook_document(self, params: types.DidOpenNotebookDocumentParams): notebook = params.notebook_document