orionis 0.400.0__py3-none-any.whl → 0.402.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.
Files changed (38) hide show
  1. orionis/console/output/console.py +26 -26
  2. orionis/container/providers/service_provider.py +4 -4
  3. orionis/foundation/application.py +4 -2
  4. orionis/foundation/config/base.py +112 -0
  5. orionis/foundation/config/logging/entities/channels.py +87 -69
  6. orionis/foundation/config/logging/entities/chunked.py +37 -92
  7. orionis/foundation/config/logging/entities/daily.py +43 -81
  8. orionis/foundation/config/logging/entities/hourly.py +16 -71
  9. orionis/foundation/config/logging/entities/logging.py +37 -38
  10. orionis/foundation/config/logging/entities/monthly.py +19 -75
  11. orionis/foundation/config/logging/entities/stack.py +13 -69
  12. orionis/foundation/config/logging/entities/weekly.py +19 -75
  13. orionis/foundation/config/logging/enums/levels.py +6 -7
  14. orionis/foundation/config/logging/validators/__init__.py +7 -0
  15. orionis/foundation/config/logging/validators/level.py +54 -0
  16. orionis/foundation/config/logging/validators/path.py +34 -0
  17. orionis/foundation/providers/console_provider.py +0 -4
  18. orionis/foundation/providers/dumper_provider.py +0 -4
  19. orionis/foundation/providers/logger_provider.py +17 -0
  20. orionis/foundation/providers/path_resolver_provider.py +0 -4
  21. orionis/foundation/providers/progress_bar_provider.py +0 -4
  22. orionis/foundation/providers/workers_provider.py +0 -4
  23. orionis/metadata/framework.py +1 -1
  24. orionis/services/log/contracts/__init__.py +0 -0
  25. orionis/services/log/contracts/log_service.py +23 -0
  26. orionis/services/log/exceptions/__init__.py +5 -0
  27. orionis/services/log/exceptions/runtime.py +19 -0
  28. orionis/services/log/handlers/__init__.py +0 -0
  29. orionis/services/log/handlers/size_rotating.py +52 -0
  30. orionis/services/log/handlers/timed_rotating.py +53 -0
  31. orionis/services/log/log_service.py +188 -143
  32. orionis/support/facades/logger.py +15 -0
  33. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/METADATA +1 -1
  34. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/RECORD +38 -25
  35. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/WHEEL +0 -0
  36. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/licenses/LICENCE +0 -0
  37. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/top_level.txt +0 -0
  38. {orionis-0.400.0.dist-info → orionis-0.402.0.dist-info}/zip-safe +0 -0
@@ -1,10 +1,11 @@
1
- from dataclasses import asdict, dataclass, field, fields
1
+ from dataclasses import dataclass, field
2
+ from orionis.foundation.config.base import BaseConfigEntity
3
+ from orionis.foundation.config.logging.validators import IsValidPath, IsValidLevel
2
4
  from orionis.foundation.exceptions import OrionisIntegrityException
3
5
  from orionis.foundation.config.logging.enums import Level
4
- import re
5
6
 
6
7
  @dataclass(unsafe_hash=True, kw_only=True)
7
- class Chunked:
8
+ class Chunked(BaseConfigEntity):
8
9
  """
9
10
  Configuration for chunked log file rotation.
10
11
 
@@ -25,23 +26,23 @@ class Chunked:
25
26
  """
26
27
 
27
28
  path: str = field(
28
- default='storage/log/application.log',
29
- metadata={
30
- "description": "Filesystem path where chunked log files are stored.",
29
+ default = 'storage/log/application.log',
30
+ metadata = {
31
+ "description": "The file path where the log is stored.",
31
32
  "default": "storage/log/application.log",
32
33
  },
33
34
  )
34
35
 
35
36
  level: int | str | Level = field(
36
- default=Level.INFO,
37
- metadata={
38
- "description": "Logging level for the log file. Accepts int, str, or Level enum.",
37
+ default = Level.INFO,
38
+ metadata = {
39
+ "description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
39
40
  "default": Level.INFO,
40
41
  },
41
42
  )
42
43
 
43
44
  mb_size: int = field(
44
- default=10,
45
+ default = 10,
45
46
  metadata={
46
47
  "description": "Maximum size (in MB) of a log file before chunking.",
47
48
  "default": 10,
@@ -49,7 +50,7 @@ class Chunked:
49
50
  )
50
51
 
51
52
  files: int = field(
52
- default=5,
53
+ default = 5,
53
54
  metadata={
54
55
  "description": "Maximum number of log files to retain.",
55
56
  "default": 5,
@@ -57,94 +58,38 @@ class Chunked:
57
58
  )
58
59
 
59
60
  def __post_init__(self):
60
- # Validate 'path'
61
- if not isinstance(self.path, str) or not self.path.strip():
62
- raise OrionisIntegrityException(
63
- f"Chunked log configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
64
- )
61
+ """
62
+ Performs validation and normalization of configuration fields.
65
63
 
66
- # Validate 'level'
67
- valid_level_types = (int, str, Level)
68
- if not isinstance(self.level, valid_level_types):
69
- raise OrionisIntegrityException(
70
- f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
71
- )
64
+ - path: Validates that the path is correct using the IsValidPath validator.
65
+ - level: Validates that the log level is correct using the IsValidLevel validator.
66
+ - mb_size: Checks that it is an integer between 1 and 1000 (MB).
67
+ - files: Checks that it is a positive integer greater than 0.
68
+
69
+ Raises
70
+ ------
71
+ OrionisIntegrityException
72
+ If any of the configuration values are invalid.
73
+ """
72
74
 
73
- if isinstance(self.level, str):
74
- _value = self.level.strip().upper()
75
- if not _value:
76
- raise OrionisIntegrityException(
77
- "File cache configuration error: 'level' string cannot be empty."
78
- )
79
- if _value not in Level.__members__:
80
- raise OrionisIntegrityException(
81
- f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
82
- )
83
- self.level = Level[_value].value
84
- elif isinstance(self.level, int):
85
- valid_values = [level.value for level in Level]
86
- if self.level not in valid_values:
87
- raise OrionisIntegrityException(
88
- f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
89
- )
90
- elif isinstance(self.level, Level):
91
- self.level = self.level.value
75
+ # Validate 'path' using the IsValidPath validator
76
+ IsValidPath(self.path)
77
+
78
+ # Validate 'level' using the IsValidLevel validator
79
+ IsValidLevel(self.level)
92
80
 
93
81
  # Validate 'mb_size'
94
- if isinstance(self.mb_size, str):
95
- match = re.match(r'^(\d+)\s*(MB|KB|B)?$', self.mb_size.strip(), re.IGNORECASE)
96
- if not match:
97
- raise OrionisIntegrityException(
98
- f"Chunked log configuration error: 'mb_size' string must be like '10MB', '500KB', or integer, got '{self.mb_size}'."
99
- )
100
- size, unit = match.groups()
101
- size = int(size)
102
- if unit is None or unit.upper() == 'MB':
103
- self.mb_size = size
104
- elif unit.upper() == 'KB':
105
- self.mb_size = max(1, size // 1024)
106
- elif unit.upper() == 'B':
107
- self.mb_size = max(1, size // (1024 * 1024))
108
- if not isinstance(self.mb_size, int) or self.mb_size < 1:
82
+ if not isinstance(self.mb_size, int):
109
83
  raise OrionisIntegrityException(
110
- f"Chunked log configuration error: 'mb_size' must be a positive integer (MB), got {self.mb_size}."
84
+ f"'mb_size' must be an integer in MB, got {type(self.mb_size).__name__}."
85
+ )
86
+ if self.mb_size < 1 or self.mb_size > 1000:
87
+ raise OrionisIntegrityException(
88
+ f"'mb_size' must be between 1 and 1000 MB, got {self.mb_size}."
111
89
  )
112
90
 
113
91
  # Validate 'files'
114
92
  if not isinstance(self.files, int) or self.files < 1:
115
93
  raise OrionisIntegrityException(
116
- f"Chunked log configuration error: 'files' must be a positive integer, got {self.files}."
117
- )
118
-
119
- def toDict(self) -> dict:
120
- """
121
- Returns a dictionary representation of the Chunked configuration.
122
-
123
- Returns
124
- -------
125
- dict
126
- Dictionary containing all configuration fields and their values.
127
- """
128
- return asdict(self)
129
-
130
- def getFields(self):
131
- """
132
- Retrieves a list of field information for the current dataclass instance.
133
-
134
- Returns:
135
- list: A list of dictionaries, each containing details about a field:
136
- - name (str): The name of the field.
137
- - type (type): The type of the field.
138
- - default: The default value of the field, if specified; otherwise, the value from metadata or None.
139
- - metadata (mapping): The metadata associated with the field.
140
- """
141
- __fields = []
142
- for field in fields(self):
143
- __metadata = dict(field.metadata) or {}
144
- __fields.append({
145
- "name": field.name,
146
- "type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
147
- "default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
148
- "metadata": __metadata
149
- })
150
- return __fields
94
+ f"'files' must be a positive integer greater than 0, got {self.files} ({type(self.files).__name__})."
95
+ )
@@ -1,10 +1,12 @@
1
- from dataclasses import asdict, dataclass, field, fields
2
- from datetime import time
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime, time
3
+ from orionis.foundation.config.base import BaseConfigEntity
4
+ from orionis.foundation.config.logging.validators import IsValidPath, IsValidLevel
3
5
  from orionis.foundation.exceptions import OrionisIntegrityException
4
6
  from orionis.foundation.config.logging.enums import Level
5
7
 
6
8
  @dataclass(unsafe_hash=True, kw_only=True)
7
- class Daily:
9
+ class Daily(BaseConfigEntity):
8
10
  """
9
11
  Represents the configuration for daily log file rotation.
10
12
 
@@ -16,34 +18,34 @@ class Daily:
16
18
  """
17
19
 
18
20
  path: str = field(
19
- default='storage/log/application.log',
20
- metadata={
21
+ default = 'storage/log/application.log',
22
+ metadata = {
21
23
  "description": "The file path where the log is stored.",
22
24
  "default": "storage/log/application.log",
23
25
  },
24
26
  )
25
27
 
26
28
  level: int | str | Level = field(
27
- default=Level.INFO,
28
- metadata={
29
- "description": "The logging level (e.g., 'info', 'error', 'debug').",
29
+ default = Level.INFO,
30
+ metadata = {
31
+ "description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
30
32
  "default": Level.INFO,
31
33
  },
32
34
  )
33
35
 
34
36
  retention_days: int = field(
35
- default=7,
36
- metadata={
37
+ default = 7,
38
+ metadata = {
37
39
  "description": "The number of days to retain log files before deletion.",
38
40
  "default": 7,
39
41
  },
40
42
  )
41
43
 
42
- at: time = field(
43
- default=time(0, 0),
44
+ at: time | str = field(
45
+ default = time(0, 0).strftime("%H:%M"),
44
46
  metadata={
45
47
  "description": "The time of day when the log rotation should occur.",
46
- "default": time(0, 0),
48
+ "default": time(0, 0).strftime("%H:%M"),
47
49
  },
48
50
  )
49
51
 
@@ -54,82 +56,42 @@ class Daily:
54
56
  Raises:
55
57
  OrionisIntegrityException: If any attribute is invalid.
56
58
  """
57
- # Validate 'path'
58
- if not isinstance(self.path, str) or not self.path.strip():
59
- raise OrionisIntegrityException(
60
- f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
61
- )
62
59
 
63
- # Validate 'level'
64
- valid_level_types = (int, str, Level)
65
- if not isinstance(self.level, valid_level_types):
66
- raise OrionisIntegrityException(
67
- f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
68
- )
60
+ # Validate 'path' using the IsValidPath validator
61
+ IsValidPath(self.path)
69
62
 
70
- # Normalize 'level' to int
71
- if isinstance(self.level, str):
72
- _value = self.level.strip().upper()
73
- if not _value:
74
- raise OrionisIntegrityException(
75
- "File cache configuration error: 'level' string cannot be empty."
76
- )
77
- if _value not in Level.__members__:
78
- raise OrionisIntegrityException(
79
- f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
80
- )
81
- self.level = Level[_value].value
82
- elif isinstance(self.level, int):
83
- valid_values = [level.value for level in Level]
84
- if self.level not in valid_values:
85
- raise OrionisIntegrityException(
86
- f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
87
- )
88
- elif isinstance(self.level, Level):
89
- self.level = self.level.value
63
+ # Validate 'level' using the IsValidLevel validator
64
+ IsValidLevel(self.level)
90
65
 
91
66
  # Validate 'retention_days'
92
- if not isinstance(self.retention_days, int) or self.retention_days < 1 or self.retention_days > 90:
67
+ if not isinstance(self.retention_days, int):
93
68
  raise OrionisIntegrityException(
94
- f"File cache configuration error: 'retention_days' must be a positive integer between 1 and 90, got {repr(self.retention_days)}."
69
+ f"'retention_days' must be an integer, got {type(self.retention_days).__name__}."
95
70
  )
96
-
97
- # Validate 'at'
98
- if not isinstance(self.at, time):
71
+ if not (1 <= self.retention_days <= 90):
99
72
  raise OrionisIntegrityException(
100
- f"File cache configuration error: 'at' must be a datetime.time instance, got {type(self.at).__name__}."
73
+ f"'retention_days' must be between 1 and 90, got {self.retention_days}."
101
74
  )
102
75
 
103
- # Convert 'at' to "HH:MM:SS" string format
104
- self.at = self.at.strftime("%H:%M:%S")
105
-
106
- def toDict(self) -> dict:
107
- """
108
- Converts the Daily object to a dictionary representation.
109
-
110
- Returns:
111
- dict: A dictionary containing the dataclass fields and their values.
112
- """
113
- return asdict(self)
76
+ # Validate 'at' must be a time instance or a valid "HH:MM" string
77
+ if not isinstance(self.at, (time, str)):
78
+ raise OrionisIntegrityException(
79
+ f"'at' must be a datetime.time instance or a 'HH:MM' string, got {type(self.at).__name__}."
80
+ )
114
81
 
115
- def getFields(self):
116
- """
117
- Retrieves a list of field information for the current dataclass instance.
82
+ # Validate and normalize 'at'
83
+ if isinstance(self.at, str):
84
+ try:
85
+ parsed_time = datetime.strptime(self.at, "%H:%M").time()
86
+ self.at = parsed_time
87
+ except ValueError:
88
+ raise OrionisIntegrityException(
89
+ f"'at' must be a valid time string in 'HH:MM' format, got '{self.at}'."
90
+ )
91
+ elif not isinstance(self.at, time):
92
+ raise OrionisIntegrityException(
93
+ f"'at' must be a datetime.time instance or a 'HH:MM' string, got {type(self.at).__name__}."
94
+ )
118
95
 
119
- Returns:
120
- list: A list of dictionaries, each containing details about a field:
121
- - name (str): The name of the field.
122
- - type (type): The type of the field.
123
- - default: The default value of the field, if specified; otherwise, the value from metadata or None.
124
- - metadata (mapping): The metadata associated with the field.
125
- """
126
- __fields = []
127
- for field in fields(self):
128
- __metadata = dict(field.metadata) or {}
129
- __fields.append({
130
- "name": field.name,
131
- "type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
132
- "default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
133
- "metadata": __metadata
134
- })
135
- return __fields
96
+ # Normalize 'at' to "HH:MM" format
97
+ self.at = self.at.strftime("%H:%M")
@@ -1,9 +1,11 @@
1
- from dataclasses import asdict, dataclass, field, fields
1
+ from dataclasses import dataclass, field
2
+ from orionis.foundation.config.base import BaseConfigEntity
3
+ from orionis.foundation.config.logging.validators import IsValidLevel, IsValidPath
2
4
  from orionis.foundation.exceptions import OrionisIntegrityException
3
5
  from orionis.foundation.config.logging.enums import Level
4
6
 
5
7
  @dataclass(unsafe_hash=True, kw_only=True)
6
- class Hourly:
8
+ class Hourly(BaseConfigEntity):
7
9
  """
8
10
  Represents the configuration for hourly log file management.
9
11
 
@@ -14,24 +16,24 @@ class Hourly:
14
16
  """
15
17
 
16
18
  path: str = field(
17
- default='storage/log/application.log',
18
- metadata={
19
+ default = 'storage/log/application.log',
20
+ metadata = {
19
21
  "description": "The file path where the log is stored.",
20
22
  "default": "storage/log/application.log",
21
23
  },
22
24
  )
23
25
 
24
26
  level: int | str | Level = field(
25
- default=Level.INFO,
26
- metadata={
27
- "description": "The logging level (e.g., 'info', 'error', 'debug').",
27
+ default = Level.INFO,
28
+ metadata = {
29
+ "description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
28
30
  "default": Level.INFO,
29
31
  },
30
32
  )
31
33
 
32
34
  retention_hours: int = field(
33
- default=24,
34
- metadata={
35
+ default = 24,
36
+ metadata = {
35
37
  "description": "The number of hours to retain log files before deletion.",
36
38
  "default": 24,
37
39
  },
@@ -47,38 +49,12 @@ class Hourly:
47
49
  - 'level' must be an int, str, or Level enum, and a valid value.
48
50
  - 'retention_hours' must be an integer between 1 and 168.
49
51
  """
50
- # Validate 'path'
51
- if not isinstance(self.path, str) or not self.path.strip():
52
- raise OrionisIntegrityException(
53
- f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
54
- )
55
52
 
56
- # Validate 'level'
57
- valid_level_types = (int, str, Level)
58
- if not isinstance(self.level, valid_level_types):
59
- raise OrionisIntegrityException(
60
- f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
61
- )
53
+ # Validate 'path' using the IsValidPath validator
54
+ IsValidPath(self.path)
62
55
 
63
- if isinstance(self.level, str):
64
- _value = self.level.strip().upper()
65
- if not _value:
66
- raise OrionisIntegrityException(
67
- "File cache configuration error: 'level' string cannot be empty."
68
- )
69
- if _value not in Level.__members__:
70
- raise OrionisIntegrityException(
71
- f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
72
- )
73
- self.level = Level[_value].value
74
- elif isinstance(self.level, int):
75
- valid_values = [level.value for level in Level]
76
- if self.level not in valid_values:
77
- raise OrionisIntegrityException(
78
- f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
79
- )
80
- elif isinstance(self.level, Level):
81
- self.level = self.level.value
56
+ # Validate 'level' using the IsValidLevel validator
57
+ IsValidLevel(self.level)
82
58
 
83
59
  # Validate 'retention_hours'
84
60
  if not isinstance(self.retention_hours, int) or self.retention_hours < 0:
@@ -88,35 +64,4 @@ class Hourly:
88
64
  if self.retention_hours < 1 or self.retention_hours > 168:
89
65
  raise OrionisIntegrityException(
90
66
  f"File cache configuration error: 'retention_hours' must be between 1 and 168, got {self.retention_hours}."
91
- )
92
-
93
- def toDict(self) -> dict:
94
- """
95
- Converts the Hourly object to a dictionary representation.
96
-
97
- Returns:
98
- dict: A dictionary containing all the dataclass fields and their values.
99
- """
100
- return asdict(self)
101
-
102
- def getFields(self):
103
- """
104
- Retrieves a list of field information for the current dataclass instance.
105
-
106
- Returns:
107
- list: A list of dictionaries, each containing details about a field:
108
- - name (str): The name of the field.
109
- - type (type): The type of the field.
110
- - default: The default value of the field, if specified; otherwise, the value from metadata or None.
111
- - metadata (mapping): The metadata associated with the field.
112
- """
113
- __fields = []
114
- for field in fields(self):
115
- __metadata = dict(field.metadata) or {}
116
- __fields.append({
117
- "name": field.name,
118
- "type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
119
- "default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
120
- "metadata": __metadata
121
- })
122
- return __fields
67
+ )
@@ -1,9 +1,10 @@
1
- from dataclasses import dataclass, field, asdict, fields
1
+ from dataclasses import dataclass, field, fields
2
+ from orionis.foundation.config.base import BaseConfigEntity
2
3
  from orionis.foundation.config.logging.entities.channels import Channels
3
4
  from orionis.foundation.exceptions import OrionisIntegrityException
4
5
 
5
6
  @dataclass(unsafe_hash=True, kw_only=True)
6
- class Logging:
7
+ class Logging(BaseConfigEntity):
7
8
  """
8
9
  Represents the logging system configuration.
9
10
 
@@ -18,21 +19,39 @@ class Logging:
18
19
  default="stack",
19
20
  metadata={
20
21
  "description": "The default logging channel to use.",
21
- "default": "stack",
22
+ "default": "stack"
22
23
  }
23
24
  )
24
- channels: Channels = field(
25
- default_factory=Channels,
25
+
26
+ channels: Channels | dict = field(
27
+ default_factory = lambda: Channels(),
26
28
  metadata={
27
29
  "description": "A collection of available logging channels.",
28
- "default": "Channels()",
30
+ "default": lambda: Channels().toDict()
29
31
  }
30
32
  )
31
33
 
32
34
  def __post_init__(self):
33
35
  """
34
- Validates the types of the attributes after initialization.
36
+ Validates the logging configuration after dataclass initialization by ensuring
37
+ the default channel and channels configuration are properly formatted and valid.
38
+ Parameters
39
+ ----------
40
+ None
41
+ Raises
42
+ ------
43
+ OrionisIntegrityException
44
+ If the default channel is not a string or doesn't match available channel options.
45
+ OrionisIntegrityException
46
+ If the channels configuration is malformed or cannot be converted to a Channels instance.
47
+ OrionisIntegrityException
48
+ If the channels property is not a Channels instance or a dictionary.
49
+ Notes
50
+ -----
51
+ This method performs the following validations:
52
+ - Ensures 'default' is a string matching available channel options from Channels fields
35
53
  """
54
+
36
55
  options = [field.name for field in fields(Channels)]
37
56
  if not isinstance(self.default, str) or self.default not in options:
38
57
  raise OrionisIntegrityException(
@@ -40,34 +59,14 @@ class Logging:
40
59
  )
41
60
 
42
61
  if not isinstance(self.channels, Channels):
43
- raise OrionisIntegrityException(
44
- "The 'channels' property must be an instance of Channels."
45
- )
46
-
47
- def toDict(self) -> dict:
48
- """
49
- Converts the current instance into a dictionary representation.
50
- """
51
- return asdict(self)
52
-
53
- def getFields(self):
54
- """
55
- Retrieves a list of field information for the current dataclass instance.
56
-
57
- Returns:
58
- list: A list of dictionaries, each containing details about a field:
59
- - name (str): The name of the field.
60
- - type (type): The type of the field.
61
- - default: The default value of the field, if specified; otherwise, the value from metadata or None.
62
- - metadata (mapping): The metadata associated with the field.
63
- """
64
- __fields = []
65
- for field in fields(self):
66
- __metadata = dict(field.metadata) or {}
67
- __fields.append({
68
- "name": field.name,
69
- "type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
70
- "default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
71
- "metadata": __metadata
72
- })
73
- return __fields
62
+ if isinstance(self.channels, dict):
63
+ try:
64
+ self.channels = Channels(**self.channels)
65
+ except TypeError as e:
66
+ raise OrionisIntegrityException(
67
+ f"Invalid channels configuration: {e}"
68
+ )
69
+ else:
70
+ raise OrionisIntegrityException(
71
+ "The 'channels' property must be an instance of Channels or a dictionary."
72
+ )