psychopy 2024.1.2__py3-none-any.whl → 2024.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (23) hide show
  1. psychopy/__init__.py +2 -2
  2. psychopy/experiment/components/mouse/__init__.py +12 -0
  3. psychopy/experiment/routines/counterbalance/__init__.py +1 -1
  4. psychopy/hardware/keyboard.py +1 -1
  5. psychopy/tests/test_experiment/test_components/__init__.py +1 -1
  6. psychopy/tests/test_experiment/test_components/{test_ButtonBox.py → test_ButtonBoxComponent.py} +9 -27
  7. psychopy/tests/test_experiment/test_components/{test_Code.py → test_CodeComponent.py} +8 -20
  8. psychopy/tests/test_experiment/test_components/test_GratingComponent.py +7 -0
  9. psychopy/tests/test_experiment/test_components/test_ImageComponent.py +8 -0
  10. psychopy/tests/test_experiment/test_components/{test_Mouse.py → test_MouseComponent.py} +44 -57
  11. psychopy/tests/test_experiment/test_components/{test_Polygon.py → test_PolygonComponent.py} +9 -25
  12. psychopy/tests/test_experiment/test_components/{test_ResourceManager.py → test_ResourceManagerComponent.py} +3 -13
  13. psychopy/tests/test_experiment/test_components/{test_Settings.py → test_SettingsComponent.py} +1 -3
  14. psychopy/tests/test_experiment/test_components/{test_Static.py → test_StaticComponent.py} +3 -12
  15. psychopy/tests/test_experiment/test_components/test_all_components.py +8 -66
  16. psychopy/tests/test_experiment/test_components/test_base_components.py +212 -125
  17. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/METADATA +2 -2
  18. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/RECORD +22 -21
  19. psychopy/tests/test_experiment/test_components/test_Image.py +0 -24
  20. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/WHEEL +0 -0
  21. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/entry_points.txt +0 -0
  22. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/licenses/AUTHORS.md +0 -0
  23. {psychopy-2024.1.2.dist-info → psychopy-2024.1.3.dist-info}/licenses/LICENSE +0 -0
psychopy/__init__.py CHANGED
@@ -12,14 +12,14 @@
12
12
  import os
13
13
  import sys
14
14
 
15
- __version__ = '2024.1.2'
15
+ __version__ = '2024.1.3'
16
16
  __license__ = 'GPL v3'
17
17
  __author__ = 'Open Science Tools Ltd'
18
18
  __author_email__ = 'support@opensciencetools.org'
19
19
  __maintainer_email__ = 'support@opensciencetools.org'
20
20
  __url__ = 'https://www.psychopy.org/'
21
21
  __download_url__ = 'https://github.com/psychopy/psychopy/releases/'
22
- __git_sha__ = '4e4ac7977'
22
+ __git_sha__ = 'a2e6ccdec'
23
23
  __build_platform__ = 'n/a'
24
24
 
25
25
  __all__ = ["gui", "misc", "visual", "core",
@@ -364,10 +364,22 @@ class MouseComponent(BaseComponent):
364
364
  buff.writeIndentedLines(code % self.params)
365
365
  buff.setIndentLevel(1, relative=True)
366
366
  dedent += 1
367
+ # keep track of whether something's been written
368
+ hasContent = False
369
+ # write code to check clickable stim, if there are any
367
370
  if self.params['clickable'].val:
368
371
  self._writeClickableObjectsCode(buff)
372
+ hasContent = True
373
+ # write code to check correct stim, if there are any
369
374
  if self.params['storeCorrect']:
370
375
  self._writeCorrectAnsCode(buff)
376
+ hasContent = True
377
+ # if current if statement has no content, add a pass
378
+ if not hasContent:
379
+ buff.writeIndentedLines(
380
+ "pass"
381
+ )
382
+
371
383
  return buff, dedent
372
384
 
373
385
  # No mouse tracking, end routine on any or valid click
@@ -43,7 +43,7 @@ class CounterbalanceRoutine(BaseStandaloneRoutine):
43
43
  self.params['specMode'] = Param(
44
44
  specMode, valType="str", inputType="choice", categ="Basic",
45
45
  allowedVals=["uniform", "file"],
46
- allowedLabels=[_translate("Num. groups"), _translate("Conditions file")],
46
+ allowedLabels=[_translate("Num. groups"), _translate("Conditions file (local only)")],
47
47
  label=_translate("Groups from..."),
48
48
  hint=_translate(
49
49
  "Specify groups using an Excel file (for fine tuned control), specify as a variable name, or specify a "
@@ -573,7 +573,7 @@ class KeyboardDevice(BaseResponseDevice, aliases=["keyboard"]):
573
573
  if message.type == "KEYBOARD_PRESS":
574
574
  # if message is from a key down event, make a new response
575
575
  response = KeyPress(code=message.char, tDown=message.time, name=message.key)
576
- response.rt = response.tDown
576
+ response.rt = response.tDown - self.clock.getLastResetTime()
577
577
  self._keysStillDown.append(response)
578
578
  else:
579
579
  # if message is from a key up event, alter existing response
@@ -1 +1 @@
1
- from .test_base_components import _TestBaseComponentsMixin, _TestDisabledMixin
1
+ from .test_base_components import BaseComponentTests
@@ -11,21 +11,14 @@ from psychopy.hardware.button import ButtonResponse
11
11
  from psychopy.experiment.routines import Routine
12
12
  from psychopy.experiment.components.buttonBox import ButtonBoxComponent
13
13
  from psychopy.experiment.components.code import CodeComponent
14
- from .test_base_components import _TestBaseComponentsMixin
14
+ from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests
15
+ from psychopy.hardware.button import ButtonBox
15
16
 
16
17
 
17
- class TestButtonBoxComponent(_TestBaseComponentsMixin):
18
- def setup_method(self):
19
- self.exp = Experiment()
20
- # make blank routine
21
- self.routine = Routine(name="testRoutine", exp=self.exp)
22
- self.exp.addRoutine("testRoutine", self.routine)
23
- self.exp.flow.addRoutine(self.routine, 0)
24
- # make component
25
- self.comp = ButtonBoxComponent(
26
- exp=self.exp, name="testPhotodiodeValidatorRoutine", parentName="testRoutine"
27
- )
28
-
18
+ class TestButtonBoxComponent(BaseComponentTests):
19
+ comp = ButtonBoxComponent
20
+ libraryClass = ButtonBox
21
+
29
22
  def test_values(self):
30
23
  """
31
24
  Test that a variety of different values work when run from Builder.
@@ -65,9 +58,8 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
65
58
  thisCase[keys[i]] = val
66
59
  # add case
67
60
  cases.append(thisCase)
68
-
69
- # make an experiment
70
- exp = Experiment()
61
+ # make minimal experiment just for this test
62
+ comp, rt, exp = self.make_minimal_experiment()
71
63
  # configure experiment
72
64
  exp.requireImport("ButtonResponse", importFrom="psychopy.hardware.button")
73
65
  exp.settings.params['Full-screen window'].val = False
@@ -79,18 +71,8 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
79
71
  resps = case.pop("resps")
80
72
  if not isinstance(resps, (list, tuple)):
81
73
  resps = [resps]
82
- # make a name
83
- name = f"rt{i}"
84
- # make a Routine
85
- rt = exp.addRoutine(name, Routine(name=name, exp=exp))
86
- exp.flow.addRoutine(rt, 0)
87
74
  # add timeout
88
75
  rt.settings.params['stopVal'].val = 0.2
89
- # make a Component
90
- comp = ButtonBoxComponent(
91
- exp, parentName=name, name=name + "_comp", **case
92
- )
93
- rt.addComponent(comp)
94
76
  # make a Code Component to send responses
95
77
  code = (
96
78
  "if frameN > 1:\n"
@@ -104,7 +86,7 @@ class TestButtonBoxComponent(_TestBaseComponentsMixin):
104
86
  name=comp.name, value=resp.value, channel=resp.channel
105
87
  )
106
88
  codeComp = CodeComponent(
107
- exp, parentName=name + "_code", eachFrame=code
89
+ exp, parentName=comp.name + "_code", eachFrame=code
108
90
  )
109
91
  rt.addComponent(codeComp)
110
92
  # save exp in temp directory
@@ -2,42 +2,30 @@ from pathlib import Path
2
2
  from tempfile import mkdtemp
3
3
 
4
4
  from psychopy import experiment
5
- from . import _TestDisabledMixin, _TestBaseComponentsMixin
5
+ from . import BaseComponentTests
6
6
  from psychopy.experiment.loops import TrialHandler
7
7
  from psychopy.experiment.routines import Routine
8
8
  from psychopy.experiment.components.code import CodeComponent
9
9
  from psychopy.tests.utils import TESTS_DATA_PATH
10
10
 
11
11
 
12
- class TestCodeComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
12
+ class TestCodeComponent(BaseComponentTests):
13
13
  """
14
14
  Test that Code coponents have the correct params and write as expected.
15
15
  """
16
+ comp = CodeComponent
16
17
 
17
18
  @classmethod
18
19
  def setup_class(cls):
19
- cls.exp = experiment.Experiment() # create once, not every test
20
20
  try:
21
21
  cls.tempDir = mkdtemp(dir=Path(__file__).root, prefix='psychopy-tests-app')
22
22
  except (PermissionError, OSError):
23
23
  # can't write to root on Linux
24
24
  cls.tempDir = mkdtemp(prefix='psychopy-tests-app')
25
25
 
26
- def setup_method(self):
27
- # Make blank experiment
28
- self.exp = experiment.Experiment()
29
- # Make blank routine
30
- self.routine = Routine(name="testRoutine", exp=self.exp)
31
- self.exp.addRoutine("testRoutine", self.routine)
32
- self.exp.flow.addRoutine(self.routine, 0)
33
- # Add loop around routine
34
- self.loop = TrialHandler(exp=self.exp, name="testLoop")
35
- self.exp.flow.addLoop(self.loop, 0, -1)
36
- # Make Mouse component
37
- self.comp = CodeComponent(exp=self.exp, parentName="testRoutine", name="testCode")
38
- self.routine.addComponent(self.comp)
39
-
40
26
  def test_all_code_component_tabs(self):
27
+ # make minimal experiment just for this test
28
+ comp, rt, exp = self.make_minimal_experiment()
41
29
  # Names of each tab in a Code component
42
30
  tabs = {
43
31
  'Before Experiment': '___before_experiment___',
@@ -50,11 +38,11 @@ class TestCodeComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
50
38
  # Add markers to component
51
39
  for paramName, marker in tabs.items():
52
40
  jsParamName = paramName.replace(" ", " JS ")
53
- self.comp.params[paramName].val = self.comp.params[jsParamName].val = marker
41
+ comp.params[paramName].val = comp.params[jsParamName].val = " = ".join([self.comp.__name__, comp.name, marker])
54
42
 
55
43
  # Write script
56
- pyScript = self.exp.writeScript(target="PsychoPy")
57
- jsScript = self.exp.writeScript(target="PsychoJS")
44
+ pyScript = exp.writeScript(target="PsychoPy")
45
+ jsScript = exp.writeScript(target="PsychoJS")
58
46
 
59
47
  # Check that code from each tab exists in compiled script
60
48
  for lang, script in {"Python": pyScript, "JS": jsScript}.items():
@@ -0,0 +1,7 @@
1
+ from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestLibraryClassMixin
2
+ from psychopy.experiment.components.grating import GratingComponent
3
+ from psychopy.visual import GratingStim
4
+
5
+ class TestGratingComponent(BaseComponentTests, _TestLibraryClassMixin):
6
+ comp = GratingComponent
7
+ libraryClass = GratingStim
@@ -0,0 +1,8 @@
1
+ from psychopy.experiment.components.image import ImageComponent
2
+ from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestDepthMixin, _TestLibraryClassMixin
3
+ from psychopy.visual.image import ImageStim
4
+
5
+
6
+ class TestImage(BaseComponentTests, _TestLibraryClassMixin):
7
+ comp = ImageComponent
8
+ libraryClass = ImageStim
@@ -1,65 +1,52 @@
1
1
  from pathlib import Path
2
2
 
3
- from . import _TestDisabledMixin, _TestBaseComponentsMixin
4
- from psychopy.experiment import Experiment
3
+ from . import BaseComponentTests
5
4
  from psychopy.experiment.loops import TrialHandler
6
- from psychopy.experiment.routines import Routine
7
5
  from psychopy.experiment.components.mouse import MouseComponent
8
6
  from psychopy.experiment.components.polygon import PolygonComponent
9
7
  from psychopy.tests.utils import TESTS_DATA_PATH
10
8
  from psychopy.hardware.mouse import Mouse
11
9
 
12
10
 
13
- class TestMouseComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
11
+ class TestMouseComponent(BaseComponentTests):
14
12
  """
15
13
  Test that Mouse coponents have the correct params and write as expected.
16
14
  """
15
+ comp = MouseComponent
17
16
  libraryClass = Mouse
18
17
 
19
- def setup_method(self):
20
- # Make blank experiment
21
- self.exp = Experiment()
22
- # Make blank routine
23
- self.routine = Routine(name="testRoutine", exp=self.exp)
24
- self.exp.addRoutine("testRoutine", self.routine)
25
- self.exp.flow.addRoutine(self.routine, 0)
26
- # Add loop around routine
27
- self.loop = TrialHandler(exp=self.exp, name="testLoop")
28
- self.exp.flow.addLoop(self.loop, 0, -1)
29
- # Make Mouse component
30
- self.comp = MouseComponent(exp=self.exp, parentName="testRoutine", name="testMouse")
31
- self.routine.addComponent(self.comp)
32
- # Make a rect for when we need something to click on
33
- self.target = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon")
34
- self.routine.addComponent(self.target)
35
-
36
18
  def test_click_save_end_clickable_cases(self):
37
19
  """
38
20
  Test all combinations of options for what to save, what can be clicked on & what kind of clicks to end the
39
21
  routine on.
40
22
  """
23
+ # make minimal experiment just for this test
24
+ comp, rt, exp = self.make_minimal_experiment()
25
+ # make a rect for when we need something to click on
26
+ target = PolygonComponent(exp=exp, parentName=rt.name, name="testPolygon")
27
+
41
28
  saveMouseStateCases = [
42
29
  {'val': "final",
43
- 'want': ["thisExp.addData('testMouse.x', x)"], # should contain code for adding final value of x
44
- 'avoid': ["testMouse.x.append(x)"]}, # should not contain code to update testMouse.x in frame loop
30
+ 'want': [f"thisExp.addData('{comp.name}.x', x)"], # should contain code for adding final value of x
31
+ 'avoid': [f"{comp.name}.x.append(x)"]}, # should not contain code to update testMouse.x in frame loop
45
32
  {'val': "on click",
46
- 'want': ["thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end
47
- "testMouse.x.append(x)"], # should contain code to update testMouse.x in frame loop
48
- 'avoid': ["thisExp.addData('testMouse.x', x)"]}, # should not add final value of x
33
+ 'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
34
+ f"{comp.name}.x.append(x)"], # should contain code to update testMouse.x in frame loop
35
+ 'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
49
36
  {'val': "on valid click",
50
- 'want': ["thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end
51
- "testMouse.x.append(x)", # should contain code to update testMouse.x in frame loop
37
+ 'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
38
+ f"{comp.name}.x.append(x)", # should contain code to update testMouse.x in frame loop
52
39
  "if gotValidClick:"], # should check for valid clicks
53
- 'avoid': ["thisExp.addData('testMouse.x', x)"]}, # should not add final value of x
40
+ 'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
54
41
  {'val': "every frame",
55
- 'want': ["thisExp.addData('testMouse.x', testMouse.x)", # should add testMouse.x at the end
56
- "testMouse.x.append(x)"], # should contain code to update testMouse.x in frame loop
57
- 'avoid': ["thisExp.addData('testMouse.x', x)"]}, # should not add final value of x
42
+ 'want': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should add testMouse.x at the end
43
+ f"{comp.name}.x.append(x)"], # should contain code to update testMouse.x in frame loop
44
+ 'avoid': [f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x
58
45
  {'val': "never",
59
46
  'want': [],
60
- 'avoid': ["thisExp.addData('testMouse.x', testMouse.x)", # should not add testMouse.x at the end
61
- "testMouse.x.append(x)", # should not contain code to update testMouse.x in frame loop
62
- "thisExp.addData('testMouse.x', x)"]}, # should not add final value of x]},
47
+ 'avoid': [f"thisExp.addData('{comp.name}.x', {comp.name}.x)", # should not add testMouse.x at the end
48
+ f"{comp.name}.x.append(x)", # should not contain code to update testMouse.x in frame loop
49
+ f"thisExp.addData('{comp.name}.x', x)"]}, # should not add final value of x]},
63
50
  ]
64
51
  forceEndRoutineOnPressCases = [
65
52
  {'val': "never",
@@ -85,54 +72,54 @@ class TestMouseComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
85
72
  # Iterate through saveMouseState cases
86
73
  for SMScase in saveMouseStateCases:
87
74
  # Set saveMouseState
88
- self.comp.params['saveMouseState'].val = SMScase['val']
75
+ comp.params['saveMouseState'].val = SMScase['val']
89
76
  for FEROPcase in forceEndRoutineOnPressCases:
90
77
  # Set forceEndRoutineOnPress
91
- self.comp.params['forceEndRoutineOnPress'].val = FEROPcase['val']
78
+ comp.params['forceEndRoutineOnPress'].val = FEROPcase['val']
92
79
  for Ccase in clickableCases:
93
80
  # Set clickable
94
- self.comp.params['clickable'].val = Ccase['val']
81
+ comp.params['clickable'].val = Ccase['val']
95
82
 
96
83
  # Compile script
97
- script = self.exp.writeScript(target="PsychoPy")
84
+ script = exp.writeScript(target="PsychoPy")
98
85
  try:
99
86
  # Look for wanted phrases
100
87
  for phrase in SMScase['want']:
101
88
  assert phrase in script, (
102
- f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, "
103
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
104
- f"clickable={self.comp.params['clickable']}"
89
+ f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
90
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
91
+ f"clickable={comp.params['clickable']}"
105
92
  )
106
93
  for phrase in FEROPcase['want']:
107
94
  assert phrase in script, (
108
- f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, "
109
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
110
- f"clickable={self.comp.params['clickable']}"
95
+ f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
96
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
97
+ f"clickable={comp.params['clickable']}"
111
98
  )
112
99
  for phrase in Ccase['want']:
113
100
  assert phrase in script, (
114
- f"{phrase} not found in script when saveMouseState={self.comp.params['saveMouseState']}, "
115
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
116
- f"clickable={self.comp.params['clickable']}"
101
+ f"{phrase} not found in script when saveMouseState={comp.params['saveMouseState']}, "
102
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
103
+ f"clickable={comp.params['clickable']}"
117
104
  )
118
105
  # Check there's no avoid phrases
119
106
  for phrase in SMScase['avoid']:
120
107
  assert phrase not in script, (
121
- f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, "
122
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
123
- f"clickable={self.comp.params['clickable']}"
108
+ f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
109
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
110
+ f"clickable={comp.params['clickable']}"
124
111
  )
125
112
  for phrase in FEROPcase['avoid']:
126
113
  assert phrase not in script, (
127
- f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, "
128
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
129
- f"clickable={self.comp.params['clickable']}"
114
+ f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
115
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
116
+ f"clickable={comp.params['clickable']}"
130
117
  )
131
118
  for phrase in Ccase['avoid']:
132
119
  assert phrase not in script, (
133
- f"{phrase} found in script when saveMouseState={self.comp.params['saveMouseState']}, "
134
- f"forceEndRoutineOnPress={self.comp.params['forceEndRoutineOnPress']} and "
135
- f"clickable={self.comp.params['clickable']}"
120
+ f"{phrase} found in script when saveMouseState={comp.params['saveMouseState']}, "
121
+ f"forceEndRoutineOnPress={comp.params['forceEndRoutineOnPress']} and "
122
+ f"clickable={comp.params['clickable']}"
136
123
  )
137
124
  except AssertionError as err:
138
125
  # If any assertion fails, save script to view
@@ -1,37 +1,21 @@
1
- from pathlib import Path
2
-
3
- from . import _TestDisabledMixin, _TestBaseComponentsMixin
4
- from psychopy.experiment import Experiment
5
- from psychopy.experiment.loops import TrialHandler
6
- from psychopy.experiment.routines import Routine
1
+ from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _TestLibraryClassMixin
7
2
  from psychopy.experiment.components.polygon import PolygonComponent
8
3
  from psychopy.visual.polygon import Polygon
9
4
 
10
5
 
11
- class TestPolygonComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
6
+ class TestPolygonComponent(BaseComponentTests, _TestLibraryClassMixin):
12
7
  """
13
8
  Test that Polygon coponents have the correct params and write as expected.
14
9
  """
10
+ comp = PolygonComponent
15
11
  libraryClass = Polygon
16
12
 
17
- def setup_method(self):
18
- # Make blank experiment
19
- self.exp = Experiment()
20
- # Make blank routine
21
- self.routine = Routine(name="testRoutine", exp=self.exp)
22
- self.exp.addRoutine("testRoutine", self.routine)
23
- self.exp.flow.addRoutine(self.routine, 0)
24
- # Add loop around routine
25
- self.loop = TrialHandler(exp=self.exp, name="testLoop")
26
- self.exp.flow.addLoop(self.loop, 0, -1)
27
- # Make a rect for when we need something to click on
28
- self.comp = PolygonComponent(exp=self.exp, parentName="testRoutine", name="testPolygon")
29
- self.routine.addComponent(self.comp)
30
-
31
13
  def test_vertices_usage(self):
32
14
  """
33
15
  Test that vertices values are used only under the correct conditions
34
16
  """
17
+ # make minimal experiment just for this test
18
+ comp, rt, exp = self.make_minimal_experiment()
35
19
  # Define values to look for and avoid in code according to value of shape
36
20
  cases = [
37
21
  # Shape is a line
@@ -61,14 +45,14 @@ class TestPolygonComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
61
45
  'avoid': ["___nVertices___"]},
62
46
  ]
63
47
  # Setup component with markers for nVertices and vertices
64
- self.comp.params['nVertices'].val = "___nVertices___"
65
- self.comp.params['vertices'].val = "___vertices___"
48
+ comp.params['nVertices'].val = "___nVertices___"
49
+ comp.params['vertices'].val = "___vertices___"
66
50
  # Test each case
67
51
  for case in cases:
68
52
  # Set shape
69
- self.comp.params['shape'].val = case['val']
53
+ comp.params['shape'].val = case['val']
70
54
  # Write experiment
71
- pyScript = self.exp.writeScript(target="PsychoPy")
55
+ pyScript = exp.writeScript(target="PsychoPy")
72
56
  # Look for sought values in experiment script
73
57
  for seekVal in case['seek']:
74
58
  assert seekVal in pyScript, (
@@ -1,23 +1,13 @@
1
1
  from pathlib import Path
2
2
 
3
- from . import _TestBaseComponentsMixin, _TestDisabledMixin
4
- from .test_base_components import _find_global_resource_in_js_experiment
3
+ from psychopy.tests.test_experiment.test_components.test_base_components import BaseComponentTests, _find_global_resource_in_js_experiment
5
4
  from psychopy.experiment.components.resourceManager import ResourceManagerComponent
6
5
  from psychopy import experiment
7
6
  from ...utils import TESTS_DATA_PATH
8
7
 
9
8
 
10
- class TestResourceManagerComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
11
- def setup_method(self):
12
- # Make blank experiment
13
- self.exp = experiment.Experiment()
14
- # Make blank routine
15
- self.routine = experiment.routines.Routine(name="testRoutine", exp=self.exp)
16
- self.exp.addRoutine("testRoutine", self.routine)
17
- self.exp.flow.addRoutine(self.routine, 0)
18
- # Make Resource Manager component
19
- self.comp = ResourceManagerComponent(exp=self.exp, parentName="testRoutine", name="testResourceManager")
20
- self.routine.addComponent(self.comp)
9
+ class TestResourceManagerComponent(BaseComponentTests):
10
+ comp = ResourceManagerComponent
21
11
 
22
12
  def test_handled_resources_removed(self):
23
13
  """
@@ -1,13 +1,11 @@
1
1
  from pathlib import Path
2
2
 
3
- from . import _TestBaseComponentsMixin, _TestDisabledMixin
4
3
  from .test_base_components import _find_global_resource_in_js_experiment
5
- from psychopy.experiment.components.settings import SettingsComponent
6
4
  from psychopy import experiment
7
5
  from ...utils import TESTS_DATA_PATH
8
6
 
9
7
 
10
- class TestSettingsComponent(_TestBaseComponentsMixin):
8
+ class TestSettingsComponent:
11
9
  def test_unhandled_resources_js(self):
12
10
  """
13
11
  Check that resources not otherwise handled are present at the start of the experiment
@@ -1,23 +1,14 @@
1
1
  from pathlib import Path
2
2
 
3
- from . import _TestBaseComponentsMixin, _TestDisabledMixin
3
+ from . import BaseComponentTests
4
4
  from .test_base_components import _find_global_resource_in_js_experiment
5
5
  from psychopy.experiment.components.static import StaticComponent
6
6
  from psychopy import experiment, data
7
7
  from ...utils import TESTS_DATA_PATH
8
8
 
9
9
 
10
- class TestStaticComponent(_TestBaseComponentsMixin, _TestDisabledMixin):
11
- def setup_method(self):
12
- # Make blank experiment
13
- self.exp = experiment.Experiment()
14
- # Make blank routine
15
- self.routine = experiment.routines.Routine(name="testRoutine", exp=self.exp)
16
- self.exp.addRoutine("testRoutine", self.routine)
17
- self.exp.flow.addRoutine(self.routine, 0)
18
- # Make Static component
19
- self.comp = StaticComponent(exp=self.exp, parentName="testRoutine", name="testStatic")
20
- self.routine.addComponent(self.comp)
10
+ class TestStaticComponent(BaseComponentTests):
11
+ comp = StaticComponent
21
12
 
22
13
  def test_handled_resources_removed(self):
23
14
  """
@@ -1,10 +1,10 @@
1
1
  from psychopy.experiment.exports import IndentingBuffer
2
- from . import _TestBaseComponentsMixin, _TestDisabledMixin
2
+ from . import BaseComponentTests
3
3
  from psychopy import experiment
4
4
  import inspect
5
5
 
6
6
 
7
- class _Generic(_TestBaseComponentsMixin, _TestDisabledMixin):
7
+ class _Generic(BaseComponentTests):
8
8
  def __init__(self, compClass):
9
9
  self.exp = experiment.Experiment()
10
10
  self.rt = experiment.routines.Routine(exp=self.exp, name="testRoutine")
@@ -18,14 +18,12 @@ def test_all_components():
18
18
  for compName, compClass in experiment.getAllComponents().items():
19
19
  if compName == "SettingsComponent":
20
20
  continue
21
- # Make a generic testing object for this component
22
- tester = _Generic(compClass)
23
- # Run each method from _TestBaseComponentsMixin on tester
24
- for attr, meth in _TestBaseComponentsMixin.__dict__.items():
25
- if inspect.ismethod(meth):
26
- meth(tester)
27
- # Run each method from _TestBaseComponentsMixin on tester
28
- for attr, meth in _TestDisabledMixin.__dict__.items():
21
+ # make a generic testing object for this component
22
+ tester = BaseComponentTests()
23
+ # make sure it has a comp class assigned
24
+ tester.comp = compClass
25
+ # Run each method from BaseComponentTests on tester
26
+ for attr, meth in BaseComponentTests.__dict__.items():
29
27
  if inspect.ismethod(meth):
30
28
  meth(tester)
31
29
 
@@ -104,59 +102,3 @@ def test_visual_set_autodraw():
104
102
  f"{compName} does not set autoDraw in its Each Frame code. If this is acceptable, add the component name "
105
103
  f"to `skipComponents`."
106
104
  )
107
-
108
- def test_indentation_consistency():
109
- """
110
- No component should exit any of its write methods at a different indent level as it entered, as this would break
111
- subsequent components / routines.
112
- """
113
- for compName, compClass in experiment.getAllComponents().items():
114
- if compName == "SettingsComponent":
115
- continue
116
- # Make a generic testing object for this component
117
- tester = _Generic(compClass)
118
- # Skip if component doesn't have a start/stop time
119
- if "startVal" not in tester.comp.params or "stopVal" not in tester.comp.params:
120
- continue
121
- # Check that each write method exits at the same indent level as it entered
122
- buff = IndentingBuffer(target="PsychoPy")
123
- msg = "Writing {} code for {} changes indent level by {} when start is `{}` and stop is `{}`."
124
- # Setup flow for writing
125
- tester.exp.flow.writeStartCode(buff)
126
- # Try combinations of start/stop being set/unset
127
- cases = [
128
- {"startVal": "0", "stopVal": "1"},
129
- {"startVal": "", "stopVal": "1"},
130
- {"startVal": "0", "stopVal": ""},
131
- {"startVal": "", "stopVal": ""},
132
- ]
133
- for case in cases:
134
- tester.comp.params["startType"].val = "time (s)"
135
- tester.comp.params["stopType"].val = "time (s)"
136
- for param, val in case.items():
137
- tester.comp.params[param].val = val
138
- # Init
139
- tester.comp.writeInitCode(buff)
140
- assert buff.indentLevel == 0, msg.format(
141
- "init", type(tester.comp).__name__, buff.indentLevel, case['startVal'], case['stopVal']
142
- )
143
- # Start routine
144
- tester.comp.writeRoutineStartCode(buff)
145
- assert buff.indentLevel == 0, msg.format(
146
- "routine start", type(tester.comp).__name__, buff.indentLevel, case['startVal'], case['stopVal']
147
- )
148
- # Each frame
149
- tester.comp.writeFrameCode(buff)
150
- assert buff.indentLevel == 0, msg.format(
151
- "each frame", type(tester.comp).__name__, buff.indentLevel, case['startVal'], case['stopVal']
152
- )
153
- # End routine
154
- tester.comp.writeRoutineEndCode(buff)
155
- assert buff.indentLevel == 0, msg.format(
156
- "routine end", type(tester.comp).__name__, buff.indentLevel, case['startVal'], case['stopVal']
157
- )
158
- # End experiment
159
- tester.comp.writeExperimentEndCode(buff)
160
- assert buff.indentLevel == 0, msg.format(
161
- "experiment end", type(tester.comp).__name__, buff.indentLevel, case['startVal'], case['stopVal']
162
- )
@@ -6,27 +6,9 @@ from pathlib import Path
6
6
  import pytest
7
7
 
8
8
  from psychopy import experiment
9
+ from psychopy.experiment.loops import TrialHandler
9
10
  from psychopy.experiment.components import BaseComponent
10
-
11
-
12
- def _make_minimal_experiment(obj):
13
- """
14
- Make a minimal experiment with just one routine containing just one component, of the same class as the current
15
- component but with all default params.
16
- """
17
- # Skip whole test if required attributes aren't present
18
- if not hasattr(obj, "comp"):
19
- pytest.skip()
20
- # Make blank experiment
21
- exp = experiment.Experiment()
22
- rt = exp.addRoutine(routineName='TestRoutine')
23
- exp.flow.addRoutine(rt, 0)
24
- # Create instance of this component with all default params
25
- compClass = type(obj.comp)
26
- comp = compClass(exp=exp, parentName='TestRoutine', name=f"test{compClass.__name__}")
27
- rt.append(comp)
28
- # Return experiment, routine and component
29
- return comp, rt, exp
11
+ from psychopy.experiment.exports import IndentingBuffer
30
12
 
31
13
 
32
14
  def _find_global_resource_in_js_experiment(script, resource):
@@ -48,34 +30,228 @@ def _find_global_resource_in_js_experiment(script, resource):
48
30
  return resource in resourcesStr
49
31
 
50
32
 
51
- class _TestBaseComponentsMixin:
52
- # Class in the PsychoPy libraries (visual, sound, hardware, etc.) corresponding to this component
53
- libraryClass = None
33
+ class BaseComponentTests:
34
+ # component class to test
35
+ comp = None
54
36
 
55
- def test_icons(self):
56
- """Check that component has icons for each app theme"""
57
- # Skip whole test if required attributes aren't present
58
- if not hasattr(self, "comp"):
37
+ # --- Utility methods ---
38
+ def make_minimal_experiment(self):
39
+ """
40
+ Make a minimal experiment with just one routine containing just one component, of the same class as the current component but with all default params.
41
+ """
42
+ # make blank experiment
43
+ exp = experiment.Experiment()
44
+ # add a Routine
45
+ rt = exp.addRoutine(routineName='TestRoutine')
46
+ exp.flow.addRoutine(rt, 0)
47
+ # add a loop around the Routine
48
+ loop = TrialHandler(exp=exp, name="testLoop")
49
+ exp.flow.addLoop(loop, 0, -1)
50
+ # create instance of this test's Component with all default params
51
+ comp = self.comp(exp=exp, parentName='TestRoutine', name=f"test{self.comp.__name__}")
52
+ rt.append(comp)
53
+ # return experiment, Routine and Component
54
+ return comp, rt, exp
55
+
56
+ @pytest.fixture(autouse=True)
57
+ def assert_comp_class(self):
58
+ """
59
+ Make sure this test object has an associated Component class - and skip the test if not. This is run before each test by default.
60
+ """
61
+ # skip whole test if there is no Component connected to test class
62
+ if self.comp is None:
59
63
  pytest.skip()
60
- # Pathify icon file path
64
+ # continue with the test as normal
65
+ yield
66
+
67
+ # --- Heritable tests ---
68
+
69
+ def test_icons(self):
70
+ """
71
+ Check that Component has icons for each app theme and that these point to real files
72
+ """
73
+ # pathify icon file path
61
74
  icon = Path(self.comp.iconFile)
62
- # Get paths for each theme
75
+ # get paths for each theme
63
76
  files = [
64
77
  icon.parent / "light" / icon.name,
65
78
  icon.parent / "dark" / icon.name,
66
79
  icon.parent / "classic" / icon.name,
67
80
  ]
68
- # Check that each path is a file
81
+ # check that each path is a file
69
82
  for file in files:
70
- assert file.is_file()
83
+ assert file.is_file(), (
84
+ f"Could not find file: {file}"
85
+ )
86
+
87
+ def test_indentation_consistency(self):
88
+ """
89
+ No component should exit any of its write methods at a different indent level as it entered, as this would break subsequent components / routines.
90
+ """
91
+ # make minimal experiment just for this test
92
+ comp, rt, exp = self.make_minimal_experiment()
93
+ # skip if component doesn't have a start/stop time
94
+ if "startVal" not in comp.params or "stopVal" not in comp.params:
95
+ pytest.skip()
96
+ # create a text buffer to write to
97
+ buff = IndentingBuffer(target="PsychoPy")
98
+ # template message for if test fails
99
+ errMsgTemplate = "Writing {} code for {} changes indent level by {} when start is `{}` and stop is `{}`."
100
+ # setup flow for writing
101
+ exp.flow.writeStartCode(buff)
102
+ # combinations of start/stop being set/unset to try
103
+ cases = [
104
+ {"startVal": "0", "stopVal": "1"},
105
+ {"startVal": "", "stopVal": "1"},
106
+ {"startVal": "0", "stopVal": ""},
107
+ {"startVal": "", "stopVal": ""},
108
+ ]
109
+ for case in cases:
110
+ # update error message for this case
111
+ errMsg = errMsgTemplate.format(
112
+ "{}", type(comp).__name__, "{}", case['startVal'], case['stopVal']
113
+ )
114
+ # set start/stop types
115
+ comp.params["startType"].val = "time (s)"
116
+ comp.params["stopType"].val = "time (s)"
117
+ # set start/stop values
118
+ for param, val in case.items():
119
+ comp.params[param].val = val
120
+ # write init code
121
+ comp.writeInitCode(buff)
122
+ # check indent
123
+ assert buff.indentLevel == 0, errMsg.format(
124
+ "init", buff.indentLevel
125
+ )
126
+ # write routine start code
127
+ comp.writeRoutineStartCode(buff)
128
+ # check indent
129
+ assert buff.indentLevel == 0, errMsg.format(
130
+ "routine start", buff.indentLevel
131
+ )
132
+ # write each frame code
133
+ comp.writeFrameCode(buff)
134
+ # check indent
135
+ assert buff.indentLevel == 0, errMsg.format(
136
+ "each frame", buff.indentLevel
137
+ )
138
+ # write end routine code
139
+ comp.writeRoutineEndCode(buff)
140
+ # check indent
141
+ assert buff.indentLevel == 0, errMsg.format(
142
+ "routine end", buff.indentLevel
143
+ )
144
+ # write end experiment code
145
+ comp.writeExperimentEndCode(buff)
146
+ # check indent
147
+ assert buff.indentLevel == 0, errMsg.format(
148
+ "experiment end", buff.indentLevel
149
+ )
150
+
151
+ def test_disabled_default_val(self):
152
+ """
153
+ Test that components created with default params are not disabled
154
+ """
155
+ # make minimal experiment just for this test
156
+ comp, rt, exp = self.make_minimal_experiment()
157
+ # check whether it can be disabled
158
+ assert 'disabled' in comp.params, (
159
+ f"{type(comp).__name__} does not have a 'disabled' attribute."
160
+ )
161
+ # check that disabled defaults to False
162
+ assert comp.params['disabled'].val is False, f"{type(comp).__name__} is defaulting to disabled."
163
+
164
+ def test_disabled_code_muting(self):
165
+ """
166
+ Test that components are only written when enabled and targets match.
167
+ """
168
+ # Code Component is never referenced by name, so skip it for this test
169
+ if self.comp.__name__ == "CodeComponent":
170
+ pytest.skip()
171
+ # Make minimal experiment just for this test
172
+ comp, rt, exp = self.make_minimal_experiment()
173
+ # Write experiment and check that component is written
174
+ pyScript = exp.writeScript(target="PsychoPy")
175
+ if "PsychoPy" in type(comp).targets:
176
+ assert comp.name in pyScript, (
177
+ f"{type(comp).__name__} not found in compiled Python script when enabled and PsychoPy in targets."
178
+ )
179
+ else:
180
+ assert comp.name not in pyScript, (
181
+ f"{type(comp).__name__} found in compiled Python script when enabled but PsychoPy not in targets."
182
+ )
183
+ # ## disabled until js can compile without saving
184
+ # jsScript = exp.writeScript(target="PsychoJS")
185
+ # if "PsychoJS" in type(comp).targets:
186
+ # assert comp.name in jsScript, (
187
+ # f"{type(comp).__name__} not found in compiled Python script when enabled and PsychoJS in targets."
188
+ # )
189
+ # else:
190
+ # assert comp.name not in jsScript, (
191
+ # f"{type(comp).__name__} found in compiled Python script when enabled but PsychoJS not in targets."
192
+ # )
193
+
194
+ # disable component then do same tests but assert not present
195
+ comp.params['disabled'].val = True
196
+
197
+ pyScript = exp.writeScript(target="PsychoPy")
198
+ if "PsychoPy" in type(comp).targets:
199
+ assert comp.name not in pyScript, (
200
+ f"{type(comp).__name__} found in compiled Python script when disabled but PsychoPy in targets."
201
+ )
202
+ else:
203
+ assert comp.name not in pyScript, (
204
+ f"{type(comp).__name__} found in compiled Python script when disabled and PsychoPy not in targets."
205
+ )
206
+ # ## disabled until js can compile without saving
207
+ # jsScript = exp.writeScript(target="PsychoJS")
208
+ # if "PsychoJS" in type(comp).targets:
209
+ # assert comp.name not in jsScript, (
210
+ # f"{type(comp).__name__} found in compiled Python script when disabled but PsychoJS in targets."
211
+ # )
212
+ # else:
213
+ # assert comp.name not in jsScript, (
214
+ # f"{type(comp).__name__} found in compiled Python script when disabled and PsychoJS not in targets."
215
+ # )
71
216
 
72
- def testDeviceClassRefs(self):
217
+ def test_disabled_components_stay_in_routine(self):
218
+ """
219
+ Test that disabled components aren't removed from their routine when experiment is written.
220
+ """
221
+ comp, rt, exp = self.make_minimal_experiment()
222
+ # Disable component
223
+ comp.params['disabled'].val = True
224
+ # Writing the script drops the component but, if working properly, only from a copy of the routine, not the
225
+ # original!
226
+ exp.writeScript()
227
+
228
+ assert comp in rt, f"Disabling {type(comp).name} appears to remove it from its routine on compile."
229
+ class _TestLibraryClassMixin:
230
+ # class in the PsychoPy libraries (visual, sound, hardware, etc.) corresponding to this component
231
+ libraryClass = None
232
+
233
+ # --- Utility methods ---
234
+
235
+ @pytest.fixture(autouse=True)
236
+ def assert_lib_class(self):
237
+ """
238
+ Make sure this test object has an associated library class - and skip the test if not. This is run before each test by default.
239
+ """
240
+ # skip whole test if there is no Component connected to test class
241
+ if self.libraryClass is None:
242
+ pytest.skip()
243
+ # continue with the test as normal
244
+ yield
245
+
246
+ # --- Heritable tests ---
247
+
248
+ def test_device_class_refs(self):
73
249
  """
74
250
  Check that any references to device classes in this Routine object point to classes which
75
251
  exist.
76
252
  """
77
253
  # make minimal experiment just for this test
78
- comp, rt, exp = _make_minimal_experiment(self)
254
+ comp, rt, exp = self.make_minimal_experiment()
79
255
  # skip test if this element doesn't point to any hardware class
80
256
  if not hasattr(comp, "deviceClasses"):
81
257
  pytest.skip()
@@ -91,13 +267,7 @@ class _TestBaseComponentsMixin:
91
267
 
92
268
  def test_params_used(self):
93
269
  # Make minimal experiment just for this test
94
- comp, rt, exp = _make_minimal_experiment(self)
95
- # Skip if component shouldn't use all of its params
96
- if type(comp).__name__ in ["SettingsComponent", "CodeComponent"]:
97
- pytest.skip()
98
- # Skip if component is deprecated
99
- if type(comp).__name__ in ['RatingScaleComponent', 'PatchComponent']:
100
- pytest.skip()
270
+ comp, rt, exp = self.make_minimal_experiment()
101
271
  # Try with PsychoPy and PsychoJS
102
272
  for target in ("PsychoPy", "PsychoJS"):
103
273
  ## Skip PsychoJS until can write script without saving
@@ -134,11 +304,8 @@ class _TestBaseComponentsMixin:
134
304
  """
135
305
  Check that all params which are settable each frame/repeat have a set method in the corresponding class.
136
306
  """
137
- # Skip if there's no corresponding library class
138
- if self.libraryClass is None:
139
- return
140
307
  # Make minimal experiment just for this test
141
- comp, rt, exp = _make_minimal_experiment(self)
308
+ comp, rt, exp = self.make_minimal_experiment()
142
309
  # Check each param
143
310
  for paramName, param in comp.params.items():
144
311
  if not param.direct:
@@ -167,91 +334,11 @@ class _TestBaseComponentsMixin:
167
334
  )
168
335
 
169
336
 
170
- class _TestDisabledMixin:
171
- def test_disabled_default_val(self):
172
- """
173
- Test that components created with default params are not disabled
174
- """
175
- # Make minimal experiment just for this test
176
- comp, rt, exp = _make_minimal_experiment(self)
177
- # Check whether it can be disabled
178
- assert 'disabled' in comp.params, (
179
- f"{type(comp).__name__} does not have a 'disabled' attribute."
180
- )
181
- # Check that disabled defaults to False
182
- assert comp.params['disabled'].val is False, f"{type(comp).__name__} is defaulting to disabled."
183
-
184
- def test_code_muting(self):
185
- """
186
- Test that components are only written when enabled and targets match.
187
- """
188
- # Make minimal experiment just for this test
189
- comp, rt, exp = _make_minimal_experiment(self)
190
- # Skip for Code components as these purely inject code, name isn't used
191
- if type(comp).__name__ in ("CodeComponent"):
192
- pytest.skip()
193
- # Write experiment and check that component is written
194
- pyScript = exp.writeScript(target="PsychoPy")
195
- if "PsychoPy" in type(comp).targets:
196
- assert comp.name in pyScript, (
197
- f"{type(comp).__name__} not found in compiled Python script when enabled and PsychoPy in targets."
198
- )
199
- else:
200
- assert comp.name not in pyScript, (
201
- f"{type(comp).__name__} found in compiled Python script when enabled but PsychoPy not in targets."
202
- )
203
- # ## disabled until js can compile without saving
204
- # jsScript = exp.writeScript(target="PsychoJS")
205
- # if "PsychoJS" in type(comp).targets:
206
- # assert comp.name in jsScript, (
207
- # f"{type(comp).__name__} not found in compiled Python script when enabled and PsychoJS in targets."
208
- # )
209
- # else:
210
- # assert comp.name not in jsScript, (
211
- # f"{type(comp).__name__} found in compiled Python script when enabled but PsychoJS not in targets."
212
- # )
213
-
214
- # Disable component then do same tests but assert not present
215
- comp.params['disabled'].val = True
216
-
217
- pyScript = exp.writeScript(target="PsychoPy")
218
- if "PsychoPy" in type(comp).targets:
219
- assert comp.name not in pyScript, (
220
- f"{type(comp).__name__} found in compiled Python script when disabled but PsychoPy in targets."
221
- )
222
- else:
223
- assert comp.name not in pyScript, (
224
- f"{type(comp).__name__} found in compiled Python script when disabled and PsychoPy not in targets."
225
- )
226
- # ## disabled until js can compile without saving
227
- # jsScript = exp.writeScript(target="PsychoJS")
228
- # if "PsychoJS" in type(comp).targets:
229
- # assert comp.name not in jsScript, (
230
- # f"{type(comp).__name__} found in compiled Python script when disabled but PsychoJS in targets."
231
- # )
232
- # else:
233
- # assert comp.name not in jsScript, (
234
- # f"{type(comp).__name__} found in compiled Python script when disabled and PsychoJS not in targets."
235
- # )
236
-
237
- def test_disabled_components_stay_in_routine(self):
238
- """
239
- Test that disabled components aren't removed from their routine when experiment is written.
240
- """
241
- comp, rt, exp = _make_minimal_experiment(self)
242
- # Disable component
243
- comp.params['disabled'].val = True
244
- # Writing the script drops the component but, if working properly, only from a copy of the routine, not the
245
- # original!
246
- exp.writeScript()
247
-
248
- assert comp in rt, f"Disabling {type(comp).name} appears to remove it from its routine on compile."
249
-
250
337
 
251
338
  class _TestDepthMixin:
252
339
  def test_depth(self):
253
340
  # Make minimal experiment
254
- comp, rt, exp = _make_minimal_experiment(self)
341
+ comp, rt, exp = self.make_minimal_experiment()
255
342
  # Get class we're currently working with
256
343
  compClass = type(comp)
257
344
  # Add index to component name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: psychopy
3
- Version: 2024.1.2
3
+ Version: 2024.1.3
4
4
  Summary: PsychoPy provides easy, precise, flexible experiments in behavioural sciences
5
5
  Author-Email: Open Science Tools Ltd <support@opensciencetools.org>
6
6
  Maintainer-Email: Open Science Tools Ltd <support@opensciencetools.org>
@@ -23,7 +23,7 @@ Requires-Dist: matplotlib
23
23
  Requires-Dist: pyglet==1.4.11; platform_system == "Windows"
24
24
  Requires-Dist: pyglet==1.5.27; platform_system != "Windows"
25
25
  Requires-Dist: pillow>=9.4.0
26
- Requires-Dist: pyqt5
26
+ Requires-Dist: pyqt6
27
27
  Requires-Dist: pandas>=1.5.3
28
28
  Requires-Dist: questplus>=2023.1
29
29
  Requires-Dist: openpyxl
@@ -1,12 +1,12 @@
1
- psychopy-2024.1.2.dist-info/METADATA,sha256=M1igQPo08nMH1m8fmlLpFSVVdfg5b9-9IvJC1I0sEYU,6993
2
- psychopy-2024.1.2.dist-info/WHEEL,sha256=7sv5iXvIiTVJSnAxCz2tGBm9DHsb2vPSzeYeT7pvGUY,90
3
- psychopy-2024.1.2.dist-info/entry_points.txt,sha256=1LXVqRMSILrbEagrzpJDtmXyEPHX3GnpBJ85TEnxuoI,56
4
- psychopy-2024.1.2.dist-info/licenses/AUTHORS.md,sha256=MmZf-GmSBscBa_z7ePoqYxbaGe6XOJnld_s6JIQGfS4,2395
5
- psychopy-2024.1.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
1
+ psychopy-2024.1.3.dist-info/METADATA,sha256=Dgf2hQgHlVrGjHGkRlF_xx0N_lUO9X6FETfx-vdWyic,6993
2
+ psychopy-2024.1.3.dist-info/WHEEL,sha256=7sv5iXvIiTVJSnAxCz2tGBm9DHsb2vPSzeYeT7pvGUY,90
3
+ psychopy-2024.1.3.dist-info/entry_points.txt,sha256=1LXVqRMSILrbEagrzpJDtmXyEPHX3GnpBJ85TEnxuoI,56
4
+ psychopy-2024.1.3.dist-info/licenses/AUTHORS.md,sha256=MmZf-GmSBscBa_z7ePoqYxbaGe6XOJnld_s6JIQGfS4,2395
5
+ psychopy-2024.1.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
6
  psychopy/CHANGELOG.txt,sha256=bRyB9AEemUO4KBYhtBOIWOaXFmX9X3pHgMMF9QyeITA,203527
7
7
  psychopy/LICENSE.txt,sha256=d2HeFOYbuXc2LZobE1EkFq3lYV5ge3YSaTL3y1r7KQc,746
8
8
  psychopy/LICENSES.txt,sha256=T59KljDkUEEtd40IHWS-S0OTSjzqueGD6oNmgqpRIf0,3145
9
- psychopy/__init__.py,sha256=tS0DFSNAdVdbqrTmPYpidSvE19az5kUVCOO6yrUaiWM,2757
9
+ psychopy/__init__.py,sha256=TaSpE94UGKdQ0J3pbhPIO_aH4HsJ3AlNIdFspPjwik0,2757
10
10
  psychopy/alerts/__init__.py,sha256=buvC7HFn71FhIp1D4XGXkZLeNLk22uO9TSQsxLlYZqU,2178
11
11
  psychopy/alerts/_alerts.py,sha256=IMHMXY1mzTCmlOYhHW0_Upb9oAcYzzTWe44BUNXj-ec,6120
12
12
  psychopy/alerts/_errorHandler.py,sha256=JNkdx3OJv0PeAeARG09Rgne7dH-aGgUdxdoriDd4KW4,1501
@@ -1446,7 +1446,7 @@ psychopy/experiment/components/microphone/dark/microphone.png,sha256=q6XPPab-nnF
1446
1446
  psychopy/experiment/components/microphone/dark/microphone@2x.png,sha256=x58B_E0YQNUV2dyye52APaf-25S7nfpP70j-2-7PpLQ,1853
1447
1447
  psychopy/experiment/components/microphone/light/microphone.png,sha256=bQztv6e7oCUK8852Zc5RcLt_kEgqI9c_d_S8W-6WEt4,988
1448
1448
  psychopy/experiment/components/microphone/light/microphone@2x.png,sha256=swrycVelLJd8k57jbaS5AYXq5ievFSs-rhsNePv_rQM,1841
1449
- psychopy/experiment/components/mouse/__init__.py,sha256=4dSvLs6h2aZirBRTflbwgpi2GqP_LFfBU4jiEhTG1aE,34264
1449
+ psychopy/experiment/components/mouse/__init__.py,sha256=pT5xrJxpOhiZEUKrbkPBFgTGG_yA6xgsDYmZj9dNqUE,34739
1450
1450
  psychopy/experiment/components/mouse/classic/mouse.png,sha256=rXSJ4vFYekhNjANSuJnLdLmCF0-Y6p6s8b7JyL6It9Y,3735
1451
1451
  psychopy/experiment/components/mouse/classic/mouse@2x.png,sha256=Ms-piLdbP1zKFBP5bbiBb7sF_Bps67GG7ZpBnItxoNM,11310
1452
1452
  psychopy/experiment/components/mouse/dark/mouse.png,sha256=KWXV2z5bUx80lwu3H98-8h2XWo9ZyQvM3iRU22CQ7z8,772
@@ -1609,7 +1609,7 @@ psychopy/experiment/questPlusTemplate.xltx,sha256=8c1yt6el4v_iE79FoelLX267TpFrPs
1609
1609
  psychopy/experiment/questTemplate.xltx,sha256=p0FzYmMWJH0uaGRFlAdPT4uSzNBHpV7uPM0gZN5yYds,20668
1610
1610
  psychopy/experiment/routines/__init__.py,sha256=mQTjLeu5H7K_EGp_WZ7I5jXDKNgPF4ViJz00aG_pV-g,3672
1611
1611
  psychopy/experiment/routines/_base.py,sha256=VMln_MS1QPNga64wBR0fB0pMsHu28UeyElZAuIil7pA,38584
1612
- psychopy/experiment/routines/counterbalance/__init__.py,sha256=NuH3o-tm0aF6zlfdD1eEd0vh79zgbdHlf47tYp7CrAk,12603
1612
+ psychopy/experiment/routines/counterbalance/__init__.py,sha256=XxkGNfrQoTvuL0i80bWWYy2VANsxN7Fl93Y2C7xpc5c,12616
1613
1613
  psychopy/experiment/routines/counterbalance/classic/counterbalance.png,sha256=ficKpm9rX1A-YIzgFNfM6EbwAklenfKeOwP74ktCgfo,3977
1614
1614
  psychopy/experiment/routines/counterbalance/classic/counterbalance@2x.png,sha256=UwSvAzg0Y9k1jEtkvbzr277tApPB08o4gFRuhhN5NpA,10628
1615
1615
  psychopy/experiment/routines/counterbalance/counterbalanceItems.xltx,sha256=Ht1ZE6c800RGF1LxE6WjWGujNwsMQ04pw6j_VZu86uE,18707
@@ -1683,7 +1683,7 @@ psychopy/hardware/forp.py,sha256=P19o4cmrZD4kHhJwNT0kj5LR-F4kdwXSu5GpYyiis4U,135
1683
1683
  psychopy/hardware/gammasci.py,sha256=odRzQ1kKduDqrQshp582YtKnXnSFPXWc8qic9jjPZMc,1000
1684
1684
  psychopy/hardware/iolab.py,sha256=8fqy-xGe15naHLgYfxELpes5ooNDTUytLTTz9_NhTi0,1067
1685
1685
  psychopy/hardware/joystick/__init__.py,sha256=wBhX_21kRPAlKM2UyEZBYBKhDCfiLHIp-K7r_2bDorI,19109
1686
- psychopy/hardware/keyboard.py,sha256=SCMaOrRwwz5jVZwH-0g7IOa7D1CAFfwNNJk9w6e19Gg,34160
1686
+ psychopy/hardware/keyboard.py,sha256=enf7nluy1w3_SYA2B2mCrVY0Cx3ytM5BjtXdkIjiLqk,34192
1687
1687
  psychopy/hardware/knownDevices.json,sha256=rGsTu0MaBdUTUA6bwCKU9D-J03i0oFPDWOw28Eu4Ik0,1791
1688
1688
  psychopy/hardware/labhackers.py,sha256=_fRtMUtSd5f2DXRVgVsXQdF3x9ZDFypXcuxaD0J1C0E,864
1689
1689
  psychopy/hardware/labjacks.py,sha256=CQfxgty2uGdG2bzkcbfw-pKBzxHlnSSVfcPHKlsOC8E,980
@@ -2744,17 +2744,18 @@ psychopy/tests/test_experiment/needs_wx/test_Experiment.py,sha256=HpN-2uiKFitW6y
2744
2744
  psychopy/tests/test_experiment/needs_wx/test_components.py,sha256=nr06cyK0vPbJVOFXESx5Q5ehjKI9i669HbS6cJDzlGA,7092
2745
2745
  psychopy/tests/test_experiment/test_component_compile_js.py,sha256=LrVUQ-izAN23e5M1t3Tg7NaourscsDABSBtPZeEWm24,2775
2746
2746
  psychopy/tests/test_experiment/test_component_compile_python.py,sha256=SALVfXswwu64hyfIsqzPhDru5EVO5tyuUDAOLrXDE10,7002
2747
- psychopy/tests/test_experiment/test_components/__init__.py,sha256=EdYXBYmZlZcMS0LtpZpGIIE7fa0MBka2QLCPvkiFo1I,78
2748
- psychopy/tests/test_experiment/test_components/test_ButtonBox.py,sha256=DRlb5ZteA3owgNqa29c-sKdIfqBt2HxCaRbAa-eymxg,7347
2749
- psychopy/tests/test_experiment/test_components/test_Code.py,sha256=wmFAzpk4SXzLp7Hhatm6MaZEpYPni2HTgoezxPgZG-k,4044
2750
- psychopy/tests/test_experiment/test_components/test_Image.py,sha256=h8rdATikkkkoS8EOem4y_sb0jAnSPnGSxPvkCfANXUk,1057
2751
- psychopy/tests/test_experiment/test_components/test_Mouse.py,sha256=3dhAT7nRfg8aK6ePASBmr6mnGlyZX7HKYB3Ozd54_X4,8609
2752
- psychopy/tests/test_experiment/test_components/test_Polygon.py,sha256=qc3gwI2rze6fzNUbIg8dRaXacQJWlEokfkLo3QE6BK8,3588
2753
- psychopy/tests/test_experiment/test_components/test_ResourceManager.py,sha256=CPozNSM4s9rulbijBlnViS8dhmsa0DjziG8VG7f4s3Q,3040
2754
- psychopy/tests/test_experiment/test_components/test_Settings.py,sha256=iFwD6xN0KZHs5C09OD97OzBwFSec1E5KY3M4KpjO3Ys,3493
2755
- psychopy/tests/test_experiment/test_components/test_Static.py,sha256=wpGpwm8r4vxJA4gIIAwVL3PPcwsMt0narkVmeKL0xJI,2955
2756
- psychopy/tests/test_experiment/test_components/test_all_components.py,sha256=Kc39UU4NJw5uyFO0DR5TEDI-pdRopW68yR9muQvIDcU,7280
2757
- psychopy/tests/test_experiment/test_components/test_base_components.py,sha256=H_VXPo5ul524U9lt7okfAttW9taL0GxAmD8xbrxH8_c,14389
2747
+ psychopy/tests/test_experiment/test_components/__init__.py,sha256=qQqf5031kA1-Qcj4ActVGQbE6wB4DQQ2JpdBnf4e7Sc,52
2748
+ psychopy/tests/test_experiment/test_components/test_ButtonBoxComponent.py,sha256=sG_M3v97JIrFvpDGRYn7Y21--zvzpQkPTtEnsXspBPk,6748
2749
+ psychopy/tests/test_experiment/test_components/test_CodeComponent.py,sha256=8U4Z2FDhn6Gf1DRnOAEEScl47gFHWz1-6_nhAiQwrkI,3460
2750
+ psychopy/tests/test_experiment/test_components/test_GratingComponent.py,sha256=zdaSZhd-XAkihUBt7mnq93QEuEDniUeRxE0LHWl98Qo,363
2751
+ psychopy/tests/test_experiment/test_components/test_ImageComponent.py,sha256=oFlxj67_r5H68VF84FcGa8rrgBxTo81lURS9L6mBSpA,366
2752
+ psychopy/tests/test_experiment/test_components/test_MouseComponent.py,sha256=2pFUhxQmCWBeUvaUxWaayEDFfgkIOP8CtzWP3o2e4UI,7876
2753
+ psychopy/tests/test_experiment/test_components/test_PolygonComponent.py,sha256=wXaGVfe-qERMcgtRexFofpjsBjR4WdWKBFtPIBut8Wo,2954
2754
+ psychopy/tests/test_experiment/test_components/test_ResourceManagerComponent.py,sha256=n-mB_VoTn3PxUWETjggyU5yvh56k4xQQMYruOy2ScM0,2533
2755
+ psychopy/tests/test_experiment/test_components/test_SettingsComponent.py,sha256=hdcoBh8u9X4si97L8naTz7Ix2rzlHK4FpP52-hDYENY,3338
2756
+ psychopy/tests/test_experiment/test_components/test_StaticComponent.py,sha256=Ta0gzNMN-lqE77iyQ6z0eR_71ijNlGhSr01IFaiHJoE,2434
2757
+ psychopy/tests/test_experiment/test_components/test_all_components.py,sha256=O-7fSqlk-oaTK4HC65_lsDv0sTF4xQ9p0P3uXoenMzc,4403
2758
+ psychopy/tests/test_experiment/test_components/test_base_components.py,sha256=StKDjDX8Hg88s_znEpD6ivZlz4Fxuvm_UWiS38dJv_k,17849
2758
2759
  psychopy/tests/test_experiment/test_experiment.py,sha256=WRWimiyviyzkPc85PuqTumrVrbrZe0yB95WYraGXLsw,4439
2759
2760
  psychopy/tests/test_experiment/test_loops.py,sha256=8-lHo7nAXaiorP_hpv2oSyA_UZAt2617L3CyusrIUCs,3703
2760
2761
  psychopy/tests/test_experiment/test_params.py,sha256=e2WjO4T1xvb0sRtZXph9CAcN3YjIN79hzziTiqoEu8o,13172
@@ -2947,4 +2948,4 @@ psychopy/voicekey/parallel_vks.py,sha256=I6a07GvN0Q2k9xidl9YbEUDORG2v9l2H_5Owbpw
2947
2948
  psychopy/voicekey/signal.py,sha256=Mk363qVsvJampVDfsOTdGq7lPESlKmogmNFqasdghJE,1013
2948
2949
  psychopy/voicekey/vk_tools.py,sha256=pFvOvF9b9CfS-JkZ_fv38LxI59hJXQqiz9ymcrJiAFc,8483
2949
2950
  psychopy/web.py,sha256=ohiFb7lW1-0pH_62cR-z8ufZ5Nta2jRIjGS4_zosX_U,10228
2950
- psychopy-2024.1.2.dist-info/RECORD,,
2951
+ psychopy-2024.1.3.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- from psychopy.experiment import Experiment
2
- from psychopy.experiment.components.image import ImageComponent
3
- from psychopy.experiment.loops import TrialHandler
4
- from psychopy.experiment.routines import Routine
5
- from .test_base_components import _TestDepthMixin, _TestBaseComponentsMixin
6
- from psychopy.visual.image import ImageStim
7
-
8
-
9
- class TestImage(_TestBaseComponentsMixin, _TestDepthMixin):
10
- libraryClass = ImageStim
11
-
12
- def setup_method(self):
13
- # Make blank experiment
14
- self.exp = Experiment()
15
- # Make blank routine
16
- self.routine = Routine(name="testRoutine", exp=self.exp)
17
- self.exp.addRoutine("testRoutine", self.routine)
18
- self.exp.flow.addRoutine(self.routine, 0)
19
- # Add loop around routine
20
- self.loop = TrialHandler(exp=self.exp, name="testLoop")
21
- self.exp.flow.addLoop(self.loop, 0, -1)
22
- # Make a rect for when we need something to click on
23
- self.comp = ImageComponent(exp=self.exp, parentName="testRoutine", name="testImage")
24
- self.routine.addComponent(self.comp)