Skip to content

Commit c8d7adb

Browse files
author
Sylvain MARIE
committed
Fields order are preserved by @autofields even in the case of a field with just a type annotation. Fixed #76
1 parent aaff721 commit c8d7adb

File tree

4 files changed

+50
-3
lines changed

4 files changed

+50
-3
lines changed

pyfields/autofields_.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,12 @@ def v_gen():
179179
# optional field : copy the default value by default
180180
new_field = field(check_type=need_to_check_type, default_factory=copy_value(default_value))
181181

182-
# Attach the newly created field to the class
182+
# Attach the newly created field to the class. Delete attr first so that order is preserved
183+
# even if one of them had only an annotation.
184+
try:
185+
delattr(cls, member_name)
186+
except AttributeError:
187+
pass
183188
setattr(cls, member_name, new_field)
184189
new_field.set_as_cls_member(cls, member_name, type_hint=type_hint)
185190

pyfields/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,12 @@ def get_fields(cls_or_obj,
207207
):
208208
# type: (...) -> T
209209
"""
210-
Utility method to collect all fields defined in a class, including all inherited or not.
210+
Utility method to collect all fields defined in a class, including all inherited or not, in definition order.
211211
212212
By default duplicates are removed and ancestor fields are included and appear first. If a field is overridden,
213213
it will appear at the position of the overridden field in the order.
214214
215-
If an object is provided, `getfields` will be executed on its class.
215+
If an object is provided, `get_fields` will be executed on its class.
216216
217217
:param cls_or_obj:
218218
:param include_inherited:

pyfields/tests/issues/_test_py36.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Authors: Sylvain Marie <[email protected]>
22
#
33
# Copyright (c) Schneider Electric Industries, 2019. All right reserved.
4+
from typing import Optional, List
5+
46
from pyfields import field, init_fields, autofields
57

68

@@ -44,3 +46,21 @@ def test_issue_73_cross_ref():
4446
# note: we have to define the classes outside the function for the cross-ref to work
4547
# indeed typing.get_type_hints() will only access the globals of the defining module
4648
return A, B
49+
50+
51+
def test_issue_74():
52+
@autofields
53+
class City:
54+
name: Optional[str]
55+
buildings: List[str] = []
56+
57+
return City
58+
59+
60+
def test_issue_76():
61+
@autofields
62+
class Foo:
63+
c: int
64+
b: str = "hello"
65+
66+
return Foo
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import sys
2+
3+
import pytest
4+
5+
from pyfields import get_fields
6+
7+
8+
@pytest.mark.skipif(sys.version_info < (3, 6), reason="Annotations not supported in python < 3.6")
9+
def test_issue_74():
10+
"""test associated with the non-issue 74"""
11+
from ._test_py36 import test_issue_74
12+
City = test_issue_74()
13+
c = City(name=None)
14+
assert c.name is None
15+
assert c.buildings == []
16+
17+
18+
def test_issue_76():
19+
""" issue 76 is fixed """
20+
from ._test_py36 import test_issue_76
21+
Foo = test_issue_76()
22+
assert [f.name for f in get_fields(Foo)] == ['c', 'b']

0 commit comments

Comments
 (0)