Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
TobiX committed Jun 13, 2024
1 parent 2b7ca3f commit 04166a1
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 35 deletions.
4 changes: 1 addition & 3 deletions dosagelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,5 @@
__version__ = version(AppName) # PEP 396
except PackageNotFoundError:
# package is not installed
out.warn('{} is not installed, no version available.'
' Use at least {!r} or {!r} to fix this.'.format(
AppName, 'pip install -e .', 'setup.py egg_info'))
out.warn('{} is not installed, no version available.' ' Use at least {!r} or {!r} to fix this.'.format( AppName, 'pip install -e .', 'setup.py egg_info'))
__version__ = 'ERR.NOT.INSTALLED'
12 changes: 5 additions & 7 deletions dosagelib/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from platformdirs import PlatformDirs

from . import events, configuration, singleton, director
from . import configuration, director, events, singleton, logging
from . import AppName, __version__
from .output import out
from .scraper import scrapers as scrapercache
Expand Down Expand Up @@ -230,6 +230,7 @@ def vote_comic(scraperobj):
def run(options):
"""Execute comic commands."""
set_output_info(options)
logging.setup_console(options.verbose, options.timestamps)
scrapercache.adddir(user_plugin_path)
# ensure only one instance of dosage is running
if not options.allow_multiple:
Expand All @@ -256,8 +257,7 @@ def do_list(column_list=True, verbose=False, listall=False):
"""List available comics."""
with out.pager():
out.info(u'Available comic scrapers:')
out.info(u'Comics tagged with [{}] require age confirmation'
' with the --adult option.'.format(TAG_ADULT))
out.info(u'Comics tagged with [{}] require age confirmation' ' with the --adult option.'.format(TAG_ADULT))
out.info(u'Non-english comics are tagged with [%s].' % TAG_LANG)
scrapers = sorted(scrapercache.all(listall),
key=lambda s: s.name.lower())
Expand All @@ -268,8 +268,7 @@ def do_list(column_list=True, verbose=False, listall=False):
out.info(u'%d supported comics.' % num)
if disabled:
out.info('')
out.info(u'Some comics are disabled, they are tagged with'
' [{}:REASON], where REASON is one of:'.format(TAG_DISABLED))
out.info(u'Some comics are disabled, they are tagged with' ' [{}:REASON], where REASON is one of:'.format(TAG_DISABLED))
for k in disabled:
out.info(u' %-10s %s' % (k, disabled[k]))
return 0
Expand Down Expand Up @@ -298,8 +297,7 @@ def do_column_list(scrapers):
maxlen = max(len(name) for name in names)
names_per_line = max(width // (maxlen + 1), 1)
while names:
out.info(u''.join(name.ljust(maxlen) for name in
names[:names_per_line]))
out.info(u''.join(name.ljust(maxlen) for name in names[:names_per_line]))
del names[:names_per_line]
return num, disabled

Expand Down
3 changes: 1 addition & 2 deletions dosagelib/comic.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ def connect(self, lastchange=None):
if maintype == 'image':
self.ext = '.' + subtype.replace('jpeg', 'jpg')
self.contentLength = int(self.urlobj.headers.get('content-length', 0))
out.debug(u'... filename = %r, ext = %r, contentLength = %d' % (
self.filename, self.ext, self.contentLength))
out.debug(u'... filename = %r, ext = %r, contentLength = %d' % (self.filename, self.ext, self.contentLength))

def save(self, basepath):
"""Save comic URL to filename on disk."""
Expand Down
9 changes: 3 additions & 6 deletions dosagelib/director.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,7 @@ def saveComicStrip(self, strip):
if self.stopped:
break
except Exception as msg:
out.exception('Could not save image at {} to {}: {!r}'.format(
image.referrer, image.filename, msg))
out.exception('Could not save image at {} to {}: {!r}'.format(image.referrer, image.filename, msg))
self.errors += 1
return allskipped

Expand Down Expand Up @@ -245,11 +244,9 @@ def shouldRunScraper(scraperobj, adult=True, listing=False):

def warn_adult(scraperobj):
"""Print warning about adult content."""
out.warn(u"skipping adult comic {};"
" use the --adult option to confirm your age".format(scraperobj.name))
out.warn(u"skipping adult comic {};" " use the --adult option to confirm your age".format(scraperobj.name))


def warn_disabled(scraperobj, reasons):
"""Print warning about disabled comic modules."""
out.warn(u"Skipping comic {}: {}".format(
scraperobj.name, ' '.join(reasons.values())))
out.warn(u"Skipping comic {}: {}".format( scraperobj.name, ' '.join(reasons.values())))
65 changes: 65 additions & 0 deletions dosagelib/logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2024 Tobias Gruetzmacher
"""
Helpers for logging.
"""
import logging

from rich.console import ConsoleRenderable
from rich.logging import RichHandler
from rich.text import Text


def setup_console(level: int, timestamps: bool) -> None:
"""
Configure Python logging for simple Dosage console output. This tries to
emulate Dosage's legacy output style as much as possible.
Levels work roughly like this:
0 - The default, with shortened exception logging
1 - Enables verbose exception logging
2 - Enables DEBUG logging
3 - Enables TRACE logging
"""
logging.basicConfig(
level=translate_level(level),
format="%(threadName)s> %(message)s",
datefmt="%X",
handlers=[DosageRichHandler(show_time=timestamps)]
)

def translate_level(level: int) -> int:
if level < 2:
return logging.INFO
if level == 2:
return logging.DEBUG
return logging.NOTSET


class DosageRichHandler(RichHandler):
def __init__(self,
show_time: bool = True) -> None:
super().__init__(show_level=False, show_time=show_time, show_path=False)

def render_message(self, record: logging.LogRecord, message: str) -> ConsoleRenderable:
if record.levelno > logging.INFO:
message = f"{record.levelname.upper()}: {message}"
style = f"logging.level.{record.levelname.lower()}"
else:
style = ''

use_markup = getattr(record, "markup", self.markup)
message_text = Text.from_markup(message, style=style) if use_markup else Text(message,
style=style)

highlighter = getattr(record, "highlighter", self.highlighter)
if highlighter:
message_text = highlighter(message_text)

if self.keywords is None:
self.keywords = self.KEYWORDS

if self.keywords:
message_text.highlight_words(self.keywords, "logging.keyword")

return message_text
15 changes: 11 additions & 4 deletions dosagelib/output.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# SPDX-License-Identifier: MIT
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam
# Copyright (C) 2015-2020 Tobias Gruetzmacher
# SPDX-FileCopyrightText: © 2004 Tristan Seligmann and Jonathan Jacobs
# SPDX-FileCopyrightText: © 2012 Bastian Kleineidam
# SPDX-FileCopyrightText: © 2015 Tobias Gruetzmacher
import codecs
import contextlib
import io
import logging
import os
import pydoc
import sys
Expand All @@ -18,6 +19,7 @@
from colorama import Fore, Style


logger = logging.getLogger(__name__)
lock = threading.Lock()


Expand All @@ -26,7 +28,7 @@ def get_threadname():
return threading.current_thread().name


class Output(object):
class Output:
"""Print output with context, indentation and optional timestamps."""

DEFAULT_WIDTH = 80
Expand Down Expand Up @@ -55,20 +57,24 @@ def __init__(self, stream=None):
def info(self, s, level=0):
"""Write an informational message."""
self.write(s, level=level)
logger.info(s)

def debug(self, s, level=2):
"""Write a debug message."""
# "white" is the default color for most terminals...
self.write(s, level=level, color=Fore.WHITE)
logger.debug(s)

def warn(self, s, level=0):
"""Write a warning message."""
self.write(u"WARN: %s" % s, level=level, color=Style.BRIGHT +
Fore.YELLOW)
logger.warning(s)

def error(self, s, level=0):
"""Write an error message."""
self.write(u"ERROR: %s" % s, level=level, color=Style.DIM + Fore.RED)
logger.error(s)

def exception(self, s):
"""Write error message with traceback info."""
Expand All @@ -77,6 +83,7 @@ def exception(self, s):
self.writelines(traceback.format_stack(), 1)
self.writelines(traceback.format_tb(tb)[1:], 1)
self.writelines(traceback.format_exception_only(type, value), 1)
logger.exeception(s)

def write(self, s, level=0, color=None):
"""Write message with indentation, context and optional timestamp."""
Expand Down
19 changes: 6 additions & 13 deletions dosagelib/scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,12 @@ def getComicStrip(self, url, data) -> ComicStrip:
# remove duplicate URLs
urls = uniq(urls)
if len(urls) > 1 and not self.multipleImagesPerStrip:
out.warn(
u"Found %d images instead of 1 at %s with expressions %s" %
(len(urls), url, prettyMatcherList(self.imageSearch)))
out.warn( u"Found %d images instead of 1 at %s with expressions %s" % (len(urls), url, prettyMatcherList(self.imageSearch)))
image = urls[0]
out.warn("Choosing image %s" % image)
urls = (image,)
elif not urls:
out.warn("Found no images at %s with expressions %s" % (url,
prettyMatcherList(self.imageSearch)))
out.warn("Found no images at %s with expressions %s" % (url, prettyMatcherList(self.imageSearch)))
if self.textSearch:
text = self.fetchText(url, data, self.textSearch,
optional=self.textOptional)
Expand Down Expand Up @@ -407,8 +404,7 @@ def fetchUrls(self, url, data, urlSearch):
if not searchUrl:
raise ValueError("Pattern %s matched empty URL at %s." %
(search.pattern, url))
out.debug(u'matched URL %r with pattern %s' %
(searchUrl, search.pattern))
out.debug(u'matched URL %r with pattern %s' % (searchUrl, search.pattern))
searchUrls.append(normaliseURL(urljoin(data[1], searchUrl)))
if searchUrls:
# do not search other links if one pattern matched
Expand All @@ -425,8 +421,7 @@ def fetchText(self, url, data, textSearch, optional):
match = textSearch.search(data[0])
if match:
text = match.group(1)
out.debug(u'matched text %r with pattern %s' %
(text, textSearch.pattern))
out.debug(u'matched text %r with pattern %s' % (text, textSearch.pattern))
return html.unescape(text).strip()
if optional:
return None
Expand Down Expand Up @@ -593,8 +588,7 @@ def load(self) -> None:
modules += 1
classes += self.addmodule(module)
self.validate()
out.debug("... %d scrapers loaded from %d classes in %d modules." % (
len(self.data), classes, modules))
out.debug("... %d scrapers loaded from %d classes in %d modules." % ( len(self.data), classes, modules))

def adddir(self, path) -> None:
"""Add an additional directory with python modules to the scraper list.
Expand All @@ -613,8 +607,7 @@ def adddir(self, path) -> None:
self.validate()
self.userdirs.add(path)
if classes > 0:
out.debug("Added %d user classes from %d modules." % (
classes, modules))
out.debug("Added %d user classes from %d modules." % ( classes, modules))

def addmodule(self, module) -> int:
"""Adds all valid plugin classes from the specified module to the cache.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
"lxml>=4.0.0",
"platformdirs",
"requests>=2.0",
"rich",
"importlib_resources>=5.0.0;python_version<'3.9'",
]
dynamic = ["version"]
Expand Down

0 comments on commit 04166a1

Please sign in to comment.