xn-model 0.10.3.dev1__py3-none-any.whl → 0.10.4__py3-none-any.whl

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.
x_model/__init__.py CHANGED
@@ -3,25 +3,6 @@ from tortoise import Tortoise, connections, ConfigurationError
3
3
  from tortoise.backends.asyncpg import AsyncpgDBClient
4
4
  from tortoise.exceptions import DBConnectionError
5
5
 
6
- from .enum import FieldType
7
- from .field import PointField, RangeField, PolygonField, CollectionField, ListField, DatetimeSecField
8
- from .model import Model, TsModel, User
9
- from .func import Array
10
-
11
- __all__ = [
12
- "FieldType",
13
- "PointField",
14
- "RangeField",
15
- "PolygonField",
16
- "CollectionField",
17
- "ListField",
18
- "DatetimeSecField",
19
- "Model",
20
- "TsModel",
21
- "User",
22
- "Array",
23
- ]
24
-
25
6
 
26
7
  async def init_db(dsn: str, models: ModuleType, create_tables: bool = False) -> AsyncpgDBClient | str:
27
8
  try:
x_model/model.py CHANGED
@@ -1,172 +1,43 @@
1
1
  from datetime import datetime
2
- from passlib.context import CryptContext
3
2
  from pydantic import create_model
4
- from tortoise import Model as BaseModel
5
- from tortoise.contrib.postgres.fields import ArrayField
3
+ from tortoise import Model as TortoiseModel
6
4
  from tortoise.contrib.pydantic import pydantic_model_creator, PydanticModel
7
- from tortoise.fields import (
8
- Field,
9
- CharField,
10
- IntField,
11
- SmallIntField,
12
- BigIntField,
13
- DecimalField,
14
- FloatField,
15
- TextField,
16
- BooleanField,
17
- DatetimeField,
18
- DateField,
19
- TimeField,
20
- JSONField,
21
- ForeignKeyRelation,
22
- OneToOneRelation,
23
- ManyToManyRelation,
24
- ForeignKeyNullableRelation,
25
- OneToOneNullableRelation,
26
- IntEnumField,
27
- )
28
- from tortoise.fields.data import IntEnumFieldInstance, CharEnumFieldInstance
29
- from tortoise.fields.relational import (
30
- BackwardFKRelation,
31
- ForeignKeyFieldInstance,
32
- ManyToManyFieldInstance,
33
- OneToOneFieldInstance,
34
- BackwardOneToOneRelation,
35
- RelationalField,
36
- )
5
+ from tortoise import fields
37
6
  from tortoise.models import MetaInfo
38
7
  from tortoise.queryset import QuerySet
39
8
 
40
- from x_model import FieldType, PointField, PolygonField, RangeField
41
- from x_model.enum import UserStatus, UserRole
42
- from x_model.field import DatetimeSecField, SetField
9
+ from x_model.field import DatetimeSecField
43
10
  from x_model.pydantic import PydList
44
11
 
45
12
 
46
- class Model(BaseModel):
47
- id: int = IntField(pk=True)
48
- _name: set[str] = {"name"}
49
- _icon: str = "" # https://unpkg.com/@tabler/icons@2.30.0/icons/icon_name.svg
50
- _sorts: list[str] = ["-id"]
51
- _ownable_fields: dict[str, str | None] = {"one": None, "list": None, "in": None}
52
- _pydIn: type[PydanticModel] = None
53
- _pyd: type[PydanticModel] = None
54
- _pydListItem: type[PydanticModel] = None
55
- _permissions: tuple[bool, bool, bool] = True, True, True
56
-
57
- @classmethod
58
- def cols(cls) -> list[dict]:
59
- meta = cls._meta
60
- return [
61
- {"data": c, "orderable": c not in meta.fetch_fields or c in meta.fk_fields}
62
- for c in meta.fields_map
63
- if not c.endswith("_id")
64
- ]
65
-
66
- @classmethod
67
- def pyd(cls) -> type[PydanticModel]:
68
- cls._pyd = cls._pyd or pydantic_model_creator(cls)
69
- return cls._pyd
70
-
71
- @classmethod
72
- def pydIn(cls) -> type[PydanticModel]:
73
- if not cls._pydIn:
74
- opts = tuple(k for k, v in cls._meta.fields_map.items() if not v.required)
75
- cls._pydIn = pydantic_model_creator(
76
- cls,
77
- name=cls.__name__ + "In",
78
- meta_override=cls.PydanticMetaIn,
79
- optional=opts,
80
- exclude_readonly=True,
81
- exclude=("created_at", "updated_at"),
82
- )
83
- if m2ms := cls._meta.m2m_fields: # hack for direct inserting m2m values
84
- cls._pydIn = create_model(
85
- cls._pydIn.__name__, __base__=cls._pydIn, **{m2m: (list[int] | None, None) for m2m in m2ms}
86
- )
87
- return cls._pydIn
88
-
89
- @classmethod
90
- def pydListItem(cls) -> type[PydanticModel]:
91
- if not cls._pydListItem:
92
- cls._pydListItem = pydantic_model_creator(
93
- cls, name=cls.__name__ + "ListItem", meta_override=cls.PydanticMetaListItem
94
- )
95
- return cls._pydListItem
96
-
97
- @classmethod
98
- def pydsList(cls) -> type[PydList]:
99
- return create_model(
100
- cls.__name__ + "List",
101
- data=(list[cls.pydListItem()], []),
102
- total=(int, 0),
103
- filtered=(int | None, None),
104
- __base__=PydList[cls.pydListItem()],
105
- )
13
+ class BaseModel(TortoiseModel):
14
+ # todo: resolve ownable + add only own list method
15
+ # todo: refact: clean old garbage
16
+ id: int = fields.IntField(True)
106
17
 
107
- @classmethod
108
- async def one(cls, uid: int, owner: int = None, **kwargs) -> PydanticModel:
109
- if owner and (of := cls._ownable_fields.get("one")):
110
- kwargs.update({of: owner})
111
- q = cls.get(id=uid, **kwargs)
112
- return await cls.pyd().from_queryset_single(q)
113
-
114
- @classmethod
115
- def pageQuery(
116
- cls, sorts: list[str], limit: int = 1000, offset: int = 0, q: str = None, owner: int = None, **kwargs
117
- ) -> QuerySet:
118
- rels, keys = [], ["id"]
119
- for nam in cls._name:
120
- parts = nam.split("__")
121
- if len(parts) > 1:
122
- rels.append("__".join(parts[:-1]))
123
- keys.append(nam)
124
- query = (
125
- cls.filter(**kwargs)
126
- .order_by(*sorts)
127
- .limit(limit)
128
- .offset(offset)
129
- .prefetch_related(*(cls._meta.fetch_fields & set(kwargs)), *rels)
130
- )
131
- if q:
132
- query = query.filter(**{f"{cls._name}__icontains": q})
133
- if owner and (of := cls._ownable_fields.get("list")):
134
- query = query.filter(**{of: owner})
135
- return query
18
+ _name: tuple[str] = {"name"}
19
+ _sorts: tuple[str] = ["-id"]
136
20
 
137
- @classmethod
138
- async def pagePyd(
139
- cls, sorts: list[str], limit: int = 1000, offset: int = 0, q: str = None, owner: int = None, **kwargs
140
- ) -> PydList:
141
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
142
- pyd = cls.pydListItem()
143
- query = cls.pageQuery(sorts, limit, offset, q, owner, **kwargs)
144
- await query
145
- data = await pyd.from_queryset(query)
146
- if limit - (li := len(data)):
147
- filtered = total = li + offset
148
- else:
149
- total = await cls.all().count()
150
- filtered_query = cls.filter(**kwargs)
151
- if q:
152
- filtered_query = filtered_query.filter(**{f"{cls._name}__icontains": q})
153
- filtered = await filtered_query.count()
154
- pyds = cls.pydsList()
155
- return pyds(data=data, total=total, filtered=filtered)
156
-
157
- def repr(self) -> str:
21
+ def repr(self, sep: str = " ") -> str:
158
22
  if self._name in self._meta.db_fields:
159
- return " ".join(getattr(self, name_fragment) for name_fragment in self._name)
23
+ return sep.join(getattr(self, name_fragment) for name_fragment in self._name)
160
24
  return self.__repr__()
161
25
 
162
26
  @classmethod
163
- async def getOrCreateByName(cls, name: str, attr_name: str = None, def_dict: dict = None) -> BaseModel:
27
+ async def get_or_create_by_name(cls, name: str, attr_name: str = None, def_dict: dict = None) -> TortoiseModel:
164
28
  attr_name = attr_name or list(cls._name)[0]
165
29
  if not (obj := await cls.get_or_none(**{attr_name: name})):
166
30
  next_id = (await cls.all().order_by("-id").first()).id + 1
167
31
  obj = await cls.create(id=next_id, **{attr_name: name}, **(def_dict or {}))
168
32
  return obj
169
33
 
34
+ @classmethod
35
+ def _page_query(cls, sorts: tuple[str], limit: int = 1000, offset: int = 0, q: str = None, **filters) -> QuerySet:
36
+ query = cls.filter(**filters).order_by(*sorts).limit(limit).offset(offset)
37
+ if q:
38
+ query = query.filter(**{f"{cls._name}__icontains": q})
39
+ return query
40
+
170
41
  @classmethod
171
42
  async def upsert(cls, data: dict, oid=None):
172
43
  meta: MetaInfo = cls._meta
@@ -188,7 +59,7 @@ class Model(BaseModel):
188
59
  # save relations
189
60
  for k, ids in m2ms.items():
190
61
  if ids:
191
- m2m_rel: ManyToManyRelation = getattr(obj, k)
62
+ m2m_rel: fields.ManyToManyRelation = getattr(obj, k)
192
63
  items = [await m2m_rel.remote_model[i] for i in ids]
193
64
  await m2m_rel.clear() # for updating, not just adding
194
65
  await m2m_rel.add(*items)
@@ -204,73 +75,19 @@ class Model(BaseModel):
204
75
  await obj.fetch_related(*cls._meta.fetch_fields)
205
76
  return obj
206
77
 
207
- @classmethod
208
- def field_input_map(cls) -> dict:
209
- def type2input(ft: type[Field]):
210
- dry = {
211
- "base_field": hasattr(ft, "base_field") and {**type2input(ft.base_field)},
212
- "step": hasattr(ft, "step") and ft.step,
213
- "labels": hasattr(ft, "labels") and ft.labels,
214
- }
215
- type2inputs: {Field: dict} = {
216
- CharField: {"input": FieldType.input.name},
217
- IntField: {"input": FieldType.input.name, "type": "number"},
218
- SmallIntField: {"input": FieldType.input.name, "type": "number"},
219
- BigIntField: {"input": FieldType.input.name, "type": "number"},
220
- DecimalField: {"input": FieldType.input.name, "type": "number", "step": "0.01"},
221
- FloatField: {"input": FieldType.input.name, "type": "number", "step": "0.001"},
222
- TextField: {"input": FieldType.textarea.name, "rows": "2"},
223
- BooleanField: {"input": FieldType.checkbox.name},
224
- DatetimeField: {"input": FieldType.input.name, "type": "datetime"},
225
- DatetimeSecField: {"input": FieldType.input.name, "type": "datetime"},
226
- DateField: {"input": FieldType.input.name, "type": "date"},
227
- TimeField: {"input": FieldType.input.name, "type": "time"},
228
- JSONField: {"input": FieldType.input.name},
229
- IntEnumFieldInstance: {"input": FieldType.select.name},
230
- CharEnumFieldInstance: {"input": FieldType.select.name},
231
- ForeignKeyFieldInstance: {"input": FieldType.select.name},
232
- OneToOneFieldInstance: {"input": FieldType.select.name},
233
- ManyToManyFieldInstance: {"input": FieldType.select.name, "multiple": True},
234
- ForeignKeyRelation: {"input": FieldType.select.name, "multiple": True},
235
- OneToOneRelation: {"input": FieldType.select.name},
236
- BackwardOneToOneRelation: {"input": FieldType.select.name},
237
- ManyToManyRelation: {"input": FieldType.select.name, "multiple": True},
238
- ForeignKeyNullableRelation: {"input": FieldType.select.name, "multiple": True},
239
- BackwardFKRelation: {"input": FieldType.select.name, "multiple": True},
240
- ArrayField: {"input": FieldType.select.name, "multiple": True},
241
- SetField: {"input": FieldType.select.name, "multiple": True},
242
- OneToOneNullableRelation: {"input": FieldType.select.name},
243
- PointField: {"input": FieldType.collection.name, **dry},
244
- PolygonField: {"input": FieldType.list.name, **dry},
245
- RangeField: {"input": FieldType.collection.name, **dry},
246
- }
247
- return type2inputs[ft]
248
-
249
- def field2input(_key: str, field: Field):
250
- attrs: dict = {"required": not field.null}
251
- if isinstance(field, CharEnumFieldInstance):
252
- attrs.update({"options": {en.name: en.value for en in field.enum_type}})
253
- elif isinstance(field, IntEnumFieldInstance) or isinstance(field, SetField):
254
- attrs.update({"options": {en.value: en.name.replace("_", " ") for en in field.enum_type}})
255
- elif isinstance(field, RelationalField):
256
- attrs.update({"source_field": field.source_field}) # 'table': attrs[key]['multiple'],
257
- elif field.generated or ("auto_now" in field.__dict__ and (field.auto_now or field.auto_now_add)): # noqa
258
- attrs.update({"auto": True})
259
- return {**type2input(type(field)), **attrs}
260
-
261
- return {key: field2input(key, field) for key, field in cls._meta.fields_map.items() if not key.endswith("_id")}
262
-
263
78
  class Meta:
264
79
  abstract = True
265
80
 
81
+
82
+ class Model(BaseModel):
83
+ _pyd: type[PydanticModel] = None
84
+ _pydIn: type[PydanticModel] = None
85
+ _pydListItem: type[PydanticModel] = None
86
+
266
87
  class PydanticMeta:
267
- #: If not empty, only fields this property contains will be in the pydantic model
268
88
  # include: tuple[str, ...] = ()
269
- # #: Fields listed in this property will be excluded from pydantic model
270
89
  # exclude: tuple[str, ...] = ()
271
- # #: Computed fields can be listed here to use in pydantic model
272
90
  # computed: tuple[str, ...] = ()
273
-
274
91
  exclude_raw_fields = False # default: True
275
92
  max_recursion: int = 1 # default: 3
276
93
 
@@ -285,46 +102,71 @@ class Model(BaseModel):
285
102
  exclude_raw_fields = False # default: True
286
103
  sort_alphabetically: bool = True # default: False
287
104
 
105
+ @classmethod
106
+ def pyd(cls) -> type[PydanticModel]:
107
+ cls._pyd = cls._pyd or pydantic_model_creator(cls)
108
+ return cls._pyd
288
109
 
289
- class TsModel(Model):
290
- created_at: datetime | None = DatetimeSecField(auto_now_add=True)
291
- updated_at: datetime | None = DatetimeSecField(auto_now=True)
292
-
293
- class Meta:
294
- abstract = True
295
-
296
-
297
- class User(TsModel):
298
- status: UserStatus = IntEnumField(UserStatus, default=UserStatus.WAIT)
299
- username: str | None = CharField(95, unique=True, null=True)
300
- email: str | None = CharField(100, unique=True, null=True)
301
- password: str | None = CharField(60, null=True)
302
- phone: int | None = BigIntField(null=True)
303
- role: UserRole = IntEnumField(UserRole, default=UserRole.CLIENT)
304
-
305
- _icon = "user"
306
- _name = {"username"}
307
-
308
- class Meta:
309
- table_description = "Users"
310
-
110
+ @classmethod
111
+ def pyd_in(cls) -> type[PydanticModel]:
112
+ if not cls._pydIn:
113
+ opts = tuple(k for k, v in cls._meta.fields_map.items() if not v.required)
114
+ cls._pydIn = pydantic_model_creator(
115
+ cls,
116
+ name=cls.__name__ + "In",
117
+ meta_override=cls.PydanticMetaIn,
118
+ optional=opts,
119
+ exclude_readonly=True,
120
+ exclude=("created_at", "updated_at"),
121
+ )
122
+ if m2ms := cls._meta.m2m_fields: # hack for direct inserting m2m values
123
+ cls._pydIn = create_model(
124
+ cls._pydIn.__name__, __base__=cls._pydIn, **{m2m: (list[int] | None, None) for m2m in m2ms}
125
+ )
126
+ return cls._pydIn
311
127
 
312
- class UserPasswordTrait(TsModel):
313
- password: str | None = CharField(60, null=True)
128
+ @classmethod
129
+ def pyd_list_item(cls) -> type[PydanticModel]:
130
+ if not cls._pydListItem:
131
+ cls._pydListItem = pydantic_model_creator(
132
+ cls, name=cls.__name__ + "ListItem", meta_override=cls.PydanticMetaListItem
133
+ )
134
+ return cls._pydListItem
314
135
 
315
- __cc = CryptContext(schemes=["bcrypt"])
136
+ @classmethod
137
+ def pyds_list(cls) -> type[PydList]:
138
+ return create_model(
139
+ cls.__name__ + "List",
140
+ data=(list[cls.pyd_list_item()], []),
141
+ total=(int, 0),
142
+ filtered=(int | None, None),
143
+ __base__=PydList[cls.pyd_list_item()],
144
+ )
316
145
 
317
- def pwd_vrf(self, pwd: str) -> bool:
318
- return self.__cc.verify(pwd, self.password)
146
+ # # # CRUD Methods # # #
147
+ @classmethod
148
+ async def one_pyd(cls, uid: int, **filters) -> PydanticModel:
149
+ q = cls.get(id=uid, **filters)
150
+ return await cls.pyd().from_queryset_single(q)
319
151
 
320
152
  @classmethod
321
- async def create(cls, using_db=None, **kwargs) -> "User":
322
- user: "User" | Model = await super().create(using_db, **kwargs)
323
- if pwd := kwargs.get("password"):
324
- # noinspection PyUnresolvedReferences
325
- await user.set_pwd(pwd)
326
- return user
153
+ async def page_pyd(cls, sorts: tuple[str], limit: int = 1000, offset: int = 0, q: str = None, **filters) -> PydList:
154
+ filters = {k: v for k, v in filters.items() if v is not None}
155
+ pyd_item = cls.pyd_list_item()
156
+ query = cls._page_query(sorts, limit, offset, q, **filters)
157
+ data = await pyd_item.from_queryset(query)
158
+ if limit - (li := len(data)):
159
+ filtered = total = li + offset
160
+ else:
161
+ total = await cls.all().count()
162
+ filtered_query = cls.filter(**filters)
163
+ if q:
164
+ filtered_query = filtered_query.filter(**{f"{cls._name}__icontains": q})
165
+ filtered = await filtered_query.count()
166
+ pyds = cls.pyds_list()
167
+ return pyds(data=data, total=total, filtered=filtered)
168
+
327
169
 
328
- async def set_pwd(self, pwd: str = password) -> None:
329
- self.password = self.__cc.hash(pwd)
330
- await self.save()
170
+ class TsTrait:
171
+ created_at: datetime | None = DatetimeSecField(auto_now_add=True)
172
+ updated_at: datetime | None = DatetimeSecField(auto_now=True)
x_model/pydantic.py CHANGED
@@ -1,8 +1,6 @@
1
1
  from typing import TypeVar, Generic
2
2
  from pydantic import BaseModel, ConfigDict
3
3
 
4
- from x_model.enum import UserStatus, UserRole
5
-
6
4
 
7
5
  RootModelType = TypeVar("RootModelType")
8
6
 
@@ -13,32 +11,6 @@ class PydList(BaseModel, Generic[RootModelType]):
13
11
  total: int
14
12
 
15
13
 
16
- class UserPwd(BaseModel):
17
- password: str
18
-
19
-
20
- class UserReg(UserPwd):
21
- username: str
22
- email: str | None = None
23
- phone: int | None = None
24
-
25
-
26
- class UserUpdate(BaseModel):
27
- username: str
28
- status: UserStatus
29
- email: str | None
30
- phone: int | None
31
- role: UserRole
32
-
33
-
34
- class UserAuth(UserUpdate):
35
- id: int
36
- username: str
37
- status: UserStatus
38
- role: UserRole
39
- # ref_id: int | None
40
-
41
-
42
14
  class Names(BaseModel):
43
15
  # models for name endpoint for select2 inputs
44
16
  class Name(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xn-model
3
- Version: 0.10.3.dev1
3
+ Version: 0.10.4
4
4
  Summary: Base model for xn-api
5
5
  Author-email: Mike Artemiev <mixartemev@gmail.com>
6
6
  License: MIT
@@ -10,8 +10,6 @@ Keywords: tortoise,model,crud,generator,api,admin
10
10
  Requires-Python: >=3.12
11
11
  Description-Content-Type: text/markdown
12
12
  Requires-Dist: tortoise-orm[accel,asyncpg]
13
- Requires-Dist: passlib[bcrypt]
14
- Requires-Dist: pydantic
15
13
  Requires-Dist: python-dotenv
16
14
  Provides-Extra: dev
17
15
  Requires-Dist: pytest ; extra == 'dev'
@@ -0,0 +1,9 @@
1
+ x_model/__init__.py,sha256=HBOZMb1HheFkak6Ih7YkSGiNtGkQq7L_fiWgL_zpEC0,607
2
+ x_model/field.py,sha256=S461M94ryQG7yu8lreXtWnZo3YdCP97xhbcCJ3BzXsY,2751
3
+ x_model/func.py,sha256=E7jDoHJGaFpKvxbHnT_lyBxUZeMo-GRd5gv9dLw7B9s,289
4
+ x_model/model.py,sha256=0KICJmgVffe0pQfFNHYTADUtkwuT6Szs0Os_vNBagQg,6893
5
+ x_model/pydantic.py,sha256=OjNCp4ZFGvUIxZSbDCmXISl9mOjI_UnWi5JMVntSqjM,590
6
+ xn_model-0.10.4.dist-info/METADATA,sha256=yLU0mFuqDdBv3zQengXTfsRfFjcP54_7goExIT_qG3M,1002
7
+ xn_model-0.10.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
8
+ xn_model-0.10.4.dist-info/top_level.txt,sha256=QCYyfv5AA_8jPPtCpShkBXzQRUCGuuW7Ro0mqysDE8E,8
9
+ xn_model-0.10.4.dist-info/RECORD,,
x_model/enum.py DELETED
@@ -1,31 +0,0 @@
1
- from enum import IntEnum
2
-
3
-
4
- class FieldType(IntEnum):
5
- input = 1
6
- checkbox = 2
7
- select = 3
8
- textarea = 4
9
- collection = 5
10
- list = 6
11
-
12
-
13
- class UserStatus(IntEnum):
14
- BANNED = 0
15
- WAIT = 1 # waiting for approve
16
- TEST = 2 # trial
17
- ACTIVE = 3
18
- PREMIUM = 4
19
-
20
-
21
- class Scope(IntEnum):
22
- READ = 4
23
- WRITE = 2
24
- ALL = 1 # not only my
25
-
26
-
27
- class UserRole(IntEnum):
28
- CLIENT = Scope.READ # 4
29
- AUTHOR = Scope.WRITE # 2
30
- MANAGER = Scope.READ + Scope.WRITE # 6
31
- ADMIN = Scope.READ + Scope.WRITE + Scope.ALL # 7
@@ -1,10 +0,0 @@
1
- x_model/__init__.py,sha256=LrDAUX7IsIS0HvdPAyefqzNknvnn3-3XVk-UDLBukwA,1007
2
- x_model/enum.py,sha256=yrTIzMVnqG-kAD4ifhJhMSnYI-DIDLkB236w-Ol2I-M,543
3
- x_model/field.py,sha256=S461M94ryQG7yu8lreXtWnZo3YdCP97xhbcCJ3BzXsY,2751
4
- x_model/func.py,sha256=E7jDoHJGaFpKvxbHnT_lyBxUZeMo-GRd5gv9dLw7B9s,289
5
- x_model/model.py,sha256=rzKwyk6Uu9c6hYCuVFbB4DHgYaMrGUkwIK1Zq0jCwwE,13856
6
- x_model/pydantic.py,sha256=lTQ4eGNg_jsyGNLu8B1fTO-Ky8C3F057abhyAdhmVoQ,1047
7
- xn_model-0.10.3.dev1.dist-info/METADATA,sha256=yQ4EovBcmvABOeff2zdqiwpxYxvSaIuMVEJKQaP7MQY,1062
8
- xn_model-0.10.3.dev1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
9
- xn_model-0.10.3.dev1.dist-info/top_level.txt,sha256=QCYyfv5AA_8jPPtCpShkBXzQRUCGuuW7Ro0mqysDE8E,8
10
- xn_model-0.10.3.dev1.dist-info/RECORD,,