contentstack-management 1.9.0__tar.gz → 1.10.0__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 (83) hide show
  1. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/PKG-INFO +1 -1
  2. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/__init__.py +22 -2
  3. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/contentstack.py +18 -8
  4. contentstack_management-1.10.0/contentstack_management/endpoint.py +180 -0
  5. contentstack_management-1.10.0/contentstack_management/region_refresh.py +81 -0
  6. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/PKG-INFO +1 -1
  7. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/SOURCES.txt +2 -0
  8. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/setup.py +17 -0
  9. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/LICENSE +0 -0
  10. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/README.md +0 -0
  11. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/_api_client.py +0 -0
  12. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/_constant.py +0 -0
  13. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/_errors.py +0 -0
  14. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/_messages.py +0 -0
  15. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/aliases/__init__.py +0 -0
  16. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/aliases/aliases.py +0 -0
  17. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/assets/__init__.py +0 -0
  18. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/assets/assets.py +0 -0
  19. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/auditlogs/__init__.py +0 -0
  20. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/auditlogs/auditlog.py +0 -0
  21. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/branches/__init__.py +0 -0
  22. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/branches/branches.py +0 -0
  23. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/bulk_operations/__init__.py +0 -0
  24. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/bulk_operations/bulk_operation.py +0 -0
  25. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/common.py +0 -0
  26. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/content_types/__init__.py +0 -0
  27. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/content_types/content_type.py +0 -0
  28. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/delivery_token/__init__.py +0 -0
  29. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/delivery_token/delivery_token.py +0 -0
  30. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/entries/__init__.py +0 -0
  31. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/entries/entry.py +0 -0
  32. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/entry_variants/__init__.py +0 -0
  33. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/entry_variants/entry_variants.py +0 -0
  34. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/environments/__init__.py +0 -0
  35. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/environments/environment.py +0 -0
  36. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/extensions/__init__.py +0 -0
  37. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/extensions/extension.py +0 -0
  38. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/global_fields/__init__.py +0 -0
  39. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/global_fields/global_fields.py +0 -0
  40. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/labels/__init__.py +0 -0
  41. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/labels/label.py +0 -0
  42. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/locale/__init__.py +0 -0
  43. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/locale/locale.py +0 -0
  44. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/management_token/__init__.py +0 -0
  45. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/management_token/management_token.py +0 -0
  46. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/metadata/__init__.py +0 -0
  47. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/metadata/metadata.py +0 -0
  48. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/oauth/__init__.py +0 -0
  49. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/oauth/oauth_handler.py +0 -0
  50. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/oauth/oauth_interceptor.py +0 -0
  51. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/organizations/__init__.py +0 -0
  52. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/organizations/organization.py +0 -0
  53. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/publish_queue/__init__.py +0 -0
  54. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/publish_queue/publish_queue.py +0 -0
  55. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/release_items/__init__.py +0 -0
  56. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/release_items/release_item.py +0 -0
  57. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/releases/__init__.py +0 -0
  58. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/releases/release.py +0 -0
  59. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/roles/__init__.py +0 -0
  60. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/roles/roles.py +0 -0
  61. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/stack/__init__.py +0 -0
  62. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/stack/stack.py +0 -0
  63. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/taxonomies/__init__.py +0 -0
  64. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/taxonomies/taxonomy.py +0 -0
  65. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/terms/__init__.py +0 -0
  66. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/terms/terms.py +0 -0
  67. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/user_session/__init__.py +0 -0
  68. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/user_session/user_session.py +0 -0
  69. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/users/__init__.py +0 -0
  70. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/users/user.py +0 -0
  71. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/variant_group/__init__.py +0 -0
  72. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/variant_group/variant_group.py +0 -0
  73. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/variants/__init__.py +0 -0
  74. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/variants/variants.py +0 -0
  75. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/webhooks/__init__.py +0 -0
  76. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/webhooks/webhook.py +0 -0
  77. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/workflows/__init__.py +0 -0
  78. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management/workflows/workflows.py +0 -0
  79. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/dependency_links.txt +0 -0
  80. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/requires.txt +0 -0
  81. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/contentstack_management.egg-info/top_level.txt +0 -0
  82. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/setup.cfg +0 -0
  83. {contentstack_management-1.9.0 → contentstack_management-1.10.0}/tests/test_contentstack.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contentstack-management
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Contentstack API Client Library for Python
5
5
  Home-page: https://github.com/contentstack/contentstack-management-python
6
6
  Author: dev-ex
@@ -18,6 +18,7 @@ from .environments.environment import Environment
18
18
  from .entries.entry import Entry
19
19
  from .entry_variants.entry_variants import EntryVariants
20
20
  from .contentstack import Client, Region
21
+ from .endpoint import Endpoint
21
22
  from ._api_client import _APIClient
22
23
  from .common import Parameter
23
24
  from ._errors import ArgumentException
@@ -36,11 +37,13 @@ from .variant_group.variant_group import VariantGroup
36
37
  from .variants.variants import Variants
37
38
  from .oauth.oauth_handler import OAuthHandler
38
39
  from .oauth.oauth_interceptor import OAuthInterceptor
40
+ from .region_refresh import refresh_regions
39
41
 
40
42
 
41
43
  __all__ = (
42
44
  "Client",
43
45
  "Region",
46
+ "Endpoint",
44
47
  "_APIClient",
45
48
  "Parameter",
46
49
  "ArgumentException",
@@ -75,14 +78,31 @@ __all__ = (
75
78
  "VariantGroup",
76
79
  "Variants",
77
80
  "OAuthHandler",
78
- "OAuthInterceptor"
81
+ "OAuthInterceptor",
82
+ "refresh_regions",
79
83
  )
80
84
 
85
+ def get_contentstack_endpoint(region='us', service='', omit_https=False):
86
+ """
87
+ Resolve a Contentstack service endpoint URL for a given region.
88
+
89
+ Proxy to :class:`Endpoint.get_contentstack_endpoint` for convenience —
90
+ mirrors ``Contentstack::getContentstackEndpoint()`` in the PHP SDK.
91
+
92
+ :param region: Region ID or alias ('us', 'eu', 'azure-na', 'gcp-eu', ...).
93
+ :param service: Service key ('contentDelivery', 'contentManagement', ...).
94
+ When empty, returns a dict of all endpoints for the region.
95
+ :param omit_https: When True, strips 'https://' from the returned URL(s).
96
+ :returns: str when service is provided, dict[str,str] otherwise.
97
+ """
98
+ return Endpoint.get_contentstack_endpoint(region, service, omit_https)
99
+
100
+
81
101
  __title__ = 'contentstack-management-python'
82
102
  __author__ = 'dev-ex'
83
103
  __status__ = 'debug'
84
104
  __region__ = 'na'
85
- __version__ = '1.9.0'
105
+ __version__ = '1.10.0'
86
106
  __host__ = 'api.contentstack.io'
87
107
  __protocol__ = 'https://'
88
108
  __api_version__ = 'v3'
@@ -2,6 +2,7 @@ from enum import Enum
2
2
  import os
3
3
  import pyotp
4
4
  from ._api_client import _APIClient
5
+ from .endpoint import Endpoint
5
6
  from contentstack_management.organizations import organization
6
7
  from contentstack_management.stack import stack
7
8
  from contentstack_management.user_session import user_session
@@ -37,16 +38,25 @@ class Client:
37
38
  authtoken: str = None , management_token=None, headers: dict = None,
38
39
  region: Region = Region.US.value, version='v3', timeout=2, max_retries: int = 18, early_access: list = None,
39
40
  oauth_config: dict = None, **kwargs):
40
- self.endpoint = 'https://api.contentstack.io/v3/'
41
-
42
- if region is not None and region is not Region.US.value:
43
- if host is not None and host != 'api.contentstack.io':
41
+ _DEFAULT_HOST = 'api.contentstack.io'
42
+ self.endpoint = f'{scheme}{_DEFAULT_HOST}/{version}/'
43
+
44
+ if host is None or host == _DEFAULT_HOST:
45
+ # No custom host — resolve via Endpoint (regions.json-driven)
46
+ try:
47
+ base = Endpoint.get_contentstack_endpoint(
48
+ region or 'us', 'contentManagement', omit_https=True)
49
+ self.endpoint = f'{scheme}{base}/{version}/'
50
+ except (ValueError, RuntimeError):
51
+ # Unknown/custom region string — fall back to legacy pattern
52
+ if region and region != Region.US.value:
53
+ self.endpoint = f'{scheme}{region}-api.contentstack.com/{version}/'
54
+ else:
55
+ # Explicit custom host always wins; apply region prefix when non-US
56
+ if region and region != Region.US.value:
44
57
  self.endpoint = f'{scheme}{region}-api.{host}/{version}/'
45
58
  else:
46
- host = 'api.contentstack.com'
47
- self.endpoint = f'{scheme}{region}-{host}/{version}/'
48
- elif host is not None and host != 'api.contentstack.io':
49
- self.endpoint = f'{scheme}{host}/{version}/'
59
+ self.endpoint = f'{scheme}{host}/{version}/'
50
60
  if headers is None:
51
61
  headers = {}
52
62
  if early_access is not None:
@@ -0,0 +1,180 @@
1
+ """
2
+ Endpoint — Contentstack region-to-URL resolver for the Management SDK.
3
+
4
+ Resolves Contentstack service endpoint URLs for any supported region.
5
+ Region data is loaded from contentstack_management/data/regions.json (bundled)
6
+ and cached in-memory for the lifetime of the process. When the bundled file is
7
+ absent the class attempts a live download from the Contentstack CDN so the
8
+ SDK continues to work even when the file was not created during installation.
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import re
14
+
15
+ REGIONS_URL = 'https://artifacts.contentstack.com/regions.json'
16
+
17
+
18
+ class Endpoint:
19
+ """
20
+ Resolves Contentstack service endpoint URLs for any supported region.
21
+
22
+ Usage::
23
+
24
+ from contentstack_management.endpoint import Endpoint
25
+
26
+ # Single service URL
27
+ url = Endpoint.get_contentstack_endpoint('eu', 'contentManagement')
28
+ # 'https://eu-api.contentstack.com'
29
+
30
+ # All services for a region
31
+ endpoints = Endpoint.get_contentstack_endpoint('azure-na')
32
+ # {'contentDelivery': '...', 'contentManagement': '...', ...}
33
+
34
+ # Strip scheme (useful when building endpoint strings manually)
35
+ host = Endpoint.get_contentstack_endpoint('gcp-eu', 'contentManagement', omit_https=True)
36
+ # 'gcp-eu-api.contentstack.com'
37
+ """
38
+
39
+ _regions_data = None # in-memory cache — shared across all instances
40
+
41
+ @staticmethod
42
+ def get_contentstack_endpoint(region='us', service='', omit_https=False):
43
+ """
44
+ Resolve a Contentstack service endpoint URL for a given region.
45
+
46
+ :param region: Region ID or alias ('us', 'eu', 'azure-na', 'gcp-eu', etc.).
47
+ Defaults to 'us' (AWS North America).
48
+ :param service: Service key ('contentDelivery', 'contentManagement', ...).
49
+ When empty, returns a dict of all endpoints for the region.
50
+ :param omit_https: When True, strips 'https://' prefix from returned URL(s).
51
+ :returns: str when service is provided, dict[str,str] otherwise.
52
+ :raises ValueError: When region is empty, unknown, or service is not found.
53
+ :raises RuntimeError: When regions.json cannot be read or parsed.
54
+ """
55
+ if not region:
56
+ raise ValueError('Empty region provided. Please put valid region.')
57
+
58
+ data = Endpoint._load_regions()
59
+ normalized = region.strip().lower()
60
+ region_row = Endpoint._find_region(data['regions'], normalized)
61
+
62
+ if region_row is None:
63
+ raise ValueError(f'Invalid region: {region}')
64
+
65
+ if service:
66
+ if service not in region_row['endpoints']:
67
+ raise ValueError(
68
+ f'Service "{service}" not found for region "{region_row["id"]}"'
69
+ )
70
+ url = region_row['endpoints'][service]
71
+ return Endpoint._strip_https(url) if omit_https else url
72
+
73
+ endpoints = region_row['endpoints']
74
+ if omit_https:
75
+ return {k: Endpoint._strip_https(v) for k, v in endpoints.items()}
76
+ return dict(endpoints)
77
+
78
+ @staticmethod
79
+ def _load_regions():
80
+ """
81
+ Load and cache regions.json.
82
+
83
+ Resolution order:
84
+ 1. In-memory static cache (zero I/O after first call)
85
+ 2. contentstack_management/data/regions.json on disk (written by download script)
86
+ 3. Live download from artifacts.contentstack.com (fallback)
87
+ """
88
+ if Endpoint._regions_data is not None:
89
+ return Endpoint._regions_data
90
+
91
+ data_dir = os.path.join(os.path.dirname(__file__), 'data')
92
+ path = os.path.join(data_dir, 'regions.json')
93
+
94
+ if not os.path.exists(path):
95
+ Endpoint._download_and_save(path)
96
+
97
+ if not os.path.exists(path):
98
+ raise RuntimeError(
99
+ 'contentstack-management: regions.json not found and could not be downloaded. '
100
+ 'Run "python scripts/download_regions.py" and ensure network access.'
101
+ )
102
+
103
+ try:
104
+ with open(path, 'r', encoding='utf-8') as f:
105
+ decoded = json.load(f)
106
+ except (OSError, json.JSONDecodeError) as exc:
107
+ raise RuntimeError(
108
+ f'contentstack-management: Could not read or parse regions.json: {exc}. '
109
+ 'Run "python scripts/download_regions.py" to re-download it.'
110
+ ) from exc
111
+
112
+ if not isinstance(decoded, dict) or 'regions' not in decoded:
113
+ raise RuntimeError(
114
+ 'contentstack-management: regions.json is corrupt. '
115
+ 'Run "python scripts/download_regions.py" to re-download it.'
116
+ )
117
+
118
+ Endpoint._regions_data = decoded
119
+ return Endpoint._regions_data
120
+
121
+ @staticmethod
122
+ def _download_and_save(dest):
123
+ """
124
+ Download regions.json from the Contentstack CDN and save to disk.
125
+ Uses the requests library (already an SDK dependency).
126
+ Silent on failure — the caller decides whether a missing file is fatal.
127
+
128
+ :param dest: Absolute path to write the file to.
129
+ """
130
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
131
+
132
+ try:
133
+ import requests
134
+ response = requests.get(REGIONS_URL, timeout=30)
135
+ response.raise_for_status()
136
+ data = response.text
137
+ except Exception: # noqa: BLE001
138
+ return
139
+
140
+ try:
141
+ decoded = json.loads(data)
142
+ except json.JSONDecodeError:
143
+ return
144
+
145
+ if isinstance(decoded, dict) and 'regions' in decoded:
146
+ try:
147
+ with open(dest, 'w', encoding='utf-8') as f:
148
+ f.write(data)
149
+ except OSError:
150
+ pass
151
+
152
+ @staticmethod
153
+ def _find_region(regions, input_str):
154
+ """
155
+ Find a region entry by its id or any alias (case-insensitive).
156
+
157
+ Two-pass: exact id match first, then alias[] scan — mirrors PHP implementation.
158
+
159
+ :param regions: list of region dicts from regions.json
160
+ :param input_str: already-lowercased input
161
+ :returns: region dict or None
162
+ """
163
+ for row in regions:
164
+ if row['id'] == input_str:
165
+ return row
166
+ for row in regions:
167
+ for alias in row.get('alias', []):
168
+ if alias.lower() == input_str:
169
+ return row
170
+ return None
171
+
172
+ @staticmethod
173
+ def _strip_https(url):
174
+ """Strip the https:// (or http://) scheme from a URL string."""
175
+ return re.sub(r'^https?://', '', url)
176
+
177
+ @staticmethod
178
+ def reset_cache():
179
+ """Reset the internal region cache. Intended for testing only."""
180
+ Endpoint._regions_data = None
@@ -0,0 +1,81 @@
1
+ """
2
+ Utility to pull the latest regions.json from the Contentstack CDN and
3
+ overwrite the bundled copy at contentstack_management/data/regions.json.
4
+
5
+ Exposed as a package-level function so tooling and CI pipelines can call it
6
+ programmatically instead of invoking the script directly:
7
+
8
+ from contentstack_management import refresh_regions
9
+ refresh_regions()
10
+ """
11
+
12
+ import json
13
+ import os
14
+ import sys
15
+ import urllib.request
16
+
17
+ _REGIONS_URL = "https://artifacts.contentstack.com/regions.json"
18
+ _ASSET_PATH = os.path.join(os.path.dirname(__file__), "data", "regions.json")
19
+
20
+
21
+ def refresh_regions(
22
+ url: str = _REGIONS_URL,
23
+ dest: str = _ASSET_PATH,
24
+ *,
25
+ timeout: int = 30,
26
+ silent: bool = False,
27
+ ) -> dict:
28
+ """
29
+ Download the latest regions manifest from the Contentstack CDN and write
30
+ it to the bundled data file so all consumers get the update.
31
+
32
+ @param url - URL to fetch regions.json from (defaults to Contentstack CDN)
33
+ @param dest - Destination file path (defaults to contentstack_management/data/regions.json)
34
+ @param timeout - HTTP request timeout in seconds
35
+ @param silent - Suppress progress output when True
36
+ @returns The parsed regions dict on success
37
+ @raises RuntimeError on download failure, invalid JSON, or unexpected schema
38
+ """
39
+ dest = os.path.normpath(dest)
40
+
41
+ if not silent:
42
+ print(f"Fetching {url} ...")
43
+
44
+ try:
45
+ with urllib.request.urlopen(url, timeout=timeout) as resp:
46
+ data = resp.read().decode("utf-8")
47
+ except Exception as exc:
48
+ raise RuntimeError(f"Could not download regions.json: {exc}") from exc
49
+
50
+ try:
51
+ decoded = json.loads(data)
52
+ except json.JSONDecodeError as exc:
53
+ raise RuntimeError(f"Downloaded content is not valid JSON: {exc}") from exc
54
+
55
+ if not isinstance(decoded, dict) or "regions" not in decoded:
56
+ raise RuntimeError("Downloaded JSON does not contain a 'regions' key.")
57
+
58
+ os.makedirs(os.path.dirname(dest), exist_ok=True)
59
+ with open(dest, "w", encoding="utf-8") as fh:
60
+ json.dump(decoded, fh, indent=2, ensure_ascii=False)
61
+ fh.write("\n")
62
+
63
+ region_count = len(decoded["regions"])
64
+ if not silent:
65
+ print(f"OK: Wrote {region_count} regions to {dest}")
66
+
67
+ return decoded
68
+
69
+
70
+ def _cli_main() -> int:
71
+ """Entry point kept for backward compatibility with the scripts/ invocation."""
72
+ try:
73
+ refresh_regions()
74
+ return 0
75
+ except RuntimeError as exc:
76
+ print(f"ERROR: {exc}", file=sys.stderr)
77
+ return 1
78
+
79
+
80
+ if __name__ == "__main__":
81
+ sys.exit(_cli_main())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: contentstack-management
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Contentstack API Client Library for Python
5
5
  Home-page: https://github.com/contentstack/contentstack-management-python
6
6
  Author: dev-ex
@@ -8,6 +8,8 @@ contentstack_management/_errors.py
8
8
  contentstack_management/_messages.py
9
9
  contentstack_management/common.py
10
10
  contentstack_management/contentstack.py
11
+ contentstack_management/endpoint.py
12
+ contentstack_management/region_refresh.py
11
13
  contentstack_management.egg-info/PKG-INFO
12
14
  contentstack_management.egg-info/SOURCES.txt
13
15
  contentstack_management.egg-info/dependency_links.txt
@@ -1,6 +1,21 @@
1
1
  import os
2
2
  import re
3
+ import sys
3
4
  from setuptools import setup, find_packages
5
+ from setuptools.command.build_py import build_py
6
+
7
+
8
+ class BuildPyWithRegions(build_py):
9
+ """Fetch latest regions.json from Contentstack CDN before packaging."""
10
+
11
+ def run(self):
12
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
13
+ try:
14
+ from contentstack_management.region_refresh import refresh_regions
15
+ refresh_regions()
16
+ except Exception as exc:
17
+ print(f"WARNING: Could not refresh regions.json: {exc}", file=sys.stderr)
18
+ super().run()
4
19
 
5
20
  with open("README.md", "r") as f:
6
21
  long_description = f.read()
@@ -33,9 +48,11 @@ def get_author_email(package):
33
48
  init_py, re.MULTILINE).group(1)
34
49
 
35
50
  setup(
51
+ cmdclass={"build_py": BuildPyWithRegions},
36
52
  name="contentstack-management",
37
53
  version=get_version(package),
38
54
  packages=find_packages(exclude=['tests']),
55
+ package_data={'contentstack_management': ['data/regions.json']},
39
56
  py_modules=['_api_client', 'contentstack','common','_errors','_constant'],
40
57
  description="Contentstack API Client Library for Python",
41
58
  long_description=long_description,