Skip to content

Commit 163abce

Browse files
authored
Merge branch 'pydata:main' into poc/unified-zarr-chunk-grid
2 parents 362b5f1 + e059cc7 commit 163abce

16 files changed

Lines changed: 318 additions & 22 deletions

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1+
### Description
2+
3+
### Checklist
4+
15
<!-- Feel free to remove check-list items aren't relevant to your change -->
26

37
- [ ] Closes #xxxx
48
- [ ] Tests added
59
- [ ] User visible changes (including notable bug fixes) are documented in `whats-new.rst`
610
- [ ] New functions/methods are listed in `api.rst`
11+
12+
### AI Disclosure
13+
14+
<!--- Please review our AI & contribution guidelines: https://docs.xarray.dev/en/stable/contribute/ai-policy.html. Remove this section if your PR does not contain AI-generated content. --->
15+
16+
- [ ] This PR contains AI-generated content.
17+
- [ ] I have tested any AI-generated content in my PR.
18+
- [ ] I take responsibility for any AI-generated content in my PR.
19+
<!--- If you used AI to generate code, please specify the tool used and the prompt below. --->
20+
Tools: {e.g., Claude, Codex, GitHub Copilot, ChatGPT, etc.}

.github/workflows/ci-additional.yaml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545

4646
steps:
4747
- uses: actions/checkout@v6
48-
- uses: Parcels-code/pixi-lock/create-and-cache@a9aee67fa67426e6b0297fa5bef80600572be153
48+
- uses: Parcels-code/pixi-lock/create-and-cache@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
4949
id: pixi-lock
5050
- uses: actions/upload-artifact@v7
5151
with:
@@ -72,7 +72,7 @@ jobs:
7272
echo "TODAY=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
7373
7474
- name: Restore cached pixi lockfile
75-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
75+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
7676
with:
7777
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
7878
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -110,7 +110,7 @@ jobs:
110110
with:
111111
fetch-depth: 0 # Fetch all history for all branches and tags.
112112
- name: Restore cached pixi lockfile
113-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
113+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
114114
with:
115115
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
116116
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -155,7 +155,7 @@ jobs:
155155
with:
156156
fetch-depth: 0 # Fetch all history for all branches and tags.
157157
- name: Restore cached pixi lockfile
158-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
158+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
159159
with:
160160
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
161161
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -204,7 +204,7 @@ jobs:
204204
fetch-depth: 0
205205

206206
- name: Restore cached pixi lockfile
207-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
207+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
208208
with:
209209
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
210210
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -254,7 +254,7 @@ jobs:
254254
fetch-depth: 0 # Fetch all history for all branches and tags.
255255

256256
- name: Restore cached pixi lockfile
257-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
257+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
258258
with:
259259
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
260260
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -302,7 +302,7 @@ jobs:
302302
fetch-depth: 0 # Fetch all history for all branches and tags.
303303

304304
- name: Restore cached pixi lockfile
305-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
305+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
306306
with:
307307
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
308308

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545

4646
steps:
4747
- uses: actions/checkout@v6
48-
- uses: Parcels-code/pixi-lock/create-and-cache@a9aee67fa67426e6b0297fa5bef80600572be153
48+
- uses: Parcels-code/pixi-lock/create-and-cache@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
4949
id: pixi-lock
5050
- uses: actions/upload-artifact@v7
5151
with:
@@ -96,7 +96,7 @@ jobs:
9696
with:
9797
fetch-depth: 0 # Fetch all history for all branches and tags.
9898
- name: Restore cached pixi lockfile
99-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
99+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
100100
with:
101101
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
102102
- uses: prefix-dev/setup-pixi@v0.9.4

.github/workflows/hypothesis.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
pixi-version: ${{ steps.pixi-lock.outputs.pixi-version }}
5353
steps:
5454
- uses: actions/checkout@v6
55-
- uses: Parcels-code/pixi-lock/create-and-cache@a9aee67fa67426e6b0297fa5bef80600572be153
55+
- uses: Parcels-code/pixi-lock/create-and-cache@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
5656
id: pixi-lock
5757
- uses: actions/upload-artifact@v7
5858
with:
@@ -78,7 +78,7 @@ jobs:
7878
fetch-depth: 0 # Fetch all history for all branches and tags.
7979

8080
- name: Restore cached pixi lockfile
81-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
81+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
8282
with:
8383
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
8484
- uses: prefix-dev/setup-pixi@v0.9.4

.github/workflows/upstream-dev-ci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
pixi-version: ${{ steps.pixi-lock.outputs.pixi-version }}
6060
steps:
6161
- uses: actions/checkout@v6
62-
- uses: Parcels-code/pixi-lock/create-and-cache@a9aee67fa67426e6b0297fa5bef80600572be153
62+
- uses: Parcels-code/pixi-lock/create-and-cache@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
6363
id: pixi-lock
6464
- uses: actions/upload-artifact@v7
6565
with:
@@ -84,7 +84,7 @@ jobs:
8484
with:
8585
fetch-depth: 0 # Fetch all history for all branches and tags.
8686
- name: Restore cached pixi lockfile
87-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
87+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
8888
with:
8989
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
9090
- uses: prefix-dev/setup-pixi@v0.9.4
@@ -137,7 +137,7 @@ jobs:
137137
fetch-depth: 0 # Fetch all history for all branches and tags.
138138

139139
- name: Restore cached pixi lockfile
140-
uses: Parcels-code/pixi-lock/restore@a9aee67fa67426e6b0297fa5bef80600572be153
140+
uses: Parcels-code/pixi-lock/restore@38495788b79a5ff26009aecc15daa9a8310b8832 # v0.1.0
141141
with:
142142
cache-key: ${{ needs.cache-pixi-lock.outputs.cache-key }}
143143
- uses: prefix-dev/setup-pixi@v0.9.4

AI_POLICY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
doc/contribute/ai-policy.md

CORE_TEAM_GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ resources such as:
274274
- [python-xarray](https://stackoverflow.com/questions/tagged/python-xarray) on Stack Overflow.
275275
- The Xarray channel in the [OSSci Zulip](https://ossci.zulipchat.com/)
276276
- [@xarray_dev](https://x.com/xarray_dev) on X.
277-
- [xarray-dev](https://discord.gg/bsSGdwBn) discord community (normally only used for remote synchronous chat during sprints).
277+
- **(superseded by the OSSci Zulip)** [xarray-dev](https://discord.com/invite/wEKPCt4PDu) discord community (normally only used for remote synchronous chat during sprints).
278278

279279
You are not required to monitor any of the social resources.
280280

ci/numpydoc-public-api.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#!/usr/bin/env python
2+
"""A script that can be quickly run that explores the public API of Xarray
3+
and validates docstrings along the way according to the numpydoc conventions."""
4+
5+
import functools
6+
import importlib
7+
import logging
8+
import sys
9+
import types
10+
from pathlib import Path
11+
12+
from numpydoc.validate import validate
13+
14+
logger = logging.getLogger("numpydoc-public-api")
15+
handler = logging.StreamHandler()
16+
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
17+
logger.addHandler(handler)
18+
19+
PROJECT_ROOT = (Path(__file__).parent / "..").resolve()
20+
PUBLIC_MODULES = ["xarray"]
21+
ROOT_PACKAGE = "xarray"
22+
23+
# full list of numpydoc error codes: https://numpydoc.readthedocs.io/en/latest/validation.html
24+
SKIP_ERRORS = [ # TODO: Curate these for Xarray
25+
"GL01", # parcels is fine with the summary line starting directly after `"""`, or on the next line.
26+
"SA01", # Parcels doesn't require the "See also" section
27+
"SA04",
28+
"ES01", # We don't require the extended summary for all docstrings
29+
"EX01", # We don't require the "Examples" section for all docstrings
30+
"SS06", # Not possible to make all summaries one line
31+
#
32+
# To be fixed up
33+
"GL02", # Closing quotes should be placed in the line after the last text in the docstring (do not close the quotes in the same line as the text, or leave a blank line between the last text and the quotes)
34+
"GL03", # Double line break found; please use only one blank line to separate sections or paragraphs, and do not leave blank lines at the end of docstrings
35+
"GL07", # Sections are in the wrong order. Correct order is: {correct_sections}
36+
"GL08", # The object does not have a docstring
37+
"SS01", # No summary found (a short summary in a single line should be present at the beginning of the docstring)
38+
"SS02", # Summary does not start with a capital letter
39+
"SS03", # Summary does not end with a period
40+
"SS04", # Summary contains heading whitespaces
41+
"SS05", # Summary must start with infinitive verb, not third person (e.g. use "Generate" instead of "Generates")
42+
"PR01", # Parameters {missing_params} not documented
43+
"PR02", # Unknown parameters {unknown_params}
44+
"PR03", # Wrong parameters order. Actual: {actual_params}. Documented: {documented_params}
45+
"SA02", # Missing period at end of description for See Also "{reference_name}" reference
46+
"SA03", # Description should be capitalized for See Also
47+
#
48+
# TODO consider whether to continue ignoring the following
49+
"GL09", # Deprecation warning should precede extended summary
50+
"GL10", # reST directives {directives} must be followed by two colons
51+
"PR04", # Parameter "{param_name}" has no type
52+
"PR05", # Parameter "{param_name}" type should not finish with "."
53+
"PR06", # Parameter "{param_name}" type should use "{right_type}" instead of "{wrong_type}"
54+
"PR07", # Parameter "{param_name}" has no description
55+
"PR08", # Parameter "{param_name}" description should start with a capital letter
56+
"PR09", # Parameter "{param_name}" description should finish with "."
57+
"PR10", # Parameter "{param_name}" requires a space before the colon separating the parameter name and type
58+
"RT01", # No Returns section found
59+
"RT02", # The first line of the Returns section should contain only the type, unless multiple values are being returned
60+
"RT03", # Return value has no description
61+
"RT04", # Return value description should start with a capital letter
62+
"RT05", # Return value description should finish with "."
63+
"YD01", # No Yields section found
64+
]
65+
66+
67+
def is_built_in(type_or_instance: type | object):
68+
if isinstance(type_or_instance, type):
69+
return type_or_instance.__module__ == "builtins"
70+
else:
71+
return type_or_instance.__class__.__module__ == "builtins"
72+
73+
74+
def walk_module(module_str: str, public_api: list[str] | None = None) -> list[str]:
75+
if public_api is None:
76+
public_api = []
77+
78+
module = importlib.import_module(module_str)
79+
try:
80+
all_ = module.__all__
81+
except AttributeError:
82+
print(f"No __all__ variable found in public module {module_str!r}")
83+
return public_api
84+
85+
if module_str not in public_api:
86+
public_api.append(module_str)
87+
for item_str in all_:
88+
item = getattr(module, item_str)
89+
if isinstance(item, types.ModuleType):
90+
walk_module(f"{module_str}.{item_str}", public_api)
91+
if isinstance(item, (types.FunctionType,)):
92+
public_api.append(f"{module_str}.{item_str}")
93+
elif is_built_in(item):
94+
print(f"Found builtin at '{module_str}.{item_str}' of type {type(item)}")
95+
continue
96+
elif isinstance(item, type):
97+
public_api.append(f"{module_str}.{item_str}")
98+
walk_class(module_str, item, public_api)
99+
else:
100+
logger.info(
101+
f"Encountered unexpected public object at '{module_str}.{item_str}' of {item!r} in public API. Don't know how to handle with numpydoc - ignoring."
102+
)
103+
104+
return public_api
105+
106+
107+
def get_public_class_attrs(class_: type) -> set[str]:
108+
return {a for a in dir(class_) if not a.startswith("_")}
109+
110+
111+
def walk_class(module_str: str, class_: type, public_api: list[str]) -> list[str]:
112+
class_str = class_.__name__
113+
114+
# attributes that were introduced by this class specifically - not from inheritance
115+
attrs = get_public_class_attrs(class_) - functools.reduce(
116+
set.union, (get_public_class_attrs(base) for base in class_.__bases__)
117+
)
118+
119+
public_api.extend([f"{module_str}.{class_str}.{attr_str}" for attr_str in attrs])
120+
return public_api
121+
122+
123+
def main():
124+
import argparse
125+
126+
parser = argparse.ArgumentParser(
127+
description="Validate numpydoc docstrings in the public API"
128+
)
129+
parser.add_argument(
130+
"-v",
131+
"--verbose",
132+
action="count",
133+
default=0,
134+
help="Increase verbosity (can be repeated)",
135+
)
136+
args = parser.parse_args()
137+
138+
# Set logging level based on verbosity: 0=WARNING, 1=INFO, 2+=DEBUG
139+
if args.verbose == 0:
140+
log_level = logging.WARNING
141+
elif args.verbose == 1:
142+
log_level = logging.INFO
143+
else:
144+
log_level = logging.DEBUG
145+
146+
logger.setLevel(log_level)
147+
148+
public_api = []
149+
for module in PUBLIC_MODULES:
150+
public_api += walk_module(module)
151+
152+
errors = 0
153+
for item in public_api:
154+
logger.info(f"Processing validating {item}")
155+
try:
156+
res = validate(item)
157+
except (AttributeError, StopIteration, ValueError) as e:
158+
if (
159+
isinstance(e, ValueError) and "Error parsing See Also entry" in str(e)
160+
): # TODO: Fix later https://github.com/pydata/xarray/issues/8596#issuecomment-3832443795
161+
logger.info(f"Skipping See Also parsing error for {item!r}.")
162+
continue
163+
logger.warning(f"Could not process {item!r}. Encountered error. {e!r}")
164+
continue
165+
if res["type"] in ("module", "float", "int", "dict"):
166+
continue
167+
for err in res["errors"]:
168+
if err[0] not in SKIP_ERRORS:
169+
print(f"{item}: {err}")
170+
errors += 1
171+
sys.exit(errors)
172+
173+
174+
if __name__ == "__main__":
175+
main()

0 commit comments

Comments
 (0)