jararaca 0.3.12a17__py3-none-any.whl → 0.3.12a19__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.

Potentially problematic release.


This version of jararaca might be problematic. Click here for more details.

@@ -114,9 +114,14 @@ async def providing_new_session(
114
114
  current_session.bind,
115
115
  ) as new_session, new_session.begin() as new_tx:
116
116
  with providing_session(new_session, new_tx, connection_name):
117
- yield new_session
118
- if new_tx.is_active:
119
- await new_tx.commit()
117
+ try:
118
+ yield new_session
119
+ if new_tx.is_active:
120
+ await new_tx.commit()
121
+ except Exception:
122
+ if new_tx.is_active:
123
+ await new_tx.rollback()
124
+ raise
120
125
 
121
126
 
122
127
  def use_session(connection_name: str | None = None) -> AsyncSession:
@@ -24,7 +24,7 @@ from typing import (
24
24
  from uuid import UUID
25
25
 
26
26
  from fastapi import Request, Response, UploadFile
27
- from fastapi.params import Body, Cookie, Depends, Header, Path, Query
27
+ from fastapi.params import Body, Cookie, Depends, Form, Header, Path, Query
28
28
  from fastapi.security.http import HTTPBase
29
29
  from pydantic import BaseModel, PlainValidator
30
30
  from pydantic_core import PydanticUndefined
@@ -48,6 +48,29 @@ def is_constant(name: str) -> bool:
48
48
  return CONSTANT_PATTERN.match(name) is not None
49
49
 
50
50
 
51
+ def is_upload_file_type(field_type: Any) -> bool:
52
+ """
53
+ Check if a type is UploadFile or a list/array of UploadFile.
54
+
55
+ Args:
56
+ field_type: The type to check
57
+
58
+ Returns:
59
+ True if it's UploadFile or list[UploadFile], False otherwise
60
+ """
61
+ if field_type == UploadFile:
62
+ return True
63
+
64
+ # Check for list[UploadFile], List[UploadFile], etc.
65
+ origin = get_origin(field_type)
66
+ if origin in (list, frozenset, set):
67
+ args = getattr(field_type, "__args__", ())
68
+ if args and args[0] == UploadFile:
69
+ return True
70
+
71
+ return False
72
+
73
+
51
74
  def should_exclude_field(
52
75
  field_name: str, field_type: Any, basemodel_type: Type[Any]
53
76
  ) -> bool:
@@ -625,6 +648,21 @@ def write_microservice_to_typescript_interface(
625
648
  // noinspection JSUnusedGlobalSymbols
626
649
 
627
650
  import { HttpService, HttpBackend, HttpBackendRequest, ResponseType, createClassQueryHooks , createClassMutationHooks, createClassInfiniteQueryHooks, paginationModelByFirstArgPaginationFilter } from "@jararaca/core";
651
+
652
+ function makeFormData(data: Record<string, any>): FormData {
653
+ const formData = new FormData();
654
+ for (const key in data) {
655
+ if (Array.isArray(data[key])) {
656
+ data[key].forEach((item: any) => {
657
+ formData.append(key, item);
658
+ });
659
+ } else {
660
+ formData.append(key, data[key]);
661
+ }
662
+ }
663
+ return formData;
664
+ }
665
+
628
666
  export type WebSocketMessageMap = {
629
667
  %s
630
668
  }
@@ -768,10 +806,21 @@ def write_rest_controller_to_typescript_interface(
768
806
  class_buffer.write(f'\t\t\tmethod: "{mapping.method}",\n')
769
807
 
770
808
  endpoint_path = parse_path_with_params(mapping.path, arg_params_spec)
771
- final_path = "/".join(
772
- s.strip("/") for s in [rest_controller.path, endpoint_path]
773
- )
774
- class_buffer.write(f"\t\t\tpath: `/{final_path}`,\n")
809
+
810
+ # Properly handle path joining to avoid double slashes
811
+ controller_path = rest_controller.path or ""
812
+ path_parts = []
813
+
814
+ if controller_path and controller_path.strip("/"):
815
+ path_parts.append(controller_path.strip("/"))
816
+ if endpoint_path and endpoint_path.strip("/"):
817
+ path_parts.append(endpoint_path.strip("/"))
818
+
819
+ final_path = "/".join(path_parts) if path_parts else ""
820
+ # Ensure the path starts with a single slash
821
+ formatted_path = f"/{final_path}" if final_path else "/"
822
+
823
+ class_buffer.write(f"\t\t\tpath: `{formatted_path}`,\n")
775
824
 
776
825
  # Sort path params
777
826
  path_params = sorted(
@@ -803,10 +852,25 @@ def write_rest_controller_to_typescript_interface(
803
852
  class_buffer.write(f'\t\t\t\t"{param.name}": {param.name},\n')
804
853
  class_buffer.write("\t\t\t},\n")
805
854
 
806
- if (
807
- body := next((x for x in arg_params_spec if x.type_ == "body"), None)
808
- ) is not None:
809
- class_buffer.write(f"\t\t\tbody: {body.name}\n")
855
+ # Check if we need to use FormData (for file uploads or form parameters)
856
+ form_params = [param for param in arg_params_spec if param.type_ == "form"]
857
+ body_param = next((x for x in arg_params_spec if x.type_ == "body"), None)
858
+
859
+ if form_params:
860
+ # Use FormData for file uploads and form parameters
861
+ class_buffer.write("\t\t\tbody: makeFormData({\n")
862
+
863
+ # Add form parameters (including file uploads)
864
+ for param in form_params:
865
+ class_buffer.write(f'\t\t\t\t"{param.name}": {param.name},\n')
866
+
867
+ # Add body parameter if it exists alongside form params
868
+ if body_param:
869
+ class_buffer.write(f"\t\t\t\t...{body_param.name},\n")
870
+
871
+ class_buffer.write("\t\t\t})\n")
872
+ elif body_param is not None:
873
+ class_buffer.write(f"\t\t\tbody: {body_param.name}\n")
810
874
  else:
811
875
  class_buffer.write("\t\t\tbody: undefined\n")
812
876
 
@@ -862,7 +926,7 @@ EXCLUDED_REQUESTS_TYPES = [Request, Response]
862
926
 
863
927
  @dataclass
864
928
  class HttpParemeterSpec:
865
- type_: Literal["query", "path", "body", "header", "cookie"]
929
+ type_: Literal["query", "path", "body", "header", "cookie", "form"]
866
930
  name: str
867
931
  required: bool
868
932
  argument_type_str: str
@@ -960,6 +1024,16 @@ def extract_parameters(
960
1024
  argument_type_str=get_field_type_for_ts(str),
961
1025
  )
962
1026
  )
1027
+ elif isinstance(annotated_type_hook, Form):
1028
+ mapped_types.add(annotated_type)
1029
+ parameters_list.append(
1030
+ HttpParemeterSpec(
1031
+ type_="form",
1032
+ name=parameter_name,
1033
+ required=True,
1034
+ argument_type_str=get_field_type_for_ts(annotated_type),
1035
+ )
1036
+ )
963
1037
  elif isinstance(annotated_type_hook, Body):
964
1038
  mapped_types.update(extract_all_envolved_types(parameter_type))
965
1039
  # For body parameters, use Input suffix if it's a split model
@@ -1067,26 +1141,37 @@ def extract_parameters(
1067
1141
  )
1068
1142
  else:
1069
1143
  mapped_types.add(annotated_type)
1070
- # For default parameters (treated as query), use Input suffix if it's a split model
1071
- context_suffix = (
1072
- "Input"
1073
- if (
1074
- inspect.isclass(annotated_type)
1075
- and hasattr(annotated_type, "__dict__")
1076
- and SplitInputOutput.is_split_model(annotated_type)
1144
+ # Special handling for UploadFile and list[UploadFile] - should be treated as form data
1145
+ if is_upload_file_type(annotated_type):
1146
+ parameters_list.append(
1147
+ HttpParemeterSpec(
1148
+ type_="form",
1149
+ name=parameter_name,
1150
+ required=True,
1151
+ argument_type_str=get_field_type_for_ts(annotated_type),
1152
+ )
1077
1153
  )
1078
- else ""
1079
- )
1080
- parameters_list.append(
1081
- HttpParemeterSpec(
1082
- type_="query",
1083
- name=parameter_name,
1084
- required=True,
1085
- argument_type_str=get_field_type_for_ts(
1086
- annotated_type, context_suffix
1087
- ),
1154
+ else:
1155
+ # For default parameters (treated as query), use Input suffix if it's a split model
1156
+ context_suffix = (
1157
+ "Input"
1158
+ if (
1159
+ inspect.isclass(annotated_type)
1160
+ and hasattr(annotated_type, "__dict__")
1161
+ and SplitInputOutput.is_split_model(annotated_type)
1162
+ )
1163
+ else ""
1164
+ )
1165
+ parameters_list.append(
1166
+ HttpParemeterSpec(
1167
+ type_="query",
1168
+ name=parameter_name,
1169
+ required=True,
1170
+ argument_type_str=get_field_type_for_ts(
1171
+ annotated_type, context_suffix
1172
+ ),
1173
+ )
1088
1174
  )
1089
- )
1090
1175
 
1091
1176
  elif inspect.isclass(parameter_type) and issubclass(
1092
1177
  parameter_type, BaseModel
@@ -1106,6 +1191,17 @@ def extract_parameters(
1106
1191
  ),
1107
1192
  )
1108
1193
  )
1194
+ elif parameter_type == UploadFile or is_upload_file_type(parameter_type):
1195
+ # UploadFile and list[UploadFile] should be treated as form data
1196
+ mapped_types.add(parameter_type)
1197
+ parameters_list.append(
1198
+ HttpParemeterSpec(
1199
+ type_="form",
1200
+ name=parameter_name,
1201
+ required=True,
1202
+ argument_type_str=get_field_type_for_ts(parameter_type),
1203
+ )
1204
+ )
1109
1205
  elif (
1110
1206
  # Match both simple parameters {param} and parameters with converters {param:converter}
1111
1207
  re.search(f"{{{parameter_name}(:.*?)?}}", controller.path) is not None
@@ -1134,26 +1230,37 @@ def extract_parameters(
1134
1230
  )
1135
1231
  else:
1136
1232
  mapped_types.add(parameter_type)
1137
- # For default parameters (treated as query), use Input suffix if it's a split model
1138
- context_suffix = (
1139
- "Input"
1140
- if (
1141
- inspect.isclass(parameter_type)
1142
- and hasattr(parameter_type, "__dict__")
1143
- and SplitInputOutput.is_split_model(parameter_type)
1233
+ # Special handling for UploadFile and list[UploadFile] - should be treated as form data
1234
+ if is_upload_file_type(parameter_type):
1235
+ parameters_list.append(
1236
+ HttpParemeterSpec(
1237
+ type_="form",
1238
+ name=parameter_name,
1239
+ required=True,
1240
+ argument_type_str=get_field_type_for_ts(parameter_type),
1241
+ )
1144
1242
  )
1145
- else ""
1146
- )
1147
- parameters_list.append(
1148
- HttpParemeterSpec(
1149
- type_="query",
1150
- name=parameter_name,
1151
- required=True,
1152
- argument_type_str=get_field_type_for_ts(
1153
- parameter_type, context_suffix
1154
- ),
1243
+ else:
1244
+ # For default parameters (treated as query), use Input suffix if it's a split model
1245
+ context_suffix = (
1246
+ "Input"
1247
+ if (
1248
+ inspect.isclass(parameter_type)
1249
+ and hasattr(parameter_type, "__dict__")
1250
+ and SplitInputOutput.is_split_model(parameter_type)
1251
+ )
1252
+ else ""
1253
+ )
1254
+ parameters_list.append(
1255
+ HttpParemeterSpec(
1256
+ type_="query",
1257
+ name=parameter_name,
1258
+ required=True,
1259
+ argument_type_str=get_field_type_for_ts(
1260
+ parameter_type, context_suffix
1261
+ ),
1262
+ )
1155
1263
  )
1156
- )
1157
1264
 
1158
1265
  if inspect.isclass(parameter_type) and not is_primitive(parameter_type):
1159
1266
  signature = inspect.signature(parameter_type)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jararaca
3
- Version: 0.3.12a17
3
+ Version: 0.3.12a19
4
4
  Summary: A simple and fast API framework for Python
5
5
  Home-page: https://github.com/LuscasLeo/jararaca
6
6
  Author: Lucas S
@@ -1,6 +1,6 @@
1
1
  LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
2
  README.md,sha256=2qMM__t_MoLKZr4IY9tXjo-Jn6LKjuHMb1qbyXpgL08,3401
3
- pyproject.toml,sha256=stDeBAKKaDdBk5oL2gpGAiJLpPeIrDN16gnoXjeZmlE,2041
3
+ pyproject.toml,sha256=mgJOhSQjMq5tiTypMWYNHonlLtptX67W82wrg7OUiFg,2041
4
4
  jararaca/__init__.py,sha256=vK3zyIVLckwZgj1FPX6jzSbzaSWmSy3wQ2KMwmpJnmg,22046
5
5
  jararaca/__main__.py,sha256=-O3vsB5lHdqNFjUtoELDF81IYFtR-DSiiFMzRaiSsv4,67
6
6
  jararaca/broker_backend/__init__.py,sha256=GzEIuHR1xzgCJD4FE3harNjoaYzxHMHoEL0_clUaC-k,3528
@@ -32,7 +32,7 @@ jararaca/observability/providers/otel.py,sha256=8N1F32W43t7c8cwmtTh6Yz9b7HyfGFSR
32
32
  jararaca/persistence/base.py,sha256=xnGUbsLNz3gO-9iJt-Sn5NY13Yc9-misP8wLwQuGGoM,1024
33
33
  jararaca/persistence/exports.py,sha256=Ghx4yoFaB4QVTb9WxrFYgmcSATXMNvrOvT8ybPNKXCA,62
34
34
  jararaca/persistence/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=kHI1rb71o3Ug3JzkQ0RQ2uqJuENsIRgDF69MX7H65lk,6760
35
+ jararaca/persistence/interceptors/aiosqa_interceptor.py,sha256=-YDKDicaocEz71fbt1X1S33KJBwqMYZG-4igNylbh9A,6922
36
36
  jararaca/persistence/interceptors/constants.py,sha256=o8g5RxDX9dSSnM9eXYlTJalGPfWxYm6-CAA8-FooUzE,36
37
37
  jararaca/persistence/interceptors/decorators.py,sha256=p37r9ux5w_bvn8xPNPhScl3fjCc2enqTR0cJYLxMsrI,1627
38
38
  jararaca/persistence/session.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -70,12 +70,12 @@ jararaca/tools/app_config/decorators.py,sha256=-ckkMZ1dswOmECdo1rFrZ15UAku--txaN
70
70
  jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7HqJtoVO9tfRnQ,2611
71
71
  jararaca/tools/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  jararaca/tools/typescript/decorators.py,sha256=y1zBq8mBZ8CBXlZ0nKy2RyIgCvP9kp4elACbaC6dptQ,2946
73
- jararaca/tools/typescript/interface_parser.py,sha256=OMvz4vcsKD0eYPwH1hd3PJpLP2Bc_Ex0CefIhsJArf4,47306
73
+ jararaca/tools/typescript/interface_parser.py,sha256=dkM3Nsab_1HDRY_clC6LcZLtDXFHpAqNdcqIC_MzALc,51753
74
74
  jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  jararaca/utils/rabbitmq_utils.py,sha256=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
76
76
  jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
77
- jararaca-0.3.12a17.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
78
- jararaca-0.3.12a17.dist-info/METADATA,sha256=lwNzy_Aycpsghy1xAsRkSIqz15GWEuCqyu_gNFC39Oc,4996
79
- jararaca-0.3.12a17.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
80
- jararaca-0.3.12a17.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
81
- jararaca-0.3.12a17.dist-info/RECORD,,
77
+ jararaca-0.3.12a19.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
78
+ jararaca-0.3.12a19.dist-info/METADATA,sha256=lpSsuVzfv-LNkPBbg-eFlR-E2PcsCzlNXl0da8gQbQk,4996
79
+ jararaca-0.3.12a19.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
80
+ jararaca-0.3.12a19.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
81
+ jararaca-0.3.12a19.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "jararaca"
3
- version = "0.3.12a17"
3
+ version = "0.3.12a19"
4
4
  description = "A simple and fast API framework for Python"
5
5
  authors = ["Lucas S <me@luscasleo.dev>"]
6
6
  readme = "README.md"