test_var.py 50 KB

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