From 3a8e1614aebb99e1636d9550c9bc27c177ce18e5 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Tue, 2 Sep 2025 17:23:14 +0530 Subject: [PATCH 1/4] Refactor error handling to use centralized error messages across multiple files. --- contentstack/asset.py | 5 +-- contentstack/basequery.py | 15 ++++++--- contentstack/contenttype.py | 12 +++---- contentstack/controller.py | 5 +-- contentstack/deep_merge_lp.py | 4 ++- contentstack/entry.py | 18 +++++------ contentstack/entryqueryable.py | 5 +-- contentstack/error_messages.py | 59 ++++++++++++++++++++++++++++++++++ contentstack/query.py | 21 ++++++------ contentstack/stack.py | 15 +++------ contentstack/utility.py | 5 +-- contentstack/variants.py | 3 +- 12 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 contentstack/error_messages.py diff --git a/contentstack/asset.py b/contentstack/asset.py index cd21586..ee09a06 100644 --- a/contentstack/asset.py +++ b/contentstack/asset.py @@ -6,6 +6,7 @@ import logging from urllib import parse +from contentstack.error_messages import ErrorMessages class Asset: 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): self.asset_params = {} self.__uid = uid if self.__uid is None or self.__uid.strip() == 0: - raise KeyError('Please provide valid uid') + raise KeyError(ErrorMessages.INVALID_UID) self.base_url = f'{self.http_instance.endpoint}/assets/{self.__uid}' if 'environment' in self.http_instance.headers: self.asset_params['environment'] = self.http_instance.headers['environment'] @@ -68,7 +69,7 @@ def params(self, key, value): ----------------------------- """ if None in (key, value) or not isinstance(key, str): - raise KeyError('Kindly provide valid params') + raise KeyError(ErrorMessages.INVALID_PARAMS) self.asset_params[key] = value return self diff --git a/contentstack/basequery.py b/contentstack/basequery.py index f2d9fca..8f56d1b 100644 --- a/contentstack/basequery.py +++ b/contentstack/basequery.py @@ -1,5 +1,6 @@ import enum import logging +from contentstack.error_messages import ErrorMessages class QueryOperation(enum.Enum): """ @@ -137,8 +138,10 @@ def param(self, key: str, value): ----------------------------------- """ - if None in (key, value): - raise KeyError('Invalid key or value') + if key is None: + raise KeyError(ErrorMessages.INVALID_KEY) + if value is None: + raise KeyError(ErrorMessages.INVALID_VALUE) self.query_params[key] = str(value) return self @@ -164,8 +167,10 @@ def query(self, key: str, value): Returns: self-- Class instance, So that method chaining can be performed """ - if None in (key, value): - raise KeyError('Invalid key or value') + if key is None: + raise KeyError(ErrorMessages.INVALID_KEY) + if value is None: + raise KeyError(ErrorMessages.INVALID_VALUE) self.parameters[key] = str(value) return self @@ -186,7 +191,7 @@ def remove_param(self, key: str): ---------------------------------- """ if key is None: - raise ValueError('Kindly provide valid key') + raise ValueError(ErrorMessages.INVALID_KEY) if key in self.query_params: self.query_params.pop(key, None) return self diff --git a/contentstack/contenttype.py b/contentstack/contenttype.py index 2795e29..1423216 100644 --- a/contentstack/contenttype.py +++ b/contentstack/contenttype.py @@ -10,6 +10,7 @@ import json import logging from urllib import parse +from contentstack.error_messages import ErrorMessages from contentstack.entry import Entry from contentstack.query import Query @@ -46,11 +47,9 @@ def entry(self, entry_uid: str): -------------------------------- """ if self.__content_type_uid is None: - raise PermissionError('Please provide valid content_type_uid') + raise PermissionError(ErrorMessages.INVALID_CONTENT_TYPE_UID) if entry_uid is None: - raise PermissionError(json.dumps({ - "message": 'Please provide valid entry uid', - "message_detail": 'Entry UID can not be None'})) + raise PermissionError(ErrorMessages.INVALID_UID) entry = Entry(self.http_instance, self.__content_type_uid, entry_uid=entry_uid) return entry @@ -69,7 +68,7 @@ def query(self): ------------------------------ """ if self.__content_type_uid is None: - raise PermissionError('Kindly provide content_type_uid') + raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED) return Query(self.http_instance, self.__content_type_uid) def fetch(self): @@ -87,8 +86,7 @@ def fetch(self): ------------------------------ """ if self.__content_type_uid is None: - raise KeyError( - 'content_type_uid can not be None to fetch contenttype') + raise KeyError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED) self.local_param['environment'] = self.http_instance.headers['environment'] uri = f'{self.http_instance.endpoint}/content_types/{self.__content_type_uid}' encoded_params = parse.urlencode(self.local_param) diff --git a/contentstack/controller.py b/contentstack/controller.py index e03a900..2f5b183 100644 --- a/contentstack/controller.py +++ b/contentstack/controller.py @@ -1,5 +1,6 @@ import requests from requests.utils import guess_json_utf +from contentstack.error_messages import ErrorMessages class RequestError(Exception): @@ -18,14 +19,14 @@ def get_request(session, url, headers, timeout): response.encoding = 'utf-8' except requests.exceptions.RequestException as e: error = { - 'error': f"Failed to connect to {url}: {str(e)}", + 'error': ErrorMessages.CONNECTION_FAILED.format(url=url, error=str(e)), 'error_code': '400', 'error_message': {str(e)} } raise RequestError(error) except Exception as e: error = { - 'error': f"An unexpected error while making request to {url}, '400', {str(e)}", + 'error': ErrorMessages.OPERATION_FAILED.format(url=url, error=str(e)), 'error_code': '400', 'error_message': {str(e)} } diff --git a/contentstack/deep_merge_lp.py b/contentstack/deep_merge_lp.py index 3970d65..047f85d 100644 --- a/contentstack/deep_merge_lp.py +++ b/contentstack/deep_merge_lp.py @@ -1,8 +1,10 @@ +from contentstack.error_messages import ErrorMessages + class DeepMergeMixin: def __init__(self, entry_response, lp_response): if not isinstance(entry_response, list) or not isinstance(lp_response, list): - raise TypeError("Both entry_response and lp_response must be lists of dictionaries") + raise TypeError(ErrorMessages.INVALID_RESPONSE_TYPE) self.entry_response = entry_response self.lp_response = lp_response diff --git a/contentstack/entry.py b/contentstack/entry.py index f2836f8..06c5faa 100644 --- a/contentstack/entry.py +++ b/contentstack/entry.py @@ -5,6 +5,7 @@ #min-similarity-lines=10 import logging from urllib import parse +from contentstack.error_messages import ErrorMessages from contentstack.deep_merge_lp import DeepMergeMixin from contentstack.entryqueryable import EntryQueryable @@ -49,7 +50,7 @@ def environment(self, environment): ------------------------------ """ if environment is None: - raise KeyError('Kindly provide a valid environment') + raise KeyError(ErrorMessages.INVALID_ENVIRONMENT) self.http_instance.headers['environment'] = environment return self @@ -72,7 +73,7 @@ def version(self, version): ------------------------------ """ if version is None: - raise KeyError('Kindly provide a valid version') + raise KeyError(ErrorMessages.INVALID_VERSION) self.entry_param['version'] = version return self @@ -94,7 +95,7 @@ def param(self, key, value): ----------------------------- """ if None in (key, value) and not isinstance(key, str): - raise ValueError('Kindly provide valid key and value arguments') + raise ValueError(ErrorMessages.INVALID_KEY_VALUE_ARGS) self.entry_param[key] = value return self @@ -113,7 +114,7 @@ def include_fallback(self): >>> result = entry.fetch() ---------------------------- """ - print('Requesting fallback....') + print(ErrorMessages.REQUESTING_FALLBACK) self.entry_param['include_fallback'] = 'true' return self @@ -157,8 +158,7 @@ def __get_base_url(self, endpoint=''): if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace self.http_instance.endpoint = endpoint if None in (self.http_instance, self.content_type_id, self.entry_uid): - raise KeyError( - 'Provide valid http_instance, content_type_uid or entry_uid') + raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE) url = f'{self.http_instance.endpoint}/content_types/{self.content_type_id}/entries/{self.entry_uid}' return url @@ -217,12 +217,12 @@ def _merged_response(self): if not isinstance(lp_entry, list): lp_entry = [lp_entry] # Wrap in a list if it's a dict if not all(isinstance(item, dict) for item in entry_response): - raise TypeError(f"entry_response must be a list of dictionaries. Got: {entry_response}") + raise TypeError(ErrorMessages.INVALID_ENTRY_RESPONSE) if not all(isinstance(item, dict) for item in lp_entry): - raise TypeError(f"lp_entry must be a list of dictionaries. Got: {lp_entry}") + raise TypeError(ErrorMessages.INVALID_LP_ENTRY) merged_response = DeepMergeMixin(entry_response, lp_entry).to_dict() # Convert to dictionary return merged_response # Now correctly returns a dictionary - raise ValueError("Missing required keys in live_preview data") + raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS) def variants(self, variant_uid: str | list[str], params: dict = None): """ diff --git a/contentstack/entryqueryable.py b/contentstack/entryqueryable.py index 16ac6c7..7426510 100644 --- a/contentstack/entryqueryable.py +++ b/contentstack/entryqueryable.py @@ -3,6 +3,7 @@ that is used as parents class for the query and entry classes """ import logging +from contentstack.error_messages import ErrorMessages class EntryQueryable: """ @@ -55,7 +56,7 @@ def only(self, field_uid: str): if isinstance(field_uid, str): self.entry_queryable_param['only[BASE][]'] = field_uid else: - raise KeyError("Invalid field_uid provided") + raise KeyError(ErrorMessages.INVALID_FIELD_UID) return self def excepts(self, field_uid: str): @@ -69,7 +70,7 @@ def excepts(self, field_uid: str): if isinstance(field_uid, str): self.entry_queryable_param['except[BASE][]'] = field_uid else: - raise KeyError("Invalid field_uid provided") + raise KeyError(ErrorMessages.INVALID_FIELD_UID) return self def include_reference(self, field_uid): diff --git a/contentstack/error_messages.py b/contentstack/error_messages.py new file mode 100644 index 0000000..11b9daa --- /dev/null +++ b/contentstack/error_messages.py @@ -0,0 +1,59 @@ +""" +Centralized error messages for the Contentstack Python SDK. +All error messages should be defined here and imported where needed. +""" + +class ErrorMessages: + # BaseQuery errors + INVALID_KEY = "Invalid key. Provide a valid key and try again." + INVALID_VALUE = "Invalid value. Provide a valid value and try again." + INVALID_KEY_OR_VALUE = "Invalid key or value. Provide valid values and try again." + + # ContentType errors + INVALID_CONTENT_TYPE_UID = "Content type UID is invalid. Provide a valid UID and try again." + CONTENT_TYPE_UID_REQUIRED = "Content type UID is required. Provide a UID and try again." + + # EntryQueryable errors + INVALID_FIELD_UID = "Invalid field UID. Provide a valid UID and try again." + + # DeepMergeLp errors + INVALID_RESPONSE_TYPE = "Invalid input. entry_response and lp_response must be lists of dictionaries. Update the values and try again." + + # Entry errors + INVALID_ENVIRONMENT = "Invalid environment. Provide a valid environment and try again." + INVALID_VERSION = "Invalid version. Provide a valid version and try again." + INVALID_KEY_VALUE_ARGS = "Invalid key or value arguments. Provide valid values and try again." + REQUESTING_FALLBACK = "Requesting fallback content for the specified locale." + INVALID_ENTRY_RESPONSE = "Invalid entry_response format. Provide a list of dictionaries, each containing entry data, and try again." + INVALID_LP_ENTRY = "Invalid lp_entry. Provide a list of dictionaries and try again." + MISSING_LIVE_PREVIEW_KEYS = "Missing required keys in live preview data. Provide all required keys and try again." + + # Asset errors + INVALID_UID = "Invalid UID. Provide a valid UID and try again." + INVALID_PARAMS = "Invalid parameters. Provide valid parameters and try again." + + # Controller errors + CONNECTION_FAILED = "Connection failed. Unable to connect to {url}. Error: {error}. Check your connection and try again." + OPERATION_FAILED = "Operation failed. An unexpected error occurred while making request to {url}. Error: {error}. Check your inputs and try again." + + # Query errors + DEPRECATED_SEARCH = """The search() method is deprecated since version 1.7.0. Use regex() instead. +Example: query.regex("title", "^Blog.*") to search for titles starting with "Blog".""" + INVALID_JSON = "Invalid JSON. Error: {error}. Provide valid JSON and try again." + MISSING_ENTRIES_KEY = "Invalid response. The 'entries' key is missing. Include the 'entries' key and try again." + MISSING_ENTRY_KEY = "Invalid lp_response. The 'entry' key is missing. Include the 'entry' key and try again." + + # Variants errors + ENTRY_UID_REQUIRED = "Missing entry UID. Provide a valid UID and try again." + + # Stack errors + INVALID_STACK_UID = "Invalid UID. Provide a valid UID and try again." + + # Utility errors + INVALID_PARAMS_TYPE = "Invalid params. Provide a dictionary and try again." + INVALID_URL_PARAMS = "Invalid input. Provide base_url as a string and params as a dictionary, then try again." + + # Stack errors + INVALID_API_KEY = "Invalid API key. Provide a valid API key and try again." + INVALID_DELIVERY_TOKEN = "Invalid delivery token. Provide a valid delivery token and try again." + INVALID_ENVIRONMENT_TOKEN = "Invalid environment. Provide a valid environment and try again." diff --git a/contentstack/query.py b/contentstack/query.py index 40b9bbf..9a18dcb 100644 --- a/contentstack/query.py +++ b/contentstack/query.py @@ -6,6 +6,7 @@ import json import logging import warnings +from contentstack.error_messages import ErrorMessages from urllib import parse from contentstack.basequery import BaseQuery @@ -44,8 +45,7 @@ def __init__(self, http_instance, content_type_uid, logger=None): self.content_type_uid = content_type_uid self.http_instance = http_instance if self.content_type_uid is None: - raise PermissionError( - 'You are not allowed here without content_type_uid') + raise PermissionError(ErrorMessages.CONTENT_TYPE_UID_REQUIRED) self.base_url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries' self.base_url = self.__get_base_url() self.logger = logger or logging.getLogger(__name__) @@ -54,8 +54,7 @@ def __get_base_url(self, endpoint=''): if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace self.http_instance.endpoint = endpoint if None in (self.http_instance, self.content_type_uid): - raise KeyError( - 'Provide valid http_instance, content_type_uid or entry_uid') + raise KeyError(ErrorMessages.INVALID_KEY_OR_VALUE) url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries' return url @@ -138,7 +137,7 @@ def search(self, value: str): >>> result = query.find() ------------------------------------- """ - warnings.warn('deprecated in 1.7.0, Use regex function instead') + warnings.warn(ErrorMessages.DEPRECATED_SEARCH) if value is not None: self.query_params["typeahead"] = value return self @@ -170,7 +169,7 @@ def where_in(self, key: str, query_object): self.query_params["query"] = { key: {"$in_query": query_object.parameters}} else: - raise ValueError('Invalid Key or Value provided') + raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE) return self def where_not_in(self, key, query_object): @@ -200,7 +199,7 @@ def where_not_in(self, key, query_object): self.query_params["query"] = { key: {"$nin_query": query_object.parameters}} else: - raise ValueError('Invalid Key or Value provided') + raise ValueError(ErrorMessages.INVALID_KEY_OR_VALUE) return self def include_fallback(self): @@ -330,14 +329,14 @@ def __execute_network_call(self): try: response = json.loads(response) # Convert JSON string to dictionary except json.JSONDecodeError as e: - print(f"JSON decode error: {e}") + print(ErrorMessages.INVALID_JSON.format(error=str(e))) return {"error": "Invalid JSON response"} # Return an error dictionary if self.http_instance.live_preview is not None and 'errors' not in response: if 'entries' in response: self.http_instance.live_preview['entry_response'] = response['entries'][0] # Get first entry else: - print(f"Error: 'entries' key missing in response: {response}") + print(ErrorMessages.MISSING_ENTRIES_KEY) return {"error": "'entries' key missing in response"} return self._merged_response() return response @@ -356,7 +355,7 @@ def _impl_live_preview(self): if 'entry' in lp_resp: self.http_instance.live_preview['lp_response'] = {'entry': lp_resp['entry']} # Extract entry else: - print(f"Warning: Missing 'entry' key in lp_response: {lp_resp}") + print(ErrorMessages.MISSING_ENTRY_KEY) return None return None @@ -368,4 +367,4 @@ def _merged_response(self): merged_response = DeepMergeMixin(entry_response, lp_response) return merged_response # Return the merged dictionary - raise ValueError("Missing required keys in live_preview data") \ No newline at end of file + raise ValueError(ErrorMessages.MISSING_LIVE_PREVIEW_KEYS) \ No newline at end of file diff --git a/contentstack/stack.py b/contentstack/stack.py index 8194ef4..269df06 100644 --- a/contentstack/stack.py +++ b/contentstack/stack.py @@ -2,6 +2,7 @@ import logging from urllib import parse from urllib3.util import Retry +from contentstack.error_messages import ErrorMessages from contentstack.asset import Asset from contentstack.assetquery import AssetQuery @@ -112,17 +113,11 @@ def __init__(self, api_key: str, delivery_token: str, environment: str, def _validate_stack(self): if self.api_key is None or self.api_key == '': - raise PermissionError( - 'You are not permitted to the stack without valid APIKey' - ) + raise PermissionError(ErrorMessages.INVALID_API_KEY) if self.delivery_token is None or self.delivery_token == "": - raise PermissionError( - 'You are not permitted to the stack without valid Delivery Token' - ) + raise PermissionError(ErrorMessages.INVALID_DELIVERY_TOKEN) if self.environment is None or self.environment == "": - raise PermissionError( - 'You are not permitted to the stack without valid Environment' - ) + raise PermissionError(ErrorMessages.INVALID_ENVIRONMENT_TOKEN) if self.region.value == 'eu' and self.host == DEFAULT_HOST: self.host = 'eu-cdn.contentstack.com' @@ -244,7 +239,7 @@ def asset(self, uid): ----------------------------- """ if uid is None or not isinstance(uid, str): - raise KeyError('Please provide a valid uid') + raise KeyError(ErrorMessages.INVALID_UID) return Asset(self.http_instance, uid=uid) def asset_query(self): diff --git a/contentstack/utility.py b/contentstack/utility.py index 4a49d4a..947ebcd 100644 --- a/contentstack/utility.py +++ b/contentstack/utility.py @@ -7,6 +7,7 @@ import json import logging from urllib.parse import urlencode +from contentstack.error_messages import ErrorMessages def setup_logging(logging_type=logging.INFO, filename='app.log'): @@ -65,7 +66,7 @@ def do_url_encode(params): :return: Encoded URL query string """ if not isinstance(params, dict): - raise ValueError("params must be a dictionary") + raise ValueError(ErrorMessages.INVALID_PARAMS_TYPE) return urlencode(params, doseq=True) @staticmethod @@ -79,7 +80,7 @@ def get_complete_url(base_url: str, params: dict, skip_encoding=False) -> str: :return: Complete URL """ if not isinstance(base_url, str) or not isinstance(params, dict): - raise ValueError("base_url must be a string and params must be a dictionary") + raise ValueError(ErrorMessages.INVALID_URL_PARAMS) if 'query' in params and not skip_encoding: params["query"] = json.dumps(params["query"], separators=(',', ':')) diff --git a/contentstack/variants.py b/contentstack/variants.py index 133630d..1cdc92e 100644 --- a/contentstack/variants.py +++ b/contentstack/variants.py @@ -1,5 +1,6 @@ import logging from urllib import parse +from contentstack.error_messages import ErrorMessages from contentstack.entryqueryable import EntryQueryable @@ -74,7 +75,7 @@ def fetch(self, params=None): :return: Entry, so you can chain this call. """ if self.entry_uid is None: - raise ValueError("entry_uid is required") + raise ValueError(ErrorMessages.ENTRY_UID_REQUIRED) else: headers = self.http_instance.headers.copy() # Create a local copy of headers if isinstance(self.variant_uid, str): From 09d15a27b74a8bc720d3fcb6a0f8d659551359a7 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Fri, 31 Oct 2025 16:59:58 +0530 Subject: [PATCH 2/4] Update error messages in test cases for clarity and consistency. --- tests/test_assets.py | 4 ++-- tests/test_entry.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_assets.py b/tests/test_assets.py index b9b374b..3492247 100644 --- a/tests/test_assets.py +++ b/tests/test_assets.py @@ -102,14 +102,14 @@ def test_071_check_none_coverage(self): try: self.asset = self.stack.asset(None) except Exception as inst: - self.assertEqual('Please provide a valid uid', inst.args[0]) + self.assertEqual('Invalid UID. Provide a valid UID and try again.', inst.args[0]) def test_072_check_none_coverage_test(self): try: self.asset = self.stack.asset(uid=ASSET_UID) self.asset.params(2, 'value') except Exception as inst: - self.assertEqual('Kindly provide valid params', inst.args[0]) + self.assertEqual('Invalid parameters. Provide valid parameters and try again.', inst.args[0]) def test_08_support_include_fallback(self): self.asset = self.stack.asset(uid=ASSET_UID) diff --git a/tests/test_entry.py b/tests/test_entry.py index cdfeb4d..e910a58 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -91,7 +91,7 @@ def test_14_entry_queryable_only(self): self.assertEqual(None, result['uid']) except KeyError as e: if hasattr(e, 'message'): - self.assertEqual("Invalid field_UID provided", e.args[0]) + self.assertEqual("Invalid field UID. Provide a valid UID and try again.", e.args[0]) def test_entry_queryable_excepts(self): try: @@ -100,7 +100,7 @@ def test_entry_queryable_excepts(self): self.assertEqual(None, result['uid']) except KeyError as e: if hasattr(e, 'message'): - self.assertEqual("Invalid field_UID provided", e.args[0]) + self.assertEqual("Invalid field UID. Provide a valid UID and try again.", e.args[0]) def test_16_entry_queryable_include_content_type(self): entry = self.stack.content_type('faq').entry(FAQ_UID).include_content_type() From 181e5e057eb1f66c225ed7223a4e816d92c6e7db Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Fri, 7 Nov 2025 14:50:12 +0530 Subject: [PATCH 3/4] version bump --- CHANGELOG.md | 6 ++++++ contentstack/__init__.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f529fd..11ba6dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## _v2.4.1_ + +### **Date: 10-November-2025** + +- Improved Error messages. + ## _v2.4.0_ ### **Date: 01-September-2025** diff --git a/contentstack/__init__.py b/contentstack/__init__.py index 6e78ad5..70b2807 100644 --- a/contentstack/__init__.py +++ b/contentstack/__init__.py @@ -22,7 +22,7 @@ __title__ = 'contentstack-delivery-python' __author__ = 'contentstack' __status__ = 'debug' -__version__ = 'v2.4.0' +__version__ = 'v2.4.1' __endpoint__ = 'cdn.contentstack.io' __email__ = 'support@contentstack.com' __developer_email__ = 'mobile@contentstack.com' From 80276b4957aabb1bbea1c4f17c2ea50975317482 Mon Sep 17 00:00:00 2001 From: reeshika-h Date: Fri, 7 Nov 2025 14:58:48 +0530 Subject: [PATCH 4/4] snyk fix --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 60ac155..384ef54 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ tox==4.5.1 virtualenv==20.26.6 Sphinx==7.3.7 sphinxcontrib-websupport==1.2.7 -pip==25.1.1 +pip==25.3 build==0.10.0 wheel==0.45.1 lxml==5.3.1