ypres 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ tests:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
16
+
17
+ steps:
18
+ - name: Check out repository
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Set up Python
22
+ uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+
26
+ - name: Set up uv
27
+ uses: astral-sh/setup-uv@v4
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --group dev
31
+
32
+ - name: Run tests
33
+ run: uv run python -m unittest tests.test_serializer tests.test_async_serializer tests.test_fields
34
+
35
+ build:
36
+ runs-on: ubuntu-latest
37
+ needs: tests
38
+
39
+ steps:
40
+ - name: Check out repository
41
+ uses: actions/checkout@v4
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v5
45
+ with:
46
+ python-version: "3.14"
47
+
48
+ - name: Set up uv
49
+ uses: astral-sh/setup-uv@v4
50
+
51
+ - name: Build distributions
52
+ run: uv build
53
+
54
+ - name: Check distributions
55
+ run: uvx twine check dist/*
56
+
57
+ - name: Upload distributions
58
+ uses: actions/upload-artifact@v4
59
+ with:
60
+ name: python-package-distributions
61
+ path: dist/
@@ -0,0 +1,82 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ tests:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
18
+
19
+ steps:
20
+ - name: Check out repository
21
+ uses: actions/checkout@v4
22
+
23
+ - name: Set up Python
24
+ uses: actions/setup-python@v5
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Set up uv
29
+ uses: astral-sh/setup-uv@v4
30
+
31
+ - name: Install dependencies
32
+ run: uv sync --group dev
33
+
34
+ - name: Run tests
35
+ run: uv run python -m unittest tests.test_serializer tests.test_async_serializer tests.test_fields
36
+
37
+ build:
38
+ runs-on: ubuntu-latest
39
+ needs: tests
40
+
41
+ steps:
42
+ - name: Check out repository
43
+ uses: actions/checkout@v4
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: "3.14"
49
+
50
+ - name: Set up uv
51
+ uses: astral-sh/setup-uv@v4
52
+
53
+ - name: Build distributions
54
+ run: uv build
55
+
56
+ - name: Check distributions
57
+ run: uvx twine check dist/*
58
+
59
+ - name: Upload distributions
60
+ uses: actions/upload-artifact@v4
61
+ with:
62
+ name: python-package-distributions
63
+ path: dist/
64
+
65
+ publish:
66
+ runs-on: ubuntu-latest
67
+ needs: build
68
+ permissions:
69
+ id-token: write
70
+
71
+ environment:
72
+ name: pypi
73
+
74
+ steps:
75
+ - name: Download distributions
76
+ uses: actions/download-artifact@v4
77
+ with:
78
+ name: python-package-distributions
79
+ path: dist/
80
+
81
+ - name: Publish to PyPI
82
+ uses: pypa/gh-action-pypi-publish@release/v1
ypres-1.1.2/.gitignore ADDED
@@ -0,0 +1,49 @@
1
+ # Backup files
2
+ *.~
3
+
4
+ # Byte-compiled / optimized / DLL files
5
+ __pycache__/
6
+ *.py[cod]
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ bin/
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ eggs/
17
+ .eggs/
18
+ lib/
19
+ lib64/
20
+ parts/
21
+ sdist/
22
+ var/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # Installer logs
29
+ pip-log.txt
30
+ pip-delete-this-directory.txt
31
+
32
+ # Unit test / coverage reports
33
+ .tox/
34
+ .coverage
35
+ .cache
36
+ nosetests.xml
37
+ coverage.xml
38
+
39
+ # Translations
40
+ *.mo
41
+
42
+ # Sphinx documentation
43
+ docs/_build/
44
+
45
+ # Vim swap files
46
+ *.swp
47
+ .idea
48
+
49
+ .DS_Store
@@ -0,0 +1 @@
1
+ 3.13
ypres-1.1.2/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Clark DuVall
4
+ Copyright (c) 2023 Andrew Hankinson, RISM Digital Center
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ include LICENSE
ypres-1.1.2/PKG-INFO ADDED
@@ -0,0 +1,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: ypres
3
+ Version: 1.1.2
4
+ Summary: ypres is a simple object serialization framework built for speed.
5
+ Project-URL: Homepage, https://github.com/rism-digital/ypres
6
+ Project-URL: Source, https://github.com/rism-digital/ypres
7
+ Project-URL: Issues, https://github.com/rism-digital/ypres/issues
8
+ Author-email: Andrew Hankinson <andrew.hankinson@gmail.com>
9
+ License: The MIT License (MIT)
10
+
11
+ Copyright (c) 2015 Clark DuVall
12
+ Copyright (c) 2023 Andrew Hankinson, RISM Digital Center
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: asyncio,json,serialization,serializer,typing
33
+ Classifier: Development Status :: 5 - Production/Stable
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Programming Language :: Python :: 3.14
42
+ Classifier: Programming Language :: Python :: Implementation :: CPython
43
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
44
+ Classifier: Typing :: Typed
45
+ Requires-Python: <4.0,>=3.11
46
+ Description-Content-Type: text/markdown
47
+
48
+ # ypres: ridiculously fast object serialization
49
+
50
+ This project started as a fork of the amazing [Serpy serializer](https://github.com/clarkduvall/serpy), which has been
51
+ [marked as feature-complete](https://github.com/clarkduvall/serpy/issues/69) by the original author. This fork
52
+ adds some newer features, such as `asyncio` support so that asynchronous
53
+ methods may be called from within a serializer.
54
+
55
+ It was renamed to "ypres" ("serpy" backwards, pronounced like the [Belgian town
56
+ name](https://en.wikipedia.org/wiki/Ypres)) to avoid confusion with the original.
57
+
58
+ Since forking it has undergone numerous changes and rewrites. The core of it
59
+ is still somewhat recognizable, but there have also been many changes.
60
+
61
+ **ypres** is a simple object serialization framework built for
62
+ speed. **ypres** serializes complex datatypes (Django Models, custom
63
+ classes, ...) to simple native types (dicts, lists, strings, ...). The
64
+ native types can easily be converted to JSON or any other format needed.
65
+
66
+ The goal of **ypres** is to be able to do this *simply*, *reliably*, and
67
+ *quickly*. Since serializers are class based, they can be combined,
68
+ extended and customized with very little code duplication.
69
+
70
+ ## Changes from Serpy
71
+
72
+ There are some notable changes from the original Serpy serializer in this fork.
73
+
74
+ ### New Serializer classes: AsyncSerializer and AsyncDictSerializer
75
+
76
+ Serpy did not allow for `MethodField` implementations to use async / await methods.
77
+ For those instances where you wish to embed an async / await coroutine in your serializer,
78
+ two new serializer classes, `AsyncSerializer` and `AsyncDictSerializer`, will automatically
79
+ detect whether the method being called is a coroutine and handle it appropriately.
80
+
81
+ ### New StaticField class
82
+
83
+ When combining many fields and manipulating output, it is sometimes desirable to have
84
+ a fixed value for certain fields in the output. The new `StaticField` class allows
85
+ you to specify a fixed value for the field, and this will always appear in the output.
86
+
87
+ ### Serializers allow a context object
88
+
89
+ Additional context can be passed in to a serializer. This is helpful if you have some context
90
+ that you wish to use when serializing the object. For example, you might pass in a user object
91
+ that could customize the responses in the serializer with their name, or only perform certain
92
+ serialization tasks if they are of a specific class (e.g., admin).
93
+
94
+ ### Date and DateTime Serializer Fields
95
+
96
+ Date and DateTime fields can be serialized, based on the implementation from another fork,
97
+ https://github.com/PKharlamov/drf-serpy/blob/master/drf_serpy/fields.py.
98
+
99
+ ### Deprecated the `.data` property.
100
+
101
+ Since `.data` can return either a `list` (with `many=True`) or a `dict`, type checkers
102
+ complained when you serialized a single object because the calling code does not handle
103
+ the case of `data` being a list.
104
+
105
+ Instead, two new properties, `serialized` and `serialized_many` are introduced that
106
+ return a dict and a list directly. In the course of doing this work the class structure
107
+ for the serializers was reworked to better implement common checks and data in the superclass.
108
+
109
+ ```python
110
+ import ypres
111
+
112
+
113
+ class MySerializer(ypres.Serializer):
114
+ foo = ypres.MethodField()
115
+ blah = ypres.MethodField()
116
+
117
+ def get_foo(self, obj):
118
+ foo_data = obj.foo
119
+ ctx_data = self.context.get("additional", "")
120
+ return f"{foo_data}_{ctx_data}"
121
+
122
+ def get_blah(self, obj):
123
+ blah_data = obj.blah
124
+ ctx_data = self.context.get("additional", "")
125
+ return f"{blah_data}_{ctx_data}"
126
+
127
+
128
+ class Foo:
129
+ foo = "foo"
130
+ blah = "blah"
131
+
132
+ my_data = MySerializer(Foo(), context={"additional": "bar"}).serialized
133
+
134
+ # {"foo": "foo_bar", "blah": "blah_bar"}
135
+ ```
136
+
137
+ ### Changed behaviour of None
138
+
139
+ By default, data that evaluates to a value of `None` will **not** be included
140
+ in the output. To explicitly mark that a field should emit a `None` value,
141
+ it should be instantiated with an `emit_none=True` argument.
142
+
143
+ Note that the combination of `emit_none` and `required` deserve special attention.
144
+
145
+ - If `emit_none` is `False` and `required` is `True` (default), then the object
146
+ being serialized must have the matching attribute available, otherwise it will
147
+ raise an error. The only exception is if the field is a `MethodField`, in which
148
+ case the attribute does not need to be present on the object.
149
+ This behaviour is not changed.
150
+ - If `emit_none` is `False` and `required` is `False` then the object being
151
+ serialized will not appear in the output if its value is `None`
152
+ - If `emit_none` is `True` and `required` is `True`, then the object being
153
+ serialized will attempt to return the value. However, it may fail if the `to_value`
154
+ method being used does not accept `None`. An example of this is the `IntField`
155
+ serializer, where the `to_value` method would effectively be calling `int(None)`.
156
+ In this case, a `TypeError` will be raised. (This is the same as trying to serialize
157
+ a string with an `IntField`, for example)
158
+ - If `emit_none` is `True` and `required` is `False`, then the object being
159
+ serialized will actually skip the `to_value` step and simply return `None`.
160
+
161
+ Further to this, the behaviour of the `StrField` and `BoolField` were changed,
162
+ where calling `StrField` on a value of `None` would actually return the string
163
+ `"None"`. Similarly, calling `bool(None)` evaluates to `False`. In both of these
164
+ cases the `to_value` handler has been modified to return `None` if the incoming
165
+ value is `None`.
166
+
167
+ This prevents unexpected type values from appearing in the
168
+ output. For values that cannot be cast to `None` for `IntField` and `FloatField`,
169
+ a `None` input will raise an exception.
170
+
171
+ ### Modern Python standards
172
+
173
+ The project uses update Python packaging setups with `pyproject.toml`. It also
174
+ adds configurations for `ruff`, works with `uv`, and fully supports type annotations.
175
+
176
+ ## Source
177
+
178
+ Source at: <https://github.com/rism-digital/ypres>
179
+
180
+ If you want a feature, send a pull request!
181
+
182
+ ## Installation
183
+
184
+ ``` bash
185
+ $ pip install git+https://github.com/rism-digital/ypres
186
+ ```
187
+
188
+ ## Examples
189
+
190
+ ### Simple Example
191
+
192
+ ```python
193
+ import ypres
194
+
195
+ class Foo(object):
196
+ """The object to be serialized."""
197
+ y = 'hello'
198
+ z = 9.5
199
+
200
+ def __init__(self, x):
201
+ self.x = x
202
+
203
+
204
+ class FooSerializer(ypres.Serializer):
205
+ """The serializer schema definition."""
206
+ # Use a Field subclass like IntField if you need more validation.
207
+ x = ypres.IntField()
208
+ y = ypres.Field()
209
+ z = ypres.Field()
210
+
211
+ f = Foo(1)
212
+ FooSerializer(f).serialized
213
+ # {'x': 1, 'y': 'hello', 'z': 9.5}
214
+
215
+ fs = [Foo(i) for i in range(100)]
216
+ FooSerializer(fs, many=True).serialized_many
217
+ # [{'x': 0, 'y': 'hello', 'z': 9.5}, {'x': 1, 'y': 'hello', 'z': 9.5}, ...]
218
+ ```
219
+
220
+ ### Nested Example
221
+
222
+ ```python
223
+ import ypres
224
+
225
+ class Nestee(object):
226
+ """An object nested inside another object."""
227
+ n = 'hi'
228
+
229
+
230
+ class Foo(object):
231
+ x = 1
232
+ nested = Nestee()
233
+
234
+
235
+ class NesteeSerializer(ypres.Serializer):
236
+ n = ypres.Field()
237
+
238
+
239
+ class FooSerializer(ypres.Serializer):
240
+ x = ypres.Field()
241
+ # Use another serializer as a field.
242
+ nested = NesteeSerializer()
243
+
244
+ f = Foo()
245
+ FooSerializer(f).serialized
246
+ # {'x': 1, 'nested': {'n': 'hi'}}
247
+ ```
248
+
249
+ ### Complex Example
250
+
251
+ ```python
252
+ import ypres
253
+
254
+ class Foo(object):
255
+ y = 1
256
+ z = 2
257
+ super_long_thing = 10
258
+
259
+ def x(self):
260
+ return 5
261
+
262
+
263
+ class FooSerializer(ypres.Serializer):
264
+ w = ypres.Field(attr='super_long_thing')
265
+ x = ypres.Field(call=True)
266
+ plus = ypres.MethodField()
267
+
268
+ def get_plus(self, obj):
269
+ return obj.y + obj.z
270
+
271
+ f = Foo()
272
+ FooSerializer(f).serialized
273
+ # {'w': 10, 'x': 5, 'plus': 3}
274
+ ```
275
+
276
+ ### Inheritance Example
277
+
278
+ ```python
279
+ import ypres
280
+
281
+ class Foo(object):
282
+ a = 1
283
+ b = 2
284
+
285
+
286
+ class ASerializer(ypres.Serializer):
287
+ a = ypres.Field()
288
+
289
+
290
+ class ABSerializer(ASerializer):
291
+ """ABSerializer inherits the 'a' field from ASerializer.
292
+
293
+ This also works with multiple inheritance and mixins.
294
+ """
295
+ b = ypres.Field()
296
+
297
+ f = Foo()
298
+ ASerializer(f).serialized
299
+ # {'a': 1}
300
+ ABSerializer(f).serialized
301
+ # {'a': 1, 'b': 2}
302
+ ```
303
+
304
+ ## License
305
+
306
+ ypres is free software distributed under the terms of the MIT license.
307
+ See the [LICENSE](https://github.com/clarkduvall/serpy/blob/master/LICENSE) file.