PyroLab 0.3.2__py3-none-any.whl → 0.5.0__py3-none-any.whl
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/__init__.py +42 -93
- pyrolab/api.py +21 -9
- pyrolab/cli.py +604 -205
- pyrolab/configure.py +327 -104
- pyrolab/drivers/VISAResourceExtensions.py +65 -34
- pyrolab/drivers/__init__.py +50 -25
- pyrolab/drivers/arduino/arduino.py +71 -19
- pyrolab/drivers/cameras/amcam.py +61 -0
- pyrolab/drivers/cameras/sciTSI.py +41 -34
- pyrolab/drivers/cameras/thorcam.py +122 -73
- pyrolab/drivers/cameras/uc480.py +68 -63
- pyrolab/drivers/fpgas/__init__.py +16 -0
- pyrolab/drivers/fpgas/analogdiscovery.py +309 -0
- pyrolab/drivers/function_generators/__init__.py +16 -0
- pyrolab/drivers/function_generators/smcv100b.py +389 -0
- pyrolab/drivers/lasers/ppcl55x.py +302 -262
- pyrolab/drivers/lasers/sachertec.py +25 -20
- pyrolab/drivers/lasers/tsl550.py +232 -176
- pyrolab/drivers/motion/KMTS25E.py +92 -0
- pyrolab/drivers/motion/kinesis/__init__.py +9 -5
- pyrolab/drivers/motion/kinesis/bpc303.py +86 -43
- pyrolab/drivers/motion/kinesis/kdc101.py +123 -95
- pyrolab/drivers/motion/max31x.py +31 -24
- pyrolab/drivers/motion/prm1z8.py +2 -1
- pyrolab/drivers/motion/z8xx.py +13 -10
- pyrolab/drivers/sample.py +16 -11
- pyrolab/drivers/scopes/rohdeschwarz.py +137 -114
- pyrolab/drivers/smu/keysight.py +94 -83
- pyrolab/locations.py +129 -0
- pyrolab/logs.py +391 -0
- pyrolab/manager.py +906 -278
- pyrolab/nameserver.py +58 -25
- pyrolab/pyrolabd.py +289 -73
- pyrolab/server.py +110 -62
- pyrolab/service.py +13 -8
- pyrolab/updates.py +108 -0
- pyrolab/utils.py +180 -12
- pyrolab-0.5.0.dist-info/METADATA +301 -0
- pyrolab-0.5.0.dist-info/RECORD +51 -0
- {pyrolab-0.3.2.dist-info → pyrolab-0.5.0.dist-info}/WHEEL +1 -1
- pyrolab-0.5.0.dist-info/entry_points.txt +2 -0
- {pyrolab-0.3.2.dist-info → pyrolab-0.5.0.dist-info/licenses}/NOTICE.txt +1 -1
- pyrolab-0.3.2.dist-info/AUTHORS.txt +0 -20
- pyrolab-0.3.2.dist-info/METADATA +0 -973
- pyrolab-0.3.2.dist-info/RECORD +0 -43
- pyrolab-0.3.2.dist-info/entry_points.txt +0 -2
- {pyrolab-0.3.2.dist-info → pyrolab-0.5.0.dist-info/licenses}/LICENSE +0 -0
- {pyrolab-0.3.2.dist-info → pyrolab-0.5.0.dist-info}/top_level.txt +0 -0
pyrolab/__init__.py
CHANGED
|
@@ -23,119 +23,68 @@ PyroLab
|
|
|
23
23
|
A framework for using remote lab instruments as local resources built on Pyro5.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
+
import logging
|
|
26
27
|
import os
|
|
27
|
-
import pathlib
|
|
28
28
|
import platform
|
|
29
29
|
import sys
|
|
30
30
|
|
|
31
31
|
# Check if Python version is supported
|
|
32
32
|
pyversion = sys.version_info
|
|
33
|
-
if pyversion < (3,
|
|
33
|
+
if pyversion < (3, 11, 0):
|
|
34
34
|
raise Exception(
|
|
35
|
-
"PyroLab requires Python 3.
|
|
35
|
+
"PyroLab requires Python 3.11+ (version "
|
|
36
36
|
+ platform.python_version()
|
|
37
37
|
+ " detected)."
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# Metadata
|
|
42
|
-
|
|
42
|
+
__title__ = "PyroLab"
|
|
43
43
|
__author__ = "CamachoLab"
|
|
44
44
|
__copyright__ = "Copyright 2020, The PyroLab Project"
|
|
45
|
-
|
|
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"
|
|
46
52
|
__license__ = "GPLv3+"
|
|
47
53
|
__maintainer__ = "Sequoia Ploeg"
|
|
48
54
|
__maintainer_email__ = "sequoia.ploeg@byu.edu"
|
|
49
|
-
__status__ = "Development"
|
|
55
|
+
__status__ = "Development" # "Production"
|
|
50
56
|
__project_url__ = "https://github.com/sequoiap/pyrolab"
|
|
51
57
|
__forum_url__ = "https://github.com/sequoiap/pyrolab/issues"
|
|
52
58
|
__website_url__ = "https://camacholab.byu.edu/"
|
|
53
59
|
|
|
54
60
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
# Set up logging to file
|
|
88
|
-
import logging
|
|
89
|
-
import logging.handlers
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def get_loglevel() -> int:
|
|
93
|
-
loglevel = os.getenv("PYROLAB_LOGLEVEL", "INFO")
|
|
94
|
-
try:
|
|
95
|
-
loglevel = getattr(logging, loglevel.upper())
|
|
96
|
-
except AttributeError:
|
|
97
|
-
loglevel = logging.INFO
|
|
98
|
-
return loglevel
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if len(logging.root.handlers) == 0:
|
|
102
|
-
logfile = PYROLAB_LOGDIR / f"pyrolab_{os.getpid()}.log"
|
|
103
|
-
root = logging.getLogger()
|
|
104
|
-
h = logging.handlers.RotatingFileHandler(logfile, 'a', 30000, 10)
|
|
105
|
-
f = logging.Formatter('[%(asctime)s.%(msecs)03d] %(process)-5s %(processName)-10s %(name)-12s %(levelname)-8s %(message)s', datefmt="%Y-%m-%d %H:%M:%S")
|
|
106
|
-
h.setFormatter(f)
|
|
107
|
-
root.addHandler(h)
|
|
108
|
-
root.setLevel(get_loglevel())
|
|
109
|
-
root.debug("PyroLab logging configured")
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Include remote traceback in local tracebacks
|
|
113
|
-
import Pyro5.errors
|
|
114
|
-
|
|
115
|
-
sys.excepthook = Pyro5.errors.excepthook
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
# Check for updates to PyroLab
|
|
119
|
-
try:
|
|
120
|
-
import json
|
|
121
|
-
import requests
|
|
122
|
-
from requests.adapters import HTTPAdapter
|
|
123
|
-
from packaging.version import parse
|
|
124
|
-
|
|
125
|
-
url = "https://pypi.org/pypi/pyrolab/json"
|
|
126
|
-
with requests.Session() as s:
|
|
127
|
-
s.mount('https://pypi.org', HTTPAdapter(max_retries=3))
|
|
128
|
-
resp = s.get(url).text
|
|
129
|
-
|
|
130
|
-
v = json.loads(resp)['info']['version']
|
|
131
|
-
curver = parse(__version__)
|
|
132
|
-
latest = parse(v)
|
|
133
|
-
|
|
134
|
-
log = logging.getLogger(__name__)
|
|
135
|
-
|
|
136
|
-
if curver < latest:
|
|
137
|
-
message = f"A new version of PyroLab is available (latest is {latest}, but {curver} is installed)."
|
|
138
|
-
warnings.warn(message, stacklevel=2)
|
|
139
|
-
log.info(message)
|
|
140
|
-
except:
|
|
141
|
-
pass
|
|
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())
|
pyrolab/api.py
CHANGED
|
@@ -14,11 +14,11 @@ from Pyro5.core import locate_ns
|
|
|
14
14
|
from Pyro5.server import behavior, expose, oneway, serve
|
|
15
15
|
from pyrolab import USER_CONFIG_FILE
|
|
16
16
|
from pyrolab.configure import (
|
|
17
|
-
PyroLabConfiguration,
|
|
18
|
-
NameServerConfiguration,
|
|
19
|
-
DaemonConfiguration,
|
|
20
|
-
ServiceConfiguration,
|
|
21
|
-
reset_config,
|
|
17
|
+
PyroLabConfiguration,
|
|
18
|
+
NameServerConfiguration,
|
|
19
|
+
DaemonConfiguration,
|
|
20
|
+
ServiceConfiguration,
|
|
21
|
+
reset_config,
|
|
22
22
|
update_config,
|
|
23
23
|
)
|
|
24
24
|
from pyrolab.server import Daemon, LockableDaemon
|
|
@@ -47,8 +47,20 @@ __all__ = [
|
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
# If a user config file exists, load the first listed nameserver by default,
|
|
50
|
-
# so that locate_ns "just works."
|
|
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
|
+
)
|