testing/py3-dataclasses-serialization: new aport

https://github.com/madman-bob/python-dataclasses-serialization
Serialize/deserialize Python dataclasses
This commit is contained in:
psykose 2023-07-27 13:54:14 +00:00
parent 899917d331
commit 211584ea2d
17 changed files with 731 additions and 0 deletions

View File

@ -0,0 +1,61 @@
# Maintainer: psykose <alice@ayaya.dev>
pkgname=py3-dataclasses-serialization
pkgver=1.3.1
pkgrel=0
pkgdesc="Serialize/deserialize Python dataclasses"
url="https://github.com/madman-bob/python-dataclasses-serialization"
arch="noarch"
license="MIT"
depends="
py3-more-properties
py3-toolz
py3-toposort
py3-typing_inspect
"
makedepends="
py3-gpep517
py3-setuptools
py3-wheel
"
checkdepends="
py3-pytest-forked
py3-pytest-xdist
"
subpackages="$pkgname-pyc"
source="$pkgname-$pkgver.tar.gz::https://github.com/madman-bob/python-dataclasses-serialization/archive/refs/tags/$pkgver.tar.gz
$pkgname-fix-deserialize.patch::https://github.com/madman-bob/python-dataclasses-serialization/commit/00fbd280034abeff277523a6579f14d10e8427a2.diff
root.patch
"
builddir="$srcdir/python-dataclasses-serialization-$pkgver/"
prepare() {
default_prepare
# fixup location
mv pypi_upload/setup.py .
}
build() {
gpep517 build-wheel \
--wheel-dir .dist \
--output-fd 3 3>&1 >&2
}
check() {
python3 -m venv --clear --without-pip --system-site-packages .testenv
.testenv/bin/python3 -m installer .dist/*.whl
# https://github.com/madman-bob/python-dataclasses-serialization/issues/16
.testenv/bin/python3 -m pytest -n auto --forked \
-k 'not test_json_serialization_types'
}
package() {
python3 -m installer -d "$pkgdir" \
.dist/*.whl
}
sha512sums="
5dc3569675749c82aaf1d9a8e2507fa9a2ee2bb14d6cb082fde15a108e4651afbdb0b1d6c8ca60dca839206abfca2c10b0fc9efe4867e521c1522a7a68b8a7c1 py3-dataclasses-serialization-1.3.1.tar.gz
4febeff6f08f12267fdceb1243449099c16002c3731cdde80a841a3d63fcb27333ae871fd8d05f45ee8881eccc77b69e8bc6b737ccb49d3c55cb008b49e32762 py3-dataclasses-serialization-fix-deserialize.patch
78c1714b7373586fcf67adc5dd3ec39b42f9f4cd03cad705d568cbc45de10f506e04a76d112b5cc6ea9b3e0e110f32dd4b9163a3eddeecc1fc94237887b4ed45 root.patch
"

View File

@ -0,0 +1,4 @@
Metadata-Version: 2.1
Name: UNKNOWN
Version: 0.0.0

View File

@ -0,0 +1,4 @@
UNKNOWN-0.0.0.dist-info/METADATA,sha256=-waPzueBeQZEhNDwrQ6SrCv-VlDxcEe0StZKrgA5-uA,52
UNKNOWN-0.0.0.dist-info/WHEEL,sha256=AtBG6SXL3KF_v0NxLf0ehyVOh0cold-JbJYXNGorC6Q,92
UNKNOWN-0.0.0.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
UNKNOWN-0.0.0.dist-info/RECORD,,

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.41.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,285 @@
Metadata-Version: 2.1
Name: more-properties
Version: 1.1.1
Summary: A collection of property variants
Home-page: https://github.com/madman-bob/python-more-properties
Author: Robert Wright
Author-email: madman.bob@hotmail.co.uk
License: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: dataclasses
# `more_properties`
A collection of `property` variants.
## Basic Usage
Variants behave mostly as the built-in `property`, except where noted.
Given the following class,
```python
from more_properties import property, class_property, static_property
class Parrot:
@property
def name(self):
return "Fred"
@class_property
def order(cls):
return Psittaciformes
@static_property
def planet():
return Earth
```
the properties may be accessed like so:
```pycon
>>> Parrot().name
'Fred'
>>> Parrot.order
<class 'Psittaciformes'>
>>> Parrot.planet
<class 'Earth'>
```
## Setters/Deleters
Setters and deleters are defined in the same way as the built-in `property`.
Either with the decorator method
```python
from more_properties import class_property
class Foo:
name = "Foo"
@class_property
def identifier(cls):
"""Object identifier"""
return cls.name.lower()
@identifier.setter
def identifier(cls, value):
cls.name = value.title()
@identifier.deleter
def identifier(cls):
cls.name = None
```
or the inline method
```python
from more_properties import class_property
class Foo:
name = "Foo"
@classmethod
def get_identifier(cls):
return cls.name.lower()
@classmethod
def set_identifier(cls, value):
cls.name = value.title()
@classmethod
def del_identifier(cls):
cls.name = None
identifier = class_property(
get_identifier,
set_identifier,
del_identifier,
"Object identifier"
)
```
## Reference
### `property`
A modified version of the built-in [`property`](https://docs.python.org/3/library/functions.html#property).
Always behaves as a
[data descriptor](https://docs.python.org/3/howto/descriptor.html#descriptor-protocol),
regardless of which (if any) of getter, setter, and deleter are set.
Behaviour when accessed on a class, is undefined.
### `class_property`
A `property` for classes.
Both `cls.x` and `instance.x` call the getter with the class.
Setting `instance.x` calls the setter with the class and value.
Deleting `instance.x` call the deleter with the class only.
```python
from more_properties import class_property
class Foo:
@class_property
def identifier(cls):
"""Class identifier"""
return cls.__name__.lower()
class Bar(Foo):
pass
```
```pycon
>>> Foo.identifier
'foo'
>>> Foo().identifier
'foo'
```
```pycon
>>> Bar.identifier
'bar'
>>> Bar().identifier
'bar'
```
`classproperty` provided as a synonym, for consistency with `classmethod`.
<aside class="warning">
<p>
Due to the
<a href="https://docs.python.org/3/reference/datamodel.html#object.__set__">Python data model</a>,
using the setters/deleters on <em>classes</em> may not work as intended.
</p>
<p>
Getters always work as intended, and using setters/deleters on <em>instances</em> work as intended.
</p>
</aside>
### `static_property`
A `property` independent of its accessor.
Both `cls.x` and `instance.x` call the getter with no parameters.
Setting `instance.x` calls the setter with the value only.
Deleting `instance.x` call the deleter with no parameters.
```python
from more_properties import static_property
x = "bar"
class Foo:
@static_property
def val():
return x
```
```pycon
>>> Foo.val
'bar'
>>> Foo().val
'bar'
```
`staticproperty` provided as a synonym, for consistency with `staticmethod`.
<aside class="warning">
<p>
Due to the
<a href="https://docs.python.org/3/reference/datamodel.html#object.__set__">Python data model</a>,
using the setters/deleters on <em>classes</em> may not work as intended.
</p>
<p>
Getters always work as intended, and using setters/deleters on <em>instances</em> work as intended.
</p>
</aside>
### `cached_property`
### `cached_class_property`
### `cached_static_property`
Variants of `property`, `class_property`, and `static_property`, respectively.
They are each used in the same way as the originals,
but cache the value of the getters.
```python
from dataclasses import dataclass
from more_properties import cached_property
@dataclass
class Foo:
x: int
@cached_property
def y(self):
print("Doing work")
return self.x + 1
```
```pycon
>>> bar = Foo(1)
>>> bar.y
Doing work
2
>>> bar.y
2
```
If the setters/deleters are defined,
then the cache is cleared before they are called.
Further, the cache may be explicitly cleared through the `clear_cache` method,
exposed only during class creation.
```python
@dataclass
class Foo:
x: int
@cached_property
def y(self):
print("Doing work")
return self.x + 1
y_clear_cache = y.clear_cache
```
```pycon
>>> bar = Foo(1)
>>> bar.y
Doing work
2
>>> bar.y
2
>>> bar.y_clear_cache()
>>> bar.y
Doing work
2
```
## Installation
Install and update using the standard Python package manager [pip](https://pip.pypa.io/en/stable/):
```bash
pip install more_properties
```

View File

@ -0,0 +1,11 @@
more_properties/__init__.py,sha256=u1TijuEfL1JuqNhP5_XJkdQ941liKPYs52rC3ztWvr8,604
more_properties/cached_property.py,sha256=k8QHAjCOgSsYkb1pId2agSG7Mm3qu82R4iS9YIewt9o,3376
more_properties/class_property.py,sha256=pSUkG9zQ6X7y8UKeJPFkpOvHi2831kdj8kGEGnKzxfE,964
more_properties/property.py,sha256=KtGr88bxxLKneHp-axVOi5BiyEp3dzV3FfCYJanKFSc,1744
more_properties/types.py,sha256=4EDzgwr4X3iymUKIzqyEuyoNX160_RZDEfBZ5fD3vHU,656
more_properties/util_properties.py,sha256=93050PQimdHN9OT1of0yPkC5OOPMHhlE40u-5Iaw6L8,1385
more_properties-1.1.1.dist-info/LICENSE,sha256=g-TdIUKakft86mekdgMqlkFCXlNV3y4PWJpzi27J_Sw,1057
more_properties-1.1.1.dist-info/METADATA,sha256=b1BMTg6DT5urMGhBmdzjUjd8fwC_rd5jkh1yPbIZX5o,5694
more_properties-1.1.1.dist-info/WHEEL,sha256=AtBG6SXL3KF_v0NxLf0ehyVOh0cold-JbJYXNGorC6Q,92
more_properties-1.1.1.dist-info/top_level.txt,sha256=YFJwQUwzeEb2V96cOjyK60NaqnYdMV0LPL-CGwUfGKc,16
more_properties-1.1.1.dist-info/RECORD,,

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.41.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -0,0 +1 @@
more_properties

View File

@ -0,0 +1,24 @@
from more_properties.cached_property import (
cached_class_property,
cached_property,
cached_static_property,
)
from more_properties.class_property import class_property, static_property
from more_properties.property import property
__all__ = [
"property",
"class_property",
"classproperty",
"static_property",
"staticproperty",
"cached_property",
"cached_class_property",
"cached_static_property",
]
__version__ = "1.1.1"
# Providing aliases for consistency with classmethod and staticmethod
classproperty = class_property
staticproperty = static_property

View File

@ -0,0 +1,119 @@
from dataclasses import dataclass
from typing import Optional, Type, TypeVar, Union
from more_properties.class_property import ClassProperty, StaticProperty
from more_properties.types import Deleter
from more_properties.util_properties import NamedProperty
__all__ = [
"cached_property",
"cached_class_property",
"cached_static_property",
]
OT = TypeVar("OT", contravariant=True) # Owner Type
VT = TypeVar("VT") # Value Type
class Uncached:
pass
Cache = Union[VT, Uncached]
@dataclass
class CachedProperty(NamedProperty[OT, VT]):
@property
def cache_name(self) -> str:
if self.name is None:
raise AttributeError(f"Property {self!r} not assigned to class")
return f"__{self.name}_cache"
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT:
cache_name = self.cache_name
if cache_name not in instance.__dict__:
instance.__dict__[cache_name] = super().__get__(instance, owner)
value: VT = instance.__dict__[cache_name]
return value
def __set__(self, instance: OT, value: VT) -> None:
# Mypy seems to unwrap the descriptor recursively, while Python only does it once
clear_cache: Deleter[OT] = self.clear_cache # type: ignore
clear_cache.__get__(instance, type(instance))()
super().__set__(instance, value)
def __delete__(self, instance: OT) -> None:
# Mypy seems to unwrap the descriptor recursively, while Python only does it once
clear_cache: Deleter[OT] = self.clear_cache # type: ignore
clear_cache.__get__(instance, type(instance))()
super().__delete__(instance)
@property
def clear_cache(self) -> Deleter[OT]:
def clear_cache(instance: OT) -> None:
cache_name = self.cache_name
if cache_name in instance.__dict__:
delattr(instance, cache_name)
# Mypy doesn't recognize functions as Getable
return clear_cache # type: ignore
@dataclass
class CachedClassProperty(CachedProperty[OT, VT], ClassProperty[OT, VT]):
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT:
cache_name = self.cache_name
if cache_name in owner.__dict__:
value: VT = owner.__dict__[cache_name]
return value
value = super(CachedProperty, self).__get__(instance, owner)
setattr(owner, cache_name, value)
return value
@property
def clear_cache(self) -> Deleter[OT]:
def clear_cache(owner: Type[OT]) -> None:
cache_name = self.cache_name
if cache_name in owner.__dict__:
delattr(owner, cache_name)
return classmethod(clear_cache)
@dataclass
class CachedStaticProperty(CachedProperty[OT, VT], StaticProperty[OT, VT]):
value: Cache[VT] = Uncached()
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT:
if isinstance(self.value, Uncached):
self.value = super(CachedProperty, self).__get__(instance, owner)
return self.value
@property
def clear_cache(self) -> Deleter[OT]:
def clear_cache() -> None:
self.value = Uncached()
return staticmethod(clear_cache)
cached_property = CachedProperty
cached_class_property = CachedClassProperty
cached_static_property = CachedStaticProperty

View File

@ -0,0 +1,38 @@
from typing import TypeVar
from more_properties.util_properties import WrappedProperty
__all__ = [
"ClassProperty",
"class_property",
"StaticProperty",
"static_property",
]
OT = TypeVar("OT", contravariant=True) # Owner Type
VT = TypeVar("VT") # Value Type
class ClassProperty(WrappedProperty[OT, VT]):
wrapper = classmethod
def __post_init__(self) -> None:
fget = self.__dict__["fget"]
if isinstance(fget, self.wrapper):
fget.__doc__ = fget.__func__.__doc__
super().__post_init__()
class StaticProperty(ClassProperty[OT, VT]):
wrapper = staticmethod
# For some reason, Python 3.6 treats classmethod and staticmethod as abstract methods,
# disallowing instantiation of ClassProperty and StaticProperty
setattr(ClassProperty, "__abstractmethods__", frozenset())
setattr(StaticProperty, "__abstractmethods__", frozenset())
class_property = ClassProperty
static_property = StaticProperty

View File

@ -0,0 +1,63 @@
from dataclasses import dataclass, replace
from typing import Generic, Optional, Type, TypeVar
from more_properties.types import Deleter, Getter, Setter
__all__ = [
"Property",
"property",
]
OT = TypeVar("OT", contravariant=True) # Owner Type
VT = TypeVar("VT") # Value Type
@dataclass
class Property(Generic[OT, VT]):
fget: Optional[Getter[OT, VT]] = None
fset: Optional[Setter[OT, VT]] = None
fdel: Optional[Deleter[OT]] = None
doc: Optional[str] = None
def __post_init__(self) -> None:
self.__doc__ = (
self.doc
if self.doc is not None
else getattr(self.__dict__.get("fget"), "__doc__", None)
)
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT:
fget: Optional[Getter[OT, VT]] = self.__dict__["fget"]
if fget is None:
raise AttributeError("unreadable attribute")
return fget.__get__(instance, owner)()
def __set__(self, instance: OT, value: VT) -> None:
fset = self.__dict__["fset"]
if fset is None:
raise AttributeError("can't set attribute")
fset.__get__(instance, type(instance))(value)
def __delete__(self, instance: OT) -> None:
fdel = self.__dict__["fdel"]
if fdel is None:
raise AttributeError("can't delete attribute")
fdel.__get__(instance, type(instance))()
def getter(self, func: Getter[OT, VT]) -> "Property[OT, VT]":
return replace(self, fget=func)
def setter(self, func: Setter[OT, VT]) -> "Property[OT, VT]":
return replace(self, fset=func)
def deleter(self, func: Deleter[OT]) -> "Property[OT, VT]":
return replace(self, fdel=func)
property = Property

View File

@ -0,0 +1,22 @@
from typing import Callable, Generic, Optional, Type, TypeVar
__all__ = ["Getable", "Getter", "Setter", "Deleter"]
OT = TypeVar("OT", contravariant=True) # Owner Type
VT_co = TypeVar("VT_co", covariant=True) # Covariant Value Type
VT_contra = TypeVar("VT_contra", contravariant=True) # Contravariant Value Type
try:
from typing import Protocol
except ImportError:
Protocol = Generic
class Getable(Protocol[OT, VT_co]):
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT_co:
...
Getter = Getable[OT, Callable[[], VT_co]]
Setter = Getable[OT, Callable[[VT_contra], None]]
Deleter = Getable[OT, Callable[[], None]]

View File

@ -0,0 +1,52 @@
from dataclasses import dataclass
from typing import Generic, Optional, Type, TypeVar
from more_properties.property import Property
from more_properties.types import Getable
__all__ = [
"NamedProperty",
"WrappedProperty",
]
OT = TypeVar("OT", contravariant=True) # Owner Type
VT = TypeVar("VT") # Value Type
@dataclass
class NamedProperty(Property[OT, VT]):
name: Optional[str] = None
def __set_name__(self, owner: Type[OT], name: str) -> None:
self.name = name
@dataclass
class TrivialWrapper(Generic[OT, VT]):
getable: Getable[OT, VT]
def __get__(self, instance: Optional[OT], owner: Type[OT]) -> VT:
getable: Getable[OT, VT] = self.__dict__["getable"]
return getable.__get__(instance, owner)
class WrappedProperty(Property[OT, VT]):
wrapper: type = TrivialWrapper
def __post_init__(self) -> None:
fget = self.__dict__["fget"]
if fget is not None and not isinstance(fget, self.wrapper):
setattr(self, "fget", self.wrapper(fget))
fset = self.__dict__["fset"]
if fset is not None and not isinstance(fset, self.wrapper):
setattr(self, "fset", self.wrapper(fset))
fdel = self.__dict__["fdel"]
if fdel is not None and not isinstance(fdel, self.wrapper):
setattr(self, "fdel", self.wrapper(fdel))
super().__post_init__()

View File

@ -0,0 +1,15 @@
diff --git a/pypi_upload/setup.py b/pypi_upload/setup.py
index cd56011..427bcc9 100644
--- a/pypi_upload/setup.py
+++ b/pypi_upload/setup.py
@@ -6,8 +6,8 @@ from pathlib import Path
from setuptools import find_packages, setup
from setuptools.command.install import install
-project_root = Path(__file__).parents[1]
-
+import pathlib
+project_root = pathlib.Path().resolve()
class VerifyCommand(install):
"""Custom command to verify module integrity"""