remoteRF-server 1.0.5__tar.gz → 1.0.7__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 (73) hide show
  1. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/PKG-INFO +10 -1
  2. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/README.md +9 -0
  3. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/pyproject.toml +1 -1
  4. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/usrp/usrp_schema.py +12 -1
  5. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/device_manager.py +69 -3
  6. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/serverrf_cli.py +10 -0
  7. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/PKG-INFO +10 -1
  8. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_default_usrp_schema.py +50 -1
  9. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_dynamic_v2.py +18 -0
  10. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/setup.cfg +0 -0
  11. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/__init__.py +0 -0
  12. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/__init__.py +0 -0
  13. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/__init__.py +0 -0
  14. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
  15. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
  16. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
  17. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
  18. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
  19. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/__init__.py +0 -0
  20. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/device_schema.py +0 -0
  21. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
  22. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/pluto_schema.py +0 -0
  23. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/schema.py +0 -0
  24. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/idl/v2.py +0 -0
  25. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/__init__.py +0 -0
  26. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
  27. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/api_token.py +0 -0
  28. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/db_connection.py +0 -0
  29. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/db_location.py +0 -0
  30. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/list_string.py +0 -0
  31. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/common/utils/process_arg.py +0 -0
  32. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/__init__.py +0 -0
  33. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
  34. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
  35. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
  36. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/adalm_pluto/pluto_schema.py +0 -0
  37. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
  38. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/usrp/policy.py +0 -0
  39. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +0 -0
  40. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/host/__init__.py +0 -0
  41. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/host/host_auth_token.py +0 -0
  42. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/host/host_directory_store.py +0 -0
  43. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
  44. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/__init__.py +0 -0
  45. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/acc_perms.py +0 -0
  46. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/cert_provider.py +0 -0
  47. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/dynamic_v2.py +0 -0
  48. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/grpc_server.py +0 -0
  49. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/reservation.py +0 -0
  50. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/rpc_manager.py +0 -0
  51. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/user_group_cli.py +0 -0
  52. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/user_group_handler.py +0 -0
  53. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/server/v2_errors.py +0 -0
  54. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/tools/__init__.py +0 -0
  55. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/tools/gen_certs.py +0 -0
  56. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/tools/gist_status.py +0 -0
  57. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
  58. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
  59. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/SOURCES.txt +0 -0
  60. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
  61. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
  62. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/requires.txt +0 -0
  63. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/src/remoteRF_server.egg-info/top_level.txt +0 -0
  64. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_cert_provider.py +0 -0
  65. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_idl_schema.py +0 -0
  66. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_pluto_compat.py +0 -0
  67. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_process_arg.py +0 -0
  68. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_reservation_timestamps.py +0 -0
  69. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_rpc_manager_calln.py +0 -0
  70. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_usrp_client_server_hardware.py +0 -0
  71. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_usrp_client_server_localhost.py +0 -0
  72. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
  73. {remoterf_server-1.0.5 → remoterf_server-1.0.7}/tests/test_usrp_uhd410_hardware.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -334,6 +334,15 @@ separate policy file. This includes transmit controls, transmit streaming,
334
334
  GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
335
335
  or required by the current server.
336
336
 
337
+ RemoteRF forces `UHD_LOG_CONSOLE_LEVEL=off` before importing UHD so native UHD
338
+ messages do not overwrite the interactive `serverrf` prompt. To temporarily
339
+ restore UHD diagnostics, explicitly set `REMOTERF_UHD_CONSOLE_LEVEL` to a level
340
+ such as `warning`, `info`, or `debug` before starting the server:
341
+
342
+ ```bash
343
+ REMOTERF_UHD_CONSOLE_LEVEL=warning serverrf -s
344
+ ```
345
+
337
346
  On every qualified target, generate the introspection record and enforce the
338
347
  pinned API gate:
339
348
 
@@ -319,6 +319,15 @@ separate policy file. This includes transmit controls, transmit streaming,
319
319
  GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
320
320
  or required by the current server.
321
321
 
322
+ RemoteRF forces `UHD_LOG_CONSOLE_LEVEL=off` before importing UHD so native UHD
323
+ messages do not overwrite the interactive `serverrf` prompt. To temporarily
324
+ restore UHD diagnostics, explicitly set `REMOTERF_UHD_CONSOLE_LEVEL` to a level
325
+ such as `warning`, `info`, or `debug` before starting the server:
326
+
327
+ ```bash
328
+ REMOTERF_UHD_CONSOLE_LEVEL=warning serverrf -s
329
+ ```
330
+
322
331
  On every qualified target, generate the introspection record and enforce the
323
332
  pinned API gate:
324
333
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "remoteRF-server"
7
- version = "1.0.5"
7
+ version = "1.0.7"
8
8
  description = "RemoteRF server-side control package"
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -6,6 +6,18 @@ from remoteRF_server.drivers.usrp.uhd_manifest import build_usrp_schema_v2
6
6
  c = idl_client
7
7
 
8
8
  import itertools
9
+ import os
10
+
11
+
12
+ def _configure_uhd_console_logging():
13
+ """Keep native UHD logs out of the interactive server console by default."""
14
+ os.environ["UHD_LOG_CONSOLE_LEVEL"] = os.getenv(
15
+ "REMOTERF_UHD_CONSOLE_LEVEL",
16
+ "off",
17
+ )
18
+
19
+
20
+ _configure_uhd_console_logging()
9
21
 
10
22
  try:
11
23
  import uhd
@@ -77,7 +89,6 @@ def _connect_usrp(args=None, serial=None, addr=None, **kwargs):
77
89
 
78
90
  args = _device_args(args)
79
91
  dev = uhd_module.usrp.MultiUSRP(args)
80
- print(f"Connected to USRP with args={args!r}")
81
92
  return dev
82
93
 
83
94
 
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import os
6
6
  import re
7
7
  import shutil
8
+ import hashlib
8
9
  import time
9
10
  import yaml
10
11
  import threading
@@ -419,10 +420,60 @@ def _driver_registers_type(path: Path, device_type: str) -> bool:
419
420
  pattern = rf"(?:idl_register|register_driver)\(\s*['\"]{re.escape(device_type)}['\"]\s*\)"
420
421
  return re.search(pattern, text) is not None
421
422
 
422
- def _seed_default_driver(filename: str, *device_types: str) -> None:
423
+ def _driver_sha256(path: Path) -> str:
424
+ digest = hashlib.sha256()
425
+ with path.open("rb") as handle:
426
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
427
+ digest.update(chunk)
428
+ return digest.hexdigest()
429
+
430
+
431
+ def _managed_driver_marker(target: Path) -> Path:
432
+ return target.with_name(f".{target.name}.remoterf-managed")
433
+
434
+
435
+ def _refresh_managed_driver(
436
+ target: Path,
437
+ source: Path,
438
+ *,
439
+ legacy_hashes: tuple[str, ...] = (),
440
+ ) -> bool:
441
+ """Refresh an unmodified RemoteRF-managed schema and preserve user edits."""
442
+ marker = _managed_driver_marker(target)
443
+ try:
444
+ target_hash = _driver_sha256(target)
445
+ marker_hash = marker.read_text(encoding="utf-8").strip() if marker.exists() else ""
446
+ except OSError:
447
+ return False
448
+
449
+ if target_hash != marker_hash and target_hash not in legacy_hashes:
450
+ return False
451
+
452
+ try:
453
+ source_hash = _driver_sha256(source)
454
+ if target_hash != source_hash:
455
+ shutil.copy2(source, target)
456
+ marker.write_text(source_hash + "\n", encoding="utf-8")
457
+ except OSError:
458
+ return False
459
+ return True
460
+
461
+
462
+ def _seed_default_driver(
463
+ filename: str,
464
+ *device_types: str,
465
+ legacy_hashes: tuple[str, ...] = (),
466
+ ) -> None:
423
467
  drivers_dir = get_drivers_dir()
424
468
  target = drivers_dir / filename
469
+ source = _packaged_driver_schema_path(filename)
425
470
  if target.exists() or target.is_symlink():
471
+ if target.is_file() and source.exists():
472
+ _refresh_managed_driver(
473
+ target,
474
+ source,
475
+ legacy_hashes=legacy_hashes,
476
+ )
426
477
  return
427
478
 
428
479
  try:
@@ -437,13 +488,16 @@ def _seed_default_driver(filename: str, *device_types: str) -> None:
437
488
  if any(_driver_registers_type(driver_file, device_type) for device_type in device_types):
438
489
  return
439
490
 
440
- source = _packaged_driver_schema_path(filename)
441
491
  if not source.exists():
442
492
  print(f"Warning: default driver schema source is missing: {source}")
443
493
  return
444
494
 
445
495
  try:
446
496
  shutil.copy2(source, target)
497
+ _managed_driver_marker(target).write_text(
498
+ _driver_sha256(source) + "\n",
499
+ encoding="utf-8",
500
+ )
447
501
  print(f"Created default driver config at {target}")
448
502
  except OSError as e:
449
503
  print(f"Warning: could not create default driver config {target}: {e}")
@@ -453,7 +507,19 @@ def _seed_default_pluto_drivers() -> None:
453
507
  _seed_default_driver("pluto_mimo_schema.py", "adalm_pluto_mimo")
454
508
 
455
509
  def _seed_default_usrp_driver() -> None:
456
- _seed_default_driver("usrp_schema.py", "usrp")
510
+ _seed_default_driver(
511
+ "usrp_schema.py",
512
+ "usrp",
513
+ legacy_hashes=(
514
+ # Previously shipped built-in copies. These are safe to refresh;
515
+ # any edited/custom schema has a different digest and is preserved.
516
+ "ec30aaadb1e8edec289de01e4f997aa3c8b59d3fd0a69c5ac94b37b05dac7a65",
517
+ "baf29cd0f22e84891b546c30c963cddb02bc93db0efb50fe32cc743c8600c227",
518
+ "01360d017359d6cb65761b78b5c2bb570b8f2f55e8ab280ba23d6813deddb233",
519
+ "ca32b958759a5f7933e10426a320ca74e7546734524749ea510de83cf5084397",
520
+ "0d185cd24571bae2d5f75d6c28164694918a86b20854b96fe9b77c475ca28ed1",
521
+ ),
522
+ )
457
523
 
458
524
  def _load_all_drivers() -> None:
459
525
  _seed_default_pluto_drivers()
@@ -337,6 +337,16 @@ def _serve(*, headless: bool = False) -> int:
337
337
  if not _ensure_tos_acknowledged():
338
338
  return 1
339
339
 
340
+ # UHD writes native log messages to stderr/std::clog. Set this before the
341
+ # server imports device drivers so those messages cannot corrupt the
342
+ # interactive prompt. RemoteRF keeps this off even when the surrounding
343
+ # shell has a noisy UHD default; diagnostics require an explicit RemoteRF
344
+ # opt-in.
345
+ os.environ["UHD_LOG_CONSOLE_LEVEL"] = os.getenv(
346
+ "REMOTERF_UHD_CONSOLE_LEVEL",
347
+ "off",
348
+ )
349
+
340
350
  from remoteRF_server.server.grpc_server import (
341
351
  headless_main,
342
352
  main as interactive_main,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoteRF-server
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: RemoteRF server-side control package
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -334,6 +334,15 @@ separate policy file. This includes transmit controls, transmit streaming,
334
334
  GPIO/register methods, and property-tree writes. `usrp_policy.yml` is not read
335
335
  or required by the current server.
336
336
 
337
+ RemoteRF forces `UHD_LOG_CONSOLE_LEVEL=off` before importing UHD so native UHD
338
+ messages do not overwrite the interactive `serverrf` prompt. To temporarily
339
+ restore UHD diagnostics, explicitly set `REMOTERF_UHD_CONSOLE_LEVEL` to a level
340
+ such as `warning`, `info`, or `debug` before starting the server:
341
+
342
+ ```bash
343
+ REMOTERF_UHD_CONSOLE_LEVEL=warning serverrf -s
344
+ ```
345
+
337
346
  On every qualified target, generate the introspection record and enforce the
338
347
  pinned API gate:
339
348
 
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import hashlib
2
3
  from pathlib import Path
3
4
  import subprocess
4
5
  import sys
@@ -12,7 +13,13 @@ SOURCE_ROOT = PROJECT_ROOT / "src"
12
13
 
13
14
 
14
15
  class DefaultUsrpSchemaTests(unittest.TestCase):
15
- def _run_fresh_server_import(self, script, *, custom_driver=None):
16
+ def _run_fresh_server_import(
17
+ self,
18
+ script,
19
+ *,
20
+ custom_driver=None,
21
+ managed_driver=None,
22
+ ):
16
23
  with tempfile.TemporaryDirectory() as raw_temp:
17
24
  temp = Path(raw_temp)
18
25
  blocker = temp / "blocker"
@@ -30,6 +37,15 @@ class DefaultUsrpSchemaTests(unittest.TestCase):
30
37
  custom_driver,
31
38
  encoding="utf-8",
32
39
  )
40
+ if managed_driver is not None:
41
+ drivers_dir.mkdir(parents=True, exist_ok=True)
42
+ target = drivers_dir / "usrp_schema.py"
43
+ target.write_text(managed_driver, encoding="utf-8")
44
+ marker = drivers_dir / ".usrp_schema.py.remoterf-managed"
45
+ marker.write_text(
46
+ hashlib.sha256(managed_driver.encode("utf-8")).hexdigest() + "\n",
47
+ encoding="utf-8",
48
+ )
33
49
 
34
50
  env = dict(os.environ)
35
51
  env["HOME"] = str(temp / "home")
@@ -58,6 +74,7 @@ class DefaultUsrpSchemaTests(unittest.TestCase):
58
74
 
59
75
  seeded = get_drivers_dir() / "usrp_schema.py"
60
76
  assert seeded.is_file(), seeded
77
+ assert (get_drivers_dir() / ".usrp_schema.py.remoterf-managed").is_file()
61
78
 
62
79
  schema = get_driver_class("usrp")()
63
80
  legacy = schema.get_idl()
@@ -124,6 +141,38 @@ class DefaultUsrpSchemaTests(unittest.TestCase):
124
141
  )
125
142
  self.assertIn("CUSTOM_USRP_SCHEMA_OK", result.stdout)
126
143
 
144
+ def test_unmodified_managed_usrp_driver_is_refreshed_on_upgrade(self):
145
+ stale_managed_driver = textwrap.dedent(
146
+ """
147
+ from remoteRF_server.common.idl import DeviceSchema, idl_register
148
+
149
+ @idl_register("usrp")
150
+ class StaleManagedUsrpSchema(DeviceSchema):
151
+ device_type = "usrp"
152
+ driver_version = "stale-managed"
153
+ """
154
+ )
155
+ result = self._run_fresh_server_import(
156
+ """
157
+ from remoteRF_server.common.idl.schema import get_driver_class
158
+ from remoteRF_server.common.utils import get_drivers_dir
159
+ from remoteRF_server.server import device_manager
160
+
161
+ schema_path = get_drivers_dir() / "usrp_schema.py"
162
+ assert "stale-managed" not in schema_path.read_text(encoding="utf-8")
163
+ assert get_driver_class("usrp").driver_version != "stale-managed"
164
+ print("MANAGED_USRP_SCHEMA_REFRESHED")
165
+ """,
166
+ managed_driver=stale_managed_driver,
167
+ )
168
+
169
+ self.assertEqual(
170
+ result.returncode,
171
+ 0,
172
+ msg=f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}",
173
+ )
174
+ self.assertIn("MANAGED_USRP_SCHEMA_REFRESHED", result.stdout)
175
+
127
176
 
128
177
  if __name__ == "__main__":
129
178
  unittest.main()
@@ -14,6 +14,7 @@ from remoteRF_server.common.grpc import grpc_pb2
14
14
  from remoteRF_server.common.grpc.v2_codec import decode_value, encode_value
15
15
  from remoteRF_server.common.idl.v2 import validate_schema
16
16
  from remoteRF_server.drivers.usrp.policy import USRPPolicy
17
+ from remoteRF_server.drivers.usrp.usrp_schema import _configure_uhd_console_logging
17
18
  from remoteRF_server.drivers.usrp.uhd_manifest import (
18
19
  MULTIUSRP_METHODS,
19
20
  TARGET_UHD_VERSION,
@@ -138,6 +139,23 @@ class FakeInventorySchema:
138
139
 
139
140
 
140
141
  class DynamicV2ServerTests(unittest.TestCase):
142
+ def test_usrp_console_logging_is_forced_off_without_remoterf_opt_in(self):
143
+ with patch.dict(
144
+ os.environ,
145
+ {"UHD_LOG_CONSOLE_LEVEL": "debug"},
146
+ clear=True,
147
+ ):
148
+ _configure_uhd_console_logging()
149
+ self.assertEqual(os.environ["UHD_LOG_CONSOLE_LEVEL"], "off")
150
+
151
+ with patch.dict(
152
+ os.environ,
153
+ {"REMOTERF_UHD_CONSOLE_LEVEL": "warning"},
154
+ clear=True,
155
+ ):
156
+ _configure_uhd_console_logging()
157
+ self.assertEqual(os.environ["UHD_LOG_CONSOLE_LEVEL"], "warning")
158
+
141
159
  def test_usrp2901_manifest_is_valid_and_has_no_deferred_reachable_api(self):
142
160
  schema = build_usrp_schema_v2()
143
161
  validate_schema(schema)