We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
invalid package names png++
Hi,

I try to install some package with the "package" list in the config file, but if i use the package name png++ (i put the link under the message) it return as invalid package names.

https://archlinux.org/packages/community/any/png++/

invalid package names png++
Hi,

I try to install some package with the "package" list in the config file, but if i use the package name png++ (i put the link under the message) it return as invalid package names.

https://archlinux.org/packages/community/any/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/lib/packages/packages.py`
Content:
```
1 import json
2 import ssl
3 import urllib.request
4 from typing import Dict, Any, Tuple, List
5 
6 from ..exceptions import PackageError, SysCallError
7 from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage
8 from ..pacman import run_pacman
9 
10 BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/?name={package}'
11 # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'
12 BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/?name={group}'
13 
14 
15 def group_search(name :str) -> List[PackageSearchResult]:
16 	# TODO UPSTREAM: Implement /json/ for the groups search
17 	ssl_context = ssl.create_default_context()
18 	ssl_context.check_hostname = False
19 	ssl_context.verify_mode = ssl.CERT_NONE
20 	try:
21 		response = urllib.request.urlopen(BASE_GROUP_URL.format(group=name), context=ssl_context)
22 	except urllib.error.HTTPError as err:
23 		if err.code == 404:
24 			return []
25 		else:
26 			raise err
27 
28 	# Just to be sure some code didn't slip through the exception
29 	data = response.read().decode('UTF-8')
30 
31 	return [PackageSearchResult(**package) for package in json.loads(data)['results']]
32 
33 
34 def package_search(package :str) -> PackageSearch:
35 	"""
36 	Finds a specific package via the package database.
37 	It makes a simple web-request, which might be a bit slow.
38 	"""
39 	# TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ')
40 	# TODO: utilize pacman cache first, upstream second.
41 	ssl_context = ssl.create_default_context()
42 	ssl_context.check_hostname = False
43 	ssl_context.verify_mode = ssl.CERT_NONE
44 	response = urllib.request.urlopen(BASE_URL_PKG_SEARCH.format(package=package), context=ssl_context)
45 
46 	if response.code != 200:
47 		raise PackageError(f"Could not locate package: [{response.code}] {response}")
48 
49 	data = response.read().decode('UTF-8')
50 
51 	return PackageSearch(**json.loads(data))
52 
53 
54 def find_package(package :str) -> List[PackageSearchResult]:
55 	data = package_search(package)
56 	results = []
57 
58 	for result in data.results:
59 		if result.pkgname == package:
60 			results.append(result)
61 
62 	# If we didn't find the package in the search results,
63 	# odds are it's a group package
64 	if not results:
65 		# Check if the package is actually a group
66 		for result in group_search(package):
67 			results.append(result)
68 
69 	return results
70 
71 
72 def find_packages(*names :str) -> Dict[str, Any]:
73 	"""
74 	This function returns the search results for many packages.
75 	The function itself is rather slow, so consider not sending to
76 	many packages to the search query.
77 	"""
78 	result = {}
79 	for package in names:
80 		for found_package in find_package(package):
81 			result[package] = found_package
82 
83 	return result
84 
85 
86 def validate_package_list(packages :list) -> Tuple[list, list]:
87 	"""
88 	Validates a list of given packages.
89 	return: Tuple of lists containing valid packavges in the first and invalid
90 	packages in the second entry
91 	"""
92 	valid_packages = {package for package in packages if find_package(package)}
93 	invalid_packages = set(packages) - valid_packages
94 
95 	return list(valid_packages), list(invalid_packages)
96 
97 
98 def installed_package(package :str) -> LocalPackage:
99 	package_info = {}
100 	try:
101 		for line in run_pacman(f"-Q --info {package}"):
102 			if b':' in line:
103 				key, value = line.decode().split(':', 1)
104 				package_info[key.strip().lower().replace(' ', '_')] = value.strip()
105 	except SysCallError:
106 		pass
107 
108 	return LocalPackage(**package_info)
109
```
--- 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/lib/packages/packages.py b/archinstall/lib/packages/packages.py
--- a/archinstall/lib/packages/packages.py
+++ b/archinstall/lib/packages/packages.py
@@ -1,25 +1,35 @@
 import json
 import ssl
-import urllib.request
 from typing import Dict, Any, Tuple, List
+from urllib.error import HTTPError
+from urllib.parse import urlencode
+from urllib.request import urlopen
 
 from ..exceptions import PackageError, SysCallError
 from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage
 from ..pacman import run_pacman
 
-BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/?name={package}'
+BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/'
 # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'
-BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/?name={group}'
+BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/'
 
 
-def group_search(name :str) -> List[PackageSearchResult]:
-	# TODO UPSTREAM: Implement /json/ for the groups search
+def _make_request(url: str, params: Dict) -> Any:
 	ssl_context = ssl.create_default_context()
 	ssl_context.check_hostname = False
 	ssl_context.verify_mode = ssl.CERT_NONE
+
+	encoded = urlencode(params)
+	full_url = f'{url}?{encoded}'
+
+	return urlopen(full_url, context=ssl_context)
+
+
+def group_search(name :str) -> List[PackageSearchResult]:
+	# TODO UPSTREAM: Implement /json/ for the groups search
 	try:
-		response = urllib.request.urlopen(BASE_GROUP_URL.format(group=name), context=ssl_context)
-	except urllib.error.HTTPError as err:
+		response = _make_request(BASE_GROUP_URL, {'name': name})
+	except HTTPError as err:
 		if err.code == 404:
 			return []
 		else:
@@ -38,10 +48,7 @@
 	"""
 	# TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ')
 	# TODO: utilize pacman cache first, upstream second.
-	ssl_context = ssl.create_default_context()
-	ssl_context.check_hostname = False
-	ssl_context.verify_mode = ssl.CERT_NONE
-	response = urllib.request.urlopen(BASE_URL_PKG_SEARCH.format(package=package), context=ssl_context)
+	response = _make_request(BASE_URL_PKG_SEARCH, {'name': package})
 
 	if response.code != 200:
 		raise PackageError(f"Could not locate package: [{response.code}] {response}")

{"golden_diff": "diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py\n--- a/archinstall/lib/packages/packages.py\n+++ b/archinstall/lib/packages/packages.py\n@@ -1,25 +1,35 @@\n import json\n import ssl\n-import urllib.request\n from typing import Dict, Any, Tuple, List\n+from urllib.error import HTTPError\n+from urllib.parse import urlencode\n+from urllib.request import urlopen\n \n from ..exceptions import PackageError, SysCallError\n from ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage\n from ..pacman import run_pacman\n \n-BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/?name={package}'\n+BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/'\n # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'\n-BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/?name={group}'\n+BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/'\n \n \n-def group_search(name :str) -> List[PackageSearchResult]:\n-\t# TODO UPSTREAM: Implement /json/ for the groups search\n+def _make_request(url: str, params: Dict) -> Any:\n \tssl_context = ssl.create_default_context()\n \tssl_context.check_hostname = False\n \tssl_context.verify_mode = ssl.CERT_NONE\n+\n+\tencoded = urlencode(params)\n+\tfull_url = f'{url}?{encoded}'\n+\n+\treturn urlopen(full_url, context=ssl_context)\n+\n+\n+def group_search(name :str) -> List[PackageSearchResult]:\n+\t# TODO UPSTREAM: Implement /json/ for the groups search\n \ttry:\n-\t\tresponse = urllib.request.urlopen(BASE_GROUP_URL.format(group=name), context=ssl_context)\n-\texcept urllib.error.HTTPError as err:\n+\t\tresponse = _make_request(BASE_GROUP_URL, {'name': name})\n+\texcept HTTPError as err:\n \t\tif err.code == 404:\n \t\t\treturn []\n \t\telse:\n@@ -38,10 +48,7 @@\n \t\"\"\"\n \t# TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ')\n \t# TODO: utilize pacman cache first, upstream second.\n-\tssl_context = ssl.create_default_context()\n-\tssl_context.check_hostname = False\n-\tssl_context.verify_mode = ssl.CERT_NONE\n-\tresponse = urllib.request.urlopen(BASE_URL_PKG_SEARCH.format(package=package), context=ssl_context)\n+\tresponse = _make_request(BASE_URL_PKG_SEARCH, {'name': package})\n \n \tif response.code != 200:\n \t\traise PackageError(f\"Could not locate package: [{response.code}] {response}\")\n", "issue": "invalid package names png++\nHi,\r\n\r\nI try to install some package with the \"package\" list in the config file, but if i use the package name png++ (i put the link under the message) it return as invalid package names.\r\n\r\nhttps://archlinux.org/packages/community/any/png++/\r\n\ninvalid package names png++\nHi,\r\n\r\nI try to install some package with the \"package\" list in the config file, but if i use the package name png++ (i put the link under the message) it return as invalid package names.\r\n\r\nhttps://archlinux.org/packages/community/any/png++/\r\n\n", "before_files": [{"content": "import json\nimport ssl\nimport urllib.request\nfrom typing import Dict, Any, Tuple, List\n\nfrom ..exceptions import PackageError, SysCallError\nfrom ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage\nfrom ..pacman import run_pacman\n\nBASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/?name={package}'\n# BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'\nBASE_GROUP_URL = 'https://archlinux.org/groups/search/json/?name={group}'\n\n\ndef group_search(name :str) -> List[PackageSearchResult]:\n\t# TODO UPSTREAM: Implement /json/ for the groups search\n\tssl_context = ssl.create_default_context()\n\tssl_context.check_hostname = False\n\tssl_context.verify_mode = ssl.CERT_NONE\n\ttry:\n\t\tresponse = urllib.request.urlopen(BASE_GROUP_URL.format(group=name), context=ssl_context)\n\texcept urllib.error.HTTPError as err:\n\t\tif err.code == 404:\n\t\t\treturn []\n\t\telse:\n\t\t\traise err\n\n\t# Just to be sure some code didn't slip through the exception\n\tdata = response.read().decode('UTF-8')\n\n\treturn [PackageSearchResult(**package) for package in json.loads(data)['results']]\n\n\ndef package_search(package :str) -> PackageSearch:\n\t\"\"\"\n\tFinds a specific package via the package database.\n\tIt makes a simple web-request, which might be a bit slow.\n\t\"\"\"\n\t# TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ')\n\t# TODO: utilize pacman cache first, upstream second.\n\tssl_context = ssl.create_default_context()\n\tssl_context.check_hostname = False\n\tssl_context.verify_mode = ssl.CERT_NONE\n\tresponse = urllib.request.urlopen(BASE_URL_PKG_SEARCH.format(package=package), context=ssl_context)\n\n\tif response.code != 200:\n\t\traise PackageError(f\"Could not locate package: [{response.code}] {response}\")\n\n\tdata = response.read().decode('UTF-8')\n\n\treturn PackageSearch(**json.loads(data))\n\n\ndef find_package(package :str) -> List[PackageSearchResult]:\n\tdata = package_search(package)\n\tresults = []\n\n\tfor result in data.results:\n\t\tif result.pkgname == package:\n\t\t\tresults.append(result)\n\n\t# If we didn't find the package in the search results,\n\t# odds are it's a group package\n\tif not results:\n\t\t# Check if the package is actually a group\n\t\tfor result in group_search(package):\n\t\t\tresults.append(result)\n\n\treturn results\n\n\ndef find_packages(*names :str) -> Dict[str, Any]:\n\t\"\"\"\n\tThis function returns the search results for many packages.\n\tThe function itself is rather slow, so consider not sending to\n\tmany packages to the search query.\n\t\"\"\"\n\tresult = {}\n\tfor package in names:\n\t\tfor found_package in find_package(package):\n\t\t\tresult[package] = found_package\n\n\treturn result\n\n\ndef validate_package_list(packages :list) -> Tuple[list, list]:\n\t\"\"\"\n\tValidates a list of given packages.\n\treturn: Tuple of lists containing valid packavges in the first and invalid\n\tpackages in the second entry\n\t\"\"\"\n\tvalid_packages = {package for package in packages if find_package(package)}\n\tinvalid_packages = set(packages) - valid_packages\n\n\treturn list(valid_packages), list(invalid_packages)\n\n\ndef installed_package(package :str) -> LocalPackage:\n\tpackage_info = {}\n\ttry:\n\t\tfor line in run_pacman(f\"-Q --info {package}\"):\n\t\t\tif b':' in line:\n\t\t\t\tkey, value = line.decode().split(':', 1)\n\t\t\t\tpackage_info[key.strip().lower().replace(' ', '_')] = value.strip()\n\texcept SysCallError:\n\t\tpass\n\n\treturn LocalPackage(**package_info)\n", "path": "archinstall/lib/packages/packages.py"}], "after_files": [{"content": "import json\nimport ssl\nfrom typing import Dict, Any, Tuple, List\nfrom urllib.error import HTTPError\nfrom urllib.parse import urlencode\nfrom urllib.request import urlopen\n\nfrom ..exceptions import PackageError, SysCallError\nfrom ..models.dataclasses import PackageSearch, PackageSearchResult, LocalPackage\nfrom ..pacman import run_pacman\n\nBASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/'\n# BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/'\nBASE_GROUP_URL = 'https://archlinux.org/groups/search/json/'\n\n\ndef _make_request(url: str, params: Dict) -> Any:\n\tssl_context = ssl.create_default_context()\n\tssl_context.check_hostname = False\n\tssl_context.verify_mode = ssl.CERT_NONE\n\n\tencoded = urlencode(params)\n\tfull_url = f'{url}?{encoded}'\n\n\treturn urlopen(full_url, context=ssl_context)\n\n\ndef group_search(name :str) -> List[PackageSearchResult]:\n\t# TODO UPSTREAM: Implement /json/ for the groups search\n\ttry:\n\t\tresponse = _make_request(BASE_GROUP_URL, {'name': name})\n\texcept HTTPError as err:\n\t\tif err.code == 404:\n\t\t\treturn []\n\t\telse:\n\t\t\traise err\n\n\t# Just to be sure some code didn't slip through the exception\n\tdata = response.read().decode('UTF-8')\n\n\treturn [PackageSearchResult(**package) for package in json.loads(data)['results']]\n\n\ndef package_search(package :str) -> PackageSearch:\n\t\"\"\"\n\tFinds a specific package via the package database.\n\tIt makes a simple web-request, which might be a bit slow.\n\t\"\"\"\n\t# TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ')\n\t# TODO: utilize pacman cache first, upstream second.\n\tresponse = _make_request(BASE_URL_PKG_SEARCH, {'name': package})\n\n\tif response.code != 200:\n\t\traise PackageError(f\"Could not locate package: [{response.code}] {response}\")\n\n\tdata = response.read().decode('UTF-8')\n\n\treturn PackageSearch(**json.loads(data))\n\n\ndef find_package(package :str) -> List[PackageSearchResult]:\n\tdata = package_search(package)\n\tresults = []\n\n\tfor result in data.results:\n\t\tif result.pkgname == package:\n\t\t\tresults.append(result)\n\n\t# If we didn't find the package in the search results,\n\t# odds are it's a group package\n\tif not results:\n\t\t# Check if the package is actually a group\n\t\tfor result in group_search(package):\n\t\t\tresults.append(result)\n\n\treturn results\n\n\ndef find_packages(*names :str) -> Dict[str, Any]:\n\t\"\"\"\n\tThis function returns the search results for many packages.\n\tThe function itself is rather slow, so consider not sending to\n\tmany packages to the search query.\n\t\"\"\"\n\tresult = {}\n\tfor package in names:\n\t\tfor found_package in find_package(package):\n\t\t\tresult[package] = found_package\n\n\treturn result\n\n\ndef validate_package_list(packages :list) -> Tuple[list, list]:\n\t\"\"\"\n\tValidates a list of given packages.\n\treturn: Tuple of lists containing valid packavges in the first and invalid\n\tpackages in the second entry\n\t\"\"\"\n\tvalid_packages = {package for package in packages if find_package(package)}\n\tinvalid_packages = set(packages) - valid_packages\n\n\treturn list(valid_packages), list(invalid_packages)\n\n\ndef installed_package(package :str) -> LocalPackage:\n\tpackage_info = {}\n\ttry:\n\t\tfor line in run_pacman(f\"-Q --info {package}\"):\n\t\t\tif b':' in line:\n\t\t\t\tkey, value = line.decode().split(':', 1)\n\t\t\t\tpackage_info[key.strip().lower().replace(' ', '_')] = value.strip()\n\texcept SysCallError:\n\t\tpass\n\n\treturn LocalPackage(**package_info)\n", "path": "archinstall/lib/packages/packages.py"}]}