22
33import dataclasses
44from collections .abc import Mapping
5+ from typing import TYPE_CHECKING
6+ from urllib .parse import _NetlocResultMixinStr , urlsplit
57
68from reflex import constants
7- from reflex .utils import format
9+ from reflex .utils import console , format
810from reflex .utils .serializers import serializer
911
1012
@@ -45,36 +47,40 @@ class _HeaderData:
4547 )
4648
4749
48- @dataclasses .dataclass (frozen = True , init = False )
50+ _HEADER_DATA_FIELDS = frozenset (
51+ [field .name for field in dataclasses .fields (_HeaderData )]
52+ )
53+
54+
55+ @dataclasses .dataclass (frozen = True )
4956class HeaderData (_HeaderData ):
5057 """An object containing headers data."""
5158
52- def __init__ (self , router_data : dict | None = None ):
53- """Initialize the HeaderData object based on router_data.
59+ @classmethod
60+ def from_router_data (cls , router_data : dict ) -> "HeaderData" :
61+ """Create a HeaderData object from the given router_data.
5462
5563 Args:
5664 router_data: the router_data dict.
65+
66+ Returns:
67+ A HeaderData object initialized with the provided router_data.
5768 """
58- super ().__init__ ()
59- if router_data :
60- fields_names = [f .name for f in dataclasses .fields (self )]
61- for k , v in router_data .get (constants .RouteVar .HEADERS , {}).items ():
62- snake_case_key = format .to_snake_case (k )
63- if snake_case_key in fields_names :
64- object .__setattr__ (self , snake_case_key , v )
65- object .__setattr__ (
66- self ,
67- "raw_headers" ,
68- _FrozenDictStrStr (
69- ** {
70- k : v
71- for k , v in router_data .get (
72- constants .RouteVar .HEADERS , {}
73- ).items ()
74- if v
75- }
76- ),
77- )
69+ return cls (
70+ ** {
71+ snake_case_key : v
72+ for k , v in router_data .get (constants .RouteVar .HEADERS , {}).items ()
73+ if v
74+ and (snake_case_key := format .to_snake_case (k )) in _HEADER_DATA_FIELDS
75+ },
76+ raw_headers = _FrozenDictStrStr (
77+ ** {
78+ k : v
79+ for k , v in router_data .get (constants .RouteVar .HEADERS , {}).items ()
80+ if v
81+ }
82+ ),
83+ )
7884
7985
8086@serializer (to = dict )
@@ -90,6 +96,35 @@ def serialize_frozen_dict_str_str(obj: _FrozenDictStrStr) -> dict:
9096 return dict (obj ._data )
9197
9298
99+ class ReflexURL (str , _NetlocResultMixinStr ):
100+ """A class representing a URL split result."""
101+
102+ if TYPE_CHECKING :
103+ scheme : str
104+ netloc : str
105+ path : str
106+ query : str
107+ fragment : str
108+
109+ def __new__ (cls , url : str ):
110+ """Create a new ReflexURL instance.
111+
112+ Args:
113+ url: the URL to split.
114+
115+ Returns:
116+ A new ReflexURL instance.
117+ """
118+ (scheme , netloc , path , query , fragment ) = urlsplit (url )
119+ obj = super ().__new__ (cls , url )
120+ object .__setattr__ (obj , "scheme" , scheme )
121+ object .__setattr__ (obj , "netloc" , netloc )
122+ object .__setattr__ (obj , "path" , path )
123+ object .__setattr__ (obj , "query" , query )
124+ object .__setattr__ (obj , "fragment" , fragment )
125+ return obj
126+
127+
93128@dataclasses .dataclass (frozen = True )
94129class PageData :
95130 """An object containing page data."""
@@ -101,77 +136,115 @@ class PageData:
101136 full_raw_path : str = ""
102137 params : dict = dataclasses .field (default_factory = dict )
103138
104- def __init__ (self , router_data : dict | None = None ):
105- """Initialize the PageData object based on router_data.
139+ @classmethod
140+ def from_router_data (cls , router_data : dict ) -> "PageData" :
141+ """Create a PageData object from the given router_data.
106142
107143 Args:
108144 router_data: the router_data dict.
145+
146+ Returns:
147+ A PageData object initialized with the provided router_data.
109148 """
110- if router_data :
111- object .__setattr__ (
112- self ,
113- "host" ,
114- router_data .get (constants .RouteVar .HEADERS , {}).get ("origin" , "" ),
115- )
116- object .__setattr__ (
117- self , "path" , router_data .get (constants .RouteVar .PATH , "" )
118- )
119- object .__setattr__ (
120- self , "raw_path" , router_data .get (constants .RouteVar .ORIGIN , "" )
121- )
122- object .__setattr__ (self , "full_path" , f"{ self .host } { self .path } " )
123- object .__setattr__ (self , "full_raw_path" , f"{ self .host } { self .raw_path } " )
124- object .__setattr__ (
125- self , "params" , router_data .get (constants .RouteVar .QUERY , {})
126- )
127- else :
128- object .__setattr__ (self , "host" , "" )
129- object .__setattr__ (self , "path" , "" )
130- object .__setattr__ (self , "raw_path" , "" )
131- object .__setattr__ (self , "full_path" , "" )
132- object .__setattr__ (self , "full_raw_path" , "" )
133- object .__setattr__ (self , "params" , {})
149+ host = router_data .get (constants .RouteVar .HEADERS , {}).get ("origin" , "" )
150+ path = router_data .get (constants .RouteVar .PATH , "" )
151+ raw_path = router_data .get (constants .RouteVar .ORIGIN , "" )
152+ return cls (
153+ host = host ,
154+ path = path ,
155+ raw_path = raw_path ,
156+ full_path = f"{ host } { path } " ,
157+ full_raw_path = f"{ host } { raw_path } " ,
158+ params = router_data .get (constants .RouteVar .QUERY , {}),
159+ )
134160
135161
136- @dataclasses .dataclass (frozen = True , init = False )
162+ @dataclasses .dataclass (frozen = True )
137163class SessionData :
138164 """An object containing session data."""
139165
140166 client_token : str = ""
141167 client_ip : str = ""
142168 session_id : str = ""
143169
144- def __init__ (self , router_data : dict | None = None ):
145- """Initialize the SessionData object based on router_data.
170+ @classmethod
171+ def from_router_data (cls , router_data : dict ) -> "SessionData" :
172+ """Create a SessionData object from the given router_data.
146173
147174 Args:
148175 router_data: the router_data dict.
176+
177+ Returns:
178+ A SessionData object initialized with the provided router_data.
149179 """
150- if router_data :
151- client_token = router_data .get (constants .RouteVar .CLIENT_TOKEN , "" )
152- client_ip = router_data .get (constants .RouteVar .CLIENT_IP , "" )
153- session_id = router_data .get (constants .RouteVar .SESSION_ID , "" )
154- else :
155- client_token = client_ip = session_id = ""
156- object .__setattr__ (self , "client_token" , client_token )
157- object .__setattr__ (self , "client_ip" , client_ip )
158- object .__setattr__ (self , "session_id" , session_id )
180+ return cls (
181+ client_token = router_data .get (constants .RouteVar .CLIENT_TOKEN , "" ),
182+ client_ip = router_data .get (constants .RouteVar .CLIENT_IP , "" ),
183+ session_id = router_data .get (constants .RouteVar .SESSION_ID , "" ),
184+ )
159185
160186
161- @dataclasses .dataclass (frozen = True , init = False )
187+ @dataclasses .dataclass (frozen = True )
162188class RouterData :
163189 """An object containing RouterData."""
164190
165191 session : SessionData = dataclasses .field (default_factory = SessionData )
166192 headers : HeaderData = dataclasses .field (default_factory = HeaderData )
167- page : PageData = dataclasses .field (default_factory = PageData )
193+ _page : PageData = dataclasses .field (default_factory = PageData )
194+ url : ReflexURL = dataclasses .field (default = ReflexURL ("" ))
195+ route_id : str = ""
168196
169- def __init__ (self , router_data : dict | None = None ):
170- """Initialize the RouterData object.
197+ @property
198+ def page (self ) -> PageData :
199+ """Get the page data.
200+
201+ Returns:
202+ The PageData object.
203+ """
204+ console .deprecate (
205+ "RouterData.page" ,
206+ "Use RouterData.url instead" ,
207+ deprecation_version = "0.8.1" ,
208+ removal_version = "0.9.0" ,
209+ )
210+ return self ._page
211+
212+ @classmethod
213+ def from_router_data (cls , router_data : dict ) -> "RouterData" :
214+ """Create a RouterData object from the given router_data.
171215
172216 Args:
173217 router_data: the router_data dict.
218+
219+ Returns:
220+ A RouterData object initialized with the provided router_data.
174221 """
175- object .__setattr__ (self , "session" , SessionData (router_data ))
176- object .__setattr__ (self , "headers" , HeaderData (router_data ))
177- object .__setattr__ (self , "page" , PageData (router_data ))
222+ return cls (
223+ session = SessionData .from_router_data (router_data ),
224+ headers = HeaderData .from_router_data (router_data ),
225+ _page = PageData .from_router_data (router_data ),
226+ url = ReflexURL (
227+ router_data .get (constants .RouteVar .HEADERS , {}).get ("origin" , "" )
228+ + router_data .get (constants .RouteVar .ORIGIN , "" )
229+ ),
230+ route_id = router_data .get (constants .RouteVar .PATH , "" ),
231+ )
232+
233+
234+ @serializer (to = dict )
235+ def serialize_router_data (obj : RouterData ) -> dict :
236+ """Serialize a RouterData object to a dict.
237+
238+ Args:
239+ obj: the RouterData object.
240+
241+ Returns:
242+ A dict representation of the RouterData object.
243+ """
244+ return {
245+ "session" : obj .session ,
246+ "headers" : obj .headers ,
247+ "page" : obj ._page ,
248+ "url" : obj .url ,
249+ "route_id" : obj .route_id ,
250+ }
0 commit comments