Skip to content

Commit d9508b2

Browse files
mshaileshr@gmail.commshaileshr@gmail.com
authored andcommitted
Added Support for region in config
1 parent d8e54d6 commit d9508b2

File tree

5 files changed

+75
-26
lines changed

5 files changed

+75
-26
lines changed

contentstack/__init__.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
""" contentstack python sdk. """
2+
import warnings
23

34
__author__ = 'Shailesh Mishra'
45
__status__ = 'debug'
@@ -10,17 +11,15 @@
1011
from .asset import Asset
1112
from .config import Config
1213
from .content_type import ContentType
13-
from .errors import Error, ConfigError
14+
from .errors import Error, ConfigError, FileModeWarning
1415
from .http_connection import HTTPConnection
1516

1617
# Set a default logger to prevent "No handler found" warnings
18+
# Set default logging handler to avoid "No handler found" warnings.
1719
import logging
18-
19-
try:
20-
from logging import NullHandler
21-
except ImportError:
22-
class NullHandler(logging.Handler):
23-
def emit(self, record):
24-
pass
20+
from logging import NullHandler
2521

2622
logging.getLogger(__name__).addHandler(NullHandler())
23+
24+
# FileModeWarnings go off per the default.
25+
warnings.simplefilter('default', FileModeWarning, append=True)

contentstack/config.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,53 @@
1515
*
1616
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
1919
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2020
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2121
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222
* SOFTWARE.
2323
2424
"""
2525
import logging
26+
from enum import Enum
2627

2728
logging.basicConfig(filename='cs.log', format='%(asctime)s - %(message)s', level=logging.INFO)
2829
logging.getLogger("Config")
2930

3031

32+
class ContentstackRegion(Enum):
33+
US = 'us'
34+
EU = 'eu'
35+
36+
3137
class Config(object):
3238
"""
3339
All API paths are relative to this base URL, for example, /users actually means <scheme>://<host>/<basePath>/users.
3440
3541
"""
3642

3743
def __init__(self):
44+
self.default = dict(protocol="https", region=ContentstackRegion.US, host="cdn.contentstack.io", version="v3")
3845

39-
# It initialises the Config with the default endpoint
40-
self.default = dict(protocol="https", region="us", host="cdn.contentstack.io", port=443, version="v3")
46+
def region(self, region=ContentstackRegion.US):
47+
48+
"""
49+
The base URL for Content Delivery API is cdn.contentstack.io.
50+
default region is for ContentstackRegion is US
51+
52+
:param region: ContentstackRegion
53+
:return: self
54+
55+
Example:
56+
>>> config = Config().region(region=ContentstackRegion.US)
57+
58+
"""
59+
60+
if region is not None and isinstance(region, ContentstackRegion):
61+
self.default['region'] = region
62+
else:
63+
raise ValueError('Kindly provide a valid argument')
64+
return self
4165

4266
def host(self, host):
4367

@@ -86,5 +110,19 @@ def version(self, version=None):
86110

87111
@property
88112
def endpoint(self):
89-
url = "{0}://{1}/{2}".format(self.default["protocol"], self.default["host"], self.default["version"])
90-
return url
113+
return self.__get_url()
114+
115+
def __get_url(self):
116+
host = self.default["host"]
117+
if self.default['region'] is not ContentstackRegion.US:
118+
119+
if self.default["host"] == 'cdn.contentstack.io':
120+
# update the host to .com
121+
self.default["host"] = 'cdn.contentstack.com'
122+
else:
123+
# Find the regional value
124+
regional_host = str(self.default['region'].value)
125+
# Attach region to the host
126+
host = '{}-{}'.format(regional_host, self.default["host"])
127+
128+
return "{0}://{1}/{2}".format(self.default["protocol"], host, self.default["version"])

contentstack/errors.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525

2626
class Error:
27-
2827
"""
2928
contentstack.error
3029
~~~~~~~~~~~~~~~~~~
@@ -34,7 +33,6 @@ class Error:
3433
"""
3534

3635
def __init__(self):
37-
3836
"""
3937
4038
"""
@@ -44,7 +42,6 @@ def __init__(self):
4442
self.__cause_err = str
4543

4644
def config(self, result: dict):
47-
4845
"""
4946
5047
:param result:
@@ -61,7 +58,6 @@ def config(self, result: dict):
6158

6259
@property
6360
def error_code(self):
64-
6561
"""
6662
It returns error code from the stack response
6763
:return: error_code as int
@@ -74,7 +70,6 @@ def error_code(self):
7470

7571
@property
7672
def error_message(self):
77-
7873
"""
7974
Returns error_message from the stack response
8075
:return: error_message
@@ -88,7 +83,6 @@ def error_message(self):
8883

8984
@property
9085
def error(self):
91-
9286
"""
9387
This returns error code and error_message in dict formats
9488
:return: error dict
@@ -101,7 +95,6 @@ def error(self):
10195

10296
@property
10397
def error_info(self) -> dict:
104-
10598
"""
10699
error information
107100
:return: error information
@@ -162,3 +155,13 @@ class ContentstackError(Exception):
162155

163156
class NotSupportedException(Exception):
164157
pass
158+
159+
160+
class RequestsWarning(Warning):
161+
"""Base warning for Requests."""
162+
pass
163+
164+
165+
class FileModeWarning(RequestsWarning, DeprecationWarning):
166+
"""A file was opened in text mode, but Requests determined its binary length."""
167+
pass

contentstack/http_connection.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from urllib import parse
44
from contentstack import Error
55
from json import JSONDecodeError
6-
from requests.exceptions import Timeout
6+
from requests.exceptions import Timeout, HTTPError
77

88

99
class HTTPConnection(object):
@@ -19,11 +19,12 @@ def __init__(self, url: str, query: dict, headers: dict):
1919
def get_result(self, url: str, query: dict, headers: dict):
2020

2121
"""
22-
get Results is helpful to make HTTP methods
23-
:param url:
24-
:param query:
25-
:param headers:
26-
:return:
22+
get Results is helpful to make HTTP request
23+
:param url: Request url
24+
:param query: query parameters
25+
:param headers: headers parameters
26+
:return: response
27+
2728
"""
2829

2930
if None not in (url, query, headers):
@@ -49,11 +50,18 @@ def get_result(self, url: str, query: dict, headers: dict):
4950
if err is not None:
5051
return Error().config(err)
5152
except Timeout:
53+
# If a request times out, a Timeout exception will be raised.
5254
raise TimeoutError('The request timed out')
5355
except ConnectionError:
56+
# If there is a network problem like a DNS failure, or refused connection the Requests library will raise
57+
# a ConnectionError exception.
5458
raise ConnectionError('Connection error occurred')
5559
except JSONDecodeError:
60+
# Invalid json format response received
5661
raise JSONDecodeError('Invalid JSON in request')
62+
except HTTPError:
63+
# With invalid HTTP responses, Requests will also raise an HTTPError exception, but these are rare.
64+
raise HTTPError('Http Error Occurred')
5765

5866
def __parse_dict(self, response):
5967
# This is the private method to parse the response to their respective type

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
requests==2.22.0

0 commit comments

Comments
 (0)