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
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ dependencies = [
"pdfminer.six", # pdf -> text/plain conversion
"passlib >= 1.6.0", # strong password hashing (1.6 needed for consteq)
"sqlalchemy >= 2.0", # used by sqla store
"typing_extensions >= 4.12.2",
"XStatic >= 0.0.2, < 2.0.0", # support for static file pypi packages
"XStatic-Bootstrap >=4.5.3.1, <= 4.5.3.1",
"XStatic-Font-Awesome >= 6.2.1.0, <= 6.2.1.1",
Expand Down
18 changes: 14 additions & 4 deletions src/moin/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
Use create_app(config) to create the WSGI application (using Flask).
"""

from __future__ import annotations

import os
from os import path
import re
import sys

from os import path, PathLike
from flask import Flask, request, session
from flask import current_app as app
from flask import g as flaskg
Expand All @@ -38,6 +40,8 @@

from moin import log

from typing import Any

logging = log.getLogger(__name__)


Expand All @@ -46,14 +50,20 @@
sys.path.append(os.getcwd())


def create_app(config=None):
def create_app(config: str | PathLike[str] | None = None) -> Flask:
"""
simple wrapper around create_app_ext()
"""
return create_app_ext(flask_config_file=config)


def create_app_ext(flask_config_file=None, flask_config_dict=None, moin_config_class=None, warn_default=True, **kwargs):
def create_app_ext(
flask_config_file: str | PathLike[str] | None = None,
flask_config_dict: dict[str, Any] | None = None,
moin_config_class: type | None = None,
warn_default: bool = True,
**kwargs,
) -> Flask:
"""
Factory for moin wsgi apps

Expand Down Expand Up @@ -263,7 +273,7 @@ def deinit_backends(app):
app.router.destroy()


def setup_user():
def setup_user() -> user.User:
"""
Try to retrieve a valid user object from the request, be it
either through the session or through a login.
Expand Down
2 changes: 1 addition & 1 deletion src/moin/apps/frontend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ def show_item_meta(item):

@frontend.route("/+content/+<rev>/<itemname:item_name>")
@frontend.route("/+content/<itemname:item_name>", defaults=dict(rev=CURRENT))
def content_item(item_name, rev):
def content_item(item_name: str, rev: str):
"""same as show_item, but we only show the content"""
fqname = split_fqname(item_name)
item_displayed.send(app, fqname=fqname)
Expand Down
15 changes: 11 additions & 4 deletions src/moin/cli/_tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
MoinMoin - moin.cli common functions for tests
"""

from copy import copy
from __future__ import annotations

import datetime # noqa
import os
import re
import subprocess
import sys
from typing import Union

from copy import copy
from typing import Any, IO, Sequence
from warnings import warn

from moin._tests import get_dirs
Expand All @@ -22,8 +25,12 @@


def run(
cmd: list[str], log=None, wait: bool = True, timeout: int = None, env=None
) -> Union[subprocess.CompletedProcess, subprocess.Popen]:
cmd: Sequence[str | os.PathLike],
log: int | IO[Any] | None = None,
wait: bool = True,
timeout: int | None = None,
env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess | subprocess.Popen:
"""run a shell command, redirecting output to log
:param cmd: list of strings containing command arguments
:param log: open file handle to log file (binary mode) or None in which case output will be captured
Expand Down
40 changes: 22 additions & 18 deletions src/moin/converters/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
special wiki links.
"""

from __future__ import annotations

from flask import current_app as app
from flask import g as flaskg

from emeraldtree.ElementTree import Element

from moin.constants.misc import VALID_ITEMLINK_VIEWS
from moin.utils.interwiki import is_known_wiki, url_for_item
from moin.utils.iri import Iri
from moin.utils.iri import Iri, IriPath
from moin.utils.mime import type_moin_document
from moin.utils.tree import moin_page, xlink, xinclude, html
from moin.wikiutil import AbsItemName
Expand All @@ -27,19 +31,19 @@ class ConverterBase:
_tag_xinclude_href = xinclude.href
_tag_html_data_href = html.data_href

def handle_wiki_links(self, elem, link, to_tag=_tag_xlink_href):
def handle_wiki_links(self, elem: Element, link: Iri, to_tag=_tag_xlink_href):
pass

def handle_wikilocal_links(self, elem, link, page_name, to_tag=_tag_xlink_href):
def handle_wikilocal_links(self, elem: Element, link: Iri, page_name: Iri | None, to_tag=_tag_xlink_href):
pass

def handle_wiki_transclusions(self, elem, link):
def handle_wiki_transclusions(self, elem: Element, link: Iri):
pass

def handle_wikilocal_transclusions(self, elem, link, page_name):
def handle_wikilocal_transclusions(self, elem: Element, link: Iri, page_name):
pass

def handle_external_links(self, elem, link, to_tag=_tag_xlink_href):
def handle_external_links(self, elem: Element, link: Iri, to_tag=_tag_xlink_href):
pass

def __call__(self, *args, **kw):
Expand All @@ -52,8 +56,8 @@ def __call__(self, *args, **kw):

def traverse_tree(
self,
elem,
page=None,
elem: Element,
page: Iri | None = None,
__tag_page_href=moin_page.page_href,
__tag_link=_tag_xlink_href,
__tag_include=_tag_xinclude_href,
Expand Down Expand Up @@ -95,7 +99,7 @@ def traverse_tree(

return elem

def absolute_path(self, path, current_page_path):
def absolute_path(self, path: IriPath, current_page_path: IriPath) -> IriPath:
"""
Converts a relative iri path into an absolute one

Expand Down Expand Up @@ -166,7 +170,7 @@ def _get_do_rev(self, query):
endpoint = do_to_endpoint[do or "show"]
return endpoint, rev, query

def handle_wiki_links(self, elem, input, to_tag=ConverterBase._tag_xlink_href):
def handle_wiki_links(self, elem: Element, input: Iri, to_tag=ConverterBase._tag_xlink_href):
wiki_name = "Self"
if input.authority and input.authority.host:
wn = str(input.authority.host)
Expand All @@ -184,7 +188,7 @@ def handle_wiki_links(self, elem, input, to_tag=ConverterBase._tag_xlink_href):
link = Iri(url, query=query, fragment=input.fragment)
elem.set(to_tag, link)

def handle_wikilocal_links(self, elem, input, page, to_tag=ConverterBase._tag_xlink_href):
def handle_wikilocal_links(self, elem: Element, input: Iri, page: Iri | None, to_tag=ConverterBase._tag_xlink_href):
view_name = ""
if input.path:
item_name = str(input.path)
Expand Down Expand Up @@ -213,7 +217,7 @@ def handle_wikilocal_links(self, elem, input, page, to_tag=ConverterBase._tag_xl
link = Iri(url, query=query, fragment=input.fragment)
elem.set(to_tag, link)

def handle_external_links(self, elem, input, to_tag=ConverterBase._tag_xlink_href):
def handle_external_links(self, elem: Element, input: Iri, to_tag=ConverterBase._tag_xlink_href):
elem.set(to_tag, input)
# rst_in.py may create a link similar to "http:Home", we check input.authority to verify link is external
if elem.tag == moin_page.a and input.authority:
Expand All @@ -233,9 +237,9 @@ def _factory(cls, input, output, items=None, **kw):

def __init__(self, **kw):
super().__init__(**kw)
self.links = set()
self.transclusions = set()
self.external_links = set()
self.links: set[str] = set()
self.transclusions: set[str] = set()
self.external_links: set[str] = set()

def __call__(self, *args, **kw):
"""
Expand All @@ -249,7 +253,7 @@ def __call__(self, *args, **kw):

super().__call__(*args, **kw)

def handle_wikilocal_links(self, elem, input, page, to_tag=ConverterBase._tag_xlink_href):
def handle_wikilocal_links(self, elem: Element, input: Iri, page: Iri, to_tag=ConverterBase._tag_xlink_href):
"""
Adds the link item from the input param to self.links
:param elem: the element of the link
Expand All @@ -263,7 +267,7 @@ def handle_wikilocal_links(self, elem, input, page, to_tag=ConverterBase._tag_xl
path = self.absolute_path(path, page.path)
self.links.add(str(path))

def handle_wikilocal_transclusions(self, elem, input, page):
def handle_wikilocal_transclusions(self, elem: Element, input: Iri, page):
"""
Adds the transclusion item from input argument to self.transclusions
:param elem: the element of the transclusion
Expand All @@ -277,7 +281,7 @@ def handle_wikilocal_transclusions(self, elem, input, page):
path = self.absolute_path(path, page.path)
self.transclusions.add(str(path))

def handle_external_links(self, elem, input, to_tag=ConverterBase._tag_xlink_href):
def handle_external_links(self, elem, input: Iri, to_tag=ConverterBase._tag_xlink_href):
"""
Adds the link item from the input param to self.external_links
:param elem: the element of the link
Expand Down
9 changes: 6 additions & 3 deletions src/moin/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,16 @@
will do the logging.
"""

from io import StringIO
import os
from __future__ import annotations

import logging
import logging.config
import logging.handlers # needed for handlers defined there being configurable in logging.conf file
import os
import warnings

from io import StringIO

# This is the "last resort" fallback logging configuration for the case
# that load_config() is either not called at all or with a non-working
# logging configuration.
Expand Down Expand Up @@ -156,7 +159,7 @@ def load_config(conf_fname=None):
logger.debug(f"Running {moin.project} {moin.version} code from {code_path}")


def getLogger(name):
def getLogger(name: str | None):
"""wrapper around logging.getLogger, so we can do some more stuff:

- preprocess logger name
Expand Down
19 changes: 12 additions & 7 deletions src/moin/storage/middleware/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
usually it is even just the small and thus quick latest-revs index.
"""

from __future__ import annotations

import gc
import os
import re
Expand All @@ -75,6 +77,7 @@
from moin import user
from moin.search.analyzers import item_name_analyzer, MimeTokenizer, AclTokenizer
from moin.themes import utctimestamp
from moin.storage.middleware.routing import Backend
from moin.storage.middleware.validation import ContentMetaSchema, UserMetaSchema, validate_data
from moin.storage.error import NoSuchItemError, ItemAlreadyExistsError
from moin.utils import utcfromtimestamp
Expand All @@ -87,6 +90,8 @@

from moin import log

from typing import Any

logging = log.getLogger(__name__)


Expand Down Expand Up @@ -317,16 +322,16 @@ def tell(self, *args, **kw):


class IndexingMiddleware:
def __init__(self, index_storage, backend, acl_rights_contents=[], **kw):
def __init__(self, index_storage: tuple, backend: Backend, acl_rights_contents=[], **kw):
"""
Store params, create schemas.

See https://whoosh.readthedocs.io/en/latest/schema.html#built-in-field-types
"""
self.index_storage = index_storage
self.backend = backend
self.ix = {} # open indexes
self.schemas = {} # existing schemas
self.ix: dict[str, Any] = {} # open indexes
self.schemas: dict[str, Schema] = {} # existing schemas

# field_boosts favor hits on names, tags, summary, comment, content, namengram,
# summaryngram and contentngram respectively
Expand Down Expand Up @@ -997,15 +1002,15 @@ def _document(self, idx_name=LATEST_REVS, short=False, **kw):
with self.ix[idx_name].searcher() as searcher:
return searcher.document(**kw)

def has_item(self, name):
def has_item(self, name: str):
if name.startswith("@itemid/"):
item = Item(self, short=True, **{ITEMID: name[8:]})
else:
fqname = split_fqname(name)
item = Item(self, short=True, **{NAME_EXACT: fqname.value, NAMESPACE: fqname.namespace})
return bool(item)

def __getitem__(self, name):
def __getitem__(self, name: str):
"""
Return item with <name> (may be a new or existing item).
"""
Expand Down Expand Up @@ -1128,7 +1133,7 @@ def mtime(self):


class Item(PropertiesMixin):
def __init__(self, indexer, latest_doc=None, short=False, **query):
def __init__(self, indexer: IndexingMiddleware, latest_doc=None, short=False, **query):
"""
:param indexer: indexer middleware instance
:param latest_doc: if caller already has a latest-revs index whoosh document
Expand Down Expand Up @@ -1397,7 +1402,7 @@ class Revision(PropertiesMixin):
An existing revision (exists in the backend).
"""

def __init__(self, item, revid, doc=None, name=None):
def __init__(self, item: Item, revid: str, doc=None, name=None):
is_current = revid == CURRENT
if doc is None:
if is_current:
Expand Down
23 changes: 13 additions & 10 deletions src/moin/utils/interwiki.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
MoinMoin - interwiki support code
"""

from __future__ import annotations

from typing_extensions import LiteralString
from urllib.parse import quote as url_quote

from flask import current_app as app
Expand Down Expand Up @@ -40,7 +43,7 @@ def is_known_wiki(wiki_name):
return wiki_name in app.cfg.interwiki_map


def get_fqname(item_name, field, namespace):
def get_fqname(item_name: str, field: LiteralString, namespace: str):
"""
Compute composite name from item_name, field, namespace
composite name == [NAMESPACE/][@FIELD/]NAME
Expand All @@ -53,14 +56,14 @@ def get_fqname(item_name, field, namespace):


def url_for_item(
item_name,
wiki_name="",
field="",
namespace="",
rev=CURRENT,
endpoint="frontend.show_item",
_external=False,
regex="",
item_name: str,
wiki_name: str = "",
field: str = "",
namespace: str = "",
rev: str | None = CURRENT,
endpoint: str = "frontend.show_item",
_external: bool = False,
regex: str = "",
):
"""
Compute URL for some local or remote/interwiki item.
Expand Down Expand Up @@ -193,7 +196,7 @@ def get_root_fqname(self):
return CompositeName(self.namespace, NAME_EXACT, app.cfg.root_mapping.get(self.namespace, app.cfg.default_root))


def split_fqname(url):
def split_fqname(url: str) -> CompositeName:
"""
Split a fully qualified url into namespace, field and pagename
url -> [NAMESPACE/][@FIELD/]NAME
Expand Down
Loading