Repository: sunpy/sunpy

Issue:
Update Helio Code to expect str not bytes
Astropy 4.1 changed the output of [`parse_single_table`](https://keflavich-astropy.readthedocs.io/en/latest/api/astropy.io.votable.parse_single_table.html#astropy.io.votable.parse_single_table) to return `str` not `bytes`. https://github.com/sunpy/sunpy/pull/4628 modified our code to convert it back to bytes, but this was a short term solution for the 2.0 branch, we should be forwards-compatible and change the code to always expect `str` and decode the bytes to str after the function call (and then be able to remove that code when we depend on astropy > 4.1.


Discussion:
Is anyone working on this? Would like to try... 
No but I was planning to do this tomorrow. But as long as it gets done.
No worries, I will try to find another issue then. Any recommendations?
No I try not to recommend issues to people. Nicer if they find something they would enjoy to take on.
Sure, think I might have found one...

Resolving patch:
diff --git a/sunpy/net/helio/hec.py b/sunpy/net/helio/hec.py
--- a/sunpy/net/helio/hec.py
+++ b/sunpy/net/helio/hec.py
@@ -40,12 +40,10 @@ def votable_handler(xml_table):
     fake_file = io.BytesIO()
     fake_file.write(xml_table)
     votable = parse_single_table(fake_file)
-    # This is a workaround: in astropy 4.1 they made votable parse
-    # char type to strings over bytes which has changed the type of our return
     for i in range(len(votable.array)):
         item = votable.array[i][0]
-        if isinstance(item, str):
-            votable.array[i] = (votable.array[i][0].encode(),)
+        if isinstance(item, bytes):
+            votable.array[i] = (votable.array[i][0].decode(),)
     fake_file.close()
     return votable
 
@@ -70,7 +68,6 @@ def __init__(self, link=None):
         --------
         >>> from sunpy.net.helio import hec
         >>> hc = hec.HECClient()  # doctest: +REMOTE_DATA
-
         """
         if link is None:
             # The default wsdl file
@@ -92,14 +89,11 @@ def time_query(self, start_time, end_time, table=None, max_records=None):
         ----------
         start_time : str, `~sunpy.time.parse_time` parsable objects
             The time where the query window opens
-
         end_time : str, `~sunpy.time.parse_time` parsable objects
             The time where the query window closes
-
         table : bytes
             The table to query from. If the table is unknown, the user will be
             prompted to pick from a list of tables.
-
         max_records: int
             The maximum number of desired records.
 
@@ -110,11 +104,11 @@ def time_query(self, start_time, end_time, table=None, max_records=None):
 
         Examples
         --------
-        >>> from sunpy.net.helio import hec
-        >>> hc = hec.HECClient()  # doctest: +REMOTE_DATA
+        >>> from sunpy.net.helio import hec  # doctest: +SKIP
+        >>> hc = hec.HECClient()  # doctest: +SKIP
         >>> start = '2005/01/03'
         >>> end = '2005/12/03'
-        >>> temp = hc.time_query(start, end, max_records=10)  # doctest: +REMOTE_DATA +SKIP
+        >>> temp = hc.time_query(start, end, max_records=10)  # doctest: +SKIP
         """
         while table is None:
             table = self.select_table()
@@ -143,12 +137,12 @@ def get_table_names(self):
         --------
         >>> from sunpy.net.helio import hec
         >>> hc = hec.HECClient()  # doctest: +REMOTE_DATA
-        >>> print(hc.get_table_names())  # doctest: +REMOTE_DATA +SKIP
-        [(b'timed_see_flare',) (b'hi_event',) (b'yohkoh_flare_list',)
-         (b'wind_mfi_bs_crossing_time',) (b'seeds_soho',) (b'seeds_stb',)
+        >>> print(hc.get_table_names())  # doctest: +REMOTE_DATA
+        [('timed_see_flare',) ('hi_event',) ('yohkoh_flare_list',)
+         ('wind_mfi_bs_crossing_time',) ('seeds_soho',) ('seeds_stb',)
          ...
-         (b'rhessi_hxr_flare',) (b'cactus_soho_flow',) (b'cactus_soho_cme',)
-         (b'stereob_het_sep',)]
+         ('rhessi_hxr_flare',) ('cactus_soho_flow',) ('cactus_soho_cme',)
+         ('stereob_het_sep',)]
         """
         results = self.hec_client.service.getTableNames()
         tables = votable_handler(etree.tostring(results))
@@ -165,21 +159,20 @@ def select_table(self):
 
         Returns
         -------
-        `bytes`
-            contains the name of the table that the user picked.
+        `str`
+            Contains the name of the table that the user picked.
 
         Examples
         --------
-        >>> from sunpy.net.helio import hec
-        >>> hc = hec.HECClient()  # doctest: +REMOTE_DATA
-        >>> hc.select_table()  # doctest: +REMOTE_DATA +SKIP
-
+        >>> from sunpy.net.helio import hec  # doctest: +SKIP
+        >>> hc = hec.HECClient()  # doctest: +SKIP
+        >>> hc.select_table()  # doctest: +SKIP
         """
         tables = self.get_table_names()
         table_list = [t[0] for t in tables if len(t[0]) > 0]
         table_list.sort()
         for index, table in enumerate(table_list):
-            print(f'{index + 1} - {table.decode()}')
+            print(f'{index + 1} - {table}')
         while True:
             user_input = input(f"\nPlease enter a table number between 1 and {len(table_list)} "
                                "('e' to exit): ")
diff --git a/sunpy/net/helio/parser.py b/sunpy/net/helio/parser.py
--- a/sunpy/net/helio/parser.py
+++ b/sunpy/net/helio/parser.py
@@ -36,7 +36,7 @@ def webservice_parser(service='HEC'):
     Examples
     --------
     >>> from sunpy.net.helio import parser
-    >>> parser.webservice_parser()  # doctest: +SKIP
+    >>> parser.webservice_parser()  # doctest: +REMOTE_DATA
     ['http://msslkz.mssl.ucl.ac.uk/helio-hec/HelioService',
      'http://festung3.oats.inaf.it:8080/helio-hec/HelioService',
      'http://festung1.oats.inaf.it:8080/helio-hec/HelioService',
@@ -106,7 +106,7 @@ def endpoint_parser(link):
     for web_link in soup.find_all('a'):
         url = web_link.get('href')
         if url not in endpoints:
-            endpoints.append(url)
+            endpoints.append(url.replace(":80", "", 1))
     return endpoints
 
 
@@ -205,7 +205,7 @@ def wsdl_retriever(service='HEC'):
     --------
     >>> from sunpy.net.helio import parser
     >>> parser.wsdl_retriever()  # doctest: +REMOTE_DATA
-    'http://helio.mssl.ucl.ac.uk/helio_hec/HelioTavernaService?wsdl'
+    'http://helio.mssl.ucl.ac.uk/helio-hec/HelioTavernaService?wsdl'
 
     Notes
     -----
@@ -215,14 +215,12 @@ def wsdl_retriever(service='HEC'):
         this function to take a while to return. Timeout duration can be
         controlled through the LINK_TIMEOUT value
     """
-    def fail():
-        raise ValueError("No online HELIO servers can be found.")
     service_links = webservice_parser(service=service)
-    if service_links is not None:
+    if service_links:
         for link in service_links:
             wsdl_links = taverna_parser(link)
-            if wsdl_links is not None:
+            if wsdl_links:
                 for end_point in wsdl_links:
-                    if end_point is not None and link_test(end_point) is not None:
+                    if end_point and link_test(end_point):
                         return end_point
-    fail()
+    raise ValueError("No online HELIO servers can be found.")


Test patch:
diff --git a/sunpy/net/helio/tests/test_helio.py b/sunpy/net/helio/tests/test_helio.py
--- a/sunpy/net/helio/tests/test_helio.py
+++ b/sunpy/net/helio/tests/test_helio.py
@@ -279,14 +279,14 @@ def test_get_table_names(client):
     tables = client.get_table_names()
     assert len(tables) == 126
     table = tables[0][0]
-    assert isinstance(table, bytes)
-    assert table == b'timed_see_flare'
+    assert isinstance(table, str)
+    assert table == 'timed_see_flare'
 
 
 @pytest.mark.remote_data
 def test_select_table(client, monkeypatch):
     monkeypatch.setattr('builtins.input', lambda x: "11")
-    assert isinstance(client.select_table(), bytes)
+    assert isinstance(client.select_table(), str)
     monkeypatch.setattr('builtins.input', lambda x: "e")
     assert client.select_table() is None
 
@@ -295,7 +295,7 @@ def test_select_table(client, monkeypatch):
 def test_time_query(client):
     start = '2005/01/03'
     end = '2005/12/03'
-    table_name = b'rhessi_hxr_flare'
+    table_name = 'rhessi_hxr_flare'
     res = client.time_query(start, end, table=table_name, max_records=10)
     assert len(res.array) == 10