Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
fix issues in #7 with metaclasses (#8)
Browse files Browse the repository at this point in the history
* fix issues in #7 with metaclasses

* remove unused code
  • Loading branch information
PythonFZ authored Dec 7, 2022
1 parent 850e56e commit a64e6c8
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 44 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "zninit"
version = "0.1.4"
version = "0.1.5"
description = "Descriptor based dataclass implementation"
authors = ["zincwarecode <[email protected]>"]
license = "Apache-2.0"
Expand Down
23 changes: 23 additions & 0 deletions tests/test_i_zninit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""General 'ZnInit' integration tests."""
import zninit


class GetItemMeta(type):
"""Metaclass for general testing."""

def __getitem__(cls, item):
"""General purpose metaclass."""
return item


class ExampleCls(zninit.ZnInit, metaclass=GetItemMeta):
"""Example 'ZnInit' with metaclass."""

parameter = zninit.Descriptor()


def test_ExampleCls():
"""Test 'ZnInit' with a metaclass."""
example = ExampleCls(parameter=25)
assert example.parameter == 25
assert ExampleCls[42] == 42
2 changes: 1 addition & 1 deletion tests/test_zninit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

def test_version():
"""Test the installed version."""
assert zninit.__version__ == "0.1.4"
assert zninit.__version__ == "0.1.5"
63 changes: 23 additions & 40 deletions zninit/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,31 +114,7 @@ def auto_init(self, *args, **kwargs):
return auto_init


def update_attribute_names(cls):
"""Update changed attribute names.
E.g. 'init_descriptors' was renamed to '_init_descriptors_' but should be
backwards compatible. This was done according to PEP8 style guide where
'_single_leading_underscore' are meant for weak internal usage.
"""
if cls.init_descriptors is not None:
cls._init_descriptors_ = cls.init_descriptors # pylint: disable=W0212
if cls.use_repr is not None:
cls._use_repr_ = cls.use_repr # pylint: disable=W0212
if cls.init_subclass_basecls is not None:
cls._init_subclass_basecls_ = cls.init_subclass_basecls # pylint: disable=W0212


class Meta(type):
"""Metaclass to 'update_attribute_names'."""

def __new__(cls, *args, **kwargs):
meta_cls = super().__new__(cls, *args, **kwargs)
update_attribute_names(meta_cls)
return meta_cls


class ZnInit(metaclass=Meta):
class ZnInit:
"""Parent class for automatic __init__ generation based on descriptors.
Attributes
Expand All @@ -155,14 +131,22 @@ class ZnInit(metaclass=Meta):
__init__ of the basecls will be called via super.
"""

_init_descriptors_: typing.List[Descriptor] = [Descriptor]
_use_repr_: bool = True
_init_subclass_basecls_ = None

init_descriptors: typing.List[Descriptor] = None
use_repr: bool = None
init_descriptors: typing.List[Descriptor] = [Descriptor]
use_repr: bool = True
init_subclass_basecls = None

@property
def _init_descriptors_(self) -> typing.List[Descriptor]:
return self.init_descriptors

@property
def _use_repr_(self) -> bool:
return self.use_repr

@property
def _init_subclass_basecls_(self) -> typing.Type:
return self.init_subclass_basecls

def __init__(self):
"""Required for Error messages.
Expand All @@ -175,30 +159,29 @@ def __init__(self):

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
update_attribute_names(cls)
if cls._init_subclass_basecls_ is None:
cls._init_subclass_basecls_ = ZnInit
_init_subclass_basecls_ = object.__new__(cls)._init_subclass_basecls_

if _init_subclass_basecls_ is None:
_init_subclass_basecls_ = ZnInit
for inherited in cls.__mro__:
# Go through the mro until you find the init_subclass_basecls.
# If found an init before that class it will implement super
# if not add the fields to the __init__ automatically.
if inherited == cls._init_subclass_basecls_:
if inherited == _init_subclass_basecls_:
break

if inherited.__dict__.get("__init__") is not None:
if not getattr(inherited.__init__, "uses_auto_init", False):
return cls

log.debug(
f"Found {cls._init_subclass_basecls_} instance - adding dataclass-like"
" __init__"
f"Found {_init_subclass_basecls_} instance - adding dataclass-like __init__"
)
return cls._update_init(super_init=cls._init_subclass_basecls_.__init__)
return cls._update_init(super_init=_init_subclass_basecls_.__init__)

@classmethod
def _get_descriptors(cls):
update_attribute_names(cls)
return get_descriptors(descriptor=cls._init_descriptors_, cls=cls)
return get_descriptors(descriptor=object.__new__(cls)._init_descriptors_, cls=cls)

@classmethod
def _update_init(cls, super_init):
Expand Down
2 changes: 0 additions & 2 deletions zninit/descriptor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@ def get_descriptors(
a list of the found descriptor objects
"""
if descriptor is None:
return []
if self is None and cls is None:
raise ValueError("Either self or cls must not be None")
if self is not None and cls is not None:
Expand Down

0 comments on commit a64e6c8

Please sign in to comment.