Skip to content

Utilities

Constants

lsp_utils.HOST_ARCH module-attribute

HOST_ARCH = get_host_arch()

The native architecture of the host machine: "x32", "x64" or "arm64".

Unlike sublime.arch(), it reports the architecture of the machine rather than of Sublime Text on Windows, for example when an x64 build of Sublime Text runs emulated on an ARM64 machine.

lsp_utils.SETTINGS_FILENAME module-attribute

SETTINGS_FILENAME = 'lsp_utils.sublime-settings'

The name of the lsp_utils settings file.

Install directories

lsp_utils.InstallDirectory

InstallDirectory(parent: Path, name: str, *hash_parts: bytes | str, legacy_dirs: Sequence[Path] = ())

A versioned installation directory of a server, named <parent>/<name>-<hash>.

The hash covers everything that affects the installation (for example the server version or the contents of a lock file) so an update installs into a new directory instead of replacing files of a server that might still be running. Installation directories of the same name that are no longer current are removed in the background once they were last used by an earlier run of Sublime Text. Removal that fails (for example because files are locked on Windows) is retried during the next run.

Example of an LspPlugin that downloads a server:

class MyPlugin(LspPlugin):
    @classmethod
    def on_pre_start_async(cls, context: OnPreStartContext) -> None:
        install_dir = InstallDirectory(cls.plugin_storage_path, 'server', SERVER_VERSION)
        server_dir = install_dir.install(download_and_extract_server)
        context.variables['server_path'] = str(server_dir / 'bin' / 'server')

Create an installation directory of the given name, with a hash of hash_parts.

Parameters:

Name Type Description Default
parent Path

The directory containing the installation directories. Typically the package storage.

required
name str

The name of the installation directories, without the hash. Must not be shared with other kinds of installations within the same parent.

required
hash_parts bytes | str

All inputs that affect the installation.

()
legacy_dirs Sequence[Path]

Unversioned installation directories used previously, to remove once unused.

()

path instance-attribute

path = parent / f'{name}-{compute_install_hash(*hash_parts)}'

The path to the installation directory. Depends on the hash.

is_installed

is_installed() -> bool

Return whether the installation into path has completed.

install

install(installer: Callable[[Path], None]) -> Path

Install into path unless already installed, and record it as used.

Call this before every start of the server so that the directory isn't removed while in use.

Parameters:

Name Type Description Default
installer Callable[[Path], None]

Called with the empty installation directory to install into. Should raise on failure.

required

Returns:

Type Description
Path

The path to the complete installation.

Downloads

Installation of servers downloaded from a URL.

Combine with InstallDirectory so that every version is installed into its own directory.

lsp_utils.UnsupportedPlatformError

Bases: RuntimeError

Raised when no asset is available for the current platform and architecture.

lsp_utils.PlatformAssets module-attribute

PlatformAssets = TypedDict('PlatformAssets', {'linux': str, 'linux-arm64': str, 'linux-x32': str, 'linux-x64': str, 'osx': str, 'osx-arm64': str, 'osx-x64': str, 'windows': str, 'windows-arm64': str, 'windows-x32': str, 'windows-x64': str}, total=False)

Assets (file names or URLs) by <platform>-<arch>, for select_platform_asset.

A key of just the <platform> matches any architecture of that platform.

lsp_utils.select_platform_asset

select_platform_asset(assets: PlatformAssets) -> str

Return the asset for the current platform and architecture.

Parameters:

Name Type Description Default
assets PlatformAssets

File names or URLs by <platform>-<arch> (for example osx-arm64). A key of just the <platform> matches any architecture of that platform.

required

Raises:

Type Description
UnsupportedPlatformError

When there is no asset for the current platform and architecture.

lsp_utils.download_and_extract

download_and_extract(url: str, target_directory: Path, *, filename: str | None = None, executables: Iterable[str] = (), sha256: str | None = None) -> None

Download a file into a directory, extracting or decompressing it depending on its file name.

  • An archive (.zip, .vsix, .tar.gz, .tgz, .tar.bz2 or .tar.xz) is extracted. When all its files are in a single root directory, the contents of that directory are extracted.
  • A file compressed with gzip (.gz) is decompressed and stored under filename without the .gz extension.
  • Any other file is stored as is under filename.

An activity indicator is shown in the status bar of the active window in the meantime.

Typically called as the installer of an InstallDirectory:

VERSION = '1.2.3'
URL = f'https://example.com/server/{VERSION}/server-{sublime.platform()}.zip'

install_dir = InstallDirectory(storage_path, 'server', VERSION)
install_dir.install(lambda path: download_and_extract(URL, path, executables=['bin/server']))

For GitHub releases, see download_and_extract_github_asset.

Parameters:

Name Type Description Default
url str

The URL to download.

required
target_directory Path

The existing directory to store the file or its extracted contents in.

required
filename str | None

The file name of the download, which determines how it's handled and what a file that isn't an archive is stored as. Defaults to the last segment of the URL path. For example, pass server.gz to store a gzipped download as server.

None
executables Iterable[str]

Paths, relative to target_directory, of files to make executable once downloaded. Zip archives don't preserve the executable permission, for example.

()
sha256 str | None

The expected SHA-256 hex digest of the download, verified before it's used.

None

lsp_utils.download_file

download_file(url: str, target_path: Path) -> None

Download given URL to the specified target file path.

lsp_utils.extract_archive

extract_archive(archive_file: Path, target_directory: Path) -> Path | None

Extract all files from an archive.

Parameters:

Name Type Description Default
archive_file Path

Path to the archive file to extract.

required
target_directory Path

Directory where files will be extracted.

required

Returns:

Type Description
Path | None

Path to the top-level directory within extracted files or None. If the archive contains all files in a single root directory then returns path to that directory. If there is no single parent then returns None (means that files are directly under target_directory).

lsp_utils.http_request

http_request(url: str, *, method: str = 'GET') -> HTTPResponse

Send an HTTP request with the User-Agent of lsp_utils and a timeout. Use the response as a context manager.

GitHub releases

Installation of servers distributed as GitHub release assets.

Combine with InstallDirectory so that every release is installed into its own directory.

lsp_utils.download_and_extract_github_asset

download_and_extract_github_asset(repo: str, tag: str, asset: str, target_directory: Path, *, executables: Iterable[str] = (), filename: str | None = None, sha256: str | None = None) -> None

Download an asset of a GitHub release into a directory.

Archives are extracted and gzipped files decompressed, the same as with download_and_extract.

Typically called as the installer of an InstallDirectory:

TAG = 'v1.2.3'
ASSETS: PlatformAssets = {
    'linux-x64': 'server-x86_64-unknown-linux-gnu.tar.gz',
    'osx-arm64': 'server-aarch64-apple-darwin.tar.gz',
    'windows-x64': 'server-x86_64-pc-windows-msvc.zip',
}
BINARY = 'server.exe' if sublime.platform() == 'windows' else 'server'

install_dir = InstallDirectory(storage_path, 'server', TAG)
server_dir = install_dir.install(lambda path: download_and_extract_github_asset(
    'owner/server', TAG, select_platform_asset(ASSETS), path, executables=[BINARY]))
binary_path = server_dir / BINARY

Parameters:

Name Type Description Default
repo str

The repository, as owner/name.

required
tag str

The tag of the release.

required
asset str

The file name of the asset.

required
target_directory Path

The existing directory to store the asset or its extracted contents in.

required
executables Iterable[str]

Paths, relative to target_directory, of files to make executable once downloaded. Zip archives don't preserve the executable permission, for example.

()
filename str | None

The file name of the download, which determines how it's handled and what a file that isn't an archive is stored as. Defaults to asset.

None
sha256 str | None

The expected SHA-256 hex digest of the asset, verified before it's used.

None

lsp_utils.fetch_latest_github_release_tag

fetch_latest_github_release_tag(repo: str) -> str

Return the tag of the latest release of a GitHub repository.

Uses the redirect of the "latest release" page so that it's not subject to the rate limit of the GitHub API. Avoid calling it on every start of the server; check for updates periodically instead.

Parameters:

Name Type Description Default
repo str

The repository, as owner/name.

required

Helpers

prepend_to_path

prepend_to_path(path: str | list[str] | None, extra_paths: Sequence[str]) -> str

Return path (a PATH string or a list of its entries) with extra_paths put first.

Empty entries are dropped and entries of extra_paths that are already in path are moved to the front instead of being added again.

run_command_sync

run_command_sync(args: Sequence[str | PathLike[str]], cwd: str | PathLike[str] | None = None, env: dict[str, str] | None = None, extra_env: dict[str, str] | None = None, extra_paths: list[str] | None = None, *, shell: bool = is_windows) -> tuple[str, str | None]

Run the given command synchronously.

Returns:

Type Description
tuple[str, str | None]

A two-element tuple with the returned value and an optional error. If running the command has failed, the first tuple element will be empty string and the second will contain the potential stderr output. If the command has succeeded then the second tuple element will be None.

run_command_async

run_command_async(args: Sequence[str | PathLike[str]], on_success: StringCallback, on_error: StringCallback, **kwargs: Any) -> None

Run the given command asynchronously.

On success calls the provided on_success callback with the value the the command has returned. On error calls the provided on_error callback with the potential stderr output.

decode_bytes

decode_bytes(data: bytes) -> str

Decode provided bytes using utf-8 decoding, ignoring potential decoding errors.

rmtree_ex

rmtree_ex(path: str | Path, *, ignore_errors: bool = False) -> None

Remove a directory tree like shutil.rmtree, including entries that shutil.rmtree fails to remove.

Removes read-only files and directories that can't be listed, and paths longer than 255 characters on Windows.

Parameters:

Name Type Description Default
path str | Path

The directory to remove.

required
ignore_errors bool

Ignore entries that can't be removed even after granting permissions.

False

try_rmtree

try_rmtree(path: Path) -> bool

Remove the directory tree, logging instead of raising on failure.

Returns:

Type Description
bool

True if the directory no longer exists.

version_to_string

version_to_string(version: SemanticVersion) -> str

Return a string representation of a version tuple.