Skip to content

Commit a4a1f2f

Browse files
committed
Refactor out shared script checker util
Needed by system_tests, so need to be in src/
1 parent 4f9e236 commit a4a1f2f

File tree

3 files changed

+110
-74
lines changed

3 files changed

+110
-74
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
This private module exists to allow testing utility code to be shared between genie_python's
3+
unit tests, and the external system tests.
4+
"""
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
import tempfile
3+
from types import TracebackType
4+
from typing import IO
5+
6+
from genie_python.genie_script_checker import ScriptChecker
7+
8+
9+
def write_to_temp_file(message: list[str], suffix: str = "", dir: str = "") -> IO[bytes]:
10+
"""
11+
write to temporary file for test check_script
12+
13+
Args:
14+
message: message to write to file
15+
suffix: filename suffix
16+
dir: directory to write into
17+
18+
Returns:
19+
temporary file
20+
"""
21+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, dir=dir)
22+
for line in message:
23+
temp_file.write(line.encode("utf-8"))
24+
temp_file.close()
25+
return temp_file
26+
27+
28+
class CreateTempScriptAndReturnErrors:
29+
def __init__(
30+
self,
31+
script_checker: ScriptChecker,
32+
machine: str,
33+
script: list[str],
34+
warnings_as_error: bool = True,
35+
no_pyright: bool = False,
36+
no_pylint: bool = False,
37+
dir: str = "",
38+
) -> None:
39+
self.script = script
40+
self.machine = machine
41+
self.dir = dir
42+
self.warnings_as_error = warnings_as_error
43+
self.no_pyright = no_pyright
44+
self.no_pylint = no_pylint
45+
self.script_checker = script_checker
46+
47+
def __enter__(self) -> list[str]:
48+
self.temp_script_file = write_to_temp_file(self.script, dir=self.dir)
49+
return self.script_checker.check_script(
50+
self.temp_script_file.name,
51+
self.machine,
52+
warnings_as_error=self.warnings_as_error,
53+
no_pyright=self.no_pyright,
54+
no_pylint=self.no_pylint,
55+
)
56+
57+
def __exit__(
58+
self,
59+
exc_type: type[BaseException] | None,
60+
exc_value: BaseException | None,
61+
exc_traceback: TracebackType,
62+
) -> None:
63+
os.unlink(self.temp_script_file.name)

tests/test_script_checker.py

Lines changed: 43 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -26,52 +26,13 @@
2626

2727
from genie_python.genie_epics_api import API
2828
from genie_python.genie_script_checker import ScriptChecker
29-
30-
31-
def write_to_file(message, suffix="", dir=""):
32-
"""
33-
write to temporary file for test check_script
34-
:param message: message to write to file
35-
:return: returns temporary file
36-
"""
37-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=suffix, dir=dir)
38-
for line in message:
39-
temp_file.write(line.encode("utf-8"))
40-
temp_file.close()
41-
return temp_file
29+
from genie_python.testing_utils.script_checker import (
30+
CreateTempScriptAndReturnErrors,
31+
write_to_temp_file,
32+
)
4233

4334

4435
class TestScriptChecker(unittest.TestCase):
45-
class _CreateTempScriptAndReturnErrors:
46-
def __init__(
47-
self,
48-
script_checker,
49-
script,
50-
warnings_as_error=True,
51-
no_pyright=False,
52-
no_pylint=False,
53-
dir="",
54-
):
55-
self.script = script
56-
self.dir = dir
57-
self.warnings_as_error = warnings_as_error
58-
self.no_pyright = no_pyright
59-
self.no_pylint = no_pylint
60-
self.script_checker = script_checker
61-
62-
def __enter__(self):
63-
self.temp_script_file = write_to_file(self.script, dir=self.dir)
64-
return self.script_checker.checker.check_script(
65-
self.temp_script_file.name,
66-
self.script_checker.machine,
67-
warnings_as_error=self.warnings_as_error,
68-
no_pyright=self.no_pyright,
69-
no_pylint=self.no_pylint,
70-
)
71-
72-
def __exit__(self, exc_type, exc_value, exc_traceback):
73-
os.unlink(self.temp_script_file.name)
74-
7536
def setUp(self):
7637
self.checker = ScriptChecker()
7738
self.api = API("", None)
@@ -86,7 +47,7 @@ def tearDown(self):
8647

8748
def assertSymbolsDefined(self, script_lines, expected_symbols):
8849
dir_path = tempfile.mkdtemp()
89-
write_to_file(script_lines, suffix=".py", dir=dir_path)
50+
write_to_temp_file(script_lines, suffix=".py", dir=dir_path)
9051
result = self.checker.get_inst_attributes(dir_path)
9152
shutil.rmtree(dir_path)
9253
self.assertEqual(result, expected_symbols)
@@ -98,15 +59,15 @@ def test_GIVEN_end_without_brackets_WHEN_check_THEN_error_message(self):
9859
" g.end\n",
9960
]
10061

101-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
62+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
10263
self.assertEqual(
10364
errors, ["W: 4: Statement seems to have no effect (pointless-statement)"]
10465
)
10566

10667
def test_GIVEN_end_as_start_of_another_word_WHEN_check_THEN_no_error_message(self):
10768
script_lines = ["from genie_python import genie as g\n" "def test():\n", " endAngle = 1"]
10869

109-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
70+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
11071
self.assertEqual(errors, [])
11172

11273
def test_GIVEN_end_as_end_of_another_word_WHEN_check_THEN_no_error_message(self):
@@ -115,13 +76,15 @@ def test_GIVEN_end_as_end_of_another_word_WHEN_check_THEN_no_error_message(self)
11576
" angle_end = 1",
11677
]
11778

118-
with self._CreateTempScriptAndReturnErrors(self, script_lines, no_pyright=True) as errors:
79+
with CreateTempScriptAndReturnErrors(
80+
self.checker, self.machine, script_lines, no_pyright=True
81+
) as errors:
11982
self.assertEqual(errors, [])
12083

12184
def test_GIVEN_end_without_brackets_at_start_of_line_WHEN_check_THEN_error_message(self):
12285
script_lines = ["from genie_python import genie as g\n" "def test():\n" " g.end"]
12386

124-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
87+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
12588
self.assertEqual(
12689
errors, ["W: 3: Statement seems to have no effect (pointless-statement)"]
12790
)
@@ -131,21 +94,21 @@ def test_GIVEN_end_without_brackets_on_line_with_fn_with_brackets_WHEN_check_THE
13194
):
13295
script_lines = ["from genie_python import genie as g\n" "g.begin(); g.end "]
13396

134-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
97+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
13598
self.assertEqual(
13699
errors, ["W: 2: Statement seems to have no effect (pointless-statement)"]
137100
)
138101

139102
def test_GIVEN_end_in_string_without_brackets_WHEN_check_THEN_no_message(self):
140103
script_lines = ["def test():\n" ' " a string containing end "']
141104

142-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
105+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
143106
self.assertEqual(errors, [])
144107

145108
def test_GIVEN_end_in_comment_without_brackets_WHEN_check_THEN_no_message(self):
146109
script_lines = ["def test():\n", ' "stuff" # end "']
147110

148-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
111+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
149112
self.assertEqual(errors, [])
150113

151114
def test_GIVEN_g_assignment_WHEN_check_THEN_warning_message(self):
@@ -229,21 +192,21 @@ def test_GIVEN_g_assignment_with_space_between_assignment_and_value_WHEN_check_T
229192
def test_GIVEN_g_assignment_with_2_symbols_before_number_WHEN_check_THEN_warning_message(self):
230193
script_lines = ["from genie_python import genie as g\n", "def test():\n", " g+=12"]
231194

232-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
195+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
233196
self.assertEqual(errors, ["W: 3: 'g' assignment in line 3"])
234197

235198
def test_GIVEN_variable_assignment_with_g__WHEN_check_THEN_no_message(self):
236199
script_lines = ["going=13"]
237200

238-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
201+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
239202
self.assertEqual(errors, [])
240203

241204
def test_GIVEN_function_with_g_WHEN_check_THEN_warn_user(self):
242205
script_lines = [
243206
"from genie_python import genie as g\n" "def test():\n" " g.test_function()\n"
244207
]
245208

246-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
209+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
247210
self.assertEqual(
248211
errors,
249212
["E: 3: Module 'genie_python.genie' has no 'test_function' member (no-member)"],
@@ -255,7 +218,7 @@ def test_GIVEN_2_g_assignments_WHEN_check_THEN_warning_message(self):
255218
" g=17",
256219
]
257220

258-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
221+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
259222
self.assertEqual(
260223
errors, ["W: 3: 'g' assignment in line 3", "W: 4: 'g' assignment in line 4"]
261224
)
@@ -265,7 +228,7 @@ def test_GIVEN_g_non_existing_command_WHEN_call_THEN_error_message(self):
265228
"from genie_python import genie as g\n" "def test():\n" " g.aitfor_time(10)"
266229
]
267230

268-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
231+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
269232
self.assertEqual(
270233
errors,
271234
[
@@ -409,7 +372,7 @@ def test_GIVEN_invalid_python_expr_WHEN_call_check_THEN_error(self):
409372
script_lines = ["my_expr ="]
410373
expected = "E: 1: Parsing failed: 'invalid syntax"
411374

412-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
375+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
413376
self.assertTrue(
414377
errors[0].startswith(expected),
415378
f"Result was {errors}, expected first line to start with {expected}",
@@ -418,20 +381,20 @@ def test_GIVEN_invalid_python_expr_WHEN_call_check_THEN_error(self):
418381
def test_GIVEN_valid_python_expr_WHEN_call_check_THEN_no_error(self):
419382
script_lines = ["my_expr = {}"]
420383

421-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
384+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
422385
self.assertEqual(errors, [])
423386

424387
def test_GIVEN_valid_python_class_WHEN_call_check_THEN_no_error(self):
425388
script_lines = ["class MyClass():", "pass"]
426389

427-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
390+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
428391
self.assertEqual(errors, [])
429392

430393
def test_GIVEN_invalid_python_class_WHEN_call_check_THEN_error(self):
431394
script_lines = ["class MyClass():"]
432395
expected = "E: 1: Parsing failed: 'expected an indented block"
433396

434-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
397+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
435398
self.assertTrue(
436399
errors[0].startswith(expected),
437400
f'Result was "{errors[0]}", expected first line to start with "{expected}"',
@@ -476,15 +439,15 @@ def test_GIVEN_invalid_range_THEN_pyright_throws_exception(self):
476439

477440
expected = "E: 2: Argument of type"
478441

479-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
442+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
480443
self.assertTrue(errors[0].startswith(expected))
481444

482445
def test_GIVEN_invalid_var_type_THEN_pyright_throws_exception(self):
483446
script_lines = ["c: int | float = 3.4\n", "c = None\n"]
484447

485448
expected = "not assignable"
486449

487-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
450+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
488451
self.assertTrue(expected in errors[0])
489452

490453
def test_GIVEN_new_directory_WHEN_pyright_script_checker_called_THEN_pyright_json_created_then_destroyed_after_use(
@@ -512,7 +475,7 @@ def test_GIVEN_invalid_genie_script_WHEN_pyright_script_checker_called_THEN_pyri
512475

513476
expected = "E: 3: Argument of type"
514477

515-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
478+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
516479
self.assertTrue(errors[0].startswith(expected))
517480

518481
# Test that if linter fails then pyright does not run
@@ -530,7 +493,7 @@ def test_GIVEN_that_pylint_fails_THEN_pyright_is_not_ran(self):
530493
]
531494
expected = "E: 1: Parsing failed: 'invalid syntax"
532495

533-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
496+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
534497
self.assertTrue(
535498
errors[0].startswith(expected) and len(errors) == 1
536499
) # Will be count of 2 if pyright still runs
@@ -546,15 +509,17 @@ def test_GIVEN_invalid_genie_script_WHEN_pyright_script_checker_called_with_no_p
546509
" g.begin(1,2,3,4,5,6,7,8,9)\n",
547510
]
548511

549-
with self._CreateTempScriptAndReturnErrors(self, script_lines, no_pyright=True) as errors:
512+
with CreateTempScriptAndReturnErrors(
513+
self.checker, self.machine, script_lines, no_pyright=True
514+
) as errors:
550515
self.assertEqual(errors, [])
551516

552517
# Pyright config checks
553518

554519
def test_GIVEN_unused_variable_in_script_WHEN_pyright_script_checker_called_THEN_no_error(self):
555520
script_lines = ["a = 10\n"]
556521

557-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
522+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
558523
self.assertEqual(errors, [])
559524

560525
def test_GIVEN_trying_to_access_member_of_optional_type_var_WHEN_pyright_script_checker_called_THEN_no_error(
@@ -566,7 +531,7 @@ def test_GIVEN_trying_to_access_member_of_optional_type_var_WHEN_pyright_script_
566531
" a.upper()\n",
567532
]
568533

569-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
534+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
570535
self.assertEqual(errors, [])
571536

572537
def test_GIVEN_trying_to_index_var_of_optional_type_WHEN_pyright_script_checker_called_THEN_no_error(
@@ -578,7 +543,7 @@ def test_GIVEN_trying_to_index_var_of_optional_type_WHEN_pyright_script_checker_
578543
" return elements[0]\n",
579544
]
580545

581-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
546+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
582547
self.assertEqual(errors, [])
583548

584549
def test_GIVEN_trying_to_call_var_of_optional_type_WHEN_pyright_script_checker_called_THEN_no_error(
@@ -590,7 +555,7 @@ def test_GIVEN_trying_to_call_var_of_optional_type_WHEN_pyright_script_checker_c
590555
" callback()\n",
591556
]
592557

593-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
558+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
594559
self.assertEqual(errors, [])
595560

596561
def test_GIVEN_trying_to_iterate_over_var_of_optional_type_WHEN_pyright_script_checker_called_THEN_no_error(
@@ -602,35 +567,39 @@ def test_GIVEN_trying_to_iterate_over_var_of_optional_type_WHEN_pyright_script_c
602567
" pass\n",
603568
]
604569

605-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
570+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
606571
self.assertEqual(errors, [])
607572

608573
def test_GIVEN_trying_to_define_function_with_none_type_args_type_WHEN_pyright_script_checker_called_THEN_no_error(
609574
self,
610575
):
611576
script_lines = ["def none_func(arg: int = None):\n" " print(arg)\n"]
612577

613-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
578+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
614579
self.assertEqual(errors, [])
615580

616581
def test_GIVEN_trying_to_use_optional_operand__WHEN_pyright_script_checker_called_THEN_no_error(
617582
self,
618583
):
619584
script_lines = ["def none_func(arg1: int, arg2: int = None):\n" " print(arg2 + arg1)\n"]
620585

621-
with self._CreateTempScriptAndReturnErrors(self, script_lines) as errors:
586+
with CreateTempScriptAndReturnErrors(self.checker, self.machine, script_lines) as errors:
622587
self.assertEqual(errors, [])
623588

624589
def test_GIVEN_trying_to_use_undefined_variable_WHEN_pyright_script_checker_called_THEN_no_error(
625590
self,
626591
):
627592
script_lines = ["def func():\n" " print(arg)\n"]
628593

629-
with self._CreateTempScriptAndReturnErrors(self, script_lines, no_pylint=True) as errors:
594+
with CreateTempScriptAndReturnErrors(
595+
self.checker, self.machine, script_lines, no_pylint=True
596+
) as errors:
630597
self.assertEqual(errors, [])
631598

632599
def test_GIVEN_unused_import_WHEN_pyright_script_checker_called_THEN_no_error(self):
633600
script_lines = ["import genie\n"]
634601

635-
with self._CreateTempScriptAndReturnErrors(self, script_lines, no_pylint=True) as errors:
602+
with CreateTempScriptAndReturnErrors(
603+
self.checker, self.machine, script_lines, no_pylint=True
604+
) as errors:
636605
self.assertEqual(errors, [])

0 commit comments

Comments
 (0)