From f3074d17470f5a403166176a9efd6198505d891c Mon Sep 17 00:00:00 2001 From: Stephen Finucane Date: Fri, 26 Jan 2018 09:41:50 +0000 Subject: [PATCH 1/8] setup: Configure flake8 extension as a local plugin We don't want to install this as a system plugin. Now that flake8 3.5.0 is in the wild, we can use this. Signed-off-by: Stephen Finucane Fixes: #4492 --- setup.cfg | 4 ++++ setup.py | 13 +++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/setup.cfg b/setup.cfg index c19d0d518..cab6b74d1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,6 +34,10 @@ max-line-length = 95 ignore = E116,E241,E251,E741 exclude = .git,.tox,.venv,tests/*,build/*,doc/_build/*,sphinx/search/*,sphinx/pycode/pgen2/*,doc/ext/example*.py +[flake8:local-plugins] +extension = + X101 = utils.checks:sphinx_has_header + [mypy] python_version = 2.7 show_column_numbers = True diff --git a/setup.py b/setup.py index f35e5f88d..abf71df78 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 4): print('ERROR: Sphinx requires at least Python 2.7 or 3.4 to run.') sys.exit(1) -requires = [ +install_requires = [ 'six>=1.5', 'Jinja2>=2.3', 'Pygments>=2.0', @@ -47,7 +47,7 @@ extras_require = { 'pytest', 'pytest-cov', 'html5lib', - 'flake8', + 'flake8>=3.5.0', ], 'test:python_version<"3"': [ 'enum34', @@ -226,15 +226,8 @@ setup( 'distutils.commands': [ 'build_sphinx = sphinx.setup_command:BuildDoc', ], - # consider moving this to 'flake8:local-plugins' once flake8 3.5.0 is - # in the wild: - # http://flake8.pycqa.org/en/latest/user/configuration.html\ - # #using-local-plugins - 'flake8.extension': [ - 'X101 = utils.checks:sphinx_has_header', - ], }, - install_requires=requires, + install_requires=install_requires, extras_require=extras_require, cmdclass=cmdclass, ) From fd2425238e5407b5caf9b3f8d9e7840de7dc6583 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 27 Jan 2018 12:20:39 +0900 Subject: [PATCH 2/8] Fix #4490: autodoc: type annotation is broken with python 3.7.0a4+ --- CHANGES | 1 + sphinx/util/inspect.py | 49 ++++++++++++++++++++++++++++++++++++++ tests/test_util_inspect.py | 7 +----- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 22a5f5936..cfddb8308 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,7 @@ Bugs fixed * #4415: autodoc classifies inherited classmethods as regular methods * #4415: autodoc classifies inherited staticmethods as regular methods * #4472: DOCUMENTATION_OPTIONS is not defined +* #4490: autodoc: type annotation is broken with python 3.7.0a4+ Testing -------- diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index fd13d6fd6..65005c240 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -431,6 +431,55 @@ class Signature(object): Displaying complex types from ``typing`` relies on its private API. """ + if sys.version_info >= (3, 7): # py37+ + return self.format_annotation_new(annotation) + else: + return self.format_annotation_old(annotation) + + def format_annotation_new(self, annotation): + # type: (Any) -> str + """format_annotation() for py37+""" + module = getattr(annotation, '__module__', None) + if isinstance(annotation, string_types): + return annotation # type: ignore + elif isinstance(annotation, typing.TypeVar): # type: ignore + return annotation.__name__ + elif not annotation: + return repr(annotation) + elif module == 'builtins': + return annotation.__qualname__ + elif annotation is Ellipsis: + return '...' + + if module == 'typing': + if getattr(annotation, '_name', None): + qualname = annotation._name + elif getattr(annotation, '__qualname__', None): + qualname = annotation.__qualname__ + else: + qualname = self.format_annotation(annotation.__origin__) # ex. Union + elif hasattr(annotation, '__qualname__'): + qualname = '%s.%s' % (module, annotation.__qualname__) + else: + qualname = repr(annotation) + + if getattr(annotation, '__args__', None): + if qualname == 'Union': + args = ', '.join(self.format_annotation(a) for a in annotation.__args__) + return '%s[%s]' % (qualname, args) + elif qualname == 'Callable': + args = ', '.join(self.format_annotation(a) for a in annotation.__args__[:-1]) + returns = self.format_annotation(annotation.__args__[-1]) + return '%s[[%s], %s]' % (qualname, args, returns) + else: + args = ', '.join(self.format_annotation(a) for a in annotation.__args__) + return '%s[%s]' % (qualname, args) + + return qualname + + def format_annotation_old(self, annotation): + # type: (Any) -> str + """format_annotation() for py36 or below""" if isinstance(annotation, string_types): return annotation # type: ignore if isinstance(annotation, typing.TypeVar): # type: ignore diff --git a/tests/test_util_inspect.py b/tests/test_util_inspect.py index 136536ec6..b5d50ed71 100644 --- a/tests/test_util_inspect.py +++ b/tests/test_util_inspect.py @@ -215,12 +215,7 @@ def test_Signature_annotations(): # TypeVars and generic types with TypeVars sig = inspect.Signature(f2).format_args() - if sys.version_info < (3, 7): - sig == ('(x: typing.List[T], y: typing.List[T_co], z: T) -> ' - 'typing.List[T_contra]') - else: - sig == ('(x: typing.List[~T], y: typing.List[+T_co], z: T) -> ' - 'typing.List[-T_contra]') + assert sig == '(x: List[T], y: List[T_co], z: T) -> List[T_contra]' # Union types sig = inspect.Signature(f3).format_args() From 3fde14929bf149327c1ac1d56dccf65cf8677f0d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 27 Jan 2018 23:23:59 +0900 Subject: [PATCH 3/8] Fix #3917: citation labels are tranformed to ellipsis --- CHANGES | 1 + sphinx/transforms/__init__.py | 5 +++++ sphinx/util/nodes.py | 2 ++ 3 files changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index b265ba2c1..3d123f00d 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,7 @@ Bugs fixed * #4434: pure numbers as link targets produce warning * #4477: Build fails after building specific files * #4449: apidoc: include "empty" packages that contain modules +* #3917: citation labels are tranformed to ellipsis Testing -------- diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index acfff6a4d..ed3c19ba5 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -209,10 +209,15 @@ class CitationReferences(SphinxTransform): def apply(self): # type: () -> None + # mark citation labels as not smartquoted + for citnode in self.document.traverse(nodes.citation): + citnode[0]['support_smartquotes'] = False + for citnode in self.document.traverse(nodes.citation_reference): cittext = citnode.astext() refnode = addnodes.pending_xref(cittext, refdomain='std', reftype='citation', reftarget=cittext, refwarn=True, + support_smartquotes=False, ids=citnode["ids"]) refnode.source = citnode.source or citnode.parent.source refnode.line = citnode.line or citnode.parent.line diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 97e5b7f30..569a4c7b3 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -380,6 +380,8 @@ def is_smartquotable(node): """Check the node is smart-quotable or not.""" if isinstance(node.parent, NON_SMARTQUOTABLE_PARENT_NODES): return False + elif node.parent.get('support_smartquotes', None) is False: + return False elif getattr(node, 'support_smartquotes', None) is False: return False else: From d60ef2d6b204300bb2f590e5d9090e508979ae39 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 28 Jan 2018 15:11:59 +0900 Subject: [PATCH 4/8] Fix utils package is installed --- CHANGES | 1 + setup.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4782fa141..8d95eb9f8 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Bugs fixed * #4472: DOCUMENTATION_OPTIONS is not defined * #4491: autodoc: prefer _MockImporter over other importers in sys.meta_path * #4490: autodoc: type annotation is broken with python 3.7.0a4+ +* utils package is installed Testing -------- diff --git a/setup.py b/setup.py index abf71df78..32c6f4d50 100644 --- a/setup.py +++ b/setup.py @@ -214,7 +214,7 @@ setup( 'Topic :: Utilities', ], platforms='any', - packages=find_packages(exclude=['tests']), + packages=find_packages(exclude=['tests', 'utils']), include_package_data=True, entry_points={ 'console_scripts': [ From 41d99d36275d29bc70d1357d2be37ddabd6737b0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 28 Jan 2018 15:20:50 +0900 Subject: [PATCH 5/8] Give local-plugins.paths option to flake8 It will be available on flake8-3.5.0+. (see https://gitlab.com/pycqa/flake8/issues/379) --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index cab6b74d1..dbef653bc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,6 +37,8 @@ exclude = .git,.tox,.venv,tests/*,build/*,doc/_build/*,sphinx/search/*,sphinx/py [flake8:local-plugins] extension = X101 = utils.checks:sphinx_has_header +paths = + . [mypy] python_version = 2.7 From f3812b0d18f25fe263109c9962383bdd879c4bd7 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 28 Jan 2018 17:22:41 +0900 Subject: [PATCH 6/8] Fix #4275: Formats accepted by sphinx.util.i18n.format_date are limite --- CHANGES | 1 + sphinx/util/i18n.py | 65 ++++++++++++++++++++++++----------------- tests/test_util_i18n.py | 2 ++ 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 4782fa141..f0c937ce0 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Bugs fixed * #4472: DOCUMENTATION_OPTIONS is not defined * #4491: autodoc: prefer _MockImporter over other importers in sys.meta_path * #4490: autodoc: type annotation is broken with python 3.7.0a4+ +* #4275: Formats accepted by sphinx.util.i18n.format_date are limited Testing -------- diff --git a/sphinx/util/i18n.py b/sphinx/util/i18n.py index 75a8506fa..d52cc0aab 100644 --- a/sphinx/util/i18n.py +++ b/sphinx/util/i18n.py @@ -152,34 +152,45 @@ def find_catalog_source_files(locale_dirs, locale, domains=None, gettext_compact # date_format mappings: ustrftime() to bable.dates.format_datetime() date_format_mappings = { - '%a': 'EEE', # Weekday as locale’s abbreviated name. - '%A': 'EEEE', # Weekday as locale’s full name. - '%b': 'MMM', # Month as locale’s abbreviated name. - '%B': 'MMMM', # Month as locale’s full name. - '%c': 'medium', # Locale’s appropriate date and time representation. - '%d': 'dd', # Day of the month as a zero-padded decimal number. - '%H': 'HH', # Hour (24-hour clock) as a decimal number [00,23]. - '%I': 'hh', # Hour (12-hour clock) as a decimal number [01,12]. - '%j': 'DDD', # Day of the year as a zero-padded decimal number. - '%m': 'MM', # Month as a zero-padded decimal number. - '%M': 'mm', # Minute as a decimal number [00,59]. - '%p': 'a', # Locale’s equivalent of either AM or PM. - '%S': 'ss', # Second as a decimal number. - '%U': 'WW', # Week number of the year (Sunday as the first day of the week) - # as a zero padded decimal number. All days in a new year preceding - # the first Sunday are considered to be in week 0. - '%w': 'e', # Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. - '%W': 'WW', # Week number of the year (Monday as the first day of the week) - # as a decimal number. All days in a new year preceding the first - # Monday are considered to be in week 0. - '%x': 'medium', # Locale’s appropriate date representation. - '%X': 'medium', # Locale’s appropriate time representation. - '%y': 'YY', # Year without century as a zero-padded decimal number. - '%Y': 'YYYY', # Year with century as a decimal number. - '%Z': 'zzzz', # Time zone name (no characters if no time zone exists). - '%%': '%', + '%a': 'EEE', # Weekday as locale’s abbreviated name. + '%A': 'EEEE', # Weekday as locale’s full name. + '%b': 'MMM', # Month as locale’s abbreviated name. + '%B': 'MMMM', # Month as locale’s full name. + '%c': 'medium', # Locale’s appropriate date and time representation. + '%-d': 'd', # Day of the month as a decimal number. + '%d': 'dd', # Day of the month as a zero-padded decimal number. + '%-H': 'H', # Hour (24-hour clock) as a decimal number [0,23]. + '%H': 'HH', # Hour (24-hour clock) as a zero-padded decimal number [00,23]. + '%-I': 'h', # Hour (12-hour clock) as a decimal number [1,12]. + '%I': 'hh', # Hour (12-hour clock) as a zero-padded decimal number [01,12]. + '%-j': 'D', # Day of the year as a decimal number. + '%j': 'DDD', # Day of the year as a zero-padded decimal number. + '%-m': 'M', # Month as a decimal number. + '%m': 'MM', # Month as a zero-padded decimal number. + '%-M': 'm', # Minute as a decimal number [0,59]. + '%M': 'mm', # Minute as a zero-padded decimal number [00,59]. + '%p': 'a', # Locale’s equivalent of either AM or PM. + '%-S': 's', # Second as a decimal number. + '%S': 'ss', # Second as a zero-padded decimal number. + '%U': 'WW', # Week number of the year (Sunday as the first day of the week) + # as a zero padded decimal number. All days in a new year preceding + # the first Sunday are considered to be in week 0. + '%w': 'e', # Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. + '%-W': 'W', # Week number of the year (Monday as the first day of the week) + # as a decimal number. All days in a new year preceding the first + # Monday are considered to be in week 0. + '%W': 'WW', # Week number of the year (Monday as the first day of the week) + # as a zero-padded decimal number. + '%x': 'medium', # Locale’s appropriate date representation. + '%X': 'medium', # Locale’s appropriate time representation. + '%y': 'YY', # Year without century as a zero-padded decimal number. + '%Y': 'YYYY', # Year with century as a decimal number. + '%Z': 'zzzz', # Time zone name (no characters if no time zone exists). + '%%': '%', } +date_format_re = re.compile('(%s)' % '|'.join(date_format_mappings)) + def babel_format_date(date, format, locale, formatter=babel.dates.format_date): # type: (datetime, unicode, unicode, Callable) -> unicode @@ -214,7 +225,7 @@ def format_date(format, date=None, language=None): date = datetime.now() result = [] - tokens = re.split('(%.)', format) + tokens = date_format_re.split(format) for token in tokens: if token in date_format_mappings: babel_format = date_format_mappings.get(token, '') diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py index bec4e91e9..14a7aae6f 100644 --- a/tests/test_util_i18n.py +++ b/tests/test_util_i18n.py @@ -176,6 +176,8 @@ def test_format_date(): datet = datetime.datetime(2016, 2, 7, 5, 11, 17, 0) assert i18n.format_date(format, date=datet) == 'February 07, 2016, 05:11:17 05 AM' + format = '%B %-d, %Y, %-H:%-M:%-S %-I %p' + assert i18n.format_date(format, date=datet) == 'February 7, 2016, 5:11:17 5 AM' format = '%x' assert i18n.format_date(format, date=datet) == 'Feb 7, 2016' format = '%X' From 321b3d49cbf67e665e6fdcf6bc7df673e21a1221 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 28 Jan 2018 18:58:58 +0900 Subject: [PATCH 7/8] Fix #3952: apidoc: module header is too escaped --- CHANGES | 1 + sphinx/util/rst.py | 6 ++++-- tests/test_util_rst.py | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 4782fa141..7c257ccc2 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Bugs fixed * #4472: DOCUMENTATION_OPTIONS is not defined * #4491: autodoc: prefer _MockImporter over other importers in sys.meta_path * #4490: autodoc: type annotation is broken with python 3.7.0a4+ +* #3952: apidoc: module header is too escaped Testing -------- diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 5860b0fd5..a406e0044 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -23,13 +23,15 @@ if False: # For type annotation from typing import Generator # NOQA -symbols_re = re.compile(r'([!-/:-@\[-`{-~])') +symbols_re = re.compile(r'([!--/:-@\[-`{-~])') # symbols without dot(0x2e) logger = logging.getLogger(__name__) def escape(text): # type: (unicode) -> unicode - return symbols_re.sub(r'\\\1', text) # type: ignore + text = symbols_re.sub(r'\\\1', text) # type: ignore + text = re.sub(r'^\.', r'\.', text) # escape a dot at top + return text @contextmanager diff --git a/tests/test_util_rst.py b/tests/test_util_rst.py index 406ea710e..07b9174cc 100644 --- a/tests/test_util_rst.py +++ b/tests/test_util_rst.py @@ -14,3 +14,5 @@ from sphinx.util.rst import escape def test_escape(): assert escape(':ref:`id`') == r'\:ref\:\`id\`' assert escape('footnote [#]_') == r'footnote \[\#\]\_' + assert escape('sphinx.application') == r'sphinx.application' + assert escape('.. toctree::') == r'\.. toctree\:\:' From 91315ab3548e51ba694ad4c60fbd19ece50e6575 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 28 Jan 2018 21:05:27 +0900 Subject: [PATCH 8/8] Update CHANGES for PR #4508 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e494f9c0a..74481b4d3 100644 --- a/CHANGES +++ b/CHANGES @@ -26,7 +26,7 @@ Bugs fixed * #4472: DOCUMENTATION_OPTIONS is not defined * #4491: autodoc: prefer _MockImporter over other importers in sys.meta_path * #4490: autodoc: type annotation is broken with python 3.7.0a4+ -* utils package is installed +* utils package is no longer installed * #3952: apidoc: module header is too escaped * #4275: Formats accepted by sphinx.util.i18n.format_date are limited