Skip to content

Commit 15fd893

Browse files
committed
inital commit
0 parents  commit 15fd893

15 files changed

Lines changed: 734 additions & 0 deletions

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# execution artefacts
2+
*.pyc
3+
.coverage
4+
.DS_Store
5+
6+
# dist artefacts
7+
build/
8+
dist/
9+
cloudconvert.egg-info/
10+
*.egg
11+
12+
# dev artefacts
13+
.idea
14+
test.py

.travis.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
language: python
2+
python:
3+
- "2.7"
4+
- "3.2"
5+
- "3.3"
6+
- "3.4"
7+
install:
8+
- pip install .
9+
- pip install -r requirements-dev.txt
10+
script: nosetests
11+
sudo: false

README.rst

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
cloudconvert-python
2+
===================
3+
4+
This is a lightweight wrapper for the
5+
`CloudConvert <https://cloudconvert.com>`__ API.
6+
7+
Feel free to use, improve or modify this wrapper! If you have questions
8+
contact us or open an issue on GitHub.
9+
10+
.. image:: https://img.shields.io/pypi/v/cloudconvert.svg
11+
:alt: PyPi Version
12+
:target: https://pypi.python.org/pypi/cloudconvert
13+
.. image:: https://travis-ci.org/cloudconvert/cloudconvert-python.svg?branch=master
14+
:alt: Build Status
15+
:target: https://travis-ci.org/cloudconvert/cloudconvert-python
16+
17+
Quickstart
18+
----------
19+
20+
.. code:: python
21+
22+
import cloudconvert
23+
24+
api = cloudconvert.Api('your_api_key')
25+
26+
process = api.convert({
27+
'inputformat': 'png',
28+
'outputformat': 'jpg',
29+
'input': 'upload',
30+
'file': open('tests/input.png', 'rb')
31+
})
32+
process.wait() # wait until conversion finished
33+
process.download("tests/output.png") # download output file
34+
35+
You can use the `CloudConvert API
36+
Console <https://cloudconvert.com/apiconsole>`__ to generate
37+
ready-to-use python code snippets using this wrapper.
38+
39+
Installation
40+
------------
41+
42+
The easiest way to get the latest stable release is to grab it from
43+
`pypi <https://pypi.python.org/pypi/cloudconvert>`__ using ``pip``.
44+
45+
.. code:: bash
46+
47+
pip install cloudconvert
48+
49+
Download of multiple output files
50+
---------------------------------
51+
52+
In some cases it might be possible that there are multiple output files
53+
(e.g. converting a multi-page PDF to JPG). You can download them all to
54+
one directory using the ``downloadAll()`` method.
55+
56+
.. code:: python
57+
58+
import cloudconvert
59+
60+
api = cloudconvert.Api('your_api_key')
61+
62+
process = api.convert({
63+
'inputformat': 'pdf',
64+
'outputformat': 'jpg',
65+
'converteroptions': {
66+
'page_range': '1-3'
67+
},
68+
'input': 'upload',
69+
'file': open('tests/input.pdf', 'rb')
70+
process.wait()
71+
process.downloadAll("tests")
72+
})
73+
74+
Alternatively you can iterate over ``process['output']['files']`` and
75+
download them seperately using
76+
``process.download(localfile, remotefile)``.
77+
78+
How to run tests?
79+
-----------------
80+
81+
::
82+
83+
pip install -r requirements-dev.txt
84+
export API_KEY=your_api_key
85+
nosetests
86+
87+
Resources
88+
---------
89+
90+
- `API Documentation <https://cloudconvert.com/apidoc>`__
91+
- `Conversion Types <https://cloudconvert.com/formats>`__
92+
- `CloudConvert Blog <https://cloudconvert.com/blog>`__
93+
94+
.. |Build Status| image:: https://travis-ci.org/cloudconvert/cloudconvert-python.svg?branch=master
95+
:target: https://travis-ci.org/cloudconvert/cloudconvert-python

cloudconvert/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from .api import Api
2+
from .api import Process
3+
from .exceptions import (
4+
APIError, HTTPError, BadRequest, ConversionFailed, TemporaryUnavailable, InvalidResponse, InvalidParameterException
5+
)

cloudconvert/api.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import json
2+
import urllib
3+
4+
from requests import request, Session
5+
from requests.exceptions import RequestException
6+
from multidimensional_urlencode import urlencode
7+
8+
from .process import Process
9+
from .exceptions import (
10+
APIError, HTTPError, BadRequest, ConversionFailed, TemporaryUnavailable, InvalidResponse, InvalidParameterException
11+
)
12+
13+
class Api(object):
14+
"""
15+
Base CloudConvert API Wrapper for Python
16+
"""
17+
18+
endpoint = "api.cloudconvert.com"
19+
protocol = "https"
20+
21+
def __init__(self, api_key=None):
22+
"""
23+
Creates a new API Client. No credential check is done at this point.
24+
25+
:param str api_key: API key as provided by CloudConvert (https://cloudconvert.com/user/profile)
26+
"""
27+
28+
self._api_key = api_key
29+
30+
# use a requests session to reuse HTTPS connections between requests
31+
self._session = Session()
32+
33+
34+
35+
36+
def get(self, path, parameters=None, is_authenticated=False):
37+
"""
38+
'GET' :py:func:`Client.call` wrapper.
39+
Query string parameters can be set either directly in ``_target`` or as
40+
keywork arguments.
41+
:param string path: API method to call
42+
:param string is_authenticated: If True, send authentication headers. This is
43+
the default
44+
"""
45+
if parameters:
46+
query_string = urlencode(parameters)
47+
if '?' in path:
48+
path = '%s&%s' % (path, query_string)
49+
else:
50+
path = '%s?%s' % (path, query_string)
51+
return self.rawCall('GET', path, None, is_authenticated)
52+
53+
54+
55+
def post(self, path, parameters=None, is_authenticated=False):
56+
"""
57+
'POST' :py:func:`Client.call` wrapper
58+
Body parameters can be set either directly in ``_target`` or as keywork
59+
arguments.
60+
:param string path: API method to call
61+
:param string is_authenticated: If True, send authentication headers. This is
62+
the default
63+
"""
64+
return self.rawCall('POST', path, parameters, is_authenticated)
65+
66+
67+
68+
def delete(self, path, is_authenticated=False):
69+
"""
70+
'DELETE' :py:func:`Client.call` wrapper
71+
:param string path: API method to call
72+
:param string is_authenticated: If True, send authentication headers. This is
73+
the default
74+
"""
75+
return self.rawCall('DELETE', path, None, is_authenticated)
76+
77+
78+
79+
def rawCall(self, method, path, content=None, is_authenticated=False, stream=False):
80+
"""
81+
Low level call helper for making HTTP requests.
82+
:param str method: HTTP method of request (GET,POST,PUT,DELETE)
83+
:param str path: relative url of API request
84+
:param content: body of the request (query parameters for GET requests or body for POST requests)
85+
:param boolean is_authenticated: if the request use authentication
86+
:raises HTTPError: when underlying request failed for network reason
87+
:raises InvalidResponse: when API response could not be decoded
88+
"""
89+
90+
url = path
91+
if path.startswith("//"):
92+
url = self.protocol + ":" + path
93+
elif not path.startswith("http"):
94+
url = self.protocol + "://" + self.endpoint + path
95+
96+
97+
body = None
98+
files = None
99+
headers = {}
100+
101+
# include payload
102+
if content is not None:
103+
104+
## check if we upload anything
105+
isupload = False
106+
for key, value in content.items():
107+
if isinstance(value, file):
108+
## if it is file: remove from content dict and add it to files dict
109+
isupload = True
110+
files = {key: value}
111+
del content[key]
112+
break
113+
114+
if isupload:
115+
url += "?" + urllib.unquote(urlencode(content))
116+
else:
117+
headers['Content-type'] = 'application/json'
118+
body = json.dumps(content)
119+
120+
121+
# add auth header
122+
if is_authenticated and self._api_key is not None:
123+
headers['Authorization'] = 'Bearer ' + self._api_key
124+
125+
# attempt request
126+
try:
127+
result = self._session.request(method, url, headers=headers,
128+
data=body, files=files, stream=stream)
129+
except RequestException as error:
130+
raise HTTPError("HTTP request failed error", error)
131+
132+
code = result.status_code
133+
134+
135+
# attempt to decode and return the response
136+
137+
if not stream:
138+
try:
139+
json_result = result.json()
140+
except ValueError as error:
141+
raise InvalidResponse("Failed to decode API response", error)
142+
143+
144+
# error check
145+
if code >= 100 and code < 300:
146+
if stream:
147+
return result
148+
else:
149+
return json_result
150+
else:
151+
msg = json_result.get('message') if json_result.get('message') else json_result.get('error')
152+
if code == 400:
153+
raise BadRequest(msg)
154+
elif code == 422:
155+
raise ConversionFailed(msg)
156+
elif code == 503:
157+
raise TemporaryUnavailable(msg)
158+
else:
159+
raise APIError(msg)
160+
161+
162+
163+
def createProcess(self, parameters):
164+
"""
165+
Create a new Process
166+
:param parameters: Parameters for creating the Process. See https://cloudconvert.com/apidoc#create
167+
:raises APIError: if the CloudConvert API returns an error
168+
"""
169+
result = self.post("/process", parameters, True)
170+
return Process(self, result['url'])
171+
172+
173+
def convert(self, parameters):
174+
"""
175+
Shortcut: Create a new Process and starts it
176+
:param parameters: Parameters for starting the Process. See https://cloudconvert.com/apidoc#start
177+
:raises APIError: if the CloudConvert API returns an error
178+
"""
179+
180+
startparameters=parameters.copy()
181+
## we don't need the input file for creating the process
182+
del startparameters['file']
183+
process = self.createProcess(startparameters)
184+
return process.start(parameters)

cloudconvert/exceptions.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
All exceptions used in CloudConvert Python wrapper derives from `APIError`
3+
"""
4+
5+
class APIError(Exception):
6+
"""Base CloudConvert API exception, all specific exceptions inherits from it."""
7+
8+
class HTTPError(APIError):
9+
"""Raised when the request fails at a low level (DNS, network, ...)"""
10+
11+
class BadRequest(APIError):
12+
"""Raised when a the CloudConvert API returns any HTTP error code 400"""
13+
14+
class ConversionFailed(APIError):
15+
"""Raised when when a the CloudConvert API returns any HTTP error code 422"""
16+
17+
class TemporaryUnavailable(APIError):
18+
"""Raised when a the CloudConvert API returns any HTTP error code 503"""
19+
20+
class InvalidResponse(APIError):
21+
"""Raised when api response is not valid json"""
22+
23+
class InvalidParameterException(APIError):
24+
"""Raised when request contains bad parameters."""
25+

0 commit comments

Comments
 (0)