From 17b4e5de631eb8cba3bae36e150f3ee97b8d47be Mon Sep 17 00:00:00 2001 From: whiterabbit1983 Date: Sat, 17 Jul 2021 14:40:13 +0300 Subject: [PATCH 1/2] fix: Use portalocker library instead of fcntl, which is not cross platform --- poetry.lock | 44 +++++++++++++++++++++++++++++++++++- pyproject.toml | 2 ++ whitehead_sdk/token_cache.py | 27 ++++++---------------- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index e42d3c4..d69bc04 100644 --- a/poetry.lock +++ b/poetry.lock @@ -215,6 +215,22 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "portalocker" +version = "2.3.0" +description = "Wraps the portalocker recipe for easy usage" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pywin32 = {version = "!=226", markers = "platform_system == \"Windows\""} + +[package.extras] +docs = ["sphinx (>=1.7.1)"] +redis = ["redis"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "sphinx (>=3.0.3)", "pytest-flake8 (>=1.0.5)", "pytest-mypy (>=0.8.0)", "redis"] + [[package]] name = "py-gql-client" version = "1.0.1" @@ -236,6 +252,14 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pywin32" +version = "228" +description = "Python for Window Extensions" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "requests" version = "2.26.0" @@ -319,7 +343,7 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [metadata] lock-version = "1.1" python-versions = ">=3.6.1,<4.0" -content-hash = "d4bc61108ecbbacc90abb5725e0e5244a783706fa5066172e15f8612ada2f643" +content-hash = "8bc9c93284018a8e42f4a808bc9504f3391b52feb4d0788e313fbe5815672a0f" [metadata.files] aiohttp = [ @@ -515,6 +539,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +portalocker = [ + {file = "portalocker-2.3.0-py2.py3-none-any.whl", hash = "sha256:1d43fd8223e1743f5725e8910f69e7a45858ffd298e19252633ac903eafd83bc"}, + {file = "portalocker-2.3.0.tar.gz", hash = "sha256:4e913d807aa6598c320e8a50c50e2ee0602bc45240b485e3f8bc06f13060084c"}, +] py-gql-client = [ {file = "py-gql-client-1.0.1.tar.gz", hash = "sha256:278b580bc78211216c62d436efb287aa6649aa7efb84c2e80050f11debd71b13"}, {file = "py_gql_client-1.0.1-py3-none-any.whl", hash = "sha256:ecb816a285fc8d1df18e1c40a5b3335e11312feb5be9fe6ee2f4a14772f2473e"}, @@ -523,6 +551,20 @@ pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] +pywin32 = [ + {file = "pywin32-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"}, + {file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"}, + {file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"}, + {file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"}, + {file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"}, + {file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"}, + {file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"}, + {file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"}, + {file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"}, + {file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"}, + {file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"}, + {file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"}, +] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, diff --git a/pyproject.toml b/pyproject.toml index 1abf2ab..020b172 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,8 @@ cryptography = "^3.4.6" gql = {version = "3.0.0a5", extras = ["all"], allow-prereleases = true} py-gql-client = "^1.0.1" dataclasses = {version = "^0.6", python = "~3.6"} +portalocker = "^2.3.0" +pywin32 = {version = "==228", markers="sys_platform == 'win32'"} [build-system] diff --git a/whitehead_sdk/token_cache.py b/whitehead_sdk/token_cache.py index 95d29e5..5f98571 100644 --- a/whitehead_sdk/token_cache.py +++ b/whitehead_sdk/token_cache.py @@ -1,22 +1,11 @@ import os -import fcntl import hashlib import tempfile +import portalocker from contextlib import contextmanager, suppress from datetime import datetime -@contextmanager -def file_lock(fd, cmd): - try: - fcntl.lockf(fd, cmd) - yield - except IOError: - yield - finally: - fcntl.lockf(fd, fcntl.LOCK_UN) - - class TokenCache: def __init__(self, dev_id, api_key): self._hash = hashlib.md5(f"{dev_id}:{api_key}".encode()).hexdigest() @@ -34,13 +23,11 @@ def cache_path(self): def read(self): with suppress(FileNotFoundError): - with open(self.cache_path, "r") as f: - with file_lock(f, fcntl.LOCK_SH): - return f.read() + with portalocker.Lock(self.cache_path, "r") as f: + return f.read() def write(self, data): - with open(self.cache_path, "a") as f: - with file_lock(f, fcntl.LOCK_EX): - f.seek(0) - f.truncate(0) - f.write(data) + with portalocker.Lock(self.cache_path, "a") as f: + f.seek(0) + f.truncate(0) + f.write(data) From 351294c21404e70b6adf8d25a6e210e0cb8d237e Mon Sep 17 00:00:00 2001 From: whiterabbit1983 Date: Sat, 17 Jul 2021 14:40:43 +0300 Subject: [PATCH 2/2] fix: Raise AuthError if token was not retrieved successfully --- whitehead_sdk/__init__.py | 3 +++ whitehead_sdk/exceptions.py | 2 ++ 2 files changed, 5 insertions(+) create mode 100644 whitehead_sdk/exceptions.py diff --git a/whitehead_sdk/__init__.py b/whitehead_sdk/__init__.py index c8a77c0..fb097e2 100644 --- a/whitehead_sdk/__init__.py +++ b/whitehead_sdk/__init__.py @@ -1,6 +1,7 @@ from gql.client import Client from gql.transport.aiohttp import AIOHTTPTransport from . import config, utils, token_cache +from .exceptions import AuthError def Whitehead(api_key, developer_id): @@ -12,6 +13,8 @@ def Whitehead(api_key, developer_id): if not jwt_token: exchange_token, nonce = utils.create_exchange_token(api_key) auth_data = utils.request_jwt(developer_id, exchange_token) + if auth_data.get("enc_token") is None: + raise AuthError(auth_data.get("error")) jwt_token = utils.decrypt_jwt(auth_data, api_key, nonce) cache.write(jwt_token) diff --git a/whitehead_sdk/exceptions.py b/whitehead_sdk/exceptions.py new file mode 100644 index 0000000..5825980 --- /dev/null +++ b/whitehead_sdk/exceptions.py @@ -0,0 +1,2 @@ +class AuthError(Exception): + pass