Merge pull request #10766 from rdb/fix-10765
This commit is contained in:
commit
b3b44ea814
|
@ -0,0 +1 @@
|
||||||
|
Fixed :fixture:`tmp_path` fixture always raising :class:`OSError` on ``emscripten`` platform due to missing :func:`os.getuid`.
|
|
@ -1,4 +1,6 @@
|
||||||
"""Python version compatibility code."""
|
"""Python version compatibility code."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import functools
|
import functools
|
||||||
|
@ -12,11 +14,8 @@ from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Generic
|
from typing import Generic
|
||||||
from typing import NoReturn
|
from typing import NoReturn
|
||||||
from typing import Optional
|
|
||||||
from typing import Tuple
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -46,7 +45,7 @@ LEGACY_PATH = py.path. local
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
|
|
||||||
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
|
def legacy_path(path: str | os.PathLike[str]) -> LEGACY_PATH:
|
||||||
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
|
"""Internal wrapper to prepare lazy proxies for legacy_path instances"""
|
||||||
return LEGACY_PATH(path)
|
return LEGACY_PATH(path)
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
|
||||||
# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions
|
# https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions
|
||||||
class NotSetType(enum.Enum):
|
class NotSetType(enum.Enum):
|
||||||
token = 0
|
token = 0
|
||||||
NOTSET: "Final" = NotSetType.token # noqa: E305
|
NOTSET: Final = NotSetType.token # noqa: E305
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
|
@ -94,7 +93,7 @@ def is_async_function(func: object) -> bool:
|
||||||
return iscoroutinefunction(func) or inspect.isasyncgenfunction(func)
|
return iscoroutinefunction(func) or inspect.isasyncgenfunction(func)
|
||||||
|
|
||||||
|
|
||||||
def getlocation(function, curdir: Optional[str] = None) -> str:
|
def getlocation(function, curdir: str | None = None) -> str:
|
||||||
function = get_real_func(function)
|
function = get_real_func(function)
|
||||||
fn = Path(inspect.getfile(function))
|
fn = Path(inspect.getfile(function))
|
||||||
lineno = function.__code__.co_firstlineno
|
lineno = function.__code__.co_firstlineno
|
||||||
|
@ -132,8 +131,8 @@ def getfuncargnames(
|
||||||
*,
|
*,
|
||||||
name: str = "",
|
name: str = "",
|
||||||
is_method: bool = False,
|
is_method: bool = False,
|
||||||
cls: Optional[type] = None,
|
cls: type | None = None,
|
||||||
) -> Tuple[str, ...]:
|
) -> tuple[str, ...]:
|
||||||
"""Return the names of a function's mandatory arguments.
|
"""Return the names of a function's mandatory arguments.
|
||||||
|
|
||||||
Should return the names of all function arguments that:
|
Should return the names of all function arguments that:
|
||||||
|
@ -197,7 +196,7 @@ def getfuncargnames(
|
||||||
return arg_names
|
return arg_names
|
||||||
|
|
||||||
|
|
||||||
def get_default_arg_names(function: Callable[..., Any]) -> Tuple[str, ...]:
|
def get_default_arg_names(function: Callable[..., Any]) -> tuple[str, ...]:
|
||||||
# Note: this code intentionally mirrors the code at the beginning of
|
# Note: this code intentionally mirrors the code at the beginning of
|
||||||
# getfuncargnames, to get the arguments which were excluded from its result
|
# getfuncargnames, to get the arguments which were excluded from its result
|
||||||
# because they had default values.
|
# because they had default values.
|
||||||
|
@ -228,7 +227,7 @@ def _bytes_to_ascii(val: bytes) -> str:
|
||||||
return val.decode("ascii", "backslashreplace")
|
return val.decode("ascii", "backslashreplace")
|
||||||
|
|
||||||
|
|
||||||
def ascii_escaped(val: Union[bytes, str]) -> str:
|
def ascii_escaped(val: bytes | str) -> str:
|
||||||
r"""If val is pure ASCII, return it as an str, otherwise, escape
|
r"""If val is pure ASCII, return it as an str, otherwise, escape
|
||||||
bytes objects into a sequence of escaped bytes:
|
bytes objects into a sequence of escaped bytes:
|
||||||
|
|
||||||
|
@ -355,7 +354,6 @@ else:
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
from functools import cached_property as cached_property
|
from functools import cached_property as cached_property
|
||||||
else:
|
else:
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
class cached_property(Generic[_S, _T]):
|
class cached_property(Generic[_S, _T]):
|
||||||
__slots__ = ("func", "__doc__")
|
__slots__ = ("func", "__doc__")
|
||||||
|
@ -366,12 +364,12 @@ else:
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __get__(
|
def __get__(
|
||||||
self, instance: None, owner: Optional[Type[_S]] = ...
|
self, instance: None, owner: type[_S] | None = ...
|
||||||
) -> "cached_property[_S, _T]":
|
) -> cached_property[_S, _T]:
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def __get__(self, instance: _S, owner: Optional[Type[_S]] = ...) -> _T:
|
def __get__(self, instance: _S, owner: type[_S] | None = ...) -> _T:
|
||||||
...
|
...
|
||||||
|
|
||||||
def __get__(self, instance, owner=None):
|
def __get__(self, instance, owner=None):
|
||||||
|
@ -381,6 +379,18 @@ else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_id() -> int | None:
|
||||||
|
"""Return the current user id, or None if we cannot get it reliably on the current platform."""
|
||||||
|
# win32 does not have a getuid() function.
|
||||||
|
# On Emscripten, getuid() is a stub that always returns 0.
|
||||||
|
if sys.platform in ("win32", "emscripten"):
|
||||||
|
return None
|
||||||
|
# getuid shouldn't fail, but cpython defines such a case.
|
||||||
|
# Let's hope for the best.
|
||||||
|
uid = os.getuid()
|
||||||
|
return uid if uid != -1 else None
|
||||||
|
|
||||||
|
|
||||||
# Perform exhaustiveness checking.
|
# Perform exhaustiveness checking.
|
||||||
#
|
#
|
||||||
# Consider this example:
|
# Consider this example:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
@ -30,7 +29,7 @@ from .pathlib import make_numbered_dir
|
||||||
from .pathlib import make_numbered_dir_with_cleanup
|
from .pathlib import make_numbered_dir_with_cleanup
|
||||||
from .pathlib import rm_rf
|
from .pathlib import rm_rf
|
||||||
from .pathlib import cleanup_dead_symlink
|
from .pathlib import cleanup_dead_symlink
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final, get_user_id
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
@ -176,12 +175,9 @@ class TempPathFactory:
|
||||||
# Also, to keep things private, fixup any world-readable temp
|
# Also, to keep things private, fixup any world-readable temp
|
||||||
# rootdir's permissions. Historically 0o755 was used, so we can't
|
# rootdir's permissions. Historically 0o755 was used, so we can't
|
||||||
# just error out on this, at least for a while.
|
# just error out on this, at least for a while.
|
||||||
if sys.platform != "win32":
|
uid = get_user_id()
|
||||||
uid = os.getuid()
|
if uid is not None:
|
||||||
rootdir_stat = rootdir.stat()
|
rootdir_stat = rootdir.stat()
|
||||||
# getuid shouldn't fail, but cpython defines such a case.
|
|
||||||
# Let's hope for the best.
|
|
||||||
if uid != -1:
|
|
||||||
if rootdir_stat.st_uid != uid:
|
if rootdir_stat.st_uid != uid:
|
||||||
raise OSError(
|
raise OSError(
|
||||||
f"The temporary directory {rootdir} is not owned by the current user. "
|
f"The temporary directory {rootdir} is not owned by the current user. "
|
||||||
|
|
Loading…
Reference in New Issue