Skip to content

Commit 3a8e161

Browse files
committed
Refactor error handling to use centralized error messages across multiple files.
1 parent f1518ca commit 3a8e161

File tree

12 files changed

+115
-52
lines changed

12 files changed

+115
-52
lines changed

contentstack/asset.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import logging
88
from urllib import parse
9+
from contentstack.error_messages import ErrorMessages
910

1011
class Asset:
1112
r"""`Asset` refer to all the media files (images, videos, PDFs, audio files, and so on)."""
@@ -15,7 +16,7 @@ def __init__(self, http_instance, uid=None, logger=None):
1516
self.asset_params = {}
1617
self.__uid = uid
1718
if self.__uid is None or self.__uid.strip() == 0:
18-
raise KeyError('Please provide valid uid')
19+
raise KeyError(ErrorMessages.INVALID_UID)
1920
self.base_url = f'{self.http_instance.endpoint}/assets/{self.__uid}'
2021
if 'environment' in self.http_instance.headers:
2122
self.asset_params['environment'] = self.http_instance.headers['environment']
@@ -68,7 +69,7 @@ def params(self, key, value):
6869
-----------------------------
6970
"""
7071
if None in (key, value) or not isinstance(key, str):
71-
raise KeyError('Kindly provide valid params')
72+
raise KeyError(ErrorMessages.INVALID_PARAMS)
7273
self.asset_params[key] = value
7374
return self
7475

contentstack/basequery.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import enum
22
import logging
3+
from contentstack.error_messages import ErrorMessages
34

45
class QueryOperation(enum.Enum):
56
"""
@@ -137,8 +138,10 @@ def param(self, key: str, value):
137138
138139
-----------------------------------
139140
"""
140-
if None in (key, value):
141-
raise KeyError('Invalid key or value')
141+
if key is None:
142+
raise KeyError(ErrorMessages.INVALID_KEY)
143+
if value is None:
144+
raise KeyError(ErrorMessages.INVALID_VALUE)
142145
self.query_params[key] = str(value)
143146
return self
144147

@@ -164,8 +167,10 @@ def query(self, key: str, value):
164167
Returns:
165168
self-- Class instance, So that method chaining can be performed
166169
"""
167-
if None in (key, value):
168-
raise KeyError('Invalid key or value')
170+
if key is None:
171+
raise KeyError(ErrorMessages.INVALID_KEY)
172+
if value is None:
173+
raise KeyError(ErrorMessages.INVALID_VALUE)
169174
self.parameters[key] = str(value)
170175
return self
171176

@@ -186,7 +191,7 @@ def remove_param(self, key: str):
186191
----------------------------------
187192
"""
188193
if key is None:
189-
raise ValueError('Kindly provide valid key')
194+
raise ValueError(ErrorMessages.INVALID_KEY)
190195
if key in self.query_params:
191196
self.query_params.pop(key, None)
192197
return self

contentstack/contenttype.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import json
1111
import logging
1212
from urllib import parse
13+
from contentstack.error_messages import ErrorMessages
1314

1415
from contentstack.entry import Entry
1516
from contentstack.query import Query
@@ -46,11 +47,9 @@ def entry(self, entry_uid: str):
4647
--------------------------------
4748
"""
4849
if self.__content_type_uid is None:
49-
raise PermissionError('Please provide valid content_type_uid')
50+
raise PermissionError(ErrorMessages.INVALID_CONTENT_TYPE_UID)
5051
if entry_uid is None:
51-
raise PermissionError(json.dumps({
52-
"message": 'Please provide valid entry uid',
53-
"message_detail": 'Entry UID can not be None'}))
52+
raise PermissionError(ErrorMessages.INVALID_UID)
5453
entry = Entry(self.http_instance,
5554
self.__content_type_uid, entry_uid=entry_uid)
5655
return entry
@@ -69,7 +68,7 @@ def query(self):
6968
------------------------------
7069
"""
7170
if self.__content_type_uid is None:
72-
raise PermissionError('Kindly provide content_type_uid')
71+
raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
7372
return Query(self.http_instance, self.__content_type_uid)
7473

7574
def fetch(self):
@@ -87,8 +86,7 @@ def fetch(self):
8786
------------------------------
8887
"""
8988
if self.__content_type_uid is None:
90-
raise KeyError(
91-
'content_type_uid can not be None to fetch contenttype')
89+
raise KeyError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
9290
self.local_param['environment'] = self.http_instance.headers['environment']
9391
uri = f'{self.http_instance.endpoint}/content_types/{self.__content_type_uid}'
9492
encoded_params = parse.urlencode(self.local_param)

contentstack/controller.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import requests
22
from requests.utils import guess_json_utf
3+
from contentstack.error_messages import ErrorMessages
34

45

56
class RequestError(Exception):
@@ -18,14 +19,14 @@ def get_request(session, url, headers, timeout):
1819
response.encoding = 'utf-8'
1920
except requests.exceptions.RequestException as e:
2021
error = {
21-
'error': f"Failed to connect to {url}: {str(e)}",
22+
'error': ErrorMessages.CONNECTION_FAILED.format(url=url, error=str(e)),
2223
'error_code': '400',
2324
'error_message': {str(e)}
2425
}
2526
raise RequestError(error)
2627
except Exception as e:
2728
error = {
28-
'error': f"An unexpected error while making request to {url}, '400', {str(e)}",
29+
'error': ErrorMessages.OPERATION_FAILED.format(url=url, error=str(e)),
2930
'error_code': '400',
3031
'error_message': {str(e)}
3132
}

contentstack/deep_merge_lp.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
from contentstack.error_messages import ErrorMessages
2+
13
class DeepMergeMixin:
24

35
def __init__(self, entry_response, lp_response):
46
if not isinstance(entry_response, list) or not isinstance(lp_response, list):
5-
raise TypeError("Both entry_response and lp_response must be lists of dictionaries")
7+
raise TypeError(ErrorMessages.INVALID_RESPONSE_TYPE)
68

79
self.entry_response = entry_response
810
self.lp_response = lp_response

contentstack/entry.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#min-similarity-lines=10
66
import logging
77
from urllib import parse
8+
from contentstack.error_messages import ErrorMessages
89

910
from contentstack.deep_merge_lp import DeepMergeMixin
1011
from contentstack.entryqueryable import EntryQueryable
@@ -49,7 +50,7 @@ def environment(self, environment):
4950
------------------------------
5051
"""
5152
if environment is None:
52-
raise KeyError('Kindly provide a valid environment')
53+
raise KeyError(ErrorMessages.INVALID_ENVIRONMENT)
5354
self.http_instance.headers['environment'] = environment
5455
return self
5556

@@ -72,7 +73,7 @@ def version(self, version):
7273
------------------------------
7374
"""
7475
if version is None:
75-
raise KeyError('Kindly provide a valid version')
76+
raise KeyError(ErrorMessages.INVALID_VERSION)
7677
self.entry_param['version'] = version
7778
return self
7879

@@ -94,7 +95,7 @@ def param(self, key, value):
9495
-----------------------------
9596
"""
9697
if None in (key, value) and not isinstance(key, str):
97-
raise ValueError('Kindly provide valid key and value arguments')
98+
raise ValueError(ErrorMessages.INVALID_KEY_VALUE_ARGS)
9899
self.entry_param[key] = value
99100
return self
100101

@@ -113,7 +114,7 @@ def include_fallback(self):
113114
>>> result = entry.fetch()
114115
----------------------------
115116
"""
116-
print('Requesting fallback....')
117+
print(ErrorMessages.REQUESTING_FALLBACK)
117118
self.entry_param['include_fallback'] = 'true'
118119
return self
119120

@@ -157,8 +158,7 @@ def __get_base_url(self, endpoint=''):
157158
if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
158159
self.http_instance.endpoint = endpoint
159160
if None in (self.http_instance, self.content_type_id, self.entry_uid):
160-
raise KeyError(
161-
'Provide valid http_instance, content_type_uid or entry_uid')
161+
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
162162
url = f'{self.http_instance.endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}'
163163
return url
164164

@@ -217,12 +217,12 @@ def _merged_response(self):
217217
if not isinstance(lp_entry, list):
218218
lp_entry = [lp_entry] # Wrap in a list if it's a dict
219219
if not all(isinstance(item, dict) for item in entry_response):
220-
raise TypeError(f"entry_response must be a list of dictionaries. Got: {entry_response}")
220+
raise TypeError(ErrorMessages.INVALID_ENTRY_RESPONSE)
221221
if not all(isinstance(item, dict) for item in lp_entry):
222-
raise TypeError(f"lp_entry must be a list of dictionaries. Got: {lp_entry}")
222+
raise TypeError(ErrorMessages.INVALID_LP_ENTRY)
223223
merged_response = DeepMergeMixin(entry_response, lp_entry).to_dict() # Convert to dictionary
224224
return merged_response # Now correctly returns a dictionary
225-
raise ValueError("Missing required keys in live_preview data")
225+
raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS)
226226

227227
def variants(self, variant_uid: str | list[str], params: dict = None):
228228
"""

contentstack/entryqueryable.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
that is used as parents class for the query and entry classes
44
"""
55
import logging
6+
from contentstack.error_messages import ErrorMessages
67

78
class EntryQueryable:
89
"""
@@ -55,7 +56,7 @@ def only(self, field_uid: str):
5556
if isinstance(field_uid, str):
5657
self.entry_queryable_param['only[BASE][]'] = field_uid
5758
else:
58-
raise KeyError("Invalid field_uid provided")
59+
raise KeyError(ErrorMessages.INVALID_FIELD_UID)
5960
return self
6061

6162
def excepts(self, field_uid: str):
@@ -69,7 +70,7 @@ def excepts(self, field_uid: str):
6970
if isinstance(field_uid, str):
7071
self.entry_queryable_param['except[BASE][]'] = field_uid
7172
else:
72-
raise KeyError("Invalid field_uid provided")
73+
raise KeyError(ErrorMessages.INVALID_FIELD_UID)
7374
return self
7475

7576
def include_reference(self, field_uid):

contentstack/error_messages.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Centralized error messages for the Contentstack Python SDK.
3+
All error messages should be defined here and imported where needed.
4+
"""
5+
6+
class ErrorMessages:
7+
# BaseQuery errors
8+
INVALID_KEY = "Invalid key. Provide a valid key and try again."
9+
INVALID_VALUE = "Invalid value. Provide a valid value and try again."
10+
INVALID_KEY_OR_VALUE = "Invalid key or value. Provide valid values and try again."
11+
12+
# ContentType errors
13+
INVALID_CONTENT_TYPE_UID = "Content type UID is invalid. Provide a valid UID and try again."
14+
CONTENT_TYPE_UID_REQUIRED = "Content type UID is required. Provide a UID and try again."
15+
16+
# EntryQueryable errors
17+
INVALID_FIELD_UID = "Invalid field UID. Provide a valid UID and try again."
18+
19+
# DeepMergeLp errors
20+
INVALID_RESPONSE_TYPE = "Invalid input. entry_response and lp_response must be lists of dictionaries. Update the values and try again."
21+
22+
# Entry errors
23+
INVALID_ENVIRONMENT = "Invalid environment. Provide a valid environment and try again."
24+
INVALID_VERSION = "Invalid version. Provide a valid version and try again."
25+
INVALID_KEY_VALUE_ARGS = "Invalid key or value arguments. Provide valid values and try again."
26+
REQUESTING_FALLBACK = "Requesting fallback content for the specified locale."
27+
INVALID_ENTRY_RESPONSE = "Invalid entry_response format. Provide a list of dictionaries, each containing entry data, and try again."
28+
INVALID_LP_ENTRY = "Invalid lp_entry. Provide a list of dictionaries and try again."
29+
MISSING_LIVE_PREVIEW_KEYS = "Missing required keys in live preview data. Provide all required keys and try again."
30+
31+
# Asset errors
32+
INVALID_UID = "Invalid UID. Provide a valid UID and try again."
33+
INVALID_PARAMS = "Invalid parameters. Provide valid parameters and try again."
34+
35+
# Controller errors
36+
CONNECTION_FAILED = "Connection failed. Unable to connect to {url}. Error: {error}. Check your connection and try again."
37+
OPERATION_FAILED = "Operation failed. An unexpected error occurred while making request to {url}. Error: {error}. Check your inputs and try again."
38+
39+
# Query errors
40+
DEPRECATED_SEARCH = """The search() method is deprecated since version 1.7.0. Use regex() instead.
41+
Example: query.regex("title", "^Blog.*") to search for titles starting with "Blog"."""
42+
INVALID_JSON = "Invalid JSON. Error: {error}. Provide valid JSON and try again."
43+
MISSING_ENTRIES_KEY = "Invalid response. The 'entries' key is missing. Include the 'entries' key and try again."
44+
MISSING_ENTRY_KEY = "Invalid lp_response. The 'entry' key is missing. Include the 'entry' key and try again."
45+
46+
# Variants errors
47+
ENTRY_UID_REQUIRED = "Missing entry UID. Provide a valid UID and try again."
48+
49+
# Stack errors
50+
INVALID_STACK_UID = "Invalid UID. Provide a valid UID and try again."
51+
52+
# Utility errors
53+
INVALID_PARAMS_TYPE = "Invalid params. Provide a dictionary and try again."
54+
INVALID_URL_PARAMS = "Invalid input. Provide base_url as a string and params as a dictionary, then try again."
55+
56+
# Stack errors
57+
INVALID_API_KEY = "Invalid API key. Provide a valid API key and try again."
58+
INVALID_DELIVERY_TOKEN = "Invalid delivery token. Provide a valid delivery token and try again."
59+
INVALID_ENVIRONMENT_TOKEN = "Invalid environment. Provide a valid environment and try again."

contentstack/query.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import json
77
import logging
88
import warnings
9+
from contentstack.error_messages import ErrorMessages
910
from urllib import parse
1011

1112
from contentstack.basequery import BaseQuery
@@ -44,8 +45,7 @@ def __init__(self, http_instance, content_type_uid, logger=None):
4445
self.content_type_uid = content_type_uid
4546
self.http_instance = http_instance
4647
if self.content_type_uid is None:
47-
raise PermissionError(
48-
'You are not allowed here without content_type_uid')
48+
raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED)
4949
self.base_url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'
5050
self.base_url = self.__get_base_url()
5151
self.logger = logger or logging.getLogger(__name__)
@@ -54,8 +54,7 @@ def __get_base_url(self, endpoint=''):
5454
if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
5555
self.http_instance.endpoint = endpoint
5656
if None in (self.http_instance, self.content_type_uid):
57-
raise KeyError(
58-
'Provide valid http_instance, content_type_uid or entry_uid')
57+
raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE)
5958
url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'
6059

6160
return url
@@ -138,7 +137,7 @@ def search(self, value: str):
138137
>>> result = query.find()
139138
-------------------------------------
140139
"""
141-
warnings.warn('deprecated in 1.7.0, Use regex function instead')
140+
warnings.warn(ErrorMessages.DEPRECATED_SEARCH)
142141
if value is not None:
143142
self.query_params["typeahead"] = value
144143
return self
@@ -170,7 +169,7 @@ def where_in(self, key: str, query_object):
170169
self.query_params["query"] = {
171170
key: {"$in_query": query_object.parameters}}
172171
else:
173-
raise ValueError('Invalid Key or Value provided')
172+
raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE)
174173
return self
175174

176175
def where_not_in(self, key, query_object):
@@ -200,7 +199,7 @@ def where_not_in(self, key, query_object):
200199
self.query_params["query"] = {
201200
key: {"$nin_query": query_object.parameters}}
202201
else:
203-
raise ValueError('Invalid Key or Value provided')
202+
raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE)
204203
return self
205204

206205
def include_fallback(self):
@@ -330,14 +329,14 @@ def __execute_network_call(self):
330329
try:
331330
response = json.loads(response) # Convert JSON string to dictionary
332331
except json.JSONDecodeError as e:
333-
print(f"JSON decode error: {e}")
332+
print(ErrorMessages.INVALID_JSON.format(error=str(e)))
334333
return {"error": "Invalid JSON response"} # Return an error dictionary
335334

336335
if self.http_instance.live_preview is not None and 'errors' not in response:
337336
if 'entries' in response:
338337
self.http_instance.live_preview['entry_response'] = response['entries'][0] # Get first entry
339338
else:
340-
print(f"Error: 'entries' key missing in response: {response}")
339+
print(ErrorMessages.MISSING_ENTRIES_KEY)
341340
return {"error": "'entries' key missing in response"}
342341
return self._merged_response()
343342
return response
@@ -356,7 +355,7 @@ def _impl_live_preview(self):
356355
if 'entry' in lp_resp:
357356
self.http_instance.live_preview['lp_response'] = {'entry': lp_resp['entry']} # Extract entry
358357
else:
359-
print(f"Warning: Missing 'entry' key in lp_response: {lp_resp}")
358+
print(ErrorMessages.MISSING_ENTRY_KEY)
360359
return None
361360
return None
362361

@@ -368,4 +367,4 @@ def _merged_response(self):
368367
merged_response = DeepMergeMixin(entry_response, lp_response)
369368
return merged_response # Return the merged dictionary
370369

371-
raise ValueError("Missing required keys in live_preview data")
370+
raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS)

0 commit comments

Comments
 (0)