We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cyrillic problems (?)
I decided to test new translation and got this 
![Снимок экрана от 2022-05-06 17-47-09](https://user-images.githubusercontent.com/99257010/167248513-44b773ea-2f33-41b5-aa17-fc09a118358c.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `archinstall/__init__.py`
Content:
```
1 """Arch Linux installer - guided, templates etc."""
2 import urllib.error
3 import urllib.parse
4 import urllib.request
5 from argparse import ArgumentParser
6 
7 from .lib.disk import *
8 from .lib.exceptions import *
9 from .lib.general import *
10 from .lib.hardware import *
11 from .lib.installer import __packages__, Installer, accessibility_tools_in_use
12 from .lib.locale_helpers import *
13 from .lib.luks import *
14 from .lib.mirrors import *
15 from .lib.models.network_configuration import NetworkConfigurationHandler
16 from .lib.networking import *
17 from .lib.output import *
18 from .lib.models.dataclasses import (
19 	VersionDef,
20 	PackageSearchResult,
21 	PackageSearch,
22 	LocalPackage
23 )
24 from .lib.packages.packages import (
25 	group_search,
26 	package_search,
27 	find_package,
28 	find_packages,
29 	installed_package,
30 	validate_package_list,
31 )
32 from .lib.profiles import *
33 from .lib.services import *
34 from .lib.storage import *
35 from .lib.systemd import *
36 from .lib.user_interaction import *
37 from .lib.menu import Menu
38 from .lib.menu.list_manager import ListManager
39 from .lib.menu.text_input import TextInput
40 from .lib.menu.global_menu import GlobalMenu
41 from .lib.menu.selection_menu import (
42 	Selector,
43 	GeneralMenu
44 )
45 from .lib.translation import Translation, DeferredTranslation
46 from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony
47 from .lib.configuration import *
48 parser = ArgumentParser()
49 
50 __version__ = "2.4.2"
51 storage['__version__'] = __version__
52 
53 # add the custome _ as a builtin, it can now be used anywhere in the
54 # project to mark strings as translatable with _('translate me')
55 DeferredTranslation.install()
56 
57 
58 def define_arguments():
59 	"""
60 	Define which explicit arguments do we allow.
61 	Refer to https://docs.python.org/3/library/argparse.html for documentation and
62 			https://docs.python.org/3/howto/argparse.html for a tutorial
63 	Remember that the property/entry name python assigns to the parameters is the first string defined as argument and
64 	dashes inside it '-' are changed to '_'
65 	"""
66 	parser.add_argument("--config", nargs="?", help="JSON configuration file or URL")
67 	parser.add_argument("--creds", nargs="?", help="JSON credentials configuration file")
68 	parser.add_argument("--disk_layouts","--disk_layout","--disk-layouts","--disk-layout",nargs="?",
69 					help="JSON disk layout file")
70 	parser.add_argument("--silent", action="store_true",
71 						help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored")
72 	parser.add_argument("--dry-run", "--dry_run", action="store_true",
73 						help="Generates a configuration file and then exits instead of performing an installation")
74 	parser.add_argument("--script", default="guided", nargs="?", help="Script to run for installation", type=str)
75 	parser.add_argument("--mount-point","--mount_point", nargs="?", type=str, help="Define an alternate mount point for installation")
76 	parser.add_argument("--debug", action="store_true", default=False, help="Adds debug info into the log")
77 	parser.add_argument("--plugin", nargs="?", type=str)
78 
79 def parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict:
80 	"""We accept arguments not defined to the parser. (arguments "ad hoc").
81 	Internally argparse return to us a list of words so we have to parse its contents, manually.
82 	We accept following individual syntax for each argument
83 		--argument value
84 		--argument=value
85 		--argument = value
86 		--argument   (boolean as default)
87 	the optional paramters to the function alter a bit its behaviour:
88 	* multiple allows multivalued arguments, each value separated by whitespace. They're returned as a list
89 	* error. If set any non correctly specified argument-value pair to raise an exception. Else, simply notifies the existence of a problem and continues processing.
90 
91 	To a certain extent, multiple and error are incompatible. In fact, the only error this routine can catch, as of now, is the event
92 	argument value value ...
93 	which isn't am error if multiple is specified
94 	"""
95 	tmp_list = unknowns[:]   # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method()
96 	config = {}
97 	key = None
98 	last_key = None
99 	while tmp_list:
100 		element = tmp_list.pop(0)			  # retreive an element of the list
101 		if element.startswith('--'):		   # is an argument ?
102 			if '=' in element:				 # uses the arg=value syntax ?
103 				key, value = [x.strip() for x in element[2:].split('=', 1)]
104 				config[key] = value
105 				last_key = key				 # for multiple handling
106 				key = None					 # we have the kwy value pair we need
107 			else:
108 				key = element[2:]
109 				config[key] = True   # every argument starts its lifecycle as boolean
110 		else:
111 			if element == '=':
112 				continue
113 			if key:
114 				config[key] = element
115 				last_key = key # multiple
116 				key = None
117 			else:
118 				if multiple and last_key:
119 					if isinstance(config[last_key],str):
120 						config[last_key] = [config[last_key],element]
121 					else:
122 						config[last_key].append(element)
123 				elif error:
124 					raise ValueError(f"Entry {element} is not related to any argument")
125 				else:
126 					print(f" We ignore the entry {element} as it isn't related to any argument")
127 	return config
128 
129 def get_arguments() -> Dict[str, Any]:
130 	""" The handling of parameters from the command line
131 	Is done on following steps:
132 	0) we create a dict to store the arguments and their values
133 	1) preprocess.
134 		We take those arguments which use Json files, and read them into the argument dict. So each first level entry becomes a argument un it's own right
135 	2) Load.
136 		We convert the predefined argument list directly into the dict vía the vars() función. Non specified arguments are loaded with value None or false if they are booleans (action="store_true").
137 		The name is chosen according to argparse conventions. See above (the first text is used as argument name, but underscore substitutes dash)
138 		We then load all the undefined arguments. In this case the names are taken as written.
139 		Important. This way explicit command line arguments take precedence over configuración files.
140 	3) Amend
141 		Change whatever is needed on the configuration dictionary (it could be done in post_process_arguments but  this ougth to be left to changes anywhere else in the code, not in the arguments dictionary
142 	"""
143 	config = {}
144 	args, unknowns = parser.parse_known_args()
145 	# preprocess the json files.
146 	# TODO Expand the url access to the other JSON file arguments ?
147 	if args.config is not None:
148 		try:
149 			# First, let's check if this is a URL scheme instead of a filename
150 			parsed_url = urllib.parse.urlparse(args.config)
151 
152 			if not parsed_url.scheme:  # The Profile was not a direct match on a remote URL, it must be a local file.
153 				if not json_stream_to_structure('--config',args.config,config):
154 					exit(1)
155 			else:  # Attempt to load the configuration from the URL.
156 				with urllib.request.urlopen(urllib.request.Request(args.config, headers={'User-Agent': 'ArchInstall'})) as response:
157 					config.update(json.loads(response.read()))
158 		except Exception as e:
159 			raise ValueError(f"Could not load --config because: {e}")
160 
161 		if args.creds is not None:
162 			if not json_stream_to_structure('--creds',args.creds,config):
163 				exit(1)
164 	# load the parameters. first the known, then the unknowns
165 	config.update(vars(args))
166 	config.update(parse_unspecified_argument_list(unknowns))
167 	# amend the parameters (check internal consistency)
168 	# Installation can't be silent if config is not passed
169 	if args.config is not None :
170 		config["silent"] = args.silent
171 	else:
172 		config["silent"] = False
173 
174 	# avoiding a compatibility issue
175 	if 'dry-run' in config:
176 		del config['dry-run']
177 	return config
178 
179 def load_config():
180 	from .lib.models import NetworkConfiguration
181 	"""
182 	refine and set some arguments. Formerly at the scripts
183 	"""
184 	if arguments.get('harddrives', None) is not None:
185 		if type(arguments['harddrives']) is str:
186 			arguments['harddrives'] = arguments['harddrives'].split(',')
187 		arguments['harddrives'] = [BlockDevice(BlockDev) for BlockDev in arguments['harddrives']]
188 		# Temporarily disabling keep_partitions if config file is loaded
189 		# Temporary workaround to make Desktop Environments work
190 	if arguments.get('profile', None) is not None:
191 		if type(arguments.get('profile', None)) is dict:
192 			arguments['profile'] = Profile(None, arguments.get('profile', None)['path'])
193 		else:
194 			arguments['profile'] = Profile(None, arguments.get('profile', None))
195 	storage['_desktop_profile'] = arguments.get('desktop-environment', None)
196 	if arguments.get('mirror-region', None) is not None:
197 		if type(arguments.get('mirror-region', None)) is dict:
198 			arguments['mirror-region'] = arguments.get('mirror-region', None)
199 		else:
200 			selected_region = arguments.get('mirror-region', None)
201 			arguments['mirror-region'] = {selected_region: list_mirrors()[selected_region]}
202 	if arguments.get('sys-language', None) is not None:
203 		arguments['sys-language'] = arguments.get('sys-language', 'en_US')
204 	if arguments.get('sys-encoding', None) is not None:
205 		arguments['sys-encoding'] = arguments.get('sys-encoding', 'utf-8')
206 	if arguments.get('gfx_driver', None) is not None:
207 		storage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None)
208 	if arguments.get('servers', None) is not None:
209 		storage['_selected_servers'] = arguments.get('servers', None)
210 	if arguments.get('nic', None) is not None:
211 		handler = NetworkConfigurationHandler()
212 		handler.parse_arguments(arguments.get('nic'))
213 		arguments['nic'] = handler.configuration
214 
215 def post_process_arguments(arguments):
216 	storage['arguments'] = arguments
217 	if arguments.get('mount_point'):
218 		storage['MOUNT_POINT'] = arguments['mount_point']
219 
220 	if arguments.get('debug', False):
221 		log(f"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!", fg="red", level=logging.WARNING)
222 
223 	if arguments.get('plugin', None):
224 		load_plugin(arguments['plugin'])
225 
226 	if arguments.get('disk_layouts', None) is not None:
227 		# if 'disk_layouts' not in storage:
228 		# 	storage['disk_layouts'] = {}
229 		layout_storage = {}
230 		if not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage):
231 			exit(1)
232 		else:
233 			if arguments.get('harddrives') is None:
234 				arguments['harddrives'] = [disk for disk in layout_storage]
235 			# backward compatibility. Change partition.format for partition.wipe
236 			for disk in layout_storage:
237 				for i,partition in enumerate(layout_storage[disk].get('partitions',[])):
238 					if 'format' in partition:
239 						partition['wipe'] = partition['format']
240 						del partition['format']
241 			arguments['disk_layouts'] = layout_storage
242 
243 	load_config()
244 
245 
246 define_arguments()
247 arguments = get_arguments()
248 post_process_arguments(arguments)
249 
250 # @archinstall.plugin decorator hook to programmatically add
251 # plugins in runtime. Useful in profiles and other things.
252 def plugin(f, *args, **kwargs):
253 	plugins[f.__name__] = f
254 
255 
256 def run_as_a_module():
257 	"""
258 	Since we're running this as a 'python -m archinstall' module OR
259 	a nuitka3 compiled version of the project.
260 	This function and the file __main__ acts as a entry point.
261 	"""
262 
263 	# Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.
264 	storage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))
265 	try:
266 		script = Script(arguments.get('script', None))
267 	except ProfileNotFound as err:
268 		print(f"Couldn't find file: {err}")
269 		sys.exit(1)
270 
271 	os.chdir(os.path.abspath(os.path.dirname(__file__)))
272 
273 	# Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.
274 	storage['PROFILE_PATH'].pop()
275 	script.execute()
276
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/archinstall/__init__.py b/archinstall/__init__.py
--- a/archinstall/__init__.py
+++ b/archinstall/__init__.py
@@ -55,6 +55,10 @@
 DeferredTranslation.install()
 
 
+def set_unicode_font():
+	SysCommand('setfont UniCyr_8x16')
+
+
 def define_arguments():
 	"""
 	Define which explicit arguments do we allow.
@@ -243,6 +247,9 @@
 	load_config()
 
 
+# to ensure that cyrillic characters work in the installer
+set_unicode_font()
+
 define_arguments()
 arguments = get_arguments()
 post_process_arguments(arguments)

{"golden_diff": "diff --git a/archinstall/__init__.py b/archinstall/__init__.py\n--- a/archinstall/__init__.py\n+++ b/archinstall/__init__.py\n@@ -55,6 +55,10 @@\n DeferredTranslation.install()\n \n \n+def set_unicode_font():\n+\tSysCommand('setfont UniCyr_8x16')\n+\n+\n def define_arguments():\n \t\"\"\"\n \tDefine which explicit arguments do we allow.\n@@ -243,6 +247,9 @@\n \tload_config()\n \n \n+# to ensure that cyrillic characters work in the installer\n+set_unicode_font()\n+\n define_arguments()\n arguments = get_arguments()\n post_process_arguments(arguments)\n", "issue": "Cyrillic problems (?)\nI decided to test new translation and got this \r\n![\u0421\u043d\u0438\u043c\u043e\u043a \u044d\u043a\u0440\u0430\u043d\u0430 \u043e\u0442 2022-05-06 17-47-09](https://user-images.githubusercontent.com/99257010/167248513-44b773ea-2f33-41b5-aa17-fc09a118358c.png)\r\n\n", "before_files": [{"content": "\"\"\"Arch Linux installer - guided, templates etc.\"\"\"\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom argparse import ArgumentParser\n\nfrom .lib.disk import *\nfrom .lib.exceptions import *\nfrom .lib.general import *\nfrom .lib.hardware import *\nfrom .lib.installer import __packages__, Installer, accessibility_tools_in_use\nfrom .lib.locale_helpers import *\nfrom .lib.luks import *\nfrom .lib.mirrors import *\nfrom .lib.models.network_configuration import NetworkConfigurationHandler\nfrom .lib.networking import *\nfrom .lib.output import *\nfrom .lib.models.dataclasses import (\n\tVersionDef,\n\tPackageSearchResult,\n\tPackageSearch,\n\tLocalPackage\n)\nfrom .lib.packages.packages import (\n\tgroup_search,\n\tpackage_search,\n\tfind_package,\n\tfind_packages,\n\tinstalled_package,\n\tvalidate_package_list,\n)\nfrom .lib.profiles import *\nfrom .lib.services import *\nfrom .lib.storage import *\nfrom .lib.systemd import *\nfrom .lib.user_interaction import *\nfrom .lib.menu import Menu\nfrom .lib.menu.list_manager import ListManager\nfrom .lib.menu.text_input import TextInput\nfrom .lib.menu.global_menu import GlobalMenu\nfrom .lib.menu.selection_menu import (\n\tSelector,\n\tGeneralMenu\n)\nfrom .lib.translation import Translation, DeferredTranslation\nfrom .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony\nfrom .lib.configuration import *\nparser = ArgumentParser()\n\n__version__ = \"2.4.2\"\nstorage['__version__'] = __version__\n\n# add the custome _ as a builtin, it can now be used anywhere in the\n# project to mark strings as translatable with _('translate me')\nDeferredTranslation.install()\n\n\ndef define_arguments():\n\t\"\"\"\n\tDefine which explicit arguments do we allow.\n\tRefer to https://docs.python.org/3/library/argparse.html for documentation and\n\t\t\thttps://docs.python.org/3/howto/argparse.html for a tutorial\n\tRemember that the property/entry name python assigns to the parameters is the first string defined as argument and\n\tdashes inside it '-' are changed to '_'\n\t\"\"\"\n\tparser.add_argument(\"--config\", nargs=\"?\", help=\"JSON configuration file or URL\")\n\tparser.add_argument(\"--creds\", nargs=\"?\", help=\"JSON credentials configuration file\")\n\tparser.add_argument(\"--disk_layouts\",\"--disk_layout\",\"--disk-layouts\",\"--disk-layout\",nargs=\"?\",\n\t\t\t\t\thelp=\"JSON disk layout file\")\n\tparser.add_argument(\"--silent\", action=\"store_true\",\n\t\t\t\t\t\thelp=\"WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored\")\n\tparser.add_argument(\"--dry-run\", \"--dry_run\", action=\"store_true\",\n\t\t\t\t\t\thelp=\"Generates a configuration file and then exits instead of performing an installation\")\n\tparser.add_argument(\"--script\", default=\"guided\", nargs=\"?\", help=\"Script to run for installation\", type=str)\n\tparser.add_argument(\"--mount-point\",\"--mount_point\", nargs=\"?\", type=str, help=\"Define an alternate mount point for installation\")\n\tparser.add_argument(\"--debug\", action=\"store_true\", default=False, help=\"Adds debug info into the log\")\n\tparser.add_argument(\"--plugin\", nargs=\"?\", type=str)\n\ndef parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict:\n\t\"\"\"We accept arguments not defined to the parser. (arguments \"ad hoc\").\n\tInternally argparse return to us a list of words so we have to parse its contents, manually.\n\tWe accept following individual syntax for each argument\n\t\t--argument value\n\t\t--argument=value\n\t\t--argument = value\n\t\t--argument   (boolean as default)\n\tthe optional paramters to the function alter a bit its behaviour:\n\t* multiple allows multivalued arguments, each value separated by whitespace. They're returned as a list\n\t* error. If set any non correctly specified argument-value pair to raise an exception. Else, simply notifies the existence of a problem and continues processing.\n\n\tTo a certain extent, multiple and error are incompatible. In fact, the only error this routine can catch, as of now, is the event\n\targument value value ...\n\twhich isn't am error if multiple is specified\n\t\"\"\"\n\ttmp_list = unknowns[:]   # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method()\n\tconfig = {}\n\tkey = None\n\tlast_key = None\n\twhile tmp_list:\n\t\telement = tmp_list.pop(0)\t\t\t  # retreive an element of the list\n\t\tif element.startswith('--'):\t\t   # is an argument ?\n\t\t\tif '=' in element:\t\t\t\t # uses the arg=value syntax ?\n\t\t\t\tkey, value = [x.strip() for x in element[2:].split('=', 1)]\n\t\t\t\tconfig[key] = value\n\t\t\t\tlast_key = key\t\t\t\t # for multiple handling\n\t\t\t\tkey = None\t\t\t\t\t # we have the kwy value pair we need\n\t\t\telse:\n\t\t\t\tkey = element[2:]\n\t\t\t\tconfig[key] = True   # every argument starts its lifecycle as boolean\n\t\telse:\n\t\t\tif element == '=':\n\t\t\t\tcontinue\n\t\t\tif key:\n\t\t\t\tconfig[key] = element\n\t\t\t\tlast_key = key # multiple\n\t\t\t\tkey = None\n\t\t\telse:\n\t\t\t\tif multiple and last_key:\n\t\t\t\t\tif isinstance(config[last_key],str):\n\t\t\t\t\t\tconfig[last_key] = [config[last_key],element]\n\t\t\t\t\telse:\n\t\t\t\t\t\tconfig[last_key].append(element)\n\t\t\t\telif error:\n\t\t\t\t\traise ValueError(f\"Entry {element} is not related to any argument\")\n\t\t\t\telse:\n\t\t\t\t\tprint(f\" We ignore the entry {element} as it isn't related to any argument\")\n\treturn config\n\ndef get_arguments() -> Dict[str, Any]:\n\t\"\"\" The handling of parameters from the command line\n\tIs done on following steps:\n\t0) we create a dict to store the arguments and their values\n\t1) preprocess.\n\t\tWe take those arguments which use Json files, and read them into the argument dict. So each first level entry becomes a argument un it's own right\n\t2) Load.\n\t\tWe convert the predefined argument list directly into the dict v\u00eda the vars() funci\u00f3n. Non specified arguments are loaded with value None or false if they are booleans (action=\"store_true\").\n\t\tThe name is chosen according to argparse conventions. See above (the first text is used as argument name, but underscore substitutes dash)\n\t\tWe then load all the undefined arguments. In this case the names are taken as written.\n\t\tImportant. This way explicit command line arguments take precedence over configuraci\u00f3n files.\n\t3) Amend\n\t\tChange whatever is needed on the configuration dictionary (it could be done in post_process_arguments but  this ougth to be left to changes anywhere else in the code, not in the arguments dictionary\n\t\"\"\"\n\tconfig = {}\n\targs, unknowns = parser.parse_known_args()\n\t# preprocess the json files.\n\t# TODO Expand the url access to the other JSON file arguments ?\n\tif args.config is not None:\n\t\ttry:\n\t\t\t# First, let's check if this is a URL scheme instead of a filename\n\t\t\tparsed_url = urllib.parse.urlparse(args.config)\n\n\t\t\tif not parsed_url.scheme:  # The Profile was not a direct match on a remote URL, it must be a local file.\n\t\t\t\tif not json_stream_to_structure('--config',args.config,config):\n\t\t\t\t\texit(1)\n\t\t\telse:  # Attempt to load the configuration from the URL.\n\t\t\t\twith urllib.request.urlopen(urllib.request.Request(args.config, headers={'User-Agent': 'ArchInstall'})) as response:\n\t\t\t\t\tconfig.update(json.loads(response.read()))\n\t\texcept Exception as e:\n\t\t\traise ValueError(f\"Could not load --config because: {e}\")\n\n\t\tif args.creds is not None:\n\t\t\tif not json_stream_to_structure('--creds',args.creds,config):\n\t\t\t\texit(1)\n\t# load the parameters. first the known, then the unknowns\n\tconfig.update(vars(args))\n\tconfig.update(parse_unspecified_argument_list(unknowns))\n\t# amend the parameters (check internal consistency)\n\t# Installation can't be silent if config is not passed\n\tif args.config is not None :\n\t\tconfig[\"silent\"] = args.silent\n\telse:\n\t\tconfig[\"silent\"] = False\n\n\t# avoiding a compatibility issue\n\tif 'dry-run' in config:\n\t\tdel config['dry-run']\n\treturn config\n\ndef load_config():\n\tfrom .lib.models import NetworkConfiguration\n\t\"\"\"\n\trefine and set some arguments. Formerly at the scripts\n\t\"\"\"\n\tif arguments.get('harddrives', None) is not None:\n\t\tif type(arguments['harddrives']) is str:\n\t\t\targuments['harddrives'] = arguments['harddrives'].split(',')\n\t\targuments['harddrives'] = [BlockDevice(BlockDev) for BlockDev in arguments['harddrives']]\n\t\t# Temporarily disabling keep_partitions if config file is loaded\n\t\t# Temporary workaround to make Desktop Environments work\n\tif arguments.get('profile', None) is not None:\n\t\tif type(arguments.get('profile', None)) is dict:\n\t\t\targuments['profile'] = Profile(None, arguments.get('profile', None)['path'])\n\t\telse:\n\t\t\targuments['profile'] = Profile(None, arguments.get('profile', None))\n\tstorage['_desktop_profile'] = arguments.get('desktop-environment', None)\n\tif arguments.get('mirror-region', None) is not None:\n\t\tif type(arguments.get('mirror-region', None)) is dict:\n\t\t\targuments['mirror-region'] = arguments.get('mirror-region', None)\n\t\telse:\n\t\t\tselected_region = arguments.get('mirror-region', None)\n\t\t\targuments['mirror-region'] = {selected_region: list_mirrors()[selected_region]}\n\tif arguments.get('sys-language', None) is not None:\n\t\targuments['sys-language'] = arguments.get('sys-language', 'en_US')\n\tif arguments.get('sys-encoding', None) is not None:\n\t\targuments['sys-encoding'] = arguments.get('sys-encoding', 'utf-8')\n\tif arguments.get('gfx_driver', None) is not None:\n\t\tstorage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None)\n\tif arguments.get('servers', None) is not None:\n\t\tstorage['_selected_servers'] = arguments.get('servers', None)\n\tif arguments.get('nic', None) is not None:\n\t\thandler = NetworkConfigurationHandler()\n\t\thandler.parse_arguments(arguments.get('nic'))\n\t\targuments['nic'] = handler.configuration\n\ndef post_process_arguments(arguments):\n\tstorage['arguments'] = arguments\n\tif arguments.get('mount_point'):\n\t\tstorage['MOUNT_POINT'] = arguments['mount_point']\n\n\tif arguments.get('debug', False):\n\t\tlog(f\"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!\", fg=\"red\", level=logging.WARNING)\n\n\tif arguments.get('plugin', None):\n\t\tload_plugin(arguments['plugin'])\n\n\tif arguments.get('disk_layouts', None) is not None:\n\t\t# if 'disk_layouts' not in storage:\n\t\t# \tstorage['disk_layouts'] = {}\n\t\tlayout_storage = {}\n\t\tif not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage):\n\t\t\texit(1)\n\t\telse:\n\t\t\tif arguments.get('harddrives') is None:\n\t\t\t\targuments['harddrives'] = [disk for disk in layout_storage]\n\t\t\t# backward compatibility. Change partition.format for partition.wipe\n\t\t\tfor disk in layout_storage:\n\t\t\t\tfor i,partition in enumerate(layout_storage[disk].get('partitions',[])):\n\t\t\t\t\tif 'format' in partition:\n\t\t\t\t\t\tpartition['wipe'] = partition['format']\n\t\t\t\t\t\tdel partition['format']\n\t\t\targuments['disk_layouts'] = layout_storage\n\n\tload_config()\n\n\ndefine_arguments()\narguments = get_arguments()\npost_process_arguments(arguments)\n\n# @archinstall.plugin decorator hook to programmatically add\n# plugins in runtime. Useful in profiles and other things.\ndef plugin(f, *args, **kwargs):\n\tplugins[f.__name__] = f\n\n\ndef run_as_a_module():\n\t\"\"\"\n\tSince we're running this as a 'python -m archinstall' module OR\n\ta nuitka3 compiled version of the project.\n\tThis function and the file __main__ acts as a entry point.\n\t\"\"\"\n\n\t# Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.\n\tstorage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))\n\ttry:\n\t\tscript = Script(arguments.get('script', None))\n\texcept ProfileNotFound as err:\n\t\tprint(f\"Couldn't find file: {err}\")\n\t\tsys.exit(1)\n\n\tos.chdir(os.path.abspath(os.path.dirname(__file__)))\n\n\t# Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.\n\tstorage['PROFILE_PATH'].pop()\n\tscript.execute()\n", "path": "archinstall/__init__.py"}], "after_files": [{"content": "\"\"\"Arch Linux installer - guided, templates etc.\"\"\"\nimport urllib.error\nimport urllib.parse\nimport urllib.request\nfrom argparse import ArgumentParser\n\nfrom .lib.disk import *\nfrom .lib.exceptions import *\nfrom .lib.general import *\nfrom .lib.hardware import *\nfrom .lib.installer import __packages__, Installer, accessibility_tools_in_use\nfrom .lib.locale_helpers import *\nfrom .lib.luks import *\nfrom .lib.mirrors import *\nfrom .lib.models.network_configuration import NetworkConfigurationHandler\nfrom .lib.networking import *\nfrom .lib.output import *\nfrom .lib.models.dataclasses import (\n\tVersionDef,\n\tPackageSearchResult,\n\tPackageSearch,\n\tLocalPackage\n)\nfrom .lib.packages.packages import (\n\tgroup_search,\n\tpackage_search,\n\tfind_package,\n\tfind_packages,\n\tinstalled_package,\n\tvalidate_package_list,\n)\nfrom .lib.profiles import *\nfrom .lib.services import *\nfrom .lib.storage import *\nfrom .lib.systemd import *\nfrom .lib.user_interaction import *\nfrom .lib.menu import Menu\nfrom .lib.menu.list_manager import ListManager\nfrom .lib.menu.text_input import TextInput\nfrom .lib.menu.global_menu import GlobalMenu\nfrom .lib.menu.selection_menu import (\n\tSelector,\n\tGeneralMenu\n)\nfrom .lib.translation import Translation, DeferredTranslation\nfrom .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony\nfrom .lib.configuration import *\nparser = ArgumentParser()\n\n__version__ = \"2.4.2\"\nstorage['__version__'] = __version__\n\n# add the custome _ as a builtin, it can now be used anywhere in the\n# project to mark strings as translatable with _('translate me')\nDeferredTranslation.install()\n\n\ndef set_unicode_font():\n\tSysCommand('setfont UniCyr_8x16')\n\n\ndef define_arguments():\n\t\"\"\"\n\tDefine which explicit arguments do we allow.\n\tRefer to https://docs.python.org/3/library/argparse.html for documentation and\n\t\t\thttps://docs.python.org/3/howto/argparse.html for a tutorial\n\tRemember that the property/entry name python assigns to the parameters is the first string defined as argument and\n\tdashes inside it '-' are changed to '_'\n\t\"\"\"\n\tparser.add_argument(\"--config\", nargs=\"?\", help=\"JSON configuration file or URL\")\n\tparser.add_argument(\"--creds\", nargs=\"?\", help=\"JSON credentials configuration file\")\n\tparser.add_argument(\"--disk_layouts\",\"--disk_layout\",\"--disk-layouts\",\"--disk-layout\",nargs=\"?\",\n\t\t\t\t\thelp=\"JSON disk layout file\")\n\tparser.add_argument(\"--silent\", action=\"store_true\",\n\t\t\t\t\t\thelp=\"WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored\")\n\tparser.add_argument(\"--dry-run\", \"--dry_run\", action=\"store_true\",\n\t\t\t\t\t\thelp=\"Generates a configuration file and then exits instead of performing an installation\")\n\tparser.add_argument(\"--script\", default=\"guided\", nargs=\"?\", help=\"Script to run for installation\", type=str)\n\tparser.add_argument(\"--mount-point\",\"--mount_point\", nargs=\"?\", type=str, help=\"Define an alternate mount point for installation\")\n\tparser.add_argument(\"--debug\", action=\"store_true\", default=False, help=\"Adds debug info into the log\")\n\tparser.add_argument(\"--plugin\", nargs=\"?\", type=str)\n\ndef parse_unspecified_argument_list(unknowns :list, multiple :bool = False, error :bool = False) -> dict:\n\t\"\"\"We accept arguments not defined to the parser. (arguments \"ad hoc\").\n\tInternally argparse return to us a list of words so we have to parse its contents, manually.\n\tWe accept following individual syntax for each argument\n\t\t--argument value\n\t\t--argument=value\n\t\t--argument = value\n\t\t--argument   (boolean as default)\n\tthe optional paramters to the function alter a bit its behaviour:\n\t* multiple allows multivalued arguments, each value separated by whitespace. They're returned as a list\n\t* error. If set any non correctly specified argument-value pair to raise an exception. Else, simply notifies the existence of a problem and continues processing.\n\n\tTo a certain extent, multiple and error are incompatible. In fact, the only error this routine can catch, as of now, is the event\n\targument value value ...\n\twhich isn't am error if multiple is specified\n\t\"\"\"\n\ttmp_list = unknowns[:]   # wastes a few bytes, but avoids any collateral effect of the destructive nature of the pop method()\n\tconfig = {}\n\tkey = None\n\tlast_key = None\n\twhile tmp_list:\n\t\telement = tmp_list.pop(0)\t\t\t  # retreive an element of the list\n\t\tif element.startswith('--'):\t\t   # is an argument ?\n\t\t\tif '=' in element:\t\t\t\t # uses the arg=value syntax ?\n\t\t\t\tkey, value = [x.strip() for x in element[2:].split('=', 1)]\n\t\t\t\tconfig[key] = value\n\t\t\t\tlast_key = key\t\t\t\t # for multiple handling\n\t\t\t\tkey = None\t\t\t\t\t # we have the kwy value pair we need\n\t\t\telse:\n\t\t\t\tkey = element[2:]\n\t\t\t\tconfig[key] = True   # every argument starts its lifecycle as boolean\n\t\telse:\n\t\t\tif element == '=':\n\t\t\t\tcontinue\n\t\t\tif key:\n\t\t\t\tconfig[key] = element\n\t\t\t\tlast_key = key # multiple\n\t\t\t\tkey = None\n\t\t\telse:\n\t\t\t\tif multiple and last_key:\n\t\t\t\t\tif isinstance(config[last_key],str):\n\t\t\t\t\t\tconfig[last_key] = [config[last_key],element]\n\t\t\t\t\telse:\n\t\t\t\t\t\tconfig[last_key].append(element)\n\t\t\t\telif error:\n\t\t\t\t\traise ValueError(f\"Entry {element} is not related to any argument\")\n\t\t\t\telse:\n\t\t\t\t\tprint(f\" We ignore the entry {element} as it isn't related to any argument\")\n\treturn config\n\ndef get_arguments() -> Dict[str, Any]:\n\t\"\"\" The handling of parameters from the command line\n\tIs done on following steps:\n\t0) we create a dict to store the arguments and their values\n\t1) preprocess.\n\t\tWe take those arguments which use Json files, and read them into the argument dict. So each first level entry becomes a argument un it's own right\n\t2) Load.\n\t\tWe convert the predefined argument list directly into the dict v\u00eda the vars() funci\u00f3n. Non specified arguments are loaded with value None or false if they are booleans (action=\"store_true\").\n\t\tThe name is chosen according to argparse conventions. See above (the first text is used as argument name, but underscore substitutes dash)\n\t\tWe then load all the undefined arguments. In this case the names are taken as written.\n\t\tImportant. This way explicit command line arguments take precedence over configuraci\u00f3n files.\n\t3) Amend\n\t\tChange whatever is needed on the configuration dictionary (it could be done in post_process_arguments but  this ougth to be left to changes anywhere else in the code, not in the arguments dictionary\n\t\"\"\"\n\tconfig = {}\n\targs, unknowns = parser.parse_known_args()\n\t# preprocess the json files.\n\t# TODO Expand the url access to the other JSON file arguments ?\n\tif args.config is not None:\n\t\ttry:\n\t\t\t# First, let's check if this is a URL scheme instead of a filename\n\t\t\tparsed_url = urllib.parse.urlparse(args.config)\n\n\t\t\tif not parsed_url.scheme:  # The Profile was not a direct match on a remote URL, it must be a local file.\n\t\t\t\tif not json_stream_to_structure('--config',args.config,config):\n\t\t\t\t\texit(1)\n\t\t\telse:  # Attempt to load the configuration from the URL.\n\t\t\t\twith urllib.request.urlopen(urllib.request.Request(args.config, headers={'User-Agent': 'ArchInstall'})) as response:\n\t\t\t\t\tconfig.update(json.loads(response.read()))\n\t\texcept Exception as e:\n\t\t\traise ValueError(f\"Could not load --config because: {e}\")\n\n\t\tif args.creds is not None:\n\t\t\tif not json_stream_to_structure('--creds',args.creds,config):\n\t\t\t\texit(1)\n\t# load the parameters. first the known, then the unknowns\n\tconfig.update(vars(args))\n\tconfig.update(parse_unspecified_argument_list(unknowns))\n\t# amend the parameters (check internal consistency)\n\t# Installation can't be silent if config is not passed\n\tif args.config is not None :\n\t\tconfig[\"silent\"] = args.silent\n\telse:\n\t\tconfig[\"silent\"] = False\n\n\t# avoiding a compatibility issue\n\tif 'dry-run' in config:\n\t\tdel config['dry-run']\n\treturn config\n\ndef load_config():\n\tfrom .lib.models import NetworkConfiguration\n\t\"\"\"\n\trefine and set some arguments. Formerly at the scripts\n\t\"\"\"\n\tif arguments.get('harddrives', None) is not None:\n\t\tif type(arguments['harddrives']) is str:\n\t\t\targuments['harddrives'] = arguments['harddrives'].split(',')\n\t\targuments['harddrives'] = [BlockDevice(BlockDev) for BlockDev in arguments['harddrives']]\n\t\t# Temporarily disabling keep_partitions if config file is loaded\n\t\t# Temporary workaround to make Desktop Environments work\n\tif arguments.get('profile', None) is not None:\n\t\tif type(arguments.get('profile', None)) is dict:\n\t\t\targuments['profile'] = Profile(None, arguments.get('profile', None)['path'])\n\t\telse:\n\t\t\targuments['profile'] = Profile(None, arguments.get('profile', None))\n\tstorage['_desktop_profile'] = arguments.get('desktop-environment', None)\n\tif arguments.get('mirror-region', None) is not None:\n\t\tif type(arguments.get('mirror-region', None)) is dict:\n\t\t\targuments['mirror-region'] = arguments.get('mirror-region', None)\n\t\telse:\n\t\t\tselected_region = arguments.get('mirror-region', None)\n\t\t\targuments['mirror-region'] = {selected_region: list_mirrors()[selected_region]}\n\tif arguments.get('sys-language', None) is not None:\n\t\targuments['sys-language'] = arguments.get('sys-language', 'en_US')\n\tif arguments.get('sys-encoding', None) is not None:\n\t\targuments['sys-encoding'] = arguments.get('sys-encoding', 'utf-8')\n\tif arguments.get('gfx_driver', None) is not None:\n\t\tstorage['gfx_driver_packages'] = AVAILABLE_GFX_DRIVERS.get(arguments.get('gfx_driver', None), None)\n\tif arguments.get('servers', None) is not None:\n\t\tstorage['_selected_servers'] = arguments.get('servers', None)\n\tif arguments.get('nic', None) is not None:\n\t\thandler = NetworkConfigurationHandler()\n\t\thandler.parse_arguments(arguments.get('nic'))\n\t\targuments['nic'] = handler.configuration\n\ndef post_process_arguments(arguments):\n\tstorage['arguments'] = arguments\n\tif arguments.get('mount_point'):\n\t\tstorage['MOUNT_POINT'] = arguments['mount_point']\n\n\tif arguments.get('debug', False):\n\t\tlog(f\"Warning: --debug mode will write certain credentials to {storage['LOG_PATH']}/{storage['LOG_FILE']}!\", fg=\"red\", level=logging.WARNING)\n\n\tif arguments.get('plugin', None):\n\t\tload_plugin(arguments['plugin'])\n\n\tif arguments.get('disk_layouts', None) is not None:\n\t\t# if 'disk_layouts' not in storage:\n\t\t# \tstorage['disk_layouts'] = {}\n\t\tlayout_storage = {}\n\t\tif not json_stream_to_structure('--disk_layouts',arguments['disk_layouts'],layout_storage):\n\t\t\texit(1)\n\t\telse:\n\t\t\tif arguments.get('harddrives') is None:\n\t\t\t\targuments['harddrives'] = [disk for disk in layout_storage]\n\t\t\t# backward compatibility. Change partition.format for partition.wipe\n\t\t\tfor disk in layout_storage:\n\t\t\t\tfor i,partition in enumerate(layout_storage[disk].get('partitions',[])):\n\t\t\t\t\tif 'format' in partition:\n\t\t\t\t\t\tpartition['wipe'] = partition['format']\n\t\t\t\t\t\tdel partition['format']\n\t\t\targuments['disk_layouts'] = layout_storage\n\n\tload_config()\n\n\n# to ensure that cyrillic characters work in the installer\nset_unicode_font()\n\ndefine_arguments()\narguments = get_arguments()\npost_process_arguments(arguments)\n\n# @archinstall.plugin decorator hook to programmatically add\n# plugins in runtime. Useful in profiles and other things.\ndef plugin(f, *args, **kwargs):\n\tplugins[f.__name__] = f\n\n\ndef run_as_a_module():\n\t\"\"\"\n\tSince we're running this as a 'python -m archinstall' module OR\n\ta nuitka3 compiled version of the project.\n\tThis function and the file __main__ acts as a entry point.\n\t\"\"\"\n\n\t# Add another path for finding profiles, so that list_profiles() in Script() can find guided.py, unattended.py etc.\n\tstorage['PROFILE_PATH'].append(os.path.abspath(f'{os.path.dirname(__file__)}/examples'))\n\ttry:\n\t\tscript = Script(arguments.get('script', None))\n\texcept ProfileNotFound as err:\n\t\tprint(f\"Couldn't find file: {err}\")\n\t\tsys.exit(1)\n\n\tos.chdir(os.path.abspath(os.path.dirname(__file__)))\n\n\t# Remove the example directory from the PROFILE_PATH, to avoid guided.py etc shows up in user input questions.\n\tstorage['PROFILE_PATH'].pop()\n\tscript.execute()\n", "path": "archinstall/__init__.py"}]}