wmlinksfromhell 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wmlinksfromhell/__init__.py +73 -0
- wmlinksfromhell/cache.py +92 -0
- wmlinksfromhell/constants.py +307 -0
- wmlinksfromhell/destination.py +125 -0
- wmlinksfromhell/exceptions.py +29 -0
- wmlinksfromhell/interwiki.py +842 -0
- wmlinksfromhell/metadata.py +1175 -0
- wmlinksfromhell/models.py +165 -0
- wmlinksfromhell/nodes.py +447 -0
- wmlinksfromhell/operations.py +83 -0
- wmlinksfromhell/parser.py +186 -0
- wmlinksfromhell/pywikibot_support.py +13 -0
- wmlinksfromhell/resolver.py +266 -0
- wmlinksfromhell/url.py +402 -0
- wmlinksfromhell-1.0.0.dist-info/METADATA +373 -0
- wmlinksfromhell-1.0.0.dist-info/RECORD +19 -0
- wmlinksfromhell-1.0.0.dist-info/WHEEL +5 -0
- wmlinksfromhell-1.0.0.dist-info/licenses/LICENSE +21 -0
- wmlinksfromhell-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""small Wikimedia-aware link tools for wikitext"""
|
|
2
|
+
|
|
3
|
+
from .cache import MetadataCache
|
|
4
|
+
from .destination import Destination
|
|
5
|
+
from .exceptions import CacheError, ConversionError, MetadataMissingError, ResolutionError, SourceResolutionError, WMLinkFromHellError, WMLinksFromHellError
|
|
6
|
+
from .metadata import InterwikiMap, MetadataStore
|
|
7
|
+
from .models import DestinationType, InterwikiEntry, InterwikiInfo, ResolutionStatus, SyntaxType, WikiInfo
|
|
8
|
+
from .nodes import WMLink
|
|
9
|
+
from .parser import WMCode, parse
|
|
10
|
+
from .pywikibot_support import parse_page
|
|
11
|
+
from .resolver import ResolutionResult, Resolver
|
|
12
|
+
|
|
13
|
+
# short names cover the usual case; longer names stay available too
|
|
14
|
+
Code = WMCode
|
|
15
|
+
Link = WMLink
|
|
16
|
+
Wiki = WikiInfo
|
|
17
|
+
Metadata = MetadataStore
|
|
18
|
+
Result = ResolutionResult
|
|
19
|
+
Error = WMLinksFromHellError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def wiki(dbname: str, metadata: MetadataStore = None) -> WikiInfo:
|
|
23
|
+
result = (metadata or MetadataStore()).wiki_by_dbname(dbname)
|
|
24
|
+
if result is None:
|
|
25
|
+
raise MetadataMissingError(f"no metadata known for dbname {dbname!r}")
|
|
26
|
+
return result
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def interwiki(dbname: str, metadata: MetadataStore = None, source=None) -> InterwikiInfo:
|
|
30
|
+
result = (metadata or MetadataStore()).interwiki(dbname, source=source)
|
|
31
|
+
if result is None:
|
|
32
|
+
raise MetadataMissingError(f"no interwiki prefix known for dbname {dbname!r}")
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def url(dbname: str, title: str, metadata: MetadataStore = None, source=None) -> str:
|
|
37
|
+
return wiki(dbname, metadata=metadata).page_url(title, metadata=metadata, source=source)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def link(dbname: str, title: str, label: str = None, metadata: MetadataStore = None, source=None) -> str:
|
|
41
|
+
return wiki(dbname, metadata=metadata).link(title, label=label, metadata=metadata, source=source)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def resolve_url(value: str, source=None, metadata: MetadataStore = None) -> ResolutionResult:
|
|
45
|
+
return Resolver(metadata).resolve_url(value, source=source)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def resolve_interwiki(value: str, source=None, metadata: MetadataStore = None) -> ResolutionResult:
|
|
49
|
+
return Resolver(metadata).resolve_interwiki(value, source=source)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def to_url(destination: Destination, metadata: MetadataStore = None) -> str:
|
|
53
|
+
return Resolver(metadata).to_url(destination)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def to_interwiki(destination: Destination, source=None, metadata: MetadataStore = None) -> str:
|
|
57
|
+
return Resolver(metadata).to_interwiki(destination, source=source)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def to_local(destination: Destination, source=None, metadata: MetadataStore = None) -> str:
|
|
61
|
+
return Resolver(metadata).to_local(destination, source=source)
|
|
62
|
+
|
|
63
|
+
__version__ = "1.0.0"
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"parse", "parse_page", "resolve", "resolve_url", "resolve_interwiki", "to_url", "to_interwiki", "to_local", "wiki", "interwiki", "url", "link", "Code", "Link", "Wiki", "Metadata", "Result", "WMCode", "WMLink", "Resolver", "ResolutionResult", "MetadataStore", "MetadataCache",
|
|
67
|
+
"InterwikiMap", "InterwikiEntry", "InterwikiInfo", "Destination", "DestinationType", "ResolutionStatus", "SyntaxType", "WikiInfo",
|
|
68
|
+
"Error", "WMLinksFromHellError", "WMLinkFromHellError", "ConversionError", "MetadataMissingError", "CacheError", "SourceResolutionError", "ResolutionError",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def resolve(value: str, source=None, metadata: MetadataStore = None) -> ResolutionResult:
|
|
73
|
+
return Resolver(metadata).resolve(value, source=source)
|
wmlinksfromhell/cache.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""small atomic JSON cache used for explicit Wikimedia metadata refreshes"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import tempfile
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from .constants import CACHE_SCHEMA_VERSION
|
|
13
|
+
from .exceptions import CacheError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MetadataCache:
|
|
17
|
+
"""persistent cache; a bad cache never destroys a good in-memory bootstrap"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, path: Optional[os.PathLike] = None):
|
|
20
|
+
self.path = Path(path) if path is not None else self.default_path()
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def default_path() -> Path:
|
|
24
|
+
base = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache")
|
|
25
|
+
return Path(base) / "wmlinksfromhell" / "metadata.json"
|
|
26
|
+
|
|
27
|
+
def load(self) -> dict:
|
|
28
|
+
if not self.path.exists():
|
|
29
|
+
return self.empty()
|
|
30
|
+
try:
|
|
31
|
+
with self.path.open("r", encoding="utf-8") as fh:
|
|
32
|
+
data = json.load(fh)
|
|
33
|
+
except (OSError, UnicodeDecodeError, json.JSONDecodeError):
|
|
34
|
+
self._quarantine()
|
|
35
|
+
return self.empty()
|
|
36
|
+
if not isinstance(data, dict) or data.get("schema_version") != CACHE_SCHEMA_VERSION:
|
|
37
|
+
self._quarantine()
|
|
38
|
+
return self.empty()
|
|
39
|
+
return data
|
|
40
|
+
|
|
41
|
+
def save(self, data: dict) -> None:
|
|
42
|
+
payload = dict(data)
|
|
43
|
+
payload["schema_version"] = CACHE_SCHEMA_VERSION
|
|
44
|
+
payload["updated_at"] = time.time()
|
|
45
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
fd, tmp = tempfile.mkstemp(dir=str(self.path.parent), prefix=".metadata-", suffix=".tmp")
|
|
47
|
+
try:
|
|
48
|
+
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
49
|
+
json.dump(payload, fh, ensure_ascii=False, indent=2, sort_keys=True)
|
|
50
|
+
fh.flush()
|
|
51
|
+
os.fsync(fh.fileno())
|
|
52
|
+
os.replace(tmp, self.path)
|
|
53
|
+
except OSError as exc:
|
|
54
|
+
try:
|
|
55
|
+
os.unlink(tmp)
|
|
56
|
+
except OSError:
|
|
57
|
+
pass
|
|
58
|
+
raise CacheError(f"couldn't write metadata cache to {self.path}") from exc
|
|
59
|
+
|
|
60
|
+
def is_expired(self, max_age_seconds: float) -> bool:
|
|
61
|
+
updated_at = self.load().get("updated_at")
|
|
62
|
+
return updated_at is None or (time.time() - updated_at) > max_age_seconds
|
|
63
|
+
|
|
64
|
+
def clear(self) -> None:
|
|
65
|
+
try:
|
|
66
|
+
self.path.unlink(missing_ok=True)
|
|
67
|
+
except OSError:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
def empty(self) -> dict:
|
|
71
|
+
return {
|
|
72
|
+
"schema_version": CACHE_SCHEMA_VERSION,
|
|
73
|
+
"updated_at": None,
|
|
74
|
+
"wikis": {},
|
|
75
|
+
"languages": {},
|
|
76
|
+
"interwiki_maps": {},
|
|
77
|
+
"global_interwikis": {},
|
|
78
|
+
"global_interwiki_lists": {},
|
|
79
|
+
"family_language_aliases": {},
|
|
80
|
+
"family_language_prefixes": {},
|
|
81
|
+
"namespaces": {},
|
|
82
|
+
"http": {},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def _quarantine(self) -> None:
|
|
86
|
+
try:
|
|
87
|
+
target = self.path.with_suffix(self.path.suffix + ".corrupt")
|
|
88
|
+
if target.exists():
|
|
89
|
+
target = self.path.with_suffix(self.path.suffix + f".{int(time.time())}.corrupt")
|
|
90
|
+
os.replace(self.path, target)
|
|
91
|
+
except OSError:
|
|
92
|
+
pass
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""bootstrap data; live Wikimedia metadata can extend/override this"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .models import WikiInfo
|
|
6
|
+
|
|
7
|
+
CACHE_SCHEMA_VERSION = 2
|
|
8
|
+
|
|
9
|
+
LANGUAGES: dict[str, str] = {
|
|
10
|
+
"en": "English", "de": "German", "fr": "French", "es": "Spanish", "it": "Italian",
|
|
11
|
+
"pt": "Portuguese", "nl": "Dutch", "pl": "Polish", "ru": "Russian", "ja": "Japanese",
|
|
12
|
+
"zh": "Chinese", "hi": "Hindi", "ar": "Arabic", "sv": "Swedish", "fi": "Finnish",
|
|
13
|
+
"no": "Norwegian", "da": "Danish", "cs": "Czech", "uk": "Ukrainian", "he": "Hebrew",
|
|
14
|
+
"ko": "Korean", "id": "Indonesian", "tr": "Turkish", "os": "Ossetian", "mul": "Multilingual",
|
|
15
|
+
"az": "Azerbaijani", "ba": "Bashkir", "be": "Belarusian", "eo": "Esperanto", "hsb": "Upper Sorbian",
|
|
16
|
+
"lez": "Lezghian", "tg": "Tajik", "udm": "Udmurt", "ms": "Malay", "tk": "Turkmen",
|
|
17
|
+
"vls": "West Flemish", "dv": "Divehi", "ca": "Catalan", "fa": "Persian", "bn": "Bangla",
|
|
18
|
+
"el": "Greek", "hu": "Hungarian", "ro": "Romanian", "sk": "Slovak", "sl": "Slovenian",
|
|
19
|
+
"hr": "Croatian", "sr": "Serbian", "bg": "Bulgarian", "et": "Estonian", "lv": "Latvian",
|
|
20
|
+
"lt": "Lithuanian", "is": "Icelandic", "ga": "Irish", "cy": "Welsh", "af": "Afrikaans", "simple": "Simple English",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
MULTILINGUAL_FAMILIES: dict[str, dict[str, str]] = {
|
|
24
|
+
"wikipedia": {"project": "wikipedia", "site_code": "wiki", "suffix": "wiki"},
|
|
25
|
+
"wiktionary": {"project": "wiktionary", "site_code": "wiktionary", "suffix": "wiktionary"},
|
|
26
|
+
"wikinews": {"project": "wikinews", "site_code": "wikinews", "suffix": "wikinews"},
|
|
27
|
+
"wikibooks": {"project": "wikibooks", "site_code": "wikibooks", "suffix": "wikibooks"},
|
|
28
|
+
"wikiquote": {"project": "wikiquote", "site_code": "wikiquote", "suffix": "wikiquote"},
|
|
29
|
+
"wikisource": {"project": "wikisource", "site_code": "wikisource", "suffix": "wikisource"},
|
|
30
|
+
"wikiversity": {"project": "wikiversity", "site_code": "wikiversity", "suffix": "wikiversity"},
|
|
31
|
+
"wikivoyage": {"project": "wikivoyage", "site_code": "wikivoyage", "suffix": "wikivoyage"},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
BOOTSTRAP_LANGS_BY_FAMILY: dict[str, tuple[str, ...]] = {
|
|
35
|
+
"wikipedia": tuple(LANGUAGES) + ("simple",),
|
|
36
|
+
"wiktionary": ("en", "de", "fr", "it", "fa", "simple"),
|
|
37
|
+
"wikinews": ("en", "de"),
|
|
38
|
+
"wikibooks": ("en", "de", "nl", "it", "simple"),
|
|
39
|
+
"wikiquote": ("en", "de", "fr"),
|
|
40
|
+
"wikisource": ("en", "de", "mul", "az"),
|
|
41
|
+
"wikiversity": ("en", "de"),
|
|
42
|
+
"wikivoyage": ("en", "de", "it"),
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
SINGLE_WIKIS: dict[str, dict] = {
|
|
46
|
+
"commonswiki": dict(family="commons", project="commons", language="mul", hostname="commons.wikimedia.org", sitename="Wikimedia Commons"),
|
|
47
|
+
"mulwikisource": dict(family="wikisource", project="wikisource", language="mul", hostname="wikisource.org", sitename="Wikisource multilingual"),
|
|
48
|
+
"wikidatawiki": dict(family="wikidata", project="wikidata", language="mul", hostname="www.wikidata.org", sitename="Wikidata"),
|
|
49
|
+
"metawiki": dict(family="meta", project="meta", language="en", hostname="meta.wikimedia.org", sitename="Meta-Wiki"),
|
|
50
|
+
"mediawikiwiki": dict(family="mediawiki", project="mediawiki", language="en", hostname="www.mediawiki.org", sitename="MediaWiki.org"),
|
|
51
|
+
"specieswiki": dict(family="wikispecies", project="wikispecies", language="mul", hostname="species.wikimedia.org", sitename="Wikispecies"),
|
|
52
|
+
"incubatorwiki": dict(family="incubator", project="incubator", language="mul", hostname="incubator.wikimedia.org", sitename="Wikimedia Incubator"),
|
|
53
|
+
"foundationwiki": dict(family="foundation", project="foundation", language="en", hostname="foundation.wikimedia.org", sitename="Wikimedia Foundation Governance Wiki"),
|
|
54
|
+
"wikifunctionswiki": dict(family="wikifunctions", project="wikifunctions", language="mul", hostname="www.wikifunctions.org", sitename="Wikifunctions"),
|
|
55
|
+
"wikitechwiki": dict(family="wikitech", project="wikitech", language="en", hostname="wikitech.wikimedia.org", sitename="Wikitech"),
|
|
56
|
+
"strategywiki": dict(family="strategy", project="strategy", language="en", hostname="strategy.wikimedia.org", sitename="Wikimedia Strategic Planning"),
|
|
57
|
+
"testwiki": dict(family="test", project="test", language="en", hostname="test.wikipedia.org", sitename="Test Wikipedia"),
|
|
58
|
+
"betawikiversity": dict(family="wikiversity", project="wikiversity", language="en", hostname="beta.wikiversity.org", sitename="Beta Wikiversity"),
|
|
59
|
+
"arbcom_cswiki": dict(family="special", project="arbcom_cswiki", language="cs", hostname="arbcom-cs.wikipedia.org", sitename="Czech Wikipedia Arbitration Committee wiki"),
|
|
60
|
+
"arbcom_dewiki": dict(family="special", project="arbcom_dewiki", language="de", hostname="arbcom-de.wikipedia.org", sitename="German Wikipedia Arbitration Committee wiki"),
|
|
61
|
+
"arbcom_enwiki": dict(family="special", project="arbcom_enwiki", language="en", hostname="arbcom-en.wikipedia.org", sitename="English Wikipedia Arbitration Committee wiki"),
|
|
62
|
+
"arbcom_fiwiki": dict(family="special", project="arbcom_fiwiki", language="fi", hostname="arbcom-fi.wikipedia.org", sitename="Finnish Wikipedia Arbitration Committee wiki"),
|
|
63
|
+
"arbcom_itwiki": dict(family="special", project="arbcom_itwiki", language="it", hostname="wikipedia-it-arbcom.wikimedia.org", sitename="Italian Wikipedia Arbitration Committee wiki"),
|
|
64
|
+
"arbcom_nlwiki": dict(family="special", project="arbcom_nlwiki", language="nl", hostname="arbcom-nl.wikipedia.org", sitename="Dutch Wikipedia Arbitration Committee wiki"),
|
|
65
|
+
"arbcom_plwiki": dict(family="special", project="arbcom_plwiki", language="pl", hostname="wikipedia-pl-arbcom.wikimedia.org", sitename="Polish Wikipedia Arbitration Committee wiki"),
|
|
66
|
+
"arbcom_ruwiki": dict(family="special", project="arbcom_ruwiki", language="ru", hostname="arbcom-ru.wikipedia.org", sitename="Russian Wikipedia Arbitration Committee wiki"),
|
|
67
|
+
"arbcom_zhwiki": dict(family="special", project="arbcom_zhwiki", language="zh", hostname="wikipedia-zh-arbcom.wikimedia.org", sitename="Chinese Wikipedia Arbitration Committee wiki"),
|
|
68
|
+
"boardwiki": dict(family="special", project="boardwiki", language="en", hostname="board.wikimedia.org", sitename="Wikimedia Board wiki"),
|
|
69
|
+
"checkuserwiki": dict(family="special", project="checkuserwiki", language="en", hostname="checkuser.wikimedia.org", sitename="CheckUser wiki"),
|
|
70
|
+
"stewardwiki": dict(family="special", project="stewardwiki", language="en", hostname="steward.wikimedia.org", sitename="Steward wiki"),
|
|
71
|
+
"wikimaniateamwiki": dict(family="special", project="wikimaniateamwiki", language="en", hostname="wikimaniateam.wikimedia.org", sitename="Wikimania Team wiki"),
|
|
72
|
+
"arbcom_ukwiki": dict(family="special", project="arbcom_ukwiki", language="uk", hostname="wikipedia-uk-arbcom.wikimedia.org", sitename="Ukrainian Wikipedia Arbitration Committee wiki"),
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
COMMON_WIKIMEDIA_HOST_SUFFIXES = (
|
|
76
|
+
".wikipedia.org", ".wiktionary.org", ".wikinews.org", ".wikibooks.org", ".wikiquote.org",
|
|
77
|
+
".wikisource.org", ".wikiversity.org", ".wikivoyage.org", ".wikimedia.org", ".mediawiki.org",
|
|
78
|
+
".wikifunctions.org", ".wikitech.org", ".toolforge.org",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Short family prefixes are context-sensitive: they can carry the source
|
|
82
|
+
# language when switching to another Wikimedia project family.
|
|
83
|
+
FAMILY_PREFIXES = {
|
|
84
|
+
"w": "wikipedia",
|
|
85
|
+
"wikt": "wiktionary",
|
|
86
|
+
"n": "wikinews",
|
|
87
|
+
"b": "wikibooks",
|
|
88
|
+
"q": "wikiquote",
|
|
89
|
+
"s": "wikisource",
|
|
90
|
+
"v": "wikiversity",
|
|
91
|
+
"voy": "wikivoyage",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Long project names are actual interwiki entries and normally point at the
|
|
95
|
+
# English project. Keep them separate from context-sensitive short prefixes.
|
|
96
|
+
SINGLE_WIKI_PREFIXES = {
|
|
97
|
+
"wikipedia": "enwiki",
|
|
98
|
+
"wiktionary": "enwiktionary",
|
|
99
|
+
"wikinews": "enwikinews",
|
|
100
|
+
"wikibooks": "enwikibooks",
|
|
101
|
+
"wikiquote": "enwikiquote",
|
|
102
|
+
"wikisource": "enwikisource",
|
|
103
|
+
"wikiversity": "enwikiversity",
|
|
104
|
+
"wikivoyage": "enwikivoyage",
|
|
105
|
+
"oldwikisource": "mulwikisource",
|
|
106
|
+
"c": "commonswiki",
|
|
107
|
+
"commons": "commonswiki",
|
|
108
|
+
"d": "wikidatawiki",
|
|
109
|
+
"wikidata": "wikidatawiki",
|
|
110
|
+
"m": "metawiki",
|
|
111
|
+
"meta": "metawiki",
|
|
112
|
+
"metawiki": "metawiki",
|
|
113
|
+
"metawikimedia": "metawiki",
|
|
114
|
+
"metawikipedia": "metawiki",
|
|
115
|
+
"mw": "mediawikiwiki",
|
|
116
|
+
"mediawikiwiki": "mediawikiwiki",
|
|
117
|
+
"species": "specieswiki",
|
|
118
|
+
"wikispecies": "specieswiki",
|
|
119
|
+
"incubator": "incubatorwiki",
|
|
120
|
+
"betawikiversity": "betawikiversity",
|
|
121
|
+
"wmf": "foundationwiki",
|
|
122
|
+
"foundation": "foundationwiki",
|
|
123
|
+
"wikimedia": "foundationwiki",
|
|
124
|
+
"f": "wikifunctionswiki",
|
|
125
|
+
"wikifunctions": "wikifunctionswiki",
|
|
126
|
+
"wikitech": "wikitechwiki",
|
|
127
|
+
"strategy": "strategywiki",
|
|
128
|
+
"testwiki": "testwiki",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
FAMILY_LANGUAGE_ALIASES = {
|
|
133
|
+
("wikiversity", "mul"): "betawikiversity",
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# small bootstrap of Wikimedia global interwiki prefixes whose targets are
|
|
138
|
+
# wikimedia wikis. this is fallback prefix metadata only; explicit live
|
|
139
|
+
# interwiki metadata loaded by MetadataStore replaces/extends it.
|
|
140
|
+
WIKIMEDIA_GLOBAL_WIKI_PREFIXES = {
|
|
141
|
+
"arbcom-zh": ("https://wikipedia-zh-arbcom.wikimedia.org/wiki/$1", "arbcom_zhwiki"),
|
|
142
|
+
"abstract": ("https://abstract.wikipedia.org/wiki/$1", "abstractwiki"),
|
|
143
|
+
"advisory": ("https://advisory.wikimedia.org/wiki/$1", "advisorywiki"),
|
|
144
|
+
"betawikiversity": ("https://beta.wikiversity.org/wiki/$1", "betawikiversity"),
|
|
145
|
+
"nostalgia": ("https://nostalgia.wikipedia.org/wiki/$1", "nostalgiawiki"),
|
|
146
|
+
"tenwiki": ("https://ten.wikipedia.org/wiki/$1", "tenwiki"),
|
|
147
|
+
"test2wiki": ("https://test2.wikipedia.org/wiki/$1", "test2wiki"),
|
|
148
|
+
"testcommons": ("https://test-commons.wikimedia.org/wiki/$1", "testcommonswiki"),
|
|
149
|
+
"testwikidata": ("https://test.wikidata.org/wiki/$1", "testwikidatawiki"),
|
|
150
|
+
"wm2005": ("https://wikimania2005.wikimedia.org/wiki/$1", "wikimania2005wiki"),
|
|
151
|
+
"wm2006": ("https://wikimania2006.wikimedia.org/wiki/$1", "wikimania2006wiki"),
|
|
152
|
+
"wm2007": ("https://wikimania2007.wikimedia.org/wiki/$1", "wikimania2007wiki"),
|
|
153
|
+
"wm2008": ("https://wikimania2008.wikimedia.org/wiki/$1", "wikimania2008wiki"),
|
|
154
|
+
"wm2009": ("https://wikimania2009.wikimedia.org/wiki/$1", "wikimania2009wiki"),
|
|
155
|
+
"wm2010": ("https://wikimania2010.wikimedia.org/wiki/$1", "wikimania2010wiki"),
|
|
156
|
+
"wm2011": ("https://wikimania2011.wikimedia.org/wiki/$1", "wikimania2011wiki"),
|
|
157
|
+
"wm2012": ("https://wikimania2012.wikimedia.org/wiki/$1", "wikimania2012wiki"),
|
|
158
|
+
"wm2013": ("https://wikimania2013.wikimedia.org/wiki/$1", "wikimania2013wiki"),
|
|
159
|
+
"wm2014": ("https://wikimania2014.wikimedia.org/wiki/$1", "wikimania2014wiki"),
|
|
160
|
+
"wm2015": ("https://wikimania2015.wikimedia.org/wiki/$1", "wikimania2015wiki"),
|
|
161
|
+
"wm2016": ("https://wikimania2016.wikimedia.org/wiki/$1", "wikimania2016wiki"),
|
|
162
|
+
"wm2017": ("https://wikimania2017.wikimedia.org/wiki/$1", "wikimania2017wiki"),
|
|
163
|
+
"wm2018": ("https://wikimania2018.wikimedia.org/wiki/$1", "wikimania2018wiki"),
|
|
164
|
+
"wmteam": ("https://wikimaniateam.wikimedia.org/wiki/$1", "wikimaniateamwiki"),
|
|
165
|
+
"wmania": ("https://wikimania.wikimedia.org/wiki/$1", "wikimaniawiki"),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
SERVICE_PREFIXES = {
|
|
169
|
+
"phab": dict(hostname="phabricator.wikimedia.org", name="Phabricator", destination_type="service", url="https://phabricator.wikimedia.org/$1"),
|
|
170
|
+
"phabricator": dict(hostname="phabricator.wikimedia.org", name="Phabricator", destination_type="service", url="https://phabricator.wikimedia.org/$1"),
|
|
171
|
+
"bugzilla": dict(hostname="bugzilla.wikimedia.org", name="Bugzilla", destination_type="service", url="https://bugzilla.wikimedia.org/show_bug.cgi?id=$1"),
|
|
172
|
+
"mediazilla": dict(hostname="bugzilla.wikimedia.org", name="Bugzilla", destination_type="service", url="https://bugzilla.wikimedia.org/show_bug.cgi?id=$1"),
|
|
173
|
+
"gerrit": dict(hostname="gerrit.wikimedia.org", name="Gerrit", destination_type="service", url="https://gerrit.wikimedia.org/$1"),
|
|
174
|
+
"tools": dict(hostname="toolforge.org", name="Toolforge", destination_type="tool", url="https://toolforge.org/$1"),
|
|
175
|
+
"toolforge": dict(hostname="toolforge.org", name="Toolforge", destination_type="tool", url="https://toolforge.org/$1"),
|
|
176
|
+
"tswiki": dict(hostname="wikitech.wikimedia.org", name="Toolforge wiki", destination_type="service", url="https://wikitech.wikimedia.org/wiki/$1"),
|
|
177
|
+
"sulutil": dict(hostname="meta.wikimedia.org", name="SUL utility", destination_type="service", url="https://meta.wikimedia.org/wiki/Special:CentralAuth/$1"),
|
|
178
|
+
"mail": dict(hostname="lists.wikimedia.org", name="Wikimedia mailing lists", destination_type="service", url="https://lists.wikimedia.org/$1"),
|
|
179
|
+
"mailarchive": dict(hostname="lists.wikimedia.org", name="Wikimedia mailing list archives", destination_type="service", url="https://lists.wikimedia.org/pipermail/$1"),
|
|
180
|
+
"outreach": dict(hostname="outreach.wikimedia.org", name="Outreach wiki", destination_type="service", url="https://outreach.wikimedia.org/wiki/$1"),
|
|
181
|
+
"otrs": dict(hostname="ticket.wikimedia.org", name="Volunteer Response Team", destination_type="service", url="https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketNumber=$1"),
|
|
182
|
+
"otrswiki": dict(hostname="vrt-wiki.wikimedia.org", name="OTRS wiki", destination_type="service", url="https://vrt-wiki.wikimedia.org/wiki/$1"),
|
|
183
|
+
"quality": dict(hostname="quality.wikimedia.org", name="Wikimedia quality initiative", destination_type="service", url="https://quality.wikimedia.org/wiki/$1"),
|
|
184
|
+
"spcom": dict(hostname="spcom.wikimedia.org", name="Wikimedia Ombuds/spam commission", destination_type="service", url="https://spcom.wikimedia.org/wiki/$1"),
|
|
185
|
+
"ticket": dict(hostname="ticket.wikimedia.org", name="Wikimedia ticketing system", destination_type="service", url="https://ticket.wikimedia.org/otrs/index.pl?Action=AgentTicketZoom&TicketNumber=$1"),
|
|
186
|
+
"svn": dict(hostname="svn.wikimedia.org", name="Wikimedia Subversion", destination_type="service", url="https://svn.wikimedia.org/$1"),
|
|
187
|
+
"rev": dict(hostname="www.mediawiki.org", name="MediaWiki revision service", destination_type="service", url="https://www.mediawiki.org/wiki/Special:Code/MediaWiki/$1"),
|
|
188
|
+
"wmania": dict(hostname="wikimania.wikimedia.org", name="Wikimania", destination_type="service", url="https://wikimania.wikimedia.org/wiki/$1"),
|
|
189
|
+
"wm2016": dict(hostname="wikimania2016.wikimedia.org", name="Wikimania 2016", destination_type="service", url="https://wikimania2016.wikimedia.org/wiki/$1"),
|
|
190
|
+
"wm2017": dict(hostname="wikimania2017.wikimedia.org", name="Wikimania 2017", destination_type="service", url="https://wikimania2017.wikimedia.org/wiki/$1"),
|
|
191
|
+
"download": dict(hostname="releases.wikimedia.org", name="Wikimedia releases", destination_type="service", url="https://releases.wikimedia.org/$1"),
|
|
192
|
+
"dbdump": dict(hostname="dumps.wikimedia.org", name="Wikimedia database dumps", destination_type="service", url="https://dumps.wikimedia.org/$1"),
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
CHAPTER_PREFIXES = {
|
|
196
|
+
"wmar": "Wikimedia Argentina", "wmau": "Wikimedia Australia", "wmat": "Wikimedia Austria", "wmbd": "Wikimedia Bangladesh",
|
|
197
|
+
"wmbe": "Wikimedia Belgium", "wmbr": "Wikimedia Brasil", "wmca": "Wikimedia Canada", "wmcz": "Wikimedia Czech Republic",
|
|
198
|
+
"wmdk": "Wikimedia Danmark", "wmde": "Wikimedia Deutschland", "wmfi": "Wikimedia Suomi", "wmhk": "Wikimedia Hong Kong",
|
|
199
|
+
"wmhu": "Wikimedia Hungary", "wmin": "Wikimedia India", "wmke": "Wikimedia Kenya", "wmid": "Wikimedia Indonesia", "wmil": "Wikimedia Israel",
|
|
200
|
+
"wmit": "Wikimedia Italia", "wmnl": "Wikimedia Nederland", "wmmk": "Wikimedia Macedonia", "wmno": "Wikimedia Norge",
|
|
201
|
+
"wmpl": "Wikimedia Polska", "wmru": "Wikimedia Russia", "wmrs": "Wikimedia Srbije", "wmes": "Wikimedia Espana",
|
|
202
|
+
"wmse": "Wikimedia Sverige", "wmch": "Wikimedia CH", "wmph": "Wiki Society of the Philippines", "wmtw": "Wikimedia Taiwan", "wmua": "Wikimedia Ukraine", "wmuk": "Wikimedia UK", "wmza": "Wikimedia South Africa",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
CHAPTER_FIXED_URLS = {
|
|
206
|
+
"wmhk": "https://meta.wikimedia.org/wiki/Wikimedia_Hong_Kong",
|
|
207
|
+
"wmin": "https://meta.wikimedia.org/wiki/Wikimedia_India",
|
|
208
|
+
"wmke": "https://meta.wikimedia.org/wiki/Wikimedia_Kenya",
|
|
209
|
+
"wmph": "https://meta.wikimedia.org/wiki/Wiki_Society_of_the_Philippines",
|
|
210
|
+
"wmtw": "https://meta.wikimedia.org/wiki/Wikimedia_Taiwan",
|
|
211
|
+
"wmza": "https://meta.wikimedia.org/wiki/Wikimedia_South_Africa",
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
CHAPTER_HOSTS = {
|
|
215
|
+
"wmar": "www.wikimedia.org.ar", "wmau": "wikimedia.org.au", "wmat": "mitglieder.wikimedia.at", "wmbd": "bd.wikimedia.org",
|
|
216
|
+
"wmbe": "be.wikimedia.org", "wmbr": "br.wikimedia.org", "wmca": "ca.wikimedia.org", "wmcz": "www.wikimedia.cz",
|
|
217
|
+
"wmdk": "dk.wikimedia.org", "wmde": "wikimedia.de", "wmfi": "fi.wikimedia.org", "wmke": "meta.wikimedia.org", "wmhk": "meta.wikimedia.org",
|
|
218
|
+
"wmhu": "wikimedia.hu", "wmin": "meta.wikimedia.org", "wmid": "id.wikimedia.org", "wmil": "www.wikimedia.org.il",
|
|
219
|
+
"wmit": "wiki.wikimedia.it", "wmnl": "nl.wikimedia.org", "wmmk": "mk.wikimedia.org", "wmno": "no.wikimedia.org",
|
|
220
|
+
"wmpl": "pl.wikimedia.org", "wmru": "ru.wikimedia.org", "wmrs": "rs.wikimedia.org", "wmes": "www.wikimedia.es",
|
|
221
|
+
"wmse": "se.wikimedia.org", "wmch": "www.wikimedia.ch", "wmph": "meta.wikimedia.org", "wmtw": "meta.wikimedia.org", "wmua": "ua.wikimedia.org", "wmuk": "wikimedia.org.uk", "wmza": "meta.wikimedia.org",
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
COMMON_NAMESPACE_NAMES = frozenset({
|
|
225
|
+
"media", "special", "talk", "user", "user talk", "project", "project talk", "file", "image", "file talk", "image talk",
|
|
226
|
+
"mediawiki", "mediawiki talk", "template", "template talk", "help", "help talk", "category", "category talk", "module", "module talk",
|
|
227
|
+
"portal", "portal talk", "draft", "draft talk", "timedtext", "timedtext talk", "gadget", "gadget talk", "widget", "widget talk",
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
NAMESPACE_ALIASES = {"image": "file", "image talk": "file talk"}
|
|
231
|
+
|
|
232
|
+
PREFERRED_FAMILY_PREFIX = {
|
|
233
|
+
"wikipedia": "w", "wiktionary": "wikt", "wikinews": "n", "wikibooks": "b", "wikiquote": "q", "wikisource": "s", "wikiversity": "v", "wikivoyage": "voy",
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
SINGLE_WIKI_HOST_ALIASES = {
|
|
237
|
+
"wikidatawiki": ("wikidata.org",),
|
|
238
|
+
"metawiki": ("meta.wikipedia.org",),
|
|
239
|
+
"mediawikiwiki": ("mediawiki.org",),
|
|
240
|
+
"wikifunctionswiki": ("wikifunctions.org",),
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
WIKIMEDIA_PORTAL_HOSTS = {
|
|
244
|
+
"wikipedia.org",
|
|
245
|
+
"www.wikipedia.org",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
PREFERRED_SERVICE_PREFIX = {
|
|
249
|
+
"Phabricator": "phab",
|
|
250
|
+
"Bugzilla": "bugzilla",
|
|
251
|
+
"Gerrit": "gerrit",
|
|
252
|
+
"Toolforge": "toolforge",
|
|
253
|
+
"Toolforge wiki": "tswiki",
|
|
254
|
+
"SUL utility": "sulutil",
|
|
255
|
+
"Wikimedia mailing lists": "mail",
|
|
256
|
+
"Wikimedia mailing list archives": "mailarchive",
|
|
257
|
+
"Outreach wiki": "outreach",
|
|
258
|
+
"Volunteer Response Team": "otrs",
|
|
259
|
+
"OTRS wiki": "otrswiki",
|
|
260
|
+
"Wikimedia quality initiative": "quality",
|
|
261
|
+
"Wikimedia Subversion": "svn",
|
|
262
|
+
"MediaWiki revision service": "rev",
|
|
263
|
+
"Wikimania": "wmania",
|
|
264
|
+
"Wikimedia releases": "download",
|
|
265
|
+
"Wikimedia database dumps": "dbdump",
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
PREFERRED_SINGLE_WIKI_PREFIX = {
|
|
269
|
+
"commonswiki": "c", "wikidatawiki": "d", "metawiki": "m", "mediawikiwiki": "mw", "specieswiki": "species", "incubatorwiki": "incubator",
|
|
270
|
+
"foundationwiki": "wmf", "wikifunctionswiki": "f", "wikitechwiki": "wikitech", "strategywiki": "strategy", "testwiki": "testwiki",
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _build_wikis() -> dict[str, WikiInfo]:
|
|
275
|
+
wikis: dict[str, WikiInfo] = {}
|
|
276
|
+
for family, langs in BOOTSTRAP_LANGS_BY_FAMILY.items():
|
|
277
|
+
data = MULTILINGUAL_FAMILIES[family]
|
|
278
|
+
for lang in langs:
|
|
279
|
+
dbname = f"{lang}{data['suffix']}"
|
|
280
|
+
wikis[dbname] = WikiInfo(
|
|
281
|
+
dbname=dbname,
|
|
282
|
+
family=family,
|
|
283
|
+
project=data["project"],
|
|
284
|
+
language=lang,
|
|
285
|
+
language_name=LANGUAGES.get(lang, lang),
|
|
286
|
+
hostname=f"{lang}.{family}.org",
|
|
287
|
+
site_code=data["site_code"],
|
|
288
|
+
is_multilingual=False,
|
|
289
|
+
is_standard_project=True,
|
|
290
|
+
)
|
|
291
|
+
for dbname, data in SINGLE_WIKIS.items():
|
|
292
|
+
wikis[dbname] = WikiInfo(
|
|
293
|
+
dbname=dbname,
|
|
294
|
+
family=data["family"],
|
|
295
|
+
project=data["project"],
|
|
296
|
+
language=data["language"],
|
|
297
|
+
language_name=LANGUAGES.get(data["language"], data["language"]),
|
|
298
|
+
hostname=data["hostname"],
|
|
299
|
+
sitename=data.get("sitename"),
|
|
300
|
+
is_multilingual=data["language"] == "mul",
|
|
301
|
+
is_standard_project=False,
|
|
302
|
+
)
|
|
303
|
+
return wikis
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
WIKIS = _build_wikis()
|
|
307
|
+
MULTILINGUAL_FAMILY_NAMES = frozenset(MULTILINGUAL_FAMILIES)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""semantic destinations independent of link syntax"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .models import DestinationType
|
|
11
|
+
|
|
12
|
+
_WHITESPACE_RE = re.compile(r"[ \t_]+")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def normalize_title(title: str) -> str:
|
|
16
|
+
return _WHITESPACE_RE.sub(" ", title).strip()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def split_namespace(title: str, known_namespaces: frozenset[str]) -> tuple[Optional[str], str]:
|
|
20
|
+
if ":" not in title:
|
|
21
|
+
return None, title
|
|
22
|
+
head, rest = title.split(":", 1)
|
|
23
|
+
candidate = normalize_title(head).casefold()
|
|
24
|
+
if candidate not in known_namespaces:
|
|
25
|
+
return None, title
|
|
26
|
+
return head.strip(), rest.strip()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclasses.dataclass(frozen=True, eq=False)
|
|
30
|
+
class Destination:
|
|
31
|
+
"""a syntax-independent Wikimedia destination"""
|
|
32
|
+
|
|
33
|
+
destination_type: DestinationType
|
|
34
|
+
family: Optional[str] = None
|
|
35
|
+
project: Optional[str] = None
|
|
36
|
+
language: Optional[str] = None
|
|
37
|
+
dbname: Optional[str] = None
|
|
38
|
+
wikiid: Optional[str] = None
|
|
39
|
+
hostname: Optional[str] = None
|
|
40
|
+
title: Optional[str] = None
|
|
41
|
+
namespace: Optional[str] = None
|
|
42
|
+
fragment: Optional[str] = None
|
|
43
|
+
revision: Optional[int] = None
|
|
44
|
+
diff: Optional[object] = None
|
|
45
|
+
action: Optional[str] = None
|
|
46
|
+
special_page: Optional[str] = None
|
|
47
|
+
organization_name: Optional[str] = None
|
|
48
|
+
service_name: Optional[str] = None
|
|
49
|
+
query_parameters: tuple[tuple[str, str], ...] = ()
|
|
50
|
+
page_id: Optional[int] = None
|
|
51
|
+
canonical_url: Optional[str] = None
|
|
52
|
+
metadata_source: Optional[str] = None
|
|
53
|
+
is_multilingual: Optional[bool] = None
|
|
54
|
+
is_standard_project: Optional[bool] = None
|
|
55
|
+
is_wikimedia_project: Optional[bool] = None
|
|
56
|
+
|
|
57
|
+
def __eq__(self, other: object) -> bool:
|
|
58
|
+
if not isinstance(other, Destination):
|
|
59
|
+
return NotImplemented
|
|
60
|
+
return self.identity_key() == other.identity_key()
|
|
61
|
+
|
|
62
|
+
def __hash__(self) -> int:
|
|
63
|
+
return hash(self.identity_key())
|
|
64
|
+
|
|
65
|
+
def as_dict(self) -> dict:
|
|
66
|
+
data = dataclasses.asdict(self)
|
|
67
|
+
data["destination_type"] = self.destination_type.value
|
|
68
|
+
data["query_parameters"] = list(self.query_parameters)
|
|
69
|
+
return data
|
|
70
|
+
|
|
71
|
+
def as_json(self) -> str:
|
|
72
|
+
return json.dumps(self.as_dict(), sort_keys=True, ensure_ascii=False)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def full_title(self) -> Optional[str]:
|
|
76
|
+
if self.title is None:
|
|
77
|
+
return None
|
|
78
|
+
return f"{self.namespace}:{self.title}" if self.namespace else self.title
|
|
79
|
+
|
|
80
|
+
def page_identity_key(self) -> tuple:
|
|
81
|
+
if self.destination_type in {DestinationType.EXTERNAL, DestinationType.UNKNOWN}:
|
|
82
|
+
return (self.destination_type, self.canonical_url or self.hostname or self.full_title)
|
|
83
|
+
base = (
|
|
84
|
+
self.destination_type,
|
|
85
|
+
self.dbname,
|
|
86
|
+
self.hostname if self.dbname is None else None,
|
|
87
|
+
self.organization_name,
|
|
88
|
+
self.service_name,
|
|
89
|
+
)
|
|
90
|
+
# a diff is its own operation target, even when it includes an oldid.
|
|
91
|
+
# don't collapse it to the permanent-link identity of that oldid.
|
|
92
|
+
if self.diff is not None:
|
|
93
|
+
return base + ("diff", self.revision, self.diff)
|
|
94
|
+
if self.revision is not None:
|
|
95
|
+
return base + ("revision", self.revision)
|
|
96
|
+
if self.page_id is not None:
|
|
97
|
+
return base + ("page_id", self.page_id)
|
|
98
|
+
return base + (normalize_title(self.full_title) if self.full_title else None,)
|
|
99
|
+
|
|
100
|
+
def identity_key(self) -> tuple:
|
|
101
|
+
"""complete semantic identity, including fragment and operations."""
|
|
102
|
+
return self.page_identity_key() + (
|
|
103
|
+
self.fragment,
|
|
104
|
+
self.revision,
|
|
105
|
+
self.diff,
|
|
106
|
+
self.action,
|
|
107
|
+
self.query_parameters,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def same_page_as(self, other: "Destination") -> bool:
|
|
111
|
+
if not isinstance(other, Destination):
|
|
112
|
+
return NotImplemented
|
|
113
|
+
return self.page_identity_key() == other.page_identity_key()
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def is_revision(self) -> bool:
|
|
117
|
+
return self.revision is not None
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def is_diff(self) -> bool:
|
|
121
|
+
return self.diff is not None
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def is_action(self) -> bool:
|
|
125
|
+
return self.action is not None
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""exceptions used when a requested operation cannot be completed"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WMLinksFromHellError(Exception):
|
|
5
|
+
"""base exception for the package"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MetadataMissingError(WMLinksFromHellError):
|
|
9
|
+
"""required Wikimedia metadata is not available"""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConversionError(WMLinksFromHellError):
|
|
13
|
+
"""a destination cannot be safely rendered in the requested form"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CacheError(WMLinksFromHellError):
|
|
17
|
+
"""the metadata cache could not be written or managed"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SourceResolutionError(WMLinksFromHellError):
|
|
21
|
+
"""the supplied source wiki could not be identified"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ResolutionError(WMLinksFromHellError):
|
|
25
|
+
"""strict resolution was requested but the result was not resolvable"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# deprecated spelling kept for compatibility; use WMLinksFromHellError
|
|
29
|
+
WMLinkFromHellError = WMLinksFromHellError
|