genie-python 25.2.2__tar.gz → 26.2.0__tar.gz

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.
Files changed (90) hide show
  1. {genie_python-25.2.2 → genie_python-26.2.0}/.github/workflows/lint-and-test-nightly.yml +1 -0
  2. {genie_python-25.2.2 → genie_python-26.2.0}/.github/workflows/lint_and_test.yml +3 -3
  3. {genie_python-25.2.2 → genie_python-26.2.0}/.github/workflows/release.yml +6 -6
  4. {genie_python-25.2.2 → genie_python-26.2.0}/PKG-INFO +3 -3
  5. {genie_python-25.2.2 → genie_python-26.2.0}/pyproject.toml +3 -2
  6. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/.pylintrc +0 -4
  7. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/_version.py +16 -3
  8. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/block_names.py +33 -21
  9. genie_python-26.2.0/src/genie_python/channel_access_exceptions.py +44 -0
  10. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie.py +280 -156
  11. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_advanced.py +32 -27
  12. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_alerts.py +25 -15
  13. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_api_setup.py +2 -2
  14. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_blockserver.py +18 -10
  15. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_cachannel_wrapper.py +4 -4
  16. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_dae.py +46 -9
  17. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_epics_api.py +123 -50
  18. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_p4p_wrapper.py +2 -2
  19. genie_python-26.2.0/src/genie_python/genie_pre_post_cmd_manager.py +45 -0
  20. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_pv_connection_protocol.py +3 -3
  21. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_script_checker.py +24 -13
  22. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_simulate_impl.py +79 -24
  23. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_wait_for_move.py +35 -18
  24. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_waitfor.py +6 -7
  25. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/matplotlib_backend/ibex_websocket_backend.py +87 -55
  26. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/mysql_abstraction_layer.py +3 -1
  27. genie_python-26.2.0/src/genie_python/scanning_instrument_pylint_plugin.py +53 -0
  28. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/utilities.py +71 -47
  29. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python.egg-info/PKG-INFO +3 -3
  30. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python.egg-info/SOURCES.txt +1 -0
  31. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python.egg-info/requires.txt +1 -2
  32. {genie_python-25.2.2 → genie_python-26.2.0}/tests/py3_test_genie_experimental_data.py +1 -1
  33. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_block_names.py +2 -2
  34. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie.py +19 -1
  35. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_alerts.py +1 -2
  36. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_api_setup.py +1 -1
  37. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_blockserver_tests.py +1 -2
  38. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_dae.py +100 -10
  39. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_epics_api.py +1 -1
  40. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_matplotlib_backend.py +2 -3
  41. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_mysql_abstraction_layer.py +1 -2
  42. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_script_checker.py +4 -4
  43. genie_python-26.2.0/tests/test_scripts/test.py +2 -0
  44. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_simulation.py +1 -1
  45. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_utilities.py +1 -2
  46. genie_python-25.2.2/src/genie_python/channel_access_exceptions.py +0 -45
  47. genie_python-25.2.2/src/genie_python/genie_pre_post_cmd_manager.py +0 -21
  48. genie_python-25.2.2/src/genie_python/scanning_instrument_pylint_plugin.py +0 -31
  49. {genie_python-25.2.2 → genie_python-26.2.0}/.git-blame-ignore-revs +0 -0
  50. {genie_python-25.2.2 → genie_python-26.2.0}/.gitattributes +0 -0
  51. {genie_python-25.2.2 → genie_python-26.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  52. {genie_python-25.2.2 → genie_python-26.2.0}/.github/dependabot.yml +0 -0
  53. {genie_python-25.2.2 → genie_python-26.2.0}/.github/workflows/documentation.yml +0 -0
  54. {genie_python-25.2.2 → genie_python-26.2.0}/.gitignore +0 -0
  55. {genie_python-25.2.2 → genie_python-26.2.0}/LICENSE +0 -0
  56. {genie_python-25.2.2 → genie_python-26.2.0}/README.md +0 -0
  57. {genie_python-25.2.2 → genie_python-26.2.0}/doc/conf.py +0 -0
  58. {genie_python-25.2.2 → genie_python-26.2.0}/doc/genie_python.rst +0 -0
  59. {genie_python-25.2.2 → genie_python-26.2.0}/ruff.toml +0 -0
  60. {genie_python-25.2.2 → genie_python-26.2.0}/setup.cfg +0 -0
  61. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/__init__.py +0 -0
  62. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_change_cache.py +0 -0
  63. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_experimental_data.py +0 -0
  64. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_logging.py +0 -0
  65. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_plot.py +0 -0
  66. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_script_generator.py +0 -0
  67. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_simulate.py +0 -0
  68. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_startup.py +0 -0
  69. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/genie_toggle_settings.py +0 -0
  70. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/matplotlib_backend/__init__.py +0 -0
  71. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/testing_utils/__init__.py +0 -0
  72. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/testing_utils/script_checker.py +0 -0
  73. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/typings/CaChannel/CaChannel.pyi +0 -0
  74. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/typings/CaChannel/__init__.pyi +0 -0
  75. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/typings/CaChannel/_version.pyi +0 -0
  76. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/typings/CaChannel/ca.pyi +0 -0
  77. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python/version.py +0 -0
  78. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python.egg-info/dependency_links.txt +0 -0
  79. {genie_python-25.2.2 → genie_python-26.2.0}/src/genie_python.egg-info/top_level.txt +0 -0
  80. {genie_python-25.2.2 → genie_python-26.2.0}/tests/__init__.py +0 -0
  81. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_change_cache.py +0 -0
  82. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_wait_for_move.py +0 -0
  83. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_genie_waitfor.py +0 -0
  84. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_script_generator.py +0 -0
  85. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_scripts/error.py +0 -0
  86. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_scripts/error_for_script_checker.py +0 -0
  87. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_scripts/valid.py +0 -0
  88. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_scripts/valid_python_2.py +0 -0
  89. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_scripts/valid_to_import.py +0 -0
  90. {genie_python-25.2.2 → genie_python-26.2.0}/tests/test_utils_with_unicode_literals.py +0 -0
@@ -2,6 +2,7 @@ name: lint-and-test-nightly
2
2
  on:
3
3
  schedule:
4
4
  - cron: "0 0 * * *"
5
+ workflow_dispatch:
5
6
 
6
7
  jobs:
7
8
  lint-and-test-nightly:
@@ -13,11 +13,11 @@ jobs:
13
13
  strategy:
14
14
  matrix:
15
15
  os: ["ubuntu-latest", "windows-latest"]
16
- version: ['3.11', '3.12']
16
+ version: ['3.11', '3.12', '3.13']
17
17
  fail-fast: false
18
18
  steps:
19
- - uses: actions/checkout@v4
20
- - uses: actions/setup-python@v5
19
+ - uses: actions/checkout@v6
20
+ - uses: actions/setup-python@v6
21
21
  with:
22
22
  python-version: ${{ matrix.version }}
23
23
  - name: Install base requirements
@@ -12,9 +12,9 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
  - name: Set up Python
17
- uses: actions/setup-python@v5
17
+ uses: actions/setup-python@v6
18
18
  with:
19
19
  python-version: "3.12"
20
20
  - name: Install pypa/build
@@ -26,7 +26,7 @@ jobs:
26
26
  - name: Build a binary wheel and a source tarball
27
27
  run: python3 -m build
28
28
  - name: Store the distribution packages
29
- uses: actions/upload-artifact@v4
29
+ uses: actions/upload-artifact@v6
30
30
  with:
31
31
  name: python-package-distributions
32
32
  path: dist/
@@ -43,7 +43,7 @@ jobs:
43
43
  id-token: write # IMPORTANT: mandatory for trusted publishing
44
44
  steps:
45
45
  - name: Download all the dists
46
- uses: actions/download-artifact@v4
46
+ uses: actions/download-artifact@v7
47
47
  with:
48
48
  name: python-package-distributions
49
49
  path: dist/
@@ -62,12 +62,12 @@ jobs:
62
62
 
63
63
  steps:
64
64
  - name: Download all the dists
65
- uses: actions/download-artifact@v4
65
+ uses: actions/download-artifact@v7
66
66
  with:
67
67
  name: python-package-distributions
68
68
  path: dist/
69
69
  - name: Sign the dists with Sigstore
70
- uses: sigstore/gh-action-sigstore-python@v3.0.0
70
+ uses: sigstore/gh-action-sigstore-python@v3.2.0
71
71
  with:
72
72
  inputs: >-
73
73
  ./dist/*.tar.gz
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: genie_python
3
- Version: 25.2.2
3
+ Version: 26.2.0
4
4
  Summary: Instrument control & scripting for the ISIS Neutron & Muon source
5
5
  Author-email: ISIS Experiment Controls <ISISExperimentControls@stfc.ac.uk>
6
6
  Maintainer-email: ISIS Experiment Controls <ISISExperimentControls@stfc.ac.uk>
@@ -12,6 +12,7 @@ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
15
16
  Requires-Python: >=3.11
16
17
  Description-Content-Type: text/markdown
17
18
  License-File: LICENSE
@@ -26,7 +27,7 @@ Requires-Dist: pylint
26
27
  Requires-Dist: pyright
27
28
  Requires-Dist: pywin32; platform_system == "Windows"
28
29
  Provides-Extra: plot
29
- Requires-Dist: matplotlib==3.10.1; extra == "plot"
30
+ Requires-Dist: matplotlib==3.10.7; extra == "plot"
30
31
  Requires-Dist: py4j; extra == "plot"
31
32
  Requires-Dist: tornado; extra == "plot"
32
33
  Provides-Extra: doc
@@ -36,7 +37,6 @@ Requires-Dist: myst_parser; extra == "doc"
36
37
  Requires-Dist: sphinx-autobuild; extra == "doc"
37
38
  Provides-Extra: dev
38
39
  Requires-Dist: genie_python[doc,plot]; extra == "dev"
39
- Requires-Dist: mock; extra == "dev"
40
40
  Requires-Dist: parameterized; extra == "dev"
41
41
  Requires-Dist: pyhamcrest; extra == "dev"
42
42
  Requires-Dist: pytest; extra == "dev"
@@ -24,6 +24,7 @@ classifiers = [
24
24
  "Intended Audience :: Developers",
25
25
  "Programming Language :: Python :: 3.11",
26
26
  "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
27
28
  ]
28
29
 
29
30
  dependencies = [
@@ -56,7 +57,7 @@ dependencies = [
56
57
  # - It depends on a couple of heavyweight libs (py4j and tornado) that aren't necessary otherwise
57
58
  plot = [
58
59
  # When updating, check plotting works in GUI. Must keep pinned to a specific, tested version.
59
- "matplotlib==3.10.1",
60
+ "matplotlib==3.10.7",
60
61
  # Python <-> Java communication, to spawn matplotlib plots in GUI
61
62
  "py4j",
62
63
  # Tornado webserver used by custom backend
@@ -72,7 +73,6 @@ doc = [
72
73
 
73
74
  dev = [
74
75
  "genie_python[plot,doc]",
75
- "mock",
76
76
  "parameterized",
77
77
  "pyhamcrest",
78
78
  "pytest",
@@ -118,6 +118,7 @@ reportUnnecessaryIsInstance = true
118
118
  reportUntypedBaseClass = true
119
119
  reportUntypedClassDecorator = true
120
120
  reportUntypedFunctionDecorator = true
121
+ stubPath = "src/genie_python/typings"
121
122
 
122
123
  [tool.setuptools_scm]
123
124
  version_file = "src/genie_python/_version.py"
@@ -30,10 +30,6 @@ persistent=yes
30
30
  # Specify a configuration file.
31
31
  #rcfile=
32
32
 
33
- # When enabled, pylint would attempt to guess common misconfiguration and emit
34
- # user-friendly hints instead of false-positive error messages
35
- suggestion-mode=yes
36
-
37
33
  # Allow loading of arbitrary C extensions. Extensions are imported into the
38
34
  # active Python interpreter and may run arbitrary code.
39
35
  unsafe-load-any-extension=yes
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '25.2.2'
21
- __version_tuple__ = version_tuple = (25, 2, 2)
31
+ __version__ = version = '26.2.0'
32
+ __version_tuple__ = version_tuple = (26, 2, 0)
33
+
34
+ __commit_id__ = commit_id = 'g4a4b13072'
@@ -1,6 +1,7 @@
1
1
  import zlib
2
2
  from keyword import iskeyword
3
3
  from threading import RLock, Timer
4
+ from typing import TYPE_CHECKING, Optional
4
5
 
5
6
  from .channel_access_exceptions import UnableToConnectToPVException
6
7
  from .genie_blockserver import BLOCK_SERVER_PREFIX, PV_BLOCK_NAMES
@@ -9,6 +10,9 @@ from .utilities import dehex_decompress_and_dejson
9
10
 
10
11
  DELAY_BEFORE_RETRYING_BLOCK_NAMES_PV_ON_FAIL = 30.0
11
12
 
13
+ if TYPE_CHECKING:
14
+ from genie_python.genie import PVValue
15
+
12
16
 
13
17
  class BlockNamesManager:
14
18
  """
@@ -17,23 +21,24 @@ class BlockNamesManager:
17
21
 
18
22
  def __init__(
19
23
  self,
20
- block_names,
21
- delay_before_retry_add_monitor=DELAY_BEFORE_RETRYING_BLOCK_NAMES_PV_ON_FAIL,
22
- ):
24
+ block_names: "BlockNames",
25
+ delay_before_retry_add_monitor: float = DELAY_BEFORE_RETRYING_BLOCK_NAMES_PV_ON_FAIL,
26
+ ) -> None:
23
27
  """
24
28
  Constructor.
25
29
  :param block_names: the block name instance that this manger is managing
26
- :param delay_before_retry_add_monitor: if the block names pv doesn't exist on start the delay before retrying
30
+ :param delay_before_retry_add_monitor: if the block names pv doesn't exist
31
+ on start the delay before retrying
27
32
  """
28
33
  self._block_names = block_names
29
34
  self._cancel_monitor_fn = None
30
35
  self._delay_before_retry_add_monitor = delay_before_retry_add_monitor
31
36
  self._timer = None
32
- self._pv_name = None
37
+ self._pv_name = ""
33
38
  # lock used to access _timer or _pv_name
34
39
  self.pv_name_lock = RLock()
35
40
 
36
- def update_prefix(self, pv_prefix):
41
+ def update_prefix(self, pv_prefix: str) -> None:
37
42
  """
38
43
  Update the instrument prefix that the manager is using if it has changed
39
44
  :param pv_prefix: new pv prefix
@@ -45,44 +50,49 @@ class BlockNamesManager:
45
50
  self._pv_name = new_name
46
51
  if self._timer is None:
47
52
  self._timer = Timer(0, self._add_monitor)
48
- self._timer.setDaemon(True)
53
+ self._timer.daemon = True
49
54
  self._timer.start()
50
55
 
51
- def _add_monitor(self):
56
+ def _add_monitor(self) -> None:
52
57
  """
53
- Add a monitor to the block names pv if it is not already monitored, then reschedule task to add monitor. If the
54
- pv is monitored don't run.
58
+ Add a monitor to the block names pv if it is not already monitored,
59
+ then reschedule task to add monitor. If the pv is monitored don't run.
55
60
  """
56
61
 
57
- # Get PV we should monitor and check whether we need to add new monitor if so cancel old monitor
62
+ # Get PV we should monitor and check whether we need to add new monitor
63
+ # if so cancel old monitor
58
64
  with self.pv_name_lock:
59
65
  self._timer = None
60
66
  # not monitoring the correct pv
61
67
  if self._cancel_monitor_fn is not None:
62
68
  self._cancel_monitor_fn()
63
69
 
64
- # Add new monitor if successful then record monitored pv and pull first value, otherwise do nothing
70
+ # Add new monitor if successful then record monitored pv and pull
71
+ # first value, otherwise do nothing
65
72
  try:
66
73
  self._cancel_monitor_fn = CaChannelWrapper.add_monitor(
67
74
  self._pv_name, self._update_block_names, to_string=True
68
75
  )
69
76
  self._update_block_names(
70
- CaChannelWrapper.get_pv_value(self._pv_name, to_string=True), None, None
77
+ CaChannelWrapper.get_pv_value(self._pv_name, to_string=True), "", ""
71
78
  )
72
79
  except UnableToConnectToPVException:
73
- # Schedule next add monitor if needed; i.e. a old pv-prefix change was slower the the last pv-prefix
80
+ # Schedule next add monitor if needed; i.e. a old pv-prefix change
81
+ # was slower the the last pv-prefix
74
82
  # and so we are monitoring the wrong pv.
75
83
  self._timer = Timer(self._delay_before_retry_add_monitor, self._add_monitor)
76
- self._timer.setDaemon(True)
84
+ self._timer.daemon = True
77
85
  self._timer.start()
78
86
 
79
- def _update_block_names(self, value, _, _1):
87
+ def _update_block_names(self, value: "PVValue", _: Optional[str], _1: Optional[str]) -> None:
80
88
  """
81
89
  Update the block names from a pv
82
90
  Args:
83
91
  :param value: new value of block names pv
84
- :param _(CaChannel._ca.AlarmSeverity): severity of any alarm (not used but passed in by monitor)
85
- :param _1(CaChannel._ca.AlarmCondition): status of the alarm (not used but passed in by monitor)
92
+ :param _(CaChannel._ca.AlarmSeverity): severity of any alarm
93
+ (not used but passed in by monitor)
94
+ :param _1(CaChannel._ca.AlarmCondition): status of the alarm
95
+ (not used but passed in by monitor)
86
96
  """
87
97
  # remove old blocks
88
98
  for block_name in list(self._block_names.__dict__.keys()):
@@ -90,6 +100,7 @@ class BlockNamesManager:
90
100
 
91
101
  # add new block as attributes to class
92
102
  try:
103
+ assert isinstance(value, (str, bytes)), value
93
104
  block_names = dehex_decompress_and_dejson(value)
94
105
  for name in block_names:
95
106
  attribute_name = name
@@ -103,11 +114,12 @@ class BlockNamesManager:
103
114
 
104
115
  class BlockNames:
105
116
  """
106
- Hold names of the current blocks in config. If block is requested which does not appear in the current config
107
- block name returned but message printed about it.
117
+ Hold names of the current blocks in config. If block is requested which
118
+ does not appear in the current config block name returned but message
119
+ printed about it.
108
120
  """
109
121
 
110
- def __getattr__(self, attr):
122
+ def __getattr__(self, attr: str) -> str:
111
123
  """
112
124
  If an attribute is not set then return name requested
113
125
  :param attr: attribute name
@@ -0,0 +1,44 @@
1
+ """
2
+ Useful and slightly more explicit exceptions that can be thrown.
3
+ In general catch the super class of these.
4
+ """
5
+
6
+
7
+ class UnableToConnectToPVException(IOError): # noqa N818 Historic name
8
+ """
9
+ The system is unable to connect to a PV for some reason.
10
+ """
11
+
12
+ def __init__(self, pv_name: str, err: str) -> None:
13
+ super(UnableToConnectToPVException, self).__init__(
14
+ f"Unable to connect to PV {pv_name}: {err}"
15
+ )
16
+
17
+
18
+ class InvalidEnumStringException(KeyError): # noqa N818 Historic name
19
+ """
20
+ The enum string that is trying to be set is not listed in the pv.
21
+ """
22
+
23
+ def __init__(self, pv_name: str, valid_states: str) -> None:
24
+ super(InvalidEnumStringException, self).__init__(
25
+ f"Invalid string value entered for {pv_name}. Valid strings are {valid_states}"
26
+ )
27
+
28
+
29
+ class ReadAccessException(IOError): # noqa N818 Historic name
30
+ """
31
+ PV exists but its value is unavailable to read.
32
+ """
33
+
34
+ def __init__(self, pv_name: str) -> None:
35
+ super(ReadAccessException, self).__init__(f"Read access denied for PV {pv_name}")
36
+
37
+
38
+ class WriteAccessException(IOError): # noqa N818 Historic name
39
+ """
40
+ PV was written to but does not allow writes.
41
+ """
42
+
43
+ def __init__(self, pv_name: str) -> None:
44
+ super(WriteAccessException, self).__init__(f"Write access denied for PV {pv_name}")