def _emit_tests(self, ns, typ, json_encode): ns_name = self.namespace_name(ns) type_name = self.struct_name(typ) # The general idea here is to instantiate each type using the reference # Python code, put some random data in the fields, serialize it to # JSON, emit the JSON into the Rust test, have Rust deserialize it, and # emit assertions that the fields match. Then have Rust re-serialize to # JSON and desereialize it again, then check the fields of the # newly-deserialized struct. This verifies Rust's serializer. for test_value in self.make_test_value(typ): pyname = fmt_py_class(typ.name) json = json_encode( self.reference_impls[ns.name].__dict__[pyname + '_validator'], test_value.value, Permissions()) # "other" is a hardcoded, special-cased tag used by Stone for the # catch-all variant of open unions. Let's rewrite it to something # else, to test that the unknown variant logic actually works. # Unfortunately this requires mega-hax of rewriting the JSON text, # because the Python serializer won't let us give an arbitrary # variant name. json = json.replace( '{".tag": "other"', '{".tag": "dropbox-sdk-rust-bogus-test-variant"') with self._test_fn(type_name + test_value.test_suffix()): self.emit(u'let json = r#"{}"#;'.format(json)) self.emit( u'let x = ::serde_json::from_str::<::dropbox_sdk::{}::{}>(json).unwrap();' .format(ns_name, self.struct_name(typ))) test_value.emit_asserts(self, 'x') self.emit(u'assert_eq!(x, x.clone());') if test_value.is_serializable(): # now serialize it back to JSON, deserialize it again, and # test it again. self.emit() self.emit( u'let json2 = ::serde_json::to_string(&x).unwrap();') de = u'::serde_json::from_str::<::dropbox_sdk::{}::{}>(&json2).unwrap()' \ .format(ns_name, self.struct_name(typ)) if typ.all_fields: self.emit(u'let x2 = {};'.format(de)) test_value.emit_asserts(self, 'x2') self.emit(u'assert_eq!(x, x2);') else: self.emit(u'{};'.format(de)) else: # assert that serializing it returns an error self.emit( u'assert!(::serde_json::to_string(&x).is_err());') self.emit()
def with_path_root(self, path_root): """ Creates a clone of the Dropbox instance with the Dropbox-API-Path-Root header as the appropriate serialized instance of PathRoot. For more information, see https://www.dropbox.com/developers/reference/namespace-guide#pathrootmodes :param PathRoot path_root: instance of PathRoot to serialize into the headers field :return: A :class: `Dropbox` :rtype: Dropbox """ if not isinstance(path_root, PathRoot): raise ValueError("path_root must be an instance of PathRoot") new_headers = self._headers.copy() if self._headers else {} new_headers[PATH_ROOT_HEADER] = stone_serializers.json_encode( PathRoot_validator, path_root) return self.clone(headers=new_headers)
def request(self, route, namespace, request_arg, request_binary, timeout=None): """ Makes a request to the Dropbox API and in the process validates that the route argument and result are the expected data types. The request_arg is converted to JSON based on the arg_data_type. Likewise, the response is deserialized from JSON and converted to an object based on the {result,error}_data_type. :param host: The Dropbox API host to connect to. :param route: The route to make the request to. :type route: :class:`stone.backends.python_rsrc.stone_base.Route` :param request_arg: Argument for the route that conforms to the validator specified by route.arg_type. :param request_binary: String or file pointer representing the binary payload. Use None if there is no binary payload. :param Optional[float] timeout: Maximum duration in seconds that client will wait for any single packet from the server. After the timeout the client will give up on connection. If `None`, will use default timeout set on Dropbox object. Defaults to `None`. :return: The route's result. """ self.check_and_refresh_access_token() host = route.attrs['host'] or 'api' route_name = namespace + '/' + route.name if route.version > 1: route_name += '_v{}'.format(route.version) route_style = route.attrs['style'] or 'rpc' serialized_arg = stone_serializers.json_encode(route.arg_type, request_arg) if (timeout is None and route == files.list_folder_longpoll): # The client normally sends a timeout value to the # longpoll route. The server will respond after # <timeout> + random(0, 90) seconds. We increase the # socket timeout to the longpoll timeout value plus 90 # seconds so that we don't cut the server response short # due to a shorter socket timeout. # NB: This is done here because base.py is auto-generated timeout = request_arg.timeout + 90 res = self.request_json_string_with_retry(host, route_name, route_style, serialized_arg, request_binary, timeout=timeout) decoded_obj_result = json.loads(res.obj_result) if isinstance(res, RouteResult): returned_data_type = route.result_type obj = decoded_obj_result elif isinstance(res, RouteErrorResult): returned_data_type = route.error_type obj = decoded_obj_result['error'] user_message = decoded_obj_result.get('user_message') user_message_text = user_message and user_message.get('text') user_message_locale = user_message and user_message.get('locale') else: raise AssertionError('Expected RouteResult or RouteErrorResult, ' 'but res is %s' % type(res)) deserialized_result = stone_serializers.json_compat_obj_decode( returned_data_type, obj, strict=False) if isinstance(res, RouteErrorResult): raise ApiError(res.request_id, deserialized_result, user_message_text, user_message_locale) elif route_style == self._ROUTE_STYLE_DOWNLOAD: return (deserialized_result, res.http_resp) else: return deserialized_result
def generate(self, api): print(u'Generating Python reference code') self.reference.generate(api) with self.output_to_relative_path('reference/__init__.py'): self.emit(u'# this is the Stone-generated reference Python SDK') print(u'Loading reference code:') sys.path.insert(0, self.target_path) sys.path.insert(1, "stone") from stone.backends.python_rsrc.stone_serializers import json_encode for ns in api.namespaces: print('\t' + ns) python_ns = ns if ns == 'async': # hack to work around 'async' being a Python3 keyword python_ns = 'async_' self.reference_impls[ns] = __import__( 'reference.' + python_ns).__dict__[python_ns] print(u'Generating test code') for ns in api.namespaces.values(): ns_name = self.namespace_name(ns) with self.output_to_relative_path(ns_name + '.rs'): self._emit_header() for typ in ns.data_types: type_name = self.struct_name(typ) # the general idea here is to instantiate each type using # the reference Python code, put some random data in the # fields, serialize it to JSON, emit the JSON into the Rust # test, have Rust deserialize it, and emit assertions that # the fields match. Then have Rust re-serialize to JSON and # desereialize it again, then check the fields of the # newly-deserialized struct. This verifies Rust's # serializer. is_serializable = True test_value = None if ir.is_struct_type(typ): if typ.has_enumerated_subtypes(): # TODO: generate tests for all variants # for now, just pick the first variant variant = typ.get_enumerated_subtypes()[0] test_value = TestPolymorphicStruct( self, typ, self.reference_impls, variant) else: test_value = TestStruct(self, typ, self.reference_impls) elif ir.is_union_type(typ): # TODO: generate tests for all variants # for now, just pick the first variant # prefer choosing from this type and not the parent if we can variants = [ field for field in typ.fields if not field.catch_all ] if len(variants) == 0: # fall back to parent type's variants variants = [ field for field in typ.all_fields if not field.catch_all ] if not variants: # Rust code will refuse to serialize a type with no variants (or only # the catch-all variant), so don't bother testing that is_serializable = False variant = typ.all_fields[ 0] # this assumes there's at least one else: variant = variants[0] test_value = TestUnion(self, typ, self.reference_impls, variant) else: raise RuntimeError( u'ERROR: type {} is neither struct nor union'. format(typ)) pyname = fmt_py_class(typ.name) json = json_encode( self.reference_impls[ns.name].__dict__[pyname + '_validator'], test_value.value, Permissions()) with self._test_fn(type_name): self.emit(u'let json = r#"{}"#;'.format(json)) self.emit( u'let x = ::serde_json::from_str::<::dropbox_sdk::{}::{}>(json).unwrap();' .format(ns_name, self.struct_name(typ))) test_value.emit_asserts(self, 'x') if is_serializable: # now serialize it back to JSON, deserialize it again, and test # it again. self.emit() self.emit( u'let json2 = ::serde_json::to_string(&x).unwrap();' ) de = u'::serde_json::from_str::<::dropbox_sdk::{}::{}>(&json2).unwrap()' \ .format(ns_name, self.struct_name(typ)) if typ.all_fields: self.emit(u'let x2 = {};'.format(de)) test_value.emit_asserts(self, 'x2') else: self.emit(u'{};'.format(de)) else: # assert that serializing it returns an error self.emit( u'assert!(::serde_json::to_string(&x).is_err());' ) self.emit() # for typ # .rs test file # for ns with self.output_to_relative_path('mod.rs'): self._emit_header() for ns in api.namespaces: self.emit(u'#[cfg(feature = "dbx_{}")]'.format(ns)) self.emit(u'mod {};'.format(self.namespace_name_raw(ns))) self.emit()