4 Commits

Author SHA1 Message Date
generatedunixname89002005307016
2d224e6652 Suppress type errors for Pyrefly upgrade (landing group 135)
Summary:
This Phabricator Diff was generated from Pyrefly type-checker output by the per-target upgrade tool. It was not generated by an AI coding system.

It adds only `# pyrefly: ignore[...]` comments for type errors reported by the upcoming Pyrefly version. It does not change Python runtime behavior or application logic.

If this diff does not land before the Pyrefly upgrade, the associated type-checking tests will fail.

Landing group: `c004445c7bbada027a0d112b6d018972b720eb832b6be71177fcfbbe4758bbd0` (attempt 1, 13 batches, 86 files)
Merged patch SHA-256: `17964792f0a8a521b30c0c5a41641c2b6301018eba369246c7af786200728d97`

#pyreupgrade

Reviewed By: maggiemoss

Differential Revision: D128539582

fbshipit-source-id: 6714747585617e68fa66c240a2c05bab8e2f792f
2026-10-30 10:32:37 -07:00
generatedunixname89002005307016
204fe5831b Remove unused type error suppressions (landing group 42)
Summary:
This Phabricator Diff was generated from Pyrefly type-checker output by the per-target upgrade tool. It was not generated by an AI coding system.

It changes only type-error suppression comments: it removes suppressions that are no longer needed and adds `# pyrefly: ignore[...]` comments for remaining errors. It does not change Python runtime behavior or application logic.

Landing group: `74a406ba3ec1f23501f4841695298046d3c8db50f864040e2c4b64506cdb66bd` (attempt 1, 20 batches, 391 files)
Merged patch SHA-256: `c3e986b54ae5248e9810275f66372a9b2047a8e0aa14b3d3301ff8783f4e7e0e`

#pyreupgrade

Differential Revision: D126124958

fbshipit-source-id: f89b7d78ab77074fd80a15de7cef6f1f9fdb4237
2026-10-19 15:24:05 -07:00
Raashish Aggarwal
8b3ba6ede4 Accept tab-separated lines in load_obj (#2061)
Summary:
`load_obj` recognizes vertex, texture, normal and face lines with `line.startswith("v ")`, `"vt "`, `"vn "` and `"f "`. The OBJ format separates fields with any whitespace, and some exporters write a tab after the keyword. Those lines are skipped without any error, so the mesh silently loses vertices and faces:

```python
from io import StringIO
from pytorch3d.io import load_obj

verts, faces, _ = load_obj(StringIO("v\t0 0 0\nv 1 0 0\nv 0 1 0\nf\t1 2 3\n"))
verts.shape, faces.verts_idx.shape
# before: (2, 3), (0, 3)   after: (3, 3), (1, 3)
```

This dispatches on the first token of the already split line (`tokens[:1] == ["v"]`), the same way the `usemtl` branch already does.

Pull Request resolved: https://github.com/facebookresearch/pytorch3d/pull/2061

Test Plan: added `test_load_obj_tab_separated` to `tests/test_io_obj.py`, using tabs in `v`, `vt`, `vn` and `f` lines. It fails on main and passes with the change. I ran `tests/test_io_obj.py` on Windows without the C++ extension: the same 19 tests fail before and after (they reopen a `NamedTemporaryFile` or need `_C`), and the rest pass. flake8 is clean on the changed lines.

Reviewed By: MichaelRamamonjisoa

Differential Revision: D124656223

Pulled By: bottler

fbshipit-source-id: ab0ef3dc0f3c998c44874e6a416e6a9649f29378
2026-10-19 14:36:14 -07:00
generatedunixname89002005307016
88e182f989 Suppress type errors for Pyre upgrade
Summary:
This diff was automatically generated by the Pyre per-target upgrade tool.

It adds `# pyrefly: ignore` comments for type errors that an upcoming Pyrefly version will report. These suppressions keep existing type-checking tests passing while the upgrade rolls out. This diff changes comments only; it does not change Python runtime behavior or application logic.

pyre-upgrade-september-29

#pyreupgrade

Differential Revision: D122460195

fbshipit-source-id: 2ca62cd61e77d222c5b42ac89e73fc570a9781ae
2026-09-29 23:00:50 -07:00
11 changed files with 46 additions and 15 deletions

View File

@@ -104,7 +104,6 @@ class _SymEig3x3(nn.Module):
)
p = torch.sqrt(p2 / 6.0)
# pyrefly: ignore [unsupported-operation]
B = (inputs - q[..., None, None] * self._identity) / p[..., None, None]
r = torch.det(B) / 2.0
@@ -189,10 +188,8 @@ class _SymEig3x3(nn.Module):
"""
# Find the eigenvector corresponding to alpha0, its eigenvalue is distinct
# pyrefly: ignore [unsupported-operation]
ev0 = self._get_ev0(inputs - alpha0[..., None, None] * self._identity)
u, v = self._get_uv(ev0)
# pyrefly: ignore [unsupported-operation]
ev1 = self._get_ev1(inputs - alpha1[..., None, None] * self._identity, u, v)
# Third eigenvector is computed as the cross-product of the other two
ev2 = torch.cross(ev0, ev1, dim=-1)
@@ -267,7 +264,6 @@ class _SymEig3x3(nn.Module):
"""
min_idx = w.abs().argmin(dim=-1)
# pyrefly: ignore [bad-index]
rotation_2d = self._rotations_3d[min_idx].to(w)
u = F.normalize((rotation_2d @ w[..., None])[..., 0], dim=-1)

View File

@@ -182,10 +182,12 @@ def generate_eval_video_cameras(
)
test_cameras = PerspectiveCameras(
# pyrefly: ignore [bad-argument-type]
focal_length=focal_length,
principal_point=principal_point,
R=R,
T=T,
# pyrefly: ignore [missing-attribute]
device=focal_length.device,
)

View File

@@ -472,26 +472,26 @@ def _parse_obj(f, data_dir: str):
materials_idx = len(material_names) - 1
else:
materials_idx = material_names.index(material_name)
elif line.startswith("v "): # Line is a vertex.
elif tokens[:1] == ["v"]: # Line is a vertex.
vert = [float(x) for x in tokens[1:4]]
if len(vert) != 3:
msg = "Vertex %s does not have 3 values. Line: %s"
raise ValueError(msg % (str(vert), str(line)))
verts.append(vert)
elif line.startswith("vt "): # Line is a texture.
elif tokens[:1] == ["vt"]: # Line is a texture.
tx = [float(x) for x in tokens[1:3]]
if len(tx) != 2:
raise ValueError(
"Texture %s does not have 2 values. Line: %s" % (str(tx), str(line))
)
verts_uvs.append(tx)
elif line.startswith("vn "): # Line is a normal.
elif tokens[:1] == ["vn"]: # Line is a normal.
norm = [float(x) for x in tokens[1:4]]
if len(norm) != 3:
msg = "Normal %s does not have 3 values. Line: %s"
raise ValueError(msg % (str(norm), str(line)))
normals.append(norm)
elif line.startswith("f "): # Line is a face.
elif tokens[:1] == ["f"]: # Line is a face.
# Update face properties info.
_parse_face(
line,
@@ -794,7 +794,7 @@ def save_obj(
mtl_path = str(mtl_path)
# Save texture map to output folder
# pyre-fixme[16] # undefined attribute cpu
# undefined attribute cpu
texture_map = texture_map.detach().cpu() * 255.0
image = Image.fromarray(texture_map.numpy().astype(np.uint8))
with _open_file(image_path, path_manager, "wb") as im_f:

View File

@@ -340,8 +340,11 @@ def corresponding_points_alignment(
total_weight = torch.clamp(num_points, 1)
# special handling for heterogeneous point clouds and/or input weights
if weights is not None:
# pyrefly: ignore [bad-index]
Xc *= weights[:, :, None]
# pyrefly: ignore [bad-index]
Yc *= weights[:, :, None]
# pyrefly: ignore [missing-attribute]
total_weight = torch.clamp(weights.sum(1), eps)
if (num_points < (dim + 1)).any():

View File

@@ -427,9 +427,11 @@ class CamerasBase(TensorProperties):
if isinstance(index, tensor_types["bool"]):
# LongTensor]` has no attribute `ndim`.
# LongTensor]` has no attribute `shape`.
# pyrefly: ignore [missing-attribute]
if index.ndim != 1 or index.shape[0] != len(self):
raise ValueError(
# LongTensor]` has no attribute `shape`.
# pyrefly: ignore [missing-attribute]
f"Boolean index of shape {index.shape} does not match cameras"
)
elif max(index) >= len(self):
@@ -1790,11 +1792,13 @@ def get_ndc_to_screen_transform(
image_size = torch.tensor(image_size, device=cameras.device)
# pyrefly: ignore [missing-attribute]
image_size = image_size.view(-1, 2) # of shape (1 or B)x2
# pyrefly: ignore [missing-attribute]
height, width = image_size.unbind(1)
# For non square images, we scale the points such that smallest side
# has range [-1, 1] and the largest side has range [-u, u], with u > 1.
# This convention is consistent with the PyTorch3D renderer
# pyrefly: ignore [missing-attribute]
scale = (image_size.min(dim=1).values - 0.0) / 2.0
K[:, 0, 0] = scale

View File

@@ -136,16 +136,13 @@ class HarmonicEmbedding(torch.nn.Module):
[..., (n_harmonic_functions * 2 + int(append_input)) * num_points_per_ray]
"""
# [..., dim, n_harmonic_functions]
# pyrefly: ignore [unsupported-operation]
embed = x[..., None] * self._frequencies
# [..., 1, dim, n_harmonic_functions] + [2, 1, 1] => [..., 2, dim, n_harmonic_functions]
# pyrefly: ignore [bad-index]
embed = embed[..., None, :, :] + self._zero_half_pi[..., None, None]
# Use the trig identity cos(x) = sin(x + pi/2)
# and do one vectorized call to sin([x, x+pi/2]) instead of (sin(x), cos(x)).
embed = embed.sin()
if diag_cov is not None:
# pyrefly: ignore [no-matching-overload]
x_var = diag_cov[..., None] * torch.pow(self._frequencies, 2)
exp_var = torch.exp(-0.5 * x_var)
# [..., 2, dim, n_harmonic_functions]
@@ -184,7 +181,6 @@ class HarmonicEmbedding(torch.nn.Module):
"""
return self.get_output_dim_static(
input_dims,
# pyrefly: ignore [bad-argument-type]
len(self._frequencies),
self.append_input,
)

View File

@@ -239,12 +239,10 @@ class MeshRasterizer(nn.Module):
if raster_settings.perspective_correct is not None:
perspective_correct = raster_settings.perspective_correct
else:
# pyrefly: ignore [missing-attribute]
perspective_correct = cameras.is_perspective()
if raster_settings.z_clip_value is not None:
z_clip = raster_settings.z_clip_value
else:
# pyrefly: ignore [missing-attribute]
znear = cameras.get_znear()
if isinstance(znear, torch.Tensor):
znear = znear.min().item()

View File

@@ -482,6 +482,7 @@ class TexturesAtlas(TexturesBase):
else self._num_faces_per_mesh
)
tex.valid = self.valid.clone()
# pyrefly: ignore[bad-assignment]
tex._num_faces_per_mesh = num_faces
return tex
@@ -496,6 +497,7 @@ class TexturesAtlas(TexturesBase):
else self._num_faces_per_mesh
)
tex.valid = self.valid.detach()
# pyrefly: ignore[bad-assignment]
tex._num_faces_per_mesh = num_faces
return tex
@@ -982,6 +984,7 @@ class TexturesUV(TexturesBase):
if torch.is_tensor(self._num_faces_per_mesh)
else self._num_faces_per_mesh
)
# pyrefly: ignore[bad-assignment]
tex._num_faces_per_mesh = num_faces
tex.valid = self.valid.clone()
return tex
@@ -1014,6 +1017,7 @@ class TexturesUV(TexturesBase):
if torch.is_tensor(self._num_faces_per_mesh)
else self._num_faces_per_mesh
)
# pyrefly: ignore[bad-assignment]
tex._num_faces_per_mesh = num_faces
tex.valid = self.valid.detach()
return tex

View File

@@ -510,7 +510,9 @@ class Meshes:
index = index.squeeze(1) if index.numel() > 0 else index
# pyrefly: ignore [missing-attribute]
index = index.tolist()
# pyrefly: ignore [not-iterable]
verts = [self.verts_list()[i] for i in index]
# pyrefly: ignore [not-iterable]
faces = [self.faces_list()[i] for i in index]
else:
raise IndexError(index)

View File

@@ -411,10 +411,13 @@ class Pointclouds:
index = index.squeeze(1) if index.numel() > 0 else index
# pyrefly: ignore [missing-attribute]
index = index.tolist()
# pyrefly: ignore [not-iterable]
points = [self.points_list()[i] for i in index]
if normals_list is not None:
# pyrefly: ignore [not-iterable]
normals = [normals_list[i] for i in index]
if features_list is not None:
# pyrefly: ignore [not-iterable]
features = [features_list[i] for i in index]
else:
raise IndexError(index)

View File

@@ -319,6 +319,29 @@ class TestMeshObjIO(TestCaseMixin, unittest.TestCase):
self.assertTrue(materials is None)
self.assertTrue(tex_maps is None)
def test_load_obj_tab_separated(self):
# Tabs are whitespace too, including between the keyword and the values.
obj_file = "\n".join(
[
"v\t0.1\t0.2\t0.3",
"v 0.2 0.3 0.4",
"v\t0.3 0.4 0.5",
"vt\t0.5\t0.5",
"vn\t0.0\t0.0\t1.0",
"f\t1/1/1\t2/1/1\t3/1/1",
]
)
verts, faces, aux = load_obj(StringIO(obj_file))
expected_verts = torch.tensor(
[[0.1, 0.2, 0.3], [0.2, 0.3, 0.4], [0.3, 0.4, 0.5]], dtype=torch.float32
)
self.assertClose(verts, expected_verts)
self.assertClose(faces.verts_idx, torch.tensor([[0, 1, 2]]))
self.assertClose(faces.textures_idx, torch.tensor([[0, 0, 0]]))
self.assertClose(faces.normals_idx, torch.tensor([[0, 0, 0]]))
self.assertClose(aux.verts_uvs, torch.tensor([[0.5, 0.5]]))
self.assertClose(aux.normals, torch.tensor([[0.0, 0.0, 1.0]]))
def test_load_obj_error_textures(self):
obj_file = "\n".join(["vt 0.1"])
with NamedTemporaryFile(mode="w", suffix=".obj") as f: