-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix problems with the built in help system (#1084)
This fixes several problems with the built in help system: * The WBrowser plugin is often unable to intialize correctly because it requires a QWebEngineView widget which is often not installed (for example it is offered separately in conda) * Even when the widget is installed, it often can fail to start if OpenGL libraries are not configured correctly (often the case for conda on Linux, for example) * The RTD web site often fails to access certain items, giving a HTTP 403 (Forbidden) error, particularly with trying to fetch the list if versions (ginga.doc.download_doc._find_rtd_version()) * The HTML ZIP download (ginga.doc.download_doc. _download_rtd_zip()) sometimes fails as well Solutions in this PR: * Remove WBrowser plugin * Ginga will pop up a dialog to ask the user whether they want to view the documentation in an external browser from the RTD link, or view the (local) plugin docstring in a text widget * now handles URLs by calling Python's "webbrowser" module to open them * option to view local docstring in a plain text widget * Remove WebView widget from various Widgets.py * Fix Help menu item * Remove requirements for `beautifulsoup` and `docutils`
- Loading branch information
Showing
13 changed files
with
142 additions
and
537 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,180 +1,45 @@ | ||
"""Download rendered HTML doc from RTD.""" | ||
import os | ||
import shutil | ||
import zipfile | ||
import urllib | ||
"""Tools for accessing HTML doc from RTD.""" | ||
|
||
from astropy.utils import minversion | ||
from astropy.utils.data import get_pkg_data_path | ||
import re | ||
|
||
from ginga import toolkit | ||
from ginga.GingaPlugin import GlobalPlugin, LocalPlugin | ||
import ginga | ||
|
||
__all__ = ['get_doc'] | ||
__all__ = ['get_online_docs_url'] | ||
|
||
# base of our online documentation | ||
rtd_base_url = "https://ginga.readthedocs.io/en/" | ||
|
||
def _find_rtd_version(): | ||
"""Find closest RTD doc version.""" | ||
vstr = 'latest' | ||
try: | ||
import ginga | ||
from bs4 import BeautifulSoup | ||
except ImportError: | ||
return vstr | ||
|
||
# No active doc build before this release, just use latest. | ||
if not minversion(ginga, '2.6.0'): | ||
return vstr | ||
|
||
# Get RTD download listing. | ||
url = 'https://readthedocs.org/projects/ginga/downloads/' | ||
with urllib.request.urlopen(url) as r: # nosec | ||
soup = BeautifulSoup(r, 'html.parser') | ||
|
||
# Compile a list of available HTML doc versions for download. | ||
all_rtd_vernums = [] | ||
for link in soup.find_all('a'): | ||
href = link.get('href') | ||
if 'htmlzip' not in href: | ||
continue | ||
s = href.split('/')[-2] | ||
if s.startswith('v'): # Ignore latest and stable | ||
all_rtd_vernums.append(s) | ||
all_rtd_vernums.sort(reverse=True) | ||
|
||
# Find closest match. | ||
ginga_ver = ginga.__version__ | ||
for rtd_ver in all_rtd_vernums: | ||
if ginga_ver > rtd_ver[1:]: # Ignore "v" in comparison | ||
break | ||
else: | ||
vstr = rtd_ver | ||
|
||
return vstr | ||
|
||
|
||
def _download_rtd_zip(rtd_version=None, **kwargs): | ||
def get_online_docs_url(plugin=None): | ||
""" | ||
Download and extract HTML ZIP from RTD to installed doc data path. | ||
Download is skipped if content already exists. | ||
Return URL to online documentation closest to this Ginga version. | ||
Parameters | ||
---------- | ||
rtd_version : str or `None` | ||
RTD version to download; e.g., "latest", "stable", or "v2.6.0". | ||
If not given, download closest match to software version. | ||
kwargs : dict | ||
Keywords for ``urlretrieve()``. | ||
Returns | ||
------- | ||
index_html : str | ||
Path to local "index.html". | ||
""" | ||
# https://github.com/ejeschke/ginga/pull/451#issuecomment-298403134 | ||
if not toolkit.family.startswith('qt'): | ||
raise ValueError('Downloaded documentation not compatible with {} ' | ||
'UI toolkit browser'.format(toolkit.family)) | ||
|
||
if rtd_version is None: | ||
rtd_version = _find_rtd_version() | ||
|
||
data_path = os.path.dirname( | ||
get_pkg_data_path('help.html', package='ginga.doc')) | ||
index_html = os.path.join(data_path, 'index.html') | ||
|
||
# There is a previous download of documentation; Do nothing. | ||
# There is no check if downloaded version is outdated; The idea is that | ||
# this folder would be empty again when installing new version. | ||
if os.path.isfile(index_html): | ||
return index_html | ||
|
||
url = ('https://readthedocs.org/projects/ginga/downloads/htmlzip/' | ||
'{}/'.format(rtd_version)) | ||
local_path = urllib.request.urlretrieve(url, **kwargs)[0] # nosec | ||
|
||
with zipfile.ZipFile(local_path, 'r') as zf: | ||
zf.extractall(data_path) | ||
|
||
# RTD makes an undesirable sub-directory, so move everything there | ||
# up one level and delete it. | ||
subdir = os.path.join(data_path, 'ginga-{}'.format(rtd_version)) | ||
for s in os.listdir(subdir): | ||
src = os.path.join(subdir, s) | ||
if os.path.isfile(src): | ||
shutil.copy(src, data_path) | ||
else: # directory | ||
shutil.copytree(src, os.path.join(data_path, s)) | ||
shutil.rmtree(subdir) | ||
|
||
if not os.path.isfile(index_html): | ||
raise OSError( | ||
'{} is missing; Ginga doc download failed'.format(index_html)) | ||
|
||
return index_html | ||
|
||
|
||
def get_doc(logger=None, plugin=None, reporthook=None): | ||
""" | ||
Return URL to documentation. Attempt download if does not exist. | ||
Parameters | ||
---------- | ||
logger : obj or `None` | ||
Ginga logger. | ||
plugin : obj or `None` | ||
Plugin object. If given, URL points to plugin doc directly. | ||
If this function is called from within plugin class, | ||
pass ``self`` here. | ||
reporthook : callable or `None` | ||
Report hook for ``urlretrieve()``. | ||
Returns | ||
------- | ||
url : str or `None` | ||
URL to local documentation, if available. | ||
url : str | ||
URL to online documentation (top-level, if plugin == None). | ||
""" | ||
from ginga.GingaPlugin import GlobalPlugin, LocalPlugin | ||
|
||
if isinstance(plugin, GlobalPlugin): | ||
plugin_page = 'plugins_global' | ||
plugin_name = str(plugin) | ||
elif isinstance(plugin, LocalPlugin): | ||
plugin_page = 'plugins_local' | ||
plugin_name = str(plugin) | ||
else: | ||
plugin_page = None | ||
plugin_name = None | ||
|
||
try: | ||
index_html = _download_rtd_zip(reporthook=reporthook) | ||
|
||
# Download failed, use online resource | ||
except Exception as e: | ||
url = 'https://ginga.readthedocs.io/en/latest/' | ||
|
||
if plugin_name is not None: | ||
if toolkit.family.startswith('qt'): | ||
# This displays plugin docstring. | ||
url = None | ||
else: | ||
# This redirects to online doc. | ||
url += 'manual/{}/{}.html'.format(plugin_page, plugin_name) | ||
|
||
if logger is not None: | ||
logger.error(str(e)) | ||
|
||
# Use local resource | ||
ginga_ver = ginga.__version__ | ||
if re.match(r'^v\d+\.\d+\.\d+$', ginga_ver): | ||
rtd_version = ginga_ver | ||
else: | ||
pfx = 'file:' | ||
url = '{}{}'.format(pfx, index_html) | ||
|
||
# https://github.com/rtfd/readthedocs.org/issues/2803 | ||
if plugin_name is not None: | ||
url += '#{}'.format(plugin_name) | ||
# default to latest | ||
rtd_version = 'latest' | ||
url = f"{rtd_base_url}{rtd_version}" | ||
if plugin is not None: | ||
plugin_name = str(plugin) | ||
if isinstance(plugin, GlobalPlugin): | ||
url += f'/manual/plugins_global/{plugin_name}.html' | ||
elif isinstance(plugin, LocalPlugin): | ||
url += f'/manual/plugins_local/{plugin_name}.html' | ||
|
||
return url |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.