| 
									
										
										
										
											2020-01-19 12:50:04 -08:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  | import sys | 
					
						
							|  |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2023-05-17 15:27:29 +02:00
										 |  |  | import multiprocessing | 
					
						
							| 
									
										
										
										
											2023-02-18 12:50:42 +01:00
										 |  |  | import warnings | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 15:30:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  | if sys.version_info < (3, 8, 6): | 
					
						
							|  |  |  |     raise RuntimeError("Incompatible Python Version. 3.8.7+ is supported.") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-17 15:27:29 +02:00
										 |  |  | # don't run update if environment is frozen/compiled or if not the parent process (skip in subprocess) | 
					
						
							| 
									
										
										
										
											2024-01-14 03:09:03 +01:00
										 |  |  | _skip_update = bool(getattr(sys, "frozen", False) or multiprocessing.parent_process()) | 
					
						
							|  |  |  | update_ran = _skip_update | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class RequirementsSet(set): | 
					
						
							|  |  |  |     def add(self, e): | 
					
						
							|  |  |  |         global update_ran | 
					
						
							|  |  |  |         update_ran &= _skip_update | 
					
						
							|  |  |  |         super().add(e) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update(self, *s): | 
					
						
							|  |  |  |         global update_ran | 
					
						
							|  |  |  |         update_ran &= _skip_update | 
					
						
							|  |  |  |         super().update(*s) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | local_dir = os.path.dirname(__file__) | 
					
						
							|  |  |  | requirements_files = RequirementsSet((os.path.join(local_dir, 'requirements.txt'),)) | 
					
						
							| 
									
										
										
										
											2020-01-19 23:30:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 15:30:20 +02:00
										 |  |  | if not update_ran: | 
					
						
							| 
									
										
										
										
											2022-04-01 01:09:16 +02:00
										 |  |  |     for entry in os.scandir(os.path.join(local_dir, "worlds")): | 
					
						
							| 
									
										
										
										
											2022-10-01 17:38:39 +02:00
										 |  |  |         # skip .* (hidden / disabled) folders | 
					
						
							|  |  |  |         if not entry.name.startswith("."): | 
					
						
							|  |  |  |             if entry.is_dir(): | 
					
						
							|  |  |  |                 req_file = os.path.join(entry.path, "requirements.txt") | 
					
						
							|  |  |  |                 if os.path.exists(req_file): | 
					
						
							|  |  |  |                     requirements_files.add(req_file) | 
					
						
							| 
									
										
										
										
											2021-06-06 15:30:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  | def check_pip(): | 
					
						
							|  |  |  |     # detect if pip is available | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import pip  # noqa: F401 | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         raise RuntimeError("pip not available. Please install pip.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def confirm(msg: str): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         input(f"\n{msg}") | 
					
						
							|  |  |  |     except KeyboardInterrupt: | 
					
						
							|  |  |  |         print("\nAborting") | 
					
						
							|  |  |  |         sys.exit(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  | def update_command(): | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  |     check_pip() | 
					
						
							| 
									
										
										
										
											2021-06-06 15:11:17 +02:00
										 |  |  |     for file in requirements_files: | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  |         subprocess.call([sys.executable, "-m", "pip", "install", "-r", file, "--upgrade"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def install_pkg_resources(yes=False): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import pkg_resources  # noqa: F401 | 
					
						
							|  |  |  |     except ImportError: | 
					
						
							|  |  |  |         check_pip() | 
					
						
							|  |  |  |         if not yes: | 
					
						
							|  |  |  |             confirm("pkg_resources not found, press enter to install it") | 
					
						
							|  |  |  |         subprocess.call([sys.executable, "-m", "pip", "install", "--upgrade", "setuptools"]) | 
					
						
							| 
									
										
										
										
											2020-01-19 23:30:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-22 20:35:30 +01:00
										 |  |  | def update(yes=False, force=False): | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  |     global update_ran | 
					
						
							|  |  |  |     if not update_ran: | 
					
						
							|  |  |  |         update_ran = True | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 23:05:41 +02:00
										 |  |  |         if force: | 
					
						
							|  |  |  |             update_command() | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         install_pkg_resources(yes=yes) | 
					
						
							|  |  |  |         import pkg_resources | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 17:19:33 +02:00
										 |  |  |         prev = ""  # if a line ends in \ we store here and merge later | 
					
						
							| 
									
										
										
										
											2021-06-06 15:11:17 +02:00
										 |  |  |         for req_file in requirements_files: | 
					
						
							|  |  |  |             path = os.path.join(os.path.dirname(sys.argv[0]), req_file) | 
					
						
							|  |  |  |             if not os.path.exists(path): | 
					
						
							|  |  |  |                 path = os.path.join(os.path.dirname(__file__), req_file) | 
					
						
							|  |  |  |             with open(path) as requirementsfile: | 
					
						
							| 
									
										
										
										
											2021-11-07 14:00:13 +01:00
										 |  |  |                 for line in requirementsfile: | 
					
						
							| 
									
										
										
										
											2023-10-08 17:19:33 +02:00
										 |  |  |                     if not line or line.lstrip(" \t")[0] == "#": | 
					
						
							|  |  |  |                         if not prev: | 
					
						
							|  |  |  |                             continue  # ignore comments | 
					
						
							|  |  |  |                         line = "" | 
					
						
							|  |  |  |                     elif line.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 = "" | 
					
						
							| 
									
										
										
										
											2022-10-21 23:26:40 +02:00
										 |  |  |                     if line.startswith(("https://", "git+https://")): | 
					
						
							|  |  |  |                         # extract name and version for url | 
					
						
							|  |  |  |                         rest = line.split('/')[-1] | 
					
						
							|  |  |  |                         line = "" | 
					
						
							|  |  |  |                         if "#egg=" in rest: | 
					
						
							|  |  |  |                             # from egg info | 
					
						
							|  |  |  |                             rest, egg = rest.split("#egg=", 1) | 
					
						
							| 
									
										
										
										
											2023-02-18 12:50:42 +01:00
										 |  |  |                             egg = egg.split(";", 1)[0].rstrip() | 
					
						
							| 
									
										
										
										
											2022-10-21 23:26:40 +02:00
										 |  |  |                             if any(compare in egg for compare in ("==", ">=", ">", "<", "<=", "!=")): | 
					
						
							| 
									
										
										
										
											2023-02-18 12:50:42 +01:00
										 |  |  |                                 warnings.warn(f"Specifying version as #egg={egg} will become unavailable in pip 25.0. " | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  |                                               "Use name @ url#version instead.", DeprecationWarning) | 
					
						
							| 
									
										
										
										
											2022-10-21 23:26:40 +02:00
										 |  |  |                                 line = egg | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             egg = "" | 
					
						
							|  |  |  |                         if "@" in rest and not line: | 
					
						
							|  |  |  |                             raise ValueError("Can't deduce version from requirement") | 
					
						
							|  |  |  |                         elif not line: | 
					
						
							|  |  |  |                             # from filename | 
					
						
							|  |  |  |                             rest = rest.replace(".zip", "-").replace(".tar.gz", "-") | 
					
						
							|  |  |  |                             name, version, _ = rest.split("-", 2) | 
					
						
							|  |  |  |                             line = f'{egg or name}=={version}' | 
					
						
							| 
									
										
										
										
											2023-02-18 12:50:42 +01:00
										 |  |  |                     elif "@" in line and "#" in line: | 
					
						
							|  |  |  |                         # 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 ";" in rest:  # keep marker | 
					
						
							|  |  |  |                             line += rest[rest.find(";"):] | 
					
						
							| 
									
										
										
										
											2021-11-07 14:00:13 +01:00
										 |  |  |                     requirements = pkg_resources.parse_requirements(line) | 
					
						
							| 
									
										
										
										
											2023-02-18 12:50:42 +01:00
										 |  |  |                     for requirement in map(str, requirements): | 
					
						
							| 
									
										
										
										
											2021-11-07 14:00:13 +01:00
										 |  |  |                         try: | 
					
						
							|  |  |  |                             pkg_resources.require(requirement) | 
					
						
							|  |  |  |                         except pkg_resources.ResolutionError: | 
					
						
							|  |  |  |                             if not yes: | 
					
						
							|  |  |  |                                 import traceback | 
					
						
							|  |  |  |                                 traceback.print_exc() | 
					
						
							| 
									
										
										
										
											2023-03-25 19:54:42 +01:00
										 |  |  |                                 confirm(f"Requirement {requirement} is not satisfied, press enter to install it") | 
					
						
							| 
									
										
										
										
											2021-11-07 14:00:13 +01:00
										 |  |  |                             update_command() | 
					
						
							|  |  |  |                             return | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 23:37:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:45:52 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2021-07-26 23:05:41 +02:00
										 |  |  |     import argparse | 
					
						
							| 
									
										
										
										
											2022-01-22 20:35:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-26 23:05:41 +02:00
										 |  |  |     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') | 
					
						
							| 
									
										
										
										
											2022-05-03 22:14:03 +02:00
										 |  |  |     parser.add_argument('-a', '--append', nargs="*", dest='additional_requirements', | 
					
						
							|  |  |  |                         help='List paths to additional requirement files.') | 
					
						
							| 
									
										
										
										
											2021-07-26 23:05:41 +02:00
										 |  |  |     args = parser.parse_args() | 
					
						
							| 
									
										
										
										
											2022-05-03 22:14:03 +02:00
										 |  |  |     if args.additional_requirements: | 
					
						
							|  |  |  |         requirements_files.update(args.additional_requirements) | 
					
						
							| 
									
										
										
										
											2021-07-26 23:05:41 +02:00
										 |  |  |     update(args.yes, args.force) |