test_var.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027
  1. import json
  2. import math
  3. import typing
  4. from typing import Dict, List, Mapping, Optional, Set, Tuple, Union, cast
  5. import pytest
  6. from pandas import DataFrame
  7. import reflex as rx
  8. from reflex.base import Base
  9. from reflex.config import PerformanceMode
  10. from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
  11. from reflex.state import BaseState
  12. from reflex.utils.exceptions import (
  13. PrimitiveUnserializableToJSONError,
  14. UntypedComputedVarError,
  15. )
  16. from reflex.utils.imports import ImportVar
  17. from reflex.vars import VarData
  18. from reflex.vars.base import (
  19. ComputedVar,
  20. LiteralVar,
  21. Var,
  22. computed_var,
  23. var_operation,
  24. var_operation_return,
  25. )
  26. from reflex.vars.function import (
  27. ArgsFunctionOperation,
  28. DestructuredArg,
  29. FunctionStringVar,
  30. )
  31. from reflex.vars.number import LiteralBooleanVar, LiteralNumberVar, NumberVar
  32. from reflex.vars.object import LiteralObjectVar, ObjectVar
  33. from reflex.vars.sequence import (
  34. ArrayVar,
  35. ConcatVarOperation,
  36. LiteralArrayVar,
  37. LiteralStringVar,
  38. )
  39. test_vars = [
  40. Var(_js_expr="prop1", _var_type=int),
  41. Var(_js_expr="key", _var_type=str),
  42. Var(_js_expr="value", _var_type=str)._var_set_state("state"),
  43. Var(_js_expr="local", _var_type=str)._var_set_state("state"),
  44. Var(_js_expr="local2", _var_type=str),
  45. ]
  46. class ATestState(BaseState):
  47. """Test state."""
  48. value: str
  49. dict_val: Dict[str, List] = {}
  50. @pytest.fixture
  51. def TestObj():
  52. class TestObj(Base):
  53. foo: int
  54. bar: str
  55. return TestObj
  56. @pytest.fixture
  57. def ParentState(TestObj):
  58. class ParentState(BaseState):
  59. foo: int
  60. bar: int
  61. @computed_var
  62. def var_without_annotation(self):
  63. return TestObj
  64. return ParentState
  65. @pytest.fixture
  66. def ChildState(ParentState, TestObj):
  67. class ChildState(ParentState):
  68. @computed_var
  69. def var_without_annotation(self):
  70. """This shadows ParentState.var_without_annotation.
  71. Returns:
  72. TestObj: Test object.
  73. """
  74. return TestObj
  75. return ChildState
  76. @pytest.fixture
  77. def GrandChildState(ChildState, TestObj):
  78. class GrandChildState(ChildState):
  79. @computed_var
  80. def var_without_annotation(self):
  81. """This shadows ChildState.var_without_annotation.
  82. Returns:
  83. TestObj: Test object.
  84. """
  85. return TestObj
  86. return GrandChildState
  87. @pytest.fixture
  88. def StateWithAnyVar(TestObj):
  89. class StateWithAnyVar(BaseState):
  90. @computed_var
  91. def var_without_annotation(self) -> typing.Any:
  92. return TestObj
  93. return StateWithAnyVar
  94. @pytest.fixture
  95. def StateWithCorrectVarAnnotation():
  96. class StateWithCorrectVarAnnotation(BaseState):
  97. @computed_var
  98. def var_with_annotation(self) -> str:
  99. return "Correct annotation"
  100. return StateWithCorrectVarAnnotation
  101. @pytest.fixture
  102. def StateWithWrongVarAnnotation(TestObj):
  103. class StateWithWrongVarAnnotation(BaseState):
  104. @computed_var
  105. def var_with_annotation(self) -> str:
  106. return TestObj
  107. return StateWithWrongVarAnnotation
  108. @pytest.fixture
  109. def StateWithInitialComputedVar():
  110. class StateWithInitialComputedVar(BaseState):
  111. @computed_var(initial_value="Initial value")
  112. def var_with_initial_value(self) -> str:
  113. return "Runtime value"
  114. return StateWithInitialComputedVar
  115. @pytest.fixture
  116. def ChildWithInitialComputedVar(StateWithInitialComputedVar):
  117. class ChildWithInitialComputedVar(StateWithInitialComputedVar):
  118. @computed_var(initial_value="Initial value")
  119. def var_with_initial_value_child(self) -> str:
  120. return "Runtime value"
  121. return ChildWithInitialComputedVar
  122. @pytest.fixture
  123. def StateWithRuntimeOnlyVar():
  124. class StateWithRuntimeOnlyVar(BaseState):
  125. @computed_var(initial_value=None)
  126. def var_raises_at_runtime(self) -> str:
  127. raise ValueError("So nicht, mein Freund")
  128. return StateWithRuntimeOnlyVar
  129. @pytest.fixture
  130. def ChildWithRuntimeOnlyVar(StateWithRuntimeOnlyVar):
  131. class ChildWithRuntimeOnlyVar(StateWithRuntimeOnlyVar):
  132. @computed_var(initial_value="Initial value")
  133. def var_raises_at_runtime_child(self) -> str:
  134. raise ValueError("So nicht, mein Freund")
  135. return ChildWithRuntimeOnlyVar
  136. @pytest.mark.parametrize(
  137. "prop,expected",
  138. zip(
  139. test_vars,
  140. [
  141. "prop1",
  142. "key",
  143. "state.value",
  144. "state.local",
  145. "local2",
  146. ],
  147. strict=True,
  148. ),
  149. )
  150. def test_full_name(prop, expected):
  151. """Test that the full name of a var is correct.
  152. Args:
  153. prop: The var to test.
  154. expected: The expected full name.
  155. """
  156. assert str(prop) == expected
  157. @pytest.mark.parametrize(
  158. "prop,expected",
  159. zip(
  160. test_vars,
  161. ["prop1", "key", "state.value", "state.local", "local2"],
  162. strict=True,
  163. ),
  164. )
  165. def test_str(prop, expected):
  166. """Test that the string representation of a var is correct.
  167. Args:
  168. prop: The var to test.
  169. expected: The expected string representation.
  170. """
  171. assert str(prop) == expected
  172. @pytest.mark.parametrize(
  173. ("prop", "expected"),
  174. [
  175. (Var(_js_expr="p", _var_type=int), 0),
  176. (Var(_js_expr="p", _var_type=float), 0.0),
  177. (Var(_js_expr="p", _var_type=str), ""),
  178. (Var(_js_expr="p", _var_type=bool), False),
  179. (Var(_js_expr="p", _var_type=list), []),
  180. (Var(_js_expr="p", _var_type=dict), {}),
  181. (Var(_js_expr="p", _var_type=tuple), ()),
  182. (Var(_js_expr="p", _var_type=set), set()),
  183. ],
  184. )
  185. def test_default_value(prop: Var, expected):
  186. """Test that the default value of a var is correct.
  187. Args:
  188. prop: The var to test.
  189. expected: The expected default value.
  190. """
  191. assert prop._get_default_value() == expected
  192. @pytest.mark.parametrize(
  193. "prop,expected",
  194. zip(
  195. test_vars,
  196. [
  197. "set_prop1",
  198. "set_key",
  199. "state.set_value",
  200. "state.set_local",
  201. "set_local2",
  202. ],
  203. strict=True,
  204. ),
  205. )
  206. def test_get_setter(prop: Var, expected):
  207. """Test that the name of the setter function of a var is correct.
  208. Args:
  209. prop: The var to test.
  210. expected: The expected name of the setter function.
  211. """
  212. assert prop._get_setter_name() == expected
  213. @pytest.mark.parametrize(
  214. "value,expected",
  215. [
  216. (None, Var(_js_expr="null", _var_type=None)),
  217. (1, Var(_js_expr="1", _var_type=int)),
  218. ("key", Var(_js_expr='"key"', _var_type=str)),
  219. (3.14, Var(_js_expr="3.14", _var_type=float)),
  220. ([1, 2, 3], Var(_js_expr="[1, 2, 3]", _var_type=List[int])),
  221. (
  222. {"a": 1, "b": 2},
  223. Var(_js_expr='({ ["a"] : 1, ["b"] : 2 })', _var_type=Mapping[str, int]),
  224. ),
  225. ],
  226. )
  227. def test_create(value, expected):
  228. """Test the var create function.
  229. Args:
  230. value: The value to create a var from.
  231. expected: The expected name of the setter function.
  232. """
  233. prop = LiteralVar.create(value)
  234. assert prop.equals(expected)
  235. def test_create_type_error():
  236. """Test the var create function when inputs type error."""
  237. class ErrorType:
  238. pass
  239. value = ErrorType()
  240. with pytest.raises(TypeError):
  241. LiteralVar.create(value)
  242. def v(value) -> Var:
  243. return LiteralVar.create(value)
  244. def test_basic_operations(TestObj):
  245. """Test the var operations.
  246. Args:
  247. TestObj: The test object.
  248. """
  249. assert str(v(1) == v(2)) == "(1 === 2)"
  250. assert str(v(1) != v(2)) == "(1 !== 2)"
  251. assert str(LiteralNumberVar.create(1) < 2) == "(1 < 2)"
  252. assert str(LiteralNumberVar.create(1) <= 2) == "(1 <= 2)"
  253. assert str(LiteralNumberVar.create(1) > 2) == "(1 > 2)"
  254. assert str(LiteralNumberVar.create(1) >= 2) == "(1 >= 2)"
  255. assert str(LiteralNumberVar.create(1) + 2) == "(1 + 2)"
  256. assert str(LiteralNumberVar.create(1) - 2) == "(1 - 2)"
  257. assert str(LiteralNumberVar.create(1) * 2) == "(1 * 2)"
  258. assert str(LiteralNumberVar.create(1) / 2) == "(1 / 2)"
  259. assert str(LiteralNumberVar.create(1) // 2) == "Math.floor(1 / 2)"
  260. assert str(LiteralNumberVar.create(1) % 2) == "(1 % 2)"
  261. assert str(LiteralNumberVar.create(1) ** 2) == "(1 ** 2)"
  262. assert str(LiteralNumberVar.create(1) & v(2)) == "(1 && 2)"
  263. assert str(LiteralNumberVar.create(1) | v(2)) == "(1 || 2)"
  264. assert (
  265. str(LiteralArrayVar.create([1, 2, 3])[0])
  266. == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3], ...args)))(0))"
  267. )
  268. assert (
  269. str(LiteralObjectVar.create({"a": 1, "b": 2})["a"])
  270. == '({ ["a"] : 1, ["b"] : 2 })["a"]'
  271. )
  272. assert str(v("foo") == v("bar")) == '("foo" === "bar")'
  273. assert str(Var(_js_expr="foo") == Var(_js_expr="bar")) == "(foo === bar)"
  274. assert (
  275. str(LiteralVar.create("foo") == LiteralVar.create("bar")) == '("foo" === "bar")'
  276. )
  277. print(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state"))
  278. assert (
  279. str(
  280. Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar
  281. == LiteralVar.create("bar")
  282. )
  283. == '(state.foo["bar"] === "bar")'
  284. )
  285. assert (
  286. str(Var(_js_expr="foo").to(ObjectVar, TestObj)._var_set_state("state").bar)
  287. == 'state.foo["bar"]'
  288. )
  289. assert str(abs(LiteralNumberVar.create(1))) == "(Math.abs(1))"
  290. assert (
  291. str(LiteralArrayVar.create([1, 2, 3]).length())
  292. == "(((...args) => (((_array) => _array.length)([1, 2, 3], ...args)))())"
  293. )
  294. assert (
  295. str(LiteralArrayVar.create([1, 2]) + LiteralArrayVar.create([3, 4]))
  296. == "(((...args) => (((_lhs, _rhs) => [..._lhs, ..._rhs])([1, 2], ...args)))([3, 4]))"
  297. )
  298. # Tests for reverse operation
  299. assert (
  300. str(LiteralArrayVar.create([1, 2, 3]).reverse())
  301. == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3], ...args)))())"
  302. )
  303. assert (
  304. str(LiteralArrayVar.create(["1", "2", "3"]).reverse())
  305. == '(((...args) => (((_array) => _array.slice().reverse())(["1", "2", "3"], ...args)))())'
  306. )
  307. assert (
  308. str(Var(_js_expr="foo")._var_set_state("state").to(list).reverse())
  309. == "(((...args) => (((_array) => _array.slice().reverse())(state.foo, ...args)))())"
  310. )
  311. assert (
  312. str(Var(_js_expr="foo").to(list).reverse())
  313. == "(((...args) => (((_array) => _array.slice().reverse())(foo, ...args)))())"
  314. )
  315. assert str(Var(_js_expr="foo", _var_type=str).js_type()) == "(typeof(foo))"
  316. @pytest.mark.parametrize(
  317. "var, expected",
  318. [
  319. (v([1, 2, 3]), "[1, 2, 3]"),
  320. (v({1, 2, 3}), "[1, 2, 3]"),
  321. (v(["1", "2", "3"]), '["1", "2", "3"]'),
  322. (
  323. Var(_js_expr="foo")._var_set_state("state").to(list),
  324. "state.foo",
  325. ),
  326. (Var(_js_expr="foo").to(list), "foo"),
  327. (v((1, 2, 3)), "[1, 2, 3]"),
  328. (v(("1", "2", "3")), '["1", "2", "3"]'),
  329. (
  330. Var(_js_expr="foo")._var_set_state("state").to(tuple),
  331. "state.foo",
  332. ),
  333. (Var(_js_expr="foo").to(tuple), "foo"),
  334. ],
  335. )
  336. def test_list_tuple_contains(var, expected):
  337. assert (
  338. str(var.contains(1))
  339. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(1))'
  340. )
  341. assert (
  342. str(var.contains("1"))
  343. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))'
  344. )
  345. assert (
  346. str(var.contains(v(1)))
  347. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(1))'
  348. )
  349. assert (
  350. str(var.contains(v("1")))
  351. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))("1"))'
  352. )
  353. other_state_var = Var(_js_expr="other", _var_type=str)._var_set_state("state")
  354. other_var = Var(_js_expr="other", _var_type=str)
  355. assert (
  356. str(var.contains(other_state_var))
  357. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(state.other))'
  358. )
  359. assert (
  360. str(var.contains(other_var))
  361. == f'(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))({expected!s}, ...args)))(other))'
  362. )
  363. class Foo(rx.Base):
  364. """Foo class."""
  365. bar: int
  366. baz: str
  367. class Bar(rx.Base):
  368. """Bar class."""
  369. bar: str
  370. baz: str
  371. foo: int
  372. @pytest.mark.parametrize(
  373. ("var", "var_type"),
  374. [
  375. (Var(_js_expr="").to(Foo | Bar), Foo | Bar),
  376. (Var(_js_expr="").to(Foo | Bar).bar, Union[int, str]),
  377. (Var(_js_expr="").to(Union[Foo, Bar]), Union[Foo, Bar]),
  378. (Var(_js_expr="").to(Union[Foo, Bar]).baz, str),
  379. (
  380. Var(_js_expr="").to(Union[Foo, Bar]).foo,
  381. Union[int, None],
  382. ),
  383. ],
  384. )
  385. def test_var_types(var, var_type):
  386. assert var._var_type == var_type
  387. @pytest.mark.parametrize(
  388. "var, expected",
  389. [
  390. (v("123"), json.dumps("123")),
  391. (Var(_js_expr="foo")._var_set_state("state").to(str), "state.foo"),
  392. (Var(_js_expr="foo").to(str), "foo"),
  393. ],
  394. )
  395. def test_str_contains(var, expected):
  396. assert (
  397. str(var.contains("1"))
  398. == f'(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))("1"))'
  399. )
  400. assert (
  401. str(var.contains(v("1")))
  402. == f'(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))("1"))'
  403. )
  404. other_state_var = Var(_js_expr="other")._var_set_state("state").to(str)
  405. other_var = Var(_js_expr="other").to(str)
  406. assert (
  407. str(var.contains(other_state_var))
  408. == f"(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))(state.other))"
  409. )
  410. assert (
  411. str(var.contains(other_var))
  412. == f"(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))({expected!s}, ...args)))(other))"
  413. )
  414. @pytest.mark.parametrize(
  415. "var, expected",
  416. [
  417. (v({"a": 1, "b": 2}), '({ ["a"] : 1, ["b"] : 2 })'),
  418. (Var(_js_expr="foo")._var_set_state("state").to(dict), "state.foo"),
  419. (Var(_js_expr="foo").to(dict), "foo"),
  420. ],
  421. )
  422. def test_dict_contains(var, expected):
  423. assert str(var.contains(1)) == f"{expected!s}.hasOwnProperty(1)"
  424. assert str(var.contains("1")) == f'{expected!s}.hasOwnProperty("1")'
  425. assert str(var.contains(v(1))) == f"{expected!s}.hasOwnProperty(1)"
  426. assert str(var.contains(v("1"))) == f'{expected!s}.hasOwnProperty("1")'
  427. other_state_var = Var(_js_expr="other")._var_set_state("state").to(str)
  428. other_var = Var(_js_expr="other").to(str)
  429. assert (
  430. str(var.contains(other_state_var))
  431. == f"{expected!s}.hasOwnProperty(state.other)"
  432. )
  433. assert str(var.contains(other_var)) == f"{expected!s}.hasOwnProperty(other)"
  434. @pytest.mark.parametrize(
  435. "var",
  436. [
  437. Var(_js_expr="list", _var_type=List[int]).guess_type(),
  438. Var(_js_expr="tuple", _var_type=Tuple[int, int]).guess_type(),
  439. ],
  440. )
  441. def test_var_indexing_lists(var):
  442. """Test that we can index into str, list or tuple vars.
  443. Args:
  444. var : The str, list or tuple base var.
  445. """
  446. # Test basic indexing.
  447. assert (
  448. str(var[0])
  449. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(0))"
  450. )
  451. assert (
  452. str(var[1])
  453. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(1))"
  454. )
  455. # Test negative indexing.
  456. assert (
  457. str(var[-1])
  458. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))(-1))"
  459. )
  460. @pytest.mark.parametrize(
  461. "var, type_",
  462. [
  463. (Var(_js_expr="list", _var_type=List[int]).guess_type(), [int, int]),
  464. (
  465. Var(_js_expr="tuple", _var_type=Tuple[int, str]).guess_type(),
  466. [int, str],
  467. ),
  468. ],
  469. )
  470. def test_var_indexing_types(var, type_):
  471. """Test that indexing returns valid types.
  472. Args:
  473. var : The list, tuple base var.
  474. type_ : The type on indexed object.
  475. """
  476. assert var[0]._var_type == type_[0]
  477. assert var[1]._var_type == type_[1]
  478. def test_var_indexing_str():
  479. """Test that we can index into str vars."""
  480. str_var = Var(_js_expr="str").to(str)
  481. # Test that indexing gives a type of Var[str].
  482. assert isinstance(str_var[0], Var)
  483. assert str_var[0]._var_type is str
  484. # Test basic indexing.
  485. assert (
  486. str(str_var[0])
  487. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(0))"
  488. )
  489. assert (
  490. str(str_var[1])
  491. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(1))"
  492. )
  493. # Test negative indexing.
  494. assert (
  495. str(str_var[-1])
  496. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))(-1))"
  497. )
  498. @pytest.mark.parametrize(
  499. "var",
  500. [
  501. (Var(_js_expr="foo", _var_type=int).guess_type()),
  502. (Var(_js_expr="bar", _var_type=float).guess_type()),
  503. ],
  504. )
  505. def test_var_replace_with_invalid_kwargs(var):
  506. with pytest.raises(TypeError) as excinfo:
  507. var._replace(_this_should_fail=True)
  508. assert "unexpected keyword argument" in str(excinfo.value)
  509. def test_computed_var_replace_with_invalid_kwargs():
  510. @computed_var(initial_value=1)
  511. def test_var(state) -> int:
  512. return 1
  513. with pytest.raises(TypeError) as excinfo:
  514. test_var._replace(_random_kwarg=True)
  515. assert "Unexpected keyword argument" in str(excinfo.value)
  516. @pytest.mark.parametrize(
  517. "var, index",
  518. [
  519. (Var(_js_expr="lst", _var_type=List[int]).guess_type(), [1, 2]),
  520. (
  521. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  522. {"name": "dict"},
  523. ),
  524. (Var(_js_expr="lst", _var_type=List[int]).guess_type(), {"set"}),
  525. (
  526. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  527. (
  528. 1,
  529. 2,
  530. ),
  531. ),
  532. (Var(_js_expr="lst", _var_type=List[int]).guess_type(), 1.5),
  533. (Var(_js_expr="lst", _var_type=List[int]).guess_type(), "str"),
  534. (
  535. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  536. Var(_js_expr="string_var", _var_type=str).guess_type(),
  537. ),
  538. (
  539. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  540. Var(_js_expr="float_var", _var_type=float).guess_type(),
  541. ),
  542. (
  543. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  544. Var(_js_expr="list_var", _var_type=List[int]).guess_type(),
  545. ),
  546. (
  547. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  548. Var(_js_expr="set_var", _var_type=Set[str]).guess_type(),
  549. ),
  550. (
  551. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  552. Var(_js_expr="dict_var", _var_type=Dict[str, str]).guess_type(),
  553. ),
  554. (Var(_js_expr="str", _var_type=str).guess_type(), [1, 2]),
  555. (Var(_js_expr="lst", _var_type=str).guess_type(), {"name": "dict"}),
  556. (Var(_js_expr="lst", _var_type=str).guess_type(), {"set"}),
  557. (
  558. Var(_js_expr="lst", _var_type=str).guess_type(),
  559. Var(_js_expr="string_var", _var_type=str).guess_type(),
  560. ),
  561. (
  562. Var(_js_expr="lst", _var_type=str).guess_type(),
  563. Var(_js_expr="float_var", _var_type=float).guess_type(),
  564. ),
  565. (Var(_js_expr="str", _var_type=Tuple[str]).guess_type(), [1, 2]),
  566. (
  567. Var(_js_expr="lst", _var_type=Tuple[str]).guess_type(),
  568. {"name": "dict"},
  569. ),
  570. (Var(_js_expr="lst", _var_type=Tuple[str]).guess_type(), {"set"}),
  571. (
  572. Var(_js_expr="lst", _var_type=Tuple[str]).guess_type(),
  573. Var(_js_expr="string_var", _var_type=str).guess_type(),
  574. ),
  575. (
  576. Var(_js_expr="lst", _var_type=Tuple[str]).guess_type(),
  577. Var(_js_expr="float_var", _var_type=float).guess_type(),
  578. ),
  579. ],
  580. )
  581. def test_var_unsupported_indexing_lists(var, index):
  582. """Test unsupported indexing throws a type error.
  583. Args:
  584. var: The base var.
  585. index: The base var index.
  586. """
  587. with pytest.raises(TypeError):
  588. var[index]
  589. @pytest.mark.parametrize(
  590. "var",
  591. [
  592. Var(_js_expr="lst", _var_type=List[int]).guess_type(),
  593. Var(_js_expr="tuple", _var_type=Tuple[int, int]).guess_type(),
  594. ],
  595. )
  596. def test_var_list_slicing(var):
  597. """Test that we can slice into str, list or tuple vars.
  598. Args:
  599. var : The str, list or tuple base var.
  600. """
  601. assert (
  602. str(var[:1])
  603. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([null, 1, null]))"
  604. )
  605. assert (
  606. str(var[1:])
  607. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([1, null, null]))"
  608. )
  609. assert (
  610. str(var[:])
  611. == f"(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))({var!s}, ...args)))([null, null, null]))"
  612. )
  613. def test_str_var_slicing():
  614. """Test that we can slice into str vars."""
  615. str_var = Var(_js_expr="str").to(str)
  616. # Test that slicing gives a type of Var[str].
  617. assert isinstance(str_var[:1], Var)
  618. assert str_var[:1]._var_type is str
  619. # Test basic slicing.
  620. assert (
  621. str(str_var[:1])
  622. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, 1, null]))"
  623. )
  624. assert (
  625. str(str_var[1:])
  626. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([1, null, null]))"
  627. )
  628. assert (
  629. str(str_var[:])
  630. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, null, null]))"
  631. )
  632. assert (
  633. str(str_var[1:2])
  634. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([1, 2, null]))"
  635. )
  636. # Test negative slicing.
  637. assert (
  638. str(str_var[:-1])
  639. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, -1, null]))"
  640. )
  641. assert (
  642. str(str_var[-1:])
  643. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([-1, null, null]))"
  644. )
  645. assert (
  646. str(str_var[:-2])
  647. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([null, -2, null]))"
  648. )
  649. assert (
  650. str(str_var[-2:])
  651. == "(((...args) => (((_string, _index_or_slice) => Array.prototype.join.apply(atSliceOrIndex(_string, _index_or_slice), ['']))(str, ...args)))([-2, null, null]))"
  652. )
  653. def test_dict_indexing():
  654. """Test that we can index into dict vars."""
  655. dct = Var(_js_expr="dct").to(ObjectVar, Dict[str, str])
  656. # Check correct indexing.
  657. assert str(dct["a"]) == 'dct["a"]'
  658. assert str(dct["asdf"]) == 'dct["asdf"]'
  659. @pytest.mark.parametrize(
  660. "var, index",
  661. [
  662. (
  663. Var(_js_expr="dict", _var_type=Dict[str, str]).guess_type(),
  664. [1, 2],
  665. ),
  666. (
  667. Var(_js_expr="dict", _var_type=Dict[str, str]).guess_type(),
  668. {"name": "dict"},
  669. ),
  670. (
  671. Var(_js_expr="dict", _var_type=Dict[str, str]).guess_type(),
  672. {"set"},
  673. ),
  674. (
  675. Var(_js_expr="dict", _var_type=Dict[str, str]).guess_type(),
  676. (
  677. 1,
  678. 2,
  679. ),
  680. ),
  681. (
  682. Var(_js_expr="lst", _var_type=Dict[str, str]).guess_type(),
  683. Var(_js_expr="list_var", _var_type=List[int]).guess_type(),
  684. ),
  685. (
  686. Var(_js_expr="lst", _var_type=Dict[str, str]).guess_type(),
  687. Var(_js_expr="set_var", _var_type=Set[str]).guess_type(),
  688. ),
  689. (
  690. Var(_js_expr="lst", _var_type=Dict[str, str]).guess_type(),
  691. Var(_js_expr="dict_var", _var_type=Dict[str, str]).guess_type(),
  692. ),
  693. (
  694. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  695. [1, 2],
  696. ),
  697. (
  698. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  699. {"name": "dict"},
  700. ),
  701. (
  702. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  703. {"set"},
  704. ),
  705. (
  706. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  707. (
  708. 1,
  709. 2,
  710. ),
  711. ),
  712. (
  713. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  714. Var(_js_expr="list_var", _var_type=List[int]).guess_type(),
  715. ),
  716. (
  717. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  718. Var(_js_expr="set_var", _var_type=Set[str]).guess_type(),
  719. ),
  720. (
  721. Var(_js_expr="df", _var_type=DataFrame).guess_type(),
  722. Var(_js_expr="dict_var", _var_type=Dict[str, str]).guess_type(),
  723. ),
  724. ],
  725. )
  726. def test_var_unsupported_indexing_dicts(var, index):
  727. """Test unsupported indexing throws a type error.
  728. Args:
  729. var: The base var.
  730. index: The base var index.
  731. """
  732. with pytest.raises(TypeError):
  733. var[index]
  734. @pytest.mark.parametrize(
  735. "fixture",
  736. [
  737. "ParentState",
  738. "StateWithAnyVar",
  739. ],
  740. )
  741. def test_computed_var_without_annotation_error(request, fixture):
  742. """Test that a type error is thrown when an attribute of a computed var is
  743. accessed without annotating the computed var.
  744. Args:
  745. request: Fixture Request.
  746. fixture: The state fixture.
  747. """
  748. with pytest.raises(TypeError) as err:
  749. state = request.getfixturevalue(fixture)
  750. state.var_without_annotation.foo
  751. full_name = state.var_without_annotation._var_full_name
  752. assert (
  753. err.value.args[0]
  754. == f"You must provide an annotation for the state var `{full_name}`. Annotation cannot be `typing.Any`"
  755. )
  756. @pytest.mark.parametrize(
  757. "fixture",
  758. [
  759. "ChildState",
  760. "GrandChildState",
  761. ],
  762. )
  763. def test_shadow_computed_var_error(request: pytest.FixtureRequest, fixture: str):
  764. """Test that a name error is thrown when an attribute of a computed var is
  765. shadowed by another attribute.
  766. Args:
  767. request: Fixture Request.
  768. fixture: The state fixture.
  769. """
  770. with pytest.raises(UntypedComputedVarError):
  771. state = request.getfixturevalue(fixture)
  772. state.var_without_annotation.foo
  773. @pytest.mark.parametrize(
  774. "fixture",
  775. [
  776. "StateWithCorrectVarAnnotation",
  777. "StateWithWrongVarAnnotation",
  778. ],
  779. )
  780. def test_computed_var_with_annotation_error(request, fixture):
  781. """Test that an Attribute error is thrown when a non-existent attribute of an annotated computed var is
  782. accessed or when the wrong annotation is provided to a computed var.
  783. Args:
  784. request: Fixture Request.
  785. fixture: The state fixture.
  786. """
  787. with pytest.raises(AttributeError) as err:
  788. state = request.getfixturevalue(fixture)
  789. state.var_with_annotation.foo
  790. full_name = state.var_with_annotation._var_full_name
  791. assert (
  792. err.value.args[0]
  793. == f"The State var `{full_name}` has no attribute 'foo' or may have been annotated wrongly."
  794. )
  795. @pytest.mark.parametrize(
  796. "fixture,var_name,expected_initial,expected_runtime,raises_at_runtime",
  797. [
  798. (
  799. "StateWithInitialComputedVar",
  800. "var_with_initial_value",
  801. "Initial value",
  802. "Runtime value",
  803. False,
  804. ),
  805. (
  806. "ChildWithInitialComputedVar",
  807. "var_with_initial_value_child",
  808. "Initial value",
  809. "Runtime value",
  810. False,
  811. ),
  812. (
  813. "StateWithRuntimeOnlyVar",
  814. "var_raises_at_runtime",
  815. None,
  816. None,
  817. True,
  818. ),
  819. (
  820. "ChildWithRuntimeOnlyVar",
  821. "var_raises_at_runtime_child",
  822. "Initial value",
  823. None,
  824. True,
  825. ),
  826. ],
  827. )
  828. def test_state_with_initial_computed_var(
  829. request, fixture, var_name, expected_initial, expected_runtime, raises_at_runtime
  830. ):
  831. """Test that the initial and runtime values of a computed var are correct.
  832. Args:
  833. request: Fixture Request.
  834. fixture: The state fixture.
  835. var_name: The name of the computed var.
  836. expected_initial: The expected initial value of the computed var.
  837. expected_runtime: The expected runtime value of the computed var.
  838. raises_at_runtime: Whether the computed var is runtime only.
  839. """
  840. state = request.getfixturevalue(fixture)()
  841. state_name = state.get_full_name()
  842. initial_dict = state.dict(initial=True)[state_name]
  843. assert initial_dict[var_name] == expected_initial
  844. if raises_at_runtime:
  845. with pytest.raises(ValueError):
  846. state.dict()[state_name][var_name]
  847. else:
  848. runtime_dict = state.dict()[state_name]
  849. assert runtime_dict[var_name] == expected_runtime
  850. def test_literal_var():
  851. complicated_var = LiteralVar.create(
  852. [
  853. {"a": 1, "b": 2, "c": {"d": 3, "e": 4}},
  854. [1, 2, 3, 4],
  855. 9,
  856. "string",
  857. True,
  858. False,
  859. None,
  860. {1, 2, 3},
  861. ]
  862. )
  863. assert (
  864. str(complicated_var)
  865. == '[({ ["a"] : 1, ["b"] : 2, ["c"] : ({ ["d"] : 3, ["e"] : 4 }) }), [1, 2, 3, 4], 9, "string", true, false, null, [1, 2, 3]]'
  866. )
  867. def test_function_var():
  868. addition_func = FunctionStringVar.create("((a, b) => a + b)")
  869. assert str(addition_func.call(1, 2)) == "(((a, b) => a + b)(1, 2))"
  870. manual_addition_func = ArgsFunctionOperation.create(
  871. ("a", "b"),
  872. {
  873. "args": [Var(_js_expr="a"), Var(_js_expr="b")],
  874. "result": Var(_js_expr="a + b"),
  875. },
  876. )
  877. assert (
  878. str(manual_addition_func.call(1, 2))
  879. == '(((a, b) => ({ ["args"] : [a, b], ["result"] : a + b }))(1, 2))'
  880. )
  881. increment_func = addition_func.partial(1)
  882. assert (
  883. str(increment_func.call(2))
  884. == "(((...args) => (((a, b) => a + b)(1, ...args)))(2))"
  885. )
  886. create_hello_statement = ArgsFunctionOperation.create(
  887. ("name",), f"Hello, {Var(_js_expr='name')}!"
  888. )
  889. first_name = LiteralStringVar.create("Steven")
  890. last_name = LiteralStringVar.create("Universe")
  891. assert (
  892. str(create_hello_statement.call(f"{first_name} {last_name}"))
  893. == '(((name) => ("Hello, "+name+"!"))("Steven Universe"))'
  894. )
  895. # Test with destructured arguments
  896. destructured_func = ArgsFunctionOperation.create(
  897. (DestructuredArg(fields=("a", "b")),),
  898. Var(_js_expr="a + b"),
  899. )
  900. assert (
  901. str(destructured_func.call({"a": 1, "b": 2}))
  902. == '((({a, b}) => a + b)(({ ["a"] : 1, ["b"] : 2 })))'
  903. )
  904. # Test with explicit return
  905. explicit_return_func = ArgsFunctionOperation.create(
  906. ("a", "b"), Var(_js_expr="return a + b"), explicit_return=True
  907. )
  908. assert str(explicit_return_func.call(1, 2)) == "(((a, b) => {return a + b})(1, 2))"
  909. def test_var_operation():
  910. @var_operation
  911. def add(a: Var[int], b: Var[int]):
  912. return var_operation_return(js_expression=f"({a} + {b})", var_type=int)
  913. assert str(add(1, 2)) == "(1 + 2)"
  914. assert str(add(4, -9)) == "(4 + -9)"
  915. five = LiteralNumberVar.create(5)
  916. seven = add(2, five)
  917. assert isinstance(seven, NumberVar)
  918. def test_string_operations():
  919. basic_string = LiteralStringVar.create("Hello, World!")
  920. assert (
  921. str(basic_string.length())
  922. == '(((...args) => (((...arg) => (((_array) => _array.length)((((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])(...args)))))("Hello, World!", ...args)))())'
  923. )
  924. assert (
  925. str(basic_string.lower())
  926. == '(((...args) => (((_string) => String.prototype.toLowerCase.apply(_string))("Hello, World!", ...args)))())'
  927. )
  928. assert (
  929. str(basic_string.upper())
  930. == '(((...args) => (((_string) => String.prototype.toUpperCase.apply(_string))("Hello, World!", ...args)))())'
  931. )
  932. assert (
  933. str(basic_string.strip())
  934. == '(((...args) => (((_string) => String.prototype.trim.apply(_string))("Hello, World!", ...args)))())'
  935. )
  936. assert (
  937. str(basic_string.contains("World"))
  938. == '(((...args) => (((_haystack, _needle) => _haystack.includes(_needle))("Hello, World!", ...args)))("World"))'
  939. )
  940. assert (
  941. str(basic_string.split(" ").join(","))
  942. == '(((...args) => (((_array, _sep = "") => Array.prototype.join.apply(_array,[_sep]))((((...args) => (((_string, _sep = "") => isTrue(_sep) ? _string.split(_sep) : [..._string])("Hello, World!", ...args)))(" ")), ...args)))(","))'
  943. )
  944. def test_all_number_operations():
  945. starting_number = LiteralNumberVar.create(-5.4)
  946. complicated_number = (((-(starting_number + 1)) * 2 / 3) // 2 % 3) ** 2
  947. assert (
  948. str(complicated_number)
  949. == "((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)"
  950. )
  951. even_more_complicated_number = ~(
  952. abs(math.floor(complicated_number)) | 2 & 3 & round(complicated_number)
  953. )
  954. assert (
  955. str(even_more_complicated_number)
  956. == "!((isTrue(((Math.abs((Math.floor(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2))))) || (2 && Math.round(((Math.floor(((-((-5.4 + 1)) * 2) / 3) / 2) % 3) ** 2)))))))"
  957. )
  958. assert str(LiteralNumberVar.create(5) > False) == "(5 > 0)"
  959. assert str(LiteralBooleanVar.create(False) < 5) == "((Number(false)) < 5)"
  960. assert (
  961. str(LiteralBooleanVar.create(False) < LiteralBooleanVar.create(True))
  962. == "((Number(false)) < (Number(true)))"
  963. )
  964. @pytest.mark.parametrize(
  965. ("var", "expected"),
  966. [
  967. (Var.create(False), "false"),
  968. (Var.create(True), "true"),
  969. (Var.create("false"), '(isTrue("false"))'),
  970. (Var.create([1, 2, 3]), "(isTrue([1, 2, 3]))"),
  971. (Var.create({"a": 1, "b": 2}), '(isTrue(({ ["a"] : 1, ["b"] : 2 })))'),
  972. (Var("mysterious_var"), "(isTrue(mysterious_var))"),
  973. ],
  974. )
  975. def test_boolify_operations(var, expected):
  976. assert str(var.bool()) == expected
  977. def test_index_operation():
  978. array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
  979. assert (
  980. str(array_var[0])
  981. == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0))"
  982. )
  983. assert (
  984. str(array_var[1:2])
  985. == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([1, 2, null]))"
  986. )
  987. assert (
  988. str(array_var[1:4:2])
  989. == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([1, 4, 2]))"
  990. )
  991. assert (
  992. str(array_var[::-1])
  993. == "(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))([null, null, -1]))"
  994. )
  995. assert (
  996. str(array_var.reverse())
  997. == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3, 4, 5], ...args)))())"
  998. )
  999. assert (
  1000. str(array_var[0].to(NumberVar) + 9)
  1001. == "((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))([1, 2, 3, 4, 5], ...args)))(0)) + 9)"
  1002. )
  1003. @pytest.mark.parametrize(
  1004. "var, expected_js",
  1005. [
  1006. (Var.create(float("inf")), "Infinity"),
  1007. (Var.create(-float("inf")), "-Infinity"),
  1008. (Var.create(float("nan")), "NaN"),
  1009. ],
  1010. )
  1011. def test_inf_and_nan(var, expected_js):
  1012. assert str(var) == expected_js
  1013. assert isinstance(var, NumberVar)
  1014. assert isinstance(var, LiteralVar)
  1015. with pytest.raises(PrimitiveUnserializableToJSONError):
  1016. var.json()
  1017. def test_array_operations():
  1018. array_var = LiteralArrayVar.create([1, 2, 3, 4, 5])
  1019. assert (
  1020. str(array_var.length())
  1021. == "(((...args) => (((_array) => _array.length)([1, 2, 3, 4, 5], ...args)))())"
  1022. )
  1023. assert (
  1024. str(array_var.contains(3))
  1025. == '(((...args) => (((_haystack, _needle, _field = "") => isTrue(_field) ? _haystack.some(obj => obj[_field] === _needle) : _haystack.some(obj => obj === _needle))([1, 2, 3, 4, 5], ...args)))(3))'
  1026. )
  1027. assert (
  1028. str(array_var.reverse())
  1029. == "(((...args) => (((_array) => _array.slice().reverse())([1, 2, 3, 4, 5], ...args)))())"
  1030. )
  1031. assert str(ArrayVar.range(10)) == "[...range(10, null, 1)]"
  1032. assert str(ArrayVar.range(1, 10)) == "[...range(1, 10, 1)]"
  1033. assert str(ArrayVar.range(1, 10, 2)) == "[...range(1, 10, 2)]"
  1034. assert str(ArrayVar.range(1, 10, -1)) == "[...range(1, 10, -1)]"
  1035. def test_object_operations():
  1036. object_var = LiteralObjectVar.create({"a": 1, "b": 2, "c": 3})
  1037. assert (
  1038. str(object_var.keys()) == '(Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))'
  1039. )
  1040. assert (
  1041. str(object_var.values())
  1042. == '(Object.values(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))'
  1043. )
  1044. assert (
  1045. str(object_var.entries())
  1046. == '(Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })))'
  1047. )
  1048. assert str(object_var.a) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
  1049. assert str(object_var["a"]) == '({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["a"]'
  1050. assert (
  1051. str(object_var.merge(LiteralObjectVar.create({"c": 4, "d": 5})))
  1052. == '({...({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }), ...({ ["c"] : 4, ["d"] : 5 })})'
  1053. )
  1054. def test_var_component():
  1055. class ComponentVarState(rx.State):
  1056. field_var: rx.Component = rx.text("I am a field var")
  1057. @rx.var
  1058. def computed_var(self) -> rx.Component:
  1059. return rx.text("I am a computed var")
  1060. def has_eval_react_component(var: Var):
  1061. var_data = var._get_all_var_data()
  1062. assert var_data is not None
  1063. assert any(
  1064. any(
  1065. imported_object.name == "evalReactComponent"
  1066. for imported_object in imported_objects
  1067. )
  1068. for _, imported_objects in var_data.imports
  1069. )
  1070. has_eval_react_component(ComponentVarState.field_var) # pyright: ignore [reportArgumentType]
  1071. has_eval_react_component(ComponentVarState.computed_var)
  1072. def test_type_chains():
  1073. object_var = LiteralObjectVar.create({"a": 1, "b": 2, "c": 3})
  1074. assert (object_var._key_type(), object_var._value_type()) == (str, int)
  1075. assert (object_var.keys()._var_type, object_var.values()._var_type) == (
  1076. List[str],
  1077. List[int],
  1078. )
  1079. assert (
  1080. str(object_var.keys()[0].upper()) # pyright: ignore [reportAttributeAccessIssue]
  1081. == '(((...args) => (((_string) => String.prototype.toUpperCase.apply(_string))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.keys(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(0)), ...args)))())'
  1082. )
  1083. assert (
  1084. str(object_var.entries()[1][1] - 1) # pyright: ignore [reportCallIssue, reportOperatorIssue]
  1085. == '((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((Object.entries(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 }))), ...args)))(1)), ...args)))(1)) - 1)'
  1086. )
  1087. assert (
  1088. str(object_var["c"] + object_var["b"]) # pyright: ignore [reportCallIssue, reportOperatorIssue]
  1089. == '(({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["c"] + ({ ["a"] : 1, ["b"] : 2, ["c"] : 3 })["b"])'
  1090. )
  1091. def test_nested_dict():
  1092. arr = Var.create([{"bar": ["foo", "bar"]}]).to(List[Dict[str, List[str]]])
  1093. first_dict = arr.at(0)
  1094. bar_element = first_dict["bar"]
  1095. first_bar_element = bar_element[0]
  1096. assert (
  1097. str(first_bar_element)
  1098. == '(((...args) => (((_array, _index_or_slice) => atSliceOrIndex(_array, _index_or_slice))((((...args) => (((_array, _index) => _array.at(_index))([({ ["bar"] : ["foo", "bar"] })], ...args)))(0))["bar"], ...args)))(0))' # pyright: ignore [reportIndexIssue]
  1099. )
  1100. def nested_base():
  1101. class Boo(Base):
  1102. foo: str
  1103. bar: int
  1104. class Foo(Base):
  1105. bar: Boo
  1106. baz: int
  1107. parent_obj = LiteralObjectVar.create(
  1108. Foo(bar=Boo(foo="bar", bar=5), baz=5).dict(), Foo
  1109. )
  1110. assert (
  1111. str(parent_obj.bar.foo)
  1112. == '({ ["bar"] : ({ ["foo"] : "bar", ["bar"] : 5 }), ["baz"] : 5 })["bar"]["foo"]'
  1113. )
  1114. def test_retrival():
  1115. var_without_data = Var(_js_expr="test")
  1116. assert var_without_data is not None
  1117. original_var_data = VarData(
  1118. state="Test",
  1119. imports={"react": [ImportVar(tag="useRef")]},
  1120. hooks={"const state = useContext(StateContexts.state)": None},
  1121. )
  1122. var_with_data = var_without_data._replace(merge_var_data=original_var_data)
  1123. f_string = f"foo{var_with_data}bar"
  1124. assert REFLEX_VAR_OPENING_TAG in f_string
  1125. assert REFLEX_VAR_CLOSING_TAG in f_string
  1126. result_var_data = LiteralVar.create(f_string)._get_all_var_data()
  1127. result_immutable_var_data = Var(_js_expr=f_string)._var_data
  1128. assert result_var_data is not None and result_immutable_var_data is not None
  1129. assert (
  1130. result_var_data.state
  1131. == result_immutable_var_data.state
  1132. == original_var_data.state
  1133. )
  1134. assert (
  1135. result_var_data.imports
  1136. == result_immutable_var_data.imports
  1137. == original_var_data.imports
  1138. )
  1139. assert (
  1140. tuple(result_var_data.hooks)
  1141. == tuple(result_immutable_var_data.hooks)
  1142. == tuple(original_var_data.hooks)
  1143. )
  1144. def test_fstring_concat():
  1145. original_var_with_data = LiteralVar.create(
  1146. "imagination", _var_data=VarData(state="fear")
  1147. )
  1148. immutable_var_with_data = Var(
  1149. _js_expr="consequences",
  1150. _var_data=VarData(
  1151. imports={
  1152. "react": [ImportVar(tag="useRef")],
  1153. "utils": [ImportVar(tag="useEffect")],
  1154. }
  1155. ),
  1156. )
  1157. f_string = f"foo{original_var_with_data}bar{immutable_var_with_data}baz"
  1158. string_concat = LiteralStringVar.create(
  1159. f_string,
  1160. _var_data=VarData(
  1161. hooks={"const state = useContext(StateContexts.state)": None}
  1162. ),
  1163. )
  1164. assert str(string_concat) == '("fooimaginationbar"+consequences+"baz")'
  1165. assert isinstance(string_concat, ConcatVarOperation)
  1166. assert string_concat._get_all_var_data() == VarData(
  1167. state="fear",
  1168. imports={
  1169. "react": [ImportVar(tag="useRef")],
  1170. "utils": [ImportVar(tag="useEffect")],
  1171. },
  1172. hooks={"const state = useContext(StateContexts.state)": None},
  1173. )
  1174. var = Var(_js_expr="var", _var_type=str)
  1175. myvar = Var(_js_expr="myvar", _var_type=int)._var_set_state("state")
  1176. x = Var(_js_expr="x", _var_type=str)
  1177. @pytest.mark.parametrize(
  1178. "out, expected",
  1179. [
  1180. (f"{var}", f"<reflex.Var>{hash(var)}</reflex.Var>var"),
  1181. (
  1182. f"testing f-string with {myvar}",
  1183. f"testing f-string with <reflex.Var>{hash(myvar)}</reflex.Var>state.myvar",
  1184. ),
  1185. (
  1186. f"testing local f-string {x}",
  1187. f"testing local f-string <reflex.Var>{hash(x)}</reflex.Var>x",
  1188. ),
  1189. ],
  1190. )
  1191. def test_fstrings(out, expected):
  1192. assert out == expected
  1193. @pytest.mark.parametrize(
  1194. ("value", "expect_state"),
  1195. [
  1196. ([1], ""),
  1197. ({"a": 1}, ""),
  1198. ([LiteralVar.create(1)._var_set_state("foo")], "foo"),
  1199. ({"a": LiteralVar.create(1)._var_set_state("foo")}, "foo"),
  1200. ],
  1201. )
  1202. def test_extract_state_from_container(value, expect_state):
  1203. """Test that _var_state is extracted from containers containing BaseVar.
  1204. Args:
  1205. value: The value to create a var from.
  1206. expect_state: The expected state.
  1207. """
  1208. var_data = LiteralVar.create(value)._get_all_var_data()
  1209. var_state = var_data.state if var_data else ""
  1210. assert var_state == expect_state
  1211. @pytest.mark.parametrize(
  1212. "value",
  1213. [
  1214. "var",
  1215. "\nvar",
  1216. ],
  1217. )
  1218. def test_fstring_roundtrip(value):
  1219. """Test that f-string roundtrip carries state.
  1220. Args:
  1221. value: The value to create a Var from.
  1222. """
  1223. var = Var(_js_expr=value)._var_set_state("state")
  1224. rt_var = LiteralVar.create(f"{var}")
  1225. assert var._var_state == rt_var._var_state
  1226. assert str(rt_var) == str(var)
  1227. @pytest.mark.parametrize(
  1228. "var",
  1229. [
  1230. Var(_js_expr="var", _var_type=int).guess_type(),
  1231. Var(_js_expr="var", _var_type=float).guess_type(),
  1232. Var(_js_expr="var", _var_type=str).guess_type(),
  1233. Var(_js_expr="var", _var_type=bool).guess_type(),
  1234. Var(_js_expr="var", _var_type=None).guess_type(),
  1235. ],
  1236. )
  1237. def test_unsupported_types_for_reverse(var):
  1238. """Test that unsupported types for reverse throw a type error.
  1239. Args:
  1240. var: The base var.
  1241. """
  1242. with pytest.raises(AttributeError) as err:
  1243. var.reverse()
  1244. assert err.value.args[0] == "'Var' object has no attribute 'reverse'"
  1245. @pytest.mark.parametrize(
  1246. "var",
  1247. [
  1248. Var(_js_expr="var", _var_type=int).guess_type(),
  1249. Var(_js_expr="var", _var_type=float).guess_type(),
  1250. Var(_js_expr="var", _var_type=bool).guess_type(),
  1251. Var(_js_expr="var", _var_type=type(None)).guess_type(),
  1252. ],
  1253. )
  1254. def test_unsupported_types_for_contains(var: Var):
  1255. """Test that unsupported types for contains throw a type error.
  1256. Args:
  1257. var: The base var.
  1258. """
  1259. with pytest.raises(AttributeError) as err:
  1260. assert var.contains(1) # pyright: ignore [reportAttributeAccessIssue]
  1261. assert err.value.args[0] == "'Var' object has no attribute 'contains'"
  1262. @pytest.mark.parametrize(
  1263. "other",
  1264. [
  1265. Var(_js_expr="other", _var_type=int).guess_type(),
  1266. Var(_js_expr="other", _var_type=float).guess_type(),
  1267. Var(_js_expr="other", _var_type=bool).guess_type(),
  1268. Var(_js_expr="other", _var_type=list).guess_type(),
  1269. Var(_js_expr="other", _var_type=dict).guess_type(),
  1270. Var(_js_expr="other", _var_type=tuple).guess_type(),
  1271. Var(_js_expr="other", _var_type=set).guess_type(),
  1272. ],
  1273. )
  1274. def test_unsupported_types_for_string_contains(other):
  1275. with pytest.raises(TypeError) as err:
  1276. assert Var(_js_expr="var").to(str).contains(other)
  1277. assert (
  1278. err.value.args[0]
  1279. == f"Invalid argument other provided to argument 0 in var operation. Expected <class 'str'> but got {other._var_type}."
  1280. )
  1281. def test_unsupported_default_contains():
  1282. with pytest.raises(TypeError) as err:
  1283. assert 1 in Var(_js_expr="var", _var_type=str).guess_type() # pyright: ignore [reportOperatorIssue]
  1284. assert (
  1285. err.value.args[0]
  1286. == "'in' operator not supported for Var types, use Var.contains() instead."
  1287. )
  1288. @pytest.mark.parametrize(
  1289. "operand1_var,operand2_var,operators",
  1290. [
  1291. (
  1292. LiteralVar.create(10),
  1293. LiteralVar.create(5),
  1294. [
  1295. "+",
  1296. "-",
  1297. "/",
  1298. "//",
  1299. "*",
  1300. "%",
  1301. "**",
  1302. ">",
  1303. "<",
  1304. "<=",
  1305. ">=",
  1306. "|",
  1307. "&",
  1308. ],
  1309. ),
  1310. (
  1311. LiteralVar.create(10.5),
  1312. LiteralVar.create(5),
  1313. ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="],
  1314. ),
  1315. (
  1316. LiteralVar.create(5),
  1317. LiteralVar.create(True),
  1318. [
  1319. "+",
  1320. "-",
  1321. "/",
  1322. "//",
  1323. "*",
  1324. "%",
  1325. "**",
  1326. ">",
  1327. "<",
  1328. "<=",
  1329. ">=",
  1330. "|",
  1331. "&",
  1332. ],
  1333. ),
  1334. (
  1335. LiteralVar.create(10.5),
  1336. LiteralVar.create(5.5),
  1337. ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="],
  1338. ),
  1339. (
  1340. LiteralVar.create(10.5),
  1341. LiteralVar.create(True),
  1342. ["+", "-", "/", "//", "*", "%", "**", ">", "<", "<=", ">="],
  1343. ),
  1344. (LiteralVar.create("10"), LiteralVar.create("5"), ["+", ">", "<", "<=", ">="]),
  1345. (
  1346. LiteralVar.create([10, 20]),
  1347. LiteralVar.create([5, 6]),
  1348. ["+", ">", "<", "<=", ">="],
  1349. ),
  1350. (LiteralVar.create([10, 20]), LiteralVar.create(5), ["*"]),
  1351. (LiteralVar.create([10, 20]), LiteralVar.create(True), ["*"]),
  1352. (
  1353. LiteralVar.create(True),
  1354. LiteralVar.create(True),
  1355. [
  1356. "+",
  1357. "-",
  1358. "/",
  1359. "//",
  1360. "*",
  1361. "%",
  1362. "**",
  1363. ">",
  1364. "<",
  1365. "<=",
  1366. ">=",
  1367. "|",
  1368. "&",
  1369. ],
  1370. ),
  1371. ],
  1372. )
  1373. def test_valid_var_operations(operand1_var: Var, operand2_var, operators: List[str]):
  1374. """Test that operations do not raise a TypeError.
  1375. Args:
  1376. operand1_var: left operand.
  1377. operand2_var: right operand.
  1378. operators: list of supported operators.
  1379. """
  1380. for operator in operators:
  1381. print(
  1382. "testing",
  1383. operator,
  1384. "on",
  1385. operand1_var,
  1386. operand2_var,
  1387. " of types",
  1388. type(operand1_var),
  1389. type(operand2_var),
  1390. )
  1391. eval(f"operand1_var {operator} operand2_var")
  1392. eval(f"operand2_var {operator} operand1_var")
  1393. @pytest.mark.parametrize(
  1394. "operand1_var,operand2_var,operators",
  1395. [
  1396. (
  1397. LiteralVar.create(10),
  1398. LiteralVar.create(5),
  1399. [
  1400. "^",
  1401. "<<",
  1402. ">>",
  1403. ],
  1404. ),
  1405. (
  1406. LiteralVar.create(10.5),
  1407. LiteralVar.create(5),
  1408. [
  1409. "^",
  1410. "<<",
  1411. ">>",
  1412. ],
  1413. ),
  1414. (
  1415. LiteralVar.create(10.5),
  1416. LiteralVar.create(True),
  1417. [
  1418. "^",
  1419. "<<",
  1420. ">>",
  1421. ],
  1422. ),
  1423. (
  1424. LiteralVar.create(10.5),
  1425. LiteralVar.create(5.5),
  1426. [
  1427. "^",
  1428. "<<",
  1429. ">>",
  1430. ],
  1431. ),
  1432. (
  1433. LiteralVar.create("10"),
  1434. LiteralVar.create("5"),
  1435. [
  1436. "-",
  1437. "/",
  1438. "//",
  1439. "*",
  1440. "%",
  1441. "**",
  1442. "^",
  1443. "<<",
  1444. ">>",
  1445. ],
  1446. ),
  1447. (
  1448. LiteralVar.create([10, 20]),
  1449. LiteralVar.create([5, 6]),
  1450. [
  1451. "-",
  1452. "/",
  1453. "//",
  1454. "*",
  1455. "%",
  1456. "**",
  1457. "^",
  1458. "<<",
  1459. ">>",
  1460. ],
  1461. ),
  1462. (
  1463. LiteralVar.create([10, 20]),
  1464. LiteralVar.create(5),
  1465. [
  1466. "+",
  1467. "-",
  1468. "/",
  1469. "//",
  1470. "%",
  1471. "**",
  1472. ">",
  1473. "<",
  1474. "<=",
  1475. ">=",
  1476. "^",
  1477. "<<",
  1478. ">>",
  1479. ],
  1480. ),
  1481. (
  1482. LiteralVar.create([10, 20]),
  1483. LiteralVar.create(True),
  1484. [
  1485. "+",
  1486. "-",
  1487. "/",
  1488. "//",
  1489. "%",
  1490. "**",
  1491. ">",
  1492. "<",
  1493. "<=",
  1494. ">=",
  1495. "^",
  1496. "<<",
  1497. ">>",
  1498. ],
  1499. ),
  1500. (
  1501. LiteralVar.create([10, 20]),
  1502. LiteralVar.create("5"),
  1503. [
  1504. "-",
  1505. "/",
  1506. "//",
  1507. "*",
  1508. "%",
  1509. "**",
  1510. "^",
  1511. "<<",
  1512. ">>",
  1513. ],
  1514. ),
  1515. (
  1516. LiteralVar.create([10, 20]),
  1517. LiteralVar.create({"key": "value"}),
  1518. [
  1519. "+",
  1520. "-",
  1521. "/",
  1522. "//",
  1523. "*",
  1524. "%",
  1525. "**",
  1526. ">",
  1527. "<",
  1528. "<=",
  1529. ">=",
  1530. "^",
  1531. "<<",
  1532. ">>",
  1533. ],
  1534. ),
  1535. (
  1536. LiteralVar.create([10, 20]),
  1537. LiteralVar.create(5.5),
  1538. [
  1539. "+",
  1540. "-",
  1541. "/",
  1542. "//",
  1543. "*",
  1544. "%",
  1545. "**",
  1546. ">",
  1547. "<",
  1548. "<=",
  1549. ">=",
  1550. "^",
  1551. "<<",
  1552. ">>",
  1553. ],
  1554. ),
  1555. (
  1556. LiteralVar.create({"key": "value"}),
  1557. LiteralVar.create({"another_key": "another_value"}),
  1558. [
  1559. "+",
  1560. "-",
  1561. "/",
  1562. "//",
  1563. "*",
  1564. "%",
  1565. "**",
  1566. ">",
  1567. "<",
  1568. "<=",
  1569. ">=",
  1570. "^",
  1571. "<<",
  1572. ">>",
  1573. ],
  1574. ),
  1575. (
  1576. LiteralVar.create({"key": "value"}),
  1577. LiteralVar.create(5),
  1578. [
  1579. "+",
  1580. "-",
  1581. "/",
  1582. "//",
  1583. "*",
  1584. "%",
  1585. "**",
  1586. ">",
  1587. "<",
  1588. "<=",
  1589. ">=",
  1590. "^",
  1591. "<<",
  1592. ">>",
  1593. ],
  1594. ),
  1595. (
  1596. LiteralVar.create({"key": "value"}),
  1597. LiteralVar.create(True),
  1598. [
  1599. "+",
  1600. "-",
  1601. "/",
  1602. "//",
  1603. "*",
  1604. "%",
  1605. "**",
  1606. ">",
  1607. "<",
  1608. "<=",
  1609. ">=",
  1610. "^",
  1611. "<<",
  1612. ">>",
  1613. ],
  1614. ),
  1615. (
  1616. LiteralVar.create({"key": "value"}),
  1617. LiteralVar.create(5.5),
  1618. [
  1619. "+",
  1620. "-",
  1621. "/",
  1622. "//",
  1623. "*",
  1624. "%",
  1625. "**",
  1626. ">",
  1627. "<",
  1628. "<=",
  1629. ">=",
  1630. "^",
  1631. "<<",
  1632. ">>",
  1633. ],
  1634. ),
  1635. (
  1636. LiteralVar.create({"key": "value"}),
  1637. LiteralVar.create("5"),
  1638. [
  1639. "+",
  1640. "-",
  1641. "/",
  1642. "//",
  1643. "*",
  1644. "%",
  1645. "**",
  1646. ">",
  1647. "<",
  1648. "<=",
  1649. ">=",
  1650. "^",
  1651. "<<",
  1652. ">>",
  1653. ],
  1654. ),
  1655. ],
  1656. )
  1657. def test_invalid_var_operations(operand1_var: Var, operand2_var, operators: List[str]):
  1658. for operator in operators:
  1659. print(f"testing {operator} on {operand1_var!s} and {operand2_var!s}")
  1660. with pytest.raises(TypeError):
  1661. print(eval(f"operand1_var {operator} operand2_var"))
  1662. with pytest.raises(TypeError):
  1663. print(eval(f"operand2_var {operator} operand1_var"))
  1664. @pytest.mark.parametrize(
  1665. "var, expected",
  1666. [
  1667. (LiteralVar.create("string_value"), '"string_value"'),
  1668. (LiteralVar.create(1), "1"),
  1669. (LiteralVar.create([1, 2, 3]), "[1, 2, 3]"),
  1670. (LiteralVar.create({"foo": "bar"}), '({ ["foo"] : "bar" })'),
  1671. (
  1672. LiteralVar.create(ATestState.value),
  1673. f"{ATestState.get_full_name()}.value",
  1674. ),
  1675. (
  1676. LiteralVar.create(f"{ATestState.value} string"),
  1677. f'({ATestState.get_full_name()}.value+" string")',
  1678. ),
  1679. (
  1680. LiteralVar.create(ATestState.dict_val),
  1681. f"{ATestState.get_full_name()}.dict_val",
  1682. ),
  1683. ],
  1684. )
  1685. def test_var_name_unwrapped(var, expected):
  1686. assert str(var) == expected
  1687. def cv_fget(state: BaseState) -> int:
  1688. return 1
  1689. @pytest.mark.parametrize(
  1690. "deps,expected",
  1691. [
  1692. (["a"], {None: {"a"}}),
  1693. (["b"], {None: {"b"}}),
  1694. ([ComputedVar(fget=cv_fget)], {None: {"cv_fget"}}),
  1695. ],
  1696. )
  1697. def test_computed_var_deps(deps: List[Union[str, Var]], expected: Set[str]):
  1698. @computed_var(deps=deps)
  1699. def test_var(state) -> int:
  1700. return 1
  1701. assert test_var._static_deps == expected
  1702. @pytest.mark.parametrize(
  1703. "deps",
  1704. [
  1705. [""],
  1706. [1],
  1707. ["", "abc"],
  1708. ],
  1709. )
  1710. def test_invalid_computed_var_deps(deps: List):
  1711. with pytest.raises(TypeError):
  1712. @computed_var(deps=deps)
  1713. def test_var(state) -> int:
  1714. return 1
  1715. def test_to_string_operation():
  1716. class Email(str): ...
  1717. class TestState(BaseState):
  1718. optional_email: Optional[Email] = None
  1719. email: Email = Email("test@reflex.dev")
  1720. assert (
  1721. str(TestState.optional_email) == f"{TestState.get_full_name()}.optional_email"
  1722. )
  1723. my_state = TestState()
  1724. assert my_state.optional_email is None
  1725. assert my_state.email == "test@reflex.dev"
  1726. assert cast(Var, TestState.email)._var_type == Email
  1727. assert cast(Var, TestState.optional_email)._var_type == Optional[Email]
  1728. single_var = Var.create(Email())
  1729. assert single_var._var_type == Email
  1730. @pytest.mark.asyncio
  1731. async def test_async_computed_var():
  1732. side_effect_counter = 0
  1733. class AsyncComputedVarState(BaseState):
  1734. v: int = 1
  1735. @computed_var(cache=True)
  1736. async def async_computed_var(self) -> int:
  1737. nonlocal side_effect_counter
  1738. side_effect_counter += 1
  1739. return self.v + 1
  1740. my_state = AsyncComputedVarState()
  1741. assert await my_state.async_computed_var == 2
  1742. assert await my_state.async_computed_var == 2
  1743. my_state.v = 2
  1744. assert await my_state.async_computed_var == 3
  1745. assert await my_state.async_computed_var == 3
  1746. assert side_effect_counter == 2
  1747. def test_var_data_hooks():
  1748. var_data_str = VarData(hooks="what")
  1749. var_data_list = VarData(hooks=["what"])
  1750. var_data_dict = VarData(hooks={"what": None})
  1751. assert var_data_str == var_data_list == var_data_dict
  1752. var_data_list_multiple = VarData(hooks=["what", "whot"])
  1753. var_data_dict_multiple = VarData(hooks={"what": None, "whot": None})
  1754. assert var_data_list_multiple == var_data_dict_multiple
  1755. def test_var_data_with_hooks_value():
  1756. var_data = VarData(hooks={"what": VarData(hooks={"whot": VarData(hooks="whott")})})
  1757. assert var_data == VarData(hooks=["what", "whot", "whott"])
  1758. def test_str_var_in_components(mocker):
  1759. class StateWithVar(rx.State):
  1760. field: int = 1
  1761. mocker.patch(
  1762. "reflex.components.base.bare.get_performance_mode",
  1763. return_value=PerformanceMode.RAISE,
  1764. )
  1765. with pytest.raises(ValueError):
  1766. rx.vstack(
  1767. str(StateWithVar.field),
  1768. )
  1769. mocker.patch(
  1770. "reflex.components.base.bare.get_performance_mode",
  1771. return_value=PerformanceMode.OFF,
  1772. )
  1773. rx.vstack(
  1774. str(StateWithVar.field),
  1775. )