cgse-common 0.17.1__tar.gz → 0.17.3__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 (48) hide show
  1. {cgse_common-0.17.1 → cgse_common-0.17.3}/.gitignore +5 -0
  2. {cgse_common-0.17.1 → cgse_common-0.17.3}/PKG-INFO +1 -1
  3. {cgse_common-0.17.1 → cgse_common-0.17.3}/pyproject.toml +14 -6
  4. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/bits.py +14 -17
  5. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/decorators.py +6 -3
  6. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/device.py +0 -70
  7. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/env.py +2 -1
  8. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/exceptions.py +3 -0
  9. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/log.py +7 -8
  10. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/scpi.py +47 -112
  11. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/settings.py +31 -0
  12. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/system.py +63 -24
  13. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/version.py +24 -13
  14. cgse_common-0.17.1/src/egse/plugins/metrics/duckdb.py +0 -442
  15. cgse_common-0.17.1/src/egse/plugins/metrics/timescaledb.py +0 -596
  16. cgse_common-0.17.1/src/egse/ratelimit.py +0 -275
  17. cgse_common-0.17.1/src/egse/socketdevice.py +0 -379
  18. {cgse_common-0.17.1 → cgse_common-0.17.3}/README.md +0 -0
  19. {cgse_common-0.17.1 → cgse_common-0.17.3}/justfile +0 -0
  20. {cgse_common-0.17.1 → cgse_common-0.17.3}/noxfile.py +0 -0
  21. {cgse_common-0.17.1 → cgse_common-0.17.3}/service_registry.db +0 -0
  22. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/cgse_common/__init__.py +0 -0
  23. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/cgse_common/cgse.py +0 -0
  24. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/cgse_common/settings.yaml +0 -0
  25. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/calibration.py +0 -0
  26. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/config.py +0 -0
  27. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/counter.py +0 -0
  28. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/dicts.py +0 -0
  29. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/heartbeat.py +0 -0
  30. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/hk.py +0 -0
  31. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/metrics.py +0 -0
  32. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/observer.py +0 -0
  33. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/obsid.py +0 -0
  34. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/persistence.py +0 -0
  35. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/plugin.py +0 -0
  36. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/plugins/metrics/influxdb.py +0 -0
  37. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/process.py +0 -0
  38. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/py.typed +0 -0
  39. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/randomwalk.py +0 -0
  40. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/reload.py +0 -0
  41. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/resource.py +0 -0
  42. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/response.py +0 -0
  43. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/settings.yaml +0 -0
  44. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/setup.py +0 -0
  45. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/signal.py +0 -0
  46. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/state.py +0 -0
  47. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/task.py +0 -0
  48. {cgse_common-0.17.1 → cgse_common-0.17.3}/src/egse/zmq_ser.py +0 -0
@@ -32,6 +32,11 @@ venv
32
32
 
33
33
  .idea
34
34
 
35
+ # VSCode IDE
36
+
37
+ .vscode
38
+ *.code-workspace
39
+
35
40
  # MKDOCS documentation site
36
41
 
37
42
  /site
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cgse-common
3
- Version: 0.17.1
3
+ Version: 0.17.3
4
4
  Summary: Software framework to support hardware testing
5
5
  Author: IvS KU Leuven
6
6
  Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cgse-common"
3
- version = "0.17.1"
3
+ version = "0.17.3"
4
4
  description = "Software framework to support hardware testing"
5
5
  authors = [
6
6
  {name = "IvS KU Leuven"}
@@ -68,19 +68,23 @@ markers = [
68
68
  "slow: marks tests as slow (deselect with '-m \"not slow\"')"
69
69
  ]
70
70
 
71
+ # Some of these omitted files are only temporary omitted until refactored
71
72
  [tool.coverage.run]
72
73
  omit = [
73
74
  "tests/*",
74
75
  "conftest.py",
76
+ "src/egse/ratelimit.py",
77
+ "src/egse/plugins/metrics/duckdb.py",
78
+ "src/egse/plugins/metrics/timescaledb.py",
75
79
  ]
76
80
 
77
81
  [tool.hatch.build.targets.sdist]
78
82
  exclude = [
79
- "/tests",
80
- "/pytest.ini",
81
- "/.gitignore",
82
- "/*poetry*",
83
- "/*setuptools*",
83
+ "tests",
84
+ "pytest.ini",
85
+ ".gitignore",
86
+ "*poetry*",
87
+ "*setuptools*",
84
88
  ]
85
89
 
86
90
  [tool.hatch.build.targets.wheel]
@@ -90,8 +94,12 @@ packages = ["src/egse", "src/scripts", "src/cgse_common"]
90
94
  line-length = 120
91
95
 
92
96
  [tool.ruff.lint]
97
+ select = ["F", "I"]
93
98
  extend-select = ["E", "W"]
94
99
 
100
+ [tool.ruff.lint.isort]
101
+ force-single-line = true
102
+
95
103
  [build-system]
96
104
  requires = ["hatchling"]
97
105
  build-backend = "hatchling.build"
@@ -5,7 +5,6 @@ This module contains a number of convenience functions to work with bits, bytes
5
5
  from __future__ import annotations
6
6
 
7
7
  import ctypes
8
- from collections.abc import Iterable
9
8
  from typing import Union
10
9
 
11
10
 
@@ -33,7 +32,7 @@ def extract_bits(value: int, start_position: int, num_bits: int) -> int:
33
32
  return extracted_bits
34
33
 
35
34
 
36
- def set_bit(value: int, bit) -> int:
35
+ def set_bit(value: int, bit: int) -> int:
37
36
  """
38
37
  Set bit to 1 for the given value.
39
38
 
@@ -47,7 +46,7 @@ def set_bit(value: int, bit) -> int:
47
46
  return value | (1 << bit)
48
47
 
49
48
 
50
- def set_bits(value: int, bits: tuple) -> int:
49
+ def set_bits(value: int, bits: tuple[int, int]) -> int:
51
50
  """
52
51
  Set the given bits in value to 1.
53
52
 
@@ -63,7 +62,7 @@ def set_bits(value: int, bits: tuple) -> int:
63
62
  return value
64
63
 
65
64
 
66
- def clear_bit(value: int, bit) -> int:
65
+ def clear_bit(value: int, bit: int) -> int:
67
66
  """
68
67
  Set bit to 0 for the given value.
69
68
 
@@ -77,7 +76,7 @@ def clear_bit(value: int, bit) -> int:
77
76
  return value & ~(1 << bit)
78
77
 
79
78
 
80
- def clear_bits(value: int, bits: tuple) -> int:
79
+ def clear_bits(value: int, bits: tuple[int, int]) -> int:
81
80
  """
82
81
  Set the given bits in value to 0.
83
82
 
@@ -93,7 +92,7 @@ def clear_bits(value: int, bits: tuple) -> int:
93
92
  return value
94
93
 
95
94
 
96
- def toggle_bit(value: int, bit) -> int:
95
+ def toggle_bit(value: int, bit: int) -> int:
97
96
  """
98
97
  Toggle the bit in the given value.
99
98
 
@@ -107,7 +106,7 @@ def toggle_bit(value: int, bit) -> int:
107
106
  return value ^ (1 << bit)
108
107
 
109
108
 
110
- def bit_set(value: int, bit) -> bool:
109
+ def bit_set(value: int, bit: int) -> bool:
111
110
  """
112
111
  Return True if the bit is set.
113
112
 
@@ -122,27 +121,25 @@ def bit_set(value: int, bit) -> bool:
122
121
  return value & bit_value == bit_value
123
122
 
124
123
 
125
- def bits_set(value: int, *args: Union[int, Iterable[int]]) -> bool:
124
+ def bits_set(value: int, *args: int) -> bool:
126
125
  """
127
126
  Return True if all the bits are set.
128
127
 
129
128
  Args:
130
129
  value (int): the value to check
131
130
  args: a set of indices of the bits to check, starting from 0 at the LSB.
132
- All the indices can be given as separate arguments, or they can be passed
133
- in as a list.
131
+ All the indices shall be given as separate arguments, i.e. unpack a list
132
+ if needed.
134
133
 
135
134
  Returns:
136
135
  True if all the bits are set (1).
137
136
 
138
137
  Examples:
139
- >>> assert bits_set(0b0101_0000_1011, [0, 1, 3, 8, 10])
140
- >>> assert bits_set(0b0101_0000_1011, [3, 8])
141
- >>> assert not bits_set(0b0101_0000_1011, [1, 2, 3])
138
+ >>> assert bits_set(0b0101_0000_1011, 0, 1, 3, 8, 10)
139
+ >>> assert bits_set(0b0101_0000_1011, 3, 8)
140
+ >>> assert not bits_set(0b0101_0000_1011, *[1, 2, 3])
142
141
  """
143
142
 
144
- if len(args) == 1 and isinstance(args[0], list):
145
- args = args[0]
146
143
  return all([bit_set(value, bit) for bit in args])
147
144
 
148
145
 
@@ -506,7 +503,7 @@ def crc_calc(data: list[bytes | int], start: int, len_: int) -> int:
506
503
  Reference:
507
504
  The description of the CRC calculation for RMAP is given in the ECSS document
508
505
  _Space Engineering: SpaceWire - Remote Memory Access Protocol_, section A.3
509
- on page 80 [ECSSEST5052C].
506
+ on page 80 [ECSS-E-ST-50-52C].
510
507
 
511
508
  """
512
509
  crc: int = 0
@@ -548,7 +545,7 @@ def s16(value: int) -> int:
548
545
  >>> s16(0b1000_0000_0001_0001)
549
546
  -32751
550
547
 
551
- The 'bin()' fuction will return a strange representation of this number:
548
+ The 'bin()' function will return a strange representation of this number:
552
549
 
553
550
  >>> bin(-32751)
554
551
  '-0b111111111101111'
@@ -15,8 +15,8 @@ from typing import Optional
15
15
 
16
16
  import rich
17
17
 
18
- from egse.system import get_caller_info
19
18
  from egse.log import logger
19
+ from egse.system import get_caller_info
20
20
 
21
21
 
22
22
  def static_vars(**kwargs):
@@ -316,7 +316,10 @@ def debug(func):
316
316
 
317
317
 
318
318
  def profile_func(
319
- output_file: str = None, sort_by: str = "cumulative", lines_to_print: int = None, strip_dirs: bool = False
319
+ output_file: str | None = None,
320
+ sort_by: str = "cumulative",
321
+ lines_to_print: int | None = None,
322
+ strip_dirs: bool = False,
320
323
  ) -> Callable:
321
324
  """A time profiler decorator.
322
325
 
@@ -346,7 +349,7 @@ def profile_func(
346
349
  decorator](https://gist.github.com/ekhoda/2de44cf60d29ce24ad29758ce8635b78).
347
350
 
348
351
  Inspired by and modified the profile decorator of Giampaolo Rodola:
349
- [profile decorato](http://code.activestate.com/recipes/577817-profile-decorator/).
352
+ [profile decorator](http://code.activestate.com/recipes/577817-profile-decorator/).
350
353
 
351
354
 
352
355
  """
@@ -339,76 +339,6 @@ class AsyncDeviceTransport:
339
339
  return await self.trans(command)
340
340
 
341
341
 
342
- class AsyncDeviceConnectionInterface(DeviceConnectionObservable):
343
- """Generic connection interface for all Device classes and Controllers.
344
-
345
- This interface shall be implemented in the Controllers that directly connect to the
346
- hardware, but also in the simulators to guarantee an identical interface as the controllers.
347
-
348
- This interface will be implemented in the Proxy classes through the
349
- YAML definitions. Therefore, the YAML files shall define at least
350
- the following commands: `connect`, `disconnect`, `reconnect`, `is_connected`.
351
- """
352
-
353
- def __init__(self):
354
- super().__init__()
355
-
356
- def __enter__(self):
357
- self.connect()
358
- return self
359
-
360
- def __exit__(self, exc_type, exc_val, exc_tb):
361
- self.disconnect()
362
-
363
- async def connect(self) -> None:
364
- """Connect to the device controller.
365
-
366
- Raises:
367
- ConnectionError: when the connection can not be opened.
368
- """
369
-
370
- raise NotImplementedError
371
-
372
- async def disconnect(self) -> None:
373
- """Disconnect from the device controller.
374
-
375
- Raises:
376
- ConnectionError: when the connection can not be closed.
377
- """
378
- raise NotImplementedError
379
-
380
- async def reconnect(self):
381
- """Reconnect the device controller.
382
-
383
- Raises:
384
- ConnectionError: when the device can not be reconnected for some reason.
385
- """
386
- raise NotImplementedError
387
-
388
- async def is_connected(self) -> bool:
389
- """Check if the device is connected.
390
-
391
- Returns:
392
- True if the device is connected and responds to a command, False otherwise.
393
- """
394
- raise NotImplementedError
395
-
396
-
397
- class AsyncDeviceInterface(AsyncDeviceConnectionInterface):
398
- """A generic interface for all device classes."""
399
-
400
- def is_simulator(self) -> bool:
401
- """Checks whether the device is a simulator rather than a real hardware controller.
402
-
403
- This can be useful for testing purposes or when doing actual movement simulations.
404
-
405
- Returns:
406
- True if the Device is a Simulator; False if the Device is connected to real hardware.
407
- """
408
-
409
- raise NotImplementedError
410
-
411
-
412
342
  class DeviceFactoryInterface:
413
343
  """
414
344
  Base class for creating a device factory class to access devices.
@@ -72,8 +72,8 @@ import os
72
72
  import warnings
73
73
  from pathlib import Path
74
74
 
75
- from dotenv import load_dotenv as _load_dotenv
76
75
  from dotenv import find_dotenv
76
+ from dotenv import load_dotenv as _load_dotenv
77
77
  from rich.console import Console
78
78
 
79
79
  from egse.decorators import static_vars
@@ -697,6 +697,7 @@ def env_var(**kwargs: str | int | float | bool | None):
697
697
  def main(args: list | None = None): # pragma: no cover
698
698
  import argparse
699
699
  import sys
700
+
700
701
  import rich
701
702
 
702
703
  parser = argparse.ArgumentParser()
@@ -61,3 +61,6 @@ class Abort(RuntimeError):
61
61
 
62
62
  class InitialisationError(Error):
63
63
  """Raised when an initialisation failed."""
64
+
65
+
66
+ InitializationError = InitialisationError # Alias with American spelling
@@ -18,6 +18,7 @@ __all__ = [
18
18
  "LOG_DATE_FORMAT_FULL",
19
19
  "LOG_DATE_FORMAT_CLEAN",
20
20
  "logger",
21
+ "logging", # export to guarantee that this module is imported and initialized before users use logging
21
22
  "root_logger",
22
23
  "egse_logger",
23
24
  "get_log_level_from_env",
@@ -84,7 +85,7 @@ class NonEGSEFilter(logging.Filter):
84
85
  return not record.name.startswith("egse")
85
86
 
86
87
 
87
- def get_log_level_from_env(env_var: str = "LOG_LEVEL", default: int = logging.INFO):
88
+ def get_log_level_from_env(env_var: str = "LOG_LEVEL", default: str = "INFO") -> int:
88
89
  """Read the log level from an environment variable."""
89
90
  log_level_str = os.getenv(env_var, default)
90
91
 
@@ -95,18 +96,16 @@ def get_log_level_from_env(env_var: str = "LOG_LEVEL", default: int = logging.IN
95
96
  if 10 <= log_level <= 50:
96
97
  return log_level
97
98
  else:
98
- logging.warning(
99
- f"Log level {log_level} outside standard range (10-50). Using {logging.getLevelName(default)}."
100
- )
101
- return default
99
+ logging.warning(f"Log level {log_level} outside standard range (10-50). Using {default.upper()}.")
100
+ return logging._nameToLevel[default.upper()]
102
101
 
103
102
  except ValueError:
104
103
  log_level_str = log_level_str.upper()
105
104
  try:
106
105
  return getattr(logging, log_level_str)
107
106
  except AttributeError:
108
- logging.error(f"Invalid LOG_LEVEL '{log_level_str}'. Using {logging.getLevelName(default)}.")
109
- return default
107
+ logging.error(f"Invalid LOG_LEVEL '{log_level_str}'. Using {default.upper()}.")
108
+ return logging._nameToLevel[default.upper()]
110
109
 
111
110
 
112
111
  egse_logger = logging.getLogger("egse")
@@ -145,7 +144,7 @@ if __name__ == "__main__":
145
144
  Environment variables:
146
145
  - LOG_LEVEL=debug|info|warning|critical
147
146
  - LOG_FORMAT=full|clean
148
-
147
+
149
148
  Example logging statements
150
149
  - logging level set to INFO
151
150
  - logging format set to full