Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
applyTo: 'python_files/tests/pytestadapter/test_discovery.py'
description: 'A guide for adding new tests for pytest discovery and JSON formatting in the test_pytest_collect suite.'
---

# How to Add New Pytest Discovery Tests

This guide explains how to add new tests for pytest discovery and JSON formatting in the `test_pytest_collect` suite. Follow these steps to ensure your tests are consistent and correct.

---

## 1. Add Your Test File

- Place your new test file/files in the appropriate subfolder under:
```
python_files/tests/pytestadapter/.data/
```
- Organize folders and files to match the structure you want to test. For example, to test nested folders, create the corresponding directory structure.
- In your test file, mark each test function with a comment:
```python
def test_function(): # test_marker--test_function
...
```

**Root Node Matching:**

- The root node in your expected output must match the folder or file you pass to pytest discovery. For example, if you run discovery on a subfolder, the root `"name"`, `"path"`, and `"id_"` in your expected output should be that subfolder, not the parent `.data` folder.
- Only use `.data` as the root if you are running discovery on the entire `.data` folder.

**Example:**
If you run:

```python
helpers.runner([os.fspath(TEST_DATA_PATH / "myfolder"), "--collect-only"])
```

then your expected output root should be:

```python
{
"name": "myfolder",
"path": os.fspath(TEST_DATA_PATH / "myfolder"),
"type_": "folder",
...
}
```

---

## 2. Update `expected_discovery_test_output.py`

- Open `expected_discovery_test_output.py` in the same test suite.
- Add a new expected output dictionary for your test file, following the format of existing entries.
- Use the helper functions and path conventions:
- Use `os.fspath()` for all paths.
- Use `find_test_line_number("function_name", file_path)` for the `lineno` field.
- Use `get_absolute_test_id("relative_path::function_name", file_path)` for `id_` and `runID`.
- Always use current path concatenation (e.g., `TEST_DATA_PATH / "your_folder" / "your_file.py"`).
- Create new constants as needed to keep the code clean and maintainable.

**Important:**

- Do **not** read the entire `expected_discovery_test_output.py` file if you only need to add or reference a single constant. This file is very large; prefer searching for the relevant section or appending to the end.

**Example:**
If you run discovery on a subfolder:

```python
helpers.runner([os.fspath(TEST_DATA_PATH / "myfolder"), "--collect-only"])
```

then your expected output root should be:

```python
myfolder_path = TEST_DATA_PATH / "myfolder"
my_expected_output = {
"name": "myfolder",
"path": os.fspath(myfolder_path),
"type_": "folder",
...
}
```

- Add a comment above your dictionary describing the structure, as in the existing examples.

---

## 3. Add Your Test to `test_discovery.py`

- In `test_discovery.py`, add your new test as a parameterized case to the main `test_pytest_collect` function. Do **not** create a standalone test function for new discovery cases.
- Reference your new expected output constant from `expected_discovery_test_output.py`.

**Example:**

```python
@pytest.mark.parametrize(
("file", "expected_const"),
[
("myfolder", my_expected_output),
# ... other cases ...
],
)
def test_pytest_collect(file, expected_const):
...
```

---

## 4. Run and Verify

- Run the test suite to ensure your new test is discovered and passes.
- If the test fails, check your expected output dictionary for path or structure mismatches.

---

## 5. Tips

- Always use the helper functions for line numbers and IDs.
- Match the folder/file structure in `.data` to the expected JSON structure.
- Use comments to document the expected output structure for clarity.
- Ensure all `"path"` and `"id_"` fields in your expected output match exactly what pytest returns, including absolute paths and root node structure.

---

**Reference:**
See `expected_discovery_test_output.py` for more examples and formatting. Use search or jump to the end of the file to avoid reading the entire file when possible.
10 changes: 2 additions & 8 deletions extensions/positron-python/.github/release_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ NOTE: the number of this release is in the issue title and can be substituted in
- [ ] Update `pet`:
- [ ] Go to the [pet](https://github.com/microsoft/python-environment-tools) repo and check `main` and latest `release/*` branch. If there are new changes in `main` then create a branch called `release/YYYY.minor` (matching python extension release `major.minor`).
- [ ] Update `build\azure-pipeline.stable.yml` to point to the latest `release/YYYY.minor` for `python-environment-tools`.
- [ ] Change the version in `package.json` to the next **even** number and switch the `-dev` to `-rc`. (🤖)
- [ ] Change the version in `package.json` to the next **even** number. (🤖)
- [ ] Run `npm install` to make sure `package-lock.json` is up-to-date _(you should now see changes to the `package.json` and `package-lock.json` at this point which update the version number **only**)_. (🤖)
- [ ] Update `ThirdPartyNotices-Repository.txt` as appropriate. You can check by looking at the [commit history](https://github.com/microsoft/vscode-python/commits/main) and scrolling through to see if there's anything listed there which might have pulled in some code directly into the repository from somewhere else. If you are still unsure you can check with the team.
- [ ] Create a PR from your branch **`bump-release-[YYYY.minor]`** to `main`. Add the `"no change-log"` tag to the PR so it does not show up on the release notes before merging it.
Expand All @@ -64,7 +64,7 @@ NOTE: If there are release branches that are two versions old you can delete the
### Step 4: Return `main` to dev and unfreeze (❄️ ➡ 💧)
NOTE: The purpose of this step is ensuring that main always is on a dev version number for every night's 🌃 pre-release. Therefore it is imperative that you do this directly after the previous steps to reset the version in main to a dev version **before** a pre-release goes out.
- [ ] Create a branch called **`bump-dev-version-YYYY.[minor+1]`**.
- [ ] Bump the minor version number in the `package.json` to the next `YYYY.[minor+1]` which will be an odd number, and switch the `-rc` to `-dev`.(🤖)
- [ ] Bump the minor version number in the `package.json` to the next `YYYY.[minor+1]` which will be an odd number, and add `-dev`.(🤖)
- [ ] Run `npm install` to make sure `package-lock.json` is up-to-date _(you should now see changes to the `package.json` and `package-lock.json` only relating to the new version number)_ . (🤖)
- [ ] Create a PR from this branch against `main` and merge it.

Expand All @@ -83,12 +83,6 @@ NOTE: this PR should make all CI relating to `main` be passing again (such as th
### Step 6: Take the release branch from a candidate to the finalized release
- [ ] Make sure the [appropriate pull requests](https://github.com/microsoft/vscode-docs/pulls) for the [documentation](https://code.visualstudio.com/docs/python/python-tutorial) -- including the [WOW](https://code.visualstudio.com/docs/languages/python) page -- are ready.
- [ ] Check to make sure any final updates to the **`release/YYYY.minor`** branch have been merged.
- [ ] Create a branch against **`release/YYYY.minor`** called **`finalized-release-[YYYY.minor]`**.
- [ ] Update the version in `package.json` to remove the `-rc` (🤖) from the version.
- [ ] Run `npm install` to make sure `package-lock.json` is up-to-date _(the only update should be the version number if `package-lock.json` has been kept up-to-date)_. (🤖)
- [ ] Update `ThirdPartyNotices-Repository.txt` manually if necessary.
- [ ] Create a PR from **`finalized-release-[YYYY.minor]`** against `release/YYYY.minor` and merge it.


### Step 7: Execute the Release
- [ ] Make sure CI is passing for **`release/YYYY.minor`** release branch (🤖).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ jobs:
if (!labels.includes('skip-issue-check')) {
const prBody = context.payload.pull_request.body || '';
const issueLink = prBody.match(/https:\/\/github\.com\/\S+\/issues\/\d+/);
if (!issueLink) {
const issueReference = prBody.match(/#\d+/);
if (!issueLink && !issueReference) {
core.setFailed('No associated issue found in the PR description.');
}
}
2 changes: 1 addition & 1 deletion extensions/positron-python/.github/workflows/pr-labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
pull-requests: write
steps:
- name: 'PR impact specified'
uses: mheap/github-action-required-labels@388fd6af37b34cdfe5a23b37060e763217e58b03 # v5.5.0
uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1
with:
mode: exactly
count: 1
Expand Down
40 changes: 20 additions & 20 deletions extensions/positron-python/build/azure-pipeline.stable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ extends:
displayName: Build

- bash: |
mkdir -p $(Build.SourcesDirectory)/python-env-tools/bin
chmod +x $(Build.SourcesDirectory)/python-env-tools/bin
mkdir -p $(Build.SourcesDirectory)/python-env-tools/bin
chmod +x $(Build.SourcesDirectory)/python-env-tools/bin
displayName: Make Directory for python-env-tool binary

- bash: |
Expand Down Expand Up @@ -124,30 +124,30 @@ extends:

- task: DownloadPipelineArtifact@2
inputs:
buildType: 'specific'
project: 'Monaco'
definition: 593
buildVersionToDownload: 'latestFromBranch'
branchName: 'refs/heads/release/2025.8'
targetPath: '$(Build.SourcesDirectory)/python-env-tools/bin'
artifactName: 'bin-$(buildTarget)'
itemPattern: |
pet.exe
pet
ThirdPartyNotices.txt
buildType: 'specific'
project: 'Monaco'
definition: 593
buildVersionToDownload: 'latestFromBranch'
branchName: 'refs/heads/release/2025.12'
targetPath: '$(Build.SourcesDirectory)/python-env-tools/bin'
artifactName: 'bin-$(buildTarget)'
itemPattern: |
pet.exe
pet
ThirdPartyNotices.txt

- bash: |
ls -lf ./python-env-tools/bin
chmod +x ./python-env-tools/bin/pet*
ls -lf ./python-env-tools/bin
ls -lf ./python-env-tools/bin
chmod +x ./python-env-tools/bin/pet*
ls -lf ./python-env-tools/bin
displayName: Set chmod for pet binary

- script: python -c "import shutil; shutil.rmtree('.nox', ignore_errors=True)"
displayName: Clean up Nox
tsa:
config:
areaPath: 'Visual Studio Code Python Extensions'
serviceTreeID: '6e6194bc-7baa-4486-86d0-9f5419626d46'
enabled: true
config:
areaPath: 'Visual Studio Code Python Extensions'
serviceTreeID: '6e6194bc-7baa-4486-86d0-9f5419626d46'
enabled: true
apiScanDependentPipelineId: '593' # python-environment-tools
apiScanSoftwareVersion: '2024'
1 change: 1 addition & 0 deletions extensions/positron-python/build/test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ pytest-describe

# for pytest-ruff related tests
pytest-ruff
pytest-black
10 changes: 6 additions & 4 deletions extensions/positron-python/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,13 @@ async function addExtensionPackDependencies() {
// extension dependencies need not be installed during development
const packageJsonContents = await fsExtra.readFile('package.json', 'utf-8');
const packageJson = JSON.parse(packageJsonContents);
// --- Start Positron ---
packageJson.extensionPack = ['ms-python.debugpy'].concat(
packageJson.extensionPack = [
// --- Start Positron ---
//'ms-python.vscode-pylance',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjusting this slightly so we know what extension dependencies we are not using. ms-python.vscode-python-envs is now a bundled/required dependency.

'ms-python.debugpy',
// 'ms-python.vscode-python-envs',
// --- End Positron ---
packageJson.extensionPack ? packageJson.extensionPack : [],
);
].concat(packageJson.extensionPack ? packageJson.extensionPack : []);
// Remove potential duplicates.
packageJson.extensionPack = packageJson.extensionPack.filter(
(item, index) => packageJson.extensionPack.indexOf(item) === index,
Expand Down
26 changes: 25 additions & 1 deletion extensions/positron-python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@
"category": "Python",
"command": "python.createNewFile"
},
{
"category": "Python",
"command": "python.copyTestId",
"title": "%python.command.python.testing.copyTestId.title%"
},
{
"title": "%python.command.python.createPyprojectToml.title%",
"category": "Python",
Expand Down Expand Up @@ -1491,6 +1496,13 @@
"command": "python.reportIssue"
}
],
"testing/item/context": [
{
"command": "python.copyTestId",
"group": "navigation",
"when": "controllerId == 'python-tests'"
}
],
"commandPalette": [
{
"category": "Python",
Expand Down Expand Up @@ -1578,6 +1590,18 @@
"title": "%python.command.python.execSelectionInTerminal.title%",
"when": "false"
},
{
"category": "Python",
"command": "python.copyTestId",
"title": "%python.command.python.testing.copyTestId.title%",
"when": "false"
},
{
"category": "Python",
"command": "python.copyTestId",
"title": "%python.command.python.testing.copyTestId.title%",
"when": "false"
},
{
"category": "Python",
"command": "python.execInREPL",
Expand Down Expand Up @@ -2017,7 +2041,7 @@
{
"name": "configure_python_environment",
"displayName": "Configure Python Environment",
"modelDescription": "This tool configures a Python environment in the given workspace. ALWAYS Use this tool to set up the user's chosen environment and ALWAYS call this tool before using any other Python related tools.",
"modelDescription": "This tool configures a Python environment in the given workspace. ALWAYS Use this tool to set up the user's chosen environment and ALWAYS call this tool before using any other Python related tools or running any Python command in the terminal.",
"userDescription": "%python.languageModelTools.configure_python_environment.userDescription%",
"toolReferenceName": "configurePythonEnvironment",
"tags": [
Expand Down
4 changes: 3 additions & 1 deletion extensions/positron-python/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"python.command.python.analysis.restartLanguageServer.title": "Restart Language Server",
"python.command.python.launchTensorBoard.title": "Launch TensorBoard",
"python.command.python.refreshTensorBoard.title": "Refresh TensorBoard",
"python.command.python.testing.copyTestId.title": "Copy Test Id",
"python.command.python.interpreterDebugInfo.title": "Print interpreter debug information to Output",
"python.createEnvironment.contentButton.description": "Show or hide Create Environment button in the editor for `requirements.txt` or other dependency files.",
"python.createEnvironment.trigger.description": "Detect if environment creation is required for the current project",
Expand All @@ -61,6 +62,7 @@
"python.interpreters.exclude.markdownDescription": "List of absolute paths to Python executables or folders containing Python installations to exclude from the available Python interpreters. These interpreters will not be displayed in the Positron UI.\n\nIf an interpreter is both included via `#python.interpreters.include#` and excluded via `#python.interpreters.exclude#`, it will not be displayed in the Positron UI. See also `#python.interpreters.override#`.\n\nExample: Add `/usr/bin/python3` to exclude the specific executable, or `/opt/homebrew` to exclude all brew-installed Python installations on macOS.\n\nRequires a restart to take effect.",
"python.interpreters.override.markdownDescription": "List of absolute paths to Python executables or folders containing Python installations to override the list of available Python interpreters. Only the interpreters found at the specified paths will be displayed in the Positron UI.\n\nThis setting takes precedence over the `#python.interpreters.include#` and `#python.interpreters.exclude#` settings.\n\nExample: On Linux or Mac, add `/custom/pythons/3.10.4/bin/python` to include only this specific executable, or `/custom/pythons` to include only Python installations found within the directory.\n\nRequires a restart to take effect.",
"python.envFile.description": "Absolute path to a file containing environment variable definitions.",
"python.useEnvironmentsExtension.description": "Enables the Python Environments extension. Requires window reload on change.",
"python.experiments.enabled.description": "Enables A/B tests experiments in the Python extension. If enabled, you may get included in proposed enhancements and/or features.",
"python.experiments.optInto.description": "List of experiments to opt into. If empty, user is assigned the default experiment groups. See [here](https://github.com/microsoft/vscode-python/wiki/AB-Experiments) for more details.",
"python.experiments.optOutFrom.description": "List of experiments to opt out of. If empty, user is assigned the default experiment groups. See [here](https://github.com/microsoft/vscode-python/wiki/AB-Experiments) for more details.",
Expand Down Expand Up @@ -94,7 +96,7 @@
"python.tensorBoard.logDirectory.description": "Set this setting to your preferred TensorBoard log directory to skip log directory prompt when starting TensorBoard.",
"python.tensorBoard.logDirectory.markdownDeprecationMessage": "Tensorboard support has been moved to the extension [Tensorboard extension](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.tensorboard). Instead use the setting `tensorBoard.logDirectory`.",
"python.tensorBoard.logDirectory.deprecationMessage": "Tensorboard support has been moved to the extension Tensorboard extension. Instead use the setting `tensorBoard.logDirectory`.",
"python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things.",
"python.terminal.shellIntegration.enabled.description": "Enable [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) for the terminals running python. Shell integration enhances the terminal experience by enabling command decorations, run recent command, improving accessibility among other things. Note: PyREPL (available in Python 3.13+) is automatically disabled when shell integration is enabled to avoid cursor indentation issues.",
"python.terminal.activateEnvInCurrentTerminal.description": "Activate Python Environment in the current Terminal on load of the Extension.",
"python.terminal.activateEnvironment.description": "Activate Python Environment in all Terminals created.",
"python.terminal.executeInFileDir.description": "When executing a file in the terminal, whether to use execute in the file's directory, instead of the current open folder.",
Expand Down
Loading
Loading