Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.8.22
1.8.23
31 changes: 19 additions & 12 deletions src/torchlight/Config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import json
import logging
import os
import sys
from collections import OrderedDict
from typing import Any


class ConfigError(Exception):
"""A config file is missing, unreadable or contains invalid JSON."""


class ConfigFile:
"""Resolves a config file path under the config folder and parses it as JSON."""

Expand All @@ -20,10 +23,19 @@ def __init__(
self.config_filepath = os.path.abspath(os.path.join(config_folder, config_filename))

def load_json(self, *, ordered: bool = False) -> Any:
with open(self.config_filepath) as fp:
if ordered:
return json.load(fp, object_pairs_hook=OrderedDict)
return json.load(fp)
try:
with open(self.config_filepath) as fp:
if ordered:
return json.load(fp, object_pairs_hook=OrderedDict)
return json.load(fp)
except FileNotFoundError as e:
raise ConfigError(f"{self.config_filepath}: config file not found") from e
except OSError as e:
raise ConfigError(f"{self.config_filepath}: cannot read config file ({e.strerror or e})") from e
except json.JSONDecodeError as e:
raise ConfigError(
f"{self.config_filepath}: invalid JSON on line {e.lineno}, column {e.colno} ({e.msg})"
) from e


class Config(ConfigFile):
Expand All @@ -35,13 +47,8 @@ def __init__(
super().__init__(config_folder, config_filename)
self.config: dict[str, Any] = {}

def load(self) -> int:
try:
self.config = self.load_json()
except ValueError as e:
self.logger.error(sys._getframe().f_code.co_name + " " + str(e))
return 1
return 0
def load(self) -> None:
self.config = self.load_json()

def __getitem__(self, key: str) -> Any:
if key in self.config:
Expand Down
10 changes: 2 additions & 8 deletions src/torchlight/Sourcemod.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import copy
import sys
from collections import OrderedDict
from dataclasses import dataclass

Expand Down Expand Up @@ -34,12 +33,8 @@ def __init__(
self.sm_flags: OrderedDict = OrderedDict()
self.sm_groups: list[SourcemodGroup] = []

def Load(self) -> int:
try:
self.sm_flags = self.load_json(ordered=True)
except ValueError as e:
self.logger.error(sys._getframe().f_code.co_name + " " + str(e))
return 1
def Load(self) -> None:
self.sm_flags = self.load_json(ordered=True)
self.sm_groups.clear()
for sm_group in self.config["SourcemodGroups"]:
self.sm_groups.append(
Expand All @@ -49,7 +44,6 @@ def Load(self) -> int:
flags=sm_group["flags"],
)
)
return 0

def flagbits_to_flags(self, *, flagbits: int) -> list[str]:
flags: list[str] = []
Expand Down
15 changes: 12 additions & 3 deletions src/torchlight/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import click

from torchlight.Config import Config
from torchlight.Config import Config, ConfigError
from torchlight.PlayerManager import PlayerManager
from torchlight.SourceRCONServer import SourceRCONServer
from torchlight.TorchlightHandler import TorchlightHandler
Expand All @@ -30,7 +30,13 @@ def graceful_shutdown(signal: int, frame: FrameType | None) -> None:
@click.version_option()
def cli(config_folder: str) -> None:
config = Config(config_folder)
config.load()

# A malformed or missing config file aborts startup with the offending path and
# parse error rather than silently starting with an empty config (see issue #164).
try:
config.load()
except ConfigError as e:
raise click.ClickException(str(e)) from e

logging.basicConfig(
level=logging.getLevelName(config["Logging"]["level"]),
Expand All @@ -44,7 +50,10 @@ def cli(config_folder: str) -> None:
event_loop = asyncio.get_event_loop()

global torchlight_handler
torchlight_handler = TorchlightHandler(event_loop, config)
try:
torchlight_handler = TorchlightHandler(event_loop, config)
except ConfigError as e:
raise click.ClickException(str(e)) from e

# Handles new connections on 0.0.0.0:27015
rcon_server = SourceRCONServer(
Expand Down
Loading