Coverage for etiketti/__init__.py: 86.49%

64 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 17:54:24 +00:00

1"""Label (Finnish: etiketti) some files.""" 

2 

3import datetime as dti 

4import logging 

5import os 

6import pathlib 

7from typing import Union, no_type_check 

8 

9# [[[fill git_describe()]]] 

10__version__ = '2024.1.16+parent.g0a2f67d3' 

11# [[[end]]] (checksum: e04ad0c0084645e3a7b0cdf9c3f06a99) 

12__version_info__ = tuple( 

13 e if '-' not in e else e.split('-')[0] for part in __version__.split('+') for e in part.split('.') if e != 'parent' 

14) 

15 

16APP_ALIAS = str(pathlib.Path(__file__).parent.name) 

17APP_ENV = APP_ALIAS.upper() 

18APP_NAME = locals()['__doc__'] 

19DEBUG = bool(os.getenv(f'{APP_ENV}_DEBUG', '')) 

20VERBOSE = bool(os.getenv(f'{APP_ENV}_VERBOSE', '')) 

21QUIET = False 

22STRICT = bool(os.getenv(f'{APP_ENV}_STRICT', '')) 

23ENCODING = 'utf-8' 

24ENCODING_ERRORS_POLICY = 'ignore' 

25DEFAULT_CONFIG_NAME = f'.{APP_ALIAS}.json' 

26 

27APP_VERSION = __version__ 

28DEFAULT_LF_ONLY = 'YES' 

29DEFAULT_AUTHOR = os.getenv(f'{APP_ENV}_DEFAULT_AUTHOR', '') 

30log = logging.getLogger() # Module level logger is sufficient 

31LOG_FOLDER = pathlib.Path('logs') 

32LOG_FILE = f'{APP_ALIAS}.log' 

33LOG_PATH = pathlib.Path(LOG_FOLDER, LOG_FILE) if LOG_FOLDER.is_dir() else pathlib.Path(LOG_FILE) 

34LOG_LEVEL = logging.INFO 

35 

36COMMA = ',' 

37EQUAL = '=' 

38 

39LOG_SEPARATOR = '- ' * 80 

40INTER_PROCESS_SYNC_SECS = 0.1 

41INTER_PROCESS_SYNC_ATTEMPTS = 10 

42TS_FORMAT_ISO = '%Y-%m-%dT%H:%M:%SZ' 

43TS_FORMAT_PDF = '%Y%m%dT%H%M%SZ' # was 'D:%Y%m%dT%H%M%SZ' 

44TS_FORMAT_LOG = '%Y-%m-%dT%H:%M:%S' 

45TS_FORMAT_PAYLOADS = '%Y-%m-%d %H:%M:%S.%f UTC' 

46TS_FORMAT_PATCH = '%Y-%m-%d %H:%M:%S' 

47 

48SOURCE_NAME_PATH_STRING = os.getenv(f'{APP_ENV}_SOURCE_NAME_PATH_STRING', 'this.pdf') 

49TARGET_NAME_PATH_STRING = os.getenv(f'{APP_ENV}_TARGET_NAME_PATH_STRING', 'labeled.pdf') 

50CONFIG_PATH_STRING = os.getenv(f'{APP_ENV}_CONFIG_PATH_STRING', f'/opt/label/{APP_ALIAS}.yml') 

51 

52ContextType = dict[str, dict[str, str]] 

53ConventionsType = dict[str, pathlib.Path] 

54PathLike = Union[str, pathlib.Path] 

55 

56__all__: list[str] = [ 

57 'APP_ALIAS', 

58 'APP_ENV', 

59 'APP_NAME', 

60 'APP_VERSION', 

61 'CONFIG_PATH_STRING', 

62 'DEFAULT_AUTHOR', 

63 'ENCODING', 

64 'LOG_SEPARATOR', 

65 'SOURCE_NAME_PATH_STRING', 

66 'TARGET_NAME_PATH_STRING', 

67 'TS_FORMAT_PATCH', 

68 'TS_FORMAT_PAYLOADS', 

69 'TS_FORMAT_ISO', 

70 'ContextType', 

71 'ConventionsType', 

72 'PathLike', 

73 'log', 

74 'parse_key_value_pair_csl', 

75] 

76 

77 

78def parse_key_value_pair_csl(csl: str) -> dict[str, str]: 

79 """DRY.""" 

80 pairs = [pair.strip() for pair in csl.split(COMMA) if pair.strip() and EQUAL in pair] 

81 key_value_store = {} 

82 for pair in pairs: 82 ↛ 83line 82 didn't jump to line 83, because the loop on line 82 never started

83 try: 

84 k, v = pair.split(EQUAL, 1) 

85 except ValueError: 

86 continue 

87 real_key = k.strip() 

88 if real_key: 

89 key_value_store[real_key] = v.strip() 

90 return key_value_store 

91 

92 

93@no_type_check 

94def formatTime_RFC3339(self, record, datefmt=None): # noqa 

95 """HACK A DID ACK we could inject .astimezone() to localize ...""" 

96 return dti.datetime.fromtimestamp(record.created, dti.timezone.utc).isoformat() # pragma: no cover 

97 

98 

99@no_type_check 

100def init_logger(name=None, level=None): 

101 """Initialize module level logger""" 

102 global log # pylint: disable=global-statement 

103 

104 log_format = { 

105 'format': '%(asctime)s %(levelname)s [%(name)s]: %(message)s', 

106 'datefmt': TS_FORMAT_LOG, 

107 # 'filename': LOG_PATH, 

108 'level': LOG_LEVEL if level is None else level, 

109 } 

110 logging.Formatter.formatTime = formatTime_RFC3339 

111 logging.basicConfig(**log_format) 

112 log = logging.getLogger(APP_ENV if name is None else name) 

113 log.propagate = True 

114 

115 

116init_logger(name=APP_ENV, level=logging.DEBUG if DEBUG else None)