Skip to content

Commit f2571e1

Browse files
committed
add stubs
1 parent 3d4c5bc commit f2571e1

65 files changed

Lines changed: 30343 additions & 2 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ultraplot/*.pyi linguist-generated=true
2+
ultraplot/**/*.pyi linguist-generated=true

.github/workflows/main.yml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
- 'environment.yml'
2323
- '.github/workflows/**'
2424
- 'tools/ci/**'
25+
- 'tools/generate_stubs.py'
2526
2627
select-tests:
2728
runs-on: ubuntu-latest
@@ -99,6 +100,7 @@ jobs:
99100
--always-full 'pyproject.toml' \
100101
--always-full 'environment.yml' \
101102
--always-full 'ultraplot/__init__.py' \
103+
--always-full 'tools/generate_stubs.py' \
102104
--ignore 'docs/**' \
103105
--ignore 'README.rst'
104106
echo "Selection output:"
@@ -138,6 +140,42 @@ jobs:
138140
echo "Detected test matrix: $(echo "$OUTPUT" | jq -c '.test_matrix')"
139141
python tools/ci/version_support.py --format github-output >> $GITHUB_OUTPUT
140142
143+
stubs:
144+
name: Static API stubs
145+
runs-on: ubuntu-latest
146+
needs:
147+
- run-if-changes
148+
if: always() && needs.run-if-changes.outputs.run == 'true'
149+
steps:
150+
- uses: actions/checkout@v7
151+
152+
- uses: actions/setup-python@v7
153+
with:
154+
python-version: "3.13"
155+
cache: pip
156+
157+
- name: Install UltraPlot and typing tools
158+
run: pip install -e ".[typing]"
159+
160+
- name: Verify generated stubs
161+
run: python tools/generate_stubs.py --check
162+
163+
- name: Check Pylance-compatible consumption
164+
run: basedpyright tools/ci/stub_consumer.py --level error
165+
166+
- name: Check Pyrefly consumption and generated syntax
167+
run: |
168+
pyrefly check tools/ci/stub_consumer.py \
169+
--search-path . \
170+
--python-interpreter-path "$(command -v python)" \
171+
--progress-bar no
172+
pyrefly check 'ultraplot/**/*.pyi' \
173+
--search-path . \
174+
--python-interpreter-path "$(command -v python)" \
175+
--ignore-missing-imports icecream \
176+
--ignore-missing-imports matplotlib.fontconfig_pattern \
177+
--progress-bar no
178+
141179
coverage:
142180
name: Coverage
143181
runs-on: ubuntu-latest
@@ -213,14 +251,15 @@ jobs:
213251
needs:
214252
- build
215253
- run-if-changes
254+
- stubs
216255
if: always()
217256
runs-on: ubuntu-latest
218257
steps:
219258
- run: |
220259
if [[ '${{ needs.run-if-changes.outputs.run }}' == 'false' ]]; then
221260
echo "No changes detected, tests skipped."
222261
else
223-
if [[ '${{ needs.build.result }}' == 'success' ]]; then
262+
if [[ '${{ needs.build.result }}' == 'success' && '${{ needs.stubs.result }}' == 'success' ]]; then
224263
echo "All tests passed successfully!"
225264
else
226265
echo "Tests failed!"

docs/contributing.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,37 @@ When adding a new submodule, make sure it is compatible with the lazy loader:
130130
By following these steps, your module will integrate cleanly with the lazy loading
131131
system without requiring manual registry updates.
132132

133+
Editor type information and docstrings
134+
--------------------------------------
135+
136+
UltraPlot ships generated ``.pyi`` files so static analysis tools such as Pylance
137+
and Pyrefly can see the public API and fully expanded docstrings without importing
138+
the package. The runtime modules remain the source of truth and continue to use the
139+
lazy loader.
140+
141+
After changing a Python signature, annotation, public import, or docstring snippet,
142+
install the pinned typing tools, regenerate the stubs from the repository root, and
143+
commit the updated ``.pyi`` files:
144+
145+
.. code-block:: bash
146+
147+
pip install -e ".[typing]"
148+
python tools/generate_stubs.py
149+
150+
Installation does not generate or modify these files. Release artifacts include the
151+
stubs that were generated and checked into the repository. The generator runs
152+
Pyrefly against an isolated source-only package, merges its inferred annotations
153+
into a complete syntax-derived representation of the package, and statically
154+
expands registered docstring snippets. This preserves declarations that Pyrefly
155+
cannot discover through decorators or lazy loading.
156+
157+
To rerun inference and verify that every committed stub is up to date without
158+
changing files, run:
159+
160+
.. code-block:: bash
161+
162+
python tools/generate_stubs.py --check
163+
133164
134165
.. _contrib_pr:
135166

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ packages = {find = {exclude=["docs*", "baseline*", "logo*"]}}
5050
include-package-data = true
5151

5252
[tool.setuptools.package-data]
53-
ultraplot = ["py.typed"]
53+
ultraplot = ["py.typed", "*.pyi", "**/*.pyi"]
5454

5555
[tool.setuptools_scm]
5656
write_to = "ultraplot/_version.py"
@@ -74,6 +74,10 @@ filterwarnings = [
7474
]
7575
mpl-default-style = { axes.prop_cycle = "cycler('color', ['#4c72b0ff', '#55a868ff', '#c44e52ff', '#8172b2ff', '#ccb974ff', '#64b5cdff'])" }
7676
[project.optional-dependencies]
77+
typing = [
78+
"basedpyright==1.31.4",
79+
"pyrefly==1.2.0",
80+
]
7781
docs = [
7882
"jupyter",
7983
"jupytext",

tools/ci/stub_consumer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Representative lazy public imports consumed by static type checkers."""
2+
3+
import ultraplot as uplt
4+
5+
reveal_type(uplt.subplots)
6+
reveal_type(uplt.Axes.format)

0 commit comments

Comments
 (0)