Skip to content

Commit fcf0ce2

Browse files
committed
PyREPL: Fix missing ANSI_ESCAPE_SEQUENCE
2 parents 445ec1d + bbd2d7d commit fcf0ce2

191 files changed

Lines changed: 45535 additions & 5532 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.devcontainer/Dockerfile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/devcontainers/typescript-node:18-bookworm
1+
FROM mcr.microsoft.com/devcontainers/typescript-node:22-bookworm
22

33
RUN apt-get install -y wget bzip2
44

‎.github/actions/build-vsix/action.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ runs:
2222
using: 'composite'
2323
steps:
2424
- name: Install Node
25-
uses: actions/setup-node@v6
25+
uses: actions/setup-node@v7
2626
with:
2727
node-version: ${{ inputs.node_version }}
2828
cache: 'npm'
@@ -31,10 +31,10 @@ runs:
3131
uses: dtolnay/rust-toolchain@stable
3232

3333
# Jedi LS depends on dataclasses which is not in the stdlib in Python 3.7.
34-
- name: Use Python 3.9 for JediLSP
35-
uses: actions/setup-python@v6
34+
- name: Use Python 3.10 for JediLSP
35+
uses: actions/setup-python@v7
3636
with:
37-
python-version: 3.9
37+
python-version: '3.10'
3838
cache: 'pip'
3939
cache-dependency-path: |
4040
requirements.txt
@@ -93,7 +93,7 @@ runs:
9393
VSIX_NAME: ${{ inputs.vsix_name }}
9494

9595
- name: Upload VSIX
96-
uses: actions/upload-artifact@v5
96+
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
9797
with:
9898
name: ${{ inputs.artifact_name }}
9999
path: ${{ inputs.vsix_name }}

‎.github/actions/lint/action.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ runs:
1010
using: 'composite'
1111
steps:
1212
- name: Install Node
13-
uses: actions/setup-node@v6
13+
uses: actions/setup-node@v7
1414
with:
1515
node-version: ${{ inputs.node_version }}
1616
cache: 'npm'
@@ -36,7 +36,7 @@ runs:
3636
shell: bash
3737

3838
- name: Install Python
39-
uses: actions/setup-python@v6
39+
uses: actions/setup-python@v7
4040
with:
4141
python-version: '3.x'
4242
cache: 'pip'

‎.github/commands.json‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,14 @@
153153
"addLabel": "info-needed",
154154
"removeLabel": "~confirmation-needed",
155155
"comment": "Please diagnose the root cause of the issue by running the command `F1 > Help: Troubleshoot Issue` and following the instructions. Once you have done that, please update the issue with the results.\n\nHappy Coding!"
156+
},
157+
{
158+
"type": "label",
159+
"name": "~spam",
160+
"removeLabel": "~spam",
161+
"addLabel": "spam",
162+
"action": "close",
163+
"reason": "not_planned",
164+
"comment": "Thank you for your submission. This issue has been closed as it doesn't meet our community guidelines or appears to be spam.\n\n**If you believe this was closed in error:**\n- Please review our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- Ensure your issue contains a clear description of the problem or feature request\n- Feel free to open a new issue with appropriate detail if this was a legitimate concern\n\n**For legitimate issues, please include:**\n- Clear description of the problem\n- Steps to reproduce (for bugs)\n- Expected vs actual behavior\n- VS Code version and environment details\n\nThank you for helping us maintain a welcoming and productive community."
156165
}
157166
]

‎.github/instructions/testing-workflow.instructions.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,4 @@ envConfig.inspect
578578
- When mocking `testController.createTestItem()` in unit tests, use `typemoq.It.isAny()` for parameters when testing handler behavior (not ID/label generation logic), but consider using specific matchers (e.g., `It.is((id: string) => id.startsWith('_error_'))`) when the actual values being passed are important for correctness - this balances test precision with maintainability (2)
579579
- Remove unused variables from test code immediately - leftover tracking variables like `validationCallCount` that aren't referenced indicate dead code that should be simplified (1)
580580
- Use `Uri.file(path).fsPath` for both sides of path comparisons in tests to ensure cross-platform compatibility - Windows converts forward slashes to backslashes automatically (1)
581+
- When tests fail with "Cannot stub non-existent property", the method likely moved to a different class during refactoring - find the class that owns the method and test that class directly instead of stubbing on the original class (1)

‎.github/instructions/testing_feature_area.instructions.md‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ This document maps the testing support in the extension: discovery, execution (r
2626
- `src/client/testing/serviceRegistry.ts` — DI/wiring for testing services.
2727
- Workspace orchestration
2828
- `src/client/testing/testController/workspaceTestAdapter.ts` — `WorkspaceTestAdapter` (provider-agnostic entry used by controller).
29+
- **Project-based testing (multi-project workspaces)**
30+
- `src/client/testing/testController/common/testProjectRegistry.ts` — `TestProjectRegistry` (manages project lifecycle, discovery, and nested project handling).
31+
- `src/client/testing/testController/common/projectAdapter.ts` — `ProjectAdapter` interface (represents a single Python project with its own test infrastructure).
32+
- `src/client/testing/testController/common/projectUtils.ts` — utilities for project ID generation, display names, and shared adapter creation.
2933
- Provider adapters
3034
- Unittest
3135
- `src/client/testing/testController/unittest/testDiscoveryAdapter.ts`
@@ -151,6 +155,78 @@ The adapters in the extension don't implement test discovery/run logic themselve
151155
- Settings are consumed by `src/client/testing/common/testConfigurationManager.ts`, `src/client/testing/configurationFactory.ts`, and adapters under `src/client/testing/testController/*` which read settings to build CLI args and env for subprocesses.
152156
- The setting definitions and descriptions are in `package.json` and localized strings in `package.nls.json`.
153157

158+
## Project-based testing (multi-project workspaces)
159+
160+
Project-based testing enables multi-project workspace support where each Python project gets its own test tree root with its own Python environment.
161+
162+
### Architecture
163+
164+
- **TestProjectRegistry** (`testProjectRegistry.ts`): Central registry that:
165+
166+
- Discovers Python projects via the Python Environments API
167+
- Creates and manages `ProjectAdapter` instances per workspace
168+
- Computes nested project relationships and configures ignore lists
169+
- Falls back to "legacy" single-adapter mode when API unavailable
170+
171+
- **ProjectAdapter** (`projectAdapter.ts`): Interface representing a single project with:
172+
- Project identity (ID, name, URI from Python Environments API)
173+
- Python environment with execution details
174+
- Test framework adapters (discovery/execution)
175+
- Nested project ignore paths (for parent projects)
176+
177+
### How it works
178+
179+
1. **Activation**: When the extension activates, `PythonTestController` checks if the Python Environments API is available.
180+
2. **Project discovery**: `TestProjectRegistry.discoverAndRegisterProjects()` queries the API for all Python projects in each workspace.
181+
3. **Nested handling**: `configureNestedProjectIgnores()` identifies child projects and adds their paths to parent projects' ignore lists.
182+
4. **Test discovery**: For each project, the controller calls `project.discoveryAdapter.discoverTests()` with the project's URI. The adapter sets `PROJECT_ROOT_PATH` environment variable for the Python runner.
183+
5. **Python side**:
184+
- For pytest: `get_test_root_path()` in `vscode_pytest/__init__.py` returns `PROJECT_ROOT_PATH` (if set) or falls back to `cwd`.
185+
- For unittest: `discovery.py` uses `PROJECT_ROOT_PATH` as `top_level_dir` and `project_root_path` to root the test tree at the project directory.
186+
6. **Test tree**: Each project gets its own root node in the Test Explorer, with test IDs scoped by project ID using the `@@vsc@@` separator (defined in `projectUtils.ts`).
187+
188+
### Nested project handling: pytest vs unittest
189+
190+
**pytest** supports the `--ignore` flag to exclude paths during test collection. When nested projects are detected, parent projects automatically receive `--ignore` flags for child project paths. This ensures each test appears under exactly one project in the test tree.
191+
192+
**unittest** does not support path exclusion during `discover()`. Therefore, tests in nested project directories may appear under multiple project roots (both the parent and the child project). This is **expected behavior** for unittest:
193+
194+
- Each project discovers and displays all tests it finds within its directory structure
195+
- There is no deduplication or collision detection
196+
- Users may see the same test file under multiple project roots if their project structure has nesting
197+
198+
This approach was chosen because:
199+
200+
1. unittest's `TestLoader.discover()` has no built-in path exclusion mechanism
201+
2. Implementing custom exclusion would add significant complexity with minimal benefit
202+
3. The existing approach is transparent and predictable - each project shows what it finds
203+
204+
### Empty projects and root nodes
205+
206+
If a project discovers zero tests, its root node will still appear in the Test Explorer as an empty folder. This ensures consistent behavior and makes it clear which projects were discovered, even if they have no tests yet.
207+
208+
### Logging prefix
209+
210+
All project-based testing logs use the `[test-by-project]` prefix for easy filtering in the output channel.
211+
212+
### Key files
213+
214+
- Python side:
215+
- `python_files/vscode_pytest/__init__.py` — `get_test_root_path()` function and `PROJECT_ROOT_PATH` environment variable for pytest.
216+
- `python_files/unittestadapter/discovery.py` — `discover_tests()` with `project_root_path` parameter and `PROJECT_ROOT_PATH` handling for unittest discovery.
217+
- `python_files/unittestadapter/execution.py` — `run_tests()` with `project_root_path` parameter and `PROJECT_ROOT_PATH` handling for unittest execution.
218+
- TypeScript: `testProjectRegistry.ts`, `projectAdapter.ts`, `projectUtils.ts`, and the discovery/execution adapters.
219+
220+
### Tests
221+
222+
- `src/test/testing/testController/common/testProjectRegistry.unit.test.ts` — TestProjectRegistry tests
223+
- `src/test/testing/testController/common/projectUtils.unit.test.ts` — Project utility function tests
224+
- `python_files/tests/pytestadapter/test_discovery.py` — pytest PROJECT_ROOT_PATH tests (see `test_project_root_path_env_var()` and `test_symlink_with_project_root_path()`)
225+
- `python_files/tests/unittestadapter/test_discovery.py` — unittest `project_root_path` / PROJECT_ROOT_PATH discovery tests
226+
- `python_files/tests/unittestadapter/test_execution.py` — unittest `project_root_path` / PROJECT_ROOT_PATH execution tests
227+
- `src/test/testing/testController/unittest/testDiscoveryAdapter.unit.test.ts` — unittest discovery adapter PROJECT_ROOT_PATH tests
228+
- `src/test/testing/testController/unittest/testExecutionAdapter.unit.test.ts` — unittest execution adapter PROJECT_ROOT_PATH tests
229+
154230
## Coverage support (how it works)
155231

156232
- Coverage is supported by running the Python helper scripts with coverage enabled and then collecting a coverage payload from the runner.

‎.github/workflows/build.yml‎

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
permissions: {}
1212

1313
env:
14-
NODE_VERSION: 22.17.0
14+
NODE_VERSION: 22.21.1
1515
PYTHON_VERSION: '3.10' # YML treats 3.10 the number as 3.1, so quotes around 3.10
1616
# Force a path with spaces and to test extension works in these scenarios
1717
# Unicode characters are causing 2.7 failures so skip that for now.
@@ -84,12 +84,12 @@ jobs:
8484
# vsix-target: alpine-arm64
8585
steps:
8686
- name: Checkout
87-
uses: actions/checkout@v6
87+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
8888
with:
8989
persist-credentials: false
9090

9191
- name: Checkout Python Environment Tools
92-
uses: actions/checkout@v6
92+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
9393
with:
9494
repository: 'microsoft/python-environment-tools'
9595
path: 'python-env-tools'
@@ -115,7 +115,7 @@ jobs:
115115
runs-on: ubuntu-latest
116116
steps:
117117
- name: Checkout
118-
uses: actions/checkout@v6
118+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
119119
with:
120120
persist-credentials: false
121121

@@ -130,12 +130,12 @@ jobs:
130130
runs-on: ubuntu-latest
131131
steps:
132132
- name: Use Python ${{ env.PYTHON_VERSION }}
133-
uses: actions/setup-python@v6
133+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
134134
with:
135135
python-version: ${{ env.PYTHON_VERSION }}
136136

137137
- name: Checkout
138-
uses: actions/checkout@v6
138+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
139139
with:
140140
persist-credentials: false
141141

@@ -155,7 +155,7 @@ jobs:
155155
python -m pip install --upgrade -r build/test-requirements.txt
156156
157157
- name: Run Pyright
158-
uses: jakebailey/pyright-action@6cabc0f01c4994be48fd45cd9dbacdd6e1ee6e5e # v2.3.3
158+
uses: jakebailey/pyright-action@8ec14b5cfe41f26e5f41686a31eb6012758217ef # v3.0.2
159159
with:
160160
version: 1.1.308
161161
working-directory: 'python_files'
@@ -174,17 +174,17 @@ jobs:
174174
# macOS runners are expensive, and we assume that Ubuntu is enough to cover the Unix case.
175175
os: [ubuntu-latest, windows-latest]
176176
# Run the tests on the oldest and most recent versions of Python.
177-
python: ['3.9', '3.x', '3.13']
177+
python: ['3.10', '3.x', '3.13']
178178

179179
steps:
180180
- name: Checkout
181-
uses: actions/checkout@v6
181+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
182182
with:
183183
path: ${{ env.special-working-directory-relative }}
184184
persist-credentials: false
185185

186186
- name: Use Python ${{ matrix.python }}
187-
uses: actions/setup-python@v6
187+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
188188
with:
189189
python-version: ${{ matrix.python }}
190190

@@ -197,6 +197,9 @@ jobs:
197197
- name: Install test requirements
198198
run: python -m pip install --upgrade -r build/test-requirements.txt
199199

200+
- name: Install pytest
201+
run: python -m pip install --upgrade pytest
202+
200203
- name: Run Python unit tests
201204
run: python python_files/tests/run_all.py
202205

@@ -218,13 +221,13 @@ jobs:
218221
test-suite: [ts-unit, venv, single-workspace, multi-workspace, debugger, functional]
219222
steps:
220223
- name: Checkout
221-
uses: actions/checkout@v6
224+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
222225
with:
223226
path: ${{ env.special-working-directory-relative }}
224227
persist-credentials: false
225228

226229
- name: Checkout Python Environment Tools
227-
uses: actions/checkout@v6
230+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
228231
with:
229232
repository: 'microsoft/python-environment-tools'
230233
path: ${{ env.special-working-directory-relative }}/python-env-tools
@@ -236,7 +239,7 @@ jobs:
236239
sparse-checkout-cone-mode: false
237240

238241
- name: Install Node
239-
uses: actions/setup-node@v6
242+
uses: actions/setup-node@v7
240243
with:
241244
node-version: ${{ env.NODE_VERSION }}
242245
cache: 'npm'
@@ -252,7 +255,7 @@ jobs:
252255
run: npx @vscode/l10n-dev@latest export ./src
253256

254257
- name: Install Python ${{ matrix.python }}
255-
uses: actions/setup-python@v6
258+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
256259
with:
257260
python-version: ${{ matrix.python }}
258261

@@ -308,7 +311,7 @@ jobs:
308311
if: matrix.test-suite == 'venv'
309312
run: |
310313
python -m pip install virtualenv
311-
python -m virtualenv .virtualenv/
314+
python -m virtualenv --no-venv-redirect .virtualenv/
312315
if ('${{ matrix.os }}' -match 'windows-latest') {
313316
& ".virtualenv/Scripts/python.exe" ./build/ci/addEnvPath.py ${{ env.PYTHON_VIRTUAL_ENVS_LOCATION }} virtualEnvPath
314317
} else {
@@ -323,10 +326,11 @@ jobs:
323326
if: matrix.test-suite == 'venv' && startsWith(matrix.python, 3.)
324327
run: |
325328
python -m venv .venv
329+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
326330
if ('${{ matrix.os }}' -match 'windows-latest') {
327-
& ".venv/Scripts/python.exe" ./build/ci/addEnvPath.py ${{ env.PYTHON_VIRTUAL_ENVS_LOCATION }} venvPath
331+
& "./.venv/Scripts/python.exe" ./build/ci/addEnvPath.py ${{ env.PYTHON_VIRTUAL_ENVS_LOCATION }} venvPath
328332
} else {
329-
& ".venv/bin/python" ./build/ci/addEnvPath.py ${{ env.PYTHON_VIRTUAL_ENVS_LOCATION }} venvPath
333+
& "./.venv/bin/python" ./build/ci/addEnvPath.py ${{ env.PYTHON_VIRTUAL_ENVS_LOCATION }} venvPath
330334
}
331335
332336
- name: Prepare conda for venv tests
@@ -426,12 +430,12 @@ jobs:
426430

427431
steps:
428432
- name: Checkout
429-
uses: actions/checkout@v6
433+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
430434
with:
431435
persist-credentials: false
432436

433437
- name: Checkout Python Environment Tools
434-
uses: actions/checkout@v6
438+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
435439
with:
436440
repository: 'microsoft/python-environment-tools'
437441
path: ${{ env.special-working-directory-relative }}/python-env-tools

‎.github/workflows/codeql-analysis.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ jobs:
3636

3737
steps:
3838
- name: Checkout repository
39-
uses: actions/checkout@v6
39+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
4040
with:
4141
persist-credentials: false
4242

4343
# Initializes the CodeQL tools for scanning.
4444
- name: Initialize CodeQL
45-
uses: github/codeql-action/init@v4
45+
uses: github/codeql-action/init@v4.37.9
4646
with:
4747
languages: ${{ matrix.language }}
4848
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -67,4 +67,4 @@ jobs:
6767
# make release
6868

6969
- name: Perform CodeQL Analysis
70-
uses: github/codeql-action/analyze@v4
70+
uses: github/codeql-action/analyze@v4.37.9

‎.github/workflows/gen-issue-velocity.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ jobs:
1414

1515
steps:
1616
- name: Checkout repository
17-
uses: actions/checkout@v6
17+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1818
with:
1919
persist-credentials: false
2020

2121
- name: Set up Python
22-
uses: actions/setup-python@v6
22+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
2323
with:
2424
python-version: '3.x'
2525

‎.github/workflows/info-needed-closer.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Checkout Actions
17-
uses: actions/checkout@v6
17+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1818
with:
1919
repository: 'microsoft/vscode-github-triage-actions'
2020
path: ./actions

0 commit comments

Comments
 (0)