4343 DEFAULT_MAX_RETRIES ,
4444 DEFAULT_RENDER_TIMEOUT ,
4545 DEFAULT_TIMEOUT ,
46+ EARLIER_PHOTO_MOCKUPS_PATH ,
47+ EARLIER_PSD_MOCKUPS_PATH ,
48+ PHOTO_MOCKUPS_PATH ,
49+ PSD_MOCKUPS_PATH ,
4650 AsyncTransport ,
4751)
4852from ._public_contract import public_2d_render_targets
8589class _AsyncPsdMockupsResource :
8690 """Async PSD mockup template operations (list, get, update, delete)."""
8791
88- def __init__ (self , transport : AsyncTransport ) -> None :
92+ def __init__ (self , transport : AsyncTransport , base : str = PSD_MOCKUPS_PATH ) -> None :
8993 self ._transport = transport
94+ self ._base = base
9095
9196 async def list (
9297 self ,
@@ -116,7 +121,7 @@ async def list(
116121 """
117122 resp = await self ._transport .request (
118123 "GET" ,
119- "/api/v1/psd-mockups" ,
124+ self . _base ,
120125 params = {
121126 "limit" : limit ,
122127 "offset" : offset ,
@@ -142,7 +147,7 @@ async def get(self, uuid: str) -> Mockup:
142147 Raises:
143148 NotFoundError: If the mockup does not exist.
144149 """
145- resp = await self ._transport .request ("GET" , f"/api/v1/psd-mockups /{ uuid } " )
150+ resp = await self ._transport .request ("GET" , f"{ self . _base } /{ uuid } " )
146151 payload = resp .json ()
147152 return Mockup .model_validate ({** payload ["data" ], "warnings" : payload .get ("warnings" ) or []})
148153
@@ -159,9 +164,7 @@ async def update(self, uuid: str, *, name: str) -> Mockup:
159164 Raises:
160165 NotFoundError: If the mockup does not exist.
161166 """
162- resp = await self ._transport .request (
163- "PATCH" , f"/api/v1/psd-mockups/{ uuid } " , json = {"name" : name }
164- )
167+ resp = await self ._transport .request ("PATCH" , f"{ self ._base } /{ uuid } " , json = {"name" : name })
165168 payload = resp .json ()
166169 return Mockup .model_validate ({** payload ["data" ], "warnings" : payload .get ("warnings" ) or []})
167170
@@ -174,7 +177,7 @@ async def delete(self, uuid: str) -> None:
174177 Raises:
175178 NotFoundError: If the mockup does not exist.
176179 """
177- await self ._transport .request ("DELETE" , f"/api/v1/psd-mockups /{ uuid } " )
180+ await self ._transport .request ("DELETE" , f"{ self . _base } /{ uuid } " )
178181
179182
180183class _AsyncRendersResource :
@@ -481,9 +484,11 @@ async def create(
481484 event_naming: Which spelling of the photo-mockup events this endpoint
482485 receives: ``"current"`` (``photo_mockup.*``,
483486 ``photo_mockup_render.*``) or ``"legacy"`` (``2d_mockup.*``,
484- ``2d_render.*``). Left out, the API pins a new endpoint to
485- ``"current"``; pass ``"legacy"`` for a handler that still reads
486- the older names.
487+ ``2d_render.*``). Left out, the API reads the spelling of
488+ ``events``: a list written in the earlier names pins the
489+ endpoint to ``"legacy"``, a list written in the current names
490+ pins it to ``"current"``, and an empty or mixed list pins it to
491+ ``"legacy"``. Pass the value outright to decide it yourself.
487492 """
488493 # API field is `event_types` (empty list = subscribe to all events).
489494 # NOTE: the create endpoint has no `enabled` field (it is update-only).
@@ -601,8 +606,9 @@ async def replay_failed(self, uuid: str) -> None:
601606class _AsyncPhotoMockupsResource :
602607 """Async photo mockup operations (create, render, list, get, delete, print areas)."""
603608
604- def __init__ (self , transport : AsyncTransport ) -> None :
609+ def __init__ (self , transport : AsyncTransport , base : str = PHOTO_MOCKUPS_PATH ) -> None :
605610 self ._transport = transport
611+ self ._base = base
606612
607613 async def create (
608614 self ,
@@ -658,7 +664,7 @@ async def create(
658664
659665 resp = await self ._transport .request (
660666 "POST" ,
661- "/api/v1/photo-mockups" ,
667+ self . _base ,
662668 json = body ,
663669 headers = {"Idempotency-Key" : idempotency_key },
664670 )
@@ -722,7 +728,7 @@ async def update_2d_print_areas(
722728 """
723729 resp = await self ._transport .request (
724730 "PUT" ,
725- f"/api/v1/photo-mockups /{ mockup_id } /print-areas" ,
731+ f"{ self . _base } /{ mockup_id } /print-areas" ,
726732 json = {"print_areas" : print_areas },
727733 )
728734 return PhotoMockupPrintAreasUpdate .model_validate (resp .json ()["data" ])
@@ -793,7 +799,7 @@ async def render(
793799
794800 resp = await self ._transport .request (
795801 "POST" ,
796- f"/api/v1/photo-mockups /{ mockup_uuid } /render" ,
802+ f"{ self . _base } /{ mockup_uuid } /render" ,
797803 json = body ,
798804 timeout = self ._transport ._render_timeout ,
799805 )
@@ -814,7 +820,7 @@ async def list(
814820 """List your photo mockups (free; zero credits)."""
815821 resp = await self ._transport .request (
816822 "GET" ,
817- "/api/v1/photo-mockups" ,
823+ self . _base ,
818824 params = {
819825 "limit" : limit ,
820826 "offset" : offset ,
@@ -835,7 +841,7 @@ async def get(self, mockup_id: str) -> PhotoMockup:
835841 Raises:
836842 NotFoundError: If the photo mockup does not exist.
837843 """
838- resp = await self ._transport .request ("GET" , f"/api/v1/photo-mockups /{ mockup_id } " )
844+ resp = await self ._transport .request ("GET" , f"{ self . _base } /{ mockup_id } " )
839845 return PhotoMockup .model_validate (resp .json ()["data" ])
840846
841847 async def delete (self , mockup_id : str ) -> None :
@@ -844,7 +850,7 @@ async def delete(self, mockup_id: str) -> None:
844850 Raises:
845851 NotFoundError: If the photo mockup does not exist.
846852 """
847- await self ._transport .request ("DELETE" , f"/api/v1/photo-mockups /{ mockup_id } " )
853+ await self ._transport .request ("DELETE" , f"{ self . _base } /{ mockup_id } " )
848854
849855
850856class _AsyncImagesResource :
@@ -1077,35 +1083,92 @@ def __init__(
10771083 self .packages = _AsyncPackagesResource (self ._transport )
10781084 self .webhook_endpoints = _AsyncWebhookEndpointsResource (self ._transport )
10791085
1086+ # Same resources, pinned to the endpoints the earlier accessors have
1087+ # always called. Kept as their own objects so ``photo_mockups`` and
1088+ # ``psd_mockups`` can move on without moving the earlier callers.
1089+ self ._earlier_photo_mockups = _AsyncPhotoMockupsResource (
1090+ self ._transport , base = EARLIER_PHOTO_MOCKUPS_PATH
1091+ )
1092+ self ._earlier_psd_mockups = _AsyncPsdMockupsResource (
1093+ self ._transport , base = EARLIER_PSD_MOCKUPS_PATH
1094+ )
1095+
10801096 @property
10811097 def ai (self ) -> _AsyncPhotoMockupsResource :
1082- """Earlier name of :attr:`photo_mockups`; emits a ``DeprecationWarning``."""
1098+ """Earlier name of :attr:`photo_mockups`; emits a ``DeprecationWarning``.
1099+
1100+ Calls made through this name keep going to the endpoint they have
1101+ always gone to, so a job opened here is the job this caller has always
1102+ opened. Use :attr:`photo_mockups` for the current endpoint.
1103+ """
10831104 warnings .warn (
10841105 "AsyncSudoMock.ai is deprecated; use AsyncSudoMock.photo_mockups instead." ,
10851106 DeprecationWarning ,
10861107 stacklevel = 2 ,
10871108 )
1088- return self .photo_mockups
1109+ return self ._earlier_photo_mockups
10891110
10901111 @ai .setter
10911112 def ai (self , value : object ) -> None :
1092- """Assignment still works so existing test doubles keep running."""
1093- self .photo_mockups = value # type: ignore[assignment]
1113+ """Assignment still works so existing test doubles keep running.
1114+
1115+ What is written here is what this name reads back, and only this name:
1116+ :attr:`photo_mockups` is left where it is. A caller that saves this
1117+ accessor, swaps in a double and writes the saved value back therefore
1118+ puts both accessors exactly where they started.
1119+ """
1120+ self ._earlier_photo_mockups = value # type: ignore[assignment]
1121+
1122+ @ai .deleter
1123+ def ai (self ) -> None :
1124+ """``del client.ai`` drops the override and restores the default.
1125+
1126+ The accessor goes back to the one the client was built with, still
1127+ pinned to the endpoint this name has always called.
1128+ ``mock.patch.object`` deletes the attribute when its block ends, so
1129+ this is the way back from a patched double.
1130+ """
1131+ self ._earlier_photo_mockups = _AsyncPhotoMockupsResource (
1132+ self ._transport , base = EARLIER_PHOTO_MOCKUPS_PATH
1133+ )
10941134
10951135 @property
10961136 def mockups (self ) -> _AsyncPsdMockupsResource :
1097- """Earlier name of :attr:`psd_mockups`; emits a ``DeprecationWarning``."""
1137+ """Earlier name of :attr:`psd_mockups`; emits a ``DeprecationWarning``.
1138+
1139+ Calls made through this name keep going to the endpoint they have
1140+ always gone to. Use :attr:`psd_mockups` for the current endpoint.
1141+ """
10981142 warnings .warn (
10991143 "AsyncSudoMock.mockups is deprecated; use AsyncSudoMock.psd_mockups instead." ,
11001144 DeprecationWarning ,
11011145 stacklevel = 2 ,
11021146 )
1103- return self .psd_mockups
1147+ return self ._earlier_psd_mockups
11041148
11051149 @mockups .setter
11061150 def mockups (self , value : object ) -> None :
1107- """Assignment still works so existing test doubles keep running."""
1108- self .psd_mockups = value # type: ignore[assignment]
1151+ """Assignment still works so existing test doubles keep running.
1152+
1153+ What is written here is what this name reads back, and only this name:
1154+ :attr:`psd_mockups` is left where it is. A caller that saves this
1155+ accessor, swaps in a double and writes the saved value back therefore
1156+ puts both accessors exactly where they started.
1157+ """
1158+ self ._earlier_psd_mockups = value # type: ignore[assignment]
1159+
1160+ @mockups .deleter
1161+ def mockups (self ) -> None :
1162+ """``del client.mockups`` drops the override and restores the default.
1163+
1164+ The accessor goes back to the one the client was built with, still
1165+ pinned to the endpoint this name has always called.
1166+ ``mock.patch.object`` deletes the attribute when its block ends, so
1167+ this is the way back from a patched double.
1168+ """
1169+ self ._earlier_psd_mockups = _AsyncPsdMockupsResource (
1170+ self ._transport , base = EARLIER_PSD_MOCKUPS_PATH
1171+ )
11091172
11101173 async def close (self ) -> None :
11111174 """Close the underlying HTTP connection pool."""
0 commit comments