1 |
commit: b07b25dcc8ad0e518d801bc23d01fb59cc6de442 |
2 |
Author: Mike Frysinger <vapier <AT> chromium <DOT> org> |
3 |
AuthorDate: Wed Sep 28 06:03:01 2022 +0000 |
4 |
Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> |
5 |
CommitDate: Wed Sep 28 07:42:17 2022 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=b07b25dc |
7 |
|
8 |
lddtree: add typing info to more places |
9 |
|
10 |
Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org> |
11 |
|
12 |
lddtree.py | 87 +++++++++++++++++++++++++++++++++++++++++++------------------- |
13 |
1 file changed, 61 insertions(+), 26 deletions(-) |
14 |
|
15 |
diff --git a/lddtree.py b/lddtree.py |
16 |
index ecb353d..6939bb6 100755 |
17 |
--- a/lddtree.py |
18 |
+++ b/lddtree.py |
19 |
@@ -48,6 +48,7 @@ import mmap |
20 |
import os |
21 |
import shutil |
22 |
import sys |
23 |
+from typing import Any, Iterable, Optional, Union |
24 |
|
25 |
assert sys.version_info >= (3, 6), f'Python 3.6+ required, but found {sys.version}' |
26 |
|
27 |
@@ -60,31 +61,31 @@ from elftools.common import exceptions |
28 |
from elftools.elf.elffile import ELFFile |
29 |
|
30 |
|
31 |
-def warn(msg, prefix='warning'): |
32 |
+def warn(msg: Any, prefix: Optional[str] = "warning") -> None: |
33 |
"""Write |msg| to stderr with a |prefix| before it""" |
34 |
print('%s: %s: %s' % (os.path.basename(sys.argv[0]), prefix, msg), file=sys.stderr) |
35 |
|
36 |
|
37 |
-def err(msg, status=1): |
38 |
+def err(msg: Any, status: Optional[int] = 1) -> None: |
39 |
"""Write |msg| to stderr and exit with |status|""" |
40 |
warn(msg, prefix='error') |
41 |
sys.exit(status) |
42 |
|
43 |
|
44 |
-def dbg(debug, *args, **kwargs): |
45 |
+def dbg(debug: bool, *args, **kwargs) -> None: |
46 |
"""Pass |args| and |kwargs| to print() when |debug| is True""" |
47 |
if debug: |
48 |
print(*args, **kwargs) |
49 |
|
50 |
|
51 |
-def bstr(buf): |
52 |
+def bstr(buf: Union[bytes, str]) -> str: |
53 |
"""Decode the byte string into a string""" |
54 |
if isinstance(buf, str): |
55 |
return buf |
56 |
return buf.decode('utf-8') |
57 |
|
58 |
|
59 |
-def normpath(path): |
60 |
+def normpath(path: str) -> str: |
61 |
"""Normalize a path |
62 |
|
63 |
Python's os.path.normpath() doesn't handle some cases: |
64 |
@@ -96,7 +97,7 @@ def normpath(path): |
65 |
|
66 |
|
67 |
@functools.lru_cache(maxsize=None) |
68 |
-def readlink(path, root, prefixed=False): |
69 |
+def readlink(path: str, root: str, prefixed: Optional[bool] = False) -> str: |
70 |
"""Like os.readlink(), but relative to a |root| |
71 |
|
72 |
This does not currently handle the pathological case: |
73 |
@@ -124,14 +125,14 @@ def readlink(path, root, prefixed=False): |
74 |
return normpath((root + path) if prefixed else path) |
75 |
|
76 |
|
77 |
-def dedupe(items): |
78 |
+def dedupe(items: list[str]) -> list[str]: |
79 |
"""Remove all duplicates from |items| (keeping order)""" |
80 |
- seen = {} |
81 |
+ seen: dict[str, str] = {} |
82 |
return [seen.setdefault(x, x) for x in items if x not in seen] |
83 |
|
84 |
|
85 |
@functools.lru_cache(maxsize=None) |
86 |
-def interp_supports_argv0(interp) -> bool: |
87 |
+def interp_supports_argv0(interp: str) -> bool: |
88 |
"""See whether |interp| supports the --argv0 option. |
89 |
|
90 |
Starting with glibc-2.33, the ldso supports --argv0 to override argv[0]. |
91 |
@@ -141,7 +142,12 @@ def interp_supports_argv0(interp) -> bool: |
92 |
return mm.find(b'--argv0') >= 0 |
93 |
|
94 |
|
95 |
-def GenerateLdsoWrapper(root, path, interp, libpaths=()): |
96 |
+def GenerateLdsoWrapper( |
97 |
+ root: str, |
98 |
+ path: str, |
99 |
+ interp: str, |
100 |
+ libpaths: Iterable[str] = (), |
101 |
+) -> None: |
102 |
"""Generate a shell script wrapper which uses local ldso to run the ELF |
103 |
|
104 |
Since we cannot rely on the host glibc (or other libraries), we need to |
105 |
@@ -190,7 +196,12 @@ exec \\ |
106 |
|
107 |
|
108 |
@functools.lru_cache(maxsize=None) |
109 |
-def ParseLdPaths(str_ldpaths, root='', cwd=None, path=None): |
110 |
+def ParseLdPaths( |
111 |
+ str_ldpaths: str, |
112 |
+ root: str = "", |
113 |
+ cwd: Optional[str] = None, |
114 |
+ path: str = "", |
115 |
+) -> list[str]: |
116 |
"""Parse the colon-delimited list of paths and apply ldso rules to each |
117 |
|
118 |
Note the special handling as dictated by the ldso: |
119 |
@@ -232,7 +243,12 @@ def ParseLdPaths(str_ldpaths, root='', cwd=None, path=None): |
120 |
return dedupe(ldpaths) |
121 |
|
122 |
|
123 |
-def ParseLdSoConf(ldso_conf, root='/', debug=False, _first=True): |
124 |
+def ParseLdSoConf( |
125 |
+ ldso_conf: str, |
126 |
+ root: str = "/", |
127 |
+ debug: bool = False, |
128 |
+ _first: bool = True, |
129 |
+) -> list[str]: |
130 |
"""Load all the paths from a given ldso config file |
131 |
|
132 |
This should handle comments, whitespace, and "include" statements. |
133 |
@@ -283,7 +299,12 @@ def ParseLdSoConf(ldso_conf, root='/', debug=False, _first=True): |
134 |
return paths |
135 |
|
136 |
|
137 |
-def LoadLdpaths(root='/', cwd=None, prefix='', debug=False): |
138 |
+def LoadLdpaths( |
139 |
+ root: str = "/", |
140 |
+ cwd: Optional[str] = None, |
141 |
+ prefix: str = "", |
142 |
+ debug: bool = False, |
143 |
+) -> dict[str, list[str]]: |
144 |
"""Load linker paths from common locations |
145 |
|
146 |
This parses the ld.so.conf and LD_LIBRARY_PATH env var. |
147 |
@@ -297,7 +318,7 @@ def LoadLdpaths(root='/', cwd=None, prefix='', debug=False): |
148 |
Returns: |
149 |
dict containing library paths to search |
150 |
""" |
151 |
- ldpaths = { |
152 |
+ ldpaths: dict[str, list[str]] = { |
153 |
'conf': [], |
154 |
'env': [], |
155 |
'interp': [], |
156 |
@@ -321,7 +342,7 @@ def LoadLdpaths(root='/', cwd=None, prefix='', debug=False): |
157 |
return ldpaths |
158 |
|
159 |
|
160 |
-def CompatibleELFs(elf1, elf2): |
161 |
+def CompatibleELFs(elf1: ELFFile, elf2: ELFFile) -> bool: |
162 |
"""See if two ELFs are compatible |
163 |
|
164 |
This compares the aspects of the ELF to see if they're compatible: |
165 |
@@ -344,7 +365,13 @@ def CompatibleELFs(elf1, elf2): |
166 |
elf1.header['e_machine'] == elf2.header['e_machine']) |
167 |
|
168 |
|
169 |
-def FindLib(elf, lib, ldpaths, root='/', debug=False): |
170 |
+def FindLib( |
171 |
+ elf: ELFFile, |
172 |
+ lib: str, |
173 |
+ ldpaths: list[str], |
174 |
+ root: str = "/", |
175 |
+ debug: bool = False, |
176 |
+) -> tuple[Optional[str], Optional[str]]: |
177 |
"""Try to locate a |lib| that is compatible to |elf| in the given |ldpaths| |
178 |
|
179 |
Args: |
180 |
@@ -381,9 +408,17 @@ def FindLib(elf, lib, ldpaths, root='/', debug=False): |
181 |
|
182 |
# We abuse the _all_libs state. We probably shouldn't, but we do currently. |
183 |
# pylint: disable=dangerous-default-value |
184 |
-def ParseELF(path, root='/', cwd=None, prefix='', |
185 |
- ldpaths={'conf':[], 'env':[], 'interp':[]}, |
186 |
- display=None, debug=False, _first=True, _all_libs={}): |
187 |
+def ParseELF( |
188 |
+ path: str, |
189 |
+ root: str = "/", |
190 |
+ cwd: Optional[str] = None, |
191 |
+ prefix: str = "", |
192 |
+ ldpaths={"conf": [], "env": [], "interp": []}, |
193 |
+ display: Optional[str] = None, |
194 |
+ debug: bool = False, |
195 |
+ _first: bool = True, |
196 |
+ _all_libs={}, |
197 |
+) -> dict[str, Any]: |
198 |
"""Parse the ELF dependency tree of the specified file |
199 |
|
200 |
Args: |
201 |
@@ -522,7 +557,7 @@ def ParseELF(path, root='/', cwd=None, prefix='', |
202 |
'path': fullpath, |
203 |
'needed': [], |
204 |
} |
205 |
- if fullpath: |
206 |
+ if realpath is not None: |
207 |
try: |
208 |
lret = ParseELF(realpath, root, cwd, prefix, ldpaths, display=fullpath, |
209 |
debug=debug, _first=False, _all_libs=_all_libs) |
210 |
@@ -541,7 +576,7 @@ class _NormalizePathAction(argparse.Action): |
211 |
setattr(namespace, self.dest, normpath(values)) |
212 |
|
213 |
|
214 |
-def _ActionShow(options, elf): |
215 |
+def _ActionShow(options: argparse.Namespace, elf: dict): |
216 |
"""Show the dependency tree for this ELF""" |
217 |
def _show(lib, depth): |
218 |
chain_libs.append(lib) |
219 |
@@ -568,7 +603,7 @@ def _ActionShow(options, elf): |
220 |
|
221 |
shown_libs = set(elf['needed']) |
222 |
new_libs = elf['needed'][:] |
223 |
- chain_libs = [] |
224 |
+ chain_libs: list[str] = [] |
225 |
interp = elf['interp'] |
226 |
if interp: |
227 |
lib = os.path.basename(interp) |
228 |
@@ -590,9 +625,9 @@ def _ActionShow(options, elf): |
229 |
_show(lib, 1) |
230 |
|
231 |
|
232 |
-def _ActionCopy(options, elf): |
233 |
+def _ActionCopy(options: argparse.Namespace, elf: dict): |
234 |
"""Copy the ELF and its dependencies to a destination tree""" |
235 |
- def _StripRoot(path): |
236 |
+ def _StripRoot(path: str) -> str: |
237 |
return path[len(options.root) - 1:] |
238 |
|
239 |
def _copy(realsrc, src, striproot=True, wrapit=False, libpaths=(), |
240 |
@@ -687,7 +722,7 @@ def _ActionCopy(options, elf): |
241 |
outdir=options.bindir) |
242 |
|
243 |
|
244 |
-def GetParser(): |
245 |
+def GetParser() -> argparse.ArgumentParser: |
246 |
"""Get a CLI parser.""" |
247 |
parser = argparse.ArgumentParser( |
248 |
description=__doc__, |
249 |
@@ -758,7 +793,7 @@ def GetParser(): |
250 |
return parser |
251 |
|
252 |
|
253 |
-def main(argv): |
254 |
+def main(argv: list[str]) -> Optional[int]: |
255 |
"""The main entry point!""" |
256 |
parser = GetParser() |
257 |
options = parser.parse_args(argv) |