Skip to content

Commit 8593e1a

Browse files
committed
try something
1 parent d93d0a9 commit 8593e1a

2 files changed

Lines changed: 46 additions & 20 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ jobs:
226226
echo "CXX=g++" >> ${env:GITHUB_ENV}
227227
- name: Run tests
228228
run: |
229-
python setup.py coverage -q
229+
python setup.py coverage -q -s test.unit.test_options
230230
coverage xml
231231
- name: Upload coverage
232232
uses: codecov/codecov-action@v3

‎bfg9000/options.py‎

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import enum
2+
import sys
23
from collections import namedtuple
34
from inspect import Signature, Parameter
45

@@ -112,24 +113,29 @@ def do_recurse(result, libs):
112113
variadic = namedtuple('variadic', ['type'])
113114

114115

115-
class OptionMeta(type):
116-
def __new__(cls, name, bases, attrs):
117-
attrs.setdefault('__annotations__', {})
118-
slots = tuple(attrs['__annotations__'].keys())
119-
defaults = {}
120-
for i in slots:
121-
if i in attrs:
122-
defaults[i] = attrs.pop(i)
116+
def _get_ns_annotations(attrs):
117+
if '__annotations__' in attrs:
118+
return attrs['__annotations__']
123119

124-
attrs.update({'__slots__': slots, '_field_defaults': defaults})
125-
return type.__new__(cls, name, bases, attrs)
120+
if sys.version_info >= (3, 14):
121+
import annotationlib
122+
annotate = annotationlib.get_annotate_from_class_namespace(attrs)
123+
if annotate:
124+
return annotationlib.call_annotate_function(
125+
annotate, format=annotationlib.Format.FORWARDREF
126+
)
127+
return {}
126128

127129

128-
# This is like `typing.NamedTuple`, except with runtime type checking,
129-
# promoting strings to enums, and allowing variadic values.
130-
class Option(metaclass=OptionMeta):
131-
registry = {}
130+
def _get_annotations(thing):
131+
typ = thing if isinstance(thing, type) else type(thing)
132+
if sys.version_info >= (3, 14):
133+
import annotationlib
134+
return annotationlib.get_annotations(typ)
135+
return typ.__dict__.get('__annotations__', {})
132136

137+
138+
class OptionMeta(type):
133139
@staticmethod
134140
def __make_parameters(fields, defaults):
135141
has_variadic = False
@@ -144,6 +150,28 @@ def __make_parameters(fields, defaults):
144150
default = defaults.get(k, Parameter.empty)
145151
yield Parameter(k, kind, default=default)
146152

153+
def __new__(cls, name, bases, attrs):
154+
annotations = _get_ns_annotations(attrs)
155+
slots = tuple(annotations.keys())
156+
defaults = {}
157+
for i in slots:
158+
if i in attrs:
159+
defaults[i] = attrs.pop(i)
160+
161+
attrs.update({
162+
'__slots__': slots,
163+
'_signature': Signature(list(cls.__make_parameters(
164+
annotations, defaults
165+
)))
166+
})
167+
return type.__new__(cls, name, bases, attrs)
168+
169+
170+
# This is like `typing.NamedTuple`, except with runtime type checking,
171+
# promoting strings to enums, and allowing variadic values.
172+
class Option(metaclass=OptionMeta):
173+
registry = {}
174+
147175
@staticmethod
148176
def __check_type(typ, value):
149177
if not typ or isinstance(value, typ):
@@ -164,14 +192,12 @@ def __init_subclass__(cls, **kwargs):
164192
cls.registry[cls.__name__] = cls
165193

166194
def __init__(self, *args, **kwargs):
167-
sig = Signature(list(self.__make_parameters(
168-
self.__annotations__, self._field_defaults
169-
)))
170-
bound = sig.bind(*args, **kwargs)
195+
bound = self._signature.bind(*args, **kwargs)
171196
bound.apply_defaults()
172197

198+
annotations = _get_annotations(self)
173199
for name, value in bound.arguments.items():
174-
typ = self.__annotations__[name]
200+
typ = annotations[name]
175201
if isinstance(typ, variadic):
176202
value = [self.__check_type(typ.type, i) for i in value]
177203
else:

0 commit comments

Comments
 (0)