Skip to content

Commit 3cd49d2

Browse files
committed
refactor: config logic
1 parent 428a8fb commit 3cd49d2

16 files changed

Lines changed: 236 additions & 76 deletions

asgi_webdav/config.py

Lines changed: 53 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
AppEntryParameters,
2828
DAVCompressLevel,
2929
)
30-
from asgi_webdav.exception import DAVExceptionConfigFileNotFound
3130

3231
logger = getLogger(__name__)
3332

@@ -300,14 +299,7 @@ def update_from_app_args_and_env_and_default_value(
300299
self._complete_config()
301300

302301

303-
_config: Config = Config()
304-
305-
306-
def get_config() -> Config:
307-
return _config
308-
309-
310-
def get_config_copy_from_dict(
302+
def generate_config_from_dict(
311303
data: dict[str, Any], complete_config: bool = False
312304
) -> Config:
313305
config = Config.from_dict(data)
@@ -317,21 +309,13 @@ def get_config_copy_from_dict(
317309
return config
318310

319311

320-
def reinit_config_from_dict(
321-
data: dict[str, Any], complete_config: bool = False
322-
) -> Config:
323-
global _config
324-
325-
logger.debug("Load config value from python object(dict)")
326-
_config = get_config_copy_from_dict(data, complete_config)
327-
328-
return _config
329-
330-
331-
def reinit_config_from_file(file_name: str, complete_config: bool = False) -> bool:
312+
def generate_config_from_file(
313+
file: Path | str, complete_config: bool = False
314+
) -> Config | None:
332315
load_func: Callable[[Any], Any]
316+
if not isinstance(file, Path):
317+
file = Path(file)
333318

334-
file = Path(file_name)
335319
match file.suffix:
336320
case ".json":
337321
load_func = json.load
@@ -340,54 +324,75 @@ def reinit_config_from_file(file_name: str, complete_config: bool = False) -> bo
340324
case _:
341325
message = f"Unsupported config file type: {file.suffix}"
342326
logger.error(message)
343-
return False
327+
return None
344328

345329
try:
346330
with open(file, "rb") as f:
347331
data = load_func(f)
348332

349333
except FileNotFoundError as e:
350-
message = f"Can not open config file[{file}]!"
351-
logger.error(message)
352-
logger.error(e)
353-
# return False
354-
raise DAVExceptionConfigFileNotFound(e)
334+
logger.error(f"Can not open config file[{file}]!, {e}")
335+
return None
355336

356337
except (json.JSONDecodeError, tomllib.TOMLDecodeError) as e:
357338
message = f"Load config from file[{file}] failed!"
358339
logger.error(message)
359340
logger.error(e)
360-
return False
341+
return None
361342

362-
reinit_config_from_dict(data, complete_config)
343+
config = generate_config_from_dict(data, complete_config)
363344
logger.info(f"Load config from file: [{file}] success!")
364-
return True
345+
return config
365346

366347

367-
def reinit_config_from_file_multi_suffix(
368-
file_name: str, complete_config: bool = False
369-
) -> bool:
348+
def generate_config_from_file_with_multi_suffix(
349+
file: Path | str, complete_config: bool = False
350+
) -> Config | None:
370351
"""help users in switching from .json to .toml configuration files."""
371352

372-
try:
373-
return reinit_config_from_file(file_name, complete_config)
374-
except DAVExceptionConfigFileNotFound:
375-
logger.warning(f"Can not found config file: {file_name}!")
353+
if not isinstance(file, Path):
354+
file = Path(file)
355+
356+
config = generate_config_from_file(file, complete_config)
357+
if config is not None:
358+
return config
376359

377360
# try other suffix
378-
file = Path(file_name)
379361
stem = file.stem
380362
suffix = file.suffix
381363

382364
suffixs = {".json", ".toml"}
383-
suffixs.remove(suffix)
365+
try:
366+
suffixs.remove(suffix)
367+
except KeyError as e:
368+
logger.warning(f"Wrong file extension: {suffix}, {e}")
384369

385370
for suffix in suffixs:
386-
file_name = file.parent.joinpath(f"{stem}{suffix}").as_posix()
387-
logger.warning(f"Try load config file: {file_name}!")
388-
try:
389-
return reinit_config_from_file(file_name, complete_config)
390-
except DAVExceptionConfigFileNotFound:
391-
logger.warning(f"Can not found config file: {file_name}!")
392-
393-
return False
371+
file = file.parent.joinpath(f"{stem}{suffix}")
372+
logger.warning(f"Try load config file: {file}!")
373+
374+
config = generate_config_from_file(file, complete_config)
375+
if config is None:
376+
logger.warning(f"Can not found config file: {file}!")
377+
else:
378+
# loaded
379+
return config
380+
381+
# all failed
382+
return None
383+
384+
385+
_config: Config = Config()
386+
387+
388+
def get_config() -> Config:
389+
return _config
390+
391+
392+
def reinit_global_config(config: Config | None = None) -> None:
393+
global _config
394+
395+
if config is None:
396+
config = Config()
397+
398+
_config = config

asgi_webdav/server.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
from asgi_webdav.auth import DAVAuth
1313
from asgi_webdav.config import (
1414
Config,
15+
generate_config_from_dict,
16+
generate_config_from_file_with_multi_suffix,
1517
get_config,
16-
reinit_config_from_dict,
17-
reinit_config_from_file_multi_suffix,
18+
reinit_global_config,
1819
)
1920
from asgi_webdav.constants import AppEntryParameters, DAVMethod, DevMode
2021
from asgi_webdav.exception import DAVExceptionProviderInitFailed
@@ -109,14 +110,19 @@ def get_asgi_app(aep: AppEntryParameters, config_obj: dict[str, Any] | None = No
109110
logging.config.dictConfig(get_dav_logging_config(config=get_config()))
110111

111112
# init config
113+
config: Config | None = None
112114
if aep.config_file is not None:
113-
reinit_config_from_file_multi_suffix(aep.config_file)
115+
config = generate_config_from_file_with_multi_suffix(aep.config_file)
114116
elif config_obj is not None:
115-
reinit_config_from_dict(config_obj)
117+
config = generate_config_from_dict(config_obj)
118+
119+
if config is None:
120+
raise
116121

117-
config = get_config()
118122
config.update_from_app_args_and_env_and_default_value(aep=aep)
119123

124+
reinit_global_config(config)
125+
120126
# init logging
121127
if config.logging.enable:
122128
logging.config.dictConfig(get_dav_logging_config(config=config))

examples/config/decode-failed.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
decode failed

examples/config/no-support.ext

Whitespace-only changes.

tests/test_auth.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66

77
from asgi_webdav.auth import DAVAuth, DAVPassword, DAVPasswordType
88
from asgi_webdav.cache import DAVCacheType
9-
from asgi_webdav.config import Config, get_config_copy_from_dict
9+
from asgi_webdav.config import Config, generate_config_from_dict
1010
from asgi_webdav.constants import DAVPath, DAVUser
1111
from asgi_webdav.request import DAVRequest
1212

13-
from .asgi_test_kit import ASGITestClient, create_dav_request_object, get_webdav_app
13+
from .testkit_asgi import ASGITestClient, create_dav_request_object, get_webdav_app
1414

1515
USERNAME = "username"
1616
PASSWORD = "password"
@@ -431,7 +431,7 @@ def get_dav_request(extra_headers: dict[str, str]) -> DAVRequest:
431431
@pytest.mark.asyncio
432432
async def test_dav_auth_pick_out_user_anonymous_user():
433433
# anonymous user : ok
434-
config = get_config_copy_from_dict(
434+
config = generate_config_from_dict(
435435
BASIC_AUTHORIZATION_CONFIG_DATA_FOR_ANONYMOUS_USER_DEFAULT, complete_config=True
436436
)
437437
dav_auth = DAVAuth(config)
@@ -456,7 +456,7 @@ async def test_dav_auth_pick_out_user_anonymous_user():
456456
assert message is not None
457457

458458
# anonymous user : disable
459-
config = get_config_copy_from_dict(
459+
config = generate_config_from_dict(
460460
BASIC_AUTHORIZATION_CONFIG_DATA_FOR_ANONYMOUS_USER_DISABLE, complete_config=True
461461
)
462462
dav_auth = DAVAuth(config)
@@ -474,7 +474,7 @@ async def test_dav_auth_pick_out_user_anonymous_user():
474474
assert message is not None
475475

476476
# anonymous user: allow_missing_auth_header is False
477-
config = get_config_copy_from_dict(
477+
config = generate_config_from_dict(
478478
BASIC_AUTHORIZATION_CONFIG_DATA_FOR_ANONYMOUS_USER_ALLOW_MISSING_AUTH_HEADER_FALSE,
479479
complete_config=True,
480480
)

tests/test_config.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import pytest
2+
3+
from asgi_webdav.config import (
4+
Config,
5+
LoggingLevel,
6+
generate_config_from_dict,
7+
generate_config_from_file,
8+
generate_config_from_file_with_multi_suffix,
9+
get_config,
10+
reinit_global_config,
11+
)
12+
from asgi_webdav.constants import (
13+
DEFAULT_PASSWORD_ANONYMOUS,
14+
DEFAULT_PERMISSIONS,
15+
DEFAULT_USERNAME_ANONYMOUS,
16+
)
17+
18+
from .testkit_common import get_project_root_path
19+
20+
EXAMPLE_CONFIG_ROOT_PATH = get_project_root_path().joinpath("examples/config")
21+
22+
TEST_PERMISSIONS = ["+^/$"]
23+
24+
25+
@pytest.fixture
26+
def default_config():
27+
config = Config()
28+
29+
yield config
30+
31+
pass
32+
33+
34+
def test_defalut_config(default_config):
35+
config = default_config
36+
assert config.anonymous.enable is False
37+
assert config.anonymous.user.username == DEFAULT_USERNAME_ANONYMOUS
38+
assert config.anonymous.user.password == DEFAULT_PASSWORD_ANONYMOUS
39+
assert config.anonymous.user.permissions == DEFAULT_PERMISSIONS
40+
41+
assert config.logging.enable is True
42+
assert config.logging.level == LoggingLevel.INFO
43+
44+
45+
def test_generate_config_from_dict():
46+
config = generate_config_from_dict(
47+
{
48+
"anonymous": {
49+
"enable": True,
50+
"user": {
51+
"username": "test_user",
52+
"password": "test_password",
53+
"permissions": TEST_PERMISSIONS,
54+
},
55+
},
56+
"logging": {
57+
"enable": False,
58+
"level": "DEBUG",
59+
},
60+
}
61+
)
62+
63+
assert config.anonymous.enable is True
64+
assert config.anonymous.user.username == "test_user"
65+
assert config.anonymous.user.password == "test_password"
66+
assert config.anonymous.user.permissions == TEST_PERMISSIONS
67+
68+
69+
def test_generate_config_from_file():
70+
71+
config = generate_config_from_file(
72+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("anonymous-limited-permission.toml")
73+
)
74+
75+
assert config.anonymous.enable is True
76+
assert config.anonymous.user.permissions == TEST_PERMISSIONS
77+
78+
# no file
79+
assert generate_config_from_file("no-file.toml") is None
80+
assert generate_config_from_file("no-file.json") is None
81+
82+
# not support file
83+
assert (
84+
generate_config_from_file(EXAMPLE_CONFIG_ROOT_PATH.joinpath("no-support.ext"))
85+
is None
86+
)
87+
88+
# decode failed
89+
assert (
90+
generate_config_from_file(
91+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("decode-failed.json")
92+
)
93+
is None
94+
)
95+
96+
97+
def test_generate_config_from_file_with_multi_suffix():
98+
assert (
99+
generate_config_from_file_with_multi_suffix(
100+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("anonymous-enable.toml")
101+
).anonymous.enable
102+
is True
103+
)
104+
105+
assert (
106+
generate_config_from_file_with_multi_suffix(
107+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("anonymous-enable.toml").as_posix()
108+
).anonymous.enable
109+
is True
110+
)
111+
112+
assert (
113+
generate_config_from_file_with_multi_suffix(
114+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("anonymous-enable.json")
115+
).anonymous.enable
116+
is True
117+
)
118+
119+
assert (
120+
generate_config_from_file_with_multi_suffix(
121+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("no-support.ext")
122+
)
123+
is None
124+
)
125+
126+
127+
def test_global_config():
128+
# reset
129+
reinit_global_config()
130+
131+
config = get_config()
132+
assert config.anonymous.enable is False
133+
134+
# set
135+
reinit_global_config(
136+
generate_config_from_file_with_multi_suffix(
137+
EXAMPLE_CONFIG_ROOT_PATH.joinpath("anonymous-enable.toml")
138+
)
139+
)
140+
141+
# check
142+
config = get_config()
143+
assert config.anonymous.enable is True

tests/test_dav_lock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from asgi_webdav.constants import DAVPath
66
from asgi_webdav.lock import DAVLock, DAVLockInfo, DAVLockScope
77

8-
from .asgi_test_kit import create_dav_request_object
8+
from .testkit_asgi import create_dav_request_object
99

1010

1111
def create_request(path: str):

tests/test_middleware_cors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from asgi_webdav.middleware.cors import ASGIMiddlewareCORS
55

6-
from .asgi_test_kit import ASGIApp, ASGITestClient
6+
from .testkit_asgi import ASGIApp, ASGITestClient
77

88

99
def get_middleware_app(middleware, **kwargs):

0 commit comments

Comments
 (0)