diff --git a/fixtures/python_output/multipart_post.py b/fixtures/python_output/multipart_post.py index c945b4a..17c3c05 100644 --- a/fixtures/python_output/multipart_post.py +++ b/fixtures/python_output/multipart_post.py @@ -4,9 +4,9 @@ headers = { 'Authorization': 'Bearer ACCESS_TOKEN', } -files = { - 'attributes': '{"name":"tigers.jpeg", "parent":{"id":"11446498"}}', - 'file': open('myfile.jpg', 'rb') -} +files = [ + ('attributes', '{"name":"tigers.jpeg", "parent":{"id":"11446498"}}'), + ('file', open('myfile.jpg', 'rb')), +] requests.post('https://upload.box.com/api/2.0/files/content', headers=headers, files=files) diff --git a/fixtures/python_output/post_basic_auth_url_encoded_data.py b/fixtures/python_output/post_basic_auth_url_encoded_data.py index e9e05aa..06f5e7e 100644 --- a/fixtures/python_output/post_basic_auth_url_encoded_data.py +++ b/fixtures/python_output/post_basic_auth_url_encoded_data.py @@ -1,7 +1,7 @@ import requests -data = { - 'grant_type': 'client_credentials' -} +data = [ + ('grant_type', 'client_credentials'), +] requests.post('http://localhost/api/oauth/token/', data=data, auth=('foo', 'bar')) diff --git a/fixtures/python_output/post_data_binary_with_equals.py b/fixtures/python_output/post_data_binary_with_equals.py index d34c26c..4d40a6c 100644 --- a/fixtures/python_output/post_data_binary_with_equals.py +++ b/fixtures/python_output/post_data_binary_with_equals.py @@ -44,7 +44,7 @@ params = ( ('AC', '1'), ) -data = '{"__type":"CreateItemJsonRequest:#Exchange","Header":{"__type":"JsonRequestHeaders:#Exchange","RequestServerVersion":"Exchange2013","TimeZoneContext":{"__type":"TimeZoneContext:#Exchange","TimeZoneDefinition":{"__type":"TimeZoneDefinitionType:#Exchange","Id":"France Standard Time"}}},"Body":{"__type":"CreateItemRequest:#Exchange","Items":[{"__type":"Message:#Exchange","Subject":"API","Body":{"__type":"BodyContentType:#Exchange","BodyType":"HTML","Value":"

API Test for NickC

"},"Importance":"Normal","From":null,"ToRecipients":[{"Name":"George LUCAS","EmailAddress":"George.LUCAS@nih.mail.edu.fr","RoutingType":"SMTP","MailboxType":"Mailbox","OriginalDisplayName":"George.LUCAS@nih.mail.edu.fr","SipUri":" "}],"CcRecipients":[],"BccRecipients":[],"Sensitivity":"Normal","IsDeliveryReceiptRequested":false,"IsReadReceiptRequested":false}],"ClientSupportsIrm":true,"OutboundCharset":"AutoDetect","MessageDisposition":"SendAndSaveCopy","ComposeOperation":"newMail"}}' +data = '{"__type":"CreateItemJsonRequest:#Exchange","Header":{"__type":"JsonRequestHeaders:#Exchange","RequestServerVersion":"Exchange2013","TimeZoneContext":{"__type":"TimeZoneContext:#Exchange","TimeZoneDefinition":{"__type":"TimeZoneDefinitionType:#Exchange","Id":"France Standard Time"}}},"Body":{"__type":"CreateItemRequest:#Exchange","Items":[{"__type":"Message:#Exchange","Subject":"API","Body":{"__type":"BodyContentType:#Exchange","BodyType":"HTML","Value":"

API Test for NickC

"},"Importance":"Normal","From":null,"ToRecipients":[{"Name":"George LUCAS","EmailAddress":"George.LUCAS@nih.mail.edu.fr","RoutingType":"SMTP","MailboxType":"Mailbox","OriginalDisplayName":"George.LUCAS@nih.mail.edu.fr","SipUri":" "}],"CcRecipients":[],"BccRecipients":[],"Sensitivity":"Normal","IsDeliveryReceiptRequested":false,"IsReadReceiptRequested":false}],"ClientSupportsIrm":true,"OutboundCharset":"AutoDetect","MessageDisposition":"SendAndSaveCopy","ComposeOperation":"newMail"}}' requests.post('https://localhost/api/service.svc', headers=headers, params=params, cookies=cookies, data=data) diff --git a/fixtures/python_output/post_escaped_double_quotes_in_single_quotes.py b/fixtures/python_output/post_escaped_double_quotes_in_single_quotes.py index d837f88..bfcd4e1 100644 --- a/fixtures/python_output/post_escaped_double_quotes_in_single_quotes.py +++ b/fixtures/python_output/post_escaped_double_quotes_in_single_quotes.py @@ -1,7 +1,7 @@ import requests -data = { - 'foo': '\"bar\"' -} +data = [ + ('foo', '\\"bar\\"'), +] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_escaped_single_quotes_in_double_quotes.py b/fixtures/python_output/post_escaped_single_quotes_in_double_quotes.py index c0941a1..e4c1cf1 100644 --- a/fixtures/python_output/post_escaped_single_quotes_in_double_quotes.py +++ b/fixtures/python_output/post_escaped_single_quotes_in_double_quotes.py @@ -1,7 +1,7 @@ import requests -data = { - 'foo': '\\\'bar\\\'' -} +data = [ + ('foo', '\\\'bar\\\''), +] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_quotes_inside_data.py b/fixtures/python_output/post_quotes_inside_data.py new file mode 100644 index 0000000..5abd403 --- /dev/null +++ b/fixtures/python_output/post_quotes_inside_data.py @@ -0,0 +1,7 @@ +import requests + +data = [ + ('field', 'don\'t you like quotes'), +] + +requests.post('google.com', data=data) diff --git a/fixtures/python_output/post_same_field_multiple_times.py b/fixtures/python_output/post_same_field_multiple_times.py index 4a0380f..d7d257e 100644 --- a/fixtures/python_output/post_same_field_multiple_times.py +++ b/fixtures/python_output/post_same_field_multiple_times.py @@ -3,7 +3,7 @@ import requests data = [ ('foo', 'bar'), ('foo', ''), - ('foo', 'barbar') + ('foo', 'barbar'), ] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_with_double_quotes_inside_single_quotes.py b/fixtures/python_output/post_with_double_quotes_inside_single_quotes.py index 669618d..f60c044 100644 --- a/fixtures/python_output/post_with_double_quotes_inside_single_quotes.py +++ b/fixtures/python_output/post_with_double_quotes_inside_single_quotes.py @@ -1,7 +1,7 @@ import requests -data = { - 'foo': '"bar"' -} +data = [ + ('foo', '"bar"'), +] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_with_escaped_double_quotes.py b/fixtures/python_output/post_with_escaped_double_quotes.py index 669618d..f60c044 100644 --- a/fixtures/python_output/post_with_escaped_double_quotes.py +++ b/fixtures/python_output/post_with_escaped_double_quotes.py @@ -1,7 +1,7 @@ import requests -data = { - 'foo': '"bar"' -} +data = [ + ('foo', '"bar"'), +] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_with_single_quotes_inside_double_quotes.py b/fixtures/python_output/post_with_single_quotes_inside_double_quotes.py index 95805dc..88f3c44 100644 --- a/fixtures/python_output/post_with_single_quotes_inside_double_quotes.py +++ b/fixtures/python_output/post_with_single_quotes_inside_double_quotes.py @@ -1,7 +1,7 @@ import requests -data = { - 'foo': '\'bar\'' -} +data = [ + ('foo', '\'bar\''), +] requests.post('http://example.com/', data=data) diff --git a/fixtures/python_output/post_with_urlencoded_data.py b/fixtures/python_output/post_with_urlencoded_data.py index 88dddc7..f00cdd9 100644 --- a/fixtures/python_output/post_with_urlencoded_data.py +++ b/fixtures/python_output/post_with_urlencoded_data.py @@ -12,9 +12,9 @@ headers = { 'Connection': 'keep-alive', } -data = { - 'msg1': 'wow', - 'msg2': 'such' -} +data = [ + ('msg1', 'wow'), + ('msg2', 'such'), +] requests.post('http://fiddle.jshell.net/echo/html/', headers=headers, data=data) diff --git a/fixtures/python_output/post_with_urlencoded_data_and_headers.py b/fixtures/python_output/post_with_urlencoded_data_and_headers.py index 5259dbe..900b9aa 100644 --- a/fixtures/python_output/post_with_urlencoded_data_and_headers.py +++ b/fixtures/python_output/post_with_urlencoded_data_and_headers.py @@ -11,28 +11,28 @@ headers = { 'Connection': 'keep-alive', } -data = { - 'CultureId': '1', - 'ApplicationId': '1', - 'RecordsPerPage': '200', - 'MaximumResults': '200', - 'PropertyTypeId': '300', - 'TransactionTypeId': '2', - 'StoreyRange': '0-0', - 'BuildingTypeId': '1', - 'BedRange': '0-0', - 'BathRange': '0-0', - 'LongitudeMin': '-79.3676805496215', - 'LongitudeMax': '-79.27300930023185', - 'LatitudeMin': '43.660358732823845', - 'LatitudeMax': '43.692390574029936', - 'SortOrder': 'A', - 'SortBy': '1', - 'viewState': 'm', - 'Longitude': '-79.4107246398925', - 'Latitude': '43.6552047278685', - 'ZoomLevel': '13', - 'CurrentPage': '1' -} +data = [ + ('CultureId', '1'), + ('ApplicationId', '1'), + ('RecordsPerPage', '200'), + ('MaximumResults', '200'), + ('PropertyTypeId', '300'), + ('TransactionTypeId', '2'), + ('StoreyRange', '0-0'), + ('BuildingTypeId', '1'), + ('BedRange', '0-0'), + ('BathRange', '0-0'), + ('LongitudeMin', '-79.3676805496215'), + ('LongitudeMax', '-79.27300930023185'), + ('LatitudeMin', '43.660358732823845'), + ('LatitudeMax', '43.692390574029936'), + ('SortOrder', 'A'), + ('SortBy', '1'), + ('viewState', 'm'), + ('Longitude', '-79.4107246398925'), + ('Latitude', '43.6552047278685'), + ('ZoomLevel', '13'), + ('CurrentPage', '1'), +] requests.post('http://www.realtor.ca/api/Listing.svc/PropertySearch_Post', headers=headers, data=data) diff --git a/generators/python.js b/generators/python.js index b4edb3f..93f8d93 100644 --- a/generators/python.js +++ b/generators/python.js @@ -4,22 +4,110 @@ var querystring = require('querystring') require('string.prototype.startswith') -function pythonReprString (value) { +function repr (value) { // In context of url parameters, don't accept nulls and such. if (!value) { return "''" } else { - return "'" + value.replace('\\', '\\\\').replace("'", "\\'") + "'" + return "'" + jsesc(value, { quotes: 'single' }) + "'" } } +function getQueryDict (request) { + var queryDict = 'params = (\n' + for (var paramName in request.query) { + var rawValue = request.query[paramName] + var paramValue + if (Array.isArray(rawValue)) { + paramValue = rawValue.map(repr).join(', ') + } else { + paramValue = repr(rawValue) + } + queryDict += ' (' + repr(paramName) + ', ' + paramValue + '),\n' + } + queryDict += ')\n' + return queryDict +} + +function getDataString (request) { + if (request.data.startsWith('@')) { + var filePath = request.data.slice(1) + if (request.isDataBinary) { + return 'data = open(\'' + filePath + '\', \'rb\').read()' + } else { + return 'data = open(\'' + filePath + '\')' + } + } + + var parsedQueryString = querystring.parse(request.data) + var keyCount = Object.keys(parsedQueryString).length + var singleKeyOnly = keyCount === 1 && !parsedQueryString[Object.keys(parsedQueryString)[0]] + var singularData = request.isDataBinary || singleKeyOnly + if (singularData) { + return 'data = ' + repr(request.data) + '\n' + } else { + return getMultipleDataString(request, parsedQueryString) + } +} + +function getMultipleDataString (request, parsedQueryString) { + var repeatedKey = false + for (var key in parsedQueryString) { + var value = parsedQueryString[key] + if (Array.isArray(value)) { + repeatedKey = true + } + } + + var dataString + if (repeatedKey) { + dataString = 'data = [\n' + for (key in parsedQueryString) { + value = parsedQueryString[key] + if (Array.isArray(value)) { + for (var i = 0; i < value.length; i++) { + dataString += ' (' + repr(key) + ', ' + repr(value[i]) + '),\n' + } + } else { + dataString += ' (' + repr(key) + ', ' + repr(value) + '),\n' + } + } + dataString += ']\n' + } else { + dataString = 'data = [\n' + for (key in parsedQueryString) { + value = parsedQueryString[key] + dataString += ' (' + repr(key) + ', ' + repr(value) + '),\n' + } + dataString += ']\n' + } + + return dataString +} + +function getFilesString (request) { + // http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file + var filesString = 'files = [\n' + for (var multipartKey in request.multipartUploads) { + var multipartValue = request.multipartUploads[multipartKey] + if (multipartValue.startsWith('@')) { + filesString += ' (' + repr(multipartKey) + ', open(' + repr(multipartValue.slice(1)) + ", 'rb')),\n" + } else { + filesString += ' (' + repr(multipartKey) + ', ' + repr(multipartValue) + '),\n' + } + } + filesString += ']\n' + + return filesString +} + var toPython = function (curlCommand) { var request = util.parseCurlCommand(curlCommand) var cookieDict if (request.cookies) { cookieDict = 'cookies = {\n' for (var cookieName in request.cookies) { - cookieDict += " '" + cookieName + "': '" + request.cookies[cookieName] + "',\n" + cookieDict += ' ' + repr(cookieName) + ': ' + repr(request.cookies[cookieName]) + ',\n' } cookieDict += '}\n' } @@ -27,114 +115,22 @@ var toPython = function (curlCommand) { if (request.headers) { headerDict = 'headers = {\n' for (var headerName in request.headers) { - headerDict += " '" + headerName + "': '" + request.headers[headerName] + "',\n" + headerDict += ' ' + repr(headerName) + ': ' + repr(request.headers[headerName]) + ',\n' } headerDict += '}\n' } var queryDict if (request.query) { - queryDict = 'params = (\n' - for (var paramName in request.query) { - var rawValue = request.query[paramName] - var paramValue - if (Array.isArray(rawValue)) { - paramValue = rawValue.map(pythonReprString).join(', ') - } else { - paramValue = pythonReprString(rawValue) - } - queryDict += ' (' + pythonReprString(paramName) + ', ' + paramValue + '),\n' - } - queryDict += ')\n' + queryDict = getQueryDict(request) } var dataString var filesString if (request.data) { - if (request.data.startsWith('@')) { - var filePath = request.data.slice(1) - if (request.isDataBinary) { - dataString = 'data = open(\'' + filePath + '\', \'rb\').read()' - } else { - dataString = 'data = open(\'' + filePath + '\')' - } - } else { - var escapedData = request.data.replace(/'/g, "\\'") - if (escapedData.indexOf("'") > -1) { - escapedData = jsesc(request.data) - } - var parsedQueryString = querystring.parse(escapedData) - var keyCount = Object.keys(parsedQueryString).length - if (request.isDataBinary) { - dataString = "data = '" + request.data + "'\n" - } else if (keyCount === 1 && !parsedQueryString[Object.keys(parsedQueryString)[0]]) { - dataString = "data = '" + request.data + "'\n" - } else { - var dataIndex = 0 - var repeatedKey = false - var valueCount = 0 - for (var key in parsedQueryString) { - var value = parsedQueryString[key] - if (Array.isArray(value)) { - repeatedKey = true - valueCount += value.length - } else { - valueCount++ - } - } - if (repeatedKey) { - dataString = 'data = [\n' - for (key in parsedQueryString) { - value = parsedQueryString[key] - if (Array.isArray(value)) { - for (var i = 0; i < value.length; i++) { - dataString += " ('" + key + "', '" + value[i] + "')" - if (dataIndex < valueCount - 1) { - dataString += ',\n' - } - dataIndex++ - } - } else { - dataString += " ('" + key + "', '" + value + "')" - if (dataIndex < keyCount - 1) { - dataString += ',\n' - } - dataIndex++ - } - } - dataString += '\n]\n' - } else { - dataString = 'data = {\n' - for (key in parsedQueryString) { - value = parsedQueryString[key] - dataString += " '" + key + "': '" + value + "'" - if (dataIndex < keyCount - 1) { - dataString += ',\n' - } - dataIndex++ - } - dataString += '\n}\n' - } - } - } + dataString = getDataString(request) } else if (request.multipartUploads) { - // http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file - filesString = 'files = {\n' - var filesIndex = 0 - var filesCount = Object.keys(request.multipartUploads).length - for (var multipartKey in request.multipartUploads) { - var multipartValue = request.multipartUploads[multipartKey] - if (multipartValue.startsWith('@')) { - filesString += " '" + multipartKey + "': open('" + multipartValue.slice(1) + "', 'rb')" - } else { - filesString += " '" + multipartKey + "': '" + multipartValue + "'" - } - if (filesIndex < filesCount - 1) { - filesString += ',\n' - } - filesIndex++ - } - filesString += '\n}\n' + filesString = getFilesString(request) } var requestLineWithUrlParams = 'requests.' + request.method + '(\'' + request.urlWithoutQuery + '\'' @@ -162,7 +158,7 @@ var toPython = function (curlCommand) { var splitAuth = request.auth.split(':') var user = splitAuth[0] || '' var password = splitAuth[1] || '' - requestLineBody += ", auth=('" + user + "', '" + password + "')" + requestLineBody += ', auth=(' + repr(user) + ', ' + repr(password) + ')' } requestLineBody += ')' diff --git a/util.js b/util.js index 00f27c5..25e1b4f 100644 --- a/util.js +++ b/util.js @@ -31,8 +31,8 @@ var parseCurlCommand = function (curlCommand) { var cookieString var cookies var url = parsedArguments._[1] - // if url argument wasn't where we expected it, check other places - // it shows up + // if url argument wasn't where we expected it, check other places + // it shows up if (!url && parsedArguments['L']) { url = parsedArguments['L'] } @@ -63,7 +63,7 @@ var parseCurlCommand = function (curlCommand) { parsedArguments.F = [parsedArguments.F] } parsedArguments.F.forEach(function (multipartArgument) { - // input looks like key=value. value could be json or a file path prepended with an @ + // input looks like key=value. value could be json or a file path prepended with an @ var splitArguments = multipartArgument.split('=', 2) var key = splitArguments[0] var value = splitArguments[1]