Update cloudinary-1.26.0

This commit is contained in:
JonnyWong16 2021-10-14 21:18:46 -07:00
commit 4b28040d59
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
17 changed files with 1169 additions and 307 deletions

View file

@ -1,13 +1,15 @@
from __future__ import absolute_import
import abc
from copy import deepcopy
import hashlib
import os
import re
import logging
import numbers
import certifi
from math import ceil
from six import python_2_unicode_compatible
from six import python_2_unicode_compatible, add_metaclass
logger = logging.getLogger("Cloudinary")
ch = logging.StreamHandler()
@ -34,8 +36,9 @@ AKAMAI_SHARED_CDN = "res.cloudinary.com"
SHARED_CDN = AKAMAI_SHARED_CDN
CL_BLANK = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
URI_SCHEME = "cloudinary"
API_VERSION = "v1_1"
VERSION = "1.20.0"
VERSION = "1.26.0"
USER_AGENT = "CloudinaryPython/{} (Python {})".format(VERSION, python_version())
""" :const: USER_AGENT """
@ -94,54 +97,20 @@ def import_django_settings():
return None
class Config(object):
@add_metaclass(abc.ABCMeta)
class BaseConfig(object):
def __init__(self):
django_settings = import_django_settings()
if django_settings:
self.update(**django_settings)
elif os.environ.get("CLOUDINARY_CLOUD_NAME"):
self.update(
cloud_name=os.environ.get("CLOUDINARY_CLOUD_NAME"),
api_key=os.environ.get("CLOUDINARY_API_KEY"),
api_secret=os.environ.get("CLOUDINARY_API_SECRET"),
secure_distribution=os.environ.get("CLOUDINARY_SECURE_DISTRIBUTION"),
private_cdn=os.environ.get("CLOUDINARY_PRIVATE_CDN") == 'true',
api_proxy=os.environ.get("CLOUDINARY_API_PROXY"),
)
elif os.environ.get("CLOUDINARY_URL"):
cloudinary_url = os.environ.get("CLOUDINARY_URL")
self._parse_cloudinary_url(cloudinary_url)
def _parse_cloudinary_url(self, cloudinary_url):
uri = urlparse(cloudinary_url)
if not self._is_url_scheme_valid(uri):
raise ValueError("Invalid CLOUDINARY_URL scheme. Expecting to start with 'cloudinary://'")
for k, v in parse_qs(uri.query).items():
if self._is_nested_key(k):
self._put_nested_key(k, v)
else:
self.__dict__[k] = v[0]
self.update(
cloud_name=uri.hostname,
api_key=uri.username,
api_secret=uri.password,
private_cdn=uri.path != ''
)
if uri.path != '':
self.update(secure_distribution=uri.path[1:])
self._load_config_from_env()
def __getattr__(self, i):
if i in self.__dict__:
return self.__dict__[i]
else:
return None
return self.__dict__.get(i)
def update(self, **keywords):
for k, v in keywords.items():
self.__dict__[k] = v
def _is_nested_key(self, key):
@staticmethod
def _is_nested_key(key):
return re.match(r'\w+\[\w+\]', key)
def _put_nested_key(self, key, value):
@ -160,8 +129,7 @@ class Config(object):
value = value[0]
outer[last_key] = value
@staticmethod
def _is_url_scheme_valid(url):
def _is_url_scheme_valid(self, url):
"""
Helper function. Validates url scheme
@ -169,9 +137,81 @@ class Config(object):
:return: bool True on success or False on failure
"""
if not url.scheme or url.scheme.lower() != URI_SCHEME:
return False
return True
return url.scheme.lower() == self._uri_scheme
@staticmethod
def _parse_cloudinary_url(cloudinary_url):
return urlparse(cloudinary_url)
@abc.abstractmethod
def _config_from_parsed_url(self, parsed_url):
"""Extract additional config from the parsed URL."""
raise NotImplementedError()
def _setup_from_parsed_url(self, parsed_url):
config_from_parsed_url = self._config_from_parsed_url(parsed_url)
self.update(**config_from_parsed_url)
for k, v in parse_qs(parsed_url.query).items():
if self._is_nested_key(k):
self._put_nested_key(k, v)
else:
self.__dict__[k] = v[0]
def _load_from_url(self, url):
parsed_url = self._parse_cloudinary_url(url)
return self._setup_from_parsed_url(parsed_url)
@abc.abstractmethod
def _load_config_from_env(self):
"""Load config from environment variables or URL."""
raise NotImplementedError()
def update(self, **keywords):
for k, v in keywords.items():
self.__dict__[k] = v
class Config(BaseConfig):
def __init__(self):
self._uri_scheme = URI_SCHEME
super(Config, self).__init__()
if not self.signature_algorithm:
self.signature_algorithm = utils.SIGNATURE_SHA1
def _config_from_parsed_url(self, parsed_url):
if not self._is_url_scheme_valid(parsed_url):
raise ValueError("Invalid CLOUDINARY_URL scheme. Expecting to start with 'cloudinary://'")
is_private_cdn = parsed_url.path != ""
result = {
"cloud_name": parsed_url.hostname,
"api_key": parsed_url.username,
"api_secret": parsed_url.password,
"private_cdn": is_private_cdn,
}
if is_private_cdn:
result.update({"secure_distribution": parsed_url.path[1:]})
return result
def _load_config_from_env(self):
if os.environ.get("CLOUDINARY_CLOUD_NAME"):
config_keys = [key for key in os.environ.keys()
if key.startswith("CLOUDINARY_") and key != "CLOUDINARY_URL"]
for full_key in config_keys:
conf_key = full_key[len("CLOUDINARY_"):].lower()
conf_val = os.environ[full_key]
if conf_val in ["true", "false"]:
conf_val = conf_val == "true"
self.update(**{conf_key: conf_val})
elif os.environ.get("CLOUDINARY_URL"):
self._load_from_url(os.environ.get("CLOUDINARY_URL"))
_config = Config()
@ -257,7 +297,8 @@ class CloudinaryResource(object):
return self.get_prep_value() + '#' + self.get_expected_signature()
def get_expected_signature(self):
return utils.api_sign_request({"public_id": self.public_id, "version": self.version}, config().api_secret)
return utils.api_sign_request({"public_id": self.public_id, "version": self.version}, config().api_secret,
config().signature_algorithm)
@property
def url(self):
@ -377,7 +418,7 @@ class CloudinaryResource(object):
max_images = srcset_data.get("max_images", 20)
transformation = srcset_data.get("transformation")
kbytes_step = int(ceil(float(bytes_step)/1024))
kbytes_step = int(ceil(float(bytes_step) / 1024))
breakpoints_width_param = "auto:breakpoints_{min_width}_{max_width}_{kbytes_step}_{max_images}:json".format(
min_width=min_width, max_width=max_width, kbytes_step=kbytes_step, max_images=max_images)
@ -623,7 +664,7 @@ class CloudinaryResource(object):
return utils.cloudinary_url(poster_options['public_id'], **poster_options)[0]
def _populate_video_source_tags(self, source, options):
def _populate_video_source_tags(self, source, options):
"""
Helper function for video tag, populates source tags from provided options.
@ -750,7 +791,7 @@ class CloudinaryResource(object):
srcset_data = srcset_data.copy()
srcset_data.update(options.pop("srcset", dict()))
responsive_attrs = self._generate_image_responsive_attributes(attrs, srcset_data, **options)
responsive_attrs = self._generate_image_responsive_attributes(attrs, srcset_data, **options)
attrs.update(responsive_attrs)