Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ updates:
labels:
- 'dependencies'
- 'debt'
- 'no-changelog'
directory: '/'
schedule:
interval: 'weekly'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ jobs:
with:
mode: minimum
count: 1
labels: 'bug, debt, dependencies, feature-request, no-changelog'
labels: 'bug, debt, dependencies, feature-request'
108 changes: 0 additions & 108 deletions CHANGELOG.md

This file was deleted.

18 changes: 13 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,19 @@ Extensions consume this repository at a pinned Git commit and build both the
TypeScript and Python runtimes from that revision. The pinned source revision,
not the version strings in the package manifests, is the compatibility identity.

The private npm manifest and Python package metadata retain fixed version values
needed for local builds and installation. Ordinary source changes do not require
changing or synchronizing those values. The default Python server reports its
installed package version for diagnostics, but that value is not a source
provenance guarantee. The npm and Python packages are not published.
The private npm manifest and Python distribution metadata use the internal
build identity `vscode-python-tool-internal` and retain fixed version
values needed for local builds and installation. This identity is not a consumer
import API. Ordinary source changes do not require changing or synchronizing
those values. The default Python server reports that internal distribution's
installed version for diagnostics (or `v0.0.0-dev` when its metadata is absent),
but that value is not a source provenance guarantee. Neither runtime is
published to a package registry.

Record consumer-visible changes and upgrade instructions in pull request
descriptions. For breaking changes, add focused migration documentation.
There is no separate changelog to maintain; historical entries remain in Git
history.

Maintainers may optionally tag a reviewed commit and create a
[GitHub Release](https://github.com/microsoft/vscode-common-python-lsp/releases)
Expand Down
94 changes: 74 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Shared Python and TypeScript libraries for VS Code Python tool extensions

```
vscode-common-python-lsp/
├── python/ # Python package (bundled server-side)
│ ├── vscode_common_python_lsp/ # Package source
├── python/ # Python source (bundled server-side)
│ ├── vscode_common_python_lsp/ # Import package source
│ │ └── __init__.py
│ ├── tests/ # Python tests (pytest)
│ ├── pyproject.toml # Package metadata & build config
│ ├── pyproject.toml # Local installation metadata & build config
│ └── requirements-dev.txt # Dev/test dependencies
│
├── typescript/ # TypeScript package (VS Code client-side)
│ ├── src/ # Package source
├── typescript/ # TypeScript source (VS Code client-side)
│ ├── src/ # Public entry point and implementation
│ │ └── index.ts
│ ├── tests/ # TypeScript tests (mocha)
│ ├── package.json
Expand All @@ -31,7 +31,7 @@ vscode-common-python-lsp/
└── README.md
```

## Python Package
## Python Source

The `vscode_common_python_lsp` Python package provides server-side utilities shared
across all five extensions: path resolution, context managers, tool execution runners,
Expand All @@ -45,17 +45,17 @@ pip install -e ".[dev]"
pytest tests/
```

## TypeScript Package
## TypeScript Source

The `vscode-common-python-lsp` TypeScript package provides VS Code client-side
The TypeScript source provides VS Code client-side
utilities: extension activation, server lifecycle, settings management, Python
interpreter resolution, and logging.

### Development

```bash
cd typescript
npm install
npm ci
npm run lint
npm run build
npm test
Expand All @@ -80,18 +80,61 @@ git submodule update --init --recursive
Clone a consuming extension with `--recurse-submodules`, or run the update
command above after cloning.

Reference the TypeScript package from the submodule in `package.json`:
#### TypeScript: build, type-check, then bundle

```json
{
"dependencies": {
"@vscode/common-python-lsp": "file:external/vscode-common-python-lsp/typescript"
}
}
The extension does not declare a shared-runtime npm dependency or alias. From
the extension root, install its own locked development tools and the checkout's
locked dependencies, then build the shared JavaScript and declarations **before**
type-checking or bundling the extension:

```powershell
npm ci
npm --prefix .\external\vscode-common-python-lsp\typescript ci
npm --prefix .\external\vscode-common-python-lsp\typescript run build
```

In the extension's `src/extension.ts`, import exported symbols from the built
public entry point (not individual implementation files or a bare package name):

```typescript
import { createToolContext } from '../external/vscode-common-python-lsp/typescript/dist';
```

Use the extension's existing TypeScript configuration and development type
dependencies, including `@types/vscode` and `@types/node`, to type-check its
`src/` files. The shared declarations resolve their dependencies from the
checkout's `typescript/node_modules`; the extension's `rootDir` can remain
`src`. For example, with TypeScript already installed by the extension:

```powershell
npx --no-install tsc --noEmit
```

Install the Python package directly from the same submodule commit when
building the extension bundle. For example, in `noxfile.py`:
Bundle the source-relative entry and its JavaScript dependency closure into the
extension. For example, an extension already using esbuild can run this from
the extension root after the steps above:

```powershell
npx --no-install esbuild .\src\extension.ts --bundle --platform=node --format=cjs --external:vscode --outfile=.\dist\extension.js
```

VS Code supplies `vscode`, and esbuild leaves Node built-ins external. Do not
externalize the shared entry or its npm dependencies: they must resolve from
the checkout and be included in the bundle. Merely running `tsc` does not bundle
them; shipping only the extension's emitted JavaScript would leave a relative
runtime dependency on the checkout. Other bundlers must preserve the same
resolution and packaging behavior. Rebuild both the shared entry and extension
bundle whenever the pinned checkout changes.

When migrating an existing consumer, remove its former shared-runtime
dependency/alias, update its imports and regenerate its lockfile without that
dependency. Review the consumer's own build and packaging rules; changing this
repository's metadata alone does not migrate an extension.

#### Python: install from the same checkout

Install the Python source directly from the same submodule commit when building
the extension bundle. For example, in `noxfile.py`:

```python
import pathlib
Expand All @@ -118,8 +161,19 @@ def install_bundled_libs(session: nox.Session) -> None:
)
```

The package manifests support building from the submodule. This repository no
longer publishes packages to npm or PyPI.
The `--no-deps` example preserves the consumer's runtime dependency policy: the
extension must also bundle the dependencies declared in `python/pyproject.toml`
using its own reviewed requirements/locks. Keep the installed distribution
metadata alongside `vscode_common_python_lsp`; the default server uses its
version for diagnostics, or `v0.0.0-dev` if that metadata is absent. Python
imports continue to use `vscode_common_python_lsp`.

Both manifests use `vscode-python-tool-internal` solely as internal
local build/install metadata. npm remains private, and Python metadata is marked
`Private :: Do Not Upload`. Local-path pip installation and local wheel builds
remain supported. Neither identity is a registry-facing consumption API: do not
add a bare dependency or registry install for it. This repository publishes
neither npm nor PyPI packages.

### Dependabot updates

Expand Down
10 changes: 8 additions & 2 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# vscode-common-python-lsp (Python)
# Shared Python source

Shared Python utilities for VS Code Python tool extensions.

See the [main README](../README.md) for full documentation.
Install this directory from a pinned repository checkout. Its internal
distribution name, `vscode-python-tool-internal`, supports local
installation and wheel builds only; it is not a published product. The Python
import namespace remains `vscode_common_python_lsp`.

See the [main README](../README.md#consuming-in-extensions) for local-path
installation and bundling instructions.
5 changes: 3 additions & 2 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "vscode-common-python-lsp"
name = "vscode-python-tool-internal"
version = "0.8.3"
description = "Shared Python utilities for VS Code Python tool extensions"
description = "Internal source-only Python utilities for VS Code Python tool extensions"
readme = "README.md"
license = "MIT"
requires-python = ">=3.10"
classifiers = ["Private :: Do Not Upload"]
authors = [
{ name = "Microsoft Corporation" },
]
Expand Down
45 changes: 38 additions & 7 deletions python/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1247,10 +1247,11 @@ def test_creates_default_server(self):
patch(
"vscode_common_python_lsp.server.importlib.metadata.version",
return_value="1.2.3",
),
) as mock_version,
):
mock_cls.return_value = MagicMock()
ts = ToolServer(BASIC_CONFIG)
mock_version.assert_called_once_with("vscode-python-tool-internal")
mock_cls.assert_called_once_with(
name="testtool-server",
version="v1.2.3",
Expand All @@ -1264,22 +1265,52 @@ def test_fallback_version_when_package_not_installed(self):
patch(
"vscode_common_python_lsp.server.importlib.metadata.version",
side_effect=importlib.metadata.PackageNotFoundError(
"vscode-common-python-lsp"
"vscode-python-tool-internal"
),
),
) as mock_version,
):
mock_cls.return_value = MagicMock()
ToolServer(BASIC_CONFIG)
mock_version.assert_called_once_with("vscode-python-tool-internal")
mock_cls.assert_called_once_with(
name="testtool-server",
version="v0.0.0-dev",
max_workers=5,
)

def test_accepts_custom_server(self):
custom = MagicMock()
ts = ToolServer(BASIC_CONFIG, server=custom)
assert ts.server is custom
def test_missing_internal_metadata_does_not_use_other_distributions(self):
def lookup_version(distribution_name):
if distribution_name == "vscode-python-tool-internal":
raise importlib.metadata.PackageNotFoundError(distribution_name)
return "9.9.9"

with (
patch("vscode_common_python_lsp.server.LanguageServer") as mock_cls,
patch(
"vscode_common_python_lsp.server.importlib.metadata.version",
side_effect=lookup_version,
) as mock_version,
):
ToolServer(BASIC_CONFIG)
mock_version.assert_called_once_with("vscode-python-tool-internal")
mock_cls.assert_called_once_with(
name="testtool-server",
version="v0.0.0-dev",
max_workers=5,
)

def test_accepts_custom_server_without_metadata_lookup(self):
with (
patch("vscode_common_python_lsp.server.LanguageServer") as mock_cls,
patch(
"vscode_common_python_lsp.server.importlib.metadata.version"
) as mock_version,
):
custom = MagicMock()
ts = ToolServer(BASIC_CONFIG, server=custom)
assert ts.server is custom
mock_version.assert_not_called()
mock_cls.assert_not_called()

def test_empty_initial_state(self):
ts = _make_server()
Expand Down
Loading
Loading