|
| 1 | +# Exclude api-examples from installed package |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +This PR removes the `shiny/api-examples/` directory from the distributed package (both wheels and source distributions) to reduce package size and file count. The examples are only needed during documentation builds, which always run from the source repository. |
| 6 | + |
| 7 | +## Motivation |
| 8 | + |
| 9 | +The `api-examples/` directory contains ~310 example files used by the `@add_example()` decorator to inject code examples into docstrings during documentation generation. These files: |
| 10 | + |
| 11 | +- Are only accessed when `SHINY_ADD_EXAMPLES=true` (set during `make docs-quartodoc`) |
| 12 | +- Are never needed by end users installing the package |
| 13 | +- Added ~400KB to the package size |
| 14 | +- Contributed ~36% of the total file count in the wheel |
| 15 | + |
| 16 | +Similar to development dependencies, these are build-time artifacts that don't belong in the runtime package. |
| 17 | + |
| 18 | +## Changes |
| 19 | + |
| 20 | +### 1. `MANIFEST.in` |
| 21 | +```diff |
| 22 | +- recursive-include shiny/api-examples * |
| 23 | ++ prune shiny/api-examples |
| 24 | ++ prune shiny/experimental/api-examples |
| 25 | +``` |
| 26 | + |
| 27 | +**Why**: Controls what goes into source distributions (`.tar.gz`). The `prune` directive explicitly excludes the directories. |
| 28 | + |
| 29 | +### 2. `pyproject.toml` |
| 30 | +```diff |
| 31 | + [tool.setuptools] |
| 32 | +-packages = { find = { include = ["shiny", "shiny.*"] } } |
| 33 | ++[tool.setuptools.packages.find] |
| 34 | ++where = ["."] |
| 35 | ++include = ["shiny*"] |
| 36 | ++namespaces = false |
| 37 | ++ |
| 38 | ++[tool.setuptools.exclude-package-data] |
| 39 | ++"*" = ["api-examples/**"] |
| 40 | +``` |
| 41 | + |
| 42 | +**Why**: Two changes needed: |
| 43 | + |
| 44 | +1. **`packages.find`**: Expanded from inline syntax to dedicated section for better control and clarity |
| 45 | + |
| 46 | +2. **`exclude-package-data`**: Explicitly exclude api-examples from package data. While MANIFEST.in's `prune` directive handles source distributions, we need this for binary wheels. |
| 47 | + |
| 48 | +**Why both MANIFEST.in and pyproject.toml?** |
| 49 | + |
| 50 | +MANIFEST.in and pyproject.toml control *different build artifacts*: |
| 51 | +- **MANIFEST.in** → Source distributions (`.tar.gz`) - used by some users, required by PyPI |
| 52 | +- **pyproject.toml** → Binary wheels (`.whl`) - used by most users via `pip install` |
| 53 | + |
| 54 | +**Note on package-data**: Earlier iterations included a `[tool.setuptools.package-data]` section to explicitly list `www/**` and `templates/**`, but this was redundant. MANIFEST.in's `recursive-include` directives are respected by both sdist and wheel builds, so only the exclude needed to be in pyproject.toml. |
| 55 | + |
| 56 | +### 3. `shiny/_docstring.py` |
| 57 | +```python |
| 58 | +def find_api_examples_dir(start_dir: str) -> Optional[str]: |
| 59 | + # ... search for api-examples ... |
| 60 | + |
| 61 | + # Not found - provide helpful error for documentation builders |
| 62 | + if os.getenv("SHINY_ADD_EXAMPLES") == "true": |
| 63 | + raise FileNotFoundError( |
| 64 | + "Could not find 'api-examples/' directory. " |
| 65 | + "Documentation must be built from the source repository, " |
| 66 | + "not from an installed package. " |
| 67 | + f"Searched from: {search_start}" |
| 68 | + ) |
| 69 | + return None |
| 70 | +``` |
| 71 | + |
| 72 | +**Why**: Provides a clear error message if someone attempts to build docs from an installed package instead of from source. |
0 commit comments