From 1363ec7117eaa7a3501fb01f6f5b3d944a1af237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gemma=20Torell=C3=B3?= Date: Thu, 23 Jul 2026 11:56:09 +0200 Subject: [PATCH] feat(ads): deprecated `ads` resource forwarding to the split resources Add a manual AdsResource that forwards each call to the right resource (ad_campaigns, conversions, ...) and emits a DeprecationWarning, so existing `client.ads.*` code keeps working after the #1777 tag split. Co-Authored-By: Claude Opus 4.8 --- src/late/client/late_client.py | 2 ++ src/late/resources/__init__.py | 2 ++ src/late/resources/ads.py | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 src/late/resources/ads.py diff --git a/src/late/client/late_client.py b/src/late/client/late_client.py index adc8a2bd..90c99a12 100644 --- a/src/late/client/late_client.py +++ b/src/late/client/late_client.py @@ -17,6 +17,7 @@ AdCampaignsResource, AdCreativesResource, AdInsightsResource, + AdsResource, AdTargetingResource, AnalyticsResource, ApiKeysResource, @@ -146,6 +147,7 @@ def __init__( self.ad_creatives = AdCreativesResource(self) self.ad_insights = AdInsightsResource(self) self.ad_targeting = AdTargetingResource(self) + self.ads = AdsResource(self) self.analytics = AnalyticsResource(self) self.api_keys = ApiKeysResource(self) self.broadcasts = BroadcastsResource(self) diff --git a/src/late/resources/__init__.py b/src/late/resources/__init__.py index 0e58f44c..55c08c40 100644 --- a/src/late/resources/__init__.py +++ b/src/late/resources/__init__.py @@ -55,6 +55,7 @@ from ._generated.whatsapp_templates import WhatsappTemplatesResource from ._generated.workflows import WorkflowsResource from .accounts import AccountsResource +from .ads import AdsResource from .analytics import AnalyticsResource from .media import MediaResource from .posts import PostsResource @@ -73,6 +74,7 @@ "AdCreativesResource", "AdInsightsResource", "AdTargetingResource", + "AdsResource", "AnalyticsResource", "ApiKeysResource", "BroadcastsResource", diff --git a/src/late/resources/ads.py b/src/late/resources/ads.py new file mode 100644 index 00000000..c43a30fc --- /dev/null +++ b/src/late/resources/ads.py @@ -0,0 +1,64 @@ +""" +Backward-compat `ads` resource. + +The `ads` API was split into capability-specific resources. This alias forwards +each call to the right resource and emits a DeprecationWarning, so existing code +keeps working. It will be removed in a future major version. + +This is a hand-written (manual) resource; it overrides any auto-generated +`_generated/ads.py`. Do not replace it with a generated file. +""" + +from __future__ import annotations + +import warnings +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from ..client.base import BaseClient + +# Resources the old `ads` namespace was split into, checked in this order when +# resolving a forwarded method. +_AD_RESOURCES = [ + "ad_campaigns", + "ad_accounts", + "ad_creatives", + "ad_audiences", + "ad_targeting", + "ad_insights", + "conversions", + "messaging_ads", + "reach_and_frequency", + "lead_gen", + "tracking_tags", +] + + +class AdsResource: + """ + Deprecated. The `ads` resource was split into: ad_campaigns, ad_accounts, + ad_creatives, ad_audiences, ad_targeting, ad_insights, conversions, + messaging_ads, reach_and_frequency, lead_gen, tracking_tags. + + Calls still work (forwarded to the right resource) but emit a + DeprecationWarning. Migrate to the resource named in the warning. + """ + + def __init__(self, client: BaseClient) -> None: + self._client = client + + def __getattr__(self, name: str) -> Any: + # __getattr__ only runs when normal lookup fails, so `_client` (set in + # __init__) is resolved normally and never recurses here. + for resource_name in _AD_RESOURCES: + resource = getattr(self._client, resource_name, None) + if resource is not None and hasattr(resource, name): + warnings.warn( + f"`ads.{name}()` is deprecated: the `ads` resource was split. " + f"Use `{resource_name}.{name}()` instead. " + f"`ads` will be removed in a future major version.", + DeprecationWarning, + stacklevel=2, + ) + return getattr(resource, name) + raise AttributeError(f"'AdsResource' object has no attribute '{name}'")