예제 #1
0
파일: static.py 프로젝트: buhanec/pygot
 def __init_subclass__(mcs, **kwargs: Any) -> None:
     # We are just going to hijack the children registry for
     # subclasses instead here...
     if mcs.__name__ in StaticData:
         existing = getattr(StaticData, mcs.__name__)
         raise StaticTypeConflict(mcs.__name__, existing)
     StaticData.register(mcs)
     logger.info('%s registered with %s',
                 utils.type_name(mcs), utils.type_name(StaticData))
예제 #2
0
def jsonify(obj: Any,
            camel_case_keys: bool = True,
            arg_struct: bool = True) -> JSONType:
    """
    "JSON-ify" object.

    Attemps to serialise Python object in a fashion that would make
    JSON serialisation and deserialisation easier.

    :param obj: Python object
    :param camel_case_keys: Use camelCase keys
    :param arg_struct: Provide structure with arguments for re-creation
    :return: "JSON-ified" object
    """
    if dataclasses.is_dataclass(obj):
        return _jsonify_dataclass(obj,
                                  camel_case_keys=camel_case_keys,
                                  arg_struct=arg_struct)
    if not isinstance(obj, (str, int, float, bool)) and obj is not None:
        logger.warning('Unsupported type in jsonify: %s (%r)', type_name(obj),
                       obj)
    return obj
예제 #3
0
def unjsonify(json: JSONType, camel_case_keys: bool = True) -> Any:
    """
    "un-JSON-ify" object.

    Attempts to deserialise a previously "JSON-ified" Python object back
    to its original Python object state.

    TODO: dispatching depending on type

    :param json: "JSON-ified" object
    :param camel_case_keys: Use camelCase keys
    :return: Python object
    """
    _uj = functools.partial(unjsonify, camel_case_keys=camel_case_keys)

    # Return basic types as-is
    if isinstance(json, (str, int, float, bool)) or json is None:
        return json

    # Recursively process collections
    if isinstance(json, Sequence):
        return [_uj(j) for j in json]
    if isinstance(json, Mapping):
        mapping = {_uj(k): _uj(v) for k, v in json.items()}

        # Check if a special type, otherwise return as mapping
        module = mapping.pop(MODULE_KEY, None)
        name = mapping.pop(NAME_KEY, None)

        if module is None or name is None:
            if camel_case_keys:
                return {
                    snake_case(k) if isinstance(k, str) else k: v
                    for k, v in mapping.items()
                }
            return mapping

        try:
            cls = getattr(importlib.import_module(module), name)
        except (ImportError, AttributeError) as e:
            raise ValueError(f'Could not locate: {module}.{name}') from e

        # If we explicitly have JSON deserialisation method, use it
        if isinstance(cls, type) and issubclass(cls, JSONMixin):
            return cls.from_json(mapping)

        # If we have an enum, get correct one
        if isinstance(cls, type) and issubclass(cls, Enum):
            return getattr(cls, mapping['name'])

        # Float takes no keyword args
        if cls is float:
            return float(mapping['x'])

        # Otherwise use as kwargs
        try:
            if camel_case_keys:
                return cls(
                    **{
                        snake_case(k) if isinstance(k, str) else k: v
                        for k, v in mapping.items()
                    })
            return cls(**mapping)
        except (TypeError, ValueError) as e:
            raise ValueError(f'Bad arguments for {type_name(cls)}: {e}') from e

    logger.warning('Unsupported type in unjsonify: %s (%r)', type_name(json),
                   json)
    return json
예제 #4
0
파일: static.py 프로젝트: buhanec/pygot
    def __new__(mcs,
                name: str,
                bases: Tuple[Type[Any]] = tuple(),
                class_dict: Optional[Dict[str, Any]] = None,
                **kwargs) -> StaticData:
        """
        Create a new partially fixed static data container.

        :param name: Container name
        :param bases: Container bases
        :param class_dict: Container class dict
        :param kwargs: Kwargs for container class, if dynamically generated
        """
        if class_dict is None:
            class_dict = kwargs
            logger.info('Programmatically creating %r as instance of %s...',
                        name, utils.type_name(mcs))
        else:
            class_dict.update(kwargs)
            logger.info('Creating %r as instance of %s...',
                        name, utils.type_name(mcs))
        # Check for existing name
        if name in mcs:
            existing = getattr(mcs, name)
            existing_class_dict = {k: getattr(existing, k)
                                   for k in existing.__static_fields__}

            # This check will fail if we have any dunders available,
            # making this only work for programatically defined classes.
            if class_dict == existing_class_dict:
                return existing
            raise StaticInstanceConflict(name, existing)

        # Sort out attributes
        static_attrs = {}
        dynamic_attrs = {}
        for attr, hint in mcs.__annotations__.items():
            if attr.startswith('_'):
                continue
            if getattr(mcs, attr, None) is STATIC:
                static_attrs[attr] = hint
            else:
                dynamic_attrs[attr] = hint

        # Check for missing and unexpected attrs
        missing = static_attrs.keys() - class_dict.keys()
        if missing:
            raise ExpectedStaticAttr(name, missing)
        unexpected = class_dict.keys() & dynamic_attrs
        if unexpected:
            raise UnexpectedStaticAttr(name, unexpected)

        # Extend static attrs with additional class_dict values
        full_static = static_attrs.keys() | {k for k in class_dict.keys()
                                             if k not in dynamic_attrs
                                             and not k.startswith('_')}

        # Instantiate object
        new = super().__new__(mcs, name, bases, class_dict)

        # Create setattr to block frozen attrs from being overridden
        @functools.wraps(new.__setattr__)
        def _setattr(self_: StaticData, name_: str, value_: Any) -> None:
            if name_ in self_.__static_fields__:
                raise AttributeError(f'Frozen attribute: {name_!r}')
            # pylint: disable=bad-super-call
            super(new, self_).__setattr__(name_, value_)

        # Create half-frozen dataclass
        new.__annotations__ = dynamic_attrs
        new.__static_fields__ = full_static
        new.__setattr__ = _setattr
        new = dataclasses.dataclass(new)

        # Register instance
        mcs.register(new)
        logger.info('%s registered with %s',
                    utils.type_name(new), utils.type_name(mcs))

        return new
예제 #5
0
파일: test_utils.py 프로젝트: buhanec/pygot
def test_type_name_qualname(obj: Any, expected: str) -> None:
    assert utils.type_name(obj, local_name=True) == expected
예제 #6
0
파일: test_utils.py 프로젝트: buhanec/pygot
def test_type_name(obj: Any, expected: str) -> None:
    assert utils.type_name(obj) == expected