예제 #1
0
    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()
예제 #2
0
    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)
예제 #3
0
    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
예제 #4
0
    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()