Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions lib/createsend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from __future__ import absolute_import

# -*- coding: utf-8 -*-
__title__ = 'createsend-python'
__author__ = 'Campaign Monitor'
__license__ = 'MIT'
Expand Down
4 changes: 1 addition & 3 deletions lib/createsend/administrator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Administrator(CreateSendBase):

def __init__(self, auth=None, email_address=None):
self.email_address = email_address
super(Administrator, self).__init__(auth)
super().__init__(auth)

def get(self, email_address=None):
"""Gets an administrator by email address."""
Expand Down
6 changes: 2 additions & 4 deletions lib/createsend/campaign.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Campaign(CreateSendBase):

def __init__(self, auth=None, campaign_id=None):
self.campaign_id = campaign_id
super(Campaign, self).__init__(auth)
super().__init__(auth)

def create(self, client_id, subject, name, from_name, from_email, reply_to, html_url,
text_url, list_ids, segment_ids):
Expand Down Expand Up @@ -190,4 +188,4 @@ def bounces(self, date="", page=1, page_size=1000, order_field="date", order_dir
return json_to_py(response)

def uri_for(self, action):
return "/campaigns/%s/%s.json" % (self.campaign_id, action)
return f"/campaigns/{self.campaign_id}/{action}.json"
6 changes: 2 additions & 4 deletions lib/createsend/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Client(CreateSendBase):

def __init__(self, auth=None, client_id=None):
self.client_id = client_id
super(Client, self).__init__(auth)
super().__init__(auth)

def create(self, company, timezone, country):
"""Creates a client."""
Expand Down Expand Up @@ -186,4 +184,4 @@ def journeys(self):
return json_to_py(response)

def uri_for(self, action):
return "/clients/%s/%s.json" % (self.client_id, action)
return f"/clients/{self.client_id}/{action}.json"
24 changes: 11 additions & 13 deletions lib/createsend/createsend.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
from __future__ import absolute_import

import sys
import platform
import base64
import gzip
import os
import socket
import json
from six import BytesIO
from six.moves.urllib.parse import parse_qs, urlencode, urlparse
from io import BytesIO
from urllib.parse import parse_qs, urlencode, urlparse

from createsend.utils import VerifiedHTTPSConnection, json_to_py, get_faker

Expand All @@ -26,7 +24,7 @@ def __str__(self):
# self.data should contain Code, Message and optionally ResultData
extra = ("\nExtra result data: %s" % self.data.ResultData) if hasattr(
self.data, 'ResultData') else ""
return "The CreateSend API responded with the following error - %s: %s%s" % (self.data.Code, self.data.Message, extra)
return f"The CreateSend API responded with the following error - {self.data.Code}: {self.data.Message}{extra}"


class ClientError(Exception):
Expand Down Expand Up @@ -59,7 +57,7 @@ class ExpiredOAuthToken(Unauthorized):
pass


class CreateSendBase(object):
class CreateSendBase:
auth_details = None
timeout = socket._GLOBAL_DEFAULT_TIMEOUT # passed to VerifiedHTTPSConnection

Expand All @@ -77,7 +75,7 @@ def authorize_url(self, client_id, redirect_uri, scope, state=None):
]
if state:
params.append(('state', state))
return "%s?%s" % (CreateSend.oauth_uri, urlencode(params))
return f"{CreateSend.oauth_uri}?{urlencode(params)}"

def exchange_token(self, client_id, client_secret, redirect_uri, code):
"""Exchange a provided OAuth code for an OAuth access token, 'expires in'
Expand All @@ -95,7 +93,7 @@ def exchange_token(self, client_id, client_secret, redirect_uri, code):
r = json_to_py(response)
if hasattr(r, 'error') and hasattr(r, 'error_description'):
err = "Error exchanging code for access token: "
err += "%s - %s" % (r.error, r.error_description)
err += f"{r.error} - {r.error_description}"
raise Exception(err)
access_token, expires_in, refresh_token = r.access_token, r.expires_in, r.refresh_token
return [access_token, expires_in, refresh_token]
Expand Down Expand Up @@ -156,7 +154,7 @@ def make_request(self, method, path, params={}, body="", username=None,
overridden (e.g. when using the apikey route with username and password)."""
if username and password:
headers['Authorization'] = "Basic %s" % base64.b64encode(
("%s:%s" % (username, password)).encode()).decode()
(f"{username}:{password}").encode()).decode()
elif self.auth_details:
if 'api_key' in self.auth_details and self.auth_details['api_key']:
headers['Authorization'] = "Basic %s" % base64.b64encode(
Expand All @@ -171,7 +169,7 @@ def make_request(self, method, path, params={}, body="", username=None,
if self.fake_web:
# Check that the actual url which would be requested matches
# self.faker.url.
actual_url = "https://%s%s" % (parsed_base_uri.netloc,
actual_url = "https://{}{}".format(parsed_base_uri.netloc,
self.build_url(parsed_base_uri, path, params))
self.faker.actual_url = actual_url

Expand All @@ -186,7 +184,7 @@ def same_urls(url_a, url_b):
a.fragment == b.fragment
)
if not same_urls(self.faker.url, actual_url):
raise Exception("Faker's expected URL (%s) doesn't match actual URL (%s)" % (
raise Exception("Faker's expected URL ({}) doesn't match actual URL ({})".format(
self.faker.url, actual_url))

self.faker.actual_body = body
Expand All @@ -195,7 +193,7 @@ def same_bodies(body_a, body_b):
return json.loads(body_a) == json.loads(body_b)
if self.faker.body is not None:
if not same_bodies(self.faker.body, body):
raise Exception("Faker's expected body (%s) doesn't match actual body (%s)" % (
raise Exception("Faker's expected body ({}) doesn't match actual body ({})".format(
self.faker.body, body))

data = self.faker.open() if self.faker else ''
Expand Down Expand Up @@ -265,7 +263,7 @@ class CreateSend(CreateSendBase):
user_agent = default_user_agent

def __init__(self, auth=None):
super(CreateSend, self).__init__(auth)
super().__init__(auth)

def clients(self):
"""Gets your clients."""
Expand Down
4 changes: 1 addition & 3 deletions lib/createsend/journey.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from createsend.createsend import CreateSendBase
from createsend.utils import json_to_py

Expand All @@ -9,7 +7,7 @@ class Journey(CreateSendBase):

def __init__(self, auth=None, journey_id=None):
self.journey_id = journey_id
super(Journey, self).__init__(auth)
super().__init__(auth)

def summary(self):
"""Gets the summary of the journey"""
Expand Down
6 changes: 2 additions & 4 deletions lib/createsend/journey_email.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

from createsend.createsend import CreateSendBase
from createsend.utils import json_to_py

Expand All @@ -9,7 +7,7 @@ class JourneyEmail(CreateSendBase):

def __init__(self, auth=None, journey_email_id=None):
self.journey_email_id = journey_email_id
super(JourneyEmail, self).__init__(auth)
super().__init__(auth)

def bounces(self, date=None, page=None, page_size=None, order_direction=None):
"""Retrieves the bounces for this journey email."""
Expand Down Expand Up @@ -46,5 +44,5 @@ def get_journey_email_response(self, date, page, page_size, order_direction, uri
return json_to_py(response)

def uri_for(self, action):
return "/journeys/email/%s/%s.json" % (self.journey_email_id, action)
return f"/journeys/email/{self.journey_email_id}/{action}.json"

8 changes: 3 additions & 5 deletions lib/createsend/list.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import absolute_import

import json
from six.moves.urllib.parse import quote
from urllib.parse import quote

from createsend.createsend import CreateSendBase
from createsend.utils import json_to_py
Expand All @@ -12,7 +10,7 @@ class List(CreateSendBase):

def __init__(self, auth=None, list_id=None):
self.list_id = list_id
super(List, self).__init__(auth)
super().__init__(auth)

def create(self, client_id, title, unsubscribe_page, confirmed_opt_in,
confirmation_success_page, unsubscribe_setting="AllClientLists"):
Expand Down Expand Up @@ -206,4 +204,4 @@ def deactivate_webhook(self, webhook_id):
"webhooks/%s/deactivate" % webhook_id), ' ')

def uri_for(self, action):
return "/lists/%s/%s.json" % (self.list_id, action)
return f"/lists/{self.list_id}/{action}.json"
4 changes: 1 addition & 3 deletions lib/createsend/person.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -12,7 +10,7 @@ class Person(CreateSendBase):
def __init__(self, auth=None, client_id=None, email_address=None):
self.client_id = client_id
self.email_address = email_address
super(Person, self).__init__(auth)
super().__init__(auth)

def get(self, client_id=None, email_address=None):
"""Gets a person by client ID and email address."""
Expand Down
6 changes: 2 additions & 4 deletions lib/createsend/segment.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Segment(CreateSendBase):

def __init__(self, auth=None, segment_id=None):
self.segment_id = segment_id
super(Segment, self).__init__(auth)
super().__init__(auth)

def create(self, list_id, title, rulegroups):
"""Creates a new segment."""
Expand Down Expand Up @@ -63,4 +61,4 @@ def delete(self):
response = self._delete("/segments/%s.json" % self.segment_id)

def uri_for(self, action):
return "/segments/%s/%s.json" % (self.segment_id, action)
return f"/segments/{self.segment_id}/{action}.json"
4 changes: 1 addition & 3 deletions lib/createsend/subscriber.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase, BadRequest
Expand All @@ -12,7 +10,7 @@ class Subscriber(CreateSendBase):
def __init__(self, auth=None, list_id=None, email_address=None):
self.list_id = list_id
self.email_address = email_address
super(Subscriber, self).__init__(auth)
super().__init__(auth)

def get(self, list_id=None, email_address=None, include_tracking_preference=False):
"""Gets a subscriber by list ID and email address."""
Expand Down
4 changes: 1 addition & 3 deletions lib/createsend/template.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Template(CreateSendBase):

def __init__(self, auth=None, template_id=None):
self.template_id = template_id
super(Template, self).__init__(auth)
super().__init__(auth)

def create(self, client_id, name, html_url, zip_url):
"""Creates a new email template."""
Expand Down
8 changes: 3 additions & 5 deletions lib/createsend/transactional.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import absolute_import

import json

from createsend.createsend import CreateSendBase
Expand All @@ -11,7 +9,7 @@ class Transactional(CreateSendBase):

def __init__(self, auth=None, client_id=None):
self.client_id = client_id
super(Transactional, self).__init__(auth)
super().__init__(auth)

def smart_email_list(self, status="all", client_id=None):
"""Gets the smart email list."""
Expand All @@ -20,7 +18,7 @@ def smart_email_list(self, status="all", client_id=None):
"/transactional/smartEmail?status=%s" % status)
else:
response = self._get(
"/transactional/smartEmail?status=%s&clientID=%s" % (status, client_id))
f"/transactional/smartEmail?status={status}&clientID={client_id}")
return json_to_py(response)

def smart_email_details(self, smart_email_id):
Expand Down Expand Up @@ -93,7 +91,7 @@ def message_timeline(self, params={}):
def message_details(self, message_id, statistics=False, exclude_message_body=False):
"""Gets the details of this message."""
response = self._get(
"/transactional/messages/%s?statistics=%s&excludemessagebody=%s" % (message_id, statistics, exclude_message_body))
f"/transactional/messages/{message_id}?statistics={statistics}&excludemessagebody={exclude_message_body}")
return json_to_py(response)

def message_resend(self, message_id):
Expand Down
31 changes: 10 additions & 21 deletions lib/createsend/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from __future__ import absolute_import

import os
import re
from six.moves.http_client import HTTPSConnection
from http.client import HTTPSConnection
import socket
import ssl
import json
Expand Down Expand Up @@ -101,21 +99,12 @@ def connect(self):

cert_path = os.path.join(os.path.dirname(__file__), 'cacert.pem')

# for >= py3.7, mandatory since 3.12
if hasattr(ssl.SSLContext, 'wrap_socket'):
context = ssl.SSLContext()
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(cert_path)
if hasattr(self, 'cert_file') and hasattr(self, 'key_file') and self.cert_file and self.key_file:
context.load_cert_chain(certfile=self.cert_file, keyfile=self.key_file)
self.sock = context.wrap_socket(sock)
else:
self.sock = ssl.wrap_socket(
sock,
self.key_file,
self.cert_file,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=cert_path)
context = ssl.SSLContext()
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(cert_path)
if hasattr(self, 'cert_file') and hasattr(self, 'key_file') and self.cert_file and self.key_file:
context.load_cert_chain(certfile=self.cert_file, keyfile=self.key_file)
self.sock = context.wrap_socket(sock)

try:
match_hostname(self.sock.getpeercert(), self.host)
Expand Down Expand Up @@ -154,12 +143,12 @@ def validate_consent_to_track(user_input):
user_input = user_input.lower()
if user_input in VALID_CONSENT_TO_TRACK_VALUES:
return
raise ClientError("Consent to track value must be one of %s" % (VALID_CONSENT_TO_TRACK_VALUES,))
raise ClientError(f"Consent to track value must be one of {VALID_CONSENT_TO_TRACK_VALUES}")


def get_faker(expected_url, filename, status=None, body=None):

class Faker(object):
class Faker:
"""Represents a fake web request, including the expected URL, an open
function which reads the expected response from a fixture file, and the
expected response status code."""
Expand All @@ -172,7 +161,7 @@ def __init__(self, expected_url, filename, status, body=None):

def open(self):
if self.filename:
return open("%s/../test/fixtures/%s" % (os.path.dirname(os.path.dirname(__file__)), self.filename), mode='rb').read()
return open(f"{os.path.dirname(os.path.dirname(__file__))}/../test/fixtures/{self.filename}", mode='rb').read()
else:
return ''

Expand Down
2 changes: 1 addition & 1 deletion samples/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

# Get list of clients
for cl in clients:
print("Client: %s - Id: %s" % (cl.Name, cl.ClientID))
print(f"Client: {cl.Name} - Id: {cl.ClientID}")
Loading