PyroLab 0.4.0__tar.gz → 0.5.0rc2__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 (88) hide show
  1. {pyrolab-0.4.0/pyrolab.egg-info → pyrolab-0.5.0rc2}/PKG-INFO +59 -57
  2. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/README.md +50 -31
  3. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyproject.toml +44 -40
  4. pyrolab-0.5.0rc2/pyrolab/__init__.py +90 -0
  5. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/api.py +15 -3
  6. pyrolab-0.5.0rc2/pyrolab/cli.py +843 -0
  7. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/configure.py +221 -36
  8. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/__init__.py +32 -8
  9. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/arduino/arduino.py +42 -5
  10. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/amcam.py +9 -9
  11. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/thorcam.py +3 -5
  12. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/uc480.py +2 -3
  13. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/fpgas/analogdiscovery.py +2 -3
  14. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/ppcl55x.py +57 -43
  15. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/tsl550.py +14 -11
  16. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/bpc303.py +0 -1
  17. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/kdc101.py +10 -10
  18. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/max31x.py +0 -1
  19. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/sample.py +3 -3
  20. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/scopes/rohdeschwarz.py +19 -11
  21. pyrolab-0.5.0rc2/pyrolab/locations.py +129 -0
  22. pyrolab-0.5.0rc2/pyrolab/logs.py +359 -0
  23. pyrolab-0.5.0rc2/pyrolab/manager.py +1228 -0
  24. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/nameserver.py +35 -14
  25. pyrolab-0.5.0rc2/pyrolab/pyrolabd.py +460 -0
  26. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/server.py +41 -5
  27. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/service.py +0 -1
  28. pyrolab-0.5.0rc2/pyrolab/updates.py +108 -0
  29. pyrolab-0.5.0rc2/pyrolab/utils.py +189 -0
  30. {pyrolab-0.4.0 → pyrolab-0.5.0rc2/pyrolab.egg-info}/PKG-INFO +59 -57
  31. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/SOURCES.txt +12 -5
  32. pyrolab-0.5.0rc2/pyrolab.egg-info/entry_points.txt +2 -0
  33. pyrolab-0.5.0rc2/pyrolab.egg-info/requires.txt +28 -0
  34. pyrolab-0.5.0rc2/tests/test_cli.py +840 -0
  35. pyrolab-0.5.0rc2/tests/test_configure.py +604 -0
  36. pyrolab-0.5.0rc2/tests/test_imports.py +109 -0
  37. pyrolab-0.5.0rc2/tests/test_lifecycle.py +528 -0
  38. pyrolab-0.5.0rc2/tests/test_locations.py +131 -0
  39. pyrolab-0.5.0rc2/tests/test_logs.py +333 -0
  40. pyrolab-0.5.0rc2/tests/test_manager.py +312 -0
  41. pyrolab-0.5.0rc2/tests/test_nameserver.py +106 -0
  42. pyrolab-0.5.0rc2/tests/test_pyrolabd.py +347 -0
  43. pyrolab-0.5.0rc2/tests/test_registration.py +357 -0
  44. pyrolab-0.5.0rc2/tests/test_server.py +219 -0
  45. pyrolab-0.5.0rc2/tests/test_service.py +230 -0
  46. pyrolab-0.5.0rc2/tests/test_updates.py +154 -0
  47. pyrolab-0.5.0rc2/tests/test_utils.py +139 -0
  48. pyrolab-0.4.0/pyrolab/__init__.py +0 -190
  49. pyrolab-0.4.0/pyrolab/cli.py +0 -564
  50. pyrolab-0.4.0/pyrolab/manager.py +0 -671
  51. pyrolab-0.4.0/pyrolab/pyrolabd.py +0 -282
  52. pyrolab-0.4.0/pyrolab/utils.py +0 -64
  53. pyrolab-0.4.0/pyrolab.egg-info/entry_points.txt +0 -2
  54. pyrolab-0.4.0/pyrolab.egg-info/requires.txt +0 -46
  55. pyrolab-0.4.0/tests/test_code_interface.py +0 -29
  56. pyrolab-0.4.0/tests/test_configure.py +0 -150
  57. pyrolab-0.4.0/tests/test_daemon.py +0 -24
  58. pyrolab-0.4.0/tests/test_errors.py +0 -0
  59. pyrolab-0.4.0/tests/test_manager.py +0 -0
  60. pyrolab-0.4.0/tests/test_nameserver.py +0 -0
  61. pyrolab-0.4.0/tests/test_pyrolabd.py +0 -0
  62. pyrolab-0.4.0/tests/test_service.py +0 -0
  63. pyrolab-0.4.0/tests/test_utils.py +0 -0
  64. pyrolab-0.4.0/tests/testsrv.py +0 -21
  65. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/LICENSE +0 -0
  66. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/MANIFEST.in +0 -0
  67. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/NOTICE.txt +0 -0
  68. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/data/wordlist.txt +0 -0
  69. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/VISAResourceExtensions.py +0 -0
  70. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/arduino/__init__.py +0 -0
  71. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/__init__.py +0 -0
  72. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/sciTSI.py +2 -2
  73. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/fpgas/__init__.py +0 -0
  74. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/function_generators/__init__.py +0 -0
  75. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/function_generators/smcv100b.py +0 -0
  76. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/__init__.py +0 -0
  77. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/sachertec.py +0 -0
  78. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/KMTS25E.py +0 -0
  79. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/__init__.py +0 -0
  80. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/__init__.py +0 -0
  81. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/prm1z8.py +0 -0
  82. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/z8xx.py +0 -0
  83. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/scopes/__init__.py +0 -0
  84. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/smu/__init__.py +0 -0
  85. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/smu/keysight.py +0 -0
  86. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/dependency_links.txt +0 -0
  87. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/top_level.txt +0 -0
  88. {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrolab
3
- Version: 0.4.0
3
+ Version: 0.5.0rc2
4
4
  Summary: A framework for using remote lab instruments as local resources built on Pyro5.
5
5
  Author-email: Sequoia Ploeg <sequoiap4@gmail.com>, BYU CamachoLab <camacho@byu.edu>
6
6
  Maintainer-email: Sequoia Ploeg <sequoiap4@gmail.com>, BYU CamachoLab <camacho@byu.edu>
@@ -18,24 +18,23 @@ Classifier: Intended Audience :: Developers
18
18
  Classifier: Topic :: Scientific/Engineering
19
19
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
20
20
  Classifier: Programming Language :: Python :: 3
21
- Classifier: Programming Language :: Python :: 3.9
22
- Classifier: Programming Language :: Python :: 3.10
23
21
  Classifier: Programming Language :: Python :: 3.11
24
22
  Classifier: Programming Language :: Python :: 3.12
25
23
  Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
26
25
  Classifier: Programming Language :: Python :: 3 :: Only
27
26
  Classifier: Operating System :: MacOS
28
27
  Classifier: Operating System :: Microsoft :: Windows
29
28
  Classifier: Operating System :: POSIX :: Linux
30
29
  Classifier: Operating System :: OS Independent
31
- Requires-Python: >=3.9
30
+ Requires-Python: >=3.11
32
31
  Description-Content-Type: text/markdown
33
32
  License-File: LICENSE
34
33
  License-File: NOTICE.txt
35
34
  Requires-Dist: numpy
36
35
  Requires-Dist: scipy
37
36
  Requires-Dist: Pyro5
38
- Requires-Dist: pydantic<2.0.0,>=1.9.1
37
+ Requires-Dist: pydantic<3,>=2.8
39
38
  Requires-Dist: pyyaml
40
39
  Requires-Dist: deprecation
41
40
  Requires-Dist: typer
@@ -43,6 +42,7 @@ Requires-Dist: colorama
43
42
  Requires-Dist: tabulate
44
43
  Requires-Dist: requests
45
44
  Requires-Dist: packaging>=23.0
45
+ Requires-Dist: platformdirs>=3.0
46
46
  Provides-Extra: tsl550
47
47
  Requires-Dist: pyserial; extra == "tsl550"
48
48
  Provides-Extra: ppcl55x
@@ -52,25 +52,8 @@ Requires-Dist: pyvisa; extra == "rto"
52
52
  Requires-Dist: pyvisa-py; extra == "rto"
53
53
  Provides-Extra: arduino
54
54
  Requires-Dist: pyfirmata; extra == "arduino"
55
- Provides-Extra: test
56
- Requires-Dist: pytest; extra == "test"
57
- Requires-Dist: coverage; extra == "test"
58
- Provides-Extra: dev
59
- Requires-Dist: notebook; extra == "dev"
60
- Requires-Dist: pytest; extra == "dev"
61
- Requires-Dist: mypy; extra == "dev"
62
- Requires-Dist: coverage; extra == "dev"
63
- Requires-Dist: flake8>=4.0.1; extra == "dev"
64
- Requires-Dist: isort>=5.10.1; extra == "dev"
65
- Requires-Dist: black>=22.1.0; extra == "dev"
66
- Requires-Dist: autopep8>=1.6.0; extra == "dev"
67
- Requires-Dist: twine>=4.0.0; extra == "dev"
68
- Requires-Dist: build>=0.10.0; extra == "dev"
69
- Requires-Dist: pre-commit; extra == "dev"
70
- Requires-Dist: bump2version~=1.0.1; extra == "dev"
71
- Provides-Extra: docs
72
- Requires-Dist: jupyter-book==1.0.3; extra == "docs"
73
- Requires-Dist: numpydoc==1.5.0; extra == "docs"
55
+ Provides-Extra: cameras
56
+ Requires-Dist: opencv-python; extra == "cameras"
74
57
  Dynamic: license-file
75
58
 
76
59
  <p align="center">
@@ -80,7 +63,6 @@ Dynamic: license-file
80
63
  ---
81
64
 
82
65
  <p align="center">
83
- <img alt="Development version" src="https://img.shields.io/badge/master-v0.4.0-informational">
84
66
  <a href="https://pypi.python.org/pypi/pyrolab"><img alt="PyPI Version" src="https://img.shields.io/pypi/v/pyrolab.svg"></a>
85
67
  <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/pyrolab">
86
68
  <a href="https://pyrolab.readthedocs.io/"><img alt="Documentation Status" src="https://readthedocs.org/projects/pyrolab/badge/?version=latest"></a>
@@ -118,7 +100,7 @@ PyroLab declares several "extras", depending on which instruments you need
118
100
  to support:
119
101
 
120
102
  ```
121
- pip install pyrolab[tsl550, oscope]
103
+ pip install "pyrolab[tsl550,rto]"
122
104
  ```
123
105
 
124
106
  The full list of supported extras is:
@@ -126,18 +108,24 @@ The full list of supported extras is:
126
108
  - ``ppcl55x`` (PurePhotonics lasers)
127
109
  - ``rto`` (Rohde & Schwarz oscilloscopes)
128
110
  - ``arduino`` (Arduino)
129
- - ``test`` (PyroLab unit testing)
111
+ - ``cameras`` (ThorCam and AmScope cameras, via OpenCV)
130
112
  <!-- - ``thorlabs`` (ThorLabs motion controllers) -->
131
113
  <!-- - ``adiscovery`` (Analog Discovery boards from Digilent) -->
132
114
  <!-- - ``monitor`` (PyroMonitor web application) -->
133
115
 
134
116
  You can also clone the repository, navigate to the toplevel, and install in
135
- editable mode (make sure you have pip >= 21.1):
117
+ editable mode:
136
118
 
137
119
  ```
138
120
  pip install -e .
139
121
  ```
140
122
 
123
+ For development, use [uv](https://docs.astral.sh/uv/). From the toplevel,
124
+ ``make install`` (equivalent to ``uv sync --all-extras`` followed by
125
+ ``uv run pre-commit install``) creates a ``.venv`` with PyroLab, every extra,
126
+ and the development tools, and installs the pre-commit hooks. Run tests with
127
+ ``make test`` (or ``uv run pytest``).
128
+
141
129
  ## Web Monitor
142
130
 
143
131
  There's a web application that can monitor your PyroLab nameserver and
@@ -181,6 +169,7 @@ Run a nameserver:
181
169
 
182
170
  ```python
183
171
  from pyrolab.api import start_ns_loop
172
+
184
173
  start_ns_loop()
185
174
  ```
186
175
 
@@ -252,48 +241,61 @@ arrays, matplotlib plot objects, custom objects, etc.).
252
241
 
253
242
  ## Releasing
254
243
 
255
- Make sure you have committed a changelog file under ``docs/changelog`` titled
256
- ``<major>.<minor>.<patch>-changelog.md`` before bumping version. Also, the git
257
- directory should be clean (no uncommitted changes).
244
+ The version lives only in ``pyproject.toml``; ``pyrolab.__version__`` reads it
245
+ from the installed package's metadata.
258
246
 
259
- To bump version prior to a release, run one of the following commands:
247
+ Every release starts as a release candidate on PyPI, so what gets tested is
248
+ exactly what gets released. First commit a changelog file under
249
+ ``docs/changelog`` titled ``<major>.<minor>.<patch>-changelog.md`` for the new
250
+ version; it becomes the release notes for the candidates and the release. Then,
251
+ with a clean working tree:
260
252
 
261
253
  ```
262
- bumpversion major
263
- bumpversion minor
264
- bumpversion patch
254
+ make minor # start a candidate: 0.4.0 -> 0.5.0rc1 (or make patch, make major)
255
+ make rc # if you need another: 0.5.0rc1 -> 0.5.0rc2
256
+ make release # release the candidate: 0.5.0rc2 -> 0.5.0
265
257
  ```
266
258
 
267
- This will automatically create a git tag in the repository with the
268
- corrresponding version number and commit the modified files (where version
269
- numbers were updated). Pushing the tags (a manual process) to the remote will
270
- automatically create a new release. Releases are automatically published to
271
- PyPI and GitHub when git tags matching the "v*" pattern are created
272
- (e.g. "v0.4.0"), as bumpversion does.
273
-
274
- After bumping version, you can view the tags on the local machine by running
275
- ``git tag``. To push the tags to the remote server and trigger the release
276
- workflow, you can run ``git push origin <tagname>``.
277
-
278
- For code quality, please run isort and black before committing (note that the
279
- latest release of isort may not work through VSCode's integrated terminal, and
280
- it's safest to run it separately through another terminal).
259
+ Each one bumps the version with ``uv version``, commits ``pyproject.toml`` and
260
+ ``uv.lock``, tags ``vX.Y.Z``, and pushes the commit and tag. The tag triggers
261
+ the release workflow, which runs the test suite, builds and checks the package,
262
+ publishes it to PyPI, and creates the GitHub release with the changelog as its
263
+ notes (marked as a pre-release for a candidate). pip installs a candidate only
264
+ when asked, with ``pip install --pre pyrolab`` or ``pip install
265
+ pyrolab==0.5.0rc1``, so everyone else keeps getting the latest release.
266
+
267
+ ``make release`` only works on a candidate. While a candidate is out, ``make
268
+ patch``, ``make minor``, or ``make major`` can only move it up a level (for
269
+ example, if 0.5.1rc1 turns out to need a minor release, ``make minor`` gives
270
+ 0.6.0rc1; rename the changelog to match). The rules are in
271
+ ``scripts/release.sh``.
272
+
273
+ Publishing uses PyPI's
274
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/): PyPI accepts
275
+ uploads only from this repository's ``release.yml`` workflow running in the
276
+ ``pypi`` environment, so no PyPI token is stored in GitHub. Any PR that edits
277
+ ``release.yml`` gets a dry run (checks and build, nothing published), and the
278
+ workflow can also be dry-run by hand from the Actions tab.
279
+
280
+ ## Code Quality
281
+
282
+ Formatting and import sorting use [ruff](https://docs.astral.sh/ruff/), run
283
+ automatically by the pre-commit hooks that ``make install`` sets up. To run it
284
+ by hand, use ``make format``; ``make precommit`` runs every hook on every file,
285
+ as CI does.
281
286
 
282
287
  ## Building the Docs
283
288
 
284
289
  Much of the API documentation is autogenerated from the source code. Gitignores
285
290
  are in place to prevent you from committing autogenerated pages.
286
291
 
287
- To build the docs, navigate to the ``docs/`` directory and run:
288
-
289
- ```
290
- pip install -r requirements.txt
291
- make html
292
- ```
292
+ To build the docs, run ``make doc`` (which installs the ``docs`` dependency
293
+ group), then ``make serve`` to view them at http://localhost:8000. Read the Docs
294
+ builds them the same way, from ``uv.lock``.
293
295
 
294
296
  ## Building the Package
295
297
 
296
- You can test the build and view the bundled artifacts using
297
- [build](https://pypi.org/project/build/). It's recommended you build locally
298
+ You can test the build and view the bundled artifacts with ``make build``
299
+ (which runs ``uv build``). It's recommended you build locally
298
300
  before pushing to PyPI. In particular, double check the included files and make
299
301
  sure only the required files are there by modifying MANIFEST.in as necessary.
@@ -5,7 +5,6 @@
5
5
  ---
6
6
 
7
7
  <p align="center">
8
- <img alt="Development version" src="https://img.shields.io/badge/master-v0.4.0-informational">
9
8
  <a href="https://pypi.python.org/pypi/pyrolab"><img alt="PyPI Version" src="https://img.shields.io/pypi/v/pyrolab.svg"></a>
10
9
  <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/pyrolab">
11
10
  <a href="https://pyrolab.readthedocs.io/"><img alt="Documentation Status" src="https://readthedocs.org/projects/pyrolab/badge/?version=latest"></a>
@@ -43,7 +42,7 @@ PyroLab declares several "extras", depending on which instruments you need
43
42
  to support:
44
43
 
45
44
  ```
46
- pip install pyrolab[tsl550, oscope]
45
+ pip install "pyrolab[tsl550,rto]"
47
46
  ```
48
47
 
49
48
  The full list of supported extras is:
@@ -51,18 +50,24 @@ The full list of supported extras is:
51
50
  - ``ppcl55x`` (PurePhotonics lasers)
52
51
  - ``rto`` (Rohde & Schwarz oscilloscopes)
53
52
  - ``arduino`` (Arduino)
54
- - ``test`` (PyroLab unit testing)
53
+ - ``cameras`` (ThorCam and AmScope cameras, via OpenCV)
55
54
  <!-- - ``thorlabs`` (ThorLabs motion controllers) -->
56
55
  <!-- - ``adiscovery`` (Analog Discovery boards from Digilent) -->
57
56
  <!-- - ``monitor`` (PyroMonitor web application) -->
58
57
 
59
58
  You can also clone the repository, navigate to the toplevel, and install in
60
- editable mode (make sure you have pip >= 21.1):
59
+ editable mode:
61
60
 
62
61
  ```
63
62
  pip install -e .
64
63
  ```
65
64
 
65
+ For development, use [uv](https://docs.astral.sh/uv/). From the toplevel,
66
+ ``make install`` (equivalent to ``uv sync --all-extras`` followed by
67
+ ``uv run pre-commit install``) creates a ``.venv`` with PyroLab, every extra,
68
+ and the development tools, and installs the pre-commit hooks. Run tests with
69
+ ``make test`` (or ``uv run pytest``).
70
+
66
71
  ## Web Monitor
67
72
 
68
73
  There's a web application that can monitor your PyroLab nameserver and
@@ -106,6 +111,7 @@ Run a nameserver:
106
111
 
107
112
  ```python
108
113
  from pyrolab.api import start_ns_loop
114
+
109
115
  start_ns_loop()
110
116
  ```
111
117
 
@@ -177,48 +183,61 @@ arrays, matplotlib plot objects, custom objects, etc.).
177
183
 
178
184
  ## Releasing
179
185
 
180
- Make sure you have committed a changelog file under ``docs/changelog`` titled
181
- ``<major>.<minor>.<patch>-changelog.md`` before bumping version. Also, the git
182
- directory should be clean (no uncommitted changes).
186
+ The version lives only in ``pyproject.toml``; ``pyrolab.__version__`` reads it
187
+ from the installed package's metadata.
183
188
 
184
- To bump version prior to a release, run one of the following commands:
189
+ Every release starts as a release candidate on PyPI, so what gets tested is
190
+ exactly what gets released. First commit a changelog file under
191
+ ``docs/changelog`` titled ``<major>.<minor>.<patch>-changelog.md`` for the new
192
+ version; it becomes the release notes for the candidates and the release. Then,
193
+ with a clean working tree:
185
194
 
186
195
  ```
187
- bumpversion major
188
- bumpversion minor
189
- bumpversion patch
196
+ make minor # start a candidate: 0.4.0 -> 0.5.0rc1 (or make patch, make major)
197
+ make rc # if you need another: 0.5.0rc1 -> 0.5.0rc2
198
+ make release # release the candidate: 0.5.0rc2 -> 0.5.0
190
199
  ```
191
200
 
192
- This will automatically create a git tag in the repository with the
193
- corrresponding version number and commit the modified files (where version
194
- numbers were updated). Pushing the tags (a manual process) to the remote will
195
- automatically create a new release. Releases are automatically published to
196
- PyPI and GitHub when git tags matching the "v*" pattern are created
197
- (e.g. "v0.4.0"), as bumpversion does.
201
+ Each one bumps the version with ``uv version``, commits ``pyproject.toml`` and
202
+ ``uv.lock``, tags ``vX.Y.Z``, and pushes the commit and tag. The tag triggers
203
+ the release workflow, which runs the test suite, builds and checks the package,
204
+ publishes it to PyPI, and creates the GitHub release with the changelog as its
205
+ notes (marked as a pre-release for a candidate). pip installs a candidate only
206
+ when asked, with ``pip install --pre pyrolab`` or ``pip install
207
+ pyrolab==0.5.0rc1``, so everyone else keeps getting the latest release.
208
+
209
+ ``make release`` only works on a candidate. While a candidate is out, ``make
210
+ patch``, ``make minor``, or ``make major`` can only move it up a level (for
211
+ example, if 0.5.1rc1 turns out to need a minor release, ``make minor`` gives
212
+ 0.6.0rc1; rename the changelog to match). The rules are in
213
+ ``scripts/release.sh``.
214
+
215
+ Publishing uses PyPI's
216
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/): PyPI accepts
217
+ uploads only from this repository's ``release.yml`` workflow running in the
218
+ ``pypi`` environment, so no PyPI token is stored in GitHub. Any PR that edits
219
+ ``release.yml`` gets a dry run (checks and build, nothing published), and the
220
+ workflow can also be dry-run by hand from the Actions tab.
198
221
 
199
- After bumping version, you can view the tags on the local machine by running
200
- ``git tag``. To push the tags to the remote server and trigger the release
201
- workflow, you can run ``git push origin <tagname>``.
222
+ ## Code Quality
202
223
 
203
- For code quality, please run isort and black before committing (note that the
204
- latest release of isort may not work through VSCode's integrated terminal, and
205
- it's safest to run it separately through another terminal).
224
+ Formatting and import sorting use [ruff](https://docs.astral.sh/ruff/), run
225
+ automatically by the pre-commit hooks that ``make install`` sets up. To run it
226
+ by hand, use ``make format``; ``make precommit`` runs every hook on every file,
227
+ as CI does.
206
228
 
207
229
  ## Building the Docs
208
230
 
209
231
  Much of the API documentation is autogenerated from the source code. Gitignores
210
232
  are in place to prevent you from committing autogenerated pages.
211
233
 
212
- To build the docs, navigate to the ``docs/`` directory and run:
213
-
214
- ```
215
- pip install -r requirements.txt
216
- make html
217
- ```
234
+ To build the docs, run ``make doc`` (which installs the ``docs`` dependency
235
+ group), then ``make serve`` to view them at http://localhost:8000. Read the Docs
236
+ builds them the same way, from ``uv.lock``.
218
237
 
219
238
  ## Building the Package
220
239
 
221
- You can test the build and view the bundled artifacts using
222
- [build](https://pypi.org/project/build/). It's recommended you build locally
240
+ You can test the build and view the bundled artifacts with ``make build``
241
+ (which runs ``uv build``). It's recommended you build locally
223
242
  before pushing to PyPI. In particular, double check the included files and make
224
243
  sure only the required files are there by modifying MANIFEST.in as necessary.
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "pyrolab"
3
- version = "0.4.0"
3
+ version = "0.5.0rc2"
4
4
  description = "A framework for using remote lab instruments as local resources built on Pyro5."
5
5
  readme = "README.md"
6
- requires-python = ">=3.9"
6
+ requires-python = ">=3.11"
7
7
  license = {text = "GPL-3.0+"}
8
8
  keywords = [
9
9
  "PyroLab",
@@ -47,11 +47,10 @@ classifiers = [
47
47
  "Topic :: Scientific/Engineering",
48
48
  "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
49
49
  "Programming Language :: Python :: 3",
50
- "Programming Language :: Python :: 3.9",
51
- "Programming Language :: Python :: 3.10",
52
50
  "Programming Language :: Python :: 3.11",
53
51
  "Programming Language :: Python :: 3.12",
54
52
  "Programming Language :: Python :: 3.13",
53
+ "Programming Language :: Python :: 3.14",
55
54
  "Programming Language :: Python :: 3 :: Only",
56
55
  "Operating System :: MacOS",
57
56
  "Operating System :: Microsoft :: Windows",
@@ -63,7 +62,7 @@ dependencies = [
63
62
  "numpy",
64
63
  "scipy",
65
64
  "Pyro5",
66
- "pydantic >= 1.9.1, < 2.0.0",
65
+ "pydantic>=2.8,<3",
67
66
  "pyyaml",
68
67
  "deprecation",
69
68
  "typer",
@@ -71,38 +70,35 @@ dependencies = [
71
70
  "tabulate",
72
71
  "requests",
73
72
  "packaging>=23.0",
73
+ "platformdirs>=3.0",
74
74
  ]
75
75
 
76
- # List additional groups of dependencies here (e.g. development
77
- # dependencies). Users will be able to install these using the "extras"
78
- # syntax, for example:
76
+ # Optional hardware dependencies, installed with the "extras" syntax, e.g.
79
77
  #
80
- # $ pip install sampleproject[dev]
78
+ # $ pip install pyrolab[arduino]
81
79
  [project.optional-dependencies]
82
80
  tsl550 = ["pyserial"]
83
81
  ppcl55x = ["pyserial"]
84
82
  # thorlabs = ["thorlabs_kinesis@git+https://github.com/BYUCamachoLab/thorlabs-kinesis"]
85
83
  rto = ["pyvisa", "pyvisa-py"]
86
84
  arduino = ["pyfirmata"]
85
+ cameras = ["opencv-python"]
87
86
  # adiscovery = ["WF_SDK@git+https://github.com/Digilent/WaveForms-SDK-Getting-Started-PY#egg=WF_SDK"]
88
87
  # monitor = ["pyromonitor@git+https://github.com/BYUCamachoLab/pyromonitor"]
89
- test = ["pytest", "coverage"]
88
+
89
+ # Development-only dependencies; not published. `uv sync` installs "dev" by
90
+ # default; add `--group docs` for the documentation toolchain.
91
+ [dependency-groups]
90
92
  dev = [
91
- "notebook",
92
- "pytest",
93
- "mypy",
94
93
  "coverage",
95
- "flake8 >= 4.0.1",
96
- "isort >= 5.10.1",
97
- "black >= 22.1.0",
98
- "autopep8 >= 1.6.0",
99
- "twine >= 4.0.0",
100
- "build >= 0.10.0",
94
+ "mypy",
101
95
  "pre-commit",
102
- "bump2version ~= 1.0.1",
96
+ "pytest",
97
+ "ruff==0.16.10", # keep in step with .pre-commit-config.yaml
103
98
  ]
104
99
  docs = [
105
100
  "jupyter-book == 1.0.3",
101
+ "notebook",
106
102
  "numpydoc==1.5.0",
107
103
  ]
108
104
 
@@ -118,7 +114,7 @@ namespaces = false
118
114
  "Research Group" = "https://camacholab.byu.edu/"
119
115
 
120
116
  [project.scripts]
121
- pyrolab = "pyrolab.cli:app"
117
+ pyrolab = "pyrolab.cli:run"
122
118
 
123
119
  [tool.pytest.ini_options]
124
120
  minversion = "6.0"
@@ -126,27 +122,35 @@ addopts = "--maxfail=2 -rf"
126
122
  testpaths = ["tests"]
127
123
  console_output_style = "progress"
128
124
  filterwarnings = ["ignore::UserWarning", "ignore::DeprecationWarning"]
125
+ # A test running longer than this dumps every thread's traceback to stderr,
126
+ # so a hang shows where it is stuck instead of silently running until CI's
127
+ # job timeout.
128
+ faulthandler_timeout = 60
129
+ markers = [
130
+ "hardware: needs physical lab instruments; skipped unless --run-hardware is given",
131
+ ]
132
+
133
+ [tool.coverage.run]
134
+ source = ["pyrolab"]
135
+
136
+ [tool.coverage.report]
137
+ skip_empty = true
129
138
 
130
139
  [build-system]
131
140
  requires = ["setuptools>=62.0.0", "wheel"]
132
141
  build-backend = "setuptools.build_meta"
133
142
 
134
- [tool.isort]
135
- profile = "black"
136
- extend_skip_glob = "api.py"
137
- multi_line_output = 3
138
- include_trailing_comma = true
139
- force_grid_wrap = 0
140
- use_parentheses = true
141
- line_length = 88
142
- known_third_party = [
143
- "Pyro5",
144
- "matplotlib",
145
- "numpy",
146
- "pytest",
147
- "scipy",
148
- "setuptools",
149
- "typer",
150
- "tabulate",
151
- ]
152
- known_first_party = ["pyrolab"]
143
+ [tool.ruff]
144
+ line-length = 88
145
+
146
+ [tool.ruff.lint]
147
+ # Import sorting only, replacing isort. Ruff's default rule set is not
148
+ # enabled yet.
149
+ select = ["I"]
150
+
151
+ [tool.ruff.lint.isort]
152
+ known-first-party = ["pyrolab"]
153
+
154
+ [tool.ruff.lint.per-file-ignores]
155
+ # Import order in api.py is load-bearing.
156
+ "pyrolab/api.py" = ["I"]
@@ -0,0 +1,90 @@
1
+ # Copyright © 2020- PyroLab Project Contributors and others (see AUTHORS.txt).
2
+ # The resources, libraries, and some source files under other terms (see NOTICE.txt).
3
+ #
4
+ # This file is part of PyroLab.
5
+ #
6
+ # PyroLab is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # PyroLab is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with PyroLab. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ """
20
+ PyroLab
21
+ =======
22
+
23
+ A framework for using remote lab instruments as local resources built on Pyro5.
24
+ """
25
+
26
+ import logging
27
+ import os
28
+ import platform
29
+ import sys
30
+
31
+ # Check if Python version is supported
32
+ pyversion = sys.version_info
33
+ if pyversion < (3, 11, 0):
34
+ raise Exception(
35
+ "PyroLab requires Python 3.11+ (version "
36
+ + platform.python_version()
37
+ + " detected)."
38
+ )
39
+
40
+
41
+ # Metadata
42
+ __title__ = "PyroLab"
43
+ __author__ = "CamachoLab"
44
+ __copyright__ = "Copyright 2020, The PyroLab Project"
45
+ # pyproject.toml is the single source of the version (see `uv version`).
46
+ import importlib.metadata as _metadata
47
+
48
+ try:
49
+ __version__ = _metadata.version("pyrolab")
50
+ except _metadata.PackageNotFoundError: # source tree that isn't installed
51
+ __version__ = "0.0.0+unknown"
52
+ __license__ = "GPLv3+"
53
+ __maintainer__ = "Sequoia Ploeg"
54
+ __maintainer_email__ = "sequoia.ploeg@byu.edu"
55
+ __status__ = "Development" # "Production"
56
+ __project_url__ = "https://github.com/sequoiap/pyrolab"
57
+ __forum_url__ = "https://github.com/sequoiap/pyrolab/issues"
58
+ __website_url__ = "https://camacholab.byu.edu/"
59
+
60
+
61
+ # Where PyroLab keeps its files: per-user directories (see pyrolab.locations).
62
+ # Importing creates nothing; files and directories are created when written.
63
+ from pathlib import Path
64
+
65
+ from pyrolab.locations import LEGACY_DATA_DIR
66
+ from pyrolab.locations import locations as _find_locations
67
+
68
+ _locations = _find_locations()
69
+
70
+ PYROLAB_DATA_DIR = _locations.data_dir
71
+ NAMESERVER_STORAGE = _locations.data_dir / "nameserver"
72
+ PYROLAB_LOGDIR = _locations.log_dir
73
+ # The daemon's single log file; PYROLAB_LOGFILE overrides where it goes.
74
+ PYROLAB_LOGFILE = Path(
75
+ os.environ.get("PYROLAB_LOGFILE") or PYROLAB_LOGDIR / "pyrolab.log"
76
+ )
77
+ USER_CONFIG_FILE = _locations.config_dir / "user_configuration.yaml"
78
+ LOCKFILE = _locations.state_dir / "pyrolabd.lock"
79
+ RUNTIME_CONFIG = _locations.state_dir / "runtime_config.yaml"
80
+ # Why the background daemon exited before it could serve requests, for `up`.
81
+ STARTUP_ERROR_FILE = _locations.state_dir / "pyrolabd_startup_error.txt"
82
+ # Records that files from a pre-0.5 install were copied (see pyrolab.locations).
83
+ LEGACY_MIGRATION_MARKER = _locations.state_dir / "legacy_files_copied.txt"
84
+ UPDATE_CHECK_FILE = _locations.cache_dir / "update_check.json"
85
+
86
+
87
+ # As a library, PyroLab leaves logging to the application: its loggers do
88
+ # nothing until the application (or PyroLab's own CLI and daemon, see
89
+ # pyrolab.logs) configures logging.
90
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -48,7 +48,19 @@ __all__ = [
48
48
 
49
49
  # If a user config file exists, load the first listed nameserver by default,
50
50
  # so that locate_ns "just works."
51
+ # This is a convenience only: importing the API must never fail because the
52
+ # local configuration is invalid, has no nameservers, or names a "public"
53
+ # host that can't be resolved -- otherwise neither client scripts nor the
54
+ # CLI commands that would fix the configuration could even start.
51
55
  if USER_CONFIG_FILE.exists():
52
- cfg = PyroLabConfiguration.from_file(USER_CONFIG_FILE)
53
- nscfg = next(iter(cfg.nameservers.values()))
54
- nscfg.update_pyro_config()
56
+ try:
57
+ cfg = PyroLabConfiguration.from_file(USER_CONFIG_FILE)
58
+ nscfg = next(iter(cfg.nameservers.values()), None)
59
+ if nscfg is not None:
60
+ nscfg.update_pyro_config()
61
+ except Exception as e:
62
+ import logging
63
+
64
+ logging.getLogger(__name__).warning(
65
+ "Not applying nameserver defaults from %s: %s", USER_CONFIG_FILE, e
66
+ )