Skip to content

Commit

Permalink
Fix black config (#89)
Browse files Browse the repository at this point in the history
* add the notifications_utils directory to the include list for black

* run black
  • Loading branch information
smcmurtry authored Jul 12, 2021
1 parent 321dda7 commit 98dbf4c
Show file tree
Hide file tree
Showing 24 changed files with 828 additions and 1,024 deletions.
8 changes: 4 additions & 4 deletions notifications_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
# regexes for use in recipients.validate_email_address.
# Valid characters taken from https://en.wikipedia.org/wiki/Email_address#Local-part
# Note: Normal apostrophe eg `Firstname-o'surname@domain.com` is allowed.
hostname_part = re.compile(r'^(xn-|[a-z0-9]+)(-[a-z0-9]+)*$', re.IGNORECASE)
tld_part = re.compile(r'^([a-z]{2,63}|xn--([a-z0-9]+-)*[a-z0-9]+)$', re.IGNORECASE)
hostname_part = re.compile(r"^(xn-|[a-z0-9]+)(-[a-z0-9]+)*$", re.IGNORECASE)
tld_part = re.compile(r"^([a-z]{2,63}|xn--([a-z0-9]+-)*[a-z0-9]+)$", re.IGNORECASE)
VALID_LOCAL_CHARS = r"a-zA-ZÀ-ÿ0-9.!#$%&'*+/=?^_`{|}~\-"
EMAIL_REGEX_PATTERN = r'^[{}]+@([^.@][^@\s]+)$'.format(VALID_LOCAL_CHARS)
EMAIL_REGEX_PATTERN = r"^[{}]+@([^.@][^@\s]+)$".format(VALID_LOCAL_CHARS)
email_with_smart_quotes_regex = re.compile(
# matches wider than an email - everything between an at sign and the nearest whitespace
r'(^|\s)\S+@\S+(\s|$)',
r"(^|\s)\S+@\S+(\s|$)",
flags=re.MULTILINE,
)
6 changes: 3 additions & 3 deletions notifications_utils/base64_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@


def base64_to_bytes(key):
return urlsafe_b64decode(key + '==')
return urlsafe_b64decode(key + "==")


def bytes_to_base64(bytes):
# remove trailing = to save precious bytes
return urlsafe_b64encode(bytes).decode('ascii').rstrip('=')
return urlsafe_b64encode(bytes).decode("ascii").rstrip("=")


def base64_to_uuid(value):
# uuids are 16 bytes, and will always have two ==s of padding
return UUID(bytes=urlsafe_b64decode(value.encode('ascii') + b'=='))
return UUID(bytes=urlsafe_b64decode(value.encode("ascii") + b"=="))


def uuid_to_base64(value):
Expand Down
20 changes: 8 additions & 12 deletions notifications_utils/clients/antivirus/antivirus_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ def __init__(self, message=None, status_code=None):
@classmethod
def from_exception(cls, e):
try:
message = e.response.json()['error']
message = e.response.json()["error"]
status_code = e.response.status_code
except (TypeError, ValueError, AttributeError, KeyError):
message = 'connection error'
message = "connection error"
status_code = 503

return cls(message, status_code)
Expand All @@ -25,31 +25,27 @@ def __init__(self, api_host=None, auth_token=None):
self.auth_token = auth_token

def init_app(self, app):
self.api_host = app.config['ANTIVIRUS_API_HOST']
self.auth_token = app.config['ANTIVIRUS_API_KEY']
self.api_host = app.config["ANTIVIRUS_API_HOST"]
self.auth_token = app.config["ANTIVIRUS_API_KEY"]

def scan(self, document_stream):
try:
response = requests.post(
"{}/scan".format(self.api_host),
headers={
'Authorization': "Bearer {}".format(self.auth_token),
"Authorization": "Bearer {}".format(self.auth_token),
},
files={
'document': document_stream
}
files={"document": document_stream},
)

response.raise_for_status()

except requests.RequestException as e:
error = AntivirusError.from_exception(e)
current_app.logger.warning(
'Notify Antivirus API request failed with error: {}'.format(error.message)
)
current_app.logger.warning("Notify Antivirus API request failed with error: {}".format(error.message))

raise error
finally:
document_stream.seek(0)

return response.json()['ok']
return response.json()["ok"]
47 changes: 24 additions & 23 deletions notifications_utils/clients/redis/redis_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ def prepare_value(val):
value to redis-py.
"""
# things redis-py natively supports
if isinstance(val, (
bytes,
str,
numbers.Number,
)):
if isinstance(
val,
(
bytes,
str,
numbers.Number,
),
):
return val
# things we know we can safely cast to string
elif isinstance(val, (
uuid.UUID,
)):
elif isinstance(val, (uuid.UUID,)):
return str(val)
else:
raise ValueError('cannot cast {} to a string'.format(type(val)))
raise ValueError("cannot cast {} to a string".format(type(val)))


class RedisClient:
Expand All @@ -41,7 +42,7 @@ class RedisClient:
scripts = {}

def init_app(self, app):
self.active = app.config.get('REDIS_ENABLED')
self.active = app.config.get("REDIS_ENABLED")
if self.active:
self.redis_store.init_app(app)

Expand All @@ -52,7 +53,7 @@ def register_scripts(self):
# delete keys matching a pattern supplied as a parameter. Does so in batches of 5000 to prevent unpack from
# exceeding lua's stack limit, and also to prevent errors if no keys match the pattern.
# Inspired by https://gist.github.com/ddre54/0a4751676272e0da8186
self.scripts['delete-keys-by-pattern'] = self.redis_store.register_script(
self.scripts["delete-keys-by-pattern"] = self.redis_store.register_script(
"""
local keys = redis.call('keys', ARGV[1])
local deleted = 0
Expand All @@ -77,7 +78,7 @@ def delete_cache_keys_by_pattern(self, pattern):
Use \ to escape special characters if you want to match them verbatim
"""
if self.active:
return self.scripts['delete-keys-by-pattern'](args=[pattern])
return self.scripts["delete-keys-by-pattern"](args=[pattern])
return 0

def exceeded_rate_limit(self, cache_key, limit, interval, raise_exception=False):
Expand Down Expand Up @@ -117,13 +118,13 @@ def exceeded_rate_limit(self, cache_key, limit, interval, raise_exception=False)
pipe = self.redis_store.pipeline()
when = time()
pipe.zadd(cache_key, {when: when})
pipe.zremrangebyscore(cache_key, '-inf', when - interval)
pipe.zremrangebyscore(cache_key, "-inf", when - interval)
pipe.zcard(cache_key)
pipe.expire(cache_key, interval)
result = pipe.execute()
return result[2] > limit
except Exception as e:
self.__handle_exception(e, raise_exception, 'rate-limit-pipeline', cache_key)
self.__handle_exception(e, raise_exception, "rate-limit-pipeline", cache_key)
return False
else:
return False
Expand All @@ -135,23 +136,23 @@ def set(self, key, value, ex=None, px=None, nx=False, xx=False, raise_exception=
try:
self.redis_store.set(key, value, ex, px, nx, xx)
except Exception as e:
self.__handle_exception(e, raise_exception, 'set', key)
self.__handle_exception(e, raise_exception, "set", key)

def incr(self, key, raise_exception=False):
key = prepare_value(key)
if self.active:
try:
return self.redis_store.incr(key)
except Exception as e:
self.__handle_exception(e, raise_exception, 'incr', key)
self.__handle_exception(e, raise_exception, "incr", key)

def get(self, key, raise_exception=False):
key = prepare_value(key)
if self.active:
try:
return self.redis_store.get(key)
except Exception as e:
self.__handle_exception(e, raise_exception, 'get', key)
self.__handle_exception(e, raise_exception, "get", key)

return None

Expand All @@ -165,15 +166,15 @@ def increment_hash_value(self, key, value, raise_exception=False, incr_by=1):
try:
return self.redis_store.hincrby(key, value, incr_by)
except Exception as e:
self.__handle_exception(e, raise_exception, 'increment_hash_value', key)
self.__handle_exception(e, raise_exception, "increment_hash_value", key)

def get_all_from_hash(self, key, raise_exception=False):
key = prepare_value(key)
if self.active:
try:
return self.redis_store.hgetall(key)
except Exception as e:
self.__handle_exception(e, raise_exception, 'get_all_from_hash', key)
self.__handle_exception(e, raise_exception, "get_all_from_hash", key)

def set_hash_and_expire(self, key, values, expire_in_seconds, raise_exception=False):
key = prepare_value(key)
Expand All @@ -183,25 +184,25 @@ def set_hash_and_expire(self, key, values, expire_in_seconds, raise_exception=Fa
self.redis_store.hmset(key, values)
return self.redis_store.expire(key, expire_in_seconds)
except Exception as e:
self.__handle_exception(e, raise_exception, 'set_hash_and_expire', key)
self.__handle_exception(e, raise_exception, "set_hash_and_expire", key)

def expire(self, key, expire_in_seconds, raise_exception=False):
key = prepare_value(key)
if self.active:
try:
self.redis_store.expire(key, expire_in_seconds)
except Exception as e:
self.__handle_exception(e, raise_exception, 'expire', key)
self.__handle_exception(e, raise_exception, "expire", key)

def delete(self, *keys, raise_exception=False):
keys = [prepare_value(k) for k in keys]
if self.active:
try:
self.redis_store.delete(*keys)
except Exception as e:
self.__handle_exception(e, raise_exception, 'delete', ', '.join(keys))
self.__handle_exception(e, raise_exception, "delete", ", ".join(keys))

def __handle_exception(self, e, raise_exception, operation, key_name):
current_app.logger.exception('Redis error performing {} on {}'.format(operation, key_name))
current_app.logger.exception("Redis error performing {} on {}".format(operation, key_name))
if raise_exception:
raise e
19 changes: 7 additions & 12 deletions notifications_utils/clients/statsd/statsd_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _cached_host(self):
except Exception as e:
# If we get an error, store `None` in the cache so that we don't keep
# trying to retrieve the DNS if DNS server is having issues
current_app.logger.warning('Error resolving statsd DNS: {}'.format(str(e)))
current_app.logger.warning("Error resolving statsd DNS: {}".format(str(e)))
return None

def _send(self, data):
Expand All @@ -39,29 +39,24 @@ def _send(self, data):
# Don't send to statsd
if host is None:
return
self._sock.sendto(data.encode('ascii'), (host, self._port))
self._sock.sendto(data.encode("ascii"), (host, self._port))
except Exception as e:
current_app.logger.warning('Error sending statsd metric: {}'.format(str(e)))
current_app.logger.warning("Error sending statsd metric: {}".format(str(e)))
pass


class StatsdClient():
class StatsdClient:
def __init__(self):
self.statsd_client = None

def init_app(self, app, *args, **kwargs):
app.statsd_client = self
self.active = app.config.get('STATSD_ENABLED')
self.namespace = "{}.notifications.{}.".format(
app.config.get('NOTIFY_ENVIRONMENT'),
app.config.get('NOTIFY_APP_NAME')
)
self.active = app.config.get("STATSD_ENABLED")
self.namespace = "{}.notifications.{}.".format(app.config.get("NOTIFY_ENVIRONMENT"), app.config.get("NOTIFY_APP_NAME"))

if self.active:
self.statsd_client = NotifyStatsClient(
app.config.get('STATSD_HOST'),
app.config.get('STATSD_PORT'),
prefix=app.config.get('STATSD_PREFIX')
app.config.get("STATSD_HOST"), app.config.get("STATSD_PORT"), prefix=app.config.get("STATSD_PREFIX")
)

def format_stat_name(self, stat):
Expand Down
63 changes: 25 additions & 38 deletions notifications_utils/clients/zendesk/zendesk_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ def __init__(self, response):
self.response = response


class ZendeskClient():
PRIORITY_URGENT = 'urgent'
PRIORITY_HIGH = 'high'
PRIORITY_NORMAL = 'normal'
PRIORITY_LOW = 'low'
class ZendeskClient:
PRIORITY_URGENT = "urgent"
PRIORITY_HIGH = "high"
PRIORITY_NORMAL = "normal"
PRIORITY_LOW = "low"

TYPE_PROBLEM = 'problem'
TYPE_INCIDENT = 'incident'
TYPE_QUESTION = 'question'
TYPE_TASK = 'task'
TYPE_PROBLEM = "problem"
TYPE_INCIDENT = "incident"
TYPE_QUESTION = "question"
TYPE_TASK = "task"

TAGS_P2 = 'govuk_notify_support'
TAGS_P1 = 'govuk_notify_emergency'
TAGS_P2 = "govuk_notify_support"
TAGS_P1 = "govuk_notify_emergency"

# Group: 3rd Line--Notify Support
NOTIFY_GROUP_ID = 360000036529
Expand All @@ -28,9 +28,9 @@ class ZendeskClient():
NOTIFY_ORG_ID = 21891972

# the account used to authenticate with. If no requester is provided, the ticket will come from this account.
NOTIFY_ZENDESK_EMAIL = 'zd-api-notify@digital.cabinet-office.gov.uk'
NOTIFY_ZENDESK_EMAIL = "zd-api-notify@digital.cabinet-office.gov.uk"

ZENDESK_TICKET_URL = 'https://govuk.zendesk.com/api/v2/tickets.json'
ZENDESK_TICKET_URL = "https://govuk.zendesk.com/api/v2/tickets.json"

def __init__(self):
self.api_key = None
Expand All @@ -41,7 +41,7 @@ def __init__(self):
self.default_person_email = None

def init_app(self, app, *args, **kwargs):
self.api_key = app.config.get('ZENDESK_API_KEY')
self.api_key = app.config.get("ZENDESK_API_KEY")

def create_ticket(
self,
Expand All @@ -54,41 +54,28 @@ def create_ticket(
tags=None,
):
data = {
'ticket': {
'subject': subject,
'comment': {
'body': message
},
'group_id': self.NOTIFY_GROUP_ID,
'organization_id': self.NOTIFY_ORG_ID,
'priority': self.PRIORITY_URGENT if p1 else self.PRIORITY_NORMAL,
'tags': [self.TAGS_P1 if p1 else self.TAGS_P2] + (tags or []),
'type': ticket_type
"ticket": {
"subject": subject,
"comment": {"body": message},
"group_id": self.NOTIFY_GROUP_ID,
"organization_id": self.NOTIFY_ORG_ID,
"priority": self.PRIORITY_URGENT if p1 else self.PRIORITY_NORMAL,
"tags": [self.TAGS_P1 if p1 else self.TAGS_P2] + (tags or []),
"type": ticket_type,
}
}

# if no requester provided, then the call came from within Notify 👻
if user_email:
data['ticket']['requester'] = {
'email': user_email,
'name': user_name or '(no name supplied)'
}
data["ticket"]["requester"] = {"email": user_email, "name": user_name or "(no name supplied)"}

response = requests.post(
self.ZENDESK_TICKET_URL,
json=data,
auth=(
'{}/token'.format(self.NOTIFY_ZENDESK_EMAIL),
self.api_key
)
self.ZENDESK_TICKET_URL, json=data, auth=("{}/token".format(self.NOTIFY_ZENDESK_EMAIL), self.api_key)
)

if response.status_code != 201:
current_app.logger.error(
"Zendesk create ticket request failed with {} '{}'".format(
response.status_code,
response.json()
)
"Zendesk create ticket request failed with {} '{}'".format(response.status_code, response.json())
)

raise ZendeskError(response)
Loading

0 comments on commit 98dbf4c

Please sign in to comment.