Skip to content

Commit

Permalink
Merge pull request #236 from phenobarbital/bugfix-optional-list-str
Browse files Browse the repository at this point in the history
fix an segfault for infinite iteration on parse_type
  • Loading branch information
phenobarbital authored Jan 23, 2025
2 parents ed89b56 + 2b53e2c commit 768dbf5
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 12 deletions.
18 changes: 13 additions & 5 deletions datamodel/converters.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1223,15 +1223,16 @@ cpdef parse_type(object field, object T, object data, object encoder = None):
cdef object type_args = getattr(T, '__args__', None)
cdef dict typeinfo = getattr(T, '_typeinfo_', None)

# Check if the data is already of the correct type
if isinstance(data, T):
return data

if field._type_category == 'typing':
args = None
try:
args = type_args
except AttributeError:
pass
args = type_args or ()
if type_name == 'Dict' and isinstance(data, dict):
if args:
return {k: parse_type(field, type_args[1], v) for k, v in data.items()}

elif type_name == 'List':
if not isinstance(data, (list, tuple)):
data = [data]
Expand Down Expand Up @@ -1284,6 +1285,13 @@ cpdef parse_type(object field, object T, object data, object encoder = None):
return None
# Determine the non-None type.
non_none_arg = args[0] if args[1] is type(None) else args[1]
if non_none_arg == list:
field.args = args
field.origin = get_origin(non_none_arg)
if isinstance(data, (list, str, dict)):
return _parse_builtin_type(field, non_none_arg, data, encoder)
else:
raise ValueError(f"Unsupported type for List in Optional: {type(data)}")
# If the non-None type is exactly dict, return the dict as is.
if non_none_arg is dict:
return data
Expand Down
2 changes: 1 addition & 1 deletion datamodel/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
'simple library based on python +3.8 to use Dataclass-syntax'
'for interacting with Data'
)
__version__ = '0.9.2'
__version__ = '0.9.3'
__copyright__ = 'Copyright (c) 2020-2024 Jesus Lara'
__author__ = 'Jesus Lara'
__author_email__ = 'jesuslarag@gmail.com'
Expand Down
24 changes: 18 additions & 6 deletions examples/test_descriptor.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from dataclasses import dataclass, field
from datamodel import BaseModel
from datamodel import BaseModel, Field

class IntConversionDescriptor:
def __init__(self, *, default):
class IntConversionDescriptor(Field):
def __init__(self, *, default, **kwargs):
self._default = default
super().__init__(default=default, **kwargs)

def __set_name__(self, owner, name):
self._name = "_" + name
Expand All @@ -16,11 +16,23 @@ def __get__(self, obj, type):
def __set__(self, obj, value):
setattr(obj, self._name, int(value))

@dataclass
class InventoryItem(BaseModel):
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(default=100.10)
quantity_on_hand: IntConversionDescriptor = IntConversionDescriptor(
default=100.10,
required=True,
ui_meta={'label': 'Quantity on Hand'}
)
price_per_unit: IntConversionDescriptor = IntConversionDescriptor(
default=50.75,
required=False,
ui_meta={'label': 'Price per Unit'}
)

item = InventoryItem()
print(item.quantity_on_hand) # 100
item.quantity_on_hand = 2.573 # __set__ converts to integer
print(item.quantity_on_hand) # 2

print(item.price_per_unit) # 50
item.price_per_unit = 99.99
print(item.price_per_unit) # 99
19 changes: 19 additions & 0 deletions examples/test_optlist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Optional
from datamodel import BaseModel, Field
from datamodel.converters import parse_type

class TestOptional(BaseModel):
services_especialities: Optional[list] = Field(required=False)

opt = TestOptional(services_especialities=None)
columns = opt.get_columns()
print('Columns ', columns)
col = columns['services_especialities']

print(col, opt, columns)
data = ['Pilates', 'Yoga', 'Mindfulness', 'Running', 'Pilates']
newval = parse_type(col, col.type, data)
opt.set('services_especialities', newval)

assert opt.services_especialities == data
assert type(opt.services_especialities) == list

0 comments on commit 768dbf5

Please sign in to comment.