Skip to content

Commit

Permalink
Apply uniform formatting via black
Browse files Browse the repository at this point in the history
  • Loading branch information
Acconut committed Dec 13, 2023
1 parent a56587a commit fd8db8f
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 69 deletions.
2 changes: 1 addition & 1 deletion tusclient/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0.2'
__version__ = "1.0.2"
4 changes: 2 additions & 2 deletions tusclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ def uploader(self, *args, **kwargs) -> Uploader:
:Args:
see tusclient.uploader.Uploader for required and optional arguments.
"""
kwargs['client'] = self
kwargs["client"] = self
return Uploader(*args, **kwargs)

def async_uploader(self, *args, **kwargs) -> AsyncUploader:
kwargs['client'] = self
kwargs["client"] = self
return AsyncUploader(*args, **kwargs)
5 changes: 4 additions & 1 deletion tusclient/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ class TusCommunicationError(Exception):
- status_code (Optional[int])
- response_content (Optional[str])
"""

def __init__(self, message, status_code=None, response_content=None):
default_message = 'Communication with tus server failed with status {}'.format(status_code)
default_message = "Communication with tus server failed with status {}".format(
status_code
)
message = message or default_message
super(TusCommunicationError, self).__init__(message)
self.status_code = status_code
Expand Down
4 changes: 2 additions & 2 deletions tusclient/fingerprint/fingerprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ def get_fingerprint(self, fs: IO):
# add in the file size to minimize chances of collision
fs.seek(0, os.SEEK_END)
file_size = fs.tell()
return 'size:{}--md5:{}'.format(file_size, hasher.hexdigest())
return "size:{}--md5:{}".format(file_size, hasher.hexdigest())

def _encode_data(self, data):
try:
return data.encode('utf-8')
return data.encode("utf-8")
except AttributeError:
# in case the content is already binary, this failure would happen.
return data
43 changes: 26 additions & 17 deletions tusclient/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# Catches requests exceptions and throws custom tuspy errors.
def catch_requests_error(func):
"""Deocrator to catch requests exceptions"""

@wraps(func)
def _wrapper(*args, **kwargs):
try:
Expand Down Expand Up @@ -47,8 +48,8 @@ def __init__(self, uploader):
self.file.seek(uploader.offset)

self._request_headers = {
'upload-offset': str(uploader.offset),
'Content-Type': 'application/offset+octet-stream'
"upload-offset": str(uploader.offset),
"Content-Type": "application/offset+octet-stream",
}
self._request_headers.update(uploader.get_headers())
self._content_length = uploader.get_request_length()
Expand All @@ -58,38 +59,45 @@ def __init__(self, uploader):

def add_checksum(self, chunk: bytes):
if self._upload_checksum:
self._request_headers['upload-checksum'] = \
' '.join((
self._request_headers["upload-checksum"] = " ".join(
(
self._checksum_algorithm_name,
base64.b64encode(
self._checksum_algorithm(chunk).digest()
).decode('ascii'),
))
base64.b64encode(self._checksum_algorithm(chunk).digest()).decode(
"ascii"
),
)
)


class TusRequest(BaseTusRequest):
"""Class to handle async Tus upload requests"""

def perform(self):
"""
Perform actual request.
"""
try:
chunk = self.file.read(self._content_length)
self.add_checksum(chunk)
resp = requests.patch(self._url, data=chunk,
headers=self._request_headers,
verify=self.verify_tls_cert)
resp = requests.patch(
self._url,
data=chunk,
headers=self._request_headers,
verify=self.verify_tls_cert,
)
self.status_code = resp.status_code
self.response_content = resp.content
self.response_headers = {
k.lower(): v for k, v in resp.headers.items()}
self.response_headers = {k.lower(): v for k, v in resp.headers.items()}
except requests.exceptions.RequestException as error:
raise TusUploadFailed(error)


class AsyncTusRequest(BaseTusRequest):
"""Class to handle async Tus upload requests"""
def __init__(self, *args, io_loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs):

def __init__(
self, *args, io_loop: Optional[asyncio.AbstractEventLoop] = None, **kwargs
):
self.io_loop = io_loop
super().__init__(*args, **kwargs)

Expand All @@ -103,11 +111,12 @@ async def perform(self):
async with aiohttp.ClientSession(loop=self.io_loop) as session:
ssl = None if self.verify_tls_cert else False
async with session.patch(
self._url, data=chunk, headers=self._request_headers,
ssl=ssl) as resp:
self._url, data=chunk, headers=self._request_headers, ssl=ssl
) as resp:
self.status_code = resp.status
self.response_headers = {
k.lower(): v for k, v in resp.headers.items()}
k.lower(): v for k, v in resp.headers.items()
}
self.response_content = await resp.content.read()
except aiohttp.ClientError as error:
raise TusUploadFailed(error)
8 changes: 4 additions & 4 deletions tusclient/storage/filestorage.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def get_item(self, key: str):
:Returns: url[str]
"""
result = self._db.search(self._urls.key == key)
return result[0].get('url') if result else None
return result[0].get("url") if result else None

def set_item(self, key: str, url: str):
"""
Expand All @@ -31,15 +31,15 @@ def set_item(self, key: str, url: str):
- value[str]: The actual url value to be stored.
"""
if self._db.search(self._urls.key == key):
self._db.update({'url': url}, self._urls.key == key)
self._db.update({"url": url}, self._urls.key == key)
else:
self._db.insert({'key': key, 'url': url})
self._db.insert({"key": key, "url": url})

def remove_item(self, key: str):
"""
Remove/Delete the url value under the unique key from storage.
"""
self._db.remove(self._urls.key==key)
self._db.remove(self._urls.key == key)

def close(self):
"""
Expand Down
77 changes: 47 additions & 30 deletions tusclient/uploader/baseuploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,29 +90,41 @@ class BaseUploader:
- fingerprinter (Optional [<tusclient.fingerprint.interface.Fingerprint>])
- upload_checksum (Optional[bool])
"""

DEFAULT_HEADERS = {"Tus-Resumable": "1.0.0"}
DEFAULT_CHUNK_SIZE = MAXSIZE
CHECKSUM_ALGORITHM_PAIR = ("sha1", hashlib.sha1, )

def __init__(self, file_path: Optional[str] = None, file_stream: Optional[IO] = None,
url: Optional[str] = None, client: Optional['TusClient'] = None,
chunk_size: int = MAXSIZE, metadata: Optional[Dict] = None,
metadata_encoding: Optional[str] = 'utf-8',
retries: int = 0, retry_delay: int = 30,
verify_tls_cert: bool = True, store_url=False,
url_storage: Optional[Storage] = None,
fingerprinter: Optional[interface.Fingerprint] = None,
upload_checksum=False):
CHECKSUM_ALGORITHM_PAIR = (
"sha1",
hashlib.sha1,
)

def __init__(
self,
file_path: Optional[str] = None,
file_stream: Optional[IO] = None,
url: Optional[str] = None,
client: Optional["TusClient"] = None,
chunk_size: int = MAXSIZE,
metadata: Optional[Dict] = None,
metadata_encoding: Optional[str] = "utf-8",
retries: int = 0,
retry_delay: int = 30,
verify_tls_cert: bool = True,
store_url=False,
url_storage: Optional[Storage] = None,
fingerprinter: Optional[interface.Fingerprint] = None,
upload_checksum=False,
):
if file_path is None and file_stream is None:
raise ValueError(
"Either 'file_path' or 'file_stream' cannot be None.")
raise ValueError("Either 'file_path' or 'file_stream' cannot be None.")

if url is None and client is None:
raise ValueError("Either 'url' or 'client' cannot be None.")

if store_url and url_storage is None:
raise ValueError(
"Please specify a storage instance to enable resumablility.")
"Please specify a storage instance to enable resumablility."
)

self.verify_tls_cert = verify_tls_cert
self.file_path = file_path
Expand All @@ -133,28 +145,29 @@ def __init__(self, file_path: Optional[str] = None, file_stream: Optional[IO] =
self._retried = 0
self.retry_delay = retry_delay
self.upload_checksum = upload_checksum
self.__checksum_algorithm_name, self.__checksum_algorithm = \
self.CHECKSUM_ALGORITHM_PAIR
(
self.__checksum_algorithm_name,
self.__checksum_algorithm,
) = self.CHECKSUM_ALGORITHM_PAIR

def get_headers(self):
"""
Return headers of the uploader instance. This would include the headers of the
client instance.
"""
client_headers = getattr(self.client, 'headers', {})
client_headers = getattr(self.client, "headers", {})
return dict(self.DEFAULT_HEADERS, **client_headers)

def get_url_creation_headers(self):
"""Return headers required to create upload url"""
headers = self.get_headers()
headers['upload-length'] = str(self.get_file_size())
headers['upload-metadata'] = ','.join(self.encode_metadata())
headers["upload-length"] = str(self.get_file_size())
headers["upload-metadata"] = ",".join(self.encode_metadata())
return headers

@property
def checksum_algorithm(self):
"""The checksum algorithm to be used for the Upload-Checksum extension.
"""
"""The checksum algorithm to be used for the Upload-Checksum extension."""
return self.__checksum_algorithm

@property
Expand All @@ -172,11 +185,14 @@ def get_offset(self):
This is different from the instance attribute 'offset' because this makes an
http request to the tus server to retrieve the offset.
"""
resp = requests.head(self.url, headers=self.get_headers(), verify=self.verify_tls_cert)
offset = resp.headers.get('upload-offset')
resp = requests.head(
self.url, headers=self.get_headers(), verify=self.verify_tls_cert
)
offset = resp.headers.get("upload-offset")
if offset is None:
msg = 'Attempt to retrieve offset fails with status {}'.format(
resp.status_code)
msg = "Attempt to retrieve offset fails with status {}".format(
resp.status_code
)
raise TusCommunicationError(msg, resp.status_code, resp.content)
return int(offset)

Expand All @@ -189,13 +205,14 @@ def encode_metadata(self):
key_str = str(key) # dict keys may be of any object type.

# confirm that the key does not contain unwanted characters.
if re.search(r'^$|[\s,]+', key_str):
if re.search(r"^$|[\s,]+", key_str):
msg = 'Upload-metadata key "{}" cannot be empty nor contain spaces or commas.'
raise ValueError(msg.format(key_str))

value_bytes = value.encode(self.metadata_encoding)
encoded_list.append('{} {}'.format(
key_str, b64encode(value_bytes).decode('ascii')))
encoded_list.append(
"{} {}".format(key_str, b64encode(value_bytes).decode("ascii"))
)
return encoded_list

def __init_url_and_offset(self, url: Optional[str] = None):
Expand Down Expand Up @@ -241,14 +258,14 @@ def get_file_stream(self):
self.file_stream.seek(0)
return self.file_stream
elif os.path.isfile(self.file_path):
return open(self.file_path, 'rb')
return open(self.file_path, "rb")
else:
raise ValueError("invalid file {}".format(self.file_path))

def get_file_size(self):
"""
Return size of the file.
"""
stream = self.get_file_stream()
stream = self.get_file_stream()
stream.seek(0, os.SEEK_END)
return stream.tell()
32 changes: 20 additions & 12 deletions tusclient/uploader/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ def _verify_upload(request: TusRequest):
if 200 <= request.status_code < 300:
return True
else:
raise TusUploadFailed('', request.status_code,
request.response_content)
raise TusUploadFailed("", request.status_code, request.response_content)


class Uploader(BaseUploader):
Expand Down Expand Up @@ -52,7 +51,7 @@ def upload_chunk(self):
self._retried = 0

self._do_request()
self.offset = int(self.request.response_headers.get('upload-offset'))
self.offset = int(self.request.response_headers.get("upload-offset"))

@catch_requests_error
def create_url(self):
Expand All @@ -62,12 +61,15 @@ def create_url(self):
Makes request to tus server to create a new upload url for the required file upload.
"""
resp = requests.post(
self.client.url, headers=self.get_url_creation_headers(),
verify=self.verify_tls_cert)
self.client.url,
headers=self.get_url_creation_headers(),
verify=self.verify_tls_cert,
)
url = resp.headers.get("location")
if url is None:
msg = 'Attempt to retrieve create file url with status {}'.format(
resp.status_code)
msg = "Attempt to retrieve create file url with status {}".format(
resp.status_code
)
raise TusCommunicationError(msg, resp.status_code, resp.content)
return urljoin(self.client.url, url)

Expand Down Expand Up @@ -126,7 +128,7 @@ async def upload_chunk(self):
self._retried = 0

await self._do_request()
self.offset = int(self.request.response_headers.get('upload-offset'))
self.offset = int(self.request.response_headers.get("upload-offset"))

async def create_url(self):
"""
Expand All @@ -139,12 +141,18 @@ async def create_url(self):
headers = self.get_url_creation_headers()
ssl = None if self.verify_tls_cert else False
async with session.post(
self.client.url, headers=headers, ssl=ssl) as resp:
self.client.url, headers=headers, ssl=ssl
) as resp:
url = resp.headers.get("location")
if url is None:
msg = 'Attempt to retrieve create file url with status {}'.format(
resp.status)
raise TusCommunicationError(msg, resp.status, await resp.content.read())
msg = (
"Attempt to retrieve create file url with status {}".format(
resp.status
)
)
raise TusCommunicationError(
msg, resp.status, await resp.content.read()
)
return urljoin(self.client.url, url)
except aiohttp.ClientError as error:
raise TusCommunicationError(error)
Expand Down

0 comments on commit fd8db8f

Please sign in to comment.