Jellyfin(8096), OrbStack(8097) 포트 충돌으로 변경. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
126 lines
4.7 KiB
Python
126 lines
4.7 KiB
Python
import json
|
|
import logging
|
|
import os
|
|
|
|
"""
|
|
This configuration parsing code was just copied from my plann library (and will be removed from there at some point in the future). Test coverage is poor as for now.
|
|
"""
|
|
|
|
## This is being moved from my plann library. The code itself will be introduced into caldav 2.0, but proper test code and documentation will come in a later release (2.1?)
|
|
|
|
|
|
## TODO TODO TODO - write test code for all the corner cases
|
|
## TODO TODO TODO - write documentation of config format
|
|
def expand_config_section(config, section="default", blacklist=None):
|
|
"""
|
|
In the "normal" case, will return [ section ]
|
|
|
|
We allow:
|
|
|
|
* * includes all sections in config file
|
|
* "Meta"-sections in the config file with the keyword "contains" followed by a list of section names
|
|
* Recursive "meta"-sections
|
|
* Glob patterns (work_* for all sections starting with work_)
|
|
* Glob patterns in "meta"-sections
|
|
"""
|
|
## Optimizating for a special case. The results should be the same without this optimization.
|
|
if section == "*":
|
|
return [x for x in config if not config[x].get("disable", False)]
|
|
|
|
## If it's not a glob-pattern ...
|
|
if set(section).isdisjoint(set("[*?")):
|
|
## If it's referring to a "meta section" with the "contains" keyword
|
|
if "contains" in config[section]:
|
|
results = []
|
|
if not blacklist:
|
|
blacklist = set()
|
|
blacklist.add(section)
|
|
for subsection in config[section]["contains"]:
|
|
if not subsection in results and not subsection in blacklist:
|
|
for recursivesubsection in expand_config_section(
|
|
config, subsection, blacklist
|
|
):
|
|
if not recursivesubsection in results:
|
|
results.append(recursivesubsection)
|
|
return results
|
|
else:
|
|
## Disabled sections should be ignored
|
|
if config.get("section", {}).get("disable", False):
|
|
return []
|
|
|
|
## NORMAL CASE - return [ section ]
|
|
return [section]
|
|
## section name is a glob pattern
|
|
matching_sections = [x for x in config if fnmatch(x, section)]
|
|
results = set()
|
|
for s in matching_sections:
|
|
if set(s).isdisjoint(set("[*?")):
|
|
results.update(expand_config_section(config, s))
|
|
else:
|
|
## Section names shouldn't contain []?* ... but in case they do ... don't recurse
|
|
results.add(s)
|
|
return results
|
|
|
|
|
|
def config_section(config, section="default"):
|
|
if section in config and "inherits" in config[section]:
|
|
ret = config_section(config, config[section]["inherits"])
|
|
else:
|
|
ret = {}
|
|
if section in config:
|
|
ret.update(config[section])
|
|
return ret
|
|
|
|
|
|
def read_config(fn, interactive_error=False):
|
|
if not fn:
|
|
cfgdir = f"{os.environ.get('HOME', '/')}/.config/"
|
|
for config_file in (
|
|
f"{cfgdir}/caldav/calendar.conf",
|
|
f"{cfgdir}/caldav/calendar.yaml",
|
|
f"{cfgdir}/caldav/calendar.json",
|
|
f"{cfgdir}/calendar.conf",
|
|
"/etc/calendar.conf",
|
|
"/etc/caldav/calendar.conf",
|
|
):
|
|
cfg = read_config(config_file)
|
|
if cfg:
|
|
return cfg
|
|
return None
|
|
|
|
## This can probably be refactored into fewer lines ...
|
|
try:
|
|
try:
|
|
with open(fn, "rb") as config_file:
|
|
return json.load(config_file)
|
|
except json.decoder.JSONDecodeError:
|
|
## Late import, wrapped in try/except. yaml is external module,
|
|
## and not included in the requirements as for now.
|
|
try:
|
|
import yaml
|
|
|
|
try:
|
|
with open(fn, "rb") as config_file:
|
|
return yaml.load(config_file, yaml.Loader)
|
|
except yaml.scanner.ScannerError:
|
|
logging.error(
|
|
f"config file {fn} exists but is neither valid json nor yaml. Check the syntax."
|
|
)
|
|
except ImportError:
|
|
logging.error(
|
|
f"config file {fn} exists but is not valid json, and pyyaml is not installed."
|
|
)
|
|
|
|
except FileNotFoundError:
|
|
## File not found
|
|
logging.info("no config file found")
|
|
except ValueError:
|
|
if interactive_error:
|
|
logging.error(
|
|
"error in config file. Be aware that the interactive configuration will ignore and overwrite the current broken config file",
|
|
exc_info=True,
|
|
)
|
|
else:
|
|
logging.error("error in config file. It will be ignored", exc_info=True)
|
|
return {}
|