forked from ArchipelagoMW/Archipelago
- Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathModuleUpdate.py
More file actions
Latest commit
167 lines (142 loc) · 7.05 KB
/
Copy pathModuleUpdate.py
File metadata and controls
167 lines (142 loc) · 7.05 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
importos
importsys
importsubprocess
importmultiprocessing
importwarnings
ifsys.platformin ("win32", "darwin") andnot (3, 11, 9) <=sys.version_info< (3, 14, 0):
# Official micro version updates. This should match the number in docs/running from source.md.
raiseRuntimeError(f"Incompatible Python Version found: {sys.version_info}. "
"Official 3.11.9 through 3.13.x is supported.")
elifsys.platformin ("win32", "darwin") andsys.version_info< (3, 11, 13):
# There are known security issues, but no easy way to install fixed versions on Windows for testing.
warnings.warn(f"Python Version {sys.version_info} has security issues. Don't use in production.")
elifnot (3, 11, 0) <=sys.version_info< (3, 14, 0):
# Other platforms may get security backports instead of micro updates, so the number is unreliable.
raiseRuntimeError(f"Incompatible Python Version found: {sys.version_info}. 3.11.0 through 3.13.x is supported.")
# don't run update if environment is frozen/compiled or if not the parent process (skip in subprocess)
_skip_update=bool(
getattr(sys, "frozen", False) or
multiprocessing.parent_process() or
os.environ.get("SKIP_REQUIREMENTS_UPDATE", "").lower() in ("1", "true", "yes")
)
update_ran=_skip_update
classRequirementsSet(set):
defadd(self, e):
globalupdate_ran
update_ran&=_skip_update
super().add(e)
defupdate(self, *s):
globalupdate_ran
update_ran&=_skip_update
super().update(*s)
local_dir=os.path.dirname(__file__)
requirements_files=RequirementsSet((os.path.join(local_dir, 'requirements.txt'),))
ifnotupdate_ran:
forentryinos.scandir(os.path.join(local_dir, "worlds")):
# skip .* (hidden / disabled) folders
ifnotentry.name.startswith("."):
ifentry.is_dir():
req_file=os.path.join(entry.path, "requirements.txt")
ifos.path.exists(req_file):
requirements_files.add(req_file)
defcheck_pip():
# detect if pip is available
try:
importpip# noqa: F401
exceptImportError:
raiseRuntimeError("pip not available. Please install pip.")
defconfirm(msg: str):
try:
input(f"\n{msg}")
exceptKeyboardInterrupt:
print("\nAborting")
sys.exit(1)
defupdate_command():
check_pip()
forfileinrequirements_files:
subprocess.call([sys.executable, "-m", "pip", "install", "-r", file, "--upgrade"])
definstall_pkg_resources(yes=False):
try:
importpkg_resources# noqa: F401
except (AttributeError, ImportError):
check_pip()
ifnotyes:
confirm("pkg_resources not found, press enter to install it")
subprocess.call([sys.executable, "-m", "pip", "install", "--upgrade", "setuptools>=75,<81"])
defupdate(yes: bool=False, force: bool=False) ->None:
globalupdate_ran
ifnotupdate_ran:
update_ran=True
install_pkg_resources(yes=yes)
importpkg_resources
ifforce:
update_command()
return
prev=""# if a line ends in \ we store here and merge later
forreq_fileinrequirements_files:
path=os.path.join(os.path.dirname(sys.argv[0]), req_file)
ifnotos.path.exists(path):
path=os.path.join(os.path.dirname(__file__), req_file)
withopen(path) asrequirementsfile:
forlineinrequirementsfile:
ifnotlineorline.lstrip(" \t")[0] =="#":
ifnotprev:
continue# ignore comments
line=""
elifline.rstrip("\r\n").endswith("\\"):
prev=prev+line.rstrip("\r\n")[:-1] +" "# continue on next line
continue
line=prev+line
line=line.split("--hash=")[0] # remove hashes from requirement for version checking
prev=""
ifline.startswith(("https://", "git+https://")):
# extract name and version for url
rest=line.split('/')[-1]
line=""
if"#egg="inrest:
# from egg info
rest, egg=rest.split("#egg=", 1)
egg=egg.split(";", 1)[0].rstrip()
ifany(compareineggforcomparein ("==", ">=", ">", "<", "<=", "!=")):
warnings.warn(f"Specifying version as #egg={egg} will become unavailable in pip 25.0. "
"Use name @ url#version instead.", DeprecationWarning)
line=egg
else:
egg=""
if"@"inrestandnotline:
raiseValueError("Can't deduce version from requirement")
elifnotline:
# from filename
rest=rest.replace(".zip", "-").replace(".tar.gz", "-")
name, version, _=rest.split("-", 2)
line=f'{eggorname}=={version}'
elif"@"inlineand"#"inline:
# PEP 508 does not allow us to specify a version, so we use custom syntax
# name @ url#version ; marker
name, rest=line.split("@", 1)
version=rest.split("#", 1)[1].split(";", 1)[0].rstrip()
line=f"{name.rstrip()}=={version}"
if";"inrest: # keep marker
line+=rest[rest.find(";"):]
requirements=pkg_resources.parse_requirements(line)
forrequirementinmap(str, requirements):
try:
pkg_resources.require(requirement)
exceptpkg_resources.ResolutionError:
ifnotyes:
importtraceback
traceback.print_exc()
confirm(f"Requirement {requirement} is not satisfied, press enter to install it")
update_command()
return
if__name__=="__main__":
importargparse
parser=argparse.ArgumentParser(description='Install archipelago requirements')
parser.add_argument('-y', '--yes', dest='yes', action='store_true', help='answer "yes" to all questions')
parser.add_argument('-f', '--force', dest='force', action='store_true', help='force update')
parser.add_argument('-a', '--append', nargs="*", dest='additional_requirements',
help='List paths to additional requirement files.')
args=parser.parse_args()
ifargs.additional_requirements:
requirements_files.update(args.additional_requirements)
update(args.yes, args.force)