Skip to content

Commit be11e9b

Browse files
committed
Add type hints to relenv/common.py and relenv/runtime.py
1 parent 9785ca4 commit be11e9b

File tree

5 files changed

+2216
-194
lines changed

5 files changed

+2216
-194
lines changed

relenv/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
# Copyright 2025 Broadcom.
22
# SPDX-License-Identifier: Apache-2
3+
import sys
4+
5+
if sys.version_info < (3, 10):
6+
raise RuntimeError("Relenv requires Python 3.10 or newer.")
7+
38
from relenv.common import __version__

relenv/common.py

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"""
44
Common classes and values used around relenv.
55
"""
6+
from __future__ import annotations
7+
68
import http.client
79
import logging
810
import os
@@ -16,6 +18,7 @@
1618
import textwrap
1719
import threading
1820
import time
21+
from typing import Any, BinaryIO, Iterable, Mapping, Optional, Union
1922

2023
# relenv package version
2124
__version__ = "0.21.1"
@@ -101,22 +104,24 @@ class RelenvException(Exception):
101104
"""
102105

103106

104-
def format_shebang(python, tpl=SHEBANG_TPL):
107+
def format_shebang(python: str, tpl: str = SHEBANG_TPL) -> str:
105108
"""
106109
Return a formatted shebang.
107110
"""
108111
return tpl.format(python).strip() + "\n"
109112

110113

111-
def build_arch():
114+
def build_arch() -> str:
112115
"""
113116
Return the current machine.
114117
"""
115118
machine = platform.machine()
116119
return machine.lower()
117120

118121

119-
def work_root(root=None):
122+
def work_root(
123+
root: Optional[Union[str, os.PathLike[str]]] = None,
124+
) -> pathlib.Path:
120125
"""
121126
Get the root directory that all other relenv working directories should be based on.
122127
@@ -133,7 +138,9 @@ def work_root(root=None):
133138
return base
134139

135140

136-
def work_dir(name, root=None):
141+
def work_dir(
142+
name: str, root: Optional[Union[str, os.PathLike[str]]] = None
143+
) -> pathlib.Path:
137144
"""
138145
Get the absolute path to the relenv working directory of the given name.
139146
@@ -161,17 +168,17 @@ class WorkDirs:
161168
:type root: str
162169
"""
163170

164-
def __init__(self, root):
165-
self.root = root
166-
self.data = DATA_DIR
167-
self.toolchain_config = work_dir("toolchain", self.root)
168-
self.toolchain = work_dir("toolchain", DATA_DIR)
169-
self.build = work_dir("build", DATA_DIR)
170-
self.src = work_dir("src", DATA_DIR)
171-
self.logs = work_dir("logs", DATA_DIR)
172-
self.download = work_dir("download", DATA_DIR)
171+
def __init__(self, root: Union[str, os.PathLike[str]]) -> None:
172+
self.root: pathlib.Path = pathlib.Path(root)
173+
self.data: pathlib.Path = DATA_DIR
174+
self.toolchain_config: pathlib.Path = work_dir("toolchain", self.root)
175+
self.toolchain: pathlib.Path = work_dir("toolchain", DATA_DIR)
176+
self.build: pathlib.Path = work_dir("build", DATA_DIR)
177+
self.src: pathlib.Path = work_dir("src", DATA_DIR)
178+
self.logs: pathlib.Path = work_dir("logs", DATA_DIR)
179+
self.download: pathlib.Path = work_dir("download", DATA_DIR)
173180

174-
def __getstate__(self):
181+
def __getstate__(self) -> dict[str, pathlib.Path]:
175182
"""
176183
Return an object used for pickling.
177184
@@ -187,7 +194,7 @@ def __getstate__(self):
187194
"download": self.download,
188195
}
189196

190-
def __setstate__(self, state):
197+
def __setstate__(self, state: Mapping[str, pathlib.Path]) -> None:
191198
"""
192199
Unwrap the object returned from unpickling.
193200
@@ -203,7 +210,9 @@ def __setstate__(self, state):
203210
self.download = state["download"]
204211

205212

206-
def work_dirs(root=None):
213+
def work_dirs(
214+
root: Optional[Union[str, os.PathLike[str]]] = None,
215+
) -> WorkDirs:
207216
"""
208217
Returns a WorkDirs instance based on the given root.
209218
@@ -216,7 +225,10 @@ def work_dirs(root=None):
216225
return WorkDirs(work_root(root))
217226

218227

219-
def get_toolchain(arch=None, root=None):
228+
def get_toolchain(
229+
arch: Optional[str] = None,
230+
root: Optional[Union[str, os.PathLike[str]]] = None,
231+
) -> Optional[pathlib.Path]:
220232
"""
221233
Get a the toolchain directory, specific to the arch if supplied.
222234
@@ -250,7 +262,7 @@ def get_toolchain(arch=None, root=None):
250262
return TOOLCHAIN_PATH
251263

252264

253-
def get_triplet(machine=None, plat=None):
265+
def get_triplet(machine: Optional[str] = None, plat: Optional[str] = None) -> str:
254266
"""
255267
Get the target triplet for the specified machine and platform.
256268
@@ -280,7 +292,7 @@ def get_triplet(machine=None, plat=None):
280292
raise RelenvException(f"Unknown platform {plat}")
281293

282294

283-
def plat_from_triplet(plat):
295+
def plat_from_triplet(plat: str) -> str:
284296
"""
285297
Convert platform from build to the value of sys.platform.
286298
"""
@@ -293,11 +305,11 @@ def plat_from_triplet(plat):
293305
raise RelenvException(f"Unkown platform {plat}")
294306

295307

296-
def list_archived_builds():
308+
def list_archived_builds() -> list[tuple[str, str, str]]:
297309
"""
298310
Return a list of version, architecture and platforms for builds.
299311
"""
300-
builds = []
312+
builds: list[tuple[str, str, str]] = []
301313
dirs = work_dirs(DATA_DIR)
302314
for root, dirs, files in os.walk(dirs.build):
303315
for file in files:
@@ -309,7 +321,7 @@ def list_archived_builds():
309321
return builds
310322

311323

312-
def archived_build(triplet=None):
324+
def archived_build(triplet: Optional[str] = None) -> pathlib.Path:
313325
"""
314326
Finds a the location of an archived build.
315327
@@ -326,7 +338,9 @@ def archived_build(triplet=None):
326338
return dirs.build / archive
327339

328340

329-
def extract_archive(to_dir, archive):
341+
def extract_archive(
342+
to_dir: Union[str, os.PathLike[str]], archive: Union[str, os.PathLike[str]]
343+
) -> None:
330344
"""
331345
Extract an archive to a specific location.
332346
@@ -354,7 +368,7 @@ def extract_archive(to_dir, archive):
354368
t.extractall(to_dir)
355369

356370

357-
def get_download_location(url, dest):
371+
def get_download_location(url: str, dest: Union[str, os.PathLike[str]]) -> str:
358372
"""
359373
Get the full path to where the url will be downloaded to.
360374
@@ -369,7 +383,7 @@ def get_download_location(url, dest):
369383
return os.path.join(dest, os.path.basename(url))
370384

371385

372-
def check_url(url, timestamp=None, timeout=30):
386+
def check_url(url: str, timestamp: Optional[float] = None, timeout: float = 30) -> bool:
373387
"""
374388
Check that the url returns a 200.
375389
"""
@@ -400,7 +414,7 @@ def check_url(url, timestamp=None, timeout=30):
400414
return True
401415

402416

403-
def fetch_url(url, fp, backoff=3, timeout=30):
417+
def fetch_url(url: str, fp: BinaryIO, backoff: int = 3, timeout: float = 30) -> None:
404418
"""
405419
Fetch the contents of a url.
406420
@@ -447,7 +461,7 @@ def fetch_url(url, fp, backoff=3, timeout=30):
447461
log.info("Download complete %s", url)
448462

449463

450-
def fetch_url_content(url, backoff=3, timeout=30):
464+
def fetch_url_content(url: str, backoff: int = 3, timeout: float = 30) -> str:
451465
"""
452466
Fetch the contents of a url.
453467
@@ -503,7 +517,13 @@ def fetch_url_content(url, backoff=3, timeout=30):
503517
return fp.read().decode()
504518

505519

506-
def download_url(url, dest, verbose=True, backoff=3, timeout=60):
520+
def download_url(
521+
url: str,
522+
dest: Union[str, os.PathLike[str]],
523+
verbose: bool = True,
524+
backoff: int = 3,
525+
timeout: float = 60,
526+
) -> str:
507527
"""
508528
Download the url to the provided destination.
509529
@@ -541,7 +561,7 @@ def download_url(url, dest, verbose=True, backoff=3, timeout=60):
541561
return local
542562

543563

544-
def runcmd(*args, **kwargs):
564+
def runcmd(*args: Any, **kwargs: Any) -> subprocess.Popen[str]:
545565
"""
546566
Run a command.
547567
@@ -626,7 +646,11 @@ def enqueue_process(process, queue):
626646
return p
627647

628648

629-
def relative_interpreter(root_dir, scripts_dir, interpreter):
649+
def relative_interpreter(
650+
root_dir: Union[str, os.PathLike[str]],
651+
scripts_dir: Union[str, os.PathLike[str]],
652+
interpreter: Union[str, os.PathLike[str]],
653+
) -> pathlib.Path:
630654
"""
631655
Return a relativized path to the given scripts_dir and interpreter.
632656
"""
@@ -644,7 +668,7 @@ def relative_interpreter(root_dir, scripts_dir, interpreter):
644668
return relscripts / relinterp
645669

646670

647-
def makepath(*paths):
671+
def makepath(*paths: Union[str, os.PathLike[str]]) -> tuple[str, str]:
648672
"""
649673
Make a normalized path name from paths.
650674
"""
@@ -656,15 +680,15 @@ def makepath(*paths):
656680
return dir, os.path.normcase(dir)
657681

658682

659-
def addpackage(sitedir, name):
683+
def addpackage(sitedir: str, name: Union[str, os.PathLike[str]]) -> list[str] | None:
660684
"""
661685
Add editable package to path.
662686
"""
663687
import io
664688
import stat
665689

666690
fullname = os.path.join(sitedir, name)
667-
paths = []
691+
paths: list[str] = []
668692
try:
669693
st = os.lstat(fullname)
670694
except OSError:
@@ -710,11 +734,11 @@ def addpackage(sitedir, name):
710734
return paths
711735

712736

713-
def sanitize_sys_path(sys_path_entries):
737+
def sanitize_sys_path(sys_path_entries: Iterable[str]) -> list[str]:
714738
"""
715739
Sanitize `sys.path` to only include paths relative to the onedir environment.
716740
"""
717-
__sys_path = []
741+
__sys_path: list[str] = []
718742
__valid_path_prefixes = tuple(
719743
{
720744
pathlib.Path(sys.prefix).resolve(),
@@ -735,6 +759,8 @@ def sanitize_sys_path(sys_path_entries):
735759
for known_path in __sys_path[:]:
736760
for _ in pathlib.Path(known_path).glob("__editable__.*.pth"):
737761
paths = addpackage(known_path, _)
762+
if not paths:
763+
continue
738764
for p in paths:
739765
if p not in __sys_path:
740766
__sys_path.append(p)
@@ -746,11 +772,14 @@ class Version:
746772
Version comparisons.
747773
"""
748774

749-
def __init__(self, data):
750-
self.major, self.minor, self.micro = self.parse_string(data)
751-
self._data = data
775+
def __init__(self, data: str) -> None:
776+
major, minor, micro = self.parse_string(data)
777+
self.major: int = major
778+
self.minor: Optional[int] = minor
779+
self.micro: Optional[int] = micro
780+
self._data: str = data
752781

753-
def __str__(self):
782+
def __str__(self) -> str:
754783
"""
755784
Version as string.
756785
"""
@@ -762,7 +791,7 @@ def __str__(self):
762791
# XXX What if minor was None but micro was an int.
763792
return _
764793

765-
def __hash__(self):
794+
def __hash__(self) -> int:
766795
"""
767796
Hash of the version.
768797
@@ -771,7 +800,7 @@ def __hash__(self):
771800
return hash((self.major, self.minor, self.micro))
772801

773802
@staticmethod
774-
def parse_string(data):
803+
def parse_string(data: str) -> tuple[int, Optional[int], Optional[int]]:
775804
"""
776805
Parse a version string into major, minor, and micro integers.
777806
"""
@@ -785,7 +814,7 @@ def parse_string(data):
785814
else:
786815
raise RuntimeError("Too many parts to parse")
787816

788-
def __eq__(self, other):
817+
def __eq__(self, other: "Version") -> bool:
789818
"""
790819
Equality comparisons.
791820
"""
@@ -797,7 +826,7 @@ def __eq__(self, other):
797826
micro = 0 if other.micro is None else other.micro
798827
return mymajor == major and myminor == minor and mymicro == micro
799828

800-
def __lt__(self, other):
829+
def __lt__(self, other: "Version") -> bool:
801830
"""
802831
Less than comparrison.
803832
"""
@@ -816,7 +845,7 @@ def __lt__(self, other):
816845
return True
817846
return False
818847

819-
def __le__(self, other):
848+
def __le__(self, other: "Version") -> bool:
820849
"""
821850
Less than or equal to comparrison.
822851
"""
@@ -832,13 +861,13 @@ def __le__(self, other):
832861
return True
833862
return False
834863

835-
def __gt__(self, other):
864+
def __gt__(self, other: "Version") -> bool:
836865
"""
837866
Greater than comparrison.
838867
"""
839868
return not self.__le__(other)
840869

841-
def __ge__(self, other):
870+
def __ge__(self, other: "Version") -> bool:
842871
"""
843872
Greater than or equal to comparrison.
844873
"""

0 commit comments

Comments
 (0)