Skip to content

Commit

Permalink
General typing improvement (#610)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sachaa-Thanasius authored Apr 29, 2024
1 parent 0a4f40e commit 754a238
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 70 deletions.
4 changes: 2 additions & 2 deletions src/wheel/_setuptools_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import sys


def _not_warning(record):
def _not_warning(record: logging.LogRecord) -> bool:
return record.levelno < logging.WARNING


def configure():
def configure() -> None:
"""
Configure logging to emit warning and above to stderr
and everything else to stdout. This behavior is provided
Expand Down
70 changes: 40 additions & 30 deletions src/wheel/bdist_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from email.policy import EmailPolicy
from glob import iglob
from shutil import rmtree
from typing import TYPE_CHECKING, Callable, Iterable, Literal, Sequence, cast
from zipfile import ZIP_DEFLATED, ZIP_STORED

import setuptools
Expand All @@ -31,15 +32,18 @@
from .vendored.packaging import version as _packaging_version
from .wheelfile import WheelFile

if TYPE_CHECKING:
import types

def safe_name(name):

def safe_name(name: str) -> str:
"""Convert an arbitrary string to a standard distribution name
Any runs of non-alphanumeric/. characters are replaced with a single '-'.
"""
return re.sub("[^A-Za-z0-9.]+", "-", name)


def safe_version(version):
def safe_version(version: str) -> str:
"""
Convert an arbitrary string to a standard version string
"""
Expand All @@ -56,15 +60,15 @@ def safe_version(version):
PY_LIMITED_API_PATTERN = r"cp3\d"


def _is_32bit_interpreter():
def _is_32bit_interpreter() -> bool:
return struct.calcsize("P") == 4


def python_tag():
def python_tag() -> str:
return f"py{sys.version_info[0]}"


def get_platform(archive_root):
def get_platform(archive_root: str | None) -> str:
"""Return our platform name 'win32', 'linux_x86_64'"""
result = sysconfig.get_platform()
if result.startswith("macosx") and archive_root is not None:
Expand All @@ -82,7 +86,9 @@ def get_platform(archive_root):
return result.replace("-", "_")


def get_flag(var, fallback, expected=True, warn=True):
def get_flag(
var: str, fallback: bool, expected: bool = True, warn: bool = True
) -> bool:
"""Use a fallback value for determining SOABI flags if the needed config
var is unset or unavailable."""
val = sysconfig.get_config_var(var)
Expand All @@ -97,9 +103,9 @@ def get_flag(var, fallback, expected=True, warn=True):
return val == expected


def get_abi_tag():
def get_abi_tag() -> str | None:
"""Return the ABI tag based on SOABI (if available) or emulate SOABI (PyPy2)."""
soabi = sysconfig.get_config_var("SOABI")
soabi: str = sysconfig.get_config_var("SOABI")
impl = tags.interpreter_name()
if not soabi and impl in ("cp", "pp") and hasattr(sys, "maxunicode"):
d = ""
Expand Down Expand Up @@ -137,19 +143,23 @@ def get_abi_tag():
return abi


def safer_name(name):
def safer_name(name: str) -> str:
return safe_name(name).replace("-", "_")


def safer_version(version):
def safer_version(version: str) -> str:
return safe_version(version).replace("-", "_")


def remove_readonly(func, path, excinfo):
def remove_readonly(
func: Callable[..., object],
path: str,
excinfo: tuple[type[Exception], Exception, types.TracebackType],
) -> None:
remove_readonly_exc(func, path, excinfo[1])


def remove_readonly_exc(func, path, exc):
def remove_readonly_exc(func: Callable[..., object], path: str, exc: Exception) -> None:
os.chmod(path, stat.S_IWRITE)
func(path)

Expand Down Expand Up @@ -224,24 +234,24 @@ class bdist_wheel(Command):
boolean_options = ["keep-temp", "skip-build", "relative", "universal"]

def initialize_options(self):
self.bdist_dir = None
self.bdist_dir: str = None
self.data_dir = None
self.plat_name = None
self.plat_name: str | None = None
self.plat_tag = None
self.format = "zip"
self.keep_temp = False
self.dist_dir = None
self.dist_dir: str | None = None
self.egginfo_dir = None
self.root_is_pure = None
self.root_is_pure: bool | None = None
self.skip_build = None
self.relative = False
self.owner = None
self.group = None
self.universal = False
self.compression = "deflated"
self.python_tag = python_tag()
self.build_number = None
self.py_limited_api = False
self.universal: bool = False
self.compression: str | int = "deflated"
self.python_tag: str = python_tag()
self.build_number: str | None = None
self.py_limited_api: str | Literal[False] = False
self.plat_name_supplied = False

def finalize_options(self):
Expand Down Expand Up @@ -298,11 +308,11 @@ def wheel_dist_name(self):
components += (self.build_number,)
return "-".join(components)

def get_tag(self):
def get_tag(self) -> tuple[str, str, str]:
# bdist sets self.plat_name if unset, we should only use it for purepy
# wheels if the user supplied it.
if self.plat_name_supplied:
plat_name = self.plat_name
plat_name = cast(str, self.plat_name)
elif self.root_is_pure:
plat_name = "any"
else:
Expand Down Expand Up @@ -447,7 +457,7 @@ def run(self):
rmtree(self.bdist_dir, onexc=remove_readonly_exc)

def write_wheelfile(
self, wheelfile_base, generator="bdist_wheel (" + wheel_version + ")"
self, wheelfile_base: str, generator: str = f"bdist_wheel ({wheel_version})"
):
from email.message import Message

Expand All @@ -470,24 +480,24 @@ def write_wheelfile(
with open(wheelfile_path, "wb") as f:
BytesGenerator(f, maxheaderlen=0).flatten(msg)

def _ensure_relative(self, path):
def _ensure_relative(self, path: str) -> str:
# copied from dir_util, deleted
drive, path = os.path.splitdrive(path)
if path[0:1] == os.sep:
path = drive + path[1:]
return path

@property
def license_paths(self):
def license_paths(self) -> Iterable[str]:
if setuptools_major_version >= 57:
# Setuptools has resolved any patterns to actual file names
return self.distribution.metadata.license_files or ()

files = set()
files: set[str] = set()
metadata = self.distribution.get_option_dict("metadata")
if setuptools_major_version >= 42:
# Setuptools recognizes the license_files option but does not do globbing
patterns = self.distribution.metadata.license_files
patterns = cast(Sequence[str], self.distribution.metadata.license_files)
else:
# Prior to those, wheel is entirely responsible for handling license files
if "license_files" in metadata:
Expand Down Expand Up @@ -522,10 +532,10 @@ def license_paths(self):

return files

def egg2dist(self, egginfo_path, distinfo_path):
def egg2dist(self, egginfo_path: str, distinfo_path: str):
"""Convert an .egg-info directory into a .dist-info directory"""

def adios(p):
def adios(p: str) -> None:
"""Appropriately delete directory, file or link."""
if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p):
shutil.rmtree(p)
Expand Down
10 changes: 5 additions & 5 deletions src/wheel/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ class WheelError(Exception):
pass


def unpack_f(args):
def unpack_f(args: argparse.Namespace) -> None:
from .unpack import unpack

unpack(args.wheelfile, args.dest)


def pack_f(args):
def pack_f(args: argparse.Namespace) -> None:
from .pack import pack

pack(args.directory, args.dest_dir, args.build_number)


def convert_f(args):
def convert_f(args: argparse.Namespace) -> None:
from .convert import convert

convert(args.files, args.dest_dir, args.verbose)


def tags_f(args):
def tags_f(args: argparse.Namespace) -> None:
from .tags import tags

names = (
Expand All @@ -51,7 +51,7 @@ def tags_f(args):
print(name)


def version_f(args):
def version_f(args: argparse.Namespace) -> None:
from .. import __version__

print("wheel %s" % __version__)
Expand Down
8 changes: 4 additions & 4 deletions src/wheel/cli/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def egg2wheel(egg_path: str, dest_dir: str) -> None:
shutil.rmtree(dir)


def parse_wininst_info(wininfo_name, egginfo_name):
def parse_wininst_info(wininfo_name: str, egginfo_name: str | None):
"""Extract metadata from filenames.
Extracts the 4 metadataitems needed (name, version, pyversion, arch) from
Expand Down Expand Up @@ -167,7 +167,7 @@ def parse_wininst_info(wininfo_name, egginfo_name):
return {"name": w_name, "ver": w_ver, "arch": w_arch, "pyver": w_pyver}


def wininst2wheel(path, dest_dir):
def wininst2wheel(path: str, dest_dir: str) -> None:
with zipfile.ZipFile(path) as bdw:
# Search for egg-info in the archive
egginfo_name = None
Expand All @@ -193,7 +193,7 @@ def wininst2wheel(path, dest_dir):

# rewrite paths to trick ZipFile into extracting an egg
# XXX grab wininst .ini - between .exe, padding, and first zip file.
members = []
members: list[str] = []
egginfo_name = ""
for zipinfo in bdw.infolist():
key, basename = zipinfo.filename.split("/", 1)
Expand Down Expand Up @@ -257,7 +257,7 @@ def wininst2wheel(path, dest_dir):
shutil.rmtree(dir)


def convert(files, dest_dir, verbose):
def convert(files: list[str], dest_dir: str, verbose: bool) -> None:
for pat in files:
for installer in iglob(pat):
if os.path.splitext(installer)[1] == ".egg":
Expand Down
31 changes: 22 additions & 9 deletions src/wheel/macosx_libfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
import ctypes
import os
import sys
from io import BufferedIOBase
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Union

StrPath = Union[str, os.PathLike[str]]

"""here the needed const and struct from mach-o header files"""

Expand Down Expand Up @@ -238,7 +245,7 @@
"""


def swap32(x):
def swap32(x: int) -> int:
return (
((x << 24) & 0xFF000000)
| ((x << 8) & 0x00FF0000)
Expand All @@ -247,7 +254,10 @@ def swap32(x):
)


def get_base_class_and_magic_number(lib_file, seek=None):
def get_base_class_and_magic_number(
lib_file: BufferedIOBase,
seek: int | None = None,
) -> tuple[type[ctypes.Structure], int]:
if seek is None:
seek = lib_file.tell()
else:
Expand All @@ -271,11 +281,11 @@ def get_base_class_and_magic_number(lib_file, seek=None):
return BaseClass, magic_number


def read_data(struct_class, lib_file):
def read_data(struct_class: type[ctypes.Structure], lib_file: BufferedIOBase):
return struct_class.from_buffer_copy(lib_file.read(ctypes.sizeof(struct_class)))


def extract_macosx_min_system_version(path_to_lib):
def extract_macosx_min_system_version(path_to_lib: str):
with open(path_to_lib, "rb") as lib_file:
BaseClass, magic_number = get_base_class_and_magic_number(lib_file, 0)
if magic_number not in [FAT_MAGIC, FAT_MAGIC_64, MH_MAGIC, MH_MAGIC_64]:
Expand All @@ -301,7 +311,7 @@ class FatArch(BaseClass):
read_data(FatArch, lib_file) for _ in range(fat_header.nfat_arch)
]

versions_list = []
versions_list: list[tuple[int, int, int]] = []
for el in fat_arch_list:
try:
version = read_mach_header(lib_file, el.offset)
Expand Down Expand Up @@ -333,7 +343,10 @@ class FatArch(BaseClass):
return None


def read_mach_header(lib_file, seek=None):
def read_mach_header(
lib_file: BufferedIOBase,
seek: int | None = None,
) -> tuple[int, int, int] | None:
"""
This function parses a Mach-O header and extracts
information about the minimal macOS version.
Expand Down Expand Up @@ -380,14 +393,14 @@ class VersionBuild(base_class):
continue


def parse_version(version):
def parse_version(version: int) -> tuple[int, int, int]:
x = (version & 0xFFFF0000) >> 16
y = (version & 0x0000FF00) >> 8
z = version & 0x000000FF
return x, y, z


def calculate_macosx_platform_tag(archive_root, platform_tag):
def calculate_macosx_platform_tag(archive_root: StrPath, platform_tag: str) -> str:
"""
Calculate proper macosx platform tag basing on files which are included to wheel
Expand Down Expand Up @@ -420,7 +433,7 @@ def calculate_macosx_platform_tag(archive_root, platform_tag):

assert len(base_version) == 2
start_version = base_version
versions_dict = {}
versions_dict: dict[str, tuple[int, int]] = {}
for dirpath, _dirnames, filenames in os.walk(archive_root):
for filename in filenames:
if filename.endswith(".dylib") or filename.endswith(".so"):
Expand Down
Loading

0 comments on commit 754a238

Please sign in to comment.