Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge 23.7.x branch back into main #13094

Merged
merged 8 commits into from
Sep 12, 2023
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
13 changes: 10 additions & 3 deletions .authors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,7 @@
first_commit: 2016-12-11 16:14:03
- name: Ken Odegard
email: kodegard@anaconda.com
num_commits: 558
num_commits: 566
first_commit: 2016-09-27 18:04:21
github: kenodegard
aliases:
Expand Down Expand Up @@ -2003,7 +2003,7 @@
- name: Daniel Holth
email: dholth@anaconda.com
github: dholth
num_commits: 68
num_commits: 70
first_commit: 2021-11-18 08:57:14
- name: John Flavin
email: flavinj@gmail.com
Expand Down Expand Up @@ -2083,7 +2083,7 @@
github: beeankha
alternate_emails:
- beeankha@gmail.com
num_commits: 25
num_commits: 29
first_commit: 2022-05-12 13:39:02
- name: Kian-Meng Ang
email: kianmeng.ang@gmail.com
Expand Down Expand Up @@ -2364,3 +2364,10 @@
github: marcoesters
num_commits: 1
first_commit: 2023-07-11 05:47:23
- name: Peter Talley
email: peterctalley@gmail.com
github: otaithleigh
aliases:
- P. Talley
num_commits: 1
first_commit: 2023-08-25 20:43:39
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ Paul Yim <pseudoyim@users.noreply.github.com>
Pavel Zwerschke <pavelzw@gmail.com>
Pete Bachant <petebachant@gmail.com>
Peter Cable <petercable@gmail.com>
Peter Talley <peterctalley@gmail.com> P. Talley <peterctalley@gmail.com>
Peter Williams <peter@newton.cx>
Phil Elson <pelson.pub@gmail.com>
Philip Thomas <pthomas.v3@gmail.com> psthomas <pthomas.v3@gmail.com>
Expand Down
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ Authors are sorted alphabetically.
* Pavel Zwerschke
* Pete Bachant
* Peter Cable
* Peter Talley
* Peter Williams
* Phil Elson
* Philip Thomas
Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
[//]: # (current developments)

## 23.7.4 (2023-09-12)

### Enhancements

* Use `os.scandir()` to find conda subcommands without `stat()` overhead. (#13033, #13067)

### Bug fixes

* Fix S3 bucket name. (#12989)
* Fix performance regression of basic commands (e.g., `conda info`) on WSL. (#13035)
* Catch `PermissionError` raised by `conda.cli.find_commands.find_commands` when user's `$PATH` contains restricted paths. (#13062, #13089)
* Fix sorting error for `conda config --show-sources --json`. (#13076)

### Contributors

* @beeankha
* @dholth
* @kenodegard
* @otaithleigh made their first contribution in https://github.com/conda/conda/pull/13035


## 23.7.3 (2023-08-21)

### Bug fixes
Expand Down
18 changes: 11 additions & 7 deletions conda/cli/find_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sys
import sysconfig
from functools import lru_cache
from os.path import basename, expanduser, isdir, isfile, join
from os.path import basename, expanduser, isfile, join

from ..common.compat import on_win

Expand Down Expand Up @@ -61,16 +61,20 @@ def find_commands(include_others=True):
dir_paths.extend(os.environ.get("PATH", "").split(os.pathsep))

if on_win:
pat = re.compile(r"conda-([\w\-]+)\.(exe|bat)$")
pat = re.compile(r"conda-([\w\-]+)(\.(exe|bat))?$")
else:
pat = re.compile(r"conda-([\w\-]+)$")

res = set()
for dir_path in dir_paths:
if not isdir(dir_path):
try:
for entry in os.scandir(dir_path):
m = pat.match(entry.name)
if m and entry.is_file():
res.add(m.group(1))
except (FileNotFoundError, NotADirectoryError, PermissionError):
# FileNotFoundError: path doesn't exist
# NotADirectoryError: path is not a directory
# PermissionError: user doesn't have read access
continue
for fn in os.listdir(dir_path):
m = pat.match(fn)
if m and isfile(join(dir_path, fn)):
res.add(m.group(1))
return tuple(sorted(res))
80 changes: 80 additions & 0 deletions tests/cli/test_find_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
import os
from pathlib import Path

import pytest
from pytest import MonkeyPatch

from conda.cli.find_commands import find_commands, find_executable
from conda.common.compat import on_win


@pytest.fixture
def faux_path(tmp_path: Path, monkeypatch: MonkeyPatch) -> Path:
if not on_win:
# make a read-only location, none of these should show up in the tests
permission = tmp_path / "permission"
permission.mkdir(mode=0o333, exist_ok=True)
(permission / "conda-permission").touch()
(permission / "conda-permission.bat").touch()
(permission / "conda-permission.exe").touch()
monkeypatch.setenv("PATH", str(permission), prepend=os.pathsep)

# missing directory
missing_dir = tmp_path / "missing-directory"
monkeypatch.setenv("PATH", str(missing_dir), prepend=os.pathsep)

# not directory
not_dir = tmp_path / "not-directory"
not_dir.touch()
monkeypatch.setenv("PATH", str(not_dir), prepend=os.pathsep)

# bad executables
bad = tmp_path / "bad"
bad.mkdir(exist_ok=True)
(bad / "non-conda-bad").touch()
(bad / "non-conda-bad.bat").touch()
(bad / "non-conda-bad.exe").touch()
monkeypatch.setenv("PATH", str(bad), prepend=os.pathsep)

# good executables
bin_ = tmp_path / "bin"
bin_.mkdir(exist_ok=True)
(bin_ / "conda-bin").touch()
monkeypatch.setenv("PATH", str(bin_), prepend=os.pathsep)

bat = tmp_path / "bat"
bat.mkdir(exist_ok=True)
(bat / "conda-bat.bat").touch()
monkeypatch.setenv("PATH", str(bat), prepend=os.pathsep)

exe = tmp_path / "exe"
exe.mkdir(exist_ok=True)
(exe / "conda-exe.exe").touch()
monkeypatch.setenv("PATH", str(exe), prepend=os.pathsep)

yield tmp_path

if not on_win:
# undo read-only for clean removal
permission.chmod(permission.stat().st_mode | 0o444)


def test_find_executable(faux_path: Path):
assert (faux_path / "bin" / "conda-bin").samefile(find_executable("conda-bin"))
if on_win:
assert (faux_path / "bat" / "conda-bat.bat").samefile(
find_executable("conda-bat")
)
assert (faux_path / "exe" / "conda-exe.exe").samefile(
find_executable("conda-exe")
)


def test_find_commands(faux_path: Path):
find_commands.cache_clear()
if on_win:
assert {"bin", "bat", "exe"}.issubset(find_commands())
else:
assert {"bin"}.issubset(find_commands())
Loading