apsbits 1.0.6__tar.gz → 2.0.1__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 (134) hide show
  1. {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/code.yml +2 -2
  2. {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/docs.yml +1 -1
  3. {apsbits-1.0.6 → apsbits-2.0.1}/.github/workflows/pypi.yml +1 -1
  4. {apsbits-1.0.6 → apsbits-2.0.1}/.gitignore +1 -0
  5. {apsbits-1.0.6 → apsbits-2.0.1}/HISTORY.rst +19 -1
  6. {apsbits-1.0.6 → apsbits-2.0.1}/PKG-INFO +2 -1
  7. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/PKG-INFO +2 -1
  8. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/SOURCES.txt +2 -0
  9. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/requires.txt +1 -0
  10. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/startup.rst +1 -1
  11. {apsbits-1.0.6 → apsbits-2.0.1}/pyproject.toml +1 -0
  12. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/_version.py +3 -3
  13. apsbits-2.0.1/src/apsbits/core/catalog_init.py +143 -0
  14. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/run_engine_init.py +3 -7
  15. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/iconfig.yml +5 -1
  16. apsbits-2.0.1/src/apsbits/tests/conftest.py +124 -0
  17. apsbits-2.0.1/src/apsbits/tests/test_catalog_init.py +121 -0
  18. apsbits-2.0.1/src/apsbits/tests/test_controls_setup.py +98 -0
  19. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_general.py +6 -5
  20. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/controls_setup.py +52 -19
  21. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/helper_functions.py +3 -3
  22. apsbits-1.0.6/src/apsbits/core/catalog_init.py +0 -60
  23. apsbits-1.0.6/src/apsbits/tests/conftest.py +0 -44
  24. {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  26. {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  27. {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/other.md +0 -0
  28. {apsbits-1.0.6 → apsbits-2.0.1}/.github/ISSUE_TEMPLATE/question-issue-template.md +0 -0
  29. {apsbits-1.0.6 → apsbits-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  30. {apsbits-1.0.6 → apsbits-2.0.1}/.github/dependabot.yml +0 -0
  31. {apsbits-1.0.6 → apsbits-2.0.1}/.pre-commit-config.yaml +0 -0
  32. {apsbits-1.0.6 → apsbits-2.0.1}/LICENSE +0 -0
  33. {apsbits-1.0.6 → apsbits-2.0.1}/README.md +0 -0
  34. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/dependency_links.txt +0 -0
  35. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/entry_points.txt +0 -0
  36. {apsbits-1.0.6 → apsbits-2.0.1}/apsbits.egg-info/top_level.txt +0 -0
  37. {apsbits-1.0.6 → apsbits-2.0.1}/docs/Makefile +0 -0
  38. {apsbits-1.0.6 → apsbits-2.0.1}/docs/make.bat +0 -0
  39. {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/create-repository-name.webp +0 -0
  40. {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/create-repository-owner.webp +0 -0
  41. {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/demo.ipynb +0 -0
  42. {apsbits-1.0.6 → apsbits-2.0.1}/docs/resources/use-this-template-button.webp +0 -0
  43. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/_static/.gitkeep +0 -0
  44. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/api.rst +0 -0
  45. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/core.rst +0 -0
  46. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/demo_instrument.rst +0 -0
  47. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/demo_qserver.rst +0 -0
  48. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.best_effort_init.rst +0 -0
  49. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.catalog_init.rst +0 -0
  50. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.core.run_engine_init.rst +0 -0
  51. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.demo_nexus_callback.rst +0 -0
  52. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.demo_spec_callback.rst +0 -0
  53. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.callbacks.rst +0 -0
  54. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.configs.rst +0 -0
  55. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.devices.rst +0 -0
  56. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.plans.rst +0 -0
  57. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.plans.sim_plans.rst +0 -0
  58. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.rst +0 -0
  59. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_instrument.startup.rst +0 -0
  60. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.demo_qserver.rst +0 -0
  61. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.config_loaders.rst +0 -0
  62. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.controls_setup.rst +0 -0
  63. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.helper_functions.rst +0 -0
  64. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.logging_setup.rst +0 -0
  65. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.metadata.rst +0 -0
  66. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/generated/apsbits.utils.stored_dict.rst +0 -0
  67. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/index.rst +0 -0
  68. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/api/utils.rst +0 -0
  69. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/bits_overview.rst +0 -0
  70. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/conf.py +0 -0
  71. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/console.rst +0 -0
  72. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/dm.md +0 -0
  73. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/logging_config.rst +0 -0
  74. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/notebook.rst +0 -0
  75. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/deprecated/script.rst +0 -0
  76. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/creating_devices.rst +0 -0
  77. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/creating_instrument.rst +0 -0
  78. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/developing_bits.rst +0 -0
  79. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/dm.rst +0 -0
  80. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/index.rst +0 -0
  81. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/logging.rst +0 -0
  82. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/qserver.rst +0 -0
  83. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/qserver_service.rst +0 -0
  84. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/sessions.rst +0 -0
  85. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/setting_iconfig.rst +0 -0
  86. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/guides/template_creation.rst +0 -0
  87. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/history.rst +0 -0
  88. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/index.rst +0 -0
  89. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/install.rst +0 -0
  90. {apsbits-1.0.6 → apsbits-2.0.1}/docs/source/license.rst +0 -0
  91. {apsbits-1.0.6 → apsbits-2.0.1}/setup.cfg +0 -0
  92. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/__init__.py +0 -0
  93. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/__init__.py +0 -0
  94. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/create_new_instrument.py +0 -0
  95. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/delete_instrument.py +0 -0
  96. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/api/run_instrument.py +0 -0
  97. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/configs/logging.yml +0 -0
  98. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/__init__.py +0 -0
  99. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/best_effort_init.py +0 -0
  100. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/core/instrument_init.py +0 -0
  101. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/README.md +0 -0
  102. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/__init__.py +0 -0
  103. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/__init__.py +0 -0
  104. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/demo_nexus_callback.py +0 -0
  105. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/callbacks/demo_spec_callback.py +0 -0
  106. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/__init__.py +0 -0
  107. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/devices.yml +0 -0
  108. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/devices_aps_only.yml +0 -0
  109. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/configs/extra_logging.yml +0 -0
  110. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/devices/__init__.py +0 -0
  111. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/plans/__init__.py +0 -0
  112. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/plans/sim_plans.py +0 -0
  113. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/qserver/qs-config.yml +0 -0
  114. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/qserver/user_group_permissions.yaml +0 -0
  115. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/startup.py +0 -0
  116. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/suspenders/__init__.py +0 -0
  117. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_instrument/utils/__init__.py +0 -0
  118. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/demo_scripts/qs_host.sh +0 -0
  119. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/py.typed +0 -0
  120. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/__init__.py +0 -0
  121. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_config.py +0 -0
  122. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_delete_instrument.py +0 -0
  123. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_device_factories.py +0 -0
  124. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_make_devices.py +0 -0
  125. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_run_instrument.py +0 -0
  126. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/tests/test_stored_dict.py +0 -0
  127. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/__init__.py +0 -0
  128. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/aps_functions.py +0 -0
  129. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/baseline_setup.py +0 -0
  130. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/config_loaders.py +0 -0
  131. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/logging_setup.py +0 -0
  132. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/metadata.py +0 -0
  133. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/sim_creator.py +0 -0
  134. {apsbits-1.0.6 → apsbits-2.0.1}/src/apsbits/utils/stored_dict.py +0 -0
@@ -20,7 +20,7 @@ jobs:
20
20
  runs-on: ubuntu-latest
21
21
 
22
22
  steps:
23
- - uses: actions/checkout@v5
23
+ - uses: actions/checkout@v6
24
24
 
25
25
  - uses: actions/setup-python@v6
26
26
  with:
@@ -51,7 +51,7 @@ jobs:
51
51
  DISPLAY: ":99.0"
52
52
 
53
53
  steps:
54
- - uses: actions/checkout@v5
54
+ - uses: actions/checkout@v6
55
55
 
56
56
  - name: Install OS libraries to test Linux PyQt apps
57
57
  run: |
@@ -50,7 +50,7 @@ jobs:
50
50
  run: echo "TZ=America/Chicago" >> "$GITHUB_ENV"
51
51
 
52
52
  - name: Checkout
53
- uses: actions/checkout@v5
53
+ uses: actions/checkout@v6
54
54
  with:
55
55
  fetch-depth: 0 # required for push to dest repo
56
56
 
@@ -18,7 +18,7 @@ jobs:
18
18
  runs-on: ubuntu-latest
19
19
 
20
20
  steps:
21
- - uses: actions/checkout@v5
21
+ - uses: actions/checkout@v6
22
22
  - name: Set up Python
23
23
  uses: actions/setup-python@v6
24
24
  with:
@@ -171,6 +171,7 @@ dev_*
171
171
  existing_plans_and_devices.yaml
172
172
  .vscode/
173
173
  .logs/
174
+ .loglogin
174
175
  *.dat
175
176
  *.hdf
176
177
 
@@ -30,7 +30,7 @@ describe future plans.
30
30
  .. Coming release content can be gathered here.
31
31
  Some people object to publishing unreleased changes.
32
32
 
33
- 1.1.0
33
+ 2.0.2
34
34
  #####
35
35
 
36
36
  release expected ?
@@ -46,6 +46,24 @@ describe future plans.
46
46
  * Bump iconfig version to 2.0.1 for the baseline addition.
47
47
  * Remove run_engine section from QS config.yml file and pin QS to 0.0.22+.
48
48
 
49
+ 2.0.1
50
+ #####
51
+
52
+ released 2026-01-13
53
+
54
+ Enhancements
55
+ ------------
56
+
57
+ * add support for TILED_PROFILE_NAME & TILED_PATH_NAME
58
+
59
+ 2.0.0 (PyPI) and 1.0.6 (repository)
60
+ ###################################
61
+
62
+ released 2025-10-21
63
+
64
+ Note that PyPI version (2.0.0) is different than repository (1.0.6).
65
+ They share the same source code hash (``484b02f1537301``).
66
+
49
67
  1.0.4
50
68
  #####
51
69
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apsbits
3
- Version: 1.0.6
3
+ Version: 2.0.1
4
4
  Summary: Model of a Bluesky Data Acquisition Instrument in console, notebook, & queueserver.
5
5
  Author-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
6
6
  Maintainer-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
@@ -20,6 +20,7 @@ License-File: LICENSE
20
20
  Requires-Dist: apstools
21
21
  Requires-Dist: bluesky-queueserver>=0.0.22
22
22
  Requires-Dist: bluesky-queueserver-api
23
+ Requires-Dist: bluesky-tiled-plugins
23
24
  Requires-Dist: bluesky-widgets
24
25
  Requires-Dist: bluesky
25
26
  Requires-Dist: caproto
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apsbits
3
- Version: 1.0.6
3
+ Version: 2.0.1
4
4
  Summary: Model of a Bluesky Data Acquisition Instrument in console, notebook, & queueserver.
5
5
  Author-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
6
6
  Maintainer-email: Eric Codrea <ecodrea@anl.gov>, Pete Jemian <prjemian+instrument@gmail.com>, Rafael Vescovi <rvescovi@anl.gov>
@@ -20,6 +20,7 @@ License-File: LICENSE
20
20
  Requires-Dist: apstools
21
21
  Requires-Dist: bluesky-queueserver>=0.0.22
22
22
  Requires-Dist: bluesky-queueserver-api
23
+ Requires-Dist: bluesky-tiled-plugins
23
24
  Requires-Dist: bluesky-widgets
24
25
  Requires-Dist: bluesky
25
26
  Requires-Dist: caproto
@@ -109,7 +109,9 @@ src/apsbits/demo_instrument/utils/__init__.py
109
109
  src/apsbits/demo_scripts/qs_host.sh
110
110
  src/apsbits/tests/__init__.py
111
111
  src/apsbits/tests/conftest.py
112
+ src/apsbits/tests/test_catalog_init.py
112
113
  src/apsbits/tests/test_config.py
114
+ src/apsbits/tests/test_controls_setup.py
113
115
  src/apsbits/tests/test_delete_instrument.py
114
116
  src/apsbits/tests/test_device_factories.py
115
117
  src/apsbits/tests/test_general.py
@@ -1,6 +1,7 @@
1
1
  apstools
2
2
  bluesky-queueserver>=0.0.22
3
3
  bluesky-queueserver-api
4
+ bluesky-tiled-plugins
4
5
  bluesky-widgets
5
6
  bluesky
6
7
  caproto
@@ -70,7 +70,7 @@ Bluesky Initialization
70
70
 
71
71
  bec, peaks = init_bec_peaks(iconfig)
72
72
  cat = init_catalog(iconfig)
73
- RE, sd = init_RE(iconfig, bec_instance=bec, cat_instance=cat)
73
+ RE, sd = init_RE(iconfig, subscribers=[bec, cat])
74
74
 
75
75
  This block initializes:
76
76
  * Best Effort Callback (BEC) and peak finding
@@ -36,6 +36,7 @@ dependencies = [
36
36
  "apstools",
37
37
  "bluesky-queueserver >=0.0.22",
38
38
  "bluesky-queueserver-api",
39
+ "bluesky-tiled-plugins",
39
40
  "bluesky-widgets",
40
41
  "bluesky",
41
42
  "caproto",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.0.6'
32
- __version_tuple__ = version_tuple = (1, 0, 6)
31
+ __version__ = version = '2.0.1'
32
+ __version_tuple__ = version_tuple = (2, 0, 1)
33
33
 
34
- __commit_id__ = commit_id = 'g484b02f15'
34
+ __commit_id__ = commit_id = 'ge2e0b30f3'
@@ -0,0 +1,143 @@
1
+ """
2
+ Databroker catalog
3
+ ==================
4
+
5
+ .. autosummary::
6
+ ~init_catalog
7
+ """
8
+
9
+ import logging
10
+ import weakref
11
+ from typing import Any
12
+ from typing import Union
13
+
14
+ import databroker
15
+ from bluesky_tiled_plugins.clients.catalog_of_bluesky_runs import CatalogOfBlueskyRuns
16
+ from databroker._drivers.mongo_normalized import BlueskyMongoCatalog
17
+ from databroker._drivers.msgpack import BlueskyMsgpackCatalog
18
+ from tiled.client import from_profile
19
+ from tiled.client import from_uri
20
+ from tiled.client.container import Container
21
+ from tiled.server import SimpleTiledServer
22
+
23
+ logger = logging.getLogger(__name__)
24
+ logger.bsdev(__file__)
25
+
26
+ # The httpx (via tiled) logger is set too noisy. Make it quieter.
27
+ logging.getLogger("httpx").setLevel(logging.WARNING)
28
+
29
+ DATABROKER_CATALOG_TYPE = Union[BlueskyMongoCatalog, BlueskyMsgpackCatalog]
30
+ TILED_CATALOG_TYPE = Union[CatalogOfBlueskyRuns, Container]
31
+ ANY_CATALOG_TYPE = Union[DATABROKER_CATALOG_TYPE, TILED_CATALOG_TYPE]
32
+
33
+
34
+ def init_catalog(iconfig: dict[str, Any]) -> ANY_CATALOG_TYPE:
35
+ """
36
+ Setup for a catalog to record bluesky run documents.
37
+
38
+ Return only one catalog object, depending on the keys in 'iconfig'.
39
+ The object returned is the first successful match, in this order:
40
+
41
+ * tiled catalog: requires TILED_PROFILE_NAME and optional TILED_PATH_NAME
42
+ * databroker catalog: requires DATABROKER_CATALOG
43
+ * temporary databroker catalog: fallback is the above are not successful
44
+ * (TODO) temporary tiled catalog: replaces temporary databroker fallback
45
+ """
46
+ handlers = [ # try these, in order
47
+ _tiled_profile_client,
48
+ _databroker_named_catalog,
49
+ # fallbacks
50
+ _databroker_temporary_catalog,
51
+ # TODO: promote once `del client` is not needed for exit
52
+ _tiled_temporary_catalog,
53
+ ]
54
+ for handler in handlers:
55
+ try:
56
+ cat = handler(iconfig)
57
+ if cat is None:
58
+ continue
59
+ return cat
60
+ except Exception as exinfo:
61
+ logger.error(
62
+ "%s() Failed to create catalog: %s",
63
+ handler.__name__,
64
+ str(exinfo),
65
+ )
66
+ raise RuntimeError("Could not create a catalog for Bluesky run documents.")
67
+
68
+
69
+ def _databroker_named_catalog(
70
+ iconfig: dict[str, Any],
71
+ ) -> Union[
72
+ DATABROKER_CATALOG_TYPE,
73
+ None,
74
+ ]:
75
+ """Connect with a named databroker catalog."""
76
+ cat = None
77
+ catalog_name = iconfig.get("DATABROKER_CATALOG")
78
+ if catalog_name is not None:
79
+ cat = databroker.catalog[catalog_name].v2
80
+ logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
81
+ if cat is not None:
82
+ logger.info("Databroker catalog initialized: %s", cat.name)
83
+ return cat
84
+
85
+
86
+ def _databroker_temporary_catalog(iconfig: dict[str, Any]) -> BlueskyMsgpackCatalog:
87
+ """Connect with a temporary databroker catalog."""
88
+ cat = databroker.temp().v2
89
+ logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
90
+ logger.info("Databroker temporary catalog initialized")
91
+ return cat
92
+
93
+
94
+ def _tiled_profile_client(iconfig: dict[str, Any]) -> Union[None, TILED_CATALOG_TYPE]:
95
+ """Connect with a tiled server using a profile."""
96
+ cat = None
97
+ profile = iconfig.get("TILED_PROFILE_NAME")
98
+ path = iconfig.get("TILED_PATH_NAME")
99
+ if profile is not None:
100
+ client = from_profile(profile)
101
+ cat = client if path is None else client[path]
102
+
103
+ logger.debug("%s: cat=%s", type(cat).__name__, str(cat))
104
+ if cat is not None:
105
+ logger.info(
106
+ "Tiled server (catalog) connected, profile=%r, path=%r",
107
+ profile,
108
+ path,
109
+ )
110
+
111
+ return cat
112
+
113
+
114
+ def _tiled_temporary_catalog(iconfig: dict[str, Any]) -> Container:
115
+ """Connect with a temporary tiled catalog.
116
+
117
+ WARNING: The SimpleTiledServer creates background threads that may prevent
118
+ clean process exit. For interactive use, explicitly delete the returned
119
+ client when done: `del client`
120
+ """
121
+ save_path = iconfig.get("TILED_SAVE_PATH") # testing only?
122
+ server = SimpleTiledServer(save_path)
123
+
124
+ try:
125
+ client = from_uri(server.uri)
126
+ logger.info("Tiled server (temporary catalog) connected")
127
+
128
+ # Store server reference for cleanup when client is deleted
129
+ client._tiled_server = server
130
+
131
+ # Cleanup when client is garbage collected
132
+ def cleanup():
133
+ try:
134
+ server.close()
135
+ except Exception:
136
+ pass
137
+
138
+ weakref.finalize(client, cleanup)
139
+
140
+ return client
141
+ except Exception:
142
+ server.close()
143
+ raise
@@ -19,8 +19,8 @@ import bluesky
19
19
  import databroker._drivers.mongo_normalized
20
20
  import databroker._drivers.msgpack
21
21
  import tiled
22
- from bluesky.callbacks.tiled_writer import TiledWriter
23
22
  from bluesky.utils import ProgressBarManager
23
+ from bluesky_tiled_plugins import TiledWriter
24
24
 
25
25
  from apsbits.utils.controls_setup import connect_scan_id_pv
26
26
  from apsbits.utils.controls_setup import set_control_layer
@@ -36,7 +36,6 @@ logger.bsdev(__file__)
36
36
  def init_RE(
37
37
  iconfig: collections.abc.Mapping[str, Any],
38
38
  subscribers: Optional[list[Any]] = None,
39
- oregistry: Optional[Any] = None,
40
39
  **kwargs: Any,
41
40
  ) -> tuple[bluesky.RunEngine, bluesky.SupplementalData]:
42
41
  """
@@ -66,9 +65,6 @@ def init_RE(
66
65
  documentation.
67
66
  Order in the list does not matter.
68
67
 
69
- oregistry : Optional[Any], default=None
70
- Registry instance for scan ID PV connection.
71
-
72
68
  **kwargs: Additional keyword arguments passed to the RunEngine constructor.
73
69
  For example, run_returns_result=True.
74
70
 
@@ -130,7 +126,7 @@ def init_RE(
130
126
  # Check if it's a tiled client
131
127
  if isinstance(instance, tiled.client.container.Container):
132
128
  try:
133
- tiled_writer = TiledWriter(instance)
129
+ tiled_writer = TiledWriter(instance, batch_size=1)
134
130
  RE.subscribe(tiled_writer)
135
131
  except Exception:
136
132
  logger.exception(
@@ -172,7 +168,7 @@ def init_RE(
172
168
  raise
173
169
 
174
170
  scan_id_pv = iconfig.get("RUN_ENGINE", {}).get("SCAN_ID_PV")
175
- connect_scan_id_pv(RE, pv=scan_id_pv, oregistry=oregistry)
171
+ connect_scan_id_pv(RE, pv=scan_id_pv)
176
172
 
177
173
  if re_config.get("USE_PROGRESS_BAR", True):
178
174
  # Add a progress bar.
@@ -5,8 +5,12 @@ ICONFIG_VERSION: 2.0.1
5
5
 
6
6
  # Add additional configuration for use with your instrument.
7
7
 
8
- ### The short name for the databroker catalog.
8
+ ### The databroker catalog name. (legacy use only)
9
9
  DATABROKER_CATALOG: temp
10
+ ### Tiled server profile and path name: (overrides DATABROKER_CATALOG key)
11
+ #TILED_PROFILE_NAME: raw
12
+ # Use TILED_PATH_NAME according to local tiled server configuration
13
+ #TILED_PATH_NAME: /raw
10
14
 
11
15
  ### RunEngine configuration
12
16
  RUN_ENGINE:
@@ -0,0 +1,124 @@
1
+ """
2
+ Pytest fixtures for instrument tests.
3
+
4
+ This module provides fixtures for initializing the RunEngine with devices,
5
+ allowing tests to operate with device-dependent configurations without relying
6
+ on the production startup logic.
7
+
8
+ Fixtures:
9
+ runengine_with_devices: A RunEngine object in a session with devices configured.
10
+ """
11
+
12
+ import time
13
+ from pathlib import Path
14
+ from typing import Any
15
+
16
+ import pytest
17
+
18
+ from apsbits.demo_instrument.startup import RE
19
+ from apsbits.demo_instrument.startup import make_devices
20
+ from apsbits.utils.config_loaders import load_config
21
+
22
+
23
+ @pytest.fixture(scope="session")
24
+ def runengine_with_devices() -> Any:
25
+ """
26
+ Initialize the RunEngine with devices for testing.
27
+
28
+ This fixture calls RE with the `make_devices()` plan stub to mimic
29
+ the behavior previously performed in the startup module.
30
+
31
+ Returns:
32
+ Any: An instance of the RunEngine with devices configured.
33
+ """
34
+ # Load the configuration before testing
35
+ instrument_path = Path(__file__).parent.parent / "demo_instrument"
36
+ iconfig_path = instrument_path / "configs" / "iconfig.yml"
37
+ load_config(iconfig_path)
38
+
39
+ # Initialize instrument and make devices
40
+ from apsbits.core.instrument_init import init_instrument
41
+
42
+ instrument, oregistry = init_instrument("guarneri")
43
+ make_devices(file="devices.yml", device_manager=instrument)
44
+
45
+ return RE
46
+
47
+
48
+ @pytest.fixture(scope="session")
49
+ def ioc():
50
+ """Run a softIoc in a subprocess.
51
+
52
+ Create a temporary EPICS database file that defines a long integer
53
+ record at "test:scan_id", start softIoc in a subprocess and yield
54
+ connection info for tests. Teardown stops the subprocess and
55
+ removes the temporary file.
56
+ """
57
+ import os
58
+ import subprocess
59
+ import tempfile
60
+
61
+ # Minimal EPICS DB defining a longout record for 'test:scan_id'.
62
+ db_text = "\n".join(
63
+ [
64
+ 'record(longout, "test:scan_id") {',
65
+ # .
66
+ ' field(DESC, "scan id")',
67
+ " field(VAL, -10)",
68
+ "}",
69
+ ]
70
+ )
71
+
72
+ # Write DB to a temporary file that persists until teardown.
73
+ tf = tempfile.NamedTemporaryFile(mode="w", suffix=".db", delete=False)
74
+ try:
75
+ tf.write(db_text)
76
+ tf.flush()
77
+ tf.close()
78
+
79
+ # Start softIoc. Capture output so if it fails immediately we can
80
+ # surface useful error messages.
81
+ proc = subprocess.Popen(
82
+ [
83
+ "softIoc",
84
+ "-S",
85
+ "-d",
86
+ tf.name,
87
+ ],
88
+ stdout=subprocess.PIPE,
89
+ stderr=subprocess.PIPE,
90
+ )
91
+
92
+ # Wait briefly for the process to initialize. If it exits early,
93
+ # collect stdout/stderr and raise.
94
+ timeout = 5.0
95
+ poll = 0.0
96
+ interval = 0.05
97
+ while poll < timeout and proc.poll() is None:
98
+ time.sleep(interval)
99
+ poll += interval
100
+
101
+ if proc.poll() is not None:
102
+ out, err = proc.communicate(timeout=1)
103
+ raise RuntimeError(
104
+ "softIoc terminated unexpectedly. stdout: %r stderr: %r"
105
+ % (out.decode(errors="ignore"), err.decode(errors="ignore"))
106
+ )
107
+
108
+ # Provide connection info for tests.
109
+ yield dict(prefix="test1:", host="127.0.0.1", pv="test:scan_id")
110
+
111
+ finally:
112
+ # Teardown: terminate the softIoc subprocess and remove the DB file.
113
+ try:
114
+ proc.terminate()
115
+ proc.wait(timeout=2)
116
+ except Exception:
117
+ try:
118
+ proc.kill()
119
+ except Exception:
120
+ pass
121
+ try:
122
+ os.remove(tf.name)
123
+ except Exception:
124
+ pass
@@ -0,0 +1,121 @@
1
+ """Test the catalog_init module."""
2
+
3
+ from contextlib import nullcontext as does_not_raise
4
+ from unittest.mock import patch
5
+
6
+ import pytest
7
+ from tiled.profiles import ProfileNotFound
8
+
9
+ # Run these tests without running startup.py.
10
+ with patch("logging.Logger.bsdev"):
11
+ from apsbits.core.catalog_init import _databroker_named_catalog
12
+ from apsbits.core.catalog_init import _databroker_temporary_catalog
13
+ from apsbits.core.catalog_init import _tiled_profile_client
14
+ from apsbits.core.catalog_init import _tiled_temporary_catalog
15
+ from apsbits.core.catalog_init import init_catalog
16
+
17
+
18
+ @pytest.mark.parametrize(
19
+ "iconfig, handler, cat_type, context",
20
+ [
21
+ pytest.param(
22
+ {},
23
+ _databroker_temporary_catalog,
24
+ "BlueskyMsgpackCatalog",
25
+ does_not_raise(),
26
+ id="temporary databroker catalog",
27
+ ),
28
+ pytest.param(
29
+ {},
30
+ init_catalog,
31
+ "BlueskyMsgpackCatalog",
32
+ does_not_raise(),
33
+ id="default to temporary databroker catalog",
34
+ ),
35
+ pytest.param(
36
+ dict(
37
+ DATABROKER_CATALOG="no_such_catalog",
38
+ TILED_PROFILE_NAME="no_such_profile",
39
+ ),
40
+ init_catalog,
41
+ "BlueskyMsgpackCatalog",
42
+ does_not_raise(),
43
+ id="invalid catalog & profile: fallback to temporary catalog",
44
+ ),
45
+ pytest.param(
46
+ {},
47
+ _databroker_named_catalog,
48
+ "NoneType",
49
+ does_not_raise(),
50
+ id="no databroker catalog name",
51
+ ),
52
+ pytest.param(
53
+ dict(DATABROKER_CATALOG="no_such_catalog"),
54
+ _databroker_named_catalog,
55
+ "ignored",
56
+ pytest.raises(KeyError, match="'no_such_catalog'"),
57
+ id="no such databroker catalog name",
58
+ ),
59
+ pytest.param(
60
+ {},
61
+ _tiled_profile_client,
62
+ "NoneType",
63
+ does_not_raise(),
64
+ id="no tiled profile name",
65
+ ),
66
+ pytest.param(
67
+ dict(TILED_PROFILE_NAME="no_such_profile"),
68
+ _tiled_profile_client,
69
+ "ignored",
70
+ pytest.raises(
71
+ ProfileNotFound,
72
+ match="Profile 'no_such_profile' not found.",
73
+ ),
74
+ id="no such tiled profile name",
75
+ ),
76
+ # TODO: _tiled_profile_client with:
77
+ # valid TILED_PROFILE_NAME
78
+ # valid TILED_PROFILE_NAME & valid TILED_PATH_NAME
79
+ # valid TILED_PROFILE_NAME & invalid TILED_PATH_NAME
80
+ pytest.param(
81
+ {},
82
+ _tiled_temporary_catalog,
83
+ "Container",
84
+ does_not_raise(),
85
+ id="temporary tiled catalog",
86
+ ),
87
+ # TODO: _tiled_temporary_catalog & valid TILED_SAVE_PATH
88
+ # TODO: _tiled_temporary_catalog & invalid TILED_SAVE_PATH
89
+ ],
90
+ )
91
+ def test_handlers(iconfig, handler, cat_type, context):
92
+ """Test the handlers that create 'cat' objects."""
93
+ with context:
94
+ cat = handler(iconfig)
95
+ assert type(cat).__name__ == cat_type
96
+
97
+
98
+ def test_use_temporary_tiled_catalog():
99
+ """Typical use of the tiled temporary catalog."""
100
+ import bluesky
101
+ from bluesky_tiled_plugins import TiledWriter
102
+ from ophyd.sim import noisy_det
103
+
104
+ cat = _tiled_temporary_catalog({})
105
+ tw = TiledWriter(cat, batch_size=1)
106
+ RE = bluesky.RunEngine()
107
+ RE.subscribe(tw)
108
+
109
+ delay = 0.1
110
+ npts = 15
111
+ nruns = len(cat)
112
+ (uid,) = RE(bluesky.plans.count([noisy_det], num=npts, delay=delay))
113
+ assert isinstance(uid, str)
114
+ assert len(cat) == 1 + nruns
115
+ run = cat[uid]
116
+ assert run.stop["num_events"]["primary"] == npts
117
+ assert (run.stop["time"] - run.start["time"]) >= delay * npts
118
+
119
+ data = run.primary.read()
120
+ assert "noisy_det" in data
121
+ assert len(data["noisy_det"]) == npts