echoss-common 1.0.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.
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ exclude tests/__pycache__/
3
+ recursive-include echoss_fileformat *.txt *.md
4
+ recursive-include tests *
5
+ recursive-exclude tests __pycache__/*
6
+ recursive-exclude * *.log *.log.*
7
+ recursive-exclude * .*.un~
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: echoss-common
3
+ Version: 1.0.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Operating System :: OS Independent
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pyyaml>6.0.2
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest; extra == "dev"
11
+
12
+ # echoss-common
13
+
14
+ echoss AI Bigdata Center 공통 유틸리티 라이브러리입니다.
15
+ 로깅(Logger)과 파일 포맷(FileUtil) 기능을 제공합니다.
16
+
17
+ ## 설치
18
+
19
+ ```bash
20
+ pip install echoss-common
21
+ ```
22
+
23
+ ## 요구사항
24
+
25
+ - Python >= 3.12
26
+ - pyyaml > 6.0.2
27
+
28
+ ---
29
+
30
+ ## 기능
31
+
32
+ ### 1. Logger (`echoss_logger`)
33
+
34
+ 타임드 로테이팅 파일 핸들러와 콘솔 핸들러를 지원하는 로거 유틸리티입니다.
35
+
36
+ #### 함수 목록
37
+
38
+ | 함수 | 설명 |
39
+ |------|------|
40
+ | `get_logger()` | 새 로거 생성 또는 기존 로거 갱신 |
41
+ | `use_logger()` | 이미 생성된 로거를 서브모듈에서 가져오기 |
42
+ | `set_logger_level()` | 로거 레벨 변경 |
43
+ | `modify_loggers_by_prefix()` | 이름 prefix로 여러 로거를 일괄 수정 |
44
+
45
+ #### 상수
46
+
47
+ | 상수 | 값 |
48
+ |------|----|
49
+ | `LOG_FORMAT_DETAIL` | `[날짜시간] 레벨 모듈.함수.라인 : 메시지` 형식 |
50
+
51
+ #### 사용 예시
52
+
53
+ ```python
54
+ from echoss_common import get_logger, use_logger, set_logger_level, modify_loggers_by_prefix, LOG_FORMAT_DETAIL
55
+
56
+ # 기본 로거 생성 (콘솔 + 파일 출력)
57
+ logger = get_logger(
58
+ logger_name='myapp',
59
+ logger_format=LOG_FORMAT_DETAIL, # 상세 포맷 사용 (선택)
60
+ file_path='logs/myapp.log', # 로그 파일 경로 (기본: logs/echoss.log)
61
+ backup_count=7, # 보관할 로그 파일 수 (0이면 전부 보관)
62
+ use_console=True, # 콘솔 출력 여부
63
+ level='DEBUG' # 로그 레벨
64
+ )
65
+
66
+ logger.info("애플리케이션 시작")
67
+ logger.debug("디버그 메시지")
68
+ logger.error("오류 발생")
69
+
70
+ # 서브모듈에서 기존 로거 재사용
71
+ logger = use_logger('myapp')
72
+ logger.info("서브모듈에서 로거 사용")
73
+
74
+ # 로거 레벨만 변경
75
+ set_logger_level(logger, 'WARNING')
76
+
77
+ # prefix로 시작하는 모든 로거 일괄 수정
78
+ modify_loggers_by_prefix(
79
+ prefix='myapp',
80
+ new_format=LOG_FORMAT_DETAIL,
81
+ new_path='logs/myapp.log',
82
+ level='INFO'
83
+ )
84
+ ```
85
+
86
+ ---
87
+
88
+ ### 2. FileUtil (`fileformat`)
89
+
90
+ 설정 파일(YAML, JSON, XML, Properties)을 딕셔너리로 읽고 쓰는 유틸리티 클래스입니다.
91
+
92
+ #### 메서드 목록
93
+
94
+ | 메서드 | 설명 |
95
+ |--------|------|
96
+ | `FileUtil.dict_load()` | 설정 파일을 `dict`로 읽기 |
97
+ | `FileUtil.dict_dump()` | `dict`를 설정 파일로 쓰기 |
98
+
99
+ #### 지원 포맷
100
+
101
+ | 확장자 | 포맷 |
102
+ |--------|------|
103
+ | `.yaml`, `.yml` | YAML |
104
+ | `.json` | JSON |
105
+ | `.xml` | XML |
106
+ | `.properties` | Java Properties |
107
+
108
+ #### 사용 예시
109
+
110
+ ```python
111
+ from echoss_common.fileformat import FileUtil
112
+
113
+ # YAML 파일 읽기
114
+ config = FileUtil.dict_load('config/settings.yaml')
115
+ print(config)
116
+
117
+ # JSON 파일 읽기
118
+ data = FileUtil.dict_load('config/params.json')
119
+
120
+ # Properties 파일 읽기
121
+ props = FileUtil.dict_load('config/app.properties')
122
+
123
+ # dict를 YAML 파일로 저장
124
+ settings = {'host': 'localhost', 'port': 8080, 'debug': True}
125
+ FileUtil.dict_dump(settings, 'config/settings.yaml')
126
+
127
+ # dict를 JSON 파일로 저장 (들여쓰기 지정 가능)
128
+ FileUtil.dict_dump(settings, 'config/settings.json', indent=2)
129
+
130
+ # 파일이 이미 존재할 때 덮어쓰기 방지
131
+ FileUtil.dict_dump(settings, 'config/settings.yaml', force_write=False)
132
+ ```
133
+
134
+ ---
135
+
136
+ ## API 참조
137
+
138
+ ### `get_logger(logger_name, logger_format, file_path, backup_count, use_console, level)`
139
+
140
+ | 파라미터 | 타입 | 기본값 | 설명 |
141
+ |----------|------|--------|------|
142
+ | `logger_name` | str | `'echoss'` | 로거 이름 |
143
+ | `logger_format` | str | `LOG_FORMAT` | 로그 포맷 문자열 |
144
+ | `file_path` | str | `'logs/echoss.log'` | 로그 파일 경로 (`None`이면 파일 미사용) |
145
+ | `backup_count` | int | `0` | 보관할 로테이팅 파일 수 (0이면 전부 보관) |
146
+ | `use_console` | bool | `True` | 콘솔 출력 여부 |
147
+ | `level` | str\|int | `'DEBUG'` | 로그 레벨 |
148
+
149
+ ### `FileUtil.dict_load(file_path, file_format, **kwargs)`
150
+
151
+ | 파라미터 | 타입 | 기본값 | 설명 |
152
+ |----------|------|--------|------|
153
+ | `file_path` | str | — | 읽을 파일 경로 |
154
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
155
+
156
+ ### `FileUtil.dict_dump(config, file_path, file_format, force_write, xml_tag, **kwargs)`
157
+
158
+ | 파라미터 | 타입 | 기본값 | 설명 |
159
+ |----------|------|--------|------|
160
+ | `config` | dict | — | 저장할 딕셔너리 |
161
+ | `file_path` | str | — | 저장할 파일 경로 |
162
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
163
+ | `force_write` | bool | `True` | 기존 파일 덮어쓰기 여부 |
164
+ | `xml_tag` | str | `None` | XML 저장 시 루트 태그 이름 |
165
+
166
+ ---
167
+
168
+ ## 라이선스
169
+
170
+ echoss AI Bigdata Center
@@ -0,0 +1,159 @@
1
+ # echoss-common
2
+
3
+ echoss AI Bigdata Center 공통 유틸리티 라이브러리입니다.
4
+ 로깅(Logger)과 파일 포맷(FileUtil) 기능을 제공합니다.
5
+
6
+ ## 설치
7
+
8
+ ```bash
9
+ pip install echoss-common
10
+ ```
11
+
12
+ ## 요구사항
13
+
14
+ - Python >= 3.12
15
+ - pyyaml > 6.0.2
16
+
17
+ ---
18
+
19
+ ## 기능
20
+
21
+ ### 1. Logger (`echoss_logger`)
22
+
23
+ 타임드 로테이팅 파일 핸들러와 콘솔 핸들러를 지원하는 로거 유틸리티입니다.
24
+
25
+ #### 함수 목록
26
+
27
+ | 함수 | 설명 |
28
+ |------|------|
29
+ | `get_logger()` | 새 로거 생성 또는 기존 로거 갱신 |
30
+ | `use_logger()` | 이미 생성된 로거를 서브모듈에서 가져오기 |
31
+ | `set_logger_level()` | 로거 레벨 변경 |
32
+ | `modify_loggers_by_prefix()` | 이름 prefix로 여러 로거를 일괄 수정 |
33
+
34
+ #### 상수
35
+
36
+ | 상수 | 값 |
37
+ |------|----|
38
+ | `LOG_FORMAT_DETAIL` | `[날짜시간] 레벨 모듈.함수.라인 : 메시지` 형식 |
39
+
40
+ #### 사용 예시
41
+
42
+ ```python
43
+ from echoss_common import get_logger, use_logger, set_logger_level, modify_loggers_by_prefix, LOG_FORMAT_DETAIL
44
+
45
+ # 기본 로거 생성 (콘솔 + 파일 출력)
46
+ logger = get_logger(
47
+ logger_name='myapp',
48
+ logger_format=LOG_FORMAT_DETAIL, # 상세 포맷 사용 (선택)
49
+ file_path='logs/myapp.log', # 로그 파일 경로 (기본: logs/echoss.log)
50
+ backup_count=7, # 보관할 로그 파일 수 (0이면 전부 보관)
51
+ use_console=True, # 콘솔 출력 여부
52
+ level='DEBUG' # 로그 레벨
53
+ )
54
+
55
+ logger.info("애플리케이션 시작")
56
+ logger.debug("디버그 메시지")
57
+ logger.error("오류 발생")
58
+
59
+ # 서브모듈에서 기존 로거 재사용
60
+ logger = use_logger('myapp')
61
+ logger.info("서브모듈에서 로거 사용")
62
+
63
+ # 로거 레벨만 변경
64
+ set_logger_level(logger, 'WARNING')
65
+
66
+ # prefix로 시작하는 모든 로거 일괄 수정
67
+ modify_loggers_by_prefix(
68
+ prefix='myapp',
69
+ new_format=LOG_FORMAT_DETAIL,
70
+ new_path='logs/myapp.log',
71
+ level='INFO'
72
+ )
73
+ ```
74
+
75
+ ---
76
+
77
+ ### 2. FileUtil (`fileformat`)
78
+
79
+ 설정 파일(YAML, JSON, XML, Properties)을 딕셔너리로 읽고 쓰는 유틸리티 클래스입니다.
80
+
81
+ #### 메서드 목록
82
+
83
+ | 메서드 | 설명 |
84
+ |--------|------|
85
+ | `FileUtil.dict_load()` | 설정 파일을 `dict`로 읽기 |
86
+ | `FileUtil.dict_dump()` | `dict`를 설정 파일로 쓰기 |
87
+
88
+ #### 지원 포맷
89
+
90
+ | 확장자 | 포맷 |
91
+ |--------|------|
92
+ | `.yaml`, `.yml` | YAML |
93
+ | `.json` | JSON |
94
+ | `.xml` | XML |
95
+ | `.properties` | Java Properties |
96
+
97
+ #### 사용 예시
98
+
99
+ ```python
100
+ from echoss_common.fileformat import FileUtil
101
+
102
+ # YAML 파일 읽기
103
+ config = FileUtil.dict_load('config/settings.yaml')
104
+ print(config)
105
+
106
+ # JSON 파일 읽기
107
+ data = FileUtil.dict_load('config/params.json')
108
+
109
+ # Properties 파일 읽기
110
+ props = FileUtil.dict_load('config/app.properties')
111
+
112
+ # dict를 YAML 파일로 저장
113
+ settings = {'host': 'localhost', 'port': 8080, 'debug': True}
114
+ FileUtil.dict_dump(settings, 'config/settings.yaml')
115
+
116
+ # dict를 JSON 파일로 저장 (들여쓰기 지정 가능)
117
+ FileUtil.dict_dump(settings, 'config/settings.json', indent=2)
118
+
119
+ # 파일이 이미 존재할 때 덮어쓰기 방지
120
+ FileUtil.dict_dump(settings, 'config/settings.yaml', force_write=False)
121
+ ```
122
+
123
+ ---
124
+
125
+ ## API 참조
126
+
127
+ ### `get_logger(logger_name, logger_format, file_path, backup_count, use_console, level)`
128
+
129
+ | 파라미터 | 타입 | 기본값 | 설명 |
130
+ |----------|------|--------|------|
131
+ | `logger_name` | str | `'echoss'` | 로거 이름 |
132
+ | `logger_format` | str | `LOG_FORMAT` | 로그 포맷 문자열 |
133
+ | `file_path` | str | `'logs/echoss.log'` | 로그 파일 경로 (`None`이면 파일 미사용) |
134
+ | `backup_count` | int | `0` | 보관할 로테이팅 파일 수 (0이면 전부 보관) |
135
+ | `use_console` | bool | `True` | 콘솔 출력 여부 |
136
+ | `level` | str\|int | `'DEBUG'` | 로그 레벨 |
137
+
138
+ ### `FileUtil.dict_load(file_path, file_format, **kwargs)`
139
+
140
+ | 파라미터 | 타입 | 기본값 | 설명 |
141
+ |----------|------|--------|------|
142
+ | `file_path` | str | — | 읽을 파일 경로 |
143
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
144
+
145
+ ### `FileUtil.dict_dump(config, file_path, file_format, force_write, xml_tag, **kwargs)`
146
+
147
+ | 파라미터 | 타입 | 기본값 | 설명 |
148
+ |----------|------|--------|------|
149
+ | `config` | dict | — | 저장할 딕셔너리 |
150
+ | `file_path` | str | — | 저장할 파일 경로 |
151
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
152
+ | `force_write` | bool | `True` | 기존 파일 덮어쓰기 여부 |
153
+ | `xml_tag` | str | `None` | XML 저장 시 루트 태그 이름 |
154
+
155
+ ---
156
+
157
+ ## 라이선스
158
+
159
+ echoss AI Bigdata Center
@@ -0,0 +1,11 @@
1
+ # for v1.0
2
+ from .echoss_logger import get_logger, use_logger, LOG_FORMAT_DETAIL, set_logger_level, modify_loggers_by_prefix
3
+ from .fileformat import FileUtil
4
+
5
+ __all__ = [
6
+ "get_logger",
7
+ "use_logger",
8
+ "LOG_FORMAT_DETAIL",
9
+ "set_logger_level",
10
+ "modify_loggers_by_prefix"
11
+ ]
@@ -0,0 +1,154 @@
1
+ import logging
2
+ from logging.handlers import TimedRotatingFileHandler
3
+ import os
4
+ import sys
5
+ from typing import Optional
6
+
7
+ LOG_FORMAT = "[%(asctime)s.%(msecs)03d] %(levelname)-7s %(name)s : %(message)s"
8
+ LOG_FORMAT_DETAIL = "[%(asctime)s.%(msecs)03d] %(levelname)-7s %(module)s.%(funcName)s.%(lineno)d : %(message)s"
9
+ DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
10
+
11
+ LOG_FILE = "logs/echoss.log"
12
+
13
+ # Set environment variable for Python IO encoding
14
+ os.environ["PYTHONIOENCODING"] = "utf-8"
15
+
16
+ def _get_console_handler(formatter):
17
+ console_handler = logging.StreamHandler(sys.stdout)
18
+ console_handler.setFormatter(formatter)
19
+ # set utf-8 encoding
20
+ return console_handler
21
+
22
+ def _get_app(formatter):
23
+ console_handler = logging.StreamHandler(sys.stdout)
24
+ console_handler.setFormatter(formatter)
25
+ # set utf-8 encoding
26
+ return console_handler
27
+
28
+ def _get_file_handler(formatter, file_path, backup_count):
29
+ file_handler = TimedRotatingFileHandler(file_path, when="midnight", backupCount=backup_count, encoding='utf-8')
30
+ file_handler.setFormatter(formatter)
31
+ return file_handler
32
+
33
+ def use_logger(logger_name='echoss'):
34
+ """get parent logger in submodule python script
35
+ :param logger_name: logger name
36
+
37
+ :returns: logger of logger_name
38
+ :raises RuntimeError when logger_name is not made
39
+ """
40
+ logger = logging.getLogger(logger_name)
41
+
42
+ if logger.hasHandlers():
43
+ return logger
44
+ else:
45
+ raise RuntimeError(f"{logger_name} is not created yet")
46
+
47
+ def get_logger(logger_name='echoss', logger_format=LOG_FORMAT, file_path=LOG_FILE, backup_count=0,
48
+ use_console=True, level="DEBUG"):
49
+ """
50
+ create new echoss logger or replace old logger
51
+ :param logger_name: logger name
52
+ :param logger_format: logging format
53
+ :param file_path: file path if File logging else None
54
+ :param backup_count: if 0 keep all timed rotating log files, else only keep backup count number of files
55
+ :param use_console: use console output or not
56
+ :param level: (str or int) set logging level
57
+
58
+ :returns: generated logger
59
+ :raises None
60
+ """
61
+ logger = logging.getLogger(logger_name)
62
+ formatter = logging.Formatter(logger_format, datefmt=DATE_FORMAT)
63
+ set_logger_level(logger, level)
64
+ # with this pattern, it's rarely necessary to propagate the error up to parent
65
+ logger.propagate = False
66
+
67
+ # make file_path parent directory
68
+ parent_dir = os.path.dirname(file_path)
69
+ if parent_dir:
70
+ os.makedirs(parent_dir, exist_ok=True)
71
+
72
+ # Update or add console handler
73
+ has_console_handler = False
74
+ for handler in logger.handlers:
75
+ if not isinstance(handler, TimedRotatingFileHandler) and isinstance(handler, logging.StreamHandler):
76
+ if use_console:
77
+ handler.setFormatter(formatter)
78
+ has_console_handler = True
79
+ else:
80
+ # Remove StreamHandler
81
+ logger.removeHandler(handler)
82
+ handler.close()
83
+
84
+ if use_console and not has_console_handler:
85
+ logger.addHandler(_get_console_handler(formatter))
86
+
87
+ # Update or add file handler
88
+ file_handler_exists = False
89
+ for handler in logger.handlers:
90
+ if isinstance(handler, TimedRotatingFileHandler):
91
+ # Check if the existing handler path matches the current file_path
92
+ if handler.baseFilename == file_path:
93
+ handler.setFormatter(formatter)
94
+ file_handler_exists = True
95
+ else:
96
+ # Remove the old handler and close it
97
+ logger.removeHandler(handler)
98
+ handler.close()
99
+
100
+ if file_path and not file_handler_exists:
101
+ logger.addHandler(_get_file_handler(formatter, file_path, backup_count))
102
+
103
+ return logger
104
+
105
+
106
+ def set_logger_level(logger, level):
107
+ """
108
+ :param logger: logger generated by get_logger()
109
+ :param level: string like "DEBUG" or "INFO" or integer logging.DEBUG or logging.INFO
110
+ :return: None
111
+ :raises ValueError or TypeError if level invalid
112
+ """
113
+ if isinstance(level, str):
114
+ level = level.upper()
115
+ numeric_level = getattr(logging, level, None)
116
+ if not isinstance(numeric_level, int):
117
+ raise ValueError(f"Invalid log level: {level}")
118
+ elif isinstance(level, int):
119
+ numeric_level = level
120
+ else:
121
+ raise TypeError("Level must be an integer or string")
122
+
123
+ logger.setLevel(numeric_level)
124
+
125
+
126
+ def modify_loggers_by_prefix(prefix, new_format: str=LOG_FORMAT, new_path: str=LOG_FILE,
127
+ backup_count: int=0, use_console=True, level="DEBUG"):
128
+ """
129
+ Modify loggers that logger name starting with a specific prefix.
130
+ :param prefix: prefix of lgger name
131
+ :param new_format: new logging format
132
+ :param new_path: new file path if File logging else None
133
+ :param backup_count: if 0 keep all timed rotating log files, else only keep backup count number of files
134
+ :param use_console: use console output or not
135
+ :param level: set logging level by level name or level number
136
+ :return: first logger
137
+ :raises None
138
+ """
139
+
140
+ # Iterate over all loggers in the logging manager
141
+ is_first = True
142
+ first_logger = None
143
+ for logger_name in logging.Logger.manager.loggerDict.keys():
144
+ if logger_name.startswith(prefix):
145
+ current_logger = get_logger(logger_name, logger_format=new_format, file_path=new_path,
146
+ backup_count=backup_count, use_console=use_console, level=level)
147
+
148
+ if is_first:
149
+ is_first = False
150
+ first_logger = current_logger
151
+ if backup_count != 0:
152
+ backup_count = 0
153
+
154
+ return first_logger
@@ -0,0 +1,121 @@
1
+ """
2
+ echoss AI Bigdata Center Solution - file format utilty (static version)
3
+ """
4
+ import configparser
5
+ import io
6
+ import json
7
+ import os
8
+ import yaml
9
+
10
+ from echoss_common import get_logger
11
+
12
+ logger = get_logger("echoss_fileformat")
13
+ EMPTY_DICT = dict()
14
+
15
+
16
+ """
17
+ load/dump config file
18
+ """
19
+ def dict_load(file_path: str, file_format: str = None, **kwargs) -> dict:
20
+ """config file read to dict
21
+
22
+ Args:
23
+ file_path (str): 파일명
24
+ file_format : file extension name to read
25
+ kwargs : keyword arguments
26
+ Return:
27
+ dict
28
+ """
29
+ if file_format is None:
30
+ _, ext = os.path.splitext(file_path)
31
+ file_format = ext[1:].lower() if ext else None
32
+ if not os.path.exists(file_path):
33
+ logger.error(f"load config file [{file_path}] is not exist")
34
+ return EMPTY_DICT
35
+
36
+ if "yaml" == file_format or "yml" == file_format :
37
+ with open(file_path, 'r') as f:
38
+ yaml_dict = yaml.safe_load(f)
39
+ return yaml_dict
40
+ elif "json" == file_format:
41
+ with open(file_path, 'r') as f:
42
+ return json.load(f, **kwargs)
43
+ elif "xml" == file_format:
44
+ logger.error(f"Unsupported file format: {file_format}")
45
+ return EMPTY_DICT
46
+ elif 'properties' == file_format:
47
+ config = configparser.ConfigParser()
48
+ properties_dict = {}
49
+ with open(file_path, 'r') as f:
50
+ file_content = f.read()
51
+ if not file_content.strip().startswith('['):
52
+ file_content = '[DEFAULT]\n' + file_content
53
+ config.read_string(file_content)
54
+
55
+ if config.sections(): # Check if there are any sections
56
+ for section in config.sections():
57
+ properties_dict[section] = dict(config.items(section))
58
+ else:
59
+ properties_dict = dict(config.items(config.default_section))
60
+
61
+ return properties_dict
62
+ else:
63
+ logger.error(f"Unsupported file format [{file_format}]")
64
+ return EMPTY_DICT
65
+
66
+ def dict_dump(config: dict, file_path: str, file_format=None, force_write=True, xml_tag=None, **kwargs):
67
+ """config dict write to file
68
+
69
+ Args:
70
+ config (dict): config dictionary
71
+ file_path (str): 파일명
72
+ file_format : file extension name to use
73
+ force_write : if True overwrite exist file or just return
74
+ xml_tag (str) : root tag to wrapping dictionary
75
+ kwargs : keyword arguments
76
+ """
77
+ if file_format is None:
78
+ _, ext = os.path.splitext(file_path)
79
+ file_format = ext[1:].lower() if ext else None
80
+
81
+ if os.path.exists(file_path) and force_write is False:
82
+ logger.error(f"Can not overwrite exist config file [{file_path}] use force_write=True if need")
83
+ return
84
+
85
+ if "yaml" == file_format or "yml" == file_format :
86
+ with open(file_path, 'w') as f:
87
+ yaml.dump(config, f, default_style=False, allow_unicode=True)
88
+ elif "json" == file_format:
89
+ with open(file_path, 'w') as f:
90
+ indent = kwargs.pop('indent', 4)
91
+ json.dump(config, f, indent=indent, **kwargs)
92
+ elif "xml" == file_format:
93
+ logger.error(f"Unsupported file format: {file_format}")
94
+ return EMPTY_DICT
95
+ elif 'properties' == file_format:
96
+ parser = configparser.ConfigParser()
97
+ have_section = any(isinstance(v, dict) for v in config.values())
98
+ if have_section:
99
+ for section, params in config.items():
100
+ if isinstance(params, dict):
101
+ parser.add_section(section)
102
+ for k, v in params.items():
103
+ parser.set(section, k, str(v))
104
+ else:
105
+ parser.set('DEFAULT', section, str(params))
106
+ else:
107
+ # parser.add_section('DEFAULT')
108
+ for k, v in config.items():
109
+ parser.set('DEFAULT', k, str(v))
110
+ with open(file_path, 'w') as f:
111
+ if have_section:
112
+ parser.write(f)
113
+ else:
114
+ with io.StringIO() as string_file:
115
+ parser.write(string_file)
116
+ clean_text = string_file.getvalue().replace("[DEFAULT]\n", "")
117
+ f.write(clean_text)
118
+ else:
119
+ logger.error(f"Unsupported file format [{file_format}]")
120
+
121
+
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: echoss-common
3
+ Version: 1.0.0
4
+ Classifier: Programming Language :: Python :: 3
5
+ Classifier: Operating System :: OS Independent
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pyyaml>6.0.2
9
+ Provides-Extra: dev
10
+ Requires-Dist: pytest; extra == "dev"
11
+
12
+ # echoss-common
13
+
14
+ echoss AI Bigdata Center 공통 유틸리티 라이브러리입니다.
15
+ 로깅(Logger)과 파일 포맷(FileUtil) 기능을 제공합니다.
16
+
17
+ ## 설치
18
+
19
+ ```bash
20
+ pip install echoss-common
21
+ ```
22
+
23
+ ## 요구사항
24
+
25
+ - Python >= 3.12
26
+ - pyyaml > 6.0.2
27
+
28
+ ---
29
+
30
+ ## 기능
31
+
32
+ ### 1. Logger (`echoss_logger`)
33
+
34
+ 타임드 로테이팅 파일 핸들러와 콘솔 핸들러를 지원하는 로거 유틸리티입니다.
35
+
36
+ #### 함수 목록
37
+
38
+ | 함수 | 설명 |
39
+ |------|------|
40
+ | `get_logger()` | 새 로거 생성 또는 기존 로거 갱신 |
41
+ | `use_logger()` | 이미 생성된 로거를 서브모듈에서 가져오기 |
42
+ | `set_logger_level()` | 로거 레벨 변경 |
43
+ | `modify_loggers_by_prefix()` | 이름 prefix로 여러 로거를 일괄 수정 |
44
+
45
+ #### 상수
46
+
47
+ | 상수 | 값 |
48
+ |------|----|
49
+ | `LOG_FORMAT_DETAIL` | `[날짜시간] 레벨 모듈.함수.라인 : 메시지` 형식 |
50
+
51
+ #### 사용 예시
52
+
53
+ ```python
54
+ from echoss_common import get_logger, use_logger, set_logger_level, modify_loggers_by_prefix, LOG_FORMAT_DETAIL
55
+
56
+ # 기본 로거 생성 (콘솔 + 파일 출력)
57
+ logger = get_logger(
58
+ logger_name='myapp',
59
+ logger_format=LOG_FORMAT_DETAIL, # 상세 포맷 사용 (선택)
60
+ file_path='logs/myapp.log', # 로그 파일 경로 (기본: logs/echoss.log)
61
+ backup_count=7, # 보관할 로그 파일 수 (0이면 전부 보관)
62
+ use_console=True, # 콘솔 출력 여부
63
+ level='DEBUG' # 로그 레벨
64
+ )
65
+
66
+ logger.info("애플리케이션 시작")
67
+ logger.debug("디버그 메시지")
68
+ logger.error("오류 발생")
69
+
70
+ # 서브모듈에서 기존 로거 재사용
71
+ logger = use_logger('myapp')
72
+ logger.info("서브모듈에서 로거 사용")
73
+
74
+ # 로거 레벨만 변경
75
+ set_logger_level(logger, 'WARNING')
76
+
77
+ # prefix로 시작하는 모든 로거 일괄 수정
78
+ modify_loggers_by_prefix(
79
+ prefix='myapp',
80
+ new_format=LOG_FORMAT_DETAIL,
81
+ new_path='logs/myapp.log',
82
+ level='INFO'
83
+ )
84
+ ```
85
+
86
+ ---
87
+
88
+ ### 2. FileUtil (`fileformat`)
89
+
90
+ 설정 파일(YAML, JSON, XML, Properties)을 딕셔너리로 읽고 쓰는 유틸리티 클래스입니다.
91
+
92
+ #### 메서드 목록
93
+
94
+ | 메서드 | 설명 |
95
+ |--------|------|
96
+ | `FileUtil.dict_load()` | 설정 파일을 `dict`로 읽기 |
97
+ | `FileUtil.dict_dump()` | `dict`를 설정 파일로 쓰기 |
98
+
99
+ #### 지원 포맷
100
+
101
+ | 확장자 | 포맷 |
102
+ |--------|------|
103
+ | `.yaml`, `.yml` | YAML |
104
+ | `.json` | JSON |
105
+ | `.xml` | XML |
106
+ | `.properties` | Java Properties |
107
+
108
+ #### 사용 예시
109
+
110
+ ```python
111
+ from echoss_common.fileformat import FileUtil
112
+
113
+ # YAML 파일 읽기
114
+ config = FileUtil.dict_load('config/settings.yaml')
115
+ print(config)
116
+
117
+ # JSON 파일 읽기
118
+ data = FileUtil.dict_load('config/params.json')
119
+
120
+ # Properties 파일 읽기
121
+ props = FileUtil.dict_load('config/app.properties')
122
+
123
+ # dict를 YAML 파일로 저장
124
+ settings = {'host': 'localhost', 'port': 8080, 'debug': True}
125
+ FileUtil.dict_dump(settings, 'config/settings.yaml')
126
+
127
+ # dict를 JSON 파일로 저장 (들여쓰기 지정 가능)
128
+ FileUtil.dict_dump(settings, 'config/settings.json', indent=2)
129
+
130
+ # 파일이 이미 존재할 때 덮어쓰기 방지
131
+ FileUtil.dict_dump(settings, 'config/settings.yaml', force_write=False)
132
+ ```
133
+
134
+ ---
135
+
136
+ ## API 참조
137
+
138
+ ### `get_logger(logger_name, logger_format, file_path, backup_count, use_console, level)`
139
+
140
+ | 파라미터 | 타입 | 기본값 | 설명 |
141
+ |----------|------|--------|------|
142
+ | `logger_name` | str | `'echoss'` | 로거 이름 |
143
+ | `logger_format` | str | `LOG_FORMAT` | 로그 포맷 문자열 |
144
+ | `file_path` | str | `'logs/echoss.log'` | 로그 파일 경로 (`None`이면 파일 미사용) |
145
+ | `backup_count` | int | `0` | 보관할 로테이팅 파일 수 (0이면 전부 보관) |
146
+ | `use_console` | bool | `True` | 콘솔 출력 여부 |
147
+ | `level` | str\|int | `'DEBUG'` | 로그 레벨 |
148
+
149
+ ### `FileUtil.dict_load(file_path, file_format, **kwargs)`
150
+
151
+ | 파라미터 | 타입 | 기본값 | 설명 |
152
+ |----------|------|--------|------|
153
+ | `file_path` | str | — | 읽을 파일 경로 |
154
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
155
+
156
+ ### `FileUtil.dict_dump(config, file_path, file_format, force_write, xml_tag, **kwargs)`
157
+
158
+ | 파라미터 | 타입 | 기본값 | 설명 |
159
+ |----------|------|--------|------|
160
+ | `config` | dict | — | 저장할 딕셔너리 |
161
+ | `file_path` | str | — | 저장할 파일 경로 |
162
+ | `file_format` | str | `None` | 포맷 강제 지정 (미지정 시 확장자 자동 인식) |
163
+ | `force_write` | bool | `True` | 기존 파일 덮어쓰기 여부 |
164
+ | `xml_tag` | str | `None` | XML 저장 시 루트 태그 이름 |
165
+
166
+ ---
167
+
168
+ ## 라이선스
169
+
170
+ echoss AI Bigdata Center
@@ -0,0 +1,11 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ echoss_common/__init__.py
5
+ echoss_common/echoss_logger.py
6
+ echoss_common/fileformat.py
7
+ echoss_common.egg-info/PKG-INFO
8
+ echoss_common.egg-info/SOURCES.txt
9
+ echoss_common.egg-info/dependency_links.txt
10
+ echoss_common.egg-info/requires.txt
11
+ echoss_common.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ pyyaml>6.0.2
2
+
3
+ [dev]
4
+ pytest
@@ -0,0 +1 @@
1
+ echoss_common
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=80.9.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name="echoss-common"
7
+ version="1.0.0"
8
+ description=""
9
+ readme = "README.md"
10
+
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "Operating System :: OS Independent"
14
+ ]
15
+
16
+ requires-python='>=3.12'
17
+ dependencies=[
18
+ "pyyaml>6.0.2"
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ dev = ["pytest"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+