Skip to content

Commit 14f6703

Browse files
committed
Fields can be marked as optional using a ?, and the concept is now
conflated with nullability.
1 parent ee07a02 commit 14f6703

File tree

12 files changed

+52
-90
lines changed

12 files changed

+52
-90
lines changed

README.rst

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,14 @@ Types can also be composed of other types::
218218
is_paired=false
219219
team=null
220220

221-
Nullability
221+
Optionality
222222
^^^^^^^^^^^
223223

224-
Note in the preceding example that the ``AccountInfo.team`` field can be a
225-
``Team`` struct or ``Null``. By default, fields do not accept ``null`` as a
226-
valid value unless explicitly indicated.
224+
Note in the preceding example that the ``AccountInfo.team`` field has a data
225+
type of ``Team`` followed by ``?``. ``?`` indicates that the field is optional.
226+
To specify that the field is absent, you can use ``null`` in the example
227+
definitions. By default, fields do not accept ``null`` as a valid value unless
228+
the field is marked optional.
227229

228230
Type Inheritance
229231
^^^^^^^^^^^^^^^^
@@ -250,7 +252,7 @@ A struct can also inherit from another struct using the ``extends`` keyword::
250252
"File size in bytes."
251253
mime_type String|Null
252254
"The Internet media type determined by the file extension."
253-
media_info MediaInfo optional
255+
media_info MediaInfo?
254256
"Information specific to photo and video media."
255257

256258
example default
@@ -261,13 +263,6 @@ A struct can also inherit from another struct using the ``extends`` keyword::
261263
modified="Sat, 28 Jun 2014 18:23:21"
262264
is_deleted=false
263265

264-
Optional Fields
265-
^^^^^^^^^^^^^^^
266-
Note in the preceding example the use of the ``optional`` keyword which denotes
267-
that the field may not be present. We do not conflate the optionality of a field
268-
with the nullability of a field's data_type. However, these concepts may be
269-
intentionally conflated in languages that don't maintain a strict difference.
270-
271266
Default Values
272267
^^^^^^^^^^^^^^
273268

babelapi/babel/lexer.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def test(self, data):
102102

103103
# Tokens related to free text
104104
tokens += (
105-
'LINE',
105+
'LINE',
106106
)
107107

108108
# Whitespace tokens
@@ -131,6 +131,7 @@ def test(self, data):
131131

132132
tokens += (
133133
'ASTERIX',
134+
'Q',
134135
)
135136

136137
# Regular expression rules for simple tokens
@@ -140,6 +141,7 @@ def test(self, data):
140141
t_COMMA = r','
141142
t_PIPE = r'\|'
142143
t_ASTERIX = r'\*'
144+
t_Q = r'\?'
143145

144146
KEYWORDS = [
145147
'alias',
@@ -152,10 +154,8 @@ def test(self, data):
152154
'include',
153155
'namespace',
154156
'of',
155-
'optional',
156157
'pass',
157158
'request',
158-
'required',
159159
'response',
160160
'route',
161161
'struct',
@@ -168,9 +168,7 @@ def test(self, data):
168168
'attrs': 'ATTRS',
169169
'include': 'INCLUDE',
170170
'of': 'OF',
171-
'optional': 'OPTIONAL',
172171
'pass': 'PASS',
173-
'required': 'REQUIRED',
174172
'route': 'ROUTE',
175173
'struct': 'STRUCT',
176174
'union': 'UNION',

babelapi/babel/parser.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ def __init__(self,
115115
name,
116116
data_type_name,
117117
data_type_attrs,
118-
nullable,
119118
optional,
120119
deprecated):
121120
"""
@@ -125,7 +124,6 @@ def __init__(self,
125124
self.data_type_name = data_type_name
126125
self.data_type_attrs = data_type_attrs
127126
self.doc = None
128-
self.nullable = nullable
129127
self.has_default = False
130128
self.default = None
131129
self.optional = optional
@@ -372,25 +370,10 @@ def p_field_list_extend(self, p):
372370
p[0] = p[1]
373371
p[0].append(p[2])
374372

375-
def p_field_nullable(self, p):
376-
"""nullable : PIPE ID
373+
def p_field_optional(self, p):
374+
"""optional : Q
377375
| empty"""
378-
if p[1] is not None:
379-
if p[2] != 'Null':
380-
raise ValueError('Only Null can be specified in a union')
381-
else:
382-
p[0] = True
383-
else:
384-
p[0] = False
385-
386-
def p_field_presence(self, p):
387-
"""presence : REQUIRED
388-
| OPTIONAL
389-
| empty"""
390-
if bool(p[1]):
391-
p[0] = (p[1] == 'optional')
392-
else:
393-
p[0] = False
376+
p[0] = p[1] == '?'
394377

395378
def p_field_deprecation(self, p):
396379
"""deprecation : DEPRECATED
@@ -411,22 +394,17 @@ def p_default_option(self, p):
411394
p[0] = p[1]
412395

413396
def p_statement_field(self, p):
414-
"""field : ID ID attributes_group nullable default_option presence deprecation COLON docstring DEDENT
415-
| ID ID attributes_group nullable default_option presence deprecation NEWLINE INDENT docstring NEWLINE DEDENT
416-
| ID ID attributes_group nullable default_option presence deprecation NEWLINE"""
417-
418-
has_old_style_docstring = (p[8] == ':')
419-
has_new_style_docstring = not has_old_style_docstring and len(p) > 9
420-
p[0] = BabelField(p[1], p[2], p[3], p[4], p[6], p[7])
397+
"""field : ID ID attributes_group optional default_option deprecation NEWLINE INDENT docstring NEWLINE DEDENT
398+
| ID ID attributes_group optional default_option deprecation NEWLINE"""
399+
has_docstring = len(p) > 9
400+
p[0] = BabelField(p[1], p[2], p[3], p[4], p[6])
421401
if p[5] is not None:
422402
if p[5] is BabelNull:
423403
p[0].set_default(None)
424404
else:
425405
p[0].set_default(p[5])
426-
if has_old_style_docstring:
427-
p[0].set_doc(self._normalize_docstring(p[9]))
428-
elif has_new_style_docstring:
429-
p[0].set_doc(p[10])
406+
elif has_docstring:
407+
p[0].set_doc(p[9])
430408

431409
def p_statement_field_symbol(self, p):
432410
"""field : ID COLON docstring DEDENT

babelapi/babel/tower.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,12 +175,11 @@ def _create_field(self, env, babel_field):
175175
babel_field.name,
176176
data_type,
177177
babel_field.doc,
178-
nullable=babel_field.nullable,
179178
optional=babel_field.optional,
180179
deprecated=babel_field.deprecated,
181180
)
182181
if babel_field.has_default:
183-
if not (babel_field.nullable and babel_field.default is None):
182+
if not (babel_field.optional and babel_field.default is None):
184183
# Verify that the type of the default value is correct for this field
185184
data_type.check(babel_field.default)
186185
api_type_field.set_default(babel_field.default)

babelapi/data_type.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ def __init__(self,
244244
name,
245245
data_type,
246246
doc,
247-
nullable=False,
248247
optional=False,
249248
deprecated=False):
250249
"""
@@ -253,14 +252,12 @@ def __init__(self,
253252
:param str name: Name of the field.
254253
:param Type data_type: The type of variable for of this field.
255254
:param str doc: Documentation for the field.
256-
:param bool nullable: Whether the field can be null.
257255
:param bool optional: Whether the field can be absent.
258256
:param bool deprecated: Whether the field is deprecated.
259257
"""
260258
self.name = name
261259
self.data_type = data_type
262260
self.doc = doc
263-
self.nullable = nullable
264261
self.optional = optional
265262
self.deprecated = deprecated
266263
self.has_default = False
@@ -279,18 +276,17 @@ def default(self):
279276

280277
def check(self, val):
281278
if val is None:
282-
if self.nullable:
279+
if self.optional:
283280
return None
284281
else:
285-
raise ValueError('val is None but field is not nullable')
282+
raise ValueError('val is None but field is not optional')
286283
else:
287284
return self.data_type.check(val)
288285

289286
def __repr__(self):
290-
return 'Field(%r, %r, %r, %r)' % (self.name,
291-
self.data_type,
292-
self.nullable,
293-
self.optional)
287+
return 'Field(%r, %r, %r)' % (self.name,
288+
self.data_type,
289+
self.optional)
294290

295291
class SymbolField(object):
296292
symbol = True
@@ -473,7 +469,7 @@ def add_example(self, label, text, example):
473469
if isinstance(field.data_type, CompositeType):
474470
# An example that specifies the key as null is okay if the
475471
# field permits it.
476-
if field.nullable and example[field.name] is None:
472+
if field.optional and example[field.name] is None:
477473
ordered_example[field.name] = None
478474
else:
479475
raise KeyError('Field %r should not be specified since '

dropbox-v2/api/files.babel

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct PathTarget
99
"Path from root. Should be an empty string for root."
1010

1111
struct FileTarget extends PathTarget
12-
rev String optional
12+
rev String?
1313
"Revision of target file."
1414

1515
struct FileInfo
@@ -87,14 +87,14 @@ struct UploadCommit
8787
"Path in the user's Dropbox to save the file."
8888
mode ConflictPolicy
8989
"The course of action to take if a file already exists at :field:`path`."
90-
append_to UploadAppend|Null optional
90+
append_to UploadAppend?
9191
"If specified, the current chunk of data should be appended to an
9292
existing upload session."
93-
autorename Boolean = false optional
93+
autorename Boolean? = false
9494
"Whether the file should be autorenamed in the event of a conflict."
95-
client_modified_utc UInt64|Null = null optional
95+
client_modified_utc UInt64? = null
9696
"Self reported time of when this file was created or modified."
97-
mute Boolean = false optional
97+
mute Boolean? = false
9898
"Whether the devices that the user has linked should notify them of the
9999
new or updated file."
100100

dropbox-v2/api/users.babel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,15 @@ struct MeInfo extends BasicAccountInfo
6868

6969
email String
7070
"The user's e-mail address."
71-
country String(min_length=2, max_length=2)|Null
71+
country String(min_length=2, max_length=2)?
7272
"The user's two-letter country code, if available."
7373
locale String(min_length=2, max_length=2)
7474
"The language setting that user specified."
7575
referral_link String
7676
"The user's :link:`referral link https://www.dropbox.com/referrals`."
7777
space Space
7878
"The user's quota."
79-
team Team|Null
79+
team Team?
8080
"If this account is a member of a team."
8181
is_paired Boolean
8282
"Whether the user has a personal and work account. If the authorized

dropbox-v2/python/dbx_python_json.babelg.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ def _generate_struct_class_init(self, data_type):
342342
field.name,
343343
self._is_instance_type(field.data_type),
344344
)
345-
if field.nullable or field.optional:
345+
if field.optional:
346346
# We conflate nullability and optionality in Python
347347
self.emit_line('if {} is not None:'.format(field.name))
348348
with self.indent():
@@ -356,7 +356,7 @@ def _generate_struct_class_init(self, data_type):
356356
field.name,
357357
self._is_instance_type(field.data_type),
358358
)
359-
if field.nullable or field.optional:
359+
if field.optional:
360360
# We conflate nullability and optionality in Python
361361
self.emit_line('if {} is not None:'.format(field.name))
362362
with self.indent():
@@ -828,7 +828,7 @@ def _generate_route_method_decl(self, route, request_data_type, request_binary_b
828828
args.append('f')
829829
if not is_null_type(request_data_type):
830830
for field in request_data_type.all_fields:
831-
if field.optional or field.nullable:
831+
if field.optional:
832832
if field.has_default:
833833
arg = '{}={}'.format(field.name, self.lang.format_obj(field.default))
834834
args.append(arg)

dropbox-v2/python/dropbox/base_files.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -455,12 +455,9 @@ def __init__(self,
455455
:param append_to: If specified, the current chunk of data should be
456456
appended to an existing upload session.
457457
:type append_to: :class:`UploadAppend`
458-
:param bool autorename: Whether the file should be autorenamed in the
459-
event of a conflict.
460-
:param long client_modified_utc: Self reported time of when this file
461-
was created or modified.
462-
:param bool mute: Whether the devices that the user has linked should
463-
notify them of the new or updated file.
458+
:type autorename: bool
459+
:type client_modified_utc: long
460+
:type mute: bool
464461
"""
465462
assert isinstance(path, six.string_types), 'path must be of type six.string_types'
466463
self.path = path
@@ -1001,12 +998,9 @@ def upload(self,
1001998
:param append_to: If specified, the current chunk of data should be
1002999
appended to an existing upload session.
10031000
:type append_to: :class:`UploadAppend`
1004-
:param bool autorename: Whether the file should be autorenamed in the
1005-
event of a conflict.
1006-
:param long client_modified_utc: Self reported time of when this file
1007-
was created or modified.
1008-
:param bool mute: Whether the devices that the user has linked should
1009-
notify them of the new or updated file.
1001+
:type autorename: bool
1002+
:type client_modified_utc: long
1003+
:type mute: bool
10101004
:rtype: :class:`FileInfo`
10111005
:raises: :class:`dropbox.exceptions.ApiError`
10121006

dropbox-v2/python/dropbox/base_users.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,12 @@ def __init__(self,
190190
account_id,
191191
name,
192192
email,
193-
country,
194193
locale,
195194
referral_link,
196195
space,
197-
team,
198196
is_paired,
197+
country=None,
198+
team=None,
199199
**kwargs):
200200
"""
201201
:param str email: The user's e-mail address.
@@ -251,12 +251,14 @@ def to_json(self):
251251
d = dict(account_id=self.account_id,
252252
name=self.name.to_json(),
253253
email=self.email,
254-
country=self.country,
255254
locale=self.locale,
256255
referral_link=self.referral_link,
257256
space=self.space.to_json(),
258-
team=self.team.to_json(),
259257
is_paired=self.is_paired)
258+
if self.country:
259+
d['country'] = self.country
260+
if self.team:
261+
d['team'] = self.team.to_json()
260262
return d
261263

262264
def __repr__(self):

0 commit comments

Comments
 (0)