Skip to content
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
12 changes: 12 additions & 0 deletions great_docs/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"jupyter": "python3", # Default kernel for Quarto computations
# API discovery settings
"exclude": [],
"auto_include": [], # Names to force-include even if they match AUTO_EXCLUDE
"no_auto_exclude": False, # Bypass the built-in AUTO_EXCLUDE list entirely
# GitHub integration
"repo": None, # GitHub repository URL override (e.g., "https://github.com/owner/repo")
"github_style": "widget", # "widget" (shows stars) or "icon"
Expand Down Expand Up @@ -454,6 +456,16 @@ def exclude(self) -> list[str]:
"""Get the list of items to exclude."""
return self.get("exclude", [])

@property
def auto_include(self) -> list[str]:
"""Get names to force-include even if they match AUTO_EXCLUDE."""
return self.get("auto_include", [])

@property
def no_auto_exclude(self) -> bool:
"""Check if the built-in AUTO_EXCLUDE list should be bypassed."""
return self.get("no_auto_exclude", False)

@property
def repo(self) -> str | None:
"""Get the GitHub repository URL override."""
Expand Down
26 changes: 23 additions & 3 deletions great_docs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,8 @@ def _get_package_metadata(self) -> dict:
# Map config properties to metadata dict for backward compatibility
metadata["rich_authors"] = self._config.authors
metadata["exclude"] = self._config.exclude
metadata["auto_include"] = self._config.auto_include
metadata["no_auto_exclude"] = self._config.no_auto_exclude

# Source link configuration
metadata["source_link_enabled"] = self._config.source_enabled
Expand Down Expand Up @@ -5487,17 +5489,35 @@ def _discover_package_exports(self, package_name: str) -> list | None:
# Get config from great-docs.yml
metadata = self._get_package_metadata()
config_exclude = set(metadata.get("exclude", []))
auto_include = set(metadata.get("auto_include", []))
no_auto_exclude = metadata.get("no_auto_exclude", False)

# Determine effective auto-exclude set
if no_auto_exclude:
effective_auto_exclude: set[str] = set()
print("Auto-exclude list bypassed (no_auto_exclude: true)")
else:
effective_auto_exclude = self.AUTO_EXCLUDE - auto_include
if auto_include:
forced = auto_include & self.AUTO_EXCLUDE
if forced:
print(
f"Force-including {len(forced)} auto-excluded name(s): "
f"{', '.join(sorted(forced))}"
)

# Apply auto-exclusions
auto_excluded_found = [name for name in public_members if name in self.AUTO_EXCLUDE]
auto_excluded_found = [
name for name in public_members if name in effective_auto_exclude
]
if auto_excluded_found:
print(
f"Auto-excluding {len(auto_excluded_found)} item(s): "
f"{', '.join(sorted(auto_excluded_found))}"
)

# Combine all exclusions (auto + user-specified)
all_exclude = self.AUTO_EXCLUDE | config_exclude
all_exclude = effective_auto_exclude | config_exclude

# Filter out excluded items
filtered = [name for name in public_members if name not in all_exclude]
Expand All @@ -5507,7 +5527,7 @@ def _discover_package_exports(self, package_name: str) -> list | None:
user_excluded_found = [
name
for name in public_members
if name in config_exclude and name not in self.AUTO_EXCLUDE
if name in config_exclude and name not in effective_auto_exclude
]
if user_excluded_found:
print(
Expand Down
2 changes: 2 additions & 0 deletions skills/great-docs/references/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ All keys are optional. Defaults are auto-detected from `pyproject.toml`.
| `parser` | `str` | `"numpy"` | Docstring style: `"numpy"`, `"google"`, `"sphinx"` |
| `dynamic` | `bool` | `true` | `true`: runtime introspection; `false`: static (griffe) |
| `exclude` | `list[str]` | `[]` | Items to hide from API docs |
| `auto_include` | `list[str]` | `[]` | Force-include names that match the auto-exclude list |
| `no_auto_exclude` | `bool` | `false` | Bypass the built-in auto-exclude list entirely |

## GitHub and source links

Expand Down
15 changes: 15 additions & 0 deletions test-packages/synthetic/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,9 @@
"gdtest_sec_dir_titles", # 174
# 175: Namespace package with src/ layout and dotted module name
"gdtest_namespace_src", # 175
# 176–177: Auto-include / no-auto-exclude discovery overrides
"gdtest_auto_include", # 176
"gdtest_no_auto_exclude", # 177
]


Expand Down Expand Up @@ -1990,6 +1993,18 @@
"namespace packages in src/ layout via griffe search_paths and "
"dotted path resolution in _find_package_init."
),
"gdtest_auto_include": (
"Module exports names that match AUTO_EXCLUDE (config, logging, main) "
"alongside real API (Widget, process). The auto_include config option "
"forces config and logging back into documentation while main remains "
"excluded. Tests selective override of AUTO_EXCLUDE."
),
"gdtest_no_auto_exclude": (
"Module exports names that match AUTO_EXCLUDE (main, config, logger) "
"alongside real API (Adapter, run). The no_auto_exclude config option "
"is set to true, so ALL names pass through — none are automatically "
"excluded. Tests complete bypass of the AUTO_EXCLUDE filter."
),
}


Expand Down
157 changes: 157 additions & 0 deletions test-packages/synthetic/specs/gdtest_auto_include.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""
gdtest_auto_include — Force-include names that match AUTO_EXCLUDE.

Dimensions: A1, B7, C4, D1, E6, F6, G1, H7
Focus: __all__ includes names like "config", "logging", and "main" that are
in the AUTO_EXCLUDE set. The great-docs.yml ``auto_include`` option
forces "config" and "logging" back into the documentation while "main"
remains excluded. Validates that auto_include selectively overrides
AUTO_EXCLUDE without disabling it entirely.
"""

SPEC = {
"name": "gdtest_auto_include",
"description": "Force-include AUTO_EXCLUDE names via auto_include config",
"dimensions": ["A1", "B7", "C4", "D1", "E6", "F6", "G1", "H7"],
"pyproject_toml": {
"project": {
"name": "gdtest-auto-include",
"version": "0.1.0",
"description": "A synthetic test package testing auto_include override of AUTO_EXCLUDE",
},
"build-system": {
"requires": ["setuptools"],
"build-backend": "setuptools.build_meta",
},
},
"config": {
"auto_include": ["config", "logging"],
},
"files": {
"gdtest_auto_include/__init__.py": '''\
"""A test package with auto_include overriding AUTO_EXCLUDE."""

__version__ = "0.1.0"
__all__ = ["Widget", "process", "config", "logging", "main"]


class Widget:
"""
A public widget class.

Parameters
----------
label
Widget label.
"""

def __init__(self, label: str):
self.label = label

def render(self) -> str:
"""
Render the widget.

Returns
-------
str
Rendered HTML.
"""
return f"<widget>{self.label}</widget>"


def process(data: str) -> str:
"""
Process input data.

Parameters
----------
data
Raw input data.

Returns
-------
str
Processed data.
"""
return data.strip()


class config:
"""
Configuration manager for the package.

This is a real public API class that happens to be named ``config``
— a name normally in AUTO_EXCLUDE. The ``auto_include`` option
forces it back into documentation.

Parameters
----------
path
Configuration file path.
"""

def __init__(self, path: str = "config.ini"):
self.path = path

def load(self) -> dict:
"""
Load configuration from file.

Returns
-------
dict
Loaded configuration values.
"""
return {}


class logging:
"""
Logging facade for the package.

This is a real public API class that happens to be named ``logging``
— a name normally in AUTO_EXCLUDE. The ``auto_include`` option
forces it back into documentation.

Parameters
----------
level
Default log level.
"""

def __init__(self, level: str = "INFO"):
self.level = level

def info(self, msg: str) -> None:
"""
Log an informational message.

Parameters
----------
msg
The message to log.
"""
pass


def main():
"""CLI entry point — should still be auto-excluded."""
pass
''',
"README.md": """\
# gdtest-auto-include

A synthetic test package testing auto_include override of AUTO_EXCLUDE.
""",
},
"expected": {
"detected_name": "gdtest-auto-include",
"detected_module": "gdtest_auto_include",
"detected_parser": "numpy",
"export_names": ["Widget", "process", "config", "logging"],
"auto_excluded": ["main"],
"force_included": ["config", "logging"],
"has_user_guide": False,
},
}
Loading
Loading