Skip to content
This repository was archived by the owner on Mar 18, 2019. It is now read-only.

Commit 46bec50

Browse files
committed
Docs
1 parent df52bb3 commit 46bec50

File tree

9 files changed

+120
-45
lines changed

9 files changed

+120
-45
lines changed

coreapi/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def _lookup_link(document, keys):
5454
def _validate_parameters(link, parameters):
5555
"""
5656
Ensure that parameters passed to the link are correct.
57-
Raises a `ValidationError` if any parameters do not validate.
57+
Raises a `ParameterError` if any parameters do not validate.
5858
"""
5959
provided = set(parameters.keys())
6060
required = set([
@@ -77,7 +77,7 @@ def _validate_parameters(link, parameters):
7777
errors[item] = 'Unknown parameter.'
7878

7979
if errors:
80-
raise exceptions.ValidationError(errors)
80+
raise exceptions.ParameterError(errors)
8181

8282

8383
def get_default_decoders():

coreapi/codecs/download.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,14 @@
11
# coding: utf-8
22
from coreapi.codecs.base import BaseCodec
33
from coreapi.compat import urlparse
4+
from coreapi.utils import DownloadedFile
45
import cgi
56
import mimetypes
67
import os
78
import posixpath
89
import tempfile
910

1011

11-
class DownloadedFile(tempfile._TemporaryFileWrapper):
12-
basename = None
13-
14-
def __repr__(self):
15-
state = "closed" if self.close_called else "open"
16-
mode = "" if self.close_called else " '%s'" % self.file.mode
17-
return "<DownloadedFile '%s', %s%s>" % (self.name, state, mode)
18-
19-
def __str__(self):
20-
return self.__repr__()
21-
22-
2312
def _unique_output_path(path):
2413
"""
2514
Given a path like '/a/b/c.txt'

coreapi/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class LinkLookupError(CoreAPIException):
3737
pass
3838

3939

40-
class ValidationError(CoreAPIException):
40+
class ParameterError(CoreAPIException):
4141
"""
4242
Raised when the parameters passed do not match the link fields.
4343

coreapi/transports/http.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ def _get_params(method, encoding, fields, params=None):
8282
elif location == 'form':
8383
if not seen_body:
8484
data[key] = utils.validate_form_param(value, encoding=encoding)
85-
except exceptions.ValidationError as exc:
85+
except exceptions.ParameterError as exc:
8686
errors[key] = exc.message
8787

8888
if errors:
89-
raise exceptions.ValidationError(errors)
89+
raise exceptions.ParameterError(errors)
9090

9191
# Move any files from 'data' into 'files'.
9292
if isinstance(data, dict):

coreapi/utils.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from coreapi.compat import string_types, text_type, urlparse
33
from collections import namedtuple
44
import os
5+
import tempfile
56

67

78
File = namedtuple('File', 'name content content_type')
@@ -26,6 +27,18 @@ def guess_filename(obj):
2627
return None
2728

2829

30+
class DownloadedFile(tempfile._TemporaryFileWrapper):
31+
basename = None
32+
33+
def __repr__(self):
34+
state = "closed" if self.close_called else "open"
35+
mode = "" if self.close_called else " '%s'" % self.file.mode
36+
return "<DownloadedFile '%s', %s%s>" % (self.name, state, mode)
37+
38+
def __str__(self):
39+
return self.__repr__()
40+
41+
2942
# Negotiation utilities. USed to determine which codec or transport class
3043
# should be used, given a list of supported instances.
3144

@@ -109,7 +122,7 @@ def validate_path_param(value):
109122
value = _validate_form_field(value, allow_list=False)
110123
if not value:
111124
msg = 'Parameter %s: May not be empty.'
112-
raise exceptions.ValidationError(msg)
125+
raise exceptions.ParameterError(msg)
113126
return value
114127

115128

@@ -127,7 +140,7 @@ def validate_body_param(value, encoding):
127140
elif encoding == 'application/octet-stream':
128141
if not is_file(value):
129142
msg = 'Must be an file upload.'
130-
raise exceptions.ValidationError(msg)
143+
raise exceptions.ParameterError(msg)
131144
return value
132145
msg = 'Unsupported encoding "%s" for outgoing request.'
133146
raise exceptions.NetworkError(msg % encoding)
@@ -150,7 +163,7 @@ def _validate_form_object(value, allow_files=False):
150163
"""
151164
if not isinstance(value, dict):
152165
msg = 'Must be an object.'
153-
raise exceptions.ValidationError(msg)
166+
raise exceptions.ParameterError(msg)
154167
return {
155168
text_type(item_key): _validate_form_field(item_val, allow_files=allow_files)
156169
for item_key, item_val in value.items()
@@ -179,7 +192,7 @@ def _validate_form_field(value, allow_files=False, allow_list=True):
179192
return value
180193

181194
msg = 'Must be a primative type.'
182-
raise exceptions.ValidationError(msg)
195+
raise exceptions.ParameterError(msg)
183196

184197

185198
def _validate_json_data(value):
@@ -197,4 +210,4 @@ def _validate_json_data(value):
197210
}
198211

199212
msg = 'Must be a JSON primative.'
200-
raise exceptions.ValidationError(msg)
213+
raise exceptions.ParameterError(msg)

docs/api-guide/exceptions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ An issue occurred with the network request.
2424

2525
The keys passed in a [`client.action()`][action] call did not reference a link in the document.
2626

27-
## ValidationError
27+
## ParameterError
2828

2929
The parameters passed in a [`client.action()`][action] call did not match the set of required and optional fields made available by the link, or if the type of parameters passed could
3030
not be supported by the given encoding on the link.
3131

3232
## ErrorMessage
3333

34-
The server returned a CoreAPI [Error][error].
34+
The server returned a CoreAPI [Error][error] document.
3535

3636
[action]: /api-guide/client.md#interacting-with-an-api
3737
[error]: /api-guide/document.md#error

docs/api-guide/utils.md

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,52 @@ may be useful if writing a custom client or transport class.
55

66
---
77

8+
## File utilities
9+
10+
The following classes are used to indicate upload and download file content.
11+
12+
### File
13+
14+
May be used as a parameter with links that require a file input.
15+
16+
**Signature**: `File(name, content, content_type=None)`
17+
18+
* `name` - The filename.
19+
* `content` - A string, bytestring, or stream object.
20+
* `content_type` - An optional string representing the content type of the file.
21+
22+
An open file or other stream may also be used directly as a parameter, instead
23+
of a `File` instance, but the `File` instance makes it easier to specify the
24+
filename and content in code.
25+
26+
Example:
27+
28+
>>> from coreapi.utils import File
29+
>>> upload = File('example.csv', 'a,b,c\n1,2,3\n4,5,6\n')
30+
>>> data = client.action(document, ['store', 'upload_media'], params={'upload': upload})
31+
32+
### DownloadedFile
33+
34+
A temporary file instance, used to represent downloaded media.
35+
36+
Available attributes:
37+
38+
* `name` - The full filename, including the path.
39+
* `basename` - The filename as determined at the point of download.
40+
41+
Example:
42+
43+
>>> download = client.action(document, ['user', 'get_profile_image'])
44+
>>> download.basename
45+
'avatar.png'
46+
>>> download.read()
47+
b'...'
48+
49+
By default the file will be deleted when this object goes out of scope. See
50+
[the `DownloadCodec` documentation][download-codec] for more details.
51+
52+
---
53+
854
## Negotiation utilities
955

1056
The following functions are used to determine which of a set of transports
@@ -57,15 +103,15 @@ if an invalid value is passed.
57103

58104
Returns the value, coerced into a string primitive. Validates that the value that is suitable for use in URI-encoded path parameters. Empty strings and composite types such as dictionaries are disallowed.
59105

60-
May raise `ValidationError`.
106+
May raise `ParameterError`.
61107

62108
### validate_query_param
63109

64110
**Signature**: `validate_query_param(value)`
65111

66112
Returns the value, coerced into a string primitive. Validates that the value is suitable for use in URL query parameters.
67113

68-
May raise `ValidationError`.
114+
May raise `ParameterError`.
69115

70116
### validate_body_param
71117

@@ -75,7 +121,7 @@ Returns the value, coerced into a primitive that is valid for the given encoding
75121

76122
Valid encodings are `application/json`, `x-www-form-urlencoded`, `multipart/form-data` and `application/octet-stream`.
77123

78-
May raise `ValidationError` for an invalid value, or `NetworkError` for an unsupported encoding.
124+
May raise `ParameterError` for an invalid value, or `NetworkError` for an unsupported encoding.
79125

80126
### validate_form_param
81127

@@ -85,4 +131,7 @@ Returns the value, coerced into a primitive that is valid for the given encoding
85131

86132
Valid encodings are `application/json`, `x-www-form-urlencoded`, `multipart/form-data`.
87133

88-
May raise `ValidationError`, or `NetworkError` for an unsupported encoding.
134+
May raise `ParameterError`, or `NetworkError` for an unsupported encoding.
135+
136+
137+
[download-codec]: codecs.md#downloadcodec

docs/topics/release-notes.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,25 @@
11
# Release Notes
2+
3+
## 2.0
4+
5+
* Upload and download support.
6+
* Media type changes from `application/vnd.coreapi+json` to `application/coreapi+json`.
7+
For backwards compatibility, either are currently accepted.
8+
* Codec methods `dump()`/`load()` become `encode()`/`decode()`. The old style
9+
methods currently continue to work for backward compatibility.
10+
* The client instance validates that passed parameters match the available parameter names.
11+
Fails if unknown parameters are included, or required parameters are not included.
12+
* `.action()` now accepts a `validate=False` argument, to turn off parameter validation.
13+
* Parameter values are validated against the encoding used on the link to ensure
14+
that they can be represented in the request.
15+
* `type` annotation added to `Field` instances.
16+
* `multipart/form-data` is now consistently used on multipart links, even when
17+
no file arguments are passed.
18+
* `action`, `encoding`, and `transform` parameters to `.action()` now replaced with a
19+
single `overrides` argument. The old style arguments currently continue to work for
20+
backward compatibility.
21+
* The `supports` attribute is no longer used when defining codec classes. A
22+
`supports` property currently exists on the base class, to provide backwards
23+
compatibility for `coreapi-cli`.
24+
25+
The various backwards compatibility shims are planned to be removed in the 2.1 release.

tests/test_utils.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
def test_validate_path_param():
77
assert utils.validate_path_param(1) == '1'
88
assert utils.validate_path_param(True) == 'true'
9-
with pytest.raises(exceptions.ValidationError):
9+
with pytest.raises(exceptions.ParameterError):
1010
utils.validate_path_param(None)
11-
with pytest.raises(exceptions.ValidationError):
11+
with pytest.raises(exceptions.ParameterError):
1212
utils.validate_path_param('')
13-
with pytest.raises(exceptions.ValidationError):
13+
with pytest.raises(exceptions.ParameterError):
1414
utils.validate_path_param({})
15-
with pytest.raises(exceptions.ValidationError):
15+
with pytest.raises(exceptions.ParameterError):
1616
utils.validate_path_param([])
1717

1818

@@ -22,9 +22,9 @@ def test_validate_query_param():
2222
assert utils.validate_query_param(None) == ''
2323
assert utils.validate_query_param('') == ''
2424
assert utils.validate_query_param([1, 2, 3]) == ['1', '2', '3']
25-
with pytest.raises(exceptions.ValidationError):
25+
with pytest.raises(exceptions.ParameterError):
2626
utils.validate_query_param({})
27-
with pytest.raises(exceptions.ValidationError):
27+
with pytest.raises(exceptions.ParameterError):
2828
utils.validate_query_param([1, 2, {}])
2929

3030

@@ -44,41 +44,41 @@ def test_validate_form_data():
4444

4545
# Invalid JSON
4646
data = datetime.datetime.now()
47-
with pytest.raises(exceptions.ValidationError):
47+
with pytest.raises(exceptions.ParameterError):
4848
utils.validate_form_param(data, 'application/json')
49-
with pytest.raises(exceptions.ValidationError):
49+
with pytest.raises(exceptions.ParameterError):
5050
utils.validate_body_param(data, 'application/json')
5151

5252
data = utils.File('abc.txt', None)
53-
with pytest.raises(exceptions.ValidationError):
53+
with pytest.raises(exceptions.ParameterError):
5454
utils.validate_form_param(data, 'application/json')
55-
with pytest.raises(exceptions.ValidationError):
55+
with pytest.raises(exceptions.ParameterError):
5656
utils.validate_body_param(data, 'application/json')
5757

5858
# URL Encoded
5959
assert utils.validate_form_param(123, 'application/x-www-form-urlencoded') == '123'
6060
assert utils.validate_body_param({'a': 123}, 'application/x-www-form-urlencoded') == {'a': '123'}
61-
with pytest.raises(exceptions.ValidationError):
61+
with pytest.raises(exceptions.ParameterError):
6262
utils.validate_form_param({'a': {'foo': 'bar'}}, 'application/x-www-form-urlencoded')
63-
with pytest.raises(exceptions.ValidationError):
63+
with pytest.raises(exceptions.ParameterError):
6464
utils.validate_body_param(123, 'application/x-www-form-urlencoded')
65-
with pytest.raises(exceptions.ValidationError):
65+
with pytest.raises(exceptions.ParameterError):
6666
utils.validate_form_param(utils.File('abc.txt', None), 'application/x-www-form-urlencoded')
67-
with pytest.raises(exceptions.ValidationError):
67+
with pytest.raises(exceptions.ParameterError):
6868
utils.validate_body_param({'a': utils.File('abc.txt', None)}, 'application/x-www-form-urlencoded')
6969

7070
# Multipart
7171
assert utils.validate_form_param(123, 'multipart/form-data') == '123'
7272
assert utils.validate_form_param(utils.File('abc.txt', None), 'multipart/form-data') == utils.File('abc.txt', None)
7373
assert utils.validate_body_param({'a': 123}, 'multipart/form-data') == {'a': '123'}
7474
assert utils.validate_body_param({'a': utils.File('abc.txt', None)}, 'multipart/form-data') == {'a': utils.File('abc.txt', None)}
75-
with pytest.raises(exceptions.ValidationError):
75+
with pytest.raises(exceptions.ParameterError):
7676
utils.validate_form_param({'a': {'foo': 'bar'}}, 'multipart/form-data')
77-
with pytest.raises(exceptions.ValidationError):
77+
with pytest.raises(exceptions.ParameterError):
7878
utils.validate_body_param(123, 'multipart/form-data')
7979

8080
# Raw upload
81-
with pytest.raises(exceptions.ValidationError):
81+
with pytest.raises(exceptions.ParameterError):
8282
utils.validate_body_param(123, 'application/octet-stream')
8383

8484
# Invalid encoding on outgoing request

0 commit comments

Comments
 (0)