Skip to content
Open
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
5 changes: 2 additions & 3 deletions .github/workflows/cicd_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
opt: ["codeformat", "mypy"] # "pytype" omitted for being essentially deprecated, see #8865
opt: ["codeformat", "pyrefly"]
steps:
- name: Clean unused tools
run: |
Expand All @@ -80,8 +80,7 @@ jobs:
run: |
# clean up temporary files
$(pwd)/runtests.sh --build --clean
# Github actions have multiple cores, so parallelize pytype
$(pwd)/runtests.sh --build --${{ matrix.opt }} -j $(nproc --all)
$(pwd)/runtests.sh --build --${{ matrix.opt }}

min-dep: # Test with minumum dependencies installed for different OS, Python, and PyTorch combinations
runs-on: ${{ matrix.os }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cron.yml
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ jobs:
python -c "import torch; print(torch.__version__); print('{} of GPUs available'.format(torch.cuda.device_count()))"
python -c 'import torch; print(torch.rand(5,3, device=torch.device("cuda:0")))'
ngc --version
BUILD_MONAI=1 ./runtests.sh --build --coverage --unittests --disttests # unit tests with pytype checks, coverage report
BUILD_MONAI=1 ./runtests.sh --build --coverage --pyrefly --unittests --disttests # unit tests with pyrefly checks, coverage report
BUILD_MONAI=1 ./runtests.sh --build --coverage --net # integration tests with coverage report
coverage xml --ignore-errors
if pgrep python; then pkill python; fi
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/weekly-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
opt: ["codeformat", "mypy"]
opt: ["codeformat", "pyrefly"]
steps:
- name: Clean unused tools
run: |
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,17 @@ venv.bak/
# pytype cache

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ignores should be left in with the pyrefly ones. People will have pytype stuff hanging around still in their own working clones.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Restored .pytype/ to .gitignore alongside the pyrefly cache entries.

.pytype/

# pyrefly cache
.pyrefly_cache/

# mypy
.mypy_cache/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave the .mypy_cache ignore here as well.

.dmypy.json

examples/scd_lvsegs.npz
temp/
.idea/
.dmypy.json
.plans/

*~

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Please note that, as per PyTorch, MONAI uses American English spelling. This mea
### Preparing pull requests

To ensure the code quality, MONAI relies on several linting tools ([black](https://github.com/psf/black), [isort](https://github.com/timothycrosley/isort), [ruff](https://github.com/astral-sh/ruff)),
static type analysis tools ([mypy](https://github.com/python/mypy), [pytype](https://github.com/google/pytype)), as well as a set of unit/integration tests.
static type analysis tools ([pyrefly](https://github.com/facebook/pyrefly)), as well as a set of unit/integration tests.

This section highlights all the necessary preparation steps required before sending a pull request.
To collaborate efficiently, please read through this section and follow them.
Expand Down
3 changes: 3 additions & 0 deletions monai/apps/auto3dseg/auto_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ def inspect_datalist_folds(self, datalist_filename: str) -> int:

datalist = ConfigParser.load_config_file(datalist_filename)
if "training" not in datalist:
# pyrefly: ignore [unnecessary-type-conversion]
raise ValueError("Datalist files has no training key:" + str(datalist_filename))

fold_list = [int(d["fold"]) for d in datalist["training"] if "fold" in d]
Expand Down Expand Up @@ -790,6 +791,7 @@ def _train_algo_in_nni(self, history: list[dict[str, Any]]) -> None:
nni_config_filename = os.path.abspath(os.path.join(self.work_dir, f"{name}_nni_config.yaml"))
ConfigParser.export_config_file(nni_config, nni_config_filename, fmt="yaml", default_flow_style=None)

# pyrefly: ignore [redundant-cast]
max_trial = min(self.hpo_tasks, cast(int, default_nni_config["maxTrialNumber"]))
cmd = "nnictl create --config " + nni_config_filename + " --port 8088"

Expand All @@ -805,6 +807,7 @@ def _train_algo_in_nni(self, history: list[dict[str, Any]]) -> None:
n_trainings = len(import_bundle_algo_history(self.work_dir, only_trained=True))

cmd = "nnictl stop --all"
# pyrefly: ignore [bad-argument-type]
run_cmd(cmd.split(), check=True)
logger.info(f"NNI completes HPO on {name}")
last_total_tasks = n_trainings
Expand Down
1 change: 1 addition & 0 deletions monai/apps/auto3dseg/ensemble_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ def __init__(self, history: Sequence[dict[str, Any]], data_src_cfg_name: str | N
self.ensemble: AlgoEnsemble
self.data_src_cfg = ConfigParser(globals=False)

# pyrefly: ignore [unnecessary-type-conversion]
if data_src_cfg_name is not None and os.path.exists(str(data_src_cfg_name)):
self.data_src_cfg.read_config(data_src_cfg_name)

Expand Down
3 changes: 3 additions & 0 deletions monai/apps/deepedit/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ def _randomize(self, d, key_label):
else:
logger.info(f"Not slice IDs for label: {key_label}")
sid = None
# pyrefly: ignore [unsupported-operation]
self.sid[key_label] = sid

def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]:
Expand Down Expand Up @@ -561,6 +562,7 @@ def __init__(
self.guidance: dict[str, list[list[int]]] = {}

def randomize(self, data=None):
# pyrefly: ignore [unsupported-operation]
probability = data[self.probability]
self._will_interact = self.R.choice([True, False], p=[probability, 1.0 - probability])

Expand Down Expand Up @@ -885,6 +887,7 @@ def _randomize(self, d, key_label):
else:
logger.info(f"Not slice IDs for label: {key_label}")
sid = None
# pyrefly: ignore [unsupported-operation]
self.sid[key_label] = sid

def __call__(self, data: Mapping[Hashable, np.ndarray]) -> dict[Hashable, np.ndarray]:
Expand Down
1 change: 1 addition & 0 deletions monai/apps/deepgrow/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def _save_data_2d(vol_idx, vol_image, vol_label, dataset_dir, relative_path):
continue

# For all Labels
# pyrefly: ignore [missing-attribute]
unique_labels = np.unique(label.flatten())
unique_labels = unique_labels[unique_labels != 0]
unique_labels_count = max(unique_labels_count, len(unique_labels))
Expand Down
1 change: 1 addition & 0 deletions monai/apps/deepgrow/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ def __init__(self, guidance: str = "guidance", discrepancy: str = "discrepancy",
self._will_interact = None

def randomize(self, data=None):
# pyrefly: ignore [unsupported-operation]
probability = data[self.probability]
self._will_interact = self.R.choice([True, False], p=[probability, 1.0 - probability])

Expand Down
2 changes: 2 additions & 0 deletions monai/apps/detection/networks/retinanet_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ def forward(
)

# 4. Generate anchors and store it in self.anchors: List[Tensor]
# pyrefly: ignore [bad-argument-type]
self.generate_anchors(images, head_outputs)
# num_anchor_locs_per_level: List[int], list of HW or HWD for each level
num_anchor_locs_per_level = [x.shape[2:].numel() for x in head_outputs[self.cls_key]]
Expand All @@ -535,6 +536,7 @@ def forward(
# reshape to Tensor sized(B, sum(HWA), self.num_classes) for self.cls_key
# or (B, sum(HWA), 2* self.spatial_dims) for self.box_reg_key
# A = self.num_anchors_per_loc
# pyrefly: ignore [bad-argument-type]
head_outputs[key] = self._reshape_maps(head_outputs[key])

# 6(1). If during training, return losses
Expand Down
4 changes: 4 additions & 0 deletions monai/apps/detection/transforms/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,14 @@ def __call__(self, boxes: NdarrayTensor, src_spatial_size: Sequence[int] | int |
diff = od - zd
half = abs(diff) // 2
if diff > 0: # need padding (half, diff - half)
# pyrefly: ignore [bad-index, unsupported-operation]
zoomed_boxes[:, axis] = zoomed_boxes[:, axis] + half
# pyrefly: ignore [bad-index, unsupported-operation]
zoomed_boxes[:, axis + spatial_dims] = zoomed_boxes[:, axis + spatial_dims] + half
elif diff < 0: # need slicing (half, half + od)
# pyrefly: ignore [bad-index, unsupported-operation]
zoomed_boxes[:, axis] = zoomed_boxes[:, axis] - half
# pyrefly: ignore [bad-index, unsupported-operation]
zoomed_boxes[:, axis + spatial_dims] = zoomed_boxes[:, axis + spatial_dims] - half
return zoomed_boxes

Expand Down
2 changes: 2 additions & 0 deletions monai/apps/detection/transforms/box_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ def flip_boxes(
_flip_boxes: NdarrayTensor = boxes.clone() if isinstance(boxes, torch.Tensor) else deepcopy(boxes) # type: ignore[assignment]

for axis in flip_axes:
# pyrefly: ignore [bad-index, unsupported-operation]
_flip_boxes[:, axis + spatial_dims] = spatial_size[axis] - boxes[:, axis] - TO_REMOVE
# pyrefly: ignore [bad-index, unsupported-operation]
_flip_boxes[:, axis] = spatial_size[axis] - boxes[:, axis + spatial_dims] - TO_REMOVE

return _flip_boxes
Expand Down
1 change: 1 addition & 0 deletions monai/apps/detection/transforms/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,7 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor]) -> list[dict[Hashable,
cropper = SpatialCrop(roi_center=tuple(center), roi_size=self.spatial_size)
crop_start = [max(s.start, 0) for s in cropper.slices]
crop_end = [min(s.stop, image_size_a) for s, image_size_a in zip(cropper.slices, image_size)]
# pyrefly: ignore [unnecessary-type-conversion]
crop_slices = [slice(int(s), int(e)) for s, e in zip(crop_start, crop_end)]

# crop images
Expand Down
1 change: 1 addition & 0 deletions monai/apps/detection/utils/hard_negative_sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ def get_num_neg(self, negative: torch.Tensor, num_pos: int) -> int:
number of negative samples
"""
# always assume at least one pos sample was sampled
# pyrefly: ignore [unnecessary-type-conversion]
num_neg = int(max(1, num_pos) * abs(1 - 1.0 / float(self.positive_fraction)))
# protect against not enough negative examples and sample at least self.min_neg if possible
num_neg = min(negative.numel(), max(num_neg, self.min_neg))
Expand Down
2 changes: 2 additions & 0 deletions monai/apps/generation/maisi/networks/autoencoderkl_maisi.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:

# update padding length if necessary
padding = 3
# pyrefly: ignore [unsupported-operation]
if padding % self.stride > 0:
# pyrefly: ignore [unsupported-operation]
padding = (padding // self.stride + 1) * self.stride
if self.print_info:
logger.info(f"Padding size: {padding}")
Expand Down
1 change: 1 addition & 0 deletions monai/apps/nnunet/nnunetv2_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ def convert_dataset(self):
modality = [modality]

create_new_dataset_json(
# pyrefly: ignore [bad-argument-type]
modality=modality,
num_foreground_classes=num_foreground_classes,
num_input_channels=num_input_channels,
Expand Down
2 changes: 2 additions & 0 deletions monai/apps/nuclick/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ def bbox(self, patch_size, centroid, size):
x, y = centroid
m, n = size

# pyrefly: ignore [unnecessary-type-conversion]
x_start = int(max(x - patch_size / 2, 0))
# pyrefly: ignore [unnecessary-type-conversion]
y_start = int(max(y - patch_size / 2, 0))
x_end = x_start + patch_size
y_end = y_start + patch_size
Expand Down
1 change: 1 addition & 0 deletions monai/apps/pathology/transforms/post/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ def __call__(self, data):
d = dict(data)
for key in self.key_iterator(d):
offset = d[self.offset_key] if self.offset_key else None
# pyrefly: ignore [bad-argument-type]
centroid = self.converter(d[key], offset)
key_to_add = f"{key}_{self.centroid_key_postfix}"
if key_to_add in d:
Expand Down
4 changes: 4 additions & 0 deletions monai/apps/vista3d/inferer.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,11 @@ def point_based_window_inferer(
unravel_slice = (
slice(None),
slice(None),
# pyrefly: ignore [unnecessary-type-conversion]
slice(int(lx), int(rx)),
# pyrefly: ignore [unnecessary-type-conversion]
slice(int(ly), int(ry)),
# pyrefly: ignore [unnecessary-type-conversion]
slice(int(lz), int(rz)),
)
batch_image = image[unravel_slice]
Expand Down Expand Up @@ -151,6 +154,7 @@ def _get_window_idx_c(p: int, roi: int, s: int) -> tuple[int, int]:
elif p + roi // 2 > s:
left, right = s - roi, s
else:
# pyrefly: ignore [unnecessary-type-conversion]
left, right = int(p) - roi // 2, int(p) + roi // 2
return left, right

Expand Down
8 changes: 5 additions & 3 deletions monai/apps/vista3d/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ def _convert_name_to_index(name_to_index_mapping: dict, label_prompt: list | Non
for l in label_prompt:
if isinstance(l, (int, str)):
converted_label_prompt.append(
name_to_index_mapping.get(l.lower(), int(l) if l.isdigit() else 0) if isinstance(l, str) else int(l)
name_to_index_mapping.get(l.lower(), int(l) if l.isdigit() else 0)
if isinstance(l, str)
else int(l) # pyrefly: ignore [unnecessary-type-conversion]
)
else:
converted_label_prompt.append(l)
Expand Down Expand Up @@ -206,8 +208,8 @@ def __init__(
self.dataset_key = dataset_key
for name, mapping in label_mappings.items():
self.mappers[name] = MapLabelValue(
orig_labels=[int(pair[0]) for pair in mapping],
target_labels=[int(pair[1]) for pair in mapping],
orig_labels=[int(pair[0]) for pair in mapping], # pyrefly: ignore [unnecessary-type-conversion]
target_labels=[int(pair[1]) for pair in mapping], # pyrefly: ignore [unnecessary-type-conversion]
dtype=dtype,
)

Expand Down
1 change: 1 addition & 0 deletions monai/auto3dseg/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,5 @@ def evaluate(self, data: Any, **kwargs: Any) -> dict:
Args:
data: input data
"""
# pyrefly: ignore [missing-attribute]
return {k: v(data[k], **kwargs).tolist() for k, v in self.data.items() if (callable(v) and k in data)}
1 change: 1 addition & 0 deletions monai/auto3dseg/seg_summarizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ def summarize(self, data: list[dict]) -> dict[str, dict]:

for analyzer in self.summary_analyzers:
if callable(analyzer):
# pyrefly: ignore [missing-attribute]
report.update({analyzer.stats_name: analyzer(data)})

return report
1 change: 1 addition & 0 deletions monai/bundle/reference_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ def iter_subconfigs(cls, id: str, config: Any) -> Iterator[tuple[str, str, Any]]
"""
for k, v in config.items() if isinstance(config, dict) else enumerate(config):
sub_id = f"{id}{cls.sep}{k}" if id != "" else f"{k}"
# pyrefly: ignore [invalid-yield]
yield k, sub_id, v

@classmethod
Expand Down
3 changes: 3 additions & 0 deletions monai/bundle/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1952,8 +1952,10 @@ def create_workflow(
_args, workflow_name=ConfigWorkflow, config_file=None
) # the default workflow name is "ConfigWorkflow"
if isinstance(workflow_name, str):
# pyrefly: ignore [unnecessary-type-conversion]
workflow_class, has_built_in = optional_import("monai.bundle", name=str(workflow_name)) # search built-in
if not has_built_in:
# pyrefly: ignore [unnecessary-type-conversion]
workflow_class = locate(str(workflow_name)) # search dotted path
if workflow_class is None:
raise ValueError(f"cannot locate specified workflow class: {workflow_name}.")
Expand All @@ -1966,6 +1968,7 @@ def create_workflow(
)

if config_file is not None:
# pyrefly: ignore [unexpected-keyword]
workflow_ = workflow_class(config_file=config_file, **_args)
else:
workflow_ = workflow_class(**_args)
Expand Down
3 changes: 3 additions & 0 deletions monai/bundle/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ def add_property(self, name: str, required: str, desc: str | None = None) -> Non
desc: descriptions for the property.
"""
if self.properties is None:
# pyrefly: ignore [bad-assignment]
self.properties = {}
if name in self.properties:
logger.warning(f"property '{name}' already exists in the properties list, overriding it.")
Expand Down Expand Up @@ -329,6 +330,7 @@ def _get_property(self, name: str, property: dict) -> Any:
elif name in self._props_vals:
value = self._props_vals[name]
elif name in self.parser.config[self.parser.meta_key]: # type: ignore[index]
# pyrefly: ignore [missing-attribute]
id = self.properties.get(name, None).get(BundlePropertyConfig.ID, None)
value = self.parser[id]
else:
Expand Down Expand Up @@ -621,6 +623,7 @@ def _check_optional_id(self, name: str, property: dict) -> bool:
else:
ref = self.parser.get(ref_id, None)
# for reference IDs that not refer to a property directly but using expressions, skip the check
# pyrefly: ignore [unsupported-operation]
if ref is not None and not ref.startswith(EXPR_KEY) and ref != ID_REF_KEY + id:
return False
return True
Expand Down
1 change: 1 addition & 0 deletions monai/data/box_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ def get_spatial_dims(
raise ValueError("At least one of the inputs needs to be non-empty.")

if len(spatial_dims_list) == 1:
# pyrefly: ignore [unnecessary-type-conversion]
spatial_dims = int(spatial_dims_list[0])
spatial_dims = look_up_option(spatial_dims, supported=[2, 3])
return int(spatial_dims)
Expand Down
4 changes: 4 additions & 0 deletions monai/data/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@ def __init__(
self.hash_func = hash_func
self.num_workers = num_workers
if self.num_workers is not None:
# pyrefly: ignore [unnecessary-type-conversion]
self.num_workers = max(int(self.num_workers), 1)
self.runtime_cache = runtime_cache
self.cache_num = 0
Expand All @@ -863,6 +864,7 @@ def set_data(self, data: Sequence) -> None:
self.data = data

def _compute_cache_num(data_len: int):
# pyrefly: ignore [unnecessary-type-conversion]
self.cache_num = min(int(self.set_num), int(data_len * self.set_rate), data_len)

if self.hash_as_key:
Expand Down Expand Up @@ -1082,6 +1084,7 @@ def __init__(

self.num_replace_workers: int | None = num_replace_workers
if self.num_replace_workers is not None:
# pyrefly: ignore [unnecessary-type-conversion]
self.num_replace_workers = max(int(self.num_replace_workers), 1)

self._total_num: int = len(data)
Expand Down Expand Up @@ -1648,6 +1651,7 @@ def _cachecheck(self, item_transformed):
item_k = kvikio_numpy.fromfile(
f"{hashfile}-{k}-{i}", dtype=meta_i_k["dtype"], like=cp.empty(())
)
# pyrefly: ignore [missing-attribute]
item_k = convert_to_tensor(item[i].reshape(meta_i_k["shape"]), device=f"cuda:{self.device}")
item[i].update({k: item_k, f"{k}_meta_dict": meta_i_k})
return item
Expand Down
Loading
Loading