pferd/PFERD/__main__.py

150 lines
4.7 KiB
Python
Raw Normal View History

2021-05-13 23:52:46 +02:00
import argparse
import asyncio
2021-05-15 21:33:51 +02:00
import configparser
import sys
2021-05-13 23:52:46 +02:00
from pathlib import Path
from .cli import PARSER, load_default_section
from .config import Config, ConfigDumpError, ConfigLoadError, ConfigOptionError
from .logging import log
from .pferd import Pferd, PferdLoadError
2021-05-22 22:38:56 +02:00
from .transformer import RuleParseError
2021-05-13 23:52:46 +02:00
2021-05-15 21:33:51 +02:00
def load_config_parser(args: argparse.Namespace) -> configparser.ConfigParser:
2021-05-19 18:10:17 +02:00
log.explain_topic("Loading config")
2021-05-15 21:33:51 +02:00
parser = configparser.ConfigParser()
if args.command is None:
2021-05-19 18:10:17 +02:00
log.explain("No CLI command specified, loading config from file")
2021-05-15 21:33:51 +02:00
Config.load_parser(parser, path=args.config)
2021-05-19 18:10:17 +02:00
else:
2021-05-26 10:52:04 +02:00
log.explain("CLI command specified, loading config from its arguments")
if args.command:
args.command(args, parser)
2021-05-15 21:33:51 +02:00
load_default_section(args, parser)
2021-05-15 21:33:51 +02:00
return parser
def load_config(args: argparse.Namespace) -> Config:
try:
return Config(load_config_parser(args))
except ConfigLoadError as e:
log.error(str(e))
log.error_contd(e.reason)
sys.exit(1)
2021-05-13 23:52:46 +02:00
def configure_logging_from_args(args: argparse.Namespace) -> None:
if args.explain is not None:
log.output_explain = args.explain
2021-05-23 22:41:59 +02:00
if args.status is not None:
log.output_status = args.status
if args.report is not None:
log.output_report = args.report
# We want to prevent any unnecessary output if we're printing the config to
# stdout, otherwise it would not be a valid config file.
if args.dump_config_to == "-":
2021-05-19 18:10:17 +02:00
log.output_explain = False
2021-05-23 22:41:59 +02:00
log.output_status = False
log.output_report = False
def configure_logging_from_config(args: argparse.Namespace, config: Config) -> None:
# In configure_logging_from_args(), all normal logging is already disabled
# whenever we dump the config. We don't want to override that decision with
# values from the config file.
if args.dump_config_to == "-":
return
try:
if args.explain is None:
log.output_explain = config.default_section.explain()
2021-05-23 22:41:59 +02:00
if args.status is None:
log.output_status = config.default_section.status()
if args.report is None:
log.output_report = config.default_section.report()
except ConfigOptionError as e:
log.error(str(e))
sys.exit(1)
def dump_config(args: argparse.Namespace, config: Config) -> None:
2021-05-23 11:04:50 +02:00
log.explain_topic("Dumping config")
if args.dump_config and args.dump_config_to is not None:
log.error("--dump-config and --dump-config-to can't be specified at the same time")
sys.exit(1)
try:
if args.dump_config:
config.dump()
elif args.dump_config_to == "-":
config.dump_to_stdout()
else:
config.dump(Path(args.dump_config_to))
except ConfigDumpError as e:
log.error(str(e))
log.error_contd(e.reason)
sys.exit(1)
def main() -> None:
args = PARSER.parse_args()
# Configuring logging happens in two stages because CLI args have
# precedence over config file options and loading the config already
# produces some kinds of log messages (usually only explain()-s).
configure_logging_from_args(args)
2021-05-13 23:52:46 +02:00
config = load_config(args)
# Now, after loading the config file, we can apply its logging settings in
# all places that were not already covered by CLI args.
configure_logging_from_config(args, config)
if args.dump_config or args.dump_config_to is not None:
dump_config(args, config)
sys.exit()
2021-05-13 23:52:46 +02:00
2021-05-22 21:05:32 +02:00
try:
pferd = Pferd(config, args.crawler)
except PferdLoadError as e:
log.unlock()
2021-05-22 21:05:32 +02:00
log.error(str(e))
sys.exit(1)
try:
loop = asyncio.get_event_loop()
2021-05-26 11:37:32 +02:00
loop.run_until_complete(pferd.run(args.debug_transforms))
loop.run_until_complete(asyncio.sleep(1))
loop.close()
except ConfigOptionError as e:
log.unlock()
log.error(str(e))
sys.exit(1)
2021-05-22 22:38:56 +02:00
except RuleParseError as e:
log.unlock()
e.pretty_print()
sys.exit(1)
2021-05-22 14:45:32 +02:00
except KeyboardInterrupt:
log.unlock()
log.explain_topic("Interrupted, exiting immediately")
log.explain("Open files and connections are left for the OS to clean up")
log.explain("Temporary files are not cleaned up")
2021-05-25 15:42:46 +02:00
pferd.print_report()
2021-05-22 14:45:32 +02:00
# TODO Clean up tmp files
# And when those files *do* actually get cleaned up properly,
2021-05-25 15:42:46 +02:00
# reconsider if this should really exit with 1
sys.exit(1)
except Exception:
log.unlock()
log.unexpected_exception()
2021-05-25 15:42:46 +02:00
pferd.print_report()
sys.exit(1)
else:
pferd.print_report()