panther 3.8.2__py3-none-any.whl → 3.9.0__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.
panther/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from panther.main import Panther # noqa: F401
2
2
 
3
- __version__ = '3.8.2'
3
+ __version__ = '3.9.0'
4
4
 
5
5
 
6
6
  def version():
panther/cli/template.py CHANGED
@@ -99,6 +99,7 @@ requirements = """panther==%s
99
99
 
100
100
  TEMPLATE = {
101
101
  'app': {
102
+ '__init__.py': '',
102
103
  'apis.py': apis_py,
103
104
  'models.py': models_py,
104
105
  'serializers.py': serializers_py,
@@ -106,6 +107,7 @@ TEMPLATE = {
106
107
  'urls.py': app_urls_py,
107
108
  },
108
109
  'core': {
110
+ '__init__.py': '',
109
111
  'configs.py': configs_py,
110
112
  'urls.py': urls_py,
111
113
  },
panther/serializer.py CHANGED
@@ -1,47 +1,122 @@
1
- from pydantic import create_model
1
+ import typing
2
+
3
+ from pydantic import create_model, BaseModel
4
+ from pydantic.fields import FieldInfo
2
5
  from pydantic_core._pydantic_core import PydanticUndefined
3
6
 
7
+ from panther.db import Model
8
+
9
+
10
+ class MetaModelSerializer:
11
+ KNOWN_CONFIGS = ['model', 'fields', 'required_fields']
12
+
13
+ def __new__(
14
+ cls,
15
+ cls_name: str,
16
+ bases: tuple[type[typing.Any], ...],
17
+ namespace: dict[str, typing.Any],
18
+ **kwargs
19
+ ):
20
+ if cls_name == 'ModelSerializer':
21
+ cls.model_serializer = type(cls_name, (), namespace)
22
+ return super().__new__(cls)
23
+
24
+ # 1. Initial Check
25
+ cls.check_config(cls_name=cls_name, namespace=namespace)
26
+ config = namespace.pop('Config')
27
+
28
+ # 2. Collect `Fields`
29
+ field_definitions = cls.collect_fields(config=config, namespace=namespace)
30
+
31
+ # 3. Collect `pydantic.model_config`
32
+ model_config = cls.collect_model_config(config=config, namespace=namespace)
33
+ namespace |= {'model_config': model_config}
34
+
35
+ # 4. Create a serializer
36
+ return create_model(
37
+ __model_name=cls_name,
38
+ __module__=namespace['__module__'],
39
+ __validators__=namespace,
40
+ __base__=(cls.model_serializer, BaseModel),
41
+ __doc__=namespace.get('__doc__'),
42
+ model=(typing.ClassVar, config.model),
43
+ **field_definitions
44
+ )
4
45
 
5
- class ModelSerializer:
6
- def __new__(cls, *args, model=None, **kwargs):
7
- # Check `metaclass`
8
- if len(args) == 0:
9
- address = f'{cls.__module__}.{cls.__name__}'
10
- msg = f"you should not inherit the 'ModelSerializer', you should use it as 'metaclass' -> {address}"
11
- raise TypeError(msg)
46
+ @classmethod
47
+ def check_config(cls, cls_name: str, namespace: dict) -> None:
48
+ module = namespace['__module__']
49
+ address = f'{module}.{cls_name}'
12
50
 
13
- model_name = args[0]
14
- data = args[2]
15
- address = f'{data["__module__"]}.{model_name}'
51
+ # Check `Config`
52
+ if (config := namespace.get('Config')) is None:
53
+ msg = f'`class Config` is required in {address}.'
54
+ raise AttributeError(msg) from None
16
55
 
17
56
  # Check `model`
18
- if model is None:
19
- msg = f"'model' required while using 'ModelSerializer' metaclass -> {address}"
20
- raise AttributeError(msg)
21
- # Check `fields`
22
- if 'fields' not in data:
23
- msg = f"'fields' required while using 'ModelSerializer' metaclass. -> {address}"
57
+ if (model := getattr(config, 'model', None)) is None:
58
+ msg = f'`{cls_name}.Config.model` is required.'
24
59
  raise AttributeError(msg) from None
25
60
 
26
- model_fields = model.model_fields
27
- field_definitions = {}
61
+ # Check `model` type
62
+ try:
63
+ if not issubclass(model, Model):
64
+ msg = f'`{cls_name}.Config.model` is not subclass of `panther.db.Model`.'
65
+ raise AttributeError(msg) from None
66
+ except TypeError:
67
+ msg = f'`{cls_name}.Config.model` is not subclass of `panther.db.Model`.'
68
+ raise AttributeError(msg) from None
28
69
 
29
- # Collect `fields`
30
- for field_name in data['fields']:
31
- if field_name not in model_fields:
32
- msg = f"'{field_name}' is not in '{model.__name__}' -> {address}"
70
+ # Check `fields`
71
+ if (fields := getattr(config, 'fields', None)) is None:
72
+ msg = f'`{cls_name}.Config.fields` is required.'
73
+ raise AttributeError(msg) from None
74
+
75
+ for field_name in fields:
76
+ if field_name not in model.model_fields:
77
+ msg = f'`{cls_name}.Config.fields.{field_name}` is not valid.'
33
78
  raise AttributeError(msg) from None
34
- field_definitions[field_name] = (model_fields[field_name].annotation, model_fields[field_name])
35
79
 
36
- # Change `required_fields
37
- for required in data.get('required_fields', []):
38
- if required not in field_definitions:
39
- msg = f"'{required}' is in 'required_fields' but not in 'fields' -> {address}"
80
+ # Check `required_fields`
81
+ if not hasattr(config, 'required_fields'):
82
+ config.required_fields = []
83
+
84
+ for required in config.required_fields:
85
+ if required not in config.fields:
86
+ msg = f'`{cls_name}.Config.required_fields.{required}` should be in `Config.fields` too.'
40
87
  raise AttributeError(msg) from None
88
+
89
+ @classmethod
90
+ def collect_fields(cls, config: typing.Callable, namespace: dict) -> dict:
91
+ field_definitions = {}
92
+
93
+ # Define `fields`
94
+ for field_name in config.fields:
95
+ field_definitions[field_name] = (
96
+ config.model.model_fields[field_name].annotation,
97
+ config.model.model_fields[field_name]
98
+ )
99
+
100
+ # Apply `required_fields`
101
+ for required in config.required_fields:
41
102
  field_definitions[required][1].default = PydanticUndefined
42
103
 
43
- # Create Model
44
- return create_model(
45
- __model_name=model_name,
46
- **field_definitions
47
- )
104
+ # Collect and Override `Class Fields`
105
+ for key, value in namespace.pop('__annotations__', {}).items():
106
+ field_info = namespace.pop(key, FieldInfo(required=True))
107
+ field_info.annotation = value
108
+ field_definitions[key] = (value, field_info)
109
+
110
+ return field_definitions
111
+
112
+ @classmethod
113
+ def collect_model_config(cls, config: typing.Callable, namespace: dict) -> dict:
114
+ return {
115
+ attr: getattr(config, attr) for attr in dir(config)
116
+ if not attr.startswith('__') and attr not in cls.KNOWN_CONFIGS
117
+ } | namespace.pop('model_config', {})
118
+
119
+
120
+ class ModelSerializer(metaclass=MetaModelSerializer):
121
+ def create(self) -> type[Model]:
122
+ return self.model.insert_one(self.model_dump())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 3.8.2
3
+ Version: 3.9.0
4
4
  Summary: Fast & Friendly, Web Framework For Building Async APIs
5
5
  Home-page: https://github.com/alirn76/panther
6
6
  Author: Ali RajabNezhad
@@ -1,4 +1,4 @@
1
- panther/__init__.py,sha256=BxHPfeiPa9IiyVmruTkfZevBJk7kM_UBuyJDO4YE6J4,110
1
+ panther/__init__.py,sha256=BAEuHfAfaSKM6MIIjkjOTg21nA8Wm0UEidxR02NxD-8,110
2
2
  panther/_load_configs.py,sha256=jlDO5rK040z3LLmMFMJ6jUL9oTKaDKCWebwmdYJPDjw,9567
3
3
  panther/_utils.py,sha256=BHAOdpESn3dt90FtpPKxEZdnkXnDC7MFcncJYburEaQ,4846
4
4
  panther/app.py,sha256=3Qf0b-OtczqU5xs7AT7_hFr3t1eFMqzaThQOFu-RoqE,8060
@@ -17,7 +17,7 @@ panther/permissions.py,sha256=Q-l25369yQaP-tY11tFQm-v9PPh8iVImbfUpY3pnQUk,355
17
17
  panther/request.py,sha256=u_-pyttspRimlSwpSwyVIV0U87hHDIthsFU9Qr8LDvI,1762
18
18
  panther/response.py,sha256=5t9-HwgwRo3egqzs1uFNhIq5i5wZW-TRrVX8YEdFrgA,3724
19
19
  panther/routings.py,sha256=GfwBZL7bt3yVOnpdjOZ-49kiGVrRoF0i8VwS1qkF_oI,5273
20
- panther/serializer.py,sha256=ZvLWwHV2KQdl49ZFRshz4HWXVmFNQtUClggSWkH9pa8,1839
20
+ panther/serializer.py,sha256=yyWLNJRXiAKXHb9QXMYPH__159O7jUPfivOnOJZ-G4s,4463
21
21
  panther/status.py,sha256=Gc_PnYrHfInTsZpGbqiCfDB-py1C7Rh8KMdb6Lq9Exs,3346
22
22
  panther/test.py,sha256=Se0cF51JRAmABpB_QTIJeTDRKDcyOMlibEN6rrr4Sd8,6152
23
23
  panther/throttling.py,sha256=mVa_mGv6w_Ad7LLtV4eG5QpDwwNsk4QjFFi0mIHQBnE,231
@@ -28,7 +28,7 @@ panther/cli/create_command.py,sha256=AshVxzhSpX-L_63OLP7ZgQ5a45NINGomTbvJkCaadks
28
28
  panther/cli/main.py,sha256=pCqnOTazgMhTvFHTugutIsiFXueU5kx2VmGngwAl54Q,1679
29
29
  panther/cli/monitor_command.py,sha256=cZ7wOxeQ8jKXgRLP28SgFI9pbIEp4RK6VmVf74WKT6c,2447
30
30
  panther/cli/run_command.py,sha256=X_T9jP_Z8X_fm9S4LSoR6ARsPp4rCTMi1E5c7QWREjM,2120
31
- panther/cli/template.py,sha256=i4YUgsdVjYhWGoCpoCNQOBo2rKSVk0yx-o_GBmG4yx8,4941
31
+ panther/cli/template.py,sha256=pyAA0nTBbcwcg91DRyFdH8X3PmlxQsH33gZ_CcX8E2c,4995
32
32
  panther/cli/utils.py,sha256=nhtdr9nhYAbCm2S-17vTc3_unjQJ450uxT0ZVyZueIw,4838
33
33
  panther/db/__init__.py,sha256=w9lEL0vRqb18Qx_iUJipUR_fi5GQ5uVX0DWycx14x08,50
34
34
  panther/db/connection.py,sha256=9lXzMinGNgQraeu4Sjy92AueR7_AGRsDb0EE0_K4VP4,3039
@@ -46,9 +46,9 @@ panther/panel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  panther/panel/apis.py,sha256=HUf9Km81wWtO3pbgW0Oi5MDbLvX5LIWdiFRy05UsitE,1773
47
47
  panther/panel/urls.py,sha256=BQkWqSJBPP3VEQYeorKSHIRx-PUl21Y7Z6NFylmhs1I,192
48
48
  panther/panel/utils.py,sha256=0Rv79oR5IEqalqwpRKQHMn1p5duVY5mxMqDKiA5mWx4,437
49
- panther-3.8.2.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
50
- panther-3.8.2.dist-info/METADATA,sha256=1bNtyftxbZetjdusrShmCiWOxAAJFkZZJBrQzt-86ww,6023
51
- panther-3.8.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
52
- panther-3.8.2.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
53
- panther-3.8.2.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
54
- panther-3.8.2.dist-info/RECORD,,
49
+ panther-3.9.0.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
50
+ panther-3.9.0.dist-info/METADATA,sha256=HRcpMdvX_ZowvddAhQjCyTElTU1O7I9BpvcypW67UJo,6023
51
+ panther-3.9.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
52
+ panther-3.9.0.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
53
+ panther-3.9.0.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
54
+ panther-3.9.0.dist-info/RECORD,,