Skip to content

Commit 1577b39

Browse files
enssowtjhunter
andauthored
pyproject.toml checks (ecmwf#1042)
* adding chceks for toml files into actions * lint fixes * lint checks * link fixes * changes * disabling ruff check * change to path info instead * adding E501 and E721 to be ignored for now --------- Co-authored-by: Tim Hunter <tim.hunter@ecmwf.int>
1 parent 17ba0a5 commit 1577b39

14 files changed

Lines changed: 199 additions & 231 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jobs:
2828
# Run temporarily on a sub directory before the main restyling.
2929
run: ./scripts/actions.sh lint-check
3030

31+
- name: TOML checks
32+
run: ./scripts/actions.sh toml-check
33+
3134
- name: Type checker (pyrefly, experimental)
3235
# Do not attempt to install the default dependencies, this is much faster.
3336
# Run temporarily on a sub directory before the main restyling.

packages/common/pyproject.toml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "weathergen-common"
33
version = "0.1.0"
44
description = "The WeatherGenerator Machine Learning Earth System Model"
55
readme = "../../README.md"
6-
requires-python = ">=3.11,<3.13"
6+
requires-python = ">=3.12,<3.13"
77
dependencies = [
88
"xarray>=2025.6.1",
99
"dask>=2024.9.1",
@@ -42,12 +42,6 @@ not-callable = false
4242

4343

4444

45-
[tool.black]
46-
47-
# Wide rows
48-
line-length = 100
49-
50-
5145
# The linting configuration
5246
[tool.ruff]
5347

@@ -70,7 +64,11 @@ select = [
7064
# isort
7165
"I",
7266
# Banned imports
73-
"TID"
67+
"TID",
68+
# Naming conventions
69+
"N",
70+
# print
71+
"T201"
7472
]
7573

7674
# These rules are sensible and should be enabled at a later stage.
@@ -82,11 +80,20 @@ ignore = [
8280
"SIM118",
8381
"SIM102",
8482
"SIM401",
85-
"UP040", # TODO: enable later
8683
# To ignore, not relevant for us
87-
"SIM108" # in case additional norm layer supports are added in future
84+
"SIM108", # in case additional norm layer supports are added in future
85+
"N817", # we use heavy acronyms, e.g., allowing 'import LongModuleName as LMN' (LMN is accepted)
86+
"E731", # overly restrictive and less readable code
87+
"N812", # prevents us following the convention for importing torch.nn.functional as F
8888
]
8989

90+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
91+
"numpy.ndarray".msg = "Do not use 'ndarray' to describe a numpy array type, it is a function. Use numpy.typing.NDArray or numpy.typing.NDArray[np.float32] for example"
92+
93+
[tool.ruff.format]
94+
# Use Unix `\n` line endings for all files
95+
line-ending = "lf"
96+
9097

9198

9299
[build-system]

packages/common/src/weathergen/common/io.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,13 @@
2323

2424
# experimental value, should be inferred more intelligently
2525
CHUNK_N_SAMPLES = 16392
26-
DType: typing.TypeAlias = np.float32
26+
type DType = np.float32
2727
type NPDT64 = datetime64
2828

2929

3030
_logger = logging.getLogger(__name__)
3131

3232

33-
np.ndarray(3)
34-
35-
3633
@dataclasses.dataclass
3734
class IOReaderData:
3835
"""

packages/evaluate/pyproject.toml

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "weathergen-evaluate"
33
version = "0.1.0"
44
description = "The WeatherGenerator Machine Learning Earth System Model"
55
readme = "../../README.md"
6-
requires-python = ">=3.11,<3.13"
6+
requires-python = ">=3.12,<3.13"
77
dependencies = [
88
"cartopy>=0.24.1",
99
"xskillscore",
@@ -25,6 +25,12 @@ dev = [
2525
evaluation = "weathergen.evaluate.run_evaluation:evaluate"
2626
export = "weathergen.evaluate.export_inference:export"
2727

28+
# The linting configuration
29+
[tool.ruff]
30+
31+
# Wide rows
32+
line-length = 100
33+
2834
[tool.ruff.lint]
2935
# All disabled until the code is formatted.
3036
select = [
@@ -40,24 +46,40 @@ select = [
4046
"SIM",
4147
# isort
4248
"I",
49+
# Banned imports
50+
"TID",
51+
# Naming conventions
52+
"N",
53+
# print
54+
"T201"
4355
]
4456

4557
# These rules are sensible and should be enabled at a later stage.
4658
ignore = [
47-
"E501",
48-
"E721",
49-
"E722",
5059
# "B006",
5160
"B011",
5261
"UP008",
5362
"SIM117",
5463
"SIM118",
5564
"SIM102",
5665
"SIM401",
66+
"E501", # to be removed
67+
"E721",
5768
# To ignore, not relevant for us
58-
"E741",
69+
"SIM108", # in case additional norm layer supports are added in future
70+
"N817", # we use heavy acronyms, e.g., allowing 'import LongModuleName as LMN' (LMN is accepted)
71+
"E731", # overly restrictive and less readable code
72+
"N812", # prevents us following the convention for importing torch.nn.functional as F
5973
]
6074

75+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
76+
"numpy.ndarray".msg = "Do not use 'ndarray' to describe a numpy array type, it is a function. Use numpy.typing.NDArray or numpy.typing.NDArray[np.float32] for example"
77+
78+
[tool.ruff.format]
79+
# Use Unix `\n` line endings for all files
80+
line-ending = "lf"
81+
82+
6183
[tool.pyrefly]
6284
project-includes = ["src/"]
6385
project-excludes = [

packages/evaluate/src/weathergen/evaluate/derived_channels.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,7 @@ def get_channel(self, data_tars, data_preds, tag, level, calc_func) -> None:
105105

106106
data_updated.append(conc)
107107

108-
self.channels = self.channels + (
109-
[tag] if tag not in self.channels else []
110-
)
108+
self.channels = self.channels + ([tag] if tag not in self.channels else [])
111109

112110
else:
113111
data_updated.append(data)

packages/evaluate/src/weathergen/evaluate/io_reader.py

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,7 @@ class DataAvailability:
6767

6868

6969
class Reader:
70-
def __init__(
71-
self, eval_cfg: dict, run_id: str, private_paths: dict[str, str] | None = None
72-
):
70+
def __init__(self, eval_cfg: dict, run_id: str, private_paths: dict[str, str] | None = None):
7371
"""
7472
Generic data reader class.
7573
@@ -254,19 +252,15 @@ def _get_channels_fsteps_samples(self, stream: str, mode: str) -> DataAvailabili
254252
)
255253

256254
stream_cfg = self.get_stream(stream)
257-
assert stream_cfg.get(mode, False), (
258-
"Mode does not exist in stream config. Please add it."
259-
)
255+
assert stream_cfg.get(mode, False), "Mode does not exist in stream config. Please add it."
260256

261257
samples = stream_cfg[mode].get("sample", None)
262258
fsteps = stream_cfg[mode].get("forecast_step", None)
263259
channels = stream_cfg.get("channels", None)
264260

265261
return DataAvailability(
266262
score_availability=True,
267-
channels=None
268-
if (channels == "all" or channels is None)
269-
else list(channels),
263+
channels=None if (channels == "all" or channels is None) else list(channels),
270264
fsteps=None if (fsteps == "all" or fsteps is None) else list(fsteps),
271265
samples=None if (samples == "all" or samples is None) else list(samples),
272266
)
@@ -286,9 +280,7 @@ def __init__(self, eval_cfg: dict, run_id: str, private_paths: dict | None = Non
286280

287281
if not self.results_base_dir:
288282
self.results_base_dir = Path(get_shared_wg_path("results"))
289-
_logger.info(
290-
f"Results directory obtained from private config: {self.results_base_dir}"
291-
)
283+
_logger.info(f"Results directory obtained from private config: {self.results_base_dir}")
292284
else:
293285
_logger.info(f"Results directory parsed: {self.results_base_dir}")
294286

@@ -306,19 +298,15 @@ def __init__(self, eval_cfg: dict, run_id: str, private_paths: dict | None = Non
306298
)
307299
# for backward compatibility allow metric_dir to be specified in the run config
308300
self.metrics_dir = Path(
309-
self.eval_cfg.get(
310-
"metrics_dir", self.metrics_base_dir / self.run_id / "evaluation"
311-
)
301+
self.eval_cfg.get("metrics_dir", self.metrics_base_dir / self.run_id / "evaluation")
312302
)
313303

314304
self.fname_zarr = self.results_dir.joinpath(
315305
f"validation_epoch{self.epoch:05d}_rank{self.rank:04d}.zarr"
316306
)
317307

318308
if not self.fname_zarr.exists() or not self.fname_zarr.is_dir():
319-
_logger.error(
320-
f"Zarr file {self.fname_zarr} does not exist or is not a directory."
321-
)
309+
_logger.error(f"Zarr file {self.fname_zarr} does not exist or is not a directory.")
322310
raise FileNotFoundError(
323311
f"Zarr file {self.fname_zarr} does not exist or is not a directory."
324312
)
@@ -401,9 +389,7 @@ def get_data(
401389
# TODO: Avoid conversion of fsteps and sample to integers (as obtained from the ZarrIO)
402390
fsteps = sorted([int(fstep) for fstep in fsteps])
403391
samples = sorted(
404-
[int(sample) for sample in self.get_samples()]
405-
if samples is None
406-
else samples
392+
[int(sample) for sample in self.get_samples()] if samples is None else samples
407393
)
408394
channels = channels or stream_cfg.get("channels", all_channels)
409395
channels = to_list(channels)
@@ -429,15 +415,11 @@ def get_data(
429415
fsteps_final = []
430416

431417
for fstep in fsteps:
432-
_logger.info(
433-
f"RUN {self.run_id} - {stream}: Processing fstep {fstep}..."
434-
)
418+
_logger.info(f"RUN {self.run_id} - {stream}: Processing fstep {fstep}...")
435419
da_tars_fs, da_preds_fs = [], []
436420
pps = []
437421

438-
for sample in tqdm(
439-
samples, desc=f"Processing {self.run_id} - {stream} - {fstep}"
440-
):
422+
for sample in tqdm(samples, desc=f"Processing {self.run_id} - {stream} - {fstep}"):
441423
out = zio.get_data(sample, stream, fstep)
442424
target, pred = out.target.as_xarray(), out.prediction.as_xarray()
443425

@@ -474,17 +456,13 @@ def get_data(
474456
da_tars_fs = da_tars_fs.assign_coords(
475457
sample=(
476458
"ipoint",
477-
np.repeat(
478-
da_tars_fs.sample.values, len(da_tars_fs.ipoint)
479-
),
459+
np.repeat(da_tars_fs.sample.values, len(da_tars_fs.ipoint)),
480460
)
481461
)
482462
da_preds_fs = da_preds_fs.assign_coords(
483463
sample=(
484464
"ipoint",
485-
np.repeat(
486-
da_preds_fs.sample.values, len(da_preds_fs.ipoint)
487-
),
465+
np.repeat(da_preds_fs.sample.values, len(da_preds_fs.ipoint)),
488466
)
489467
)
490468

@@ -506,12 +484,8 @@ def get_data(
506484
points_per_sample.loc[{"forecast_step": fstep}] = np.array(pps)
507485

508486
# Safer than a list
509-
da_tars = {
510-
fstep: da for fstep, da in zip(fsteps_final, da_tars, strict=True)
511-
}
512-
da_preds = {
513-
fstep: da for fstep, da in zip(fsteps_final, da_preds, strict=True)
514-
}
487+
da_tars = {fstep: da for fstep, da in zip(fsteps_final, da_tars, strict=True)}
488+
da_preds = {fstep: da for fstep, da in zip(fsteps_final, da_preds, strict=True)}
515489

516490
return ReaderOutput(
517491
target=da_tars, prediction=da_preds, points_per_sample=points_per_sample
@@ -522,7 +496,7 @@ def get_data(
522496
def get_stream(self, stream: str):
523497
"""
524498
returns the dictionary associated to a particular stream.
525-
Returns an empty dictionary if the stream does not exist in the Zarr file.
499+
Returns an empty dictionary if the stream does not exist in the Zarr file.
526500
527501
Parameters
528502
----------

packages/evaluate/src/weathergen/evaluate/plot_utils.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,7 @@ def plot_metric_region(
103103
run_ids.append(run_id)
104104

105105
if selected_data:
106-
_logger.info(
107-
f"Creating plot for {metric} - {region} - {stream} - {ch}."
108-
)
106+
_logger.info(f"Creating plot for {metric} - {region} - {stream} - {ch}.")
109107
name = "_".join([metric, region] + sorted(set(run_ids)) + [stream, ch])
110108
plotter.plot(
111109
selected_data,
@@ -157,9 +155,7 @@ def score_card_metric_region(
157155
if channels_common is None:
158156
channels_common = set(channels_per_run)
159157
else:
160-
channels_common = set(channels_common).intersection(
161-
set(channels_per_run)
162-
)
158+
channels_common = set(channels_common).intersection(set(channels_per_run))
163159

164160
if not channels_common:
165161
continue
@@ -207,9 +203,7 @@ def get_marker_size(cls, stream_name: str) -> float:
207203
float
208204
The default marker size for the stream.
209205
"""
210-
return cls._marker_size_stream.get(
211-
stream_name.lower(), cls._default_marker_size
212-
)
206+
return cls._marker_size_stream.get(stream_name.lower(), cls._default_marker_size)
213207

214208
@classmethod
215209
def list_streams(cls):

0 commit comments

Comments
 (0)