hutch-python 1.18.4__tar.gz → 1.19.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 (147) hide show
  1. {hutch-python-1.18.4 → hutch_python-1.19.0}/.github/PULL_REQUEST_TEMPLATE.md +9 -0
  2. {hutch-python-1.18.4 → hutch_python-1.19.0}/PKG-INFO +33 -3
  3. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/releases.rst +34 -0
  4. hutch_python-1.19.0/docs/source/upcoming_release_notes/379-enh_limit_devices.rst +22 -0
  5. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/yaml_files.rst +18 -2
  6. hutch_python-1.19.0/hutch_python/_version.py +16 -0
  7. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cam_load.py +1 -1
  8. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/constants.py +1 -0
  9. hutch_python-1.19.0/hutch_python/epics_arch.py +483 -0
  10. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/happi.py +30 -15
  11. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/load_conf.py +10 -2
  12. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/camviewer.cfg +1 -1
  13. hutch_python-1.19.0/hutch_python/tests/happi_db.json +188 -0
  14. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_epics_arch.py +16 -19
  15. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_happi.py +22 -3
  16. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/PKG-INFO +33 -3
  17. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/SOURCES.txt +1 -0
  18. hutch-python-1.18.4/hutch_python/_version.py +0 -4
  19. hutch-python-1.18.4/hutch_python/epics_arch.py +0 -202
  20. hutch-python-1.18.4/hutch_python/tests/happi_db.json +0 -95
  21. {hutch-python-1.18.4 → hutch_python-1.19.0}/.coveragerc +0 -0
  22. {hutch-python-1.18.4 → hutch_python-1.19.0}/.flake8 +0 -0
  23. {hutch-python-1.18.4 → hutch_python-1.19.0}/.git_archival.txt +0 -0
  24. {hutch-python-1.18.4 → hutch_python-1.19.0}/.gitattributes +0 -0
  25. {hutch-python-1.18.4 → hutch_python-1.19.0}/.github/ISSUE_TEMPLATE.md +0 -0
  26. {hutch-python-1.18.4 → hutch_python-1.19.0}/.github/workflows/standard.yml +0 -0
  27. {hutch-python-1.18.4 → hutch_python-1.19.0}/.gitignore +0 -0
  28. {hutch-python-1.18.4 → hutch_python-1.19.0}/.landscape.yml +0 -0
  29. {hutch-python-1.18.4 → hutch_python-1.19.0}/.pre-commit-config.yaml +0 -0
  30. {hutch-python-1.18.4 → hutch_python-1.19.0}/AUTHORS.rst +0 -0
  31. {hutch-python-1.18.4 → hutch_python-1.19.0}/CONTRIBUTING.rst +0 -0
  32. {hutch-python-1.18.4 → hutch_python-1.19.0}/LICENSE +0 -0
  33. {hutch-python-1.18.4 → hutch_python-1.19.0}/MANIFEST.in +0 -0
  34. {hutch-python-1.18.4 → hutch_python-1.19.0}/README.rst +0 -0
  35. {hutch-python-1.18.4 → hutch_python-1.19.0}/bin/hutch-python +0 -0
  36. {hutch-python-1.18.4 → hutch_python-1.19.0}/conda-recipe/meta.yaml +0 -0
  37. {hutch-python-1.18.4 → hutch_python-1.19.0}/dev-requirements.txt +0 -0
  38. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/Makefile +0 -0
  39. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/pre-release-notes.sh +0 -0
  40. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/release_notes.py +0 -0
  41. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/beamline.rst +0 -0
  42. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/bug.rst +0 -0
  43. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/cache.rst +0 -0
  44. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/cli.rst +0 -0
  45. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/conf.py +0 -0
  46. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/daq.rst +0 -0
  47. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/database.rst +0 -0
  48. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/debug.rst +0 -0
  49. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/environment.rst +0 -0
  50. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/experiment.rst +0 -0
  51. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/hutch_setup.rst +0 -0
  52. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/index.rst +0 -0
  53. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/ipython_log.rst +0 -0
  54. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/load_conf.rst +0 -0
  55. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/load_parts.rst +0 -0
  56. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/log_setup.rst +0 -0
  57. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/namespace.rst +0 -0
  58. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/object_configuration.rst +0 -0
  59. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/other_modules.rst +0 -0
  60. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/presets.rst +0 -0
  61. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/report.rst +0 -0
  62. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/start.rst +0 -0
  63. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/startup.rst +0 -0
  64. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/tips.rst +0 -0
  65. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/tutorial.ipynb +0 -0
  66. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/upcoming_release_notes/template-full.rst +0 -0
  67. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/upcoming_release_notes/template-short.rst +0 -0
  68. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/user_utils.rst +0 -0
  69. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs/source/utils.rst +0 -0
  70. {hutch-python-1.18.4 → hutch_python-1.19.0}/docs-requirements.txt +0 -0
  71. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/__init__.py +0 -0
  72. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/__main__.py +0 -0
  73. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/bug.py +0 -0
  74. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cache.py +0 -0
  75. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/calc_defaults.py +0 -0
  76. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cli.py +0 -0
  77. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/cookiecutter.json +0 -0
  78. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/.gitignore +0 -0
  79. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/README.md +0 -0
  80. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/conf.yml +0 -0
  81. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/experiments/__init__.py +0 -0
  82. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/set-dev +0 -0
  83. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}/__init__.py +0 -0
  84. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}/beamline.py +0 -0
  85. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}env +0 -0
  86. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/cookiecutter/{{ cookiecutter.hutch }}/{{ cookiecutter.hutch }}python +0 -0
  87. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/debug.py +0 -0
  88. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/env_version.py +0 -0
  89. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/exp_load.py +0 -0
  90. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/ipython_log.py +0 -0
  91. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/lcls.py +0 -0
  92. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/log_setup.py +0 -0
  93. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/logging.yml +0 -0
  94. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/mpl_config.py +0 -0
  95. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/namespace.py +0 -0
  96. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/obj_config.py +0 -0
  97. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/ophyd_settings.py +0 -0
  98. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/options.py +0 -0
  99. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/plan_defaults.py +0 -0
  100. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/plan_wrappers.py +0 -0
  101. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/print_hint_banner.py +0 -0
  102. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/pt_app_config.py +0 -0
  103. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/qs_load.py +0 -0
  104. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/sim.py +0 -0
  105. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/startup_script.py +0 -0
  106. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/templates/issue.template +0 -0
  107. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/__init__.py +0 -0
  108. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/bad_script.py +0 -0
  109. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/conf.yaml +0 -0
  110. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/conftest.py +0 -0
  111. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/envs/pcds-1.0.0 +0 -0
  112. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/envs/pcds-2.0.0 +0 -0
  113. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/envs/z-env +0 -0
  114. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/experiments/sample_expname.py +0 -0
  115. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/experiments/x010.py +0 -0
  116. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/experiments/x011.py +0 -0
  117. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/sample_module_1.py +0 -0
  118. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/sample_module_2.py +0 -0
  119. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/script.py +0 -0
  120. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_bug.py +0 -0
  121. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_cache.py +0 -0
  122. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_cam_load.py +0 -0
  123. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_cli.py +0 -0
  124. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_experiment.py +0 -0
  125. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_ipython_log.py +0 -0
  126. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_load_conf.py +0 -0
  127. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_log_setup.py +0 -0
  128. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_namespace.py +0 -0
  129. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_obj_config.py +0 -0
  130. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_plan_wrappers.py +0 -0
  131. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_questionnaire.py +0 -0
  132. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_tstpython.py +0 -0
  133. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_user_load.py +0 -0
  134. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/test_utils.py +0 -0
  135. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/tst/__init__.py +0 -0
  136. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/tst/beamline.py +0 -0
  137. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/tests/tstpython +0 -0
  138. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/user_load.py +0 -0
  139. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/utils.py +0 -0
  140. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python/version.py +0 -0
  141. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/dependency_links.txt +0 -0
  142. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/entry_points.txt +0 -0
  143. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/requires.txt +0 -0
  144. {hutch-python-1.18.4 → hutch_python-1.19.0}/hutch_python.egg-info/top_level.txt +0 -0
  145. {hutch-python-1.18.4 → hutch_python-1.19.0}/pyproject.toml +0 -0
  146. {hutch-python-1.18.4 → hutch_python-1.19.0}/requirements.txt +0 -0
  147. {hutch-python-1.18.4 → hutch_python-1.19.0}/setup.cfg +0 -0
@@ -18,3 +18,12 @@
18
18
  <!--
19
19
  ## Screenshots (if appropriate):
20
20
  -->
21
+
22
+ ## Pre-merge checklist
23
+ - [ ] Code works interactively (a real hutch config file can be loaded)
24
+ - [ ] Code contains descriptive docstrings, including context and API
25
+ - [ ] New/changed functions and methods are covered in the test suite where possible
26
+ - [ ] Test suite passes locally
27
+ - [ ] Test suite passes on GitHub Actions
28
+ - [ ] Ran ``docs/pre-release-notes.sh`` and created a pre-release documentation page
29
+ - [ ] Pre-release docs include context, functional descriptions, and contributors as appropriate
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hutch-python
3
- Version: 1.18.4
3
+ Version: 1.19.0
4
4
  Summary: Launcher and Config Reader for LCLS Interactive IPython Sessions
5
5
  Author: SLAC National Accelerator Laboratory
6
6
  License: Copyright (c) 2023, The Board of Trustees of the Leland Stanford Junior
@@ -48,10 +48,40 @@ Classifier: Natural Language :: English
48
48
  Classifier: Programming Language :: Python :: 3
49
49
  Requires-Python: >=3.9
50
50
  Description-Content-Type: text/x-rst
51
- Provides-Extra: test
52
- Provides-Extra: doc
53
51
  License-File: LICENSE
54
52
  License-File: AUTHORS.rst
53
+ Requires-Dist: archapp>=1.0.2
54
+ Requires-Dist: bluesky>=1.8.0
55
+ Requires-Dist: coloredlogs>=15.0.0
56
+ Requires-Dist: cookiecutter>=1.7.0
57
+ Requires-Dist: happi>=1.9.0
58
+ Requires-Dist: ipython>=7.26.0
59
+ Requires-Dist: jinja2>=2.11.0
60
+ Requires-Dist: lightpath>=1.0.1
61
+ Requires-Dist: matplotlib>=3.4.0
62
+ Requires-Dist: nabs>=1.5.0
63
+ Requires-Dist: pcdscalc>=0.3.0
64
+ Requires-Dist: pcdsdaq>=2.3.0
65
+ Requires-Dist: pcdsdevices>=7.0.0
66
+ Requires-Dist: pcdsutils>=0.5.0
67
+ Requires-Dist: psdaq-control-minimal
68
+ Requires-Dist: pyfiglet>=0.8.0
69
+ Requires-Dist: pyqt5
70
+ Requires-Dist: pyyaml>=5.4.0
71
+ Requires-Dist: requests>=2.26.0
72
+ Requires-Dist: simplejson>=3.17.0
73
+ Provides-Extra: test
74
+ Requires-Dist: lightpath; extra == "test"
75
+ Requires-Dist: packaging; extra == "test"
76
+ Requires-Dist: pytest; extra == "test"
77
+ Requires-Dist: pytest-timeout; extra == "test"
78
+ Provides-Extra: doc
79
+ Requires-Dist: docs-versions-menu; extra == "doc"
80
+ Requires-Dist: nbsphinx; extra == "doc"
81
+ Requires-Dist: sphinx; extra == "doc"
82
+ Requires-Dist: sphinx_rtd_theme; extra == "doc"
83
+ Requires-Dist: sphinxcontrib-jquery; extra == "doc"
84
+ Requires-Dist: pypandoc; extra == "doc"
55
85
 
56
86
  ============
57
87
  Hutch Python
@@ -2,6 +2,40 @@ Release History
2
2
  ###############
3
3
 
4
4
 
5
+ v1.19.0 (2024-04-15)
6
+ ====================
7
+
8
+ Features
9
+ --------
10
+ - Updates EpicsArch script, adding several debug/testin flags and ensuring
11
+ information is up-to-date. Includes:
12
+
13
+ - ``update_file``: feature that updates EpicsArch based on current questionnaire
14
+ - ``--softlink``: point arch file to new experiment via symbolic link
15
+ - ``--level``: specify a debug level
16
+ - ``--cds-items``: displays questionnaire data for a given run and experiment
17
+ - ``--link-path``: allows user to provide custom filepath for softlinks
18
+ - Adds load_level conf.yaml key for choosing the method used to gather happi devices
19
+
20
+ Contributors
21
+ ------------
22
+ - c-tsoi
23
+ - tangkong
24
+
25
+
26
+
27
+ v1.18.5 (2023-09-14)
28
+ ====================
29
+
30
+ Maintenance
31
+ -----------
32
+ - Strips whitespace from PVs gathered during cam_load routine.
33
+
34
+ Contributors
35
+ ------------
36
+ - tangkong
37
+
38
+
5
39
  v1.18.4 (2023-07-26)
6
40
  ====================
7
41
 
@@ -0,0 +1,22 @@
1
+ 379 enh_limit_devices
2
+ #####################
3
+
4
+ API Changes
5
+ -----------
6
+ - N/A
7
+
8
+ Features
9
+ --------
10
+ - Adds load_level conf.yaml key for choosing the method used to gather happi devices
11
+
12
+ Bugfixes
13
+ --------
14
+ - N/A
15
+
16
+ Maintenance
17
+ -----------
18
+ - N/A
19
+
20
+ Contributors
21
+ ------------
22
+ - tangkong
@@ -3,8 +3,8 @@ Yaml Files
3
3
 
4
4
  ``hutch-python`` uses a ``conf.yml`` file for basic configuration. This is a
5
5
  standard yaml file with the following valid keys:
6
- ``hutch``, ``db``, ``load``, ``experiment``, ``obj_config``, ``daq_type``,
7
- ``daq_host``, and ``daq_platform``.
6
+ ``hutch``, ``db``, ``load``, ``load_level``, ``experiment``, ``obj_config``,
7
+ ``daq_type``, ``daq_host``, and ``daq_platform``.
8
8
 
9
9
 
10
10
  hutch
@@ -69,6 +69,22 @@ This key is used to include hutch-specific code.
69
69
  ``from module import *`` for each of these modules.
70
70
 
71
71
 
72
+ load_level
73
+ ----------
74
+ The ``load_level`` key expects one of the following strings, corresponding to the
75
+ amount of ophyd devices to load:
76
+
77
+ - ``UPSTREAM``: The hutch's devices, and devices upstream from the requested hutch.
78
+ If there are multiple paths to the requested hutch, all paths' devices are loaded.
79
+ - ``STANDARD``: Devices gathered via ``UPSTREAM``, plus devices that share the
80
+ "beamline" field in happi with the ``UPSTREAM`` devices. (The current standard)
81
+ - ``ALL``: All devices in the happi database. Use this option at your own risk.
82
+
83
+ .. code-block:: YAML
84
+
85
+ load_level: UPSTREAM
86
+
87
+
72
88
  experiment
73
89
  ----------
74
90
 
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '1.19.0'
16
+ __version_tuple__ = version_tuple = (1, 19, 0)
@@ -230,7 +230,7 @@ def get_det_prefix(pv_info):
230
230
  """
231
231
  pv_info = pv_info.split(';')
232
232
  try:
233
- detector_prefix = pv_info[1]
233
+ detector_prefix = pv_info[1].strip()
234
234
  except IndexError:
235
235
  # Not provided in config, guess from image base
236
236
  detector_prefix = ':'.join(pv_info[0].split(':')[:-1])
@@ -32,6 +32,7 @@ VALID_KEYS = (
32
32
  'hutch',
33
33
  'db',
34
34
  'load',
35
+ 'load_level',
35
36
  'experiment',
36
37
  'daq_platform',
37
38
  'daq_type',
@@ -0,0 +1,483 @@
1
+ """Module to help create the epicsArch file that will be read by the DAQ."""
2
+ import argparse
3
+ import logging
4
+ import os
5
+ import re
6
+ import subprocess
7
+ import sys
8
+ from dataclasses import dataclass
9
+
10
+ from prettytable import PrettyTable
11
+
12
+ from .constants import EPICS_ARCH_FILE_PATH
13
+ from .qs_load import get_qs_client
14
+
15
+ try:
16
+ import psdm_qs_cli
17
+ from psdm_qs_cli import QuestionnaireClient
18
+ except ImportError:
19
+ psdm_qs_cli = None
20
+ QuestionnaireClient = None
21
+
22
+
23
+ # Annotation with dataclass, making struct to help organize cds objects in prettytable
24
+ @dataclass
25
+ class QStruct:
26
+ alias: str
27
+ pvbase: str
28
+ pvtype: str
29
+
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ def _create_parser():
35
+ """Argument Parser Setup. Define shell commands."""
36
+ parser = argparse.ArgumentParser(description='Create an epicsArch file '
37
+ 'from the Quesionnaire')
38
+
39
+ parser.add_argument('experiment', help='Experiment name to'
40
+ ' create the epicsArch file from. E.g.: xpplv6818')
41
+
42
+ parser.add_argument('--hutch', action="store",
43
+ help='Overrides hutch detection from the experiment '
44
+ 'name. E.g.: xpp', default=None)
45
+
46
+ parser.add_argument('--path', action="store",
47
+ help='Directory path where to create the epicsArch '
48
+ 'file. E.g.: /path/to/the/directory/', default=None)
49
+
50
+ parser.add_argument('--dry-run', action='store_true', default=False,
51
+ help='Print to stdout what would be written in the '
52
+ 'archFile.')
53
+
54
+ parser.add_argument('--level', '-l', required=False, type=str, default="INFO",
55
+ help='Change the logging level, e.g. DEBUG to show the debug logging stream')
56
+
57
+ parser.add_argument('--cds-items', nargs=2, action='store', default=None,
58
+ help="Pulls all data from CDS tab in the form of a "
59
+ "dictionary. E.g.: xppx1003221 --cds-items run_xx "
60
+ "experiment_name, where xx is the run number. This option "
61
+ "will not automatically update the archfile.")
62
+
63
+ parser.add_argument('--softlink', '-sl', action='store_true', default=None,
64
+ help="create softlink for experiment. This is run after "
65
+ "the archfile has been updated/created.")
66
+ parser.add_argument('--link-path', '-sl_path', action='store',
67
+ default=EPICS_ARCH_FILE_PATH,
68
+ help="Provide user with option to supply custom path for "
69
+ "softlink. Defaults to: /cds/group/pcds/dist/pds/{}/misc/.")
70
+ return parser
71
+
72
+
73
+ def main():
74
+ """Entry point."""
75
+ parser = _create_parser()
76
+ parsed_args = parser.parse_args()
77
+ kwargs = vars(parsed_args)
78
+ logger_setup(parsed_args)
79
+ create_arch_file(**kwargs)
80
+
81
+
82
+ def logger_setup(args):
83
+ # Setting up the logger, to show the level when enabled
84
+ logging.getLogger().addHandler(logging.NullHandler())
85
+ logging.basicConfig(level="INFO")
86
+ logger.setLevel(args.level)
87
+
88
+
89
+ def create_arch_file(experiment, level=None, hutch=None, path=None, dry_run=False,
90
+ cds_items=None, softlink=None, link_path=None):
91
+ """
92
+ Create an epicsArch file for the experiment.
93
+
94
+ Parameter
95
+ ---------
96
+ experiment : str
97
+ Experiment name, e.g.: `xpplv6818`
98
+ hutch : str
99
+ Hutch name if other than the one from the experiment, e.g.: `xcs`
100
+ path : str
101
+ Directory where to create the file if other than the default one which
102
+ is `/cds/group/pcds/dist/pds/{}/misc/`
103
+ dry_run : bool
104
+ To indicate if only print to stdout the data that would be stored
105
+ in the epicsArch file and not create the file.
106
+ link_path : str
107
+ Path to overwrite softlink to experiment specific arch file.
108
+ cds_items : str
109
+ Pulls cds tab data in the form of a dictionary and prints this to Pretty Table.
110
+
111
+ Examples
112
+ --------
113
+ Create the epicsArch file for experiment `xpplv6818`. If no `--path` or
114
+ `--hutch` provided, write the file at the default path:
115
+ `/cds/group/pcds/dist/pds/{}/misc/` whre {} is the hutch name from the
116
+ experiment (`xpp`):
117
+
118
+ >>> epicsarch-qs xpplv6818
119
+
120
+ Would write: `/cds/group/pcds/dist/pds/xpp/misc/epicsArch_xpplv6818.txt`
121
+
122
+ To change the path where you want to write the epicsArch file:
123
+
124
+ >>> epicsarch-qs xpplv6818 --path /my/new/path/
125
+
126
+ Would write: `/my/new/path/epicsArch_xpplv6818.txt`
127
+
128
+ To change only the hutch name in the path:
129
+
130
+ >>> epicsarch-qs xpplv6818 --hutch xcs
131
+
132
+ Would write: `/cds/group/pcds/dist/pds/xcs/misc/epicsArch_xpplv6818.txt`
133
+
134
+ To print to standard output what would be written in the epicsArch file:
135
+
136
+ >>> epicsarch-qs xpplv6818 --dry-run
137
+ """
138
+ file_path = None
139
+ if experiment and not dry_run:
140
+ # set the path to write the epicsArch file to.
141
+ if cds_items:
142
+ pull_cds_data(experiment, cds_items)
143
+ return
144
+ if path:
145
+ if path and not os.path.exists(path):
146
+ raise OSError('Invalid path: %s' % path)
147
+ file_path = path
148
+ elif hutch:
149
+ file_path = EPICS_ARCH_FILE_PATH.format(hutch.lower())
150
+ else:
151
+ file_path = EPICS_ARCH_FILE_PATH.format(experiment[0:3])
152
+ update_file(exp_name=experiment, path=file_path)
153
+ if softlink:
154
+ create_softlink(experiment, link_path)
155
+ elif dry_run:
156
+ print_dry_run(experiment)
157
+
158
+
159
+ def pull_cds_data(exp, run):
160
+ """
161
+ Gather all user objects from the CDS tab in the questionnaire.
162
+ Parse objects and separate them based on type.
163
+ Display them in the console via PrettyTable.
164
+
165
+ Parameters
166
+ ----------
167
+ exp: ``str``
168
+ The experiment's name e.g. xppx1003221
169
+ run: ''str''
170
+ The run number e.g. run21
171
+
172
+ Outputs
173
+ -------
174
+ PrettyTable visualization of cds objects
175
+ """
176
+ # pull run data from questionnaire api, then take the data and sort it
177
+ # create PrettyTable instance and if the values from the run data contain
178
+ # pcdssetup then put them into a seperate dictionary as they are cds items
179
+ logger.debug('pull_cds_items(%s)', exp)
180
+ client = QuestionnaireClient()
181
+ logger.debug("in cds items, run numb:", str(run[1]))
182
+ runDetails_Dict = client.getProposalDetailsForRun(str(run[0]), str(run[1]))
183
+ sorted_runDetails_Dict = dict(sorted(runDetails_Dict.items()))
184
+ cds_dict = {}
185
+ myTable = PrettyTable(["Alias", "PV Base", "Type"])
186
+ for keys, vals in sorted_runDetails_Dict.items():
187
+ if "pcdssetup" in keys:
188
+ cds_dict[keys] = vals
189
+
190
+ # names are as follows:
191
+ # pcdssetup-motors, pcdssetup-areadet, pcdssetup-ao, pcdssetup-devs
192
+ # pcdssetup-ps, pcdssetup-trig, pcdssetup-vacuum, pcdssetup-temp
193
+
194
+ # iterate through all cds items and label them based on their type
195
+ # use the struct members to identify
196
+ displayList = []
197
+ for k, v in cds_dict.items():
198
+ if re.match('pcdssetup-motors.*-name', k):
199
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
200
+ displayList.append(QStruct(v, pv, "motors"))
201
+ elif re.match('pcdssetup-areadet.*-name', k):
202
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
203
+ displayList.append(QStruct(v, pv, "areadet"))
204
+ elif re.match('pcdssetup-ao.*-name', k):
205
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
206
+ displayList.append(QStruct(v, pv, "analog output"))
207
+ elif re.match('pcdssetup-devs.*-name', k):
208
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
209
+ displayList.append(QStruct(v, pv, "other devices"))
210
+ elif re.match('pcdssetup-ps.*-name', k):
211
+ pv = cds_dict.get(re.sub('name', 'pvname', k), '')
212
+ displayList.append(QStruct(v, pv, "power supplies"))
213
+ elif re.match('pcdssetup-trig.*-name', k):
214
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
215
+ displayList.append(QStruct(v, pv, "triggers"))
216
+ elif re.match('pcdssetup-vacuum.*-name', k):
217
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
218
+ displayList.append(QStruct(v, pv, "vacuum"))
219
+ elif re.match('pcdssetup-temp.*-name', k):
220
+ pv = cds_dict.get(re.sub('name', 'pvbase', k), '')
221
+ displayList.append(QStruct(v, pv, "temperature"))
222
+
223
+ for struct in displayList:
224
+ myTable.add_row([struct.alias, struct.pvbase, struct.pvtype])
225
+ print(myTable)
226
+
227
+
228
+ def create_softlink(experiment, link_path):
229
+ """
230
+ This removes the softlink in the /cds/group/pcds/dist/pds/{}/misc/ and
231
+ overwrites it with the new active experiment.
232
+ """
233
+ # Defaults new softlink in /cds/group/pcds/dist/pds/{}/misc/
234
+ if not os.path.exists(link_path):
235
+ raise OSError('Path does not exist path: %s' % link_path)
236
+
237
+ subprocess.run(['ln', '-sf', link_path.format(experiment[0:3]) + 'epicsArch_'
238
+ + experiment + '.txt', link_path.format(experiment[0:3])
239
+ + 'epicsArch_' + experiment[0:3].upper() + '_exp_specific.txt'])
240
+
241
+
242
+ def check_for_duplicates(qs_data, af_data):
243
+ """
244
+ Check for duplicate PVs in the questionnaire, the code already throws a
245
+ warning for duplicate aliases. If duplicates (PV or Alias) are found in the
246
+ questionnaire throw error and prompt user to fix and re-run. If they are
247
+ found in the epicsArch file then step through each match and update
248
+ accordingly.
249
+
250
+ Parameters
251
+ ----------
252
+ qs_data : list
253
+ af_data : list
254
+
255
+ Examples
256
+ --------
257
+ >>> epicsarch-qs xpplv6818 --dry-run
258
+
259
+ Returns
260
+ -------
261
+ updated_arch_list : list
262
+ Updated list containing sorted alias, PVs.
263
+ """
264
+ # PART 1
265
+ # Convert lists to dictionaries to sort as a key - value pair while also
266
+ # removing any whitespice in the aliases.
267
+
268
+ # Questionnaire Data, removing whitespaces and newline chars
269
+ qsDict = dict(zip(qs_data[::2], qs_data[1::2]))
270
+ qsDict = {k.replace(" ", ""): v for k, v in qsDict.items()}
271
+ qsDict = {k.replace("\n", ""): v for k, v in qsDict.items()}
272
+ qsDict = {k: v.replace(" ", "") for k, v in qsDict.items()}
273
+ qsDict = {k: v.replace("\n", "") for k, v in qsDict.items()}
274
+ sorted_qsDict = dict(sorted(qsDict.items()))
275
+
276
+ # If the archfile is not empty then clean it if not ,skip
277
+ if len(af_data) > 0:
278
+ # ArchFile Data, removing whitespaces and newline chars
279
+ afDict = dict(zip(af_data[::2], af_data[1::2]))
280
+ afDict = {k.replace(" ", ""): v for k, v in afDict.items()}
281
+ afDict = {k.replace("\n", ""): v for k, v in afDict.items()}
282
+ afDict = {k: v.replace(" ", "") for k, v in afDict.items()}
283
+ afDict = {k: v.replace("\n", "") for k, v in afDict.items()}
284
+ sorted_afDict = dict(sorted(afDict.items()))
285
+ else:
286
+ afDict = {}
287
+ sorted_afDict = {}
288
+
289
+ # PART 2
290
+ # Check the questionaire for duplicate PVs
291
+ # Making reverse multidict to help identify duplicate values in questionnaire.
292
+ rev_keyDict = {}
293
+ for key, value in sorted_qsDict.items():
294
+ rev_keyDict.setdefault(value, list()).append(key)
295
+
296
+ pvDuplicate = [key for key, values in rev_keyDict.items() if len(values) > 1]
297
+ # Looking for duplicates of PVs in the questionaire
298
+ # also print out the alias for PV, change removing to warning operator to remove dup then rerun
299
+ for dup in pvDuplicate:
300
+ logger.debug("!Duplicate PV in questionnaire!:" + str(dup))
301
+ for value in rev_keyDict[dup][1:]:
302
+ logger.debug("Found PV duplicate(s) from questionnaire: " + value
303
+ + ", " + sorted_qsDict[value])
304
+ raise ValueError("Please remove duplicates and re-run script!")
305
+
306
+ # Check to see if the archfile has any data in it
307
+ if len(af_data) == 0:
308
+ logger.debug("CFD: Case: no archfile given, returning cleaned questionnaire data.")
309
+ cleaned_qs_data = [x for item in sorted_qsDict.items() for x in item]
310
+ return cleaned_qs_data
311
+
312
+ # Once we have cleared any duplicates in the questionnaire we moving on to
313
+ # updating values according to the which field matches.
314
+
315
+ # Checking for matching PVs in questionnaire and archfile
316
+ # if the PV matches update the alias by removing the old key and making a new one
317
+ for (k, val) in sorted_qsDict.items():
318
+ # this looks up the key in the af Dictionary by finding the value
319
+ foundKey = get_key(val, sorted_afDict)
320
+ if k in sorted_afDict:
321
+ logger.debug("!Alias Match in questionnaire and archfile! Updating PV: "
322
+ + k + ", " + sorted_qsDict[k])
323
+ sorted_afDict[k] = sorted_qsDict[k]
324
+ elif foundKey:
325
+ del sorted_afDict[foundKey]
326
+ sorted_afDict[k] = val
327
+ logger.debug("!PV Match in questionnaire and archfile! Updating Alias: "
328
+ + k + ", " + val)
329
+
330
+ sorted_afDict = dict(sorted(sorted_afDict.items()))
331
+ updated_arch_list = [x for item in sorted_afDict.items() for x in item]
332
+ logger.debug("\nUpdated Arch List:\n")
333
+ logger.debug(updated_arch_list)
334
+ return updated_arch_list
335
+
336
+
337
+ def read_archfile(exp_path):
338
+ if os.path.exists(exp_path):
339
+ with open(exp_path, "r") as experiment:
340
+ lines = experiment.readlines()
341
+ return lines
342
+
343
+
344
+ def print_dry_run(exp_name):
345
+ """
346
+ Print to stdout the data that would be stored in the epicsArch file.
347
+
348
+ Parameters
349
+ ----------
350
+ exp_name : str
351
+ Experiment name, e.g.: xpplv6818
352
+
353
+ Examples
354
+ --------
355
+ >>> epicsarch-qs xpplv6818 --dry-run
356
+ """
357
+
358
+ qs_data = get_questionnaire_data(exp_name)
359
+
360
+ """
361
+ Updating experiment file.
362
+ """
363
+
364
+ af_path = EPICS_ARCH_FILE_PATH.format(exp_name[0:3]) + 'epicsArch_' + exp_name + '.txt'
365
+ af_data = read_archfile(af_path)
366
+ if not os.path.exists(af_path):
367
+ raise OSError('print_dry_run, invalid path: %s' % af_path)
368
+ elif os.path.exists(af_path):
369
+ updated_archFile = check_for_duplicates(qs_data, af_data)
370
+
371
+ for item in updated_archFile:
372
+ print(item)
373
+
374
+
375
+ def get_key(val, my_dict):
376
+ for k, v in my_dict.items():
377
+ if val == v:
378
+ return k
379
+ return None
380
+
381
+
382
+ def get_questionnaire_data(exp_name):
383
+ """
384
+ Return a list of items that would be written in the epicsArch file.
385
+
386
+ Parameters
387
+ ----------
388
+ exp_name : str
389
+ Experiment name, e.g.: xpplv6818
390
+
391
+ Returns
392
+ -------
393
+ data_list : list
394
+ List containing the names ans prefixes of items in the Questionnaire.
395
+ """
396
+ data_list = []
397
+ items = get_items(exp_name)
398
+
399
+ if items:
400
+ for item in items:
401
+ name = ''.join(('* ', item.name))
402
+ data_list.append(name)
403
+ data_list.append(item.prefix)
404
+ return data_list
405
+
406
+
407
+ def get_items(exp_name):
408
+ """
409
+ Get all_items from Questionnaire.
410
+
411
+ Parameters
412
+ ----------
413
+ exp_name : str
414
+ Experiment name, e.g.: xpplv6818
415
+
416
+ Returns
417
+ -------
418
+ items : list
419
+ List of all_items from Questionnaire.
420
+ """
421
+ try:
422
+ qs_client = get_qs_client(exp_name)
423
+ except Exception as ex:
424
+ logger.error('Failed to load the Questionnaire, %s', ex)
425
+ raise ex
426
+ sys.exit()
427
+ items = qs_client.all_items
428
+ if not items:
429
+ logger.warning("No devices found in PCDS Questionnaire for %s",
430
+ exp_name)
431
+ return
432
+ return items
433
+
434
+
435
+ def update_file(exp_name, path):
436
+ """
437
+ Create a file with aliases and pvs from the questionnaire.
438
+
439
+ Parameters
440
+ ----------
441
+ exp_name : str
442
+ Experiment name, e.g.: `xpplv6818`
443
+ path : str
444
+ Directory where to create the epicsArch file.
445
+ """
446
+ qs_data = get_questionnaire_data(exp_name)
447
+
448
+ path = str(path)
449
+ exp_name = str(exp_name)
450
+
451
+ logger.debug("UpdateFile: qs_data:\n" + str(qs_data))
452
+ logger.debug("\nPath: " + path)
453
+
454
+ af_path = path + "epicsArch_" + str(exp_name) + ".txt"
455
+
456
+ logger.debug("\nAF Path: " + af_path)
457
+
458
+ file_path = ''.join((path, 'epicsArch_', exp_name, '.txt'))
459
+ if not os.path.exists(path):
460
+ raise OSError('Invalid path: %s' % path)
461
+ # if the path exists but archfile does not, create af and pull qsd
462
+ elif os.path.exists(path) and not os.path.exists(af_path):
463
+ logger.debug("UpdateFile: Path is valid, creating archfile\n")
464
+ logger.debug('Creating epicsArch file for experiment: %s', exp_name)
465
+ cleaned_data = check_for_duplicates(qs_data, {})
466
+
467
+ # if the path and archfile exists, update af and pull
468
+ elif os.path.exists(path) and os.path.exists(af_path):
469
+ logger.debug("UpdateFile: Path exists and archfile exists\n")
470
+ af_data = read_archfile(af_path)
471
+ cleaned_data = check_for_duplicates(qs_data, af_data)
472
+
473
+ # Write updates to the corresponding file
474
+ with open(file_path, 'w') as f:
475
+ for data in cleaned_data:
476
+ try:
477
+ f.write(f'{data}\n')
478
+ except OSError as ex:
479
+ logger.error('Could not write file %s, %s', file_path, ex)
480
+
481
+
482
+ if __name__ == '__main__':
483
+ main()