Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/2.12.1' into release/2.12.1
Browse files Browse the repository at this point in the history
  • Loading branch information
liampauling committed Mar 9, 2021
2 parents 6fd80ce + a7980ec commit d0aeb94
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 16 deletions.
7 changes: 5 additions & 2 deletions betfairlightweight/apiclient.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Union, Tuple

import requests

from .baseclient import BaseClient
Expand All @@ -12,7 +14,7 @@ def __init__(
app_key: str = None,
certs: str = None,
locale: str = None,
cert_files: list = None,
cert_files: Union[Tuple[str], str, None] = None,
lightweight: bool = False,
session: requests.Session = None,
):
Expand All @@ -24,7 +26,8 @@ def __init__(
:param str app_key: App Key for account, if None will look in .bashprofile
:param str certs: Directory for certificates, if None will look in /certs
:param str locale: Exchange to be used, defaults to international (.com) exchange
:param list cert_files: Certificate and key files. If None will use `self.certs`
:param list cert_files: if String, path to ssl client cert file (.pem).
If Tuple, ('cert', 'key') path pair. If None will use `self.certs`
:param bool lightweight: If True endpoints will return dict not a resource (22x faster)
:param requests.Session session: Pass requests session object, defaults to a new request each request
"""
Expand Down
29 changes: 17 additions & 12 deletions betfairlightweight/baseclient.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import time
from typing import Union, Tuple

import requests
import collections

Expand Down Expand Up @@ -54,7 +56,7 @@ def __init__(
app_key: str = None,
certs: str = None,
locale: str = None,
cert_files: list = None,
cert_files: Union[Tuple[str], str, None] = None,
lightweight: bool = False,
session: requests.Session = None,
):
Expand All @@ -66,7 +68,8 @@ def __init__(
:param str app_key: App Key for account, if None will look in .bashprofile
:param str certs: Directory for certificates, if None will look in /certs
:param str locale: Exchange to be used, defaults to international (.com) exchange
:param list cert_files: Certificate and key files. If None will use `self.certs`
:param list cert_files: if String, path to ssl client cert file (.pem).
If Tuple, ('cert', 'key') path pair. If None will use `self.certs`
:param bool lightweight: If True endpoints will return dict not a resource (22x faster)
:param requests.Session session: Pass requests session object, defaults to a new request each request
"""
Expand Down Expand Up @@ -144,12 +147,12 @@ def session_expired(self) -> bool:
return False

@property
def cert(self) -> list:
def cert(self) -> Union[Tuple[str], str]:
"""
The betfair certificates, by default it looks for the
certificates in /certs/.
:return: Path of cert files
:return: Tuple of cert files path or single path.
:rtype: str
"""
if self.cert_files is not None:
Expand All @@ -162,20 +165,22 @@ def cert(self) -> list:
except FileNotFoundError as e:
raise CertsError(str(e))

cert = None
key = None
cert, key, pem = None, None, None
for file in cert_path:
ext = os.path.splitext(file)[-1]
if ext in [".crt", ".cert"]:
cert = os.path.join(ssl_path, file)
elif ext == ".key":
key = os.path.join(ssl_path, file)
if cert is None or key is None:
raise CertsError(
"Certificates not found in directory: '%s' (make sure .crt and .key file is present)"
% ssl_path
)
return [cert, key]
elif ext == ".pem":
pem = os.path.join(ssl_path, file)
if cert and key:
return (cert, key)
if pem:
return pem
msg = "Certificates not found in directory: '%s'" % ssl_path
hint = " (make sure .crt and .key pair or a single .pem is present)"
raise CertsError(msg + hint)

@property
def login_headers(self) -> dict:
Expand Down
31 changes: 29 additions & 2 deletions tests/unit/test_baseclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_client_certs(self):
@mock.patch("betfairlightweight.baseclient.os.listdir")
def test_client_certs_mocked(self, mock_listdir):
mock_listdir.return_value = [".DS_Store", "client-2048.crt", "client-2048.key"]
assert self.client.cert == ["/fail/client-2048.crt", "/fail/client-2048.key"]
assert self.client.cert == ("/fail/client-2048.crt", "/fail/client-2048.key")

@mock.patch("betfairlightweight.baseclient.os.listdir")
def test_client_certs_missing(self, mock_listdir):
Expand Down Expand Up @@ -183,7 +183,7 @@ def test_client_logout(self):


def normpaths(p):
return list(map(os.path.normpath, p))
return tuple(map(os.path.normpath, p))


class BaseClientRelativePathTest(unittest.TestCase):
Expand All @@ -201,6 +201,20 @@ def test_client_certs_mocked(self, mock_listdir):
["../fail/client-2048.crt", "../fail/client-2048.key"]
)

@mock.patch("betfairlightweight.baseclient.os.listdir")
def test_client_single_file_cert_mocked(self, mock_listdir):
mock_listdir.return_value = normpaths([".DS_Store", "client-2048.pem"])
assert self.client.cert == os.path.normpath("../fail/client-2048.pem")

@mock.patch("betfairlightweight.baseclient.os.listdir")
def test_client_crt_key_preferred_over_pem_mocked(self, mock_listdir):
mock_listdir.return_value = normpaths(
[".DS_Store", "client-2048.crt", "client-2048.key", "client-2048.pem"]
)
assert self.client.cert == normpaths(
["../fail/client-2048.crt", "../fail/client-2048.key"]
)


class BaseClientCertFilesTest(unittest.TestCase):
def setUp(self):
Expand All @@ -215,3 +229,16 @@ def test_client_cert_files(self):
assert self.client.cert == normpaths(
["/fail/client-2048.crt", "/fail/client-2048.key"]
)


class BaseClientSingleCertFileTest(unittest.TestCase):
def setUp(self):
self.client = APIClient(
"bf_username",
"password",
"app_key",
cert_files=os.path.normpath("/fail/client-2048.pem"),
)

def test_client_cert_files(self):
assert self.client.cert == os.path.normpath("/fail/client-2048.pem")

0 comments on commit d0aeb94

Please sign in to comment.