curl.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import argparse
  2. import warnings
  3. from shlex import split
  4. from six.moves.http_cookies import SimpleCookie
  5. from six.moves.urllib.parse import urlparse
  6. from six import string_types, iteritems
  7. from w3lib.http import basic_auth_header
  8. class CurlParser(argparse.ArgumentParser):
  9. def error(self, message):
  10. error_msg = \
  11. 'There was an error parsing the curl command: {}'.format(message)
  12. raise ValueError(error_msg)
  13. curl_parser = CurlParser()
  14. curl_parser.add_argument('url')
  15. curl_parser.add_argument('-H', '--header', dest='headers', action='append')
  16. curl_parser.add_argument('-X', '--request', dest='method', default='get')
  17. curl_parser.add_argument('-d', '--data', dest='data')
  18. curl_parser.add_argument('-u', '--user', dest='auth')
  19. safe_to_ignore_arguments = [
  20. ['--compressed'],
  21. # `--compressed` argument is not safe to ignore, but it's included here
  22. # because the `HttpCompressionMiddleware` is enabled by default
  23. ['-s', '--silent'],
  24. ['-v', '--verbose'],
  25. ['-#', '--progress-bar']
  26. ]
  27. for argument in safe_to_ignore_arguments:
  28. curl_parser.add_argument(*argument, action='store_true')
  29. def curl_to_request_kwargs(curl_command, ignore_unknown_options=True):
  30. """Convert a cURL command syntax to Request kwargs.
  31. :param str curl_command: string containing the curl command
  32. :param bool ignore_unknown_options: If true, only a warning is emitted when
  33. cURL options are unknown. Otherwise raises an error. (default: True)
  34. :return: dictionary of Request kwargs
  35. """
  36. curl_args = split(curl_command)
  37. if curl_args[0] != 'curl':
  38. raise ValueError('A curl command must start with "curl"')
  39. parsed_args, argv = curl_parser.parse_known_args(curl_args[1:])
  40. if argv:
  41. msg = 'Unrecognized options: {}'.format(', '.join(argv))
  42. if ignore_unknown_options:
  43. warnings.warn(msg)
  44. else:
  45. raise ValueError(msg)
  46. url = parsed_args.url
  47. # curl automatically prepends 'http' if the scheme is missing, but Request
  48. # needs the scheme to work
  49. parsed_url = urlparse(url)
  50. if not parsed_url.scheme:
  51. url = 'http://' + url
  52. result = {'method': parsed_args.method.upper(), 'url': url}
  53. headers = []
  54. cookies = {}
  55. for header in parsed_args.headers or ():
  56. name, val = header.split(':', 1)
  57. name = name.strip()
  58. val = val.strip()
  59. if name.title() == 'Cookie':
  60. for name, morsel in iteritems(SimpleCookie(val)):
  61. cookies[name] = morsel.value
  62. else:
  63. headers.append((name, val))
  64. if parsed_args.auth:
  65. user, password = parsed_args.auth.split(':', 1)
  66. headers.append(('Authorization', basic_auth_header(user, password)))
  67. if headers:
  68. result['headers'] = headers
  69. if cookies:
  70. result['cookies'] = cookies
  71. if parsed_args.data:
  72. result['body'] = parsed_args.data
  73. return result