metafold 0.13.1.dev1__tar.gz → 0.13.2.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/PKG-INFO +1 -1
  2. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/simulation/compression_simulation.py +11 -2
  3. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold.egg-info/PKG-INFO +1 -1
  4. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/pyproject.toml +1 -1
  5. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_compression_simulation.py +71 -0
  6. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/LICENSE +0 -0
  7. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/README.md +0 -0
  8. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/__init__.py +0 -0
  9. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/api.py +0 -0
  10. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/assets.py +0 -0
  11. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/auth.py +0 -0
  12. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/client.py +0 -0
  13. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/exceptions.py +0 -0
  14. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/jobs.py +0 -0
  15. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/materials.py +0 -0
  16. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/projects.py +0 -0
  17. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/simulation/__init__.py +0 -0
  18. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/simulation/compression_experiment.py +0 -0
  19. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/simulation/run_experiment.py +0 -0
  20. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/utils.py +0 -0
  21. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold/workflows.py +0 -0
  22. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold.egg-info/SOURCES.txt +0 -0
  23. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold.egg-info/dependency_links.txt +0 -0
  24. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold.egg-info/requires.txt +0 -0
  25. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/metafold.egg-info/top_level.txt +0 -0
  26. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/setup.cfg +0 -0
  27. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_assets.py +0 -0
  28. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_compession_experiment.py +0 -0
  29. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_jobs.py +0 -0
  30. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_materials.py +0 -0
  31. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_projects.py +0 -0
  32. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_run_experiment.py +0 -0
  33. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_shear_simulation.py +0 -0
  34. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_utils.py +0 -0
  35. {metafold-0.13.1.dev1 → metafold-0.13.2.dev0}/tests/test_workflows.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metafold
3
- Version: 0.13.1.dev1
3
+ Version: 0.13.2.dev0
4
4
  Summary: Metafold SDK for Python
5
5
  Author-email: Metafold 3D <info@metafold3d.com>
6
6
  License: Copyright 2024 Metafold 3D
@@ -656,6 +656,7 @@ class CompressionSimulation:
656
656
  base_url: str = "https://api.metafold3d.com/",
657
657
  cancel_existing_workflows: bool = True,
658
658
  project_access: Access = Access.PRIVATE,
659
+ user: Optional[str] = None,
659
660
  ) -> str:
660
661
  """Find an existing project by name or create a new one.
661
662
 
@@ -663,6 +664,10 @@ class CompressionSimulation:
663
664
  client_id + client_secret (local/programmable use, reads from env via
664
665
  setup_client). base_url must match the audience the token was issued for.
665
666
 
667
+ When user is given, only the caller's own projects are reused; a
668
+ same-named project owned by someone else (visible under org access) is
669
+ ignored so it can't be overwritten.
670
+
666
671
  When cancel_existing_workflows is True and an existing project is
667
672
  reused, its running workflows are cancelled first.
668
673
 
@@ -675,10 +680,14 @@ class CompressionSimulation:
675
680
  auth_domain=auth_domain,
676
681
  base_url=base_url,
677
682
  )
678
- # Quote the project_name string for multi-word name searchess
683
+ # Quote the values so multi-word names stay a single search term.
684
+ q = f'name:"{project_name}"'
685
+ if user is not None:
686
+ q = f'{q} AND user:"{user}"'
679
687
  existing = [
680
- p for p in client.projects.list(q=f'name:"{project_name}"')
688
+ p for p in client.projects.list(q=q)
681
689
  if p.name == project_name # ensure full match
690
+ and (user is None or p.user == user)
682
691
  ]
683
692
  if existing:
684
693
  project_id = existing[0].id
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metafold
3
- Version: 0.13.1.dev1
3
+ Version: 0.13.2.dev0
4
4
  Summary: Metafold SDK for Python
5
5
  Author-email: Metafold 3D <info@metafold3d.com>
6
6
  License: Copyright 2024 Metafold 3D
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "metafold"
7
- version = "0.13.1.dev1"
7
+ version = "0.13.2.dev0"
8
8
  authors = [
9
9
  {name = "Metafold 3D", email = "info@metafold3d.com"},
10
10
  ]
@@ -1439,3 +1439,74 @@ class TestFindOrCreateProjectCancel:
1439
1439
 
1440
1440
  assert pid == "new-pid"
1441
1441
  client.workflows.cancel.assert_not_called()
1442
+
1443
+
1444
+ class TestFindOrCreateProjectUserScope:
1445
+ """With user set, only the caller's own project is reused; a same-named
1446
+ project owned by someone else (visible under org access) is ignored."""
1447
+
1448
+ @pytest.fixture
1449
+ def patched_client_class(self, monkeypatch):
1450
+ from metafold.simulation import compression_simulation
1451
+ fake_class = MagicMock()
1452
+ monkeypatch.setattr(compression_simulation, "MetafoldClient", fake_class)
1453
+ return fake_class
1454
+
1455
+ def _project(self, pid, name, user):
1456
+ p = MagicMock()
1457
+ p.id = pid
1458
+ p.name = name
1459
+ p.user = user
1460
+ return p
1461
+
1462
+ def test_ignores_same_name_owned_by_other_user(self, patched_client_class):
1463
+ client = patched_client_class.return_value
1464
+ # A defensive belt-and-suspenders case: even if the API returned another
1465
+ # user's project, the Python filter drops it and we create a new one.
1466
+ client.projects.list.return_value = [self._project("other-pid", "shared", "userB")]
1467
+ client.projects.create.return_value = self._project("new-pid", "shared", "userA")
1468
+
1469
+ pid = CompressionSimulation.find_or_create_project(
1470
+ "shared", access_token="tok", base_url="http://x/", user="userA",
1471
+ )
1472
+
1473
+ assert pid == "new-pid"
1474
+ client.projects.create.assert_called_once()
1475
+
1476
+ def test_reuses_callers_own_project(self, patched_client_class):
1477
+ client = patched_client_class.return_value
1478
+ client.projects.list.return_value = [self._project("mine", "shared", "userA")]
1479
+
1480
+ pid = CompressionSimulation.find_or_create_project(
1481
+ "shared", access_token="tok", base_url="http://x/", user="userA",
1482
+ cancel_existing_workflows=False,
1483
+ )
1484
+
1485
+ assert pid == "mine"
1486
+ client.projects.create.assert_not_called()
1487
+
1488
+ def test_scopes_query_to_user(self, patched_client_class):
1489
+ client = patched_client_class.return_value
1490
+ client.projects.list.return_value = []
1491
+ client.projects.create.return_value = self._project("new-pid", "shared", "userA")
1492
+
1493
+ CompressionSimulation.find_or_create_project(
1494
+ "shared", access_token="tok", base_url="http://x/", user="userA",
1495
+ )
1496
+
1497
+ q = client.projects.list.call_args.kwargs["q"]
1498
+ assert 'name:"shared"' in q
1499
+ assert 'user:"userA"' in q
1500
+
1501
+ def test_no_user_keeps_unscoped_behavior(self, patched_client_class):
1502
+ client = patched_client_class.return_value
1503
+ client.projects.list.return_value = [self._project("any-pid", "shared", "userB")]
1504
+
1505
+ pid = CompressionSimulation.find_or_create_project(
1506
+ "shared", access_token="tok", base_url="http://x/",
1507
+ cancel_existing_workflows=False,
1508
+ )
1509
+
1510
+ assert pid == "any-pid"
1511
+ q = client.projects.list.call_args.kwargs["q"]
1512
+ assert "user:" not in q
File without changes
File without changes
File without changes