Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 133 additions & 1 deletion scripts/g.extension.all/g.extension.all.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,27 @@
# % label: Force operation (required for removal)
# % end
from __future__ import print_function
import http
import os
import re
import sys

try:
import xml.etree.ElementTree as etree
except ImportError:
import elementtree.ElementTree as etree # Python <= 2.4

from six.moves.urllib import request as urlrequest
from six.moves.urllib.error import HTTPError, URLError

import grass.script as gscript
from grass.exceptions import CalledModuleError

HEADERS = {
"User-Agent": "Mozilla/5.0",
}
HTTP_STATUS_CODES = list(http.HTTPStatus)


def get_extensions():
addon_base = os.getenv("GRASS_ADDON_BASE")
Expand Down Expand Up @@ -77,6 +87,128 @@ def get_extensions():
return ret


def urlopen(url, *args, **kwargs):
"""Wrapper around urlopen. Same function as 'urlopen', but with the
ability to define headers.
"""
request = urlrequest.Request(url, headers=HEADERS)
return urlrequest.urlopen(request, *args, **kwargs)


def download_modules_xml_file(url, response_format, *args, **kwargs):
"""Generates JSON file containing the download URLs of the official
Addons

:param str url: url address
:param str response_format: content type

:return response: urllib.request.urlopen response object or None
"""
try:
response = urlopen(url, *args, **kwargs)

if not response.code == 200:
index = HTTP_STATUS_CODES.index(response.code)
desc = HTTP_STATUS_CODES[index].description
gscript.fatal(
_(
"Download file from <{url}>, "
"return status code {code}, "
"{desc}".format(
url=url,
code=response.code,
desc=desc,
),
),
)
if response_format not in response.getheader("Content-Type"):
gscript.fatal(
_(
"Wrong file format downloaded. "
"Check url <{url}>. Allowed file format is "
"{response_format}.".format(
url=url,
response_format=response_format,
),
),
)
return response

except HTTPError as err:
if err.code == 404:
gscript.fatal(
_(
"The download of the modules.xml file "
"from the server was not successful. "
"File on the server <{url}> doesn't "
"exists.".format(url=url),
),
)
else:
return download_modules_xml_file(
url=url,
response_format=response_format,
)
except URLError:
gscript.fatal(
_(
"Download file from <{url}>, "
"failed. Check internet connection.".format(
url=url,
),
),
)


def find_addon_name(addons):
"""Find correct addon name if addon is a multi-addon
e.g. wx.metadata contains multiple modules g.gui.cswbrowser etc.

Examples:
- for the g.gui.cswbrowser module the wx.metadata addon name is
returned
- for the i.sentinel.download module the i.sentinel addon name is
returned
etc.

:param list addons: list of individual addon modules to be reinstalled

:return set result: set of unique addon names to be reinstalled
"""
grass_version = os.getenv("GRASS_VERSION", "unknown")
if grass_version != "unknown":
major, minor, patch = grass_version.split(".")
else:
gscript.fatal(_("Unable to get GRASS GIS version."))
url = "https://grass.osgeo.org/addons/grass{major}/modules.xml".format(
major=major,
)
response = download_modules_xml_file(
url=url,
response_format="application/xml",
)
tree = etree.fromstring(response.read())
result = []
for addon in addons:
found = False
addon_pattern = re.compile(r".*{}$".format(addon))
for i in tree:
for f in i.findall(".//binary/file"):
if re.match(addon_pattern, f.text):
result.append(i.attrib["name"])
found = True
break
if not found:
gscript.warning(
_(
"The <{}> addon cannot be reinstalled. "
"Addon wasn't found among the official "
"addons.".format(addon)
),
)
return set(result)


def main():
remove = options["operation"] == "remove"
if remove or flags["f"]:
Expand All @@ -103,7 +235,7 @@ def main():
)
return 0

for ext in extensions:
for ext in find_addon_name(addons=extensions):
gscript.message("-" * 60)
if remove:
gscript.message(_("Removing extension <%s>...") % ext)
Expand Down