Contentstack 1.11.2__tar.gz → 2.0.1__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 (31) hide show
  1. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/PKG-INFO +1 -1
  2. {contentstack-1.11.2 → contentstack-2.0.1}/PKG-INFO +1 -1
  3. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/__init__.py +3 -3
  4. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/asset.py +2 -4
  5. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/assetquery.py +2 -4
  6. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/basequery.py +2 -4
  7. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/contenttype.py +2 -4
  8. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/entry.py +2 -4
  9. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/entryqueryable.py +2 -4
  10. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/https_connection.py +0 -3
  11. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/image_transform.py +2 -3
  12. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/query.py +2 -3
  13. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/stack.py +2 -2
  14. contentstack-2.0.1/contentstack/utility.py +93 -0
  15. {contentstack-1.11.2 → contentstack-2.0.1}/tests/test_stack.py +23 -1
  16. contentstack-1.11.2/contentstack/utility.py +0 -76
  17. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/SOURCES.txt +0 -0
  18. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/dependency_links.txt +0 -0
  19. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/not-zip-safe +0 -0
  20. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/requires.txt +0 -0
  21. {contentstack-1.11.2 → contentstack-2.0.1}/Contentstack.egg-info/top_level.txt +0 -0
  22. {contentstack-1.11.2 → contentstack-2.0.1}/LICENSE +0 -0
  23. {contentstack-1.11.2 → contentstack-2.0.1}/README.md +0 -0
  24. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/controller.py +0 -0
  25. {contentstack-1.11.2 → contentstack-2.0.1}/contentstack/deep_merge_lp.py +0 -0
  26. {contentstack-1.11.2 → contentstack-2.0.1}/setup.cfg +0 -0
  27. {contentstack-1.11.2 → contentstack-2.0.1}/setup.py +0 -0
  28. {contentstack-1.11.2 → contentstack-2.0.1}/tests/test_assets.py +0 -0
  29. {contentstack-1.11.2 → contentstack-2.0.1}/tests/test_entry.py +0 -0
  30. {contentstack-1.11.2 → contentstack-2.0.1}/tests/test_live_preview.py +0 -0
  31. {contentstack-1.11.2 → contentstack-2.0.1}/tests/test_query.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Contentstack
3
- Version: 1.11.2
3
+ Version: 2.0.1
4
4
  Summary: Contentstack is a headless CMS with an API-first approach.
5
5
  Home-page: https://github.com/contentstack/contentstack-python
6
6
  Author: Contentstack
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Contentstack
3
- Version: 1.11.2
3
+ Version: 2.0.1
4
4
  Summary: Contentstack is a headless CMS with an API-first approach.
5
5
  Home-page: https://github.com/contentstack/contentstack-python
6
6
  Author: Contentstack
@@ -22,8 +22,8 @@ __all__ = (
22
22
  __title__ = 'contentstack-delivery-python'
23
23
  __author__ = 'contentstack'
24
24
  __status__ = 'debug'
25
- __version__ = 'v1.11.2'
25
+ __version__ = 'v2.0.1'
26
26
  __endpoint__ = 'cdn.contentstack.io'
27
- __email__ = 'mobile@contentstack.com'
28
- __developer_email__ = 'shailesh.mishra@contentstack.com'
27
+ __email__ = 'support@contentstack.com'
28
+ __developer_email__ = 'mobile@contentstack.com'
29
29
  __license__ = "MIT"
@@ -7,13 +7,10 @@ These files can be attached and used in multiple entries.
7
7
  import logging
8
8
  from urllib import parse
9
9
 
10
- log = logging.getLogger(__name__)
11
-
12
-
13
10
  class Asset:
14
11
  r"""`Asset` refer to all the media files (images, videos, PDFs, audio files, and so on)."""
15
12
 
16
- def __init__(self, http_instance, uid=None):
13
+ def __init__(self, http_instance, uid=None, logger=None):
17
14
  self.http_instance = http_instance
18
15
  self.asset_params = {}
19
16
  self.__uid = uid
@@ -22,6 +19,7 @@ class Asset:
22
19
  self.base_url = f'{self.http_instance.endpoint}/assets/{self.__uid}'
23
20
  if 'environment' in self.http_instance.headers:
24
21
  self.asset_params['environment'] = self.http_instance.headers['environment']
22
+ self.logger = logger or logging.getLogger(__name__)
25
23
 
26
24
  def environment(self, environment):
27
25
  r"""Provide the name of the environment if you wish to retrieve the assets published
@@ -9,15 +9,12 @@ import logging
9
9
  from contentstack.basequery import BaseQuery
10
10
  from contentstack.utility import Utils
11
11
 
12
- log = logging.getLogger(__name__)
13
-
14
-
15
12
  class AssetQuery(BaseQuery):
16
13
  """
17
14
  This call fetches the list of all the assets of a particular stack.
18
15
  """
19
16
 
20
- def __init__(self, http_instance):
17
+ def __init__(self, http_instance, logger=None):
21
18
  super().__init__()
22
19
  self.http_instance = http_instance
23
20
  self.asset_query_params = {}
@@ -25,6 +22,7 @@ class AssetQuery(BaseQuery):
25
22
  if "environment" in self.http_instance.headers:
26
23
  env = self.http_instance.headers["environment"]
27
24
  self.base_url = f"{self.base_url}?environment={env}"
25
+ self.logger = logger or logging.getLogger(__name__)
28
26
 
29
27
  def environment(self, environment):
30
28
  r"""Provide the name of the environment if you wish to retrieve the assets published
@@ -1,9 +1,6 @@
1
1
  import enum
2
2
  import logging
3
3
 
4
- log = logging.getLogger(__name__)
5
-
6
-
7
4
  class QueryOperation(enum.Enum):
8
5
  """
9
6
  QueryOperation is enum that Provides Options to perform operation to query the result.
@@ -38,9 +35,10 @@ class BaseQuery:
38
35
  Common Query class works for Query As well as Asset
39
36
  """
40
37
 
41
- def __init__(self):
38
+ def __init__(self, logger=None):
42
39
  self.parameters = {}
43
40
  self.query_params = {}
41
+ self.logger = logger or logging.getLogger(__name__)
44
42
 
45
43
  def where(self, field_uid: str, query_operation: QueryOperation, fields=None):
46
44
  """
@@ -14,9 +14,6 @@ from urllib import parse
14
14
  from contentstack.entry import Entry
15
15
  from contentstack.query import Query
16
16
 
17
- log = logging.getLogger(__name__)
18
-
19
-
20
17
  class ContentType:
21
18
  """
22
19
  Content type defines the structure or schema of a page or a
@@ -26,10 +23,11 @@ class ContentType:
26
23
  content type.
27
24
  """
28
25
 
29
- def __init__(self, http_instance, content_type_uid):
26
+ def __init__(self, http_instance, content_type_uid, logger=None):
30
27
  self.http_instance = http_instance
31
28
  self.__content_type_uid = content_type_uid
32
29
  self.local_param = {}
30
+ self.logger = logger or logging.getLogger(__name__)
33
31
 
34
32
  def entry(self, entry_uid: str):
35
33
  r"""
@@ -9,9 +9,6 @@ from urllib import parse
9
9
  from contentstack.deep_merge_lp import DeepMergeMixin
10
10
  from contentstack.entryqueryable import EntryQueryable
11
11
 
12
- log = logging.getLogger(__name__)
13
-
14
-
15
12
  class Entry(EntryQueryable):
16
13
  """
17
14
  An entry is the actual piece of content that you want to publish.
@@ -23,7 +20,7 @@ class Entry(EntryQueryable):
23
20
  locale={locale_code}
24
21
  """
25
22
 
26
- def __init__(self, http_instance, content_type_uid, entry_uid):
23
+ def __init__(self, http_instance, content_type_uid, entry_uid, logger=None):
27
24
  super().__init__()
28
25
  EntryQueryable.__init__(self)
29
26
  self.entry_param = {}
@@ -31,6 +28,7 @@ class Entry(EntryQueryable):
31
28
  self.content_type_id = content_type_uid
32
29
  self.entry_uid = entry_uid
33
30
  self.base_url = self.__get_base_url()
31
+ self.logger = logger or logging.getLogger(__name__)
34
32
 
35
33
  def environment(self, environment):
36
34
  """
@@ -4,16 +4,14 @@ that is used as parents class for the query and entry classes
4
4
  """
5
5
  import logging
6
6
 
7
- log = logging.getLogger(__name__)
8
-
9
-
10
7
  class EntryQueryable:
11
8
  """
12
9
  This class is base class for the Entry and Query class that shares common functions
13
10
  """
14
11
 
15
- def __init__(self):
12
+ def __init__(self, logger=None):
16
13
  self.entry_queryable_param = {}
14
+ self.logger = logger or logging.getLogger(__name__)
17
15
 
18
16
  def locale(self, locale: str):
19
17
  """
@@ -9,9 +9,6 @@ from requests.adapters import HTTPAdapter
9
9
  import contentstack
10
10
  from contentstack.controller import get_request
11
11
 
12
- log = logging.getLogger(__name__)
13
-
14
-
15
12
  def __get_os_platform():
16
13
  os_platform = platform.system()
17
14
  if os_platform == 'Darwin':
@@ -8,8 +8,6 @@ different transform_params in second parameter in array form
8
8
 
9
9
  import logging
10
10
 
11
- log = logging.getLogger(__name__)
12
-
13
11
 
14
12
  class ImageTransform: # pylint: disable=too-few-public-methods
15
13
  """
@@ -17,7 +15,7 @@ class ImageTransform: # pylint: disable=too-few-public-methods
17
15
  files
18
16
  """
19
17
 
20
- def __init__(self, http_instance, image_url, **kwargs):
18
+ def __init__(self, http_instance, image_url, logger=None, **kwargs):
21
19
  """
22
20
  creates instance of the ImageTransform class
23
21
  :param httpInstance: instance of HttpsConnection
@@ -35,6 +33,7 @@ class ImageTransform: # pylint: disable=too-few-public-methods
35
33
  self.http_instance = http_instance
36
34
  self.image_url = image_url
37
35
  self.image_params = kwargs
36
+ self.logger = logger or logging.getLogger(__name__)
38
37
 
39
38
  def get_url(self):
40
39
  """
@@ -12,8 +12,6 @@ from contentstack.basequery import BaseQuery
12
12
  from contentstack.deep_merge_lp import DeepMergeMixin
13
13
  from contentstack.entryqueryable import EntryQueryable
14
14
 
15
- log = logging.getLogger(__name__)
16
-
17
15
 
18
16
  class QueryType(enum.Enum):
19
17
  """
@@ -40,7 +38,7 @@ class Query(BaseQuery, EntryQueryable):
40
38
  >>> result = query.locale('locale-code').excepts('field_uid').limit(4).skip(5).find()
41
39
  """
42
40
 
43
- def __init__(self, http_instance, content_type_uid):
41
+ def __init__(self, http_instance, content_type_uid, logger=None):
44
42
  super().__init__()
45
43
  EntryQueryable.__init__(self)
46
44
  self.content_type_uid = content_type_uid
@@ -50,6 +48,7 @@ class Query(BaseQuery, EntryQueryable):
50
48
  'You are not allowed here without content_type_uid')
51
49
  self.base_url = f'{self.http_instance.endpoint}/content_types/{self.content_type_uid}/entries'
52
50
  self.base_url = self.__get_base_url()
51
+ self.logger = logger or logging.getLogger(__name__)
53
52
 
54
53
  def __get_base_url(self, endpoint=''):
55
54
  if endpoint is not None and endpoint.strip(): # .strip() removes leading/trailing whitespace
@@ -9,7 +9,6 @@ from contentstack.contenttype import ContentType
9
9
  from contentstack.https_connection import HTTPSConnection
10
10
  from contentstack.image_transform import ImageTransform
11
11
 
12
- log = logging.getLogger(__name__)
13
12
  DEFAULT_HOST = 'cdn.contentstack.io'
14
13
 
15
14
 
@@ -42,6 +41,7 @@ class Stack:
42
41
  live_preview=None,
43
42
  branch=None,
44
43
  early_access = None,
44
+ logger=None,
45
45
  ):
46
46
  """
47
47
  # Class that wraps the credentials of the authenticated user. Think of
@@ -78,7 +78,7 @@ class Stack:
78
78
  live_preview={enable=True, authorization='your auth token'}, retry_strategy= _strategy)
79
79
  ```
80
80
  """
81
- logging.basicConfig(level=logging.DEBUG)
81
+ self.logger = logger or logging.getLogger(__name__)
82
82
  self.headers = {}
83
83
  self._query_params = {}
84
84
  self.sync_param = {}
@@ -0,0 +1,93 @@
1
+ """
2
+ Utility functions for logging and URL manipulation.
3
+ Last modified by ishaileshmishra on 06/08/20.
4
+ Copyright 2019 Contentstack. All rights reserved.
5
+ """
6
+
7
+ import json
8
+ import logging
9
+ from urllib.parse import urlencode
10
+
11
+
12
+ def setup_logging(logging_type=logging.INFO, filename='app.log'):
13
+ """
14
+ Global one-time logging configuration.
15
+ Should be called from your main application entry point.
16
+ """
17
+ logging.basicConfig(
18
+ filename=filename,
19
+ level=logging_type,
20
+ format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
21
+ datefmt='%Y-%m-%d %H:%M:%S'
22
+ )
23
+
24
+
25
+ class Utils:
26
+ @staticmethod
27
+ def setup_logger(name="AppLogger", level=logging.INFO, filename='app.log'):
28
+ """
29
+ Creates and configures a named logger with file and console output.
30
+ Prevents duplicate handlers.
31
+ """
32
+ logger = logging.getLogger(name)
33
+ if not logger.handlers:
34
+ logger.setLevel(level)
35
+
36
+ formatter = logging.Formatter(
37
+ '[%(asctime)s] %(levelname)s - %(name)s - %(message)s',
38
+ datefmt='%Y-%m-%d %H:%M:%S'
39
+ )
40
+
41
+ file_handler = logging.FileHandler(filename)
42
+ file_handler.setFormatter(formatter)
43
+ logger.addHandler(file_handler)
44
+
45
+ console_handler = logging.StreamHandler()
46
+ console_handler.setFormatter(formatter)
47
+ logger.addHandler(console_handler)
48
+
49
+ return logger
50
+
51
+ @staticmethod
52
+ def log(message, level=logging.DEBUG):
53
+ """
54
+ Log a message with the specified level.
55
+ Default is DEBUG.
56
+ """
57
+ logger = logging.getLogger("AppLogger")
58
+ logger.log(level, message)
59
+
60
+ @staticmethod
61
+ def do_url_encode(params):
62
+ """
63
+ Encode query parameters to URL-safe format.
64
+ :param params: Dictionary of parameters
65
+ :return: Encoded URL query string
66
+ """
67
+ if not isinstance(params, dict):
68
+ raise ValueError("params must be a dictionary")
69
+ return urlencode(params, doseq=True)
70
+
71
+ @staticmethod
72
+ def get_complete_url(base_url: str, params: dict, skip_encoding=False) -> str:
73
+ """
74
+ Construct a full URL by combining base URL and encoded parameters.
75
+ Handles JSON stringification for the `query` key.
76
+ :param base_url: Base API URL
77
+ :param params: Dictionary of query parameters
78
+ :param skip_encoding: Set True to skip URL encoding
79
+ :return: Complete URL
80
+ """
81
+ if not isinstance(base_url, str) or not isinstance(params, dict):
82
+ raise ValueError("base_url must be a string and params must be a dictionary")
83
+
84
+ if 'query' in params and not skip_encoding:
85
+ params["query"] = json.dumps(params["query"], separators=(',', ':'))
86
+
87
+ if not skip_encoding:
88
+ query_string = urlencode(params, doseq=True)
89
+ else:
90
+ query_string = "&".join(f"{k}={v}" for k, v in params.items())
91
+
92
+ # Append with appropriate separator
93
+ return f'{base_url}&{query_string}' if '?' in base_url else f'{base_url}?{query_string}'
@@ -1,7 +1,10 @@
1
1
  import unittest
2
2
  import config
3
3
  import contentstack
4
+ import logging
5
+ import io
4
6
  from contentstack.stack import ContentstackRegion
7
+ from contentstack.stack import Stack
5
8
 
6
9
  API_KEY = config.APIKEY
7
10
  DELIVERY_TOKEN = config.DELIVERYTOKEN
@@ -182,4 +185,23 @@ class TestStack(unittest.TestCase):
182
185
  def test_23_get_early_access(self):
183
186
  stack = contentstack.Stack(
184
187
  config.APIKEY, config.DELIVERYTOKEN, config.ENVIRONMENT, early_access=["taxonomy", "teams"])
185
- self.assertEqual(self.early_access, stack.get_early_access)
188
+ self.assertEqual(self.early_access, stack.get_early_access)
189
+
190
+ def test_stack_with_custom_logger(self):
191
+ log_stream = io.StringIO()
192
+ custom_logger = logging.getLogger("contentstack.custom.test_logger")
193
+ custom_logger.setLevel(logging.INFO)
194
+
195
+ if custom_logger.hasHandlers():
196
+ custom_logger.handlers.clear()
197
+
198
+ handler = logging.StreamHandler(log_stream)
199
+ formatter = logging.Formatter('%(levelname)s - %(name)s - %(message)s')
200
+ handler.setFormatter(formatter)
201
+ custom_logger.addHandler(handler)
202
+ Stack("api_key", "delivery_token", "dev", logger=custom_logger)
203
+ custom_logger.info("INFO - contentstack.custom.test_logger - Test log entry")
204
+ handler.flush()
205
+ logs = log_stream.getvalue()
206
+ print("\nCaptured Logs:\n", logs)
207
+ self.assertIn("INFO - contentstack.custom.test_logger - Test log entry", logs)
@@ -1,76 +0,0 @@
1
- """
2
- Last modified by ishaileshmishra on 06/08/20.
3
- Copyright 2019 Contentstack. All rights reserved.
4
- """
5
-
6
- import json
7
- import logging
8
- from urllib.parse import urlencode, urljoin
9
-
10
- log = logging.getLogger(__name__)
11
-
12
-
13
- def config_logging(logging_type: logging.WARNING):
14
- """
15
- This is to create logging config
16
- :param logging_type: Level of the logging
17
- :return: basicConfig instance
18
- """
19
- logging.basicConfig(
20
- filename='app.log',
21
- level=logging_type,
22
- format='[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s',
23
- datefmt='%H:%M:%S'
24
- )
25
-
26
-
27
- class Utils:
28
-
29
- @staticmethod
30
- def config_logging():
31
- """ Setting up logging """
32
- logging.basicConfig(
33
- filename='report_log.log',
34
- format='%(asctime)s - %(message)s',
35
- level=logging.INFO
36
- )
37
-
38
- @staticmethod
39
- def setup_logger():
40
- """setup logger for the application"""
41
- return logging.getLogger("Config")
42
-
43
- @staticmethod
44
- def log(message):
45
- """this generates log message"""
46
- logging.debug(message)
47
-
48
- @staticmethod
49
- def do_url_encode(params):
50
- """
51
- To encode url with query parameters
52
- :param params:
53
- :return: encoded url
54
- """
55
- return parse.urlencode(params)
56
-
57
- @staticmethod
58
- def get_complete_url(base_url: str, params: dict) -> str:
59
- """
60
- Creates a complete URL using base_url and their respective parameters.
61
- :param base_url: The base URL to which parameters are appended.
62
- :param params: A dictionary of parameters to be included in the URL.
63
- :return: A complete URL with encoded parameters.
64
- """
65
- # Ensure 'query' is properly serialized as a JSON string without extra quotes
66
- if 'query' in params:
67
- params["query"] = json.dumps(params["query"], separators=(',', ':'))
68
-
69
- # Encode parameters
70
- query_string = urlencode(params, doseq=True)
71
-
72
- # Join base_url and query_string
73
- if '?' in base_url:
74
- return f'{base_url}&{query_string}'
75
- else:
76
- return f'{base_url}?{query_string}'
File without changes
File without changes
File without changes
File without changes