-
-
Notifications
You must be signed in to change notification settings - Fork 210
/
common.py
130 lines (109 loc) · 4.16 KB
/
common.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""Common utility functions shared between cx_Freeze modules."""
from __future__ import annotations
import shutil
from contextlib import suppress
from pathlib import Path, PurePath
from textwrap import dedent
from types import CodeType
from cx_Freeze._typing import IncludesList, InternalIncludesList
from cx_Freeze.exception import OptionError
class FilePath(Path):
"""Subclass of concrete Path to be used in TemporaryPath."""
_flavour = type(Path())._flavour
def replace(self, target):
"""Rename this path to the target path, overwriting if that path
exists. Extended to support move between file systems.
"""
with suppress(OSError):
return super().replace(target)
shutil.copyfile(self, target)
with suppress(FileNotFoundError):
self.unlink()
return self.__class__(target)
def get_resource_file_path(
dirname: str | Path, name: str | Path, ext: str
) -> Path | None:
"""Return the path to a resource file shipped with cx_Freeze.
This is used to find our base executables and initscripts when they are
just specified by name.
"""
pname = Path(name)
if pname.is_absolute():
return pname
pname = Path(__file__).resolve().parent / dirname / pname.with_suffix(ext)
if pname.exists():
return pname
# Support for name argument in the old Camelcase value
pname = pname.with_name(pname.name.lower())
if pname.exists():
return pname
return None
def normalize_to_list(
value: str | list[str] | tuple[str, ...] | None
) -> list[str]:
"""Takes the different formats of options containing multiple values and
returns the value as a list object.
"""
if value is None:
return []
if isinstance(value, str):
return value.split(",")
return list(value)
def process_path_specs(specs: IncludesList | None) -> InternalIncludesList:
"""Prepare paths specified as config.
The input is a list of either strings, or 2-tuples (source, target).
Where single strings are supplied, the basenames are used as targets.
Where targets are given explicitly, they must not be absolute paths.
Returns a list of 2-tuples, or throws OptionError if something is wrong
in the input.
"""
if specs is None:
specs = []
processed_specs: InternalIncludesList = []
for spec in specs:
if not isinstance(spec, (list, tuple)):
source = spec
target = None
elif len(spec) != 2:
error = "path spec must be a list or tuple of length two"
raise OptionError(error)
else:
source, target = spec
source = Path(source)
if not source.exists():
raise OptionError(f"cannot find file/directory named {source!s}")
target = PurePath(target or source.name)
if target.is_absolute():
error = f"target path named {target!s} cannot be absolute"
raise OptionError(error)
processed_specs.append((source, target))
return processed_specs
def code_object_replace(code: CodeType, **kwargs) -> CodeType:
"""Return a copy of the code object with new values for the specified
fields.
"""
with suppress(ValueError, KeyError):
kwargs["co_consts"] = tuple(kwargs["co_consts"])
return code.replace(**kwargs)
def code_object_replace_function(
code: CodeType, name: str, source: str
) -> CodeType:
"""Return a copy of the code object with the function 'name' replaced."""
if code is None:
return code
new_code = compile(dedent(source), code.co_filename, "exec")
new_co_func = None
for constant in new_code.co_consts:
if isinstance(constant, CodeType) and constant.co_name == name:
new_co_func = constant
break
if new_co_func is None:
return code
consts = list(code.co_consts)
for i, constant in enumerate(consts):
if isinstance(constant, CodeType) and constant.co_name == name:
consts[i] = code_object_replace(
new_co_func, co_firstlineno=constant.co_firstlineno
)
break
return code_object_replace(code, co_consts=consts)