dsw-config 4.13.0__tar.gz → 4.14.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsw-config
3
- Version: 4.13.0
3
+ Version: 4.14.0
4
4
  Summary: Library for DSW config manipulation
5
5
  Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
6
6
  License: Apache License 2.0
@@ -11,11 +11,11 @@ Keywords: dsw,config,yaml,parser
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Topic :: Text Processing
17
17
  Classifier: Topic :: Utilities
18
- Requires-Python: <4,>=3.10
18
+ Requires-Python: <4,>=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: PyYAML
@@ -9,9 +9,9 @@ BuildInfo = namedtuple(
9
9
  )
10
10
 
11
11
  BUILD_INFO = BuildInfo(
12
- version='v4.13.0~c8e1cb3',
13
- built_at='2024-12-05 08:07:29Z',
14
- sha='c8e1cb3b7e0cc93bdf509eef715cd0a926d907c3',
12
+ version='v4.14.0~213910f',
13
+ built_at='2025-01-07 08:17:37Z',
14
+ sha='213910ffb32a7cea98942ccd0f6c52cb6cf79128',
15
15
  branch='HEAD',
16
- tag='v4.13.0',
16
+ tag='v4.14.0',
17
17
  )
@@ -1,60 +1,61 @@
1
+ # pylint: disable=too-few-public-methods
1
2
  import collections
3
+ import typing
2
4
 
3
- from typing import Any, Optional, Generic, TypeVar, Callable
4
5
 
6
+ T = typing.TypeVar('T')
5
7
 
6
- T = TypeVar('T')
7
8
 
8
-
9
- def cast_bool(value: Any) -> bool:
9
+ def cast_bool(value: typing.Any) -> bool:
10
10
  return bool(value)
11
11
 
12
12
 
13
- def cast_optional_bool(value: Any) -> Optional[bool]:
13
+ def cast_optional_bool(value: typing.Any) -> bool | None:
14
14
  if value is None:
15
15
  return None
16
16
  return bool(value)
17
17
 
18
18
 
19
- def cast_int(value: Any) -> int:
19
+ def cast_int(value: typing.Any) -> int:
20
20
  return int(value)
21
21
 
22
22
 
23
- def cast_optional_int(value: Any) -> Optional[int]:
23
+ def cast_optional_int(value: typing.Any) -> int | None:
24
24
  if value is None:
25
25
  return None
26
26
  return int(value)
27
27
 
28
28
 
29
- def cast_float(value: Any) -> float:
29
+ def cast_float(value: typing.Any) -> float:
30
30
  return float(value)
31
31
 
32
32
 
33
- def cast_optional_float(value: Any) -> Optional[float]:
33
+ def cast_optional_float(value: typing.Any) -> float | None:
34
34
  if value is None:
35
35
  return None
36
36
  return float(value)
37
37
 
38
38
 
39
- def cast_str(value: Any) -> str:
39
+ def cast_str(value: typing.Any) -> str:
40
40
  return str(value)
41
41
 
42
42
 
43
- def cast_optional_str(value: Any) -> Optional[str]:
43
+ def cast_optional_str(value: typing.Any) -> str | None:
44
44
  if value is None:
45
45
  return None
46
46
  return str(value)
47
47
 
48
48
 
49
- def cast_optional_dict(value: Any) -> Optional[dict]:
49
+ def cast_optional_dict(value: typing.Any) -> dict | None:
50
50
  if not isinstance(value, dict):
51
51
  return None
52
52
  return value
53
53
 
54
54
 
55
- class ConfigKey(Generic[T]):
55
+ class ConfigKey(typing.Generic[T]):
56
56
 
57
- def __init__(self, yaml_path: list[str], cast: Callable[[Any], T],
57
+ def __init__(self, *, yaml_path: list[str],
58
+ cast: typing.Callable[[typing.Any], T],
58
59
  var_names=None, default=None, required=False):
59
60
  self.yaml_path = yaml_path
60
61
  self.var_names = var_names or [] # type: list[str]
@@ -63,12 +64,13 @@ class ConfigKey(Generic[T]):
63
64
  self.cast = cast
64
65
 
65
66
  def __str__(self):
66
- return 'ConfigKey: ' + '.'.join(self.yaml_path)
67
+ return f'ConfigKey: {".".join(self.yaml_path)}'
67
68
 
68
69
 
69
70
  class ConfigKeysMeta(type):
70
71
 
71
72
  @classmethod
73
+ # pylint: disable-next=unused-argument
72
74
  def __prepare__(mcs, name, bases, **kwargs):
73
75
  return collections.OrderedDict()
74
76
 
@@ -176,6 +178,12 @@ class _SentryKeys(ConfigKeysContainer):
176
178
  default='',
177
179
  cast=cast_str,
178
180
  )
181
+ environment = ConfigKey(
182
+ yaml_path=['sentry', 'environment'],
183
+ var_names=['SENTRY_ENVIRONMENT'],
184
+ default='production',
185
+ cast=cast_str,
186
+ )
179
187
 
180
188
 
181
189
  class _DatabaseKeys(ConfigKeysContainer):
@@ -28,6 +28,8 @@ class DSWLogger(logging.Logger):
28
28
 
29
29
 
30
30
  def prepare_logging(logging_cfg):
31
+ # pylint: disable-next=no-member
32
+ logger_dict = logging.root.manager.loggerDict
31
33
  if logging_cfg.dict_config is not None:
32
34
  logging.config.dictConfig(logging_cfg.dict_config)
33
35
  else:
@@ -36,15 +38,13 @@ def prepare_logging(logging_cfg):
36
38
  level=logging_cfg.global_level,
37
39
  format=logging_cfg.message_format
38
40
  )
39
- dsw_loggers = (logging.getLogger(name)
40
- for name in logging.root.manager.loggerDict.keys()
41
+ dsw_loggers = (logging.getLogger(name) for name in logger_dict
41
42
  if name.lower().startswith('dsw'))
42
43
  for logger in dsw_loggers:
43
44
  logger.setLevel(logging_cfg.level)
44
45
  # Set for all existing loggers
45
46
  logging.getLogger().addFilter(filter=LOG_FILTER)
46
- loggers = (logging.getLogger(name)
47
- for name in logging.root.manager.loggerDict.keys())
47
+ loggers = (logging.getLogger(name) for name in logger_dict)
48
48
  for logger in loggers:
49
49
  logger.addFilter(filter=LOG_FILTER)
50
50
  # Set for any future loggers
@@ -0,0 +1,82 @@
1
+ import dataclasses
2
+
3
+ from .logging import prepare_logging, LOG_FILTER
4
+
5
+
6
+ def _config_to_string(config: object):
7
+ lines = [f'{type(config).__name__}']
8
+ fields = (f for f in config.__dict__.keys() if not f.startswith('_'))
9
+ for field in fields:
10
+ v = str(getattr(config, field))
11
+ t = type(getattr(config, field)).__name__
12
+ lines.append(f'- {field} = {v} [{t}]')
13
+ return '\n'.join(lines)
14
+
15
+
16
+ class ConfigModel:
17
+
18
+ def __str__(self):
19
+ return _config_to_string(self)
20
+
21
+
22
+ @dataclasses.dataclass
23
+ class GeneralConfig(ConfigModel):
24
+ environment: str
25
+ client_url: str
26
+ secret: str
27
+
28
+
29
+ @dataclasses.dataclass
30
+ class SentryConfig(ConfigModel):
31
+ enabled: bool
32
+ workers_dsn: str | None
33
+ traces_sample_rate: float | None
34
+ max_breadcrumbs: int | None
35
+ environment: str
36
+
37
+
38
+ @dataclasses.dataclass
39
+ class DatabaseConfig(ConfigModel):
40
+ connection_string: str
41
+ connection_timeout: int
42
+ queue_timeout: int
43
+
44
+
45
+ @dataclasses.dataclass
46
+ class S3Config(ConfigModel):
47
+ url: str
48
+ username: str
49
+ password: str
50
+ bucket: str
51
+ region: str
52
+
53
+
54
+ @dataclasses.dataclass
55
+ class LoggingConfig(ConfigModel):
56
+ level: str
57
+ global_level: str
58
+ message_format: str
59
+ dict_config: dict | None = None
60
+
61
+ def apply(self):
62
+ prepare_logging(self)
63
+
64
+ @staticmethod
65
+ def set_logging_extra(key: str, value: str):
66
+ LOG_FILTER.set_extra(key, value)
67
+
68
+
69
+ @dataclasses.dataclass
70
+ class AWSConfig(ConfigModel):
71
+ access_key_id: str | None
72
+ secret_access_key: str | None
73
+ region: str | None
74
+
75
+ @property
76
+ def has_credentials(self) -> bool:
77
+ return self.access_key_id is not None and self.secret_access_key is not None
78
+
79
+
80
+ @dataclasses.dataclass
81
+ class CloudConfig(ConfigModel):
82
+ multi_tenant: bool
@@ -1,7 +1,7 @@
1
1
  import os
2
- import yaml
2
+ import typing
3
3
 
4
- from typing import List, Any, IO
4
+ import yaml
5
5
 
6
6
  from .keys import ConfigKey, ConfigKeys
7
7
  from .model import GeneralConfig, SentryConfig, S3Config, \
@@ -10,14 +10,14 @@ from .model import GeneralConfig, SentryConfig, S3Config, \
10
10
 
11
11
  class MissingConfigurationError(Exception):
12
12
 
13
- def __init__(self, missing: List[str]):
13
+ def __init__(self, missing: list[str]):
14
14
  self.missing = missing
15
15
 
16
16
 
17
17
  class DSWConfigParser:
18
18
 
19
19
  def __init__(self, keys=ConfigKeys):
20
- self.cfg = dict()
20
+ self.cfg = {}
21
21
  self.keys = keys
22
22
 
23
23
  @staticmethod
@@ -28,7 +28,7 @@ class DSWConfigParser:
28
28
  except Exception:
29
29
  return False
30
30
 
31
- def read_file(self, fp: IO):
31
+ def read_file(self, fp: typing.IO):
32
32
  self.cfg = yaml.load(fp, Loader=yaml.FullLoader) or self.cfg
33
33
 
34
34
  def read_string(self, content: str):
@@ -50,12 +50,12 @@ class DSWConfigParser:
50
50
  if self.has_value_for_path(key.yaml_path):
51
51
  return True
52
52
  for var_name in key.var_names:
53
- if var_name in os.environ.keys() or \
54
- self._prefix_var(var_name) in os.environ.keys():
53
+ if var_name in os.environ or self._prefix_var(var_name) in os.environ:
55
54
  return True
55
+ return False
56
56
 
57
57
  def get_or_default(self, key: ConfigKey):
58
- x = self.cfg # type: Any
58
+ x: typing.Any = self.cfg
59
59
  for p in key.yaml_path:
60
60
  if not hasattr(x, 'keys') or p not in x.keys():
61
61
  return key.default
@@ -64,9 +64,9 @@ class DSWConfigParser:
64
64
 
65
65
  def get(self, key: ConfigKey):
66
66
  for var_name in key.var_names:
67
- if var_name in os.environ.keys():
67
+ if var_name in os.environ:
68
68
  return key.cast(os.environ[var_name])
69
- if self._prefix_var(var_name) in os.environ.keys():
69
+ if self._prefix_var(var_name) in os.environ:
70
70
  return key.cast(os.environ[self._prefix_var(var_name)])
71
71
  return key.cast(self.get_or_default(key))
72
72
 
@@ -118,6 +118,7 @@ class DSWConfigParser:
118
118
  workers_dsn=self.get(self.keys.sentry.worker_dsn),
119
119
  traces_sample_rate=self.get(self.keys.sentry.traces_sample_rate),
120
120
  max_breadcrumbs=self.get(self.keys.sentry.max_breadcrumbs),
121
+ environment=self.get(self.keys.sentry.environment),
121
122
  )
122
123
 
123
124
  @property
@@ -0,0 +1,60 @@
1
+ import logging
2
+ import typing
3
+
4
+ import sentry_sdk
5
+ from sentry_sdk.types import Event, Hint
6
+ from sentry_sdk.integrations.logging import LoggingIntegration
7
+
8
+ from .model import SentryConfig
9
+
10
+
11
+ EventProcessor = typing.Callable[[Event, Hint], typing.Optional[Event]]
12
+
13
+
14
+ class SentryReporter:
15
+ report = False
16
+ filters = [] # type: list[EventProcessor]
17
+
18
+ @classmethod
19
+ def initialize(cls, *, config: SentryConfig, prog_name: str, release: str,
20
+ breadcrumb_level: int | None = logging.INFO,
21
+ event_level: int | None = logging.ERROR):
22
+ cls.report = config.enabled and config.workers_dsn is not None
23
+ if cls.report:
24
+ def before_send(event, hint):
25
+ for f in cls.filters:
26
+ if not f(event, hint):
27
+ return None
28
+ return event
29
+
30
+ sentry_sdk.init(
31
+ dsn=config.workers_dsn,
32
+ traces_sample_rate=config.traces_sample_rate or 1.0,
33
+ max_breadcrumbs=config.max_breadcrumbs or sentry_sdk.consts.DEFAULT_MAX_BREADCRUMBS,
34
+ release=release,
35
+ environment=config.environment,
36
+ before_send=before_send,
37
+ default_integrations=False,
38
+ integrations=[
39
+ LoggingIntegration(
40
+ level=breadcrumb_level,
41
+ event_level=event_level,
42
+ ),
43
+ ],
44
+ )
45
+ sentry_sdk.set_tag('component', prog_name)
46
+
47
+ @classmethod
48
+ def capture_exception(cls, *args, **kwargs):
49
+ if cls.report:
50
+ sentry_sdk.capture_exception(*args, **kwargs)
51
+
52
+ @classmethod
53
+ def capture_message(cls, *args, **kwargs):
54
+ if cls.report:
55
+ sentry_sdk.capture_message(*args, **kwargs)
56
+
57
+ @classmethod
58
+ def set_tags(cls, **tags):
59
+ if cls.report:
60
+ sentry_sdk.set_tags(tags)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dsw-config
3
- Version: 4.13.0
3
+ Version: 4.14.0
4
4
  Summary: Library for DSW config manipulation
5
5
  Author-email: Marek Suchánek <marek.suchanek@ds-wizard.org>
6
6
  License: Apache License 2.0
@@ -11,11 +11,11 @@ Keywords: dsw,config,yaml,parser
11
11
  Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Topic :: Text Processing
17
17
  Classifier: Topic :: Utilities
18
- Requires-Python: <4,>=3.10
18
+ Requires-Python: <4,>=3.11
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
21
  Requires-Dist: PyYAML
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'dsw-config'
7
- version = "4.13.0"
7
+ version = "4.14.0"
8
8
  description = 'Library for DSW config manipulation'
9
9
  readme = 'README.md'
10
10
  keywords = ['dsw', 'config', 'yaml', 'parser']
@@ -16,12 +16,12 @@ classifiers = [
16
16
  'Development Status :: 5 - Production/Stable',
17
17
  'License :: OSI Approved :: Apache Software License',
18
18
  'Programming Language :: Python',
19
- 'Programming Language :: Python :: 3.10',
20
19
  'Programming Language :: Python :: 3.11',
20
+ 'Programming Language :: Python :: 3.12',
21
21
  'Topic :: Text Processing',
22
22
  'Topic :: Utilities',
23
23
  ]
24
- requires-python = '>=3.10, <4'
24
+ requires-python = '>=3.11, <4'
25
25
  dependencies = [
26
26
  'PyYAML',
27
27
  'sentry-sdk',
@@ -1,93 +0,0 @@
1
- from typing import Optional
2
-
3
- from .logging import prepare_logging, LOG_FILTER
4
-
5
-
6
- def _config_to_string(config: object):
7
- lines = [f'{type(config).__name__}']
8
- fields = (f for f in config.__dict__.keys() if not f.startswith('_'))
9
- for field in fields:
10
- v = str(getattr(config, field))
11
- t = type(getattr(config, field)).__name__
12
- lines.append(f'- {field} = {v} [{t}]')
13
- return '\n'.join(lines)
14
-
15
-
16
- class ConfigModel:
17
-
18
- def __str__(self):
19
- return _config_to_string(self)
20
-
21
-
22
- class GeneralConfig(ConfigModel):
23
-
24
- def __init__(self, environment: str, client_url: str, secret: str):
25
- self.environment = environment
26
- self.client_url = client_url
27
- self.secret = secret
28
-
29
-
30
- class SentryConfig(ConfigModel):
31
-
32
- def __init__(self, enabled: bool, workers_dsn: Optional[str],
33
- traces_sample_rate: Optional[float], max_breadcrumbs: Optional[int]):
34
- self.enabled = enabled
35
- self.workers_dsn = workers_dsn
36
- self.traces_sample_rate = traces_sample_rate
37
- self.max_breadcrumbs = max_breadcrumbs
38
-
39
-
40
- class DatabaseConfig(ConfigModel):
41
-
42
- def __init__(self, connection_string: str, connection_timeout: int,
43
- queue_timeout: int):
44
- self.connection_string = connection_string
45
- self.connection_timeout = connection_timeout
46
- self.queue_timeout = queue_timeout
47
-
48
-
49
- class S3Config(ConfigModel):
50
-
51
- def __init__(self, url: str, username: str, password: str,
52
- bucket: str, region: str):
53
- self.url = url
54
- self.username = username
55
- self.password = password
56
- self.bucket = bucket
57
- self.region = region
58
-
59
-
60
- class LoggingConfig(ConfigModel):
61
-
62
- def __init__(self, level: str, global_level: str, message_format: str,
63
- dict_config: Optional[dict] = None):
64
- self.level = level
65
- self.global_level = global_level
66
- self.message_format = message_format
67
- self.dict_config = dict_config
68
-
69
- def apply(self):
70
- prepare_logging(self)
71
-
72
- @staticmethod
73
- def set_logging_extra(key: str, value: str):
74
- LOG_FILTER.set_extra(key, value)
75
-
76
-
77
- class AWSConfig(ConfigModel):
78
-
79
- def __init__(self, access_key_id: Optional[str], secret_access_key: Optional[str],
80
- region: Optional[str]):
81
- self.access_key_id = access_key_id
82
- self.secret_access_key = secret_access_key
83
- self.region = region
84
-
85
- @property
86
- def has_credentials(self) -> bool:
87
- return self.access_key_id is not None and self.secret_access_key is not None
88
-
89
-
90
- class CloudConfig(ConfigModel):
91
-
92
- def __init__(self, multi_tenant: bool):
93
- self.multi_tenant = multi_tenant
@@ -1,37 +0,0 @@
1
- import sentry_sdk
2
-
3
- from .model import SentryConfig
4
-
5
-
6
- class SentryReporter:
7
- report = False
8
-
9
- @classmethod
10
- def initialize(cls, dsn: str, server_name: str, environment: str,
11
- prog_name: str, release: str, config: SentryConfig):
12
- cls.report = config.enabled and config.workers_dsn is not None
13
- if cls.report:
14
- sentry_sdk.init(
15
- dsn=dsn,
16
- traces_sample_rate=config.traces_sample_rate or 1.0,
17
- max_breadcrumbs=config.max_breadcrumbs or sentry_sdk.consts.DEFAULT_MAX_BREADCRUMBS,
18
- release=release,
19
- server_name=server_name,
20
- environment=environment,
21
- )
22
- sentry_sdk.set_tag('component', prog_name)
23
-
24
- @classmethod
25
- def capture_exception(cls, *args, **kwargs):
26
- if cls.report:
27
- sentry_sdk.capture_exception(*args, **kwargs)
28
-
29
- @classmethod
30
- def capture_message(cls, *args, **kwargs):
31
- if cls.report:
32
- sentry_sdk.capture_message(*args, **kwargs)
33
-
34
- @classmethod
35
- def set_context(cls, name: str, value):
36
- if cls.report:
37
- sentry_sdk.set_context(name, value)
File without changes
File without changes
File without changes
File without changes