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.
- {pyrolab-0.4.0/pyrolab.egg-info → pyrolab-0.5.0rc2}/PKG-INFO +59 -57
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/README.md +50 -31
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyproject.toml +44 -40
- pyrolab-0.5.0rc2/pyrolab/__init__.py +90 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/api.py +15 -3
- pyrolab-0.5.0rc2/pyrolab/cli.py +843 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/configure.py +221 -36
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/__init__.py +32 -8
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/arduino/arduino.py +42 -5
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/amcam.py +9 -9
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/thorcam.py +3 -5
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/uc480.py +2 -3
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/fpgas/analogdiscovery.py +2 -3
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/ppcl55x.py +57 -43
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/tsl550.py +14 -11
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/bpc303.py +0 -1
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/kdc101.py +10 -10
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/max31x.py +0 -1
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/sample.py +3 -3
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/scopes/rohdeschwarz.py +19 -11
- pyrolab-0.5.0rc2/pyrolab/locations.py +129 -0
- pyrolab-0.5.0rc2/pyrolab/logs.py +359 -0
- pyrolab-0.5.0rc2/pyrolab/manager.py +1228 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/nameserver.py +35 -14
- pyrolab-0.5.0rc2/pyrolab/pyrolabd.py +460 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/server.py +41 -5
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/service.py +0 -1
- pyrolab-0.5.0rc2/pyrolab/updates.py +108 -0
- pyrolab-0.5.0rc2/pyrolab/utils.py +189 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2/pyrolab.egg-info}/PKG-INFO +59 -57
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/SOURCES.txt +12 -5
- pyrolab-0.5.0rc2/pyrolab.egg-info/entry_points.txt +2 -0
- pyrolab-0.5.0rc2/pyrolab.egg-info/requires.txt +28 -0
- pyrolab-0.5.0rc2/tests/test_cli.py +840 -0
- pyrolab-0.5.0rc2/tests/test_configure.py +604 -0
- pyrolab-0.5.0rc2/tests/test_imports.py +109 -0
- pyrolab-0.5.0rc2/tests/test_lifecycle.py +528 -0
- pyrolab-0.5.0rc2/tests/test_locations.py +131 -0
- pyrolab-0.5.0rc2/tests/test_logs.py +333 -0
- pyrolab-0.5.0rc2/tests/test_manager.py +312 -0
- pyrolab-0.5.0rc2/tests/test_nameserver.py +106 -0
- pyrolab-0.5.0rc2/tests/test_pyrolabd.py +347 -0
- pyrolab-0.5.0rc2/tests/test_registration.py +357 -0
- pyrolab-0.5.0rc2/tests/test_server.py +219 -0
- pyrolab-0.5.0rc2/tests/test_service.py +230 -0
- pyrolab-0.5.0rc2/tests/test_updates.py +154 -0
- pyrolab-0.5.0rc2/tests/test_utils.py +139 -0
- pyrolab-0.4.0/pyrolab/__init__.py +0 -190
- pyrolab-0.4.0/pyrolab/cli.py +0 -564
- pyrolab-0.4.0/pyrolab/manager.py +0 -671
- pyrolab-0.4.0/pyrolab/pyrolabd.py +0 -282
- pyrolab-0.4.0/pyrolab/utils.py +0 -64
- pyrolab-0.4.0/pyrolab.egg-info/entry_points.txt +0 -2
- pyrolab-0.4.0/pyrolab.egg-info/requires.txt +0 -46
- pyrolab-0.4.0/tests/test_code_interface.py +0 -29
- pyrolab-0.4.0/tests/test_configure.py +0 -150
- pyrolab-0.4.0/tests/test_daemon.py +0 -24
- pyrolab-0.4.0/tests/test_errors.py +0 -0
- pyrolab-0.4.0/tests/test_manager.py +0 -0
- pyrolab-0.4.0/tests/test_nameserver.py +0 -0
- pyrolab-0.4.0/tests/test_pyrolabd.py +0 -0
- pyrolab-0.4.0/tests/test_service.py +0 -0
- pyrolab-0.4.0/tests/test_utils.py +0 -0
- pyrolab-0.4.0/tests/testsrv.py +0 -21
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/LICENSE +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/MANIFEST.in +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/NOTICE.txt +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/data/wordlist.txt +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/VISAResourceExtensions.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/arduino/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/cameras/sciTSI.py +2 -2
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/fpgas/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/function_generators/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/function_generators/smcv100b.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/lasers/sachertec.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/KMTS25E.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/kinesis/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/prm1z8.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/motion/z8xx.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/scopes/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/smu/__init__.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab/drivers/smu/keysight.py +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/dependency_links.txt +0 -0
- {pyrolab-0.4.0 → pyrolab-0.5.0rc2}/pyrolab.egg-info/top_level.txt +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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:
|
|
56
|
-
Requires-Dist:
|
|
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,
|
|
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
|
-
- ``
|
|
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
|
|
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
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
``
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
- ``
|
|
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
|
|
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
|
-
|
|
181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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,
|
|
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
|
|
222
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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:
|
|
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.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
+
)
|