예제 #1
0
    def parse(self, filepath, filecontent):
        parse_globals = self._globals

        python = filecontent
        symbols = {}
        exec(python, parse_globals, symbols)

        objects = []
        for name, obj in symbols.items():
            if isinstance(obj, type):
                # Allow type imports
                continue

            if not Serializable.is_serializable(obj):
                raise ParseError(
                    f"Found a non-serializable top-level object: {obj}")

            attributes = obj._asdict()
            if "name" in attributes:
                attributes = attributes.copy()
                redundant_name = attributes.pop("name", None)
                if redundant_name and redundant_name != name:
                    raise ParseError(
                        "The object named {!r} is assigned to a mismatching name {!r}"
                        .format(redundant_name, name))
            obj_type = type(obj)
            named_obj = obj_type(name=name, **attributes)
            objects.append(named_obj)
        return objects
예제 #2
0
    def parse(cls, filepath: str, filecontent: bytes, parser: Parser) -> "AddressMap":
        """Parses a source for addressable Serializable objects.

        No matter the parser used, the parsed and mapped addressable objects are all 'thin'; ie: any
        objects they point to in other namespaces or even in the same namespace but from a separate
        source are left as unresolved pointers.

        :param filepath: The path to the byte source containing serialized objects.
        :param filecontent: The content of byte source containing serialized objects to be parsed.
        :param parser: The parser cls to use.
        """
        try:
            objects = parser.parse(filepath, filecontent)
        except Exception as e:
            raise MappingError(f"Failed to parse {filepath}:\n{e!r}")
        objects_by_name: Dict[str, ThinAddressableObject] = {}
        for obj in objects:
            if not Serializable.is_serializable(obj):
                raise UnaddressableObjectError("Parsed a non-serializable object: {!r}".format(obj))
            attributes = obj._asdict()

            name = attributes.get("name")
            if not name:
                raise UnaddressableObjectError("Parsed a non-addressable object: {!r}".format(obj))

            if name in objects_by_name:
                raise DuplicateNameError(
                    "An object already exists at {!r} with name {!r}: {!r}.  Cannot "
                    "map {!r}".format(filepath, name, objects_by_name[name], obj)
                )
            objects_by_name[name] = obj
        return cls(filepath, dict(sorted(objects_by_name.items())))
예제 #3
0
파일: addressable.py 프로젝트: wiwa/pants
    def _checked_value(self, instance, value):
        # We allow five forms of value:
        # 0. None.
        # 1. An opaque (to us) address pointing to a value that can be resolved by external
        #    means.
        # 2. A `Resolvable` value that we can lazily resolve and type-check in `__get__`.
        # 3. A concrete instance that meets our type constraint.
        # 4. A dict when our type constraint has exactly one Serializable subject type - we convert the
        #    dict into an instance of that type.
        if value is None:
            return None

        if isinstance(value, (str, Address, Resolvable)):
            return value

        # Support untyped dicts that we deserialize on-demand here into the required type.
        # This feature allows for more brevity in the JSON form (local type inference) and an alternate
        # construction style in the python forms.
        type_constraint = self._get_type_constraint(instance)
        if (isinstance(value, dict) and len(type_constraint.types) == 1 and
                Serializable.is_serializable_type(type_constraint.types[0])):
            if not value:
                # TODO(John Sirois): Is this the right thing to do?  Or should an empty serializable_type
                # be constructed?
                return None  # {} -> None.
            else:
                serializable_type = type_constraint.types[0]
                return serializable_type(**value)

        try:
            return type_constraint.validate_satisfied_by(value)
        except TypeConstraintError as e:
            raise AddressableTypeValidationError(
                "The value for the {} attribute of {} was invalid".format(
                    self._name, instance), e)
예제 #4
0
 def registered(type_name, object_type, name=None, **kwargs):
     if name:
         obj = object_type(name=name, type_alias=type_name, **kwargs)
         if Serializable.is_serializable(obj):
             objects.append(obj)
         return obj
     else:
         return object_type(type_alias=type_name, **kwargs)
예제 #5
0
def _object_encoder(obj, inline):
    if isinstance(obj, Resolvable):
        return obj.resolve() if inline else obj.address
    if isinstance(obj, Address):
        return obj.reference()
    if not Serializable.is_serializable(obj):
        raise ParseError(
            "Can only encode Serializable objects in JSON, given {!r} of type {}"
            .format(obj,
                    type(obj).__name__))

    encoded = obj._asdict()
    if "type_alias" not in encoded:
        encoded = encoded.copy()
        encoded[
            "type_alias"] = f"{inspect.getmodule(obj).__name__}.{type(obj).__name__}"
    return {k: v for k, v in encoded.items() if v}
예제 #6
0
    def __set__(self, instance, value):
        if not Serializable.is_serializable(instance):
            raise NotSerializableError(
                "The addressable descriptor {} can only be applied to methods or "
                "properties of Serializable objects, applied to method {} of "
                "type {}".format(type(self).__name__, self._name, type(instance).__name__)
            )

        instance_dict = instance._asdict()
        if self._name in instance_dict:
            raise MutationError(
                "Attribute {} of {} has already been set to {}, rejecting attempt to "
                "re-set with {}".format(self._name, instance, instance_dict[self._name], value)
            )

        value = self._checked_value(instance, value)

        self._register(instance, self)

        # We mutate the instance dict, which is only OK if used in the conventional idiom of setting
        # the value via this data descriptor in the instance's constructor.
        instance_dict[self._name] = value
예제 #7
0
파일: parser.py 프로젝트: wiwa/pants
 def __init__(self, parse_context, type_alias, object_type):
     self._parse_context = parse_context
     self._type_alias = type_alias
     self._object_type = object_type
     self._serializable = Serializable.is_serializable_type(self._object_type)