Skip to content

Commit

Permalink
Add PEP 706 filters to tarfile (python#10316)
Browse files Browse the repository at this point in the history
Fixes python#10315

Co-authored-by: Sebastian Rittau <[email protected]>
  • Loading branch information
JelleZijlstra and srittau authored Jun 14, 2023
1 parent 5f9d05c commit 5beddbe
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 23 deletions.
5 changes: 3 additions & 2 deletions stdlib/shutil.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import os
import sys
from _typeshed import BytesPath, FileDescriptorOrPath, StrOrBytesPath, StrPath, SupportsRead, SupportsWrite
from collections.abc import Callable, Iterable, Sequence
from tarfile import _TarfileFilter
from typing import Any, AnyStr, NamedTuple, Protocol, TypeVar, overload
from typing_extensions import TypeAlias

Expand Down Expand Up @@ -192,9 +193,9 @@ def register_archive_format(
) -> None: ...
def unregister_archive_format(name: str) -> None: ...

if sys.version_info >= (3, 12):
if sys.version_info >= (3, 8):
def unpack_archive(
filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: str | None = None
filename: StrPath, extract_dir: StrPath | None = None, format: str | None = None, *, filter: _TarfileFilter | None = None
) -> None: ...

else:
Expand Down
90 changes: 83 additions & 7 deletions stdlib/tarfile.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from collections.abc import Callable, Iterable, Iterator, Mapping
from gzip import _ReadableFileobj as _GzipReadableFileobj, _WritableFileobj as _GzipWritableFileobj
from types import TracebackType
from typing import IO, ClassVar, Protocol, overload
from typing_extensions import Literal, Self
from typing_extensions import Literal, Self, TypeAlias

__all__ = [
"TarFile",
Expand All @@ -26,6 +26,21 @@ __all__ = [
"DEFAULT_FORMAT",
"open",
]
if sys.version_info >= (3, 12):
__all__ += [
"fully_trusted_filter",
"data_filter",
"tar_filter",
"FilterError",
"AbsoluteLinkError",
"OutsideDestinationError",
"SpecialFileError",
"AbsolutePathError",
"LinkOutsideDestinationError",
]

_FilterFunction: TypeAlias = Callable[[TarInfo, str], TarInfo | None]
_TarfileFilter: TypeAlias = Literal["fully_trusted", "tar", "data"] | _FilterFunction

class _Fileobj(Protocol):
def read(self, __size: int) -> bytes: ...
Expand Down Expand Up @@ -125,6 +140,7 @@ class TarFile:
debug: int | None
errorlevel: int | None
offset: int # undocumented
extraction_filter: _FilterFunction | None
def __init__(
self,
name: StrOrBytesPath | None = None,
Expand Down Expand Up @@ -275,12 +291,32 @@ class TarFile:
def getnames(self) -> _list[str]: ...
def list(self, verbose: bool = True, *, members: _list[TarInfo] | None = None) -> None: ...
def next(self) -> TarInfo | None: ...
def extractall(
self, path: StrOrBytesPath = ".", members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False
) -> None: ...
def extract(
self, member: str | TarInfo, path: StrOrBytesPath = "", set_attrs: bool = True, *, numeric_owner: bool = False
) -> None: ...
if sys.version_info >= (3, 8):
def extractall(
self,
path: StrOrBytesPath = ".",
members: Iterable[TarInfo] | None = None,
*,
numeric_owner: bool = False,
filter: _TarfileFilter | None = ...,
) -> None: ...
def extract(
self,
member: str | TarInfo,
path: StrOrBytesPath = "",
set_attrs: bool = True,
*,
numeric_owner: bool = False,
filter: _TarfileFilter | None = ...,
) -> None: ...
else:
def extractall(
self, path: StrOrBytesPath = ".", members: Iterable[TarInfo] | None = None, *, numeric_owner: bool = False
) -> None: ...
def extract(
self, member: str | TarInfo, path: StrOrBytesPath = "", set_attrs: bool = True, *, numeric_owner: bool = False
) -> None: ...

def _extract_member(
self, tarinfo: TarInfo, targetpath: str, set_attrs: bool = True, numeric_owner: bool = False
) -> None: ... # undocumented
Expand Down Expand Up @@ -324,6 +360,31 @@ class StreamError(TarError): ...
class ExtractError(TarError): ...
class HeaderError(TarError): ...

if sys.version_info >= (3, 8):
class FilterError(TarError):
# This attribute is only set directly on the subclasses, but the documentation guarantees
# that it is always present on FilterError.
tarinfo: TarInfo

class AbsolutePathError(FilterError):
def __init__(self, tarinfo: TarInfo) -> None: ...

class OutsideDestinationError(FilterError):
def __init__(self, tarinfo: TarInfo, path: str) -> None: ...

class SpecialFileError(FilterError):
def __init__(self, tarinfo: TarInfo) -> None: ...

class AbsoluteLinkError(FilterError):
def __init__(self, tarinfo: TarInfo) -> None: ...

class LinkOutsideDestinationError(FilterError):
def __init__(self, tarinfo: TarInfo, path: str) -> None: ...

def fully_trusted_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
def tar_filter(member: TarInfo, dest_path: str) -> TarInfo: ...
def data_filter(member: TarInfo, dest_path: str) -> TarInfo: ...

class TarInfo:
name: str
path: str
Expand Down Expand Up @@ -353,6 +414,21 @@ class TarInfo:
def linkpath(self) -> str: ...
@linkpath.setter
def linkpath(self, linkname: str) -> None: ...
if sys.version_info >= (3, 8):
def replace(
self,
*,
name: str = ...,
mtime: int = ...,
mode: int = ...,
linkname: str = ...,
uid: int = ...,
gid: int = ...,
uname: str = ...,
gname: str = ...,
deep: bool = True,
) -> Self: ...

def get_info(self) -> Mapping[str, str | int | bytes | Mapping[str, str]]: ...
if sys.version_info >= (3, 8):
def tobuf(self, format: int | None = 2, encoding: str | None = "utf-8", errors: str = "surrogateescape") -> bytes: ...
Expand Down
3 changes: 3 additions & 0 deletions tests/stubtest_allowlists/py310.txt
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,6 @@ asynchat.async_chat.use_encoding
asynchat.find_prefix_at_end
pkgutil.ImpImporter\..*
pkgutil.ImpLoader\..*

# Omit internal _KEEP argument
tarfile.TarInfo.replace
3 changes: 3 additions & 0 deletions tests/stubtest_allowlists/py311.txt
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,6 @@ asynchat.async_chat.use_encoding
asynchat.find_prefix_at_end
pkgutil.ImpImporter\..*
pkgutil.ImpLoader\..*

# Omit internal _KEEP argument
tarfile.TarInfo.replace
17 changes: 3 additions & 14 deletions tests/stubtest_allowlists/py312.txt
Original file line number Diff line number Diff line change
Expand Up @@ -137,20 +137,6 @@ ssl.OP_LEGACY_SERVER_CONNECT
ssl.Options.OP_LEGACY_SERVER_CONNECT
ssl.RAND_pseudo_bytes
ssl.wrap_socket
tarfile.AbsoluteLinkError
tarfile.AbsolutePathError
tarfile.FilterError
tarfile.LinkOutsideDestinationError
tarfile.OutsideDestinationError
tarfile.SpecialFileError
tarfile.TarFile.extract
tarfile.TarFile.extractall
tarfile.TarFile.extraction_filter
tarfile.TarInfo.replace
tarfile.__all__
tarfile.data_filter
tarfile.fully_trusted_filter
tarfile.tar_filter
turtle.RawTurtle.teleport
turtle.TNavigator.teleport
turtle.TPen.teleport
Expand Down Expand Up @@ -336,3 +322,6 @@ typing_extensions\.Final
typing\.NamedTuple
typing\.LiteralString
typing\.Annotated

# Omit internal _KEEP argument
tarfile.TarInfo.replace
3 changes: 3 additions & 0 deletions tests/stubtest_allowlists/py38.txt
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,6 @@ asynchat.async_chat.use_encoding
asynchat.find_prefix_at_end
pkgutil.ImpImporter\..*
pkgutil.ImpLoader\..*

# Omit internal _KEEP argument
tarfile.TarInfo.replace
3 changes: 3 additions & 0 deletions tests/stubtest_allowlists/py39.txt
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,6 @@ asynchat.async_chat.use_encoding
asynchat.find_prefix_at_end
pkgutil.ImpImporter\..*
pkgutil.ImpLoader\..*

# Omit internal _KEEP argument
tarfile.TarInfo.replace
15 changes: 15 additions & 0 deletions tests/stubtest_allowlists/py3_common.txt
Original file line number Diff line number Diff line change
Expand Up @@ -613,3 +613,18 @@ typing.IO.__iter__ # See https://github.com/python/typeshed/commit/97bc450acd60
# but have yet to find their way to all GitHub Actions images
(sys.get_int_max_str_digits)?
(sys.set_int_max_str_digits)?

# Added or modified in a patch release, backported to all security branches,
# but have yet to find their way to all GitHub Actions images
(tarfile.tar_filter)?
(tarfile.fully_trusted_filter)?
(tarfile.data_filter)?
(tarfile.TarFile.extractall)?
(tarfile.TarFile.extract)?
(tarfile.SpecialFileError)?
(tarfile.OutsideDestinationError)?
(tarfile.LinkOutsideDestinationError)?
(tarfile.FilterError)?
(tarfile.AbsolutePathError)?
(tarfile.AbsoluteLinkError)?
(shutil.unpack_archive)?

0 comments on commit 5beddbe

Please sign in to comment.