Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate applehelp to sphinxcontrib package #5983

Merged
merged 1 commit into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Dependencies
* The sphinxcontrib-websupport package is no longer a dependency
* Some packages are separated to sub packages:

- sphinxcontrib.applehelp
- sphinxcontrib.devhelp
- sphinxcontrib.jsmath
- sphinxcontrib.qthelp
Expand Down
5 changes: 5 additions & 0 deletions doc/extdev/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ The following is a list of deprecated interfaces.
- 4.0
- ``docutils.nodes.abbreviation``

* - ``sphinx.builders.applehelp``
- 2.0
- 4.0
- ``sphinxcontrib.applehelp``

* - ``sphinx.builders.devhelp``
- 2.0
- 4.0
Expand Down
6 changes: 5 additions & 1 deletion doc/usage/builders/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ The builder's "name" must be given to the **-b** command-line option of

.. _Qt help: https://doc.qt.io/qt-4.8/qthelp-framework.html

.. module:: sphinx.builders.applehelp
.. module:: sphinxcontrib.applehelp
.. class:: AppleHelpBuilder

This builder produces an Apple Help Book based on the same output as the
Expand All @@ -117,6 +117,10 @@ The builder's "name" must be given to the **-b** command-line option of

.. versionadded:: 1.3

.. versionchanged:: 2.0

Moved to sphinxcontrib.applehelp from sphinx.builders package.

.. module:: sphinxcontrib.devhelp
.. class:: DevhelpBuilder

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
sys.exit(1)

install_requires = [
'sphinxcontrib-applehelp',
'sphinxcontrib-devhelp',
'sphinxcontrib-jsmath',
'sphinxcontrib-qthelp',
Expand Down
2 changes: 1 addition & 1 deletion sphinx/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@

builtin_extensions = (
'sphinx.addnodes',
'sphinx.builders.applehelp',
'sphinx.builders.changes',
'sphinx.builders.epub3',
'sphinx.builders.dummy',
Expand Down Expand Up @@ -106,6 +105,7 @@
'sphinx.environment.collectors.toctree',
'sphinx.environment.collectors.indexentries',
# 1st party extensions
'sphinxcontrib.applehelp',
'sphinxcontrib.devhelp',
'sphinxcontrib.qthelp',
# Strictly, alabaster theme is not a builtin extension,
Expand Down
262 changes: 18 additions & 244 deletions sphinx/builders/applehelp.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,262 +8,36 @@
:license: BSD, see LICENSE for details.
"""

import plistlib
import shlex
import subprocess
from os import path, environ
from subprocess import CalledProcessError, PIPE, STDOUT
import warnings

from sphinx import package_dir
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.errors import SphinxError
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import SkipProgressMessage, progress_message
from sphinx.util.fileutil import copy_asset, copy_asset_file
from sphinx.util.matching import Matcher
from sphinx.util.osutil import ensuredir, make_filename
from sphinxcontrib.applehelp import (
AppleHelpCodeSigningFailed,
AppleHelpIndexerFailed,
AppleHelpBuilder,
)

from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias

if False:
# For type annotation
from typing import Any, Dict # NOQA
from sphinx.application import Sphinx # NOQA


logger = logging.getLogger(__name__)
template_dir = path.join(package_dir, 'templates', 'applehelp')


class AppleHelpIndexerFailed(SphinxError):
category = __('Help indexer failed')


class AppleHelpCodeSigningFailed(SphinxError):
category = __('Code signing failed')


class AppleHelpBuilder(StandaloneHTMLBuilder):
"""
Builder that outputs an Apple help book. Requires Mac OS X as it relies
on the ``hiutil`` command line tool.
"""
name = 'applehelp'
epilog = __('The help book is in %(outdir)s.\n'
'Note that won\'t be able to view it unless you put it in '
'~/Library/Documentation/Help or install it in your application '
'bundle.')

# don't copy the reST source
copysource = False
supported_image_types = ['image/png', 'image/gif', 'image/jpeg',
'image/tiff', 'image/jp2', 'image/svg+xml']

# don't add links
add_permalinks = False

# this is an embedded HTML format
embedded = True

# don't generate the search index or include the search page
search = False

def init(self):
# type: () -> None
super().init()
# the output files for HTML help must be .html only
self.out_suffix = '.html'
self.link_suffix = '.html'

if self.config.applehelp_bundle_id is None:
raise SphinxError(__('You must set applehelp_bundle_id before '
'building Apple Help output'))

self.bundle_path = path.join(self.outdir,
self.config.applehelp_bundle_name + '.help')
self.outdir = path.join(self.bundle_path,
'Contents',
'Resources',
self.config.applehelp_locale + '.lproj')

def handle_finish(self):
# type: () -> None
super().handle_finish()

self.finish_tasks.add_task(self.copy_localized_files)
self.finish_tasks.add_task(self.build_helpbook)

@progress_message(__('copying localized files'))
def copy_localized_files(self):
# type: () -> None
source_dir = path.join(self.confdir, self.config.applehelp_locale + '.lproj')
target_dir = self.outdir

if path.isdir(source_dir):
excluded = Matcher(self.config.exclude_patterns + ['**/.*'])
copy_asset(source_dir, target_dir, excluded,
context=self.globalcontext, renderer=self.templates)

def build_helpbook(self):
# type: () -> None
contents_dir = path.join(self.bundle_path, 'Contents')
resources_dir = path.join(contents_dir, 'Resources')
language_dir = path.join(resources_dir,
self.config.applehelp_locale + '.lproj')
ensuredir(language_dir)

self.build_info_plist(contents_dir)
self.copy_applehelp_icon(resources_dir)
self.build_access_page(language_dir)
self.build_helpindex(language_dir)

if self.config.applehelp_codesign_identity:
self.do_codesign()

@progress_message(__('writing Info.plist'))
def build_info_plist(self, contents_dir):
# type: (str) -> None
"""Construct the Info.plist file."""
info_plist = {
'CFBundleDevelopmentRegion': self.config.applehelp_dev_region,
'CFBundleIdentifier': self.config.applehelp_bundle_id,
'CFBundleInfoDictionaryVersion': '6.0',
'CFBundlePackageType': 'BNDL',
'CFBundleShortVersionString': self.config.release,
'CFBundleSignature': 'hbwr',
'CFBundleVersion': self.config.applehelp_bundle_version,
'HPDBookAccessPath': '_access.html',
'HPDBookIndexPath': 'search.helpindex',
'HPDBookTitle': self.config.applehelp_title,
'HPDBookType': '3',
'HPDBookUsesExternalViewer': False,
}

if self.config.applehelp_icon is not None:
info_plist['HPDBookIconPath'] = path.basename(self.config.applehelp_icon)

if self.config.applehelp_kb_url is not None:
info_plist['HPDBookKBProduct'] = self.config.applehelp_kb_product
info_plist['HPDBookKBURL'] = self.config.applehelp_kb_url

if self.config.applehelp_remote_url is not None:
info_plist['HPDBookRemoteURL'] = self.config.applehelp_remote_url

with open(path.join(contents_dir, 'Info.plist'), 'wb') as f:
plistlib.dump(info_plist, f)

def copy_applehelp_icon(self, resources_dir):
# type: (str) -> None
"""Copy the icon, if one is supplied."""
if self.config.applehelp_icon:

try:
with progress_message(__('copying icon... ')):
applehelp_icon = path.join(self.srcdir, self.config.applehelp_icon)
copy_asset_file(applehelp_icon, resources_dir)
except Exception as err:
logger.warning(__('cannot copy icon file %r: %s'), applehelp_icon, err)

@progress_message(__('building access page'))
def build_access_page(self, language_dir):
# type: (str) -> None
"""Build the access page."""
context = {
'toc': self.config.master_doc + self.out_suffix,
'title': self.config.applehelp_title,
}
copy_asset_file(path.join(template_dir, '_access.html_t'), language_dir, context)

@progress_message(__('generating help index'))
def build_helpindex(self, language_dir):
# type: (str) -> None
"""Generate the help index."""
args = [
self.config.applehelp_indexer_path,
'-Cf',
path.join(language_dir, 'search.helpindex'),
language_dir
]

if self.config.applehelp_index_anchors is not None:
args.append('-a')

if self.config.applehelp_min_term_length is not None:
args += ['-m', '%s' % self.config.applehelp_min_term_length]

if self.config.applehelp_stopwords is not None:
args += ['-s', self.config.applehelp_stopwords]

if self.config.applehelp_locale is not None:
args += ['-l', self.config.applehelp_locale]

if self.config.applehelp_disable_external_tools:
raise SkipProgressMessage(__('you will need to index this help book with:\n %s'),
' '.join([shlex.quote(arg) for arg in args]))
else:
try:
subprocess.run(args, stdout=PIPE, stderr=STDOUT, check=True)
except OSError:
raise AppleHelpIndexerFailed(__('Command not found: %s') % args[0])
except CalledProcessError as exc:
raise AppleHelpIndexerFailed(exc.stdout)

@progress_message(__('signing help book'))
def do_codesign(self):
# type: () -> None
"""If we've been asked to, sign the bundle."""
args = [
self.config.applehelp_codesign_path,
'-s', self.config.applehelp_codesign_identity,
'-f'
]

args += self.config.applehelp_codesign_flags

args.append(self.bundle_path)

if self.config.applehelp_disable_external_tools:
raise SkipProgressMessage(__('you will need to sign this help book with:\n %s'),
' '.join([shlex.quote(arg) for arg in args]))
else:
try:
subprocess.run(args, stdout=PIPE, stderr=STDOUT, check=True)
except OSError:
raise AppleHelpCodeSigningFailed(__('Command not found: %s') % args[0])
except CalledProcessError as exc:
raise AppleHelpCodeSigningFailed(exc.stdout)
deprecated_alias('sphinx.builders.applehelp',
{
'AppleHelpCodeSigningFailed': AppleHelpCodeSigningFailed,
'AppleHelpIndexerFailed': AppleHelpIndexerFailed,
'AppleHelpBuilder': AppleHelpBuilder,
},
RemovedInSphinx40Warning)


def setup(app):
# type: (Sphinx) -> Dict[str, Any]
app.setup_extension('sphinx.builders.html')
app.add_builder(AppleHelpBuilder)

app.add_config_value('applehelp_bundle_name',
lambda self: make_filename(self.project), 'applehelp')
app.add_config_value('applehelp_bundle_id', None, 'applehelp', [str])
app.add_config_value('applehelp_dev_region', 'en-us', 'applehelp')
app.add_config_value('applehelp_bundle_version', '1', 'applehelp')
app.add_config_value('applehelp_icon', None, 'applehelp', [str])
app.add_config_value('applehelp_kb_product',
lambda self: '%s-%s' % (make_filename(self.project), self.release),
'applehelp')
app.add_config_value('applehelp_kb_url', None, 'applehelp', [str])
app.add_config_value('applehelp_remote_url', None, 'applehelp', [str])
app.add_config_value('applehelp_index_anchors', False, 'applehelp', [str])
app.add_config_value('applehelp_min_term_length', None, 'applehelp', [str])
app.add_config_value('applehelp_stopwords',
lambda self: self.language or 'en', 'applehelp')
app.add_config_value('applehelp_locale', lambda self: self.language or 'en', 'applehelp')
app.add_config_value('applehelp_title', lambda self: self.project + ' Help', 'applehelp')
app.add_config_value('applehelp_codesign_identity',
lambda self: environ.get('CODE_SIGN_IDENTITY', None),
'applehelp')
app.add_config_value('applehelp_codesign_flags',
lambda self: shlex.split(environ.get('OTHER_CODE_SIGN_FLAGS', '')),
'applehelp')
app.add_config_value('applehelp_indexer_path', '/usr/bin/hiutil', 'applehelp')
app.add_config_value('applehelp_codesign_path', '/usr/bin/codesign', 'applehelp')
app.add_config_value('applehelp_disable_external_tools', False, None)
warnings.warn('sphinx.builders.applehelp has been moved to sphinxcontrib-applehelp.',
RemovedInSphinx40Warning)
app.setup_extension('sphinxcontrib.applehelp')

return {
'version': 'builtin',
Expand Down
12 changes: 0 additions & 12 deletions sphinx/templates/applehelp/_access.html_t

This file was deleted.

3 changes: 0 additions & 3 deletions tests/roots/test-root/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@
html_last_updated_fmt = '%b %d, %Y'
html_context = {'hckey': 'hcval', 'hckey_co': 'wrong_hcval_co'}

applehelp_bundle_id = 'org.sphinx-doc.Sphinx.help'
applehelp_disable_external_tools = True

latex_additional_files = ['svgimg.svg']

coverage_c_path = ['special/*.h']
Expand Down
2 changes: 0 additions & 2 deletions tests/roots/test-root/en.lproj/localized.txt

This file was deleted.

2 changes: 1 addition & 1 deletion tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
[
# note: no 'html' - if it's ok with dirhtml it's ok with html
'dirhtml', 'singlehtml', 'pickle', 'json', 'text', 'htmlhelp',
'applehelp', 'changes', 'xml', 'pseudoxml', 'linkcheck',
'changes', 'xml', 'pseudoxml', 'linkcheck',
],
)
@mock.patch('sphinx.builders.linkcheck.requests.head',
Expand Down
Loading