Skip to content

Commit

Permalink
Support object paths and names with spaces in them (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavius authored Oct 11, 2020
1 parent 87c7a42 commit b067baf
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[flake8]
ignore = F401,F403
ignore = F401,F403,E127
max-line-length = 160
4 changes: 2 additions & 2 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ class TestObject(Test):
def setUp(self):
super(TestObject, self).setUp()

self._object_dir = '/v3io-py-test-object'
self._object_path = self._object_dir + '/object.txt'
self._object_dir = '/v3io-py-test object'
self._object_path = self._object_dir + '/obj ect.txt'

# clean up
self._delete_dir(self._object_dir)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_client_aio.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ class TestObject(Test):
async def asyncSetUp(self):
await super(TestObject, self).asyncSetUp()

self._object_dir = '/v3io-py-test-object'
self._object_path = self._object_dir + '/object.txt'
self._object_dir = '/v3io-py-test object'
self._object_path = self._object_dir + '/obj ect.txt'

# clean up
await self._delete_dir(self._object_dir)
Expand Down
2 changes: 0 additions & 2 deletions v3io/aio/dataplane/kv.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,6 @@ async def create_schema(self,
fields=None):
"""Creates a KV schema file
DEPRECATED. Use kv.create_schema
Parameters
----------
container (Required) : str
Expand Down
6 changes: 4 additions & 2 deletions v3io/aio/dataplane/transport/aiohttp.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ async def request(self,
encoder_args,
output)

self.log('Tx', method=request.method, path=request.path, headers=request.headers, body=request.body)
path = request.encode_path()

self.log('Tx', method=request.method, path=path, headers=request.headers, body=request.body)

# call the encoder to get the response
async with self._client_session.request(request.method,
self._endpoint + '/' + request.path,
self._endpoint + '/' + path,
headers=request.headers,
data=request.body,
ssl=False) as http_response:
Expand Down
2 changes: 0 additions & 2 deletions v3io/dataplane/kv.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,6 @@ def create_schema(self,
fields=None):
"""Creates a KV schema file
DEPRECATED. Use kv.create_schema
Parameters
----------
container (Required) : str
Expand Down
6 changes: 3 additions & 3 deletions v3io/dataplane/kv_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def decode(encoded_array):
raise ValueError('Not an encoded array')

# get header (which contains number of items and type
header = encoded_array[static_header_len:static_header_len+8]
values = encoded_array[static_header_len+len(header):]
header = encoded_array[static_header_len:static_header_len + 8]
values = encoded_array[static_header_len + len(header):]

# unpack the header to get the size and operand
unpacked_header = struct.unpack('II', header)
Expand All @@ -48,4 +48,4 @@ def decode(encoded_array):
num_items = int(unpacked_header[0] / 8)

# decode the values
return list(struct.unpack(typecode*num_items, values))
return list(struct.unpack(typecode * num_items, values))
2 changes: 1 addition & 1 deletion v3io/dataplane/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def _decode_typed_attributes(self, typed_attributes):
# try to decode as an array
try:
decoded_attribute = v3io.dataplane.kv_array.decode(decoded_attribute)
except:
except BaseException:
pass

elif attribute_type == 'S':
Expand Down
50 changes: 32 additions & 18 deletions v3io/dataplane/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
import datetime

try:
from urllib.parse import urlencode
from urllib.parse import urlencode, quote
except BaseException:
from urllib import urlencode
from urllib import urlencode, quote

import ujson

import v3io.common.helpers
import v3io.dataplane.kv_array
import v3io.dataplane.kv_timestamp


#
# Request
#
Expand All @@ -36,22 +37,28 @@ def __init__(self,
self.output = output

# get request params with the encoder
self.method, self.path, self.headers, self.body = encoder(container, access_key, encoder_args)
self.method, self.path, self.query, self.headers, self.body = encoder(container, access_key, encoder_args)

# used by the transport
self.transport = lambda: None

def encode_path(self):
if self.query is None:
return quote(self.path)

return quote(self.path) + '?' + urlencode(self.query, quote_via=quote)

#
# Encoders
#


#
# Container
#

def encode_get_containers(container_name, access_key, kwargs):
return _encode('GET', None, access_key, '/', {}, None)
return _encode('GET', '/', access_key, None, None, {}, None)


def encode_get_container_contents(container_name, access_key, kwargs):
Expand All @@ -72,9 +79,10 @@ def encode_get_container_contents(container_name, access_key, kwargs):
query['marker'] = kwargs['marker']

return _encode('GET',
None,
'/' + container_name,
access_key,
'/{0}?{1}'.format(container_name, urlencode(query)),
None,
query,
{},
None)

Expand All @@ -100,7 +108,7 @@ def encode_get_object(container_name, access_key, kwargs):
'Range': range_value
}

return _encode('GET', container_name, access_key, kwargs['path'], headers, None)
return _encode('GET', container_name, access_key, kwargs['path'], None, headers, None)


def encode_put_object(container_name, access_key, kwargs):
Expand All @@ -112,11 +120,11 @@ def encode_put_object(container_name, access_key, kwargs):
'Range': '-1'
}

return _encode('PUT', container_name, access_key, kwargs['path'], headers, kwargs['body'])
return _encode('PUT', container_name, access_key, kwargs['path'], None, headers, kwargs['body'])


def encode_delete_object(container_name, access_key, kwargs):
return _encode('DELETE', container_name, access_key, kwargs['path'], None, None)
return _encode('DELETE', container_name, access_key, kwargs['path'], None, None, None)


#
Expand All @@ -136,6 +144,7 @@ def encode_put_item(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or os.path.join(kwargs['table_path'], kwargs['key']),
None,
{'X-v3io-function': 'PutItem'},
body)

Expand Down Expand Up @@ -170,6 +179,7 @@ def encode_update_item(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or os.path.join(kwargs['table_path'], kwargs['key']),
None,
{'X-v3io-function': function_name},
body)

Expand All @@ -183,6 +193,7 @@ def encode_get_item(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or os.path.join(kwargs['table_path'], kwargs['key']),
None,
{'X-v3io-function': 'GetItem'},
body)

Expand Down Expand Up @@ -220,6 +231,7 @@ def encode_get_items(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['table_path'],
None,
{'X-v3io-function': 'GetItems'},
body)

Expand All @@ -238,6 +250,7 @@ def encode_create_stream(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'CreateStream'},
body)

Expand All @@ -251,6 +264,7 @@ def encode_update_stream(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'UpdateStream'},
body)

Expand All @@ -260,6 +274,7 @@ def encode_describe_stream(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'DescribeStream'},
None)

Expand All @@ -282,6 +297,7 @@ def encode_seek_shard(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'SeekShard'},
body)

Expand Down Expand Up @@ -319,6 +335,7 @@ def encode_put_records(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'PutRecords'},
body)

Expand All @@ -336,6 +353,7 @@ def encode_get_records(container_name, access_key, kwargs):
container_name,
access_key,
kwargs.get('path') or kwargs['stream_path'],
None,
{'X-v3io-function': 'GetRecords'},
body)

Expand All @@ -344,15 +362,15 @@ def encode_get_records(container_name, access_key, kwargs):
# Helpers
#

def _encode(method, container_name, access_key, path, headers, body):
if container_name:
path = _resolve_path(container_name, path)
def _encode(method, container_name, access_key, path, query, headers, body):
if path is not None:
path = v3io.common.helpers.url_join(container_name, path)
else:
path = path
path = container_name

headers, body = _resolve_body_and_headers(access_key, headers, body)

return method, path, headers, body
return method, path, query, headers, body


def _typed_attributes_to_dict(self):
Expand Down Expand Up @@ -424,7 +442,3 @@ def _resolve_body_and_headers(access_key, headers, body):
headers['Content-Type'] = 'application/json'

return headers, body


def _resolve_path(container_name, path):
return v3io.common.helpers.url_join(container_name, path)
12 changes: 7 additions & 5 deletions v3io/dataplane/transport/httpclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def __init__(self, logger, endpoint=None, max_connections=None, timeout=None, ve
# python 2 and 3 have different exceptions
if sys.version_info[0] >= 3:
self._wait_response_exceptions = (
http.client.RemoteDisconnected, ConnectionResetError, ConnectionRefusedError)
http.client.RemoteDisconnected, ConnectionResetError, ConnectionRefusedError)
self._send_request_exceptions = (
BrokenPipeError, http.client.CannotSendRequest, http.client.RemoteDisconnected)
BrokenPipeError, http.client.CannotSendRequest, http.client.RemoteDisconnected)
self._get_status_and_headers = self._get_status_and_headers_py3
else:
self._wait_response_exceptions = (http.client.BadStatusLine, socket.error)
Expand Down Expand Up @@ -115,24 +115,26 @@ def wait_response(self, request, raise_for_status=None, num_retries=1):
raise e

def _send_request_on_connection(self, request, connection_idx):
path = request.encode_path()

self.log('Tx',
connection_idx=connection_idx,
method=request.method,
path=request.path,
path=path,
headers=request.headers,
body=request.body)

connection = self._connections[connection_idx]

try:
connection.request(request.method, request.path, request.body, request.headers)
connection.request(request.method, path, request.body, request.headers)
except self._send_request_exceptions as e:
self._logger.debug_with('Disconnected while attempting to send. Recreating connection', e=type(e))

connection = self._recreate_connection_at_index(connection_idx)

# re-request
connection.request(request.method, request.path, request.body, request.headers)
connection.request(request.method, path, request.body, request.headers)
except BaseException as e:
self._logger.warn_with('Unhandled exception while sending request', e=type(e))
raise e
Expand Down

0 comments on commit b067baf

Please sign in to comment.