def parse(cls, filepath, filecontent, symbol_table_cls, parser_cls): """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 seperate source are left as unresolved pointers. :param string path: The path to the byte source containing serialized objects. :param parser_cls: The parser cls to use. :type parser_cls: A :class:`pants.engine.parser.Parser` """ try: objects = parser_cls.parse(filepath, filecontent, symbol_table_cls) except Exception as e: raise MappingError('Failed to parse {}:\n{}'.format(filepath, e)) objects_by_name = {} 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, OrderedDict(sorted(objects_by_name.items())))
def parse(cls, filepath, filecontent, symbol_table_cls, parser_cls): """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 seperate source are left as unresolved pointers. :param string path: The path to the byte source containing serialized objects. :param parser_cls: The parser cls to use. :type parser_cls: A :class:`pants.engine.parser.Parser` """ try: objects = parser_cls.parse(filepath, filecontent, symbol_table_cls) except Exception as e: raise MappingError('Failed to parse {}:\n{}'.format(filepath, e)) objects_by_name = {} 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, OrderedDict(sorted(objects_by_name.items())))
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)
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
def parse(self, filepath, filecontent): parse_globals = self._globals python = filecontent symbols = {} six.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('Found a non-serializable top-level object: {}'.format(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
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())))
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)
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'] = '{}.{}'.format(inspect.getmodule(obj).__name__, type(obj).__name__) return {k: v for k, v in encoded.items() if v}
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, (six.string_types, 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) return value
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
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
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)
def __init__(self, type_alias, object_type): self._type_alias = type_alias self._object_type = object_type self._serializable = Serializable.is_serializable_type(self._object_type)
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)