Search code examples
pythontranscrypt

Transcrypt: Cast a client-side JS Object as a dict?


I have a class definition that begins as shown below.

class Ace11():
    def __init__(self, register_list):
        """
        Parse and validate the list of dicts.
        """
        self.required_keys = """
            briefdescription defaultvalue flatname rnum rtype where
            """.strip().split()
        self.registers = register_list
        ## Validate
        for d in self.registers:
            if not isinstance(d, dict):
                raise TypeError("{d} is not a dict.".format(**locals()))

            for k in self.required_keys:
                if not k in d.keys():
                    raise ValueError("Key {k} missing from {d}"
                                     .format(**locals()))

When I try to instantiate it on the client side with a var containing an Array of Objects, TypeError is raised, presumably because isinstance is being very literal about what's a dict and what isn't. I'd like to keep the validation as is because the same code is useful on the server side.

What's the correct way to handle this?


Solution

  • The problem is that JS objects are by nature no complete dicts, they miss a number of methods. But they can be converted to dicts by the dict () constructor. This is very fast, since no data is copied, only the prototype is changed to include the required dict methods.

    The isinstance method of course could be altered to return True on isinstance (<anyJsObject>, dict). But that would make it impossible to make the distinction between a dict and an object. So methods could be called on that the object doesn't have. This beats the purpose of the typecheck.

    Since you want to keep code on server and client identical, you could convert JS objects in your register_list to dict prior to the call.

    But the check wouldn't be meaningfull anymore (at least on the client) since indeed all elements of register_list would certainly be dicts in that case.

    So, alternatively, you could just leave out the check, but you say you want to keep the validation.

    A more refined alternative is to use the same source code but do one kind of check for Transcrypt and another kind for CPython:

    from org.transcrypt.stubs.browser import __envir__
    
    if __envir__.executor_name == __envir__.transpiler_name:
        <do Transcrypt validation and, possibly, conversion, or nothing at all>
    else:
        <do CPython validation>
    

    You may e.g. in Transcrypt check if something is a JS object and then convert it to a dict by calling the dict constructor upon it.