ddns 4.0.0b2__tar.gz → 4.0.0b3__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.
Potentially problematic release.
This version of ddns might be problematic. Click here for more details.
- {ddns-4.0.0b2 → ddns-4.0.0b3}/PKG-INFO +1 -1
- {ddns-4.0.0b2 → ddns-4.0.0b3}/ddns.egg-info/PKG-INFO +1 -1
- {ddns-4.0.0b2 → ddns-4.0.0b3}/run.py +11 -11
- ddns-4.0.0b3/util/config.py +193 -0
- ddns-4.0.0b2/util/config.py +0 -169
- {ddns-4.0.0b2 → ddns-4.0.0b3}/LICENSE +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/README.md +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/ddns.egg-info/SOURCES.txt +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/ddns.egg-info/dependency_links.txt +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/ddns.egg-info/entry_points.txt +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/ddns.egg-info/top_level.txt +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/__init__.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/alidns.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/callback.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/cloudflare.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/dnscom.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/dnspod.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/dnspod_com.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/he.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/dns/huaweidns.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/setup.cfg +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/setup.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/util/__init__.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/util/cache.py +0 -0
- {ddns-4.0.0b2 → ddns-4.0.0b3}/util/ip.py +0 -0
|
@@ -19,7 +19,7 @@ from util import ip
|
|
|
19
19
|
from util.cache import Cache
|
|
20
20
|
from util.config import init_config, get_config
|
|
21
21
|
|
|
22
|
-
__version__ = "v4.0.0-
|
|
22
|
+
__version__ = "v4.0.0-beta3@2025-06-04T17:36:22+00:00" # CI 时会被Tag替换
|
|
23
23
|
__description__ = "automatically update DNS records to dynamic local IP [自动更新DNS记录指向本地IP]"
|
|
24
24
|
__doc__ = """
|
|
25
25
|
ddns[%s]
|
|
@@ -28,7 +28,7 @@ ddns[%s]
|
|
|
28
28
|
Copyright (c) New Future (MIT License)
|
|
29
29
|
""" % (__version__)
|
|
30
30
|
|
|
31
|
-
environ["DDNS_VERSION"] = "v4.0.0-
|
|
31
|
+
environ["DDNS_VERSION"] = "v4.0.0-beta3"
|
|
32
32
|
|
|
33
33
|
if getattr(sys, 'frozen', False):
|
|
34
34
|
# https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OpenSSL-Certificate
|
|
@@ -47,7 +47,7 @@ def get_ip(ip_type, index="default"):
|
|
|
47
47
|
debug("get_ip(%s, %s)", ip_type, index)
|
|
48
48
|
if index is False: # disabled
|
|
49
49
|
return False
|
|
50
|
-
elif isinstance(index, list): #
|
|
50
|
+
elif isinstance(index, list): # 如果获取到的规则是列表,则依次判断列表中每一个规则,直到获取到IP
|
|
51
51
|
for i in index:
|
|
52
52
|
value = get_ip(ip_type, i)
|
|
53
53
|
if value:
|
|
@@ -66,7 +66,7 @@ def get_ip(ip_type, index="default"):
|
|
|
66
66
|
else:
|
|
67
67
|
value = getattr(ip, index + "_v" + ip_type)()
|
|
68
68
|
except Exception as e:
|
|
69
|
-
error(e)
|
|
69
|
+
error("Failed to get %s address: %s", ip_type, e)
|
|
70
70
|
return value
|
|
71
71
|
|
|
72
72
|
|
|
@@ -81,7 +81,7 @@ def change_dns_record(dns, proxy_list, **kw):
|
|
|
81
81
|
try:
|
|
82
82
|
return dns.update_record(domain, kw['ip'], record_type=record_type)
|
|
83
83
|
except Exception as e:
|
|
84
|
-
error(e)
|
|
84
|
+
error("Failed to update %s record for %s: %s", record_type, domain, e)
|
|
85
85
|
return False
|
|
86
86
|
|
|
87
87
|
|
|
@@ -136,11 +136,11 @@ def main():
|
|
|
136
136
|
|
|
137
137
|
info("DDNS[ %s ] run: %s %s", __version__, os_name, sys.platform)
|
|
138
138
|
if get_config("config"):
|
|
139
|
-
info(
|
|
139
|
+
info('loaded Config from: %s', path.abspath(get_config('config')))
|
|
140
140
|
|
|
141
141
|
proxy = get_config('proxy') or 'DIRECT'
|
|
142
142
|
proxy_list = proxy if isinstance(
|
|
143
|
-
proxy, list) else proxy.strip(';
|
|
143
|
+
proxy, list) else proxy.strip(';').replace(',', ';').split(';')
|
|
144
144
|
|
|
145
145
|
cache_config = get_config('cache', True)
|
|
146
146
|
if cache_config is False:
|
|
@@ -151,12 +151,12 @@ def main():
|
|
|
151
151
|
cache = Cache(cache_config)
|
|
152
152
|
|
|
153
153
|
if cache is False:
|
|
154
|
-
info(
|
|
155
|
-
elif get_config(
|
|
156
|
-
warning(
|
|
154
|
+
info('Cache is disabled!')
|
|
155
|
+
elif not get_config('config_modified_time') or get_config('config_modified_time') >= cache.time:
|
|
156
|
+
warning('Cache file is out of dated.')
|
|
157
157
|
cache.clear()
|
|
158
158
|
else:
|
|
159
|
-
debug(
|
|
159
|
+
debug('Cache is empty.')
|
|
160
160
|
update_ip('4', cache, dns, proxy_list)
|
|
161
161
|
update_ip('6', cache, dns, proxy_list)
|
|
162
162
|
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter
|
|
4
|
+
from json import load as loadjson, dump as dumpjson
|
|
5
|
+
from os import stat, environ, path
|
|
6
|
+
from logging import error, getLevelName
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__cli_args = Namespace()
|
|
12
|
+
__config = {} # type: dict
|
|
13
|
+
log_levels = ['CRITICAL', 'FATAL', 'ERROR',
|
|
14
|
+
'WARN', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def str2bool(v):
|
|
18
|
+
"""
|
|
19
|
+
parse string to boolean
|
|
20
|
+
"""
|
|
21
|
+
if isinstance(v, bool):
|
|
22
|
+
return v
|
|
23
|
+
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
|
24
|
+
return True
|
|
25
|
+
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
|
|
26
|
+
return False
|
|
27
|
+
else:
|
|
28
|
+
return v
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def log_level(value):
|
|
32
|
+
"""
|
|
33
|
+
parse string to log level
|
|
34
|
+
"""
|
|
35
|
+
return getLevelName(value.upper())
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def init_config(description, doc, version):
|
|
39
|
+
"""
|
|
40
|
+
配置
|
|
41
|
+
"""
|
|
42
|
+
global __cli_args
|
|
43
|
+
parser = ArgumentParser(description=description,
|
|
44
|
+
epilog=doc, formatter_class=RawTextHelpFormatter)
|
|
45
|
+
parser.add_argument('-v', '--version',
|
|
46
|
+
action='version', version=version)
|
|
47
|
+
parser.add_argument('-c', '--config', help='run with config file [配置文件路径]')
|
|
48
|
+
|
|
49
|
+
# 参数定义
|
|
50
|
+
parser.add_argument('--dns', help='DNS Provider [DNS服务提供商]', choices=[
|
|
51
|
+
'alidns', 'cloudflare', 'dnscom', 'dnspod', 'dnspod_com', 'he', 'huaweidns', 'callback'])
|
|
52
|
+
parser.add_argument('--id', help='api ID [授权账户]')
|
|
53
|
+
parser.add_argument('--token', help='api token or Secret key [授权访问凭证或密钥]')
|
|
54
|
+
parser.add_argument('--index4', nargs='*', action=ExtendAction,
|
|
55
|
+
help='list to get ipv4 [IPV4 获取方式]')
|
|
56
|
+
parser.add_argument('--index6', nargs='*', action=ExtendAction,
|
|
57
|
+
help='list to get ipv6 [IPV6获取方式]')
|
|
58
|
+
parser.add_argument('--ipv4', nargs='*', action=ExtendAction,
|
|
59
|
+
help='ipv4 domain list [IPV4域名列表]')
|
|
60
|
+
parser.add_argument('--ipv6', nargs='*', action=ExtendAction,
|
|
61
|
+
help='ipv6 domain list [IPV6域名列表]')
|
|
62
|
+
parser.add_argument('--ttl', type=int, help='ttl for DNS [DNS 解析 TTL 时间]')
|
|
63
|
+
parser.add_argument('--proxy', nargs='*', action=ExtendAction,
|
|
64
|
+
help='https proxy [设置http 代理,多代理逐个尝试直到成功]')
|
|
65
|
+
parser.add_argument('--cache', type=str2bool, nargs='?',
|
|
66
|
+
const=True, help='cache flag [启用缓存,可配配置路径或开关]')
|
|
67
|
+
parser.add_argument('--log.file', metavar='LOG_FILE',
|
|
68
|
+
help='log file [日志文件,默认标准输出]')
|
|
69
|
+
parser.add_argument('--log.level', type=log_level,
|
|
70
|
+
metavar='|'.join(log_levels))
|
|
71
|
+
|
|
72
|
+
__cli_args = parser.parse_args()
|
|
73
|
+
is_configfile_required = not get_config("token") and not get_config("id")
|
|
74
|
+
config_file = get_config("config")
|
|
75
|
+
if not config_file:
|
|
76
|
+
# 未指定配置文件且需要读取文件时,依次查找
|
|
77
|
+
cfgs = [
|
|
78
|
+
path.abspath('config.json'),
|
|
79
|
+
path.expanduser('~/.ddns/config.json'),
|
|
80
|
+
'/etc/ddns/config.json'
|
|
81
|
+
]
|
|
82
|
+
config_file = next((cfg for cfg in cfgs if path.isfile(cfg)), cfgs[0])
|
|
83
|
+
|
|
84
|
+
if path.isfile(config_file):
|
|
85
|
+
__load_config(config_file)
|
|
86
|
+
__cli_args.config = config_file
|
|
87
|
+
elif is_configfile_required:
|
|
88
|
+
error('Config file is required, but not found: %s', config_file)
|
|
89
|
+
# 如果需要配置文件但没有指定,则自动生成
|
|
90
|
+
if generate_config(config_file):
|
|
91
|
+
sys.stdout.write(
|
|
92
|
+
'Default configure file %s is generated.\n' % config_file)
|
|
93
|
+
sys.exit(1)
|
|
94
|
+
else:
|
|
95
|
+
sys.exit('fail to load config from file: %s\n' % config_file)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def __load_config(config_path):
|
|
99
|
+
"""
|
|
100
|
+
加载配置
|
|
101
|
+
"""
|
|
102
|
+
global __config
|
|
103
|
+
try:
|
|
104
|
+
with open(config_path, 'r') as configfile:
|
|
105
|
+
__config = loadjson(configfile)
|
|
106
|
+
__config["config_modified_time"] = stat(config_path).st_mtime
|
|
107
|
+
if 'log' in __config:
|
|
108
|
+
if 'level' in __config['log'] and __config['log']['level'] is not None:
|
|
109
|
+
__config['log.level'] = log_level(__config['log']['level'])
|
|
110
|
+
if 'file' in __config['log']:
|
|
111
|
+
__config['log.file'] = __config['log']['file']
|
|
112
|
+
elif 'log.level' in __config:
|
|
113
|
+
__config['log.level'] = log_level(__config['log.level'])
|
|
114
|
+
except Exception as e:
|
|
115
|
+
error('Failed to load config file `%s`: %s', config_path, e)
|
|
116
|
+
raise
|
|
117
|
+
# 重新抛出异常
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_config(key, default=None):
|
|
121
|
+
"""
|
|
122
|
+
读取配置
|
|
123
|
+
1. 命令行参数
|
|
124
|
+
2. 配置文件
|
|
125
|
+
3. 环境变量
|
|
126
|
+
"""
|
|
127
|
+
if hasattr(__cli_args, key) and getattr(__cli_args, key) is not None:
|
|
128
|
+
return getattr(__cli_args, key)
|
|
129
|
+
if key in __config:
|
|
130
|
+
return __config.get(key)
|
|
131
|
+
env_name = 'DDNS_' + key.replace('.', '_') # type:str
|
|
132
|
+
if env_name in environ: # 环境变量
|
|
133
|
+
return environ.get(env_name)
|
|
134
|
+
elif env_name.upper() in environ: # 大写环境变量
|
|
135
|
+
return environ.get(env_name.upper())
|
|
136
|
+
elif env_name.lower() in environ: # 小写环境变量
|
|
137
|
+
return environ.get(env_name.lower())
|
|
138
|
+
return default
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class ExtendAction(Action):
|
|
142
|
+
"""
|
|
143
|
+
兼容 Python <3.8 的 extend action
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
def __call__(self, parser, namespace, values, option_string=None):
|
|
147
|
+
items = getattr(namespace, self.dest, None)
|
|
148
|
+
if items is None:
|
|
149
|
+
items = []
|
|
150
|
+
# values 可能是单个值或列表
|
|
151
|
+
if isinstance(values, list):
|
|
152
|
+
items.extend(values)
|
|
153
|
+
else:
|
|
154
|
+
items.append(values)
|
|
155
|
+
setattr(namespace, self.dest, items)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def generate_config(config_path):
|
|
159
|
+
"""
|
|
160
|
+
生成配置文件
|
|
161
|
+
"""
|
|
162
|
+
configure = {
|
|
163
|
+
'$schema': 'https://ddns.newfuture.cc/schema/v4.0.json',
|
|
164
|
+
'id': 'YOUR ID or EMAIL for DNS Provider',
|
|
165
|
+
'token': 'YOUR TOKEN or KEY for DNS Provider',
|
|
166
|
+
'dns': 'dnspod',
|
|
167
|
+
'ipv4': [
|
|
168
|
+
'newfuture.cc',
|
|
169
|
+
'ddns.newfuture.cc'
|
|
170
|
+
],
|
|
171
|
+
'ipv6': [
|
|
172
|
+
'newfuture.cc',
|
|
173
|
+
'ipv6.ddns.newfuture.cc'
|
|
174
|
+
],
|
|
175
|
+
'index4': 'default',
|
|
176
|
+
'index6': 'default',
|
|
177
|
+
'ttl': None,
|
|
178
|
+
'proxy': None,
|
|
179
|
+
'log': {
|
|
180
|
+
'level': 'INFO',
|
|
181
|
+
'file': None
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
try:
|
|
185
|
+
with open(config_path, 'w') as f:
|
|
186
|
+
dumpjson(configure, f, indent=2, sort_keys=True)
|
|
187
|
+
return True
|
|
188
|
+
except IOError:
|
|
189
|
+
error('Cannot open config file to write: `%s`!', config_path)
|
|
190
|
+
return False
|
|
191
|
+
except Exception as e:
|
|
192
|
+
error('Failed to write config file `%s`: %s', config_path, e)
|
|
193
|
+
return False
|
ddns-4.0.0b2/util/config.py
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding:utf-8 -*-
|
|
3
|
-
from argparse import Action, ArgumentParser, Namespace, RawTextHelpFormatter
|
|
4
|
-
from json import load as loadjson, dump as dumpjson
|
|
5
|
-
from os import stat, environ
|
|
6
|
-
from logging import error, getLevelName
|
|
7
|
-
|
|
8
|
-
from time import time
|
|
9
|
-
|
|
10
|
-
import sys
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
__cli_args = Namespace()
|
|
14
|
-
__config = {} # type: dict
|
|
15
|
-
log_levels = ['CRITICAL', 'FATAL', 'ERROR',
|
|
16
|
-
'WARN', 'WARNING', 'INFO', 'DEBUG', 'NOTSET']
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def str2bool(v):
|
|
20
|
-
"""
|
|
21
|
-
parse string to boolean
|
|
22
|
-
"""
|
|
23
|
-
if isinstance(v, bool):
|
|
24
|
-
return v
|
|
25
|
-
if v.lower() in ('yes', 'true', 't', 'y', '1'):
|
|
26
|
-
return True
|
|
27
|
-
elif v.lower() in ('no', 'false', 'f', 'n', '0'):
|
|
28
|
-
return False
|
|
29
|
-
else:
|
|
30
|
-
return v
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def log_level(value):
|
|
34
|
-
"""
|
|
35
|
-
parse string to log level
|
|
36
|
-
"""
|
|
37
|
-
return getLevelName(value.upper())
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def init_config(description, doc, version):
|
|
41
|
-
"""
|
|
42
|
-
配置
|
|
43
|
-
"""
|
|
44
|
-
global __cli_args
|
|
45
|
-
parser = ArgumentParser(description=description,
|
|
46
|
-
epilog=doc, formatter_class=RawTextHelpFormatter)
|
|
47
|
-
parser.add_argument('-v', '--version',
|
|
48
|
-
action='version', version=version)
|
|
49
|
-
parser.add_argument('-c', '--config', help="run with config file [配置文件路径]")
|
|
50
|
-
|
|
51
|
-
# 参数定义
|
|
52
|
-
parser.add_argument('--dns', help="DNS Provider [DNS服务提供商]", choices=[
|
|
53
|
-
'alidns', 'cloudflare', 'dnscom', 'dnspod', 'dnspod_com', 'he', 'huaweidns', 'callback'])
|
|
54
|
-
parser.add_argument('--id', help="api ID [授权账户]")
|
|
55
|
-
parser.add_argument('--token', help="api token or Secret key [授权访问凭证或密钥]")
|
|
56
|
-
parser.add_argument('--index4', nargs="*", action=ExtendAction,
|
|
57
|
-
help="list to get ipv4 [IPV4 获取方式]")
|
|
58
|
-
parser.add_argument('--index6', nargs="*", action=ExtendAction,
|
|
59
|
-
help="list to get ipv6 [IPV6获取方式]")
|
|
60
|
-
parser.add_argument('--ipv4', nargs="*", action=ExtendAction,
|
|
61
|
-
help="ipv4 domain list [IPV4域名列表]")
|
|
62
|
-
parser.add_argument('--ipv6', nargs="*", action=ExtendAction,
|
|
63
|
-
help="ipv6 domain list [IPV6域名列表]")
|
|
64
|
-
parser.add_argument('--ttl', type=int, help="ttl for DNS [DNS 解析 TTL 时间]")
|
|
65
|
-
parser.add_argument('--proxy', nargs="*", action=ExtendAction,
|
|
66
|
-
help="https proxy [设置http 代理,多代理逐个尝试直到成功]")
|
|
67
|
-
parser.add_argument('--cache', type=str2bool, nargs='?',
|
|
68
|
-
const=True, help="cache flag [启用缓存,可配配置路径或开关]")
|
|
69
|
-
parser.add_argument('--log.file', metavar="LOG_FILE",
|
|
70
|
-
help="log file [日志文件,默认标准输出]")
|
|
71
|
-
parser.add_argument('--log.level', type=log_level,
|
|
72
|
-
metavar="|".join(log_levels))
|
|
73
|
-
|
|
74
|
-
__cli_args = parser.parse_args()
|
|
75
|
-
is_configfile_optional = get_config("token") or get_config("id")
|
|
76
|
-
config_file = get_config("config")
|
|
77
|
-
if not is_configfile_optional or config_file is not None:
|
|
78
|
-
__load_config(config_file or "config.json", is_configfile_optional)
|
|
79
|
-
__cli_args.config = config_file or "config.json"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def __load_config(path="config.json", skip_auto_generation=False):
|
|
83
|
-
"""
|
|
84
|
-
加载配置
|
|
85
|
-
"""
|
|
86
|
-
global __config
|
|
87
|
-
try:
|
|
88
|
-
with open(path) as configfile:
|
|
89
|
-
__config = loadjson(configfile)
|
|
90
|
-
__config["config_modified_time"] = stat(path).st_mtime
|
|
91
|
-
if 'log' in __config:
|
|
92
|
-
if 'level' in __config['log'] and __config['log']['level'] is not None:
|
|
93
|
-
__config['log.level'] = log_level(__config['log']['level'])
|
|
94
|
-
if 'file' in __config['log']:
|
|
95
|
-
__config['log.file'] = __config['log']['file']
|
|
96
|
-
elif 'log.level' in __config:
|
|
97
|
-
__config['log.level'] = log_level(__config['log.level'])
|
|
98
|
-
except IOError:
|
|
99
|
-
if skip_auto_generation:
|
|
100
|
-
__config["config_modified_time"] = time()
|
|
101
|
-
return
|
|
102
|
-
error(' Config file `%s` does not exist!' % path)
|
|
103
|
-
with open(path, 'w') as configfile:
|
|
104
|
-
configure = {
|
|
105
|
-
"$schema": "https://ddns.newfuture.cc/schema/v4.0.json",
|
|
106
|
-
"id": "YOUR ID or EMAIL for DNS Provider",
|
|
107
|
-
"token": "YOUR TOKEN or KEY for DNS Provider",
|
|
108
|
-
"dns": "dnspod",
|
|
109
|
-
"ipv4": [
|
|
110
|
-
"newfuture.cc",
|
|
111
|
-
"ddns.newfuture.cc"
|
|
112
|
-
],
|
|
113
|
-
"ipv6": [
|
|
114
|
-
"newfuture.cc",
|
|
115
|
-
"ipv6.ddns.newfuture.cc"
|
|
116
|
-
],
|
|
117
|
-
"index4": "default",
|
|
118
|
-
"index6": "default",
|
|
119
|
-
"ttl": None,
|
|
120
|
-
"proxy": None,
|
|
121
|
-
"log": {
|
|
122
|
-
"level": "INFO",
|
|
123
|
-
"file": None
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
dumpjson(configure, configfile, indent=2, sort_keys=True)
|
|
127
|
-
sys.stdout.write(
|
|
128
|
-
"New template configure file `%s` is generated.\n" % path)
|
|
129
|
-
sys.exit(1)
|
|
130
|
-
except Exception as e:
|
|
131
|
-
sys.exit('fail to load config from file: %s\n%s' % (path, e))
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def get_config(key, default=None):
|
|
135
|
-
"""
|
|
136
|
-
读取配置
|
|
137
|
-
1. 命令行参数
|
|
138
|
-
2. 配置文件
|
|
139
|
-
3. 环境变量
|
|
140
|
-
"""
|
|
141
|
-
if hasattr(__cli_args, key) and getattr(__cli_args, key) is not None:
|
|
142
|
-
return getattr(__cli_args, key)
|
|
143
|
-
if key in __config:
|
|
144
|
-
return __config.get(key)
|
|
145
|
-
env_name = 'DDNS_' + key.replace('.', '_') # type:str
|
|
146
|
-
if env_name in environ: # 环境变量
|
|
147
|
-
return environ.get(env_name)
|
|
148
|
-
elif env_name.upper() in environ: # 大写环境变量
|
|
149
|
-
return environ.get(env_name.upper())
|
|
150
|
-
elif env_name.lower() in environ: # 小写环境变量
|
|
151
|
-
return environ.get(env_name.lower())
|
|
152
|
-
return default
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class ExtendAction(Action):
|
|
156
|
-
"""
|
|
157
|
-
兼容 Python <3.8 的 extend action
|
|
158
|
-
"""
|
|
159
|
-
|
|
160
|
-
def __call__(self, parser, namespace, values, option_string=None):
|
|
161
|
-
items = getattr(namespace, self.dest, None)
|
|
162
|
-
if items is None:
|
|
163
|
-
items = []
|
|
164
|
-
# values 可能是单个值或列表
|
|
165
|
-
if isinstance(values, list):
|
|
166
|
-
items.extend(values)
|
|
167
|
-
else:
|
|
168
|
-
items.append(values)
|
|
169
|
-
setattr(namespace, self.dest, items)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|