From a189f8fdafe8f4021042606644a9ceba233e8d6e Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Tue, 17 Aug 2021 09:18:30 +0200 Subject: [PATCH 1/8] fix: proper errors for autosummary --- sphinx/ext/autosummary/__init__.py | 14 +++++++++----- sphinx/ext/autosummary/generate.py | 6 ++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 379fd48ad0..5ab7dd84ac 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -614,7 +614,7 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> List[str]: return prefixes -def import_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, Any, Any, str]: +def import_by_name(name: str, prefixes: List[str] = [None], last_errors = None) -> Tuple[str, Any, Any, str]: """Import a Python object that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. """ @@ -625,14 +625,14 @@ def import_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, Any, A prefixed_name = '.'.join([prefix, name]) else: prefixed_name = name - obj, parent, modname = _import_by_name(prefixed_name) + obj, parent, modname = _import_by_name(prefixed_name, last_errors) return prefixed_name, obj, parent, modname except ImportError: tried.append(prefixed_name) raise ImportError('no module named %s' % ' or '.join(tried)) -def _import_by_name(name: str) -> Tuple[Any, Any, str]: +def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: """Import a Python object given its full name.""" try: name_parts = name.split('.') @@ -643,7 +643,9 @@ def _import_by_name(name: str) -> Tuple[Any, Any, str]: try: mod = import_module(modname) return getattr(mod, name_parts[-1]), mod, modname - except (ImportError, IndexError, AttributeError): + except (ImportError, IndexError, AttributeError) as e: + if last_errors is not None: + last_errors.append(str(str(e.args[0]))) pass # ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ... @@ -654,7 +656,9 @@ def _import_by_name(name: str) -> Tuple[Any, Any, str]: modname = '.'.join(name_parts[:j]) try: import_module(modname) - except ImportError: + except ImportError as e: + if last_errors is not None: + last_errors.append(str(e.args[0])) continue if modname in sys.modules: diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 4f3493659d..db5b3ca115 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -413,8 +413,10 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, path = output_dir or os.path.abspath(entry.path) ensuredir(path) + last_errors = list() + try: - name, obj, parent, modname = import_by_name(entry.name) + name, obj, parent, modname = import_by_name(entry.name, last_errors=last_errors) qualname = name.replace(modname + ".", "") except ImportError as e: try: @@ -422,7 +424,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, name, obj, parent, modname = import_ivar_by_name(entry.name) qualname = name.replace(modname + ".", "") except ImportError: - logger.warning(__('[autosummary] failed to import %r: %s') % (entry.name, e)) + logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % (entry.name, e, last_errors)) continue context: Dict[str, Any] = {} From ae86f28ed2569ce4789076ba5282d257b1360192 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Tue, 17 Aug 2021 09:36:02 +0200 Subject: [PATCH 2/8] fix: remove list duplicates and add additional errors --- sphinx/ext/autosummary/__init__.py | 6 ++++-- sphinx/ext/autosummary/generate.py | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 5ab7dd84ac..6df93f04b4 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -677,7 +677,7 @@ def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: raise ImportError(*e.args) from e -def import_ivar_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, Any, Any, str]: +def import_ivar_by_name(name: str, prefixes: List[str] = [None], last_errors = None) -> Tuple[str, Any, Any, str]: """Import an instance variable that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. """ @@ -690,7 +690,9 @@ def import_ivar_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, A # check for presence in `annotations` to include dataclass attributes if (qualname, attr) in analyzer.attr_docs or (qualname, attr) in analyzer.annotations: return real_name + "." + attr, INSTANCEATTR, obj, modname - except (ImportError, ValueError, PycodeError): + except (ImportError, ValueError, PycodeError) as e: + if last_errors is not None: + last_errors.append(str(e.args[0])) pass raise ImportError diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index db5b3ca115..b5ea912c25 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -421,9 +421,11 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, except ImportError as e: try: # try to importl as an instance attribute - name, obj, parent, modname = import_ivar_by_name(entry.name) + name, obj, parent, modname = import_ivar_by_name(entry.name, last_errors=last_errors) qualname = name.replace(modname + ".", "") except ImportError: + # remove duplicates + last_errors = list(set(last_errors)) logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % (entry.name, e, last_errors)) continue From c62ad04ea922c9ac53d05cbe9043b7752c11b569 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Wed, 18 Aug 2021 14:28:18 +0200 Subject: [PATCH 3/8] Remove double-stringify Co-authored-by: Philipp A. --- sphinx/ext/autosummary/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 6df93f04b4..621b755501 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -645,7 +645,7 @@ def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: return getattr(mod, name_parts[-1]), mod, modname except (ImportError, IndexError, AttributeError) as e: if last_errors is not None: - last_errors.append(str(str(e.args[0]))) + last_errors.append(str(e)) pass # ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ... From 7b3570b7bb0c607f1f12d89ec4512903b84b9770 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Wed, 18 Aug 2021 14:28:47 +0200 Subject: [PATCH 4/8] Remove double-stringify Co-authored-by: Philipp A. --- sphinx/ext/autosummary/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 621b755501..4603bae179 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -658,7 +658,7 @@ def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: import_module(modname) except ImportError as e: if last_errors is not None: - last_errors.append(str(e.args[0])) + last_errors.append(str(e)) continue if modname in sys.modules: @@ -692,7 +692,7 @@ def import_ivar_by_name(name: str, prefixes: List[str] = [None], last_errors = N return real_name + "." + attr, INSTANCEATTR, obj, modname except (ImportError, ValueError, PycodeError) as e: if last_errors is not None: - last_errors.append(str(e.args[0])) + last_errors.append(str(e)) pass raise ImportError From c5cd8ae2daf83c4f339cdc92d17398744424b5c2 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Tue, 7 Sep 2021 13:58:25 +0200 Subject: [PATCH 5/8] fix: minor code cleanup --- sphinx/ext/autosummary/__init__.py | 10 +++++----- sphinx/ext/autosummary/generate.py | 11 ++++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 4603bae179..8810a5088f 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -617,6 +617,8 @@ def get_import_prefixes_from_env(env: BuildEnvironment) -> List[str]: def import_by_name(name: str, prefixes: List[str] = [None], last_errors = None) -> Tuple[str, Any, Any, str]: """Import a Python object that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. + If importing fails, *last_errors* will contain the exceptions raised during + import and may help to identify the problem. """ tried = [] for prefix in prefixes: @@ -645,8 +647,7 @@ def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: return getattr(mod, name_parts[-1]), mod, modname except (ImportError, IndexError, AttributeError) as e: if last_errors is not None: - last_errors.append(str(e)) - pass + last_errors.append(e) # ... then as MODNAME, MODNAME.OBJ1, MODNAME.OBJ1.OBJ2, ... last_j = 0 @@ -658,7 +659,7 @@ def _import_by_name(name: str, last_errors = None) -> Tuple[Any, Any, str]: import_module(modname) except ImportError as e: if last_errors is not None: - last_errors.append(str(e)) + last_errors.append(e) continue if modname in sys.modules: @@ -692,8 +693,7 @@ def import_ivar_by_name(name: str, prefixes: List[str] = [None], last_errors = N return real_name + "." + attr, INSTANCEATTR, obj, modname except (ImportError, ValueError, PycodeError) as e: if last_errors is not None: - last_errors.append(str(e)) - pass + last_errors.append(e) raise ImportError diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index b5ea912c25..439a4bdc0b 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -413,20 +413,21 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, path = output_dir or os.path.abspath(entry.path) ensuredir(path) - last_errors = list() + last_errors = [] try: name, obj, parent, modname = import_by_name(entry.name, last_errors=last_errors) qualname = name.replace(modname + ".", "") except ImportError as e: try: - # try to importl as an instance attribute + # try to import as an instance attribute name, obj, parent, modname = import_ivar_by_name(entry.name, last_errors=last_errors) qualname = name.replace(modname + ".", "") except ImportError: - # remove duplicates - last_errors = list(set(last_errors)) - logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % (entry.name, e, last_errors)) + # Convert exceptions to strings and remove duplicates (covert to set, then to list) + last_errors_str = list({str(e) in last_errors}) + logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % + (entry.name, e, last_errors_str)) continue context: Dict[str, Any] = {} From 08fea4b93ec5991c5cc1a2a232c4f155b7ce7628 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Tue, 7 Sep 2021 14:08:14 +0200 Subject: [PATCH 6/8] fix: correct conversion to string exception --- sphinx/ext/autosummary/generate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 439a4bdc0b..71cd9c8e14 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -425,7 +425,8 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, qualname = name.replace(modname + ".", "") except ImportError: # Convert exceptions to strings and remove duplicates (covert to set, then to list) - last_errors_str = list({str(e) in last_errors}) + last_errors_str = list([str(e) for e in last_errors]) + last_errors_str = list(set(last_errors_str)) logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % (entry.name, e, last_errors_str)) continue From 23e3d7ce81a65e1bcfe87ff2b06cb20a640ca5c6 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Wed, 15 Sep 2021 17:49:43 +0200 Subject: [PATCH 7/8] Fix typo Co-authored-by: Takeshi KOMIYA --- sphinx/ext/autosummary/generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 71cd9c8e14..17ed98d2f6 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -424,7 +424,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, name, obj, parent, modname = import_ivar_by_name(entry.name, last_errors=last_errors) qualname = name.replace(modname + ".", "") except ImportError: - # Convert exceptions to strings and remove duplicates (covert to set, then to list) + # Convert exceptions to strings and remove duplicates (convert to set, then to list) last_errors_str = list([str(e) for e in last_errors]) last_errors_str = list(set(last_errors_str)) logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % From b6cc5282932927ed5d3c0fe6329fbb5b173c3388 Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Thu, 16 Sep 2021 09:07:51 +0200 Subject: [PATCH 8/8] fix: simplify getting error strings Co-authored-by: Takeshi KOMIYA --- sphinx/ext/autosummary/generate.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 17ed98d2f6..dcbebd341d 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -425,8 +425,7 @@ def generate_autosummary_docs(sources: List[str], output_dir: str = None, qualname = name.replace(modname + ".", "") except ImportError: # Convert exceptions to strings and remove duplicates (convert to set, then to list) - last_errors_str = list([str(e) for e in last_errors]) - last_errors_str = list(set(last_errors_str)) + last_errors_str = list(set(str(e) for e in last_errors)) logger.warning(__('[autosummary] failed to import %r: %s. Possible hints: %s') % (entry.name, e, last_errors_str)) continue