rainbow-rb-utils 0.0.9.dev43__tar.gz → 0.0.9.dev44__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 (24) hide show
  1. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/PKG-INFO +52 -4
  2. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/README.md +50 -2
  3. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/pyproject.toml +2 -2
  4. rainbow_rb_utils-0.0.9.dev44/src/rainbow/rb_utils/env.py +131 -0
  5. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow_rb_utils.egg-info/PKG-INFO +52 -4
  6. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow_rb_utils.egg-info/SOURCES.txt +1 -0
  7. rainbow_rb_utils-0.0.9.dev44/src/rainbow_rb_utils.egg-info/requires.txt +2 -0
  8. rainbow_rb_utils-0.0.9.dev43/src/rainbow_rb_utils.egg-info/requires.txt +0 -2
  9. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/setup.cfg +0 -0
  10. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/__init__.py +0 -0
  11. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/asyncio_helper.py +0 -0
  12. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/date.py +0 -0
  13. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/file.py +0 -0
  14. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/flow_manager.py +0 -0
  15. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/helper.py +0 -0
  16. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/manipulate.py +0 -0
  17. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/pagination.py +0 -0
  18. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/parser.py +0 -0
  19. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/path_utils.py +0 -0
  20. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/program.py +0 -0
  21. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/py.typed +0 -0
  22. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow/rb_utils/service_exception.py +0 -0
  23. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow_rb_utils.egg-info/dependency_links.txt +0 -0
  24. {rainbow_rb_utils-0.0.9.dev43 → rainbow_rb_utils-0.0.9.dev44}/src/rainbow_rb_utils.egg-info/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rainbow-rb-utils
3
- Version: 0.0.9.dev43
3
+ Version: 0.0.9.dev44
4
4
  Summary: Rainbow Python Utilities
5
5
  Author-email: Derek <dfd1123@rainbow-robotics.com>
6
6
  Requires-Python: >=3.11
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: psutil<8.0.0,>=7.0.0
9
- Requires-Dist: rainbow-rb-log==0.0.9.dev43
9
+ Requires-Dist: rainbow-rb-log==0.0.9.dev44
10
10
 
11
11
  # rb_utils
12
12
 
@@ -113,12 +113,60 @@ from rb_utils.file import sanitize_filename
113
113
  safe_name = sanitize_filename("../../report?.csv")
114
114
  ```
115
115
 
116
- ## 7) `rb_utils.pagination`
116
+ ## 7) `rb_utils.env`
117
+
118
+ - `env_get(key, default=None, start=None)`: `os.environ` 우선, 이후 가까운 `.env.development` → `.env` 순서로 값 조회
119
+ - `load_env(start=None, override=False)`: 가까운 env 파일들을 `os.environ`에 로드
120
+ - `iter_env_paths(start=None)`: 실제 읽을 env 파일 목록을 우선순위 순서로 반환
121
+ - `parse_env_file(path)`: env 파일을 dict로 파싱
122
+
123
+ 예시:
124
+
125
+ 새 코드에서 값 하나를 바로 읽을 때는 `load_env()`가 필요 없습니다.
126
+
127
+ ```python
128
+ from rb_utils.env import env_get
129
+
130
+ username = env_get("MONGO_USERNAME")
131
+ password = env_get("MONGO_PASSWORD")
132
+ host = env_get("MONGO_HOST", "127.0.0.1")
133
+ ```
134
+
135
+ 기존 코드가 `os.getenv(...)`를 많이 쓰고 있으면 프로세스 시작 시 한 번만 로드합니다.
136
+
137
+ ```python
138
+ import os
139
+
140
+ from rb_utils.env import load_env
141
+
142
+ load_env()
143
+
144
+ username = os.getenv("MONGO_USERNAME")
145
+ password = os.getenv("MONGO_PASSWORD")
146
+ ```
147
+
148
+ 기본 우선순위:
149
+
150
+ 1. 현재 위치에서 가장 가까운 `.env.development`
151
+ 2. 같은 디렉토리의 `.env`
152
+ 3. 부모 디렉토리의 `.env.development`
153
+ 4. 부모 디렉토리의 `.env`
154
+
155
+ 즉 개발 환경에서는 `.env.development`가 `.env` 값을 덮어씁니다.
156
+ 운영 빌드에는 `.env.development`를 copy하지 않으므로 `.env`만 적용됩니다.
157
+
158
+ 다른 suffix가 필요하면 명시적으로 지정:
159
+
160
+ ```python
161
+ env_get("KEY", env_files=(".env.local", ".env.development", ".env"))
162
+ ```
163
+
164
+ ## 8) `rb_utils.pagination`
117
165
 
118
166
  - `class PageInfo(TypedDict)`: 페이지 응답 메타 타입 (`mode/page/pages/limit/total/sort/order`)
119
167
  - `class LogsResponse(TypedDict)`: 로그 목록 응답 타입 (`items`, `pageInfo`)
120
168
 
121
- ## 8) `rb_utils.helper`
169
+ ## 9) `rb_utils.helper`
122
170
 
123
171
  - `ManipulateZenohResHelper(obj)`: Zenoh 응답 객체 보정 유틸 의도 함수
124
172
 
@@ -103,12 +103,60 @@ from rb_utils.file import sanitize_filename
103
103
  safe_name = sanitize_filename("../../report?.csv")
104
104
  ```
105
105
 
106
- ## 7) `rb_utils.pagination`
106
+ ## 7) `rb_utils.env`
107
+
108
+ - `env_get(key, default=None, start=None)`: `os.environ` 우선, 이후 가까운 `.env.development` → `.env` 순서로 값 조회
109
+ - `load_env(start=None, override=False)`: 가까운 env 파일들을 `os.environ`에 로드
110
+ - `iter_env_paths(start=None)`: 실제 읽을 env 파일 목록을 우선순위 순서로 반환
111
+ - `parse_env_file(path)`: env 파일을 dict로 파싱
112
+
113
+ 예시:
114
+
115
+ 새 코드에서 값 하나를 바로 읽을 때는 `load_env()`가 필요 없습니다.
116
+
117
+ ```python
118
+ from rb_utils.env import env_get
119
+
120
+ username = env_get("MONGO_USERNAME")
121
+ password = env_get("MONGO_PASSWORD")
122
+ host = env_get("MONGO_HOST", "127.0.0.1")
123
+ ```
124
+
125
+ 기존 코드가 `os.getenv(...)`를 많이 쓰고 있으면 프로세스 시작 시 한 번만 로드합니다.
126
+
127
+ ```python
128
+ import os
129
+
130
+ from rb_utils.env import load_env
131
+
132
+ load_env()
133
+
134
+ username = os.getenv("MONGO_USERNAME")
135
+ password = os.getenv("MONGO_PASSWORD")
136
+ ```
137
+
138
+ 기본 우선순위:
139
+
140
+ 1. 현재 위치에서 가장 가까운 `.env.development`
141
+ 2. 같은 디렉토리의 `.env`
142
+ 3. 부모 디렉토리의 `.env.development`
143
+ 4. 부모 디렉토리의 `.env`
144
+
145
+ 즉 개발 환경에서는 `.env.development`가 `.env` 값을 덮어씁니다.
146
+ 운영 빌드에는 `.env.development`를 copy하지 않으므로 `.env`만 적용됩니다.
147
+
148
+ 다른 suffix가 필요하면 명시적으로 지정:
149
+
150
+ ```python
151
+ env_get("KEY", env_files=(".env.local", ".env.development", ".env"))
152
+ ```
153
+
154
+ ## 8) `rb_utils.pagination`
107
155
 
108
156
  - `class PageInfo(TypedDict)`: 페이지 응답 메타 타입 (`mode/page/pages/limit/total/sort/order`)
109
157
  - `class LogsResponse(TypedDict)`: 로그 목록 응답 타입 (`items`, `pageInfo`)
110
158
 
111
- ## 8) `rb_utils.helper`
159
+ ## 9) `rb_utils.helper`
112
160
 
113
161
  - `ManipulateZenohResHelper(obj)`: Zenoh 응답 객체 보정 유틸 의도 함수
114
162
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rainbow-rb-utils"
7
- version = "0.0.9.dev43"
7
+ version = "0.0.9.dev44"
8
8
  requires-python = ">=3.11"
9
9
  description = "Rainbow Python Utilities"
10
10
  authors = [
@@ -14,7 +14,7 @@ readme = "README.md"
14
14
 
15
15
  dependencies = [
16
16
  "psutil>=7.0.0,<8.0.0",
17
- "rainbow-rb-log==0.0.9.dev43",
17
+ "rainbow-rb-log==0.0.9.dev44",
18
18
  ]
19
19
 
20
20
  [tool.setuptools]
@@ -0,0 +1,131 @@
1
+ """Environment file lookup helpers.
2
+
3
+ The default lookup order is intentionally local-first:
4
+
5
+ 1. nearest ``.env.development``
6
+ 2. nearest ``.env``
7
+ 3. parent ``.env.development``
8
+ 4. parent ``.env``
9
+ 5. ...
10
+
11
+ Existing process environment values always win unless ``override=True`` is
12
+ explicitly requested.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import re
19
+ import shlex
20
+ import sys
21
+ from collections.abc import Iterable
22
+ from pathlib import Path
23
+
24
+ DEFAULT_ENV_FILES = (".env.development", ".env")
25
+ _ASSIGNMENT_RE = re.compile(r"^(?:export\s+)?(?P<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?P<value>.*)$")
26
+
27
+
28
+ def _start_path(start: str | os.PathLike[str] | None) -> Path:
29
+ if start is not None:
30
+ path = Path(start).expanduser()
31
+ return path if path.is_dir() else path.parent
32
+
33
+ meipass = getattr(sys, "_MEIPASS", None)
34
+ if meipass:
35
+ return Path(meipass)
36
+
37
+ return Path.cwd()
38
+
39
+
40
+ def iter_env_paths(
41
+ start: str | os.PathLike[str] | None = None,
42
+ *,
43
+ env_files: Iterable[str] = DEFAULT_ENV_FILES,
44
+ ) -> list[Path]:
45
+ """Return existing env files in lookup precedence order.
46
+
47
+ The nearest directory wins over parents, and within each directory the
48
+ order of ``env_files`` is preserved.
49
+ """
50
+ base = _start_path(start).resolve()
51
+ names = tuple(env_files)
52
+ paths: list[Path] = []
53
+
54
+ for directory in (base, *base.parents):
55
+ for name in names:
56
+ candidate = directory / name
57
+ if candidate.is_file():
58
+ paths.append(candidate)
59
+
60
+ return paths
61
+
62
+
63
+ def parse_env_file(path: str | os.PathLike[str]) -> dict[str, str]:
64
+ """Parse a simple dotenv-style file without mutating ``os.environ``."""
65
+ values: dict[str, str] = {}
66
+ env_path = Path(path)
67
+
68
+ for raw_line in env_path.read_text(encoding="utf-8").splitlines():
69
+ line = raw_line.strip()
70
+ if not line or line.startswith("#"):
71
+ continue
72
+
73
+ match = _ASSIGNMENT_RE.match(line)
74
+ if match is None:
75
+ continue
76
+
77
+ key = match.group("key")
78
+ raw_value = match.group("value").strip()
79
+ try:
80
+ parsed = shlex.split(raw_value, comments=True, posix=True)
81
+ except ValueError:
82
+ parsed = [raw_value]
83
+ values[key] = parsed[0] if parsed else ""
84
+
85
+ return values
86
+
87
+
88
+ def load_env(
89
+ start: str | os.PathLike[str] | None = None,
90
+ *,
91
+ env_files: Iterable[str] = DEFAULT_ENV_FILES,
92
+ override: bool = False,
93
+ ) -> list[Path]:
94
+ """Load nearest env files into ``os.environ``.
95
+
96
+ Returns the files that were read, in precedence order. With the default
97
+ ``override=False``, earlier files win because later files only fill missing
98
+ keys.
99
+ """
100
+ loaded = iter_env_paths(start, env_files=env_files)
101
+
102
+ for path in loaded:
103
+ for key, value in parse_env_file(path).items():
104
+ if override or key not in os.environ:
105
+ os.environ[key] = value
106
+
107
+ return loaded
108
+
109
+
110
+ def env_get(
111
+ key: str,
112
+ default: object = None,
113
+ *,
114
+ start: str | os.PathLike[str] | None = None,
115
+ env_files: Iterable[str] = DEFAULT_ENV_FILES,
116
+ ) -> str | object:
117
+ """Read an environment value with local ``.env`` fallback.
118
+
119
+ ``os.environ`` has first priority, then nearest env files in this order:
120
+ ``.env.development``, ``.env``, parent ``.env.development``, parent
121
+ ``.env``, and so on.
122
+ """
123
+ if key in os.environ:
124
+ return os.environ[key]
125
+
126
+ for path in iter_env_paths(start, env_files=env_files):
127
+ values = parse_env_file(path)
128
+ if key in values:
129
+ return values[key]
130
+
131
+ return default
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rainbow-rb-utils
3
- Version: 0.0.9.dev43
3
+ Version: 0.0.9.dev44
4
4
  Summary: Rainbow Python Utilities
5
5
  Author-email: Derek <dfd1123@rainbow-robotics.com>
6
6
  Requires-Python: >=3.11
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: psutil<8.0.0,>=7.0.0
9
- Requires-Dist: rainbow-rb-log==0.0.9.dev43
9
+ Requires-Dist: rainbow-rb-log==0.0.9.dev44
10
10
 
11
11
  # rb_utils
12
12
 
@@ -113,12 +113,60 @@ from rb_utils.file import sanitize_filename
113
113
  safe_name = sanitize_filename("../../report?.csv")
114
114
  ```
115
115
 
116
- ## 7) `rb_utils.pagination`
116
+ ## 7) `rb_utils.env`
117
+
118
+ - `env_get(key, default=None, start=None)`: `os.environ` 우선, 이후 가까운 `.env.development` → `.env` 순서로 값 조회
119
+ - `load_env(start=None, override=False)`: 가까운 env 파일들을 `os.environ`에 로드
120
+ - `iter_env_paths(start=None)`: 실제 읽을 env 파일 목록을 우선순위 순서로 반환
121
+ - `parse_env_file(path)`: env 파일을 dict로 파싱
122
+
123
+ 예시:
124
+
125
+ 새 코드에서 값 하나를 바로 읽을 때는 `load_env()`가 필요 없습니다.
126
+
127
+ ```python
128
+ from rb_utils.env import env_get
129
+
130
+ username = env_get("MONGO_USERNAME")
131
+ password = env_get("MONGO_PASSWORD")
132
+ host = env_get("MONGO_HOST", "127.0.0.1")
133
+ ```
134
+
135
+ 기존 코드가 `os.getenv(...)`를 많이 쓰고 있으면 프로세스 시작 시 한 번만 로드합니다.
136
+
137
+ ```python
138
+ import os
139
+
140
+ from rb_utils.env import load_env
141
+
142
+ load_env()
143
+
144
+ username = os.getenv("MONGO_USERNAME")
145
+ password = os.getenv("MONGO_PASSWORD")
146
+ ```
147
+
148
+ 기본 우선순위:
149
+
150
+ 1. 현재 위치에서 가장 가까운 `.env.development`
151
+ 2. 같은 디렉토리의 `.env`
152
+ 3. 부모 디렉토리의 `.env.development`
153
+ 4. 부모 디렉토리의 `.env`
154
+
155
+ 즉 개발 환경에서는 `.env.development`가 `.env` 값을 덮어씁니다.
156
+ 운영 빌드에는 `.env.development`를 copy하지 않으므로 `.env`만 적용됩니다.
157
+
158
+ 다른 suffix가 필요하면 명시적으로 지정:
159
+
160
+ ```python
161
+ env_get("KEY", env_files=(".env.local", ".env.development", ".env"))
162
+ ```
163
+
164
+ ## 8) `rb_utils.pagination`
117
165
 
118
166
  - `class PageInfo(TypedDict)`: 페이지 응답 메타 타입 (`mode/page/pages/limit/total/sort/order`)
119
167
  - `class LogsResponse(TypedDict)`: 로그 목록 응답 타입 (`items`, `pageInfo`)
120
168
 
121
- ## 8) `rb_utils.helper`
169
+ ## 9) `rb_utils.helper`
122
170
 
123
171
  - `ManipulateZenohResHelper(obj)`: Zenoh 응답 객체 보정 유틸 의도 함수
124
172
 
@@ -3,6 +3,7 @@ pyproject.toml
3
3
  src/rainbow/rb_utils/__init__.py
4
4
  src/rainbow/rb_utils/asyncio_helper.py
5
5
  src/rainbow/rb_utils/date.py
6
+ src/rainbow/rb_utils/env.py
6
7
  src/rainbow/rb_utils/file.py
7
8
  src/rainbow/rb_utils/flow_manager.py
8
9
  src/rainbow/rb_utils/helper.py
@@ -0,0 +1,2 @@
1
+ psutil<8.0.0,>=7.0.0
2
+ rainbow-rb-log==0.0.9.dev44
@@ -1,2 +0,0 @@
1
- psutil<8.0.0,>=7.0.0
2
- rainbow-rb-log==0.0.9.dev43