PyroLab 0.3.2__tar.gz → 0.5.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 (92) hide show
  1. {pyrolab-0.3.2 → pyrolab-0.5.0}/NOTICE.txt +1 -1
  2. pyrolab-0.5.0/PKG-INFO +301 -0
  3. {pyrolab-0.3.2 → pyrolab-0.5.0}/README.md +57 -34
  4. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyproject.toml +52 -53
  5. pyrolab-0.5.0/pyrolab/__init__.py +90 -0
  6. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/api.py +21 -9
  7. pyrolab-0.5.0/pyrolab/cli.py +841 -0
  8. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/configure.py +327 -104
  9. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/VISAResourceExtensions.py +65 -34
  10. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/__init__.py +50 -25
  11. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/arduino/arduino.py +71 -19
  12. pyrolab-0.5.0/pyrolab/drivers/cameras/amcam.py +61 -0
  13. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/cameras/sciTSI.py +41 -34
  14. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/cameras/thorcam.py +122 -73
  15. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/cameras/uc480.py +68 -63
  16. pyrolab-0.5.0/pyrolab/drivers/fpgas/__init__.py +16 -0
  17. pyrolab-0.5.0/pyrolab/drivers/fpgas/analogdiscovery.py +309 -0
  18. pyrolab-0.5.0/pyrolab/drivers/function_generators/__init__.py +16 -0
  19. pyrolab-0.5.0/pyrolab/drivers/function_generators/smcv100b.py +389 -0
  20. pyrolab-0.5.0/pyrolab/drivers/lasers/ppcl55x.py +667 -0
  21. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/lasers/sachertec.py +25 -20
  22. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/lasers/tsl550.py +232 -176
  23. pyrolab-0.5.0/pyrolab/drivers/motion/KMTS25E.py +92 -0
  24. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/kinesis/__init__.py +9 -5
  25. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/kinesis/bpc303.py +86 -43
  26. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/kinesis/kdc101.py +123 -95
  27. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/max31x.py +31 -24
  28. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/prm1z8.py +2 -1
  29. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/z8xx.py +13 -10
  30. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/sample.py +16 -11
  31. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/scopes/rohdeschwarz.py +137 -114
  32. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/smu/keysight.py +94 -83
  33. pyrolab-0.5.0/pyrolab/locations.py +129 -0
  34. pyrolab-0.5.0/pyrolab/logs.py +391 -0
  35. pyrolab-0.5.0/pyrolab/manager.py +1246 -0
  36. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/nameserver.py +58 -25
  37. pyrolab-0.5.0/pyrolab/pyrolabd.py +480 -0
  38. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/server.py +110 -62
  39. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/service.py +13 -8
  40. pyrolab-0.5.0/pyrolab/updates.py +108 -0
  41. pyrolab-0.5.0/pyrolab/utils.py +224 -0
  42. pyrolab-0.5.0/pyrolab.egg-info/PKG-INFO +301 -0
  43. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab.egg-info/SOURCES.txt +18 -6
  44. pyrolab-0.5.0/pyrolab.egg-info/entry_points.txt +2 -0
  45. pyrolab-0.5.0/pyrolab.egg-info/requires.txt +28 -0
  46. pyrolab-0.5.0/tests/test_cli.py +861 -0
  47. pyrolab-0.5.0/tests/test_configure.py +604 -0
  48. pyrolab-0.5.0/tests/test_imports.py +109 -0
  49. pyrolab-0.5.0/tests/test_lifecycle.py +528 -0
  50. pyrolab-0.5.0/tests/test_locations.py +131 -0
  51. pyrolab-0.5.0/tests/test_logs.py +388 -0
  52. pyrolab-0.5.0/tests/test_manager.py +312 -0
  53. pyrolab-0.5.0/tests/test_nameserver.py +106 -0
  54. pyrolab-0.5.0/tests/test_pyrolabd.py +382 -0
  55. pyrolab-0.5.0/tests/test_registration.py +394 -0
  56. pyrolab-0.5.0/tests/test_server.py +219 -0
  57. pyrolab-0.5.0/tests/test_service.py +230 -0
  58. pyrolab-0.5.0/tests/test_updates.py +154 -0
  59. pyrolab-0.5.0/tests/test_utils.py +163 -0
  60. pyrolab-0.3.2/AUTHORS.txt +0 -20
  61. pyrolab-0.3.2/PKG-INFO +0 -937
  62. pyrolab-0.3.2/pyrolab/__init__.py +0 -141
  63. pyrolab-0.3.2/pyrolab/cli.py +0 -442
  64. pyrolab-0.3.2/pyrolab/drivers/lasers/ppcl55x.py +0 -627
  65. pyrolab-0.3.2/pyrolab/manager.py +0 -618
  66. pyrolab-0.3.2/pyrolab/pyrolabd.py +0 -264
  67. pyrolab-0.3.2/pyrolab/utils.py +0 -56
  68. pyrolab-0.3.2/pyrolab.egg-info/PKG-INFO +0 -937
  69. pyrolab-0.3.2/pyrolab.egg-info/entry_points.txt +0 -2
  70. pyrolab-0.3.2/pyrolab.egg-info/requires.txt +0 -50
  71. pyrolab-0.3.2/tests/test_code_interface.py +0 -29
  72. pyrolab-0.3.2/tests/test_configure.py +0 -150
  73. pyrolab-0.3.2/tests/test_daemon.py +0 -24
  74. pyrolab-0.3.2/tests/test_errors.py +0 -0
  75. pyrolab-0.3.2/tests/test_manager.py +0 -0
  76. pyrolab-0.3.2/tests/test_nameserver.py +0 -0
  77. pyrolab-0.3.2/tests/test_pyrolabd.py +0 -0
  78. pyrolab-0.3.2/tests/test_service.py +0 -0
  79. pyrolab-0.3.2/tests/test_utils.py +0 -0
  80. pyrolab-0.3.2/tests/testsrv.py +0 -21
  81. {pyrolab-0.3.2 → pyrolab-0.5.0}/LICENSE +0 -0
  82. {pyrolab-0.3.2 → pyrolab-0.5.0}/MANIFEST.in +0 -0
  83. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/data/wordlist.txt +0 -0
  84. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/arduino/__init__.py +0 -0
  85. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/cameras/__init__.py +0 -0
  86. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/lasers/__init__.py +0 -0
  87. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/motion/__init__.py +0 -0
  88. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/scopes/__init__.py +0 -0
  89. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab/drivers/smu/__init__.py +0 -0
  90. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab.egg-info/dependency_links.txt +0 -0
  91. {pyrolab-0.3.2 → pyrolab-0.5.0}/pyrolab.egg-info/top_level.txt +0 -0
  92. {pyrolab-0.3.2 → pyrolab-0.5.0}/setup.cfg +0 -0
@@ -31,7 +31,7 @@ PPCL55X
31
31
  The code found in the following files were included in Pyrolab with some
32
32
  modifications:
33
33
 
34
- pyrolab/drivers/lasers/pplaser.py
34
+ pyrolab/drivers/lasers/ppcl55x.py
35
35
 
36
36
  The original source is available at
37
37
  https://www.pure-photonics.com/s/ITLA_v3-CUSTOMER.PY
pyrolab-0.5.0/PKG-INFO ADDED
@@ -0,0 +1,301 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyrolab
3
+ Version: 0.5.0
4
+ Summary: A framework for using remote lab instruments as local resources built on Pyro5.
5
+ Author-email: Sequoia Ploeg <sequoiap4@gmail.com>, BYU CamachoLab <camacho@byu.edu>
6
+ Maintainer-email: Sequoia Ploeg <sequoiap4@gmail.com>, BYU CamachoLab <camacho@byu.edu>
7
+ License: GPL-3.0+
8
+ Project-URL: Homepage, https://pyrolab.readthedocs.io/en/latest/
9
+ Project-URL: Documentation, https://pyrolab.readthedocs.io/en/latest/
10
+ Project-URL: Source, https://github.com/BYUCamachoLab/pyrolab
11
+ Project-URL: Bug Reports, https://github.com/BYUCamachoLab/pyrolab/issues
12
+ Project-URL: Research Group, https://camacholab.byu.edu/
13
+ Keywords: PyroLab,Pyro5,Pyro,lab,instrument,remote,resources,framework,laboratory,instrumentation,hardware,science,remote,network,integration
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: Topic :: Scientific/Engineering
19
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Programming Language :: Python :: 3 :: Only
26
+ Classifier: Operating System :: MacOS
27
+ Classifier: Operating System :: Microsoft :: Windows
28
+ Classifier: Operating System :: POSIX :: Linux
29
+ Classifier: Operating System :: OS Independent
30
+ Requires-Python: >=3.11
31
+ Description-Content-Type: text/markdown
32
+ License-File: LICENSE
33
+ License-File: NOTICE.txt
34
+ Requires-Dist: numpy
35
+ Requires-Dist: scipy
36
+ Requires-Dist: Pyro5
37
+ Requires-Dist: pydantic<3,>=2.8
38
+ Requires-Dist: pyyaml
39
+ Requires-Dist: deprecation
40
+ Requires-Dist: typer
41
+ Requires-Dist: colorama
42
+ Requires-Dist: tabulate
43
+ Requires-Dist: requests
44
+ Requires-Dist: packaging>=23.0
45
+ Requires-Dist: platformdirs>=3.0
46
+ Provides-Extra: tsl550
47
+ Requires-Dist: pyserial; extra == "tsl550"
48
+ Provides-Extra: ppcl55x
49
+ Requires-Dist: pyserial; extra == "ppcl55x"
50
+ Provides-Extra: rto
51
+ Requires-Dist: pyvisa; extra == "rto"
52
+ Requires-Dist: pyvisa-py; extra == "rto"
53
+ Provides-Extra: arduino
54
+ Requires-Dist: pyfirmata; extra == "arduino"
55
+ Provides-Extra: cameras
56
+ Requires-Dist: opencv-python; extra == "cameras"
57
+ Dynamic: license-file
58
+
59
+ <p align="center">
60
+ <img src="https://raw.githubusercontent.com/BYUCamachoLab/pyrolab/master/docs/_static/images/pyrolab_logo.svg" width="40%" alt="PyroLab">
61
+ </p>
62
+
63
+ ---
64
+
65
+ <p align="center">
66
+ <a href="https://pypi.python.org/pypi/pyrolab"><img alt="PyPI Version" src="https://img.shields.io/pypi/v/pyrolab.svg"></a>
67
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/pyrolab">
68
+ <a href="https://pyrolab.readthedocs.io/"><img alt="Documentation Status" src="https://readthedocs.org/projects/pyrolab/badge/?version=latest"></a>
69
+ <a href="https://pypi.python.org/pypi/pyrolab/"><img alt="License" src="https://img.shields.io/pypi/l/pyrolab.svg"></a>
70
+ <a href="https://github.com/BYUCamachoLab/pyrolab/commits/master"><img alt="Latest Commit" src="https://img.shields.io/github/last-commit/BYUCamachoLab/pyrolab.svg"></a>
71
+ </p>
72
+
73
+ A framework for using remote lab instruments as local resources, built on Pyro5.
74
+
75
+ Developed by Sequoia Ploeg (for [CamachoLab](https://camacholab.byu.edu/),
76
+ Brigham Young University).
77
+
78
+ ## About
79
+ This project aims to allow all laboratory instruments to be accessed as
80
+ local objects from a remote machine. Instruments that don't natively
81
+ support such access, such as those required to be connected by a USB cable
82
+ (or similar), are wrapped with a Pyro5 interface. However, this library may
83
+ contain other instruments that are already internet-capable and don't rely
84
+ on Pyro5. That's alright; we're just trying to create a minimal-dependency,
85
+ one-stop-shop for laboratory instruments!
86
+
87
+ **Note:** while the software says "OS Independent", some of the servers *are*
88
+ OS-specific. For example, ThorLabs DLL's only work on Windows. However, you could
89
+ use PyroLab to connect to those devices from any operating system.
90
+
91
+ ## Installation
92
+
93
+ PyroLab is pip installable:
94
+
95
+ ```
96
+ pip install pyrolab
97
+ ```
98
+
99
+ PyroLab declares several "extras", depending on which instruments you need
100
+ to support:
101
+
102
+ ```
103
+ pip install "pyrolab[tsl550,rto]"
104
+ ```
105
+
106
+ The full list of supported extras is:
107
+ - ``tsl550`` (Santec lasers)
108
+ - ``ppcl55x`` (PurePhotonics lasers)
109
+ - ``rto`` (Rohde & Schwarz oscilloscopes)
110
+ - ``arduino`` (Arduino)
111
+ - ``cameras`` (ThorCam and AmScope cameras, via OpenCV)
112
+ <!-- - ``thorlabs`` (ThorLabs motion controllers) -->
113
+ <!-- - ``adiscovery`` (Analog Discovery boards from Digilent) -->
114
+ <!-- - ``monitor`` (PyroMonitor web application) -->
115
+
116
+ You can also clone the repository, navigate to the toplevel, and install in
117
+ editable mode:
118
+
119
+ ```
120
+ pip install -e .
121
+ ```
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
+
129
+ ## Web Monitor
130
+
131
+ There's a web application that can monitor your PyroLab nameserver and
132
+ provide an easy-to-access status board. It's a Flask app that can be served
133
+ using a production grade server. Install it using pip:
134
+
135
+ ```
136
+ pip install pyromonitor
137
+ ```
138
+
139
+ To run:
140
+
141
+ ```
142
+ pyromonitor up
143
+ ```
144
+
145
+ ## Example
146
+
147
+ ### Local Instruments
148
+
149
+ Locally available instruments just import drivers without using any of the
150
+ other features of PyroLab.
151
+
152
+ ```
153
+ from pyrolab.drivers.lasers.tsl550 import TSL550
154
+
155
+ laser = TSL550("COM4")
156
+ laser.on()
157
+ laser.power_dBm(12)
158
+ laser.open_shutter()
159
+ laser.sweep_set_mode(continuous=True, twoway=True, trigger=False, const_freq_step=False)
160
+ ```
161
+
162
+ ### Remote Instruments
163
+
164
+ First, make sure all configurations on the nameserver computer, instrument
165
+ server computer, and client are correct (with the proper keys, if configured,
166
+ etc.).
167
+
168
+ Run a nameserver:
169
+
170
+ ```python
171
+ from pyrolab.api import start_ns_loop
172
+
173
+ start_ns_loop()
174
+ ```
175
+
176
+ Provide a service:
177
+
178
+ ```python
179
+ from pyrolab.api import Daemon, locate_ns
180
+ from pyrolab.drivers.sample import SampleService
181
+
182
+ daemon = Daemon()
183
+ ns = locate_ns(host="localhost")
184
+ uri = daemon.register(SampleService)
185
+ ns.register("test.SampleService", uri)
186
+
187
+ try:
188
+ daemon.requestLoop()
189
+ finally:
190
+ ns.remove("test.SampleService")
191
+ ```
192
+
193
+ Connect using a remote client:
194
+
195
+ ```python
196
+ from pyrolab.api import locate_ns, Proxy
197
+
198
+ ns = locate_ns(host="localhost")
199
+ uri = ns.lookup("test.SampleService")
200
+
201
+ with Proxy(uri) as service:
202
+ resp = service.echo("Hello, server!")
203
+ print(type(resp), resp)
204
+ ```
205
+
206
+ ## Instrument Server Configuration
207
+
208
+ PyroLab stores information about instruments and servers when it closes. This
209
+ means that once PyroLab has been configured once, each time it is restarted,
210
+ it will remember and reload the previous configuration. Hence, once a server
211
+ is set up, unless the available instruments, nameserver, or other
212
+ configurations change, PyroLab will automatically work when started, every
213
+ time!
214
+
215
+ For an example of how a new PyroLab instrument server should be configured the
216
+ first time it's run, see ``examples/library-catalog/config.yaml``.
217
+
218
+ ## FAQ's
219
+ 1. **Another instrument library? What about all the others?**
220
+ In our experience, many of the other libraries are buggy or have difficulty
221
+ with network connections. So, our approach was to rely on a well developed
222
+ and time-tested framework (Pyro) instead of worrying about developing and
223
+ supporting our own custom set of servers.
224
+
225
+ 2. **Is this a standalone software that automatically supports all the advertised
226
+ instruments?**
227
+ No; many of these instruments depend on other software already being
228
+ installed. In particular, ThorLabs equipment depends on ThorLabs software
229
+ already being installed on the computer connected to the physical hardware
230
+ (but not on the remote computer!). As much as possible, though, we try to
231
+ make the drivers standalone capable.
232
+
233
+ ## For Developers
234
+
235
+ Since the passing of data is, by definition, between hosts and over IP, PyroLab
236
+ avoids the use of complex Python objects for return values that will be
237
+ transmitted to remote machines. Since serialization is complicated, and
238
+ security is even harder, we resort to using only basic Python types when
239
+ interfacing with hardware (i.e., Python lists, ints, tuples, and not NumPy
240
+ arrays, matplotlib plot objects, custom objects, etc.).
241
+
242
+ ## Releasing
243
+
244
+ The version lives only in ``pyproject.toml``; ``pyrolab.__version__`` reads it
245
+ from the installed package's metadata.
246
+
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:
252
+
253
+ ```
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
257
+ ```
258
+
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.
286
+
287
+ ## Building the Docs
288
+
289
+ Much of the API documentation is autogenerated from the source code. Gitignores
290
+ are in place to prevent you from committing autogenerated pages.
291
+
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``.
295
+
296
+ ## Building the Package
297
+
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
300
+ before pushing to PyPI. In particular, double check the included files and make
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.3.2-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,22 +42,32 @@ 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:
50
- - tsl550
51
- - oscope
52
- - arduino
53
- - monitor
49
+ - ``tsl550`` (Santec lasers)
50
+ - ``ppcl55x`` (PurePhotonics lasers)
51
+ - ``rto`` (Rohde & Schwarz oscilloscopes)
52
+ - ``arduino`` (Arduino)
53
+ - ``cameras`` (ThorCam and AmScope cameras, via OpenCV)
54
+ <!-- - ``thorlabs`` (ThorLabs motion controllers) -->
55
+ <!-- - ``adiscovery`` (Analog Discovery boards from Digilent) -->
56
+ <!-- - ``monitor`` (PyroMonitor web application) -->
54
57
 
55
58
  You can also clone the repository, navigate to the toplevel, and install in
56
- editable mode (make sure you have pip >= 21.1):
59
+ editable mode:
57
60
 
58
61
  ```
59
62
  pip install -e .
60
63
  ```
61
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
+
62
71
  ## Web Monitor
63
72
 
64
73
  There's a web application that can monitor your PyroLab nameserver and
@@ -102,6 +111,7 @@ Run a nameserver:
102
111
 
103
112
  ```python
104
113
  from pyrolab.api import start_ns_loop
114
+
105
115
  start_ns_loop()
106
116
  ```
107
117
 
@@ -173,48 +183,61 @@ arrays, matplotlib plot objects, custom objects, etc.).
173
183
 
174
184
  ## Releasing
175
185
 
176
- Make sure you have committed a changelog file under ``docs/changelog`` titled
177
- ``<major>.<minor>.<patch>-changelog.md`` before bumping version. Also, the git
178
- 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.
179
188
 
180
- 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:
181
194
 
182
195
  ```
183
- bumpversion major
184
- bumpversion minor
185
- 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
186
199
  ```
187
200
 
188
- This will automatically create a git tag in the repository with the
189
- corrresponding version number and commit the modified files (where version
190
- numbers were updated). Pushing the tags (a manual process) to the remote will
191
- automatically create a new release. Releases are automatically published to
192
- PyPI and GitHub when git tags matching the "v*" pattern are created
193
- (e.g. "v0.3.2"), 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.
194
221
 
195
- After bumping version, you can view the tags on the local machine by running
196
- ``git tag``. To push the tags to the remote server and trigger the release
197
- workflow, you can run ``git push origin <tagname>``.
222
+ ## Code Quality
198
223
 
199
- For code quality, please run isort and black before committing (note that the
200
- latest release of isort may not work through VSCode's integrated terminal, and
201
- 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.
202
228
 
203
229
  ## Building the Docs
204
230
 
205
231
  Much of the API documentation is autogenerated from the source code. Gitignores
206
232
  are in place to prevent you from committing autogenerated pages.
207
233
 
208
- To build the docs, navigate to the ``docs/`` directory and run:
209
-
210
- ```
211
- pip install -r requirements.txt
212
- make html
213
- ```
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``.
214
237
 
215
238
  ## Building the Package
216
239
 
217
- You can test the build and view the bundled artifacts using
218
- [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
219
242
  before pushing to PyPI. In particular, double check the included files and make
220
243
  sure only the required files are there by modifying MANIFEST.in as necessary.
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "pyrolab"
3
- version = "0.3.2"
3
+ version = "0.5.0"
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.7"
7
- license = {file = "LICENSE"}
6
+ requires-python = ">=3.11"
7
+ license = {text = "GPL-3.0+"}
8
8
  keywords = [
9
9
  "PyroLab",
10
10
  "Pyro5",
@@ -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.7",
51
- "Programming Language :: Python :: 3.8",
52
- "Programming Language :: Python :: 3.9",
53
- "Programming Language :: Python :: 3.10",
54
50
  "Programming Language :: Python :: 3.11",
51
+ "Programming Language :: Python :: 3.12",
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",
65
+ "pydantic>=2.8,<3",
67
66
  "pyyaml",
68
67
  "deprecation",
69
68
  "typer",
@@ -71,44 +70,36 @@ 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"]
82
+ # thorlabs = ["thorlabs_kinesis@git+https://github.com/BYUCamachoLab/thorlabs-kinesis"]
84
83
  rto = ["pyvisa", "pyvisa-py"]
85
84
  arduino = ["pyfirmata"]
86
- test = ["pytest", "coverage"]
85
+ cameras = ["opencv-python"]
86
+ # adiscovery = ["WF_SDK@git+https://github.com/Digilent/WaveForms-SDK-Getting-Started-PY#egg=WF_SDK"]
87
+ # monitor = ["pyromonitor@git+https://github.com/BYUCamachoLab/pyromonitor"]
88
+
89
+ # Development-only dependencies; not published. `uv sync` installs "dev" by
90
+ # default; add `--group docs` for the documentation toolchain.
91
+ [dependency-groups]
87
92
  dev = [
88
- "notebook",
89
- "pytest",
90
- "mypy",
91
93
  "coverage",
92
- "flake8 >= 4.0.1",
93
- "isort >= 5.10.1",
94
- "black >= 22.1.0",
95
- "autopep8 >= 1.6.0",
96
- "twine >= 4.0.0",
97
- "build >= 0.10.0",
94
+ "mypy",
98
95
  "pre-commit",
99
- "bump2version ~= 1.0.1",
96
+ "pytest",
97
+ "ruff==0.16.10", # keep in step with .pre-commit-config.yaml
100
98
  ]
101
99
  docs = [
102
- "sphinx-book-theme ~= 0.3.3",
103
- "jupyter-book ~= 0.13.2",
104
- "sphinx-design ~= 0.1.0",
105
- "matplotlib",
106
- "numpy",
107
- "numpydoc",
108
- # "sphinx-tippy",
109
- # sphinx==5.3.0
110
- # linkify-it-py==2.0.0
111
- # sphinxcontrib-bibtex==2.5.0
100
+ "jupyter-book == 1.0.3",
101
+ "notebook",
102
+ "numpydoc==1.5.0",
112
103
  ]
113
104
 
114
105
  [tool.setuptools.packages.find]
@@ -123,7 +114,7 @@ namespaces = false
123
114
  "Research Group" = "https://camacholab.byu.edu/"
124
115
 
125
116
  [project.scripts]
126
- pyrolab = "pyrolab.cli:app"
117
+ pyrolab = "pyrolab.cli:run"
127
118
 
128
119
  [tool.pytest.ini_options]
129
120
  minversion = "6.0"
@@ -131,27 +122,35 @@ addopts = "--maxfail=2 -rf"
131
122
  testpaths = ["tests"]
132
123
  console_output_style = "progress"
133
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
134
138
 
135
139
  [build-system]
136
140
  requires = ["setuptools>=62.0.0", "wheel"]
137
141
  build-backend = "setuptools.build_meta"
138
142
 
139
- [tool.isort]
140
- profile = "black"
141
- extend_skip_glob = "api.py"
142
- multi_line_output = 3
143
- include_trailing_comma = true
144
- force_grid_wrap = 0
145
- use_parentheses = true
146
- line_length = 88
147
- known_third_party = [
148
- "Pyro5",
149
- "matplotlib",
150
- "numpy",
151
- "pytest",
152
- "scipy",
153
- "setuptools",
154
- "typer",
155
- "tabulate",
156
- ]
157
- 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"]