wmlinksfromhell 1.0.0__tar.gz

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.
Files changed (48) hide show
  1. wmlinksfromhell-1.0.0/API_REFERENCE.md +898 -0
  2. wmlinksfromhell-1.0.0/CHANGELOG.md +15 -0
  3. wmlinksfromhell-1.0.0/LICENSE +21 -0
  4. wmlinksfromhell-1.0.0/MANIFEST.in +11 -0
  5. wmlinksfromhell-1.0.0/PKG-INFO +373 -0
  6. wmlinksfromhell-1.0.0/README.md +345 -0
  7. wmlinksfromhell-1.0.0/examples/comments.py +20 -0
  8. wmlinksfromhell-1.0.0/examples/convert_links.py +12 -0
  9. wmlinksfromhell-1.0.0/examples/pywikibot_example.py +13 -0
  10. wmlinksfromhell-1.0.0/examples/select_links.py +19 -0
  11. wmlinksfromhell-1.0.0/examples/simple_api.py +19 -0
  12. wmlinksfromhell-1.0.0/examples/sitematrix.py +25 -0
  13. wmlinksfromhell-1.0.0/pyproject.toml +51 -0
  14. wmlinksfromhell-1.0.0/requirements-dev.txt +3 -0
  15. wmlinksfromhell-1.0.0/setup.cfg +4 -0
  16. wmlinksfromhell-1.0.0/tests/conftest.py +31 -0
  17. wmlinksfromhell-1.0.0/tests/test_cache.py +53 -0
  18. wmlinksfromhell-1.0.0/tests/test_comments_and_simple_api.py +112 -0
  19. wmlinksfromhell-1.0.0/tests/test_conversion.py +275 -0
  20. wmlinksfromhell-1.0.0/tests/test_destination.py +52 -0
  21. wmlinksfromhell-1.0.0/tests/test_interwiki.py +487 -0
  22. wmlinksfromhell-1.0.0/tests/test_matching.py +76 -0
  23. wmlinksfromhell-1.0.0/tests/test_mediawiki_link_docs.py +439 -0
  24. wmlinksfromhell-1.0.0/tests/test_mediawiki_url_cases.py +335 -0
  25. wmlinksfromhell-1.0.0/tests/test_metadata.py +732 -0
  26. wmlinksfromhell-1.0.0/tests/test_parser.py +64 -0
  27. wmlinksfromhell-1.0.0/tests/test_public_and_operations.py +207 -0
  28. wmlinksfromhell-1.0.0/tests/test_real_world.py +50 -0
  29. wmlinksfromhell-1.0.0/tests/test_resolution.py +71 -0
  30. wmlinksfromhell-1.0.0/tests/test_simple_api.py +155 -0
  31. wmlinksfromhell-1.0.0/tests/test_special_fallbacks.py +37 -0
  32. wmlinksfromhell-1.0.0/tests/test_transformation.py +102 -0
  33. wmlinksfromhell-1.0.0/tests/test_urls.py +205 -0
  34. wmlinksfromhell-1.0.0/wmlinksfromhell/__init__.py +73 -0
  35. wmlinksfromhell-1.0.0/wmlinksfromhell/cache.py +92 -0
  36. wmlinksfromhell-1.0.0/wmlinksfromhell/constants.py +307 -0
  37. wmlinksfromhell-1.0.0/wmlinksfromhell/destination.py +125 -0
  38. wmlinksfromhell-1.0.0/wmlinksfromhell/exceptions.py +29 -0
  39. wmlinksfromhell-1.0.0/wmlinksfromhell/interwiki.py +842 -0
  40. wmlinksfromhell-1.0.0/wmlinksfromhell/metadata.py +1175 -0
  41. wmlinksfromhell-1.0.0/wmlinksfromhell/models.py +165 -0
  42. wmlinksfromhell-1.0.0/wmlinksfromhell/nodes.py +447 -0
  43. wmlinksfromhell-1.0.0/wmlinksfromhell/operations.py +83 -0
  44. wmlinksfromhell-1.0.0/wmlinksfromhell/parser.py +186 -0
  45. wmlinksfromhell-1.0.0/wmlinksfromhell/pywikibot_support.py +13 -0
  46. wmlinksfromhell-1.0.0/wmlinksfromhell/resolver.py +266 -0
  47. wmlinksfromhell-1.0.0/wmlinksfromhell/url.py +402 -0
  48. wmlinksfromhell-1.0.0/wmlinksfromhell.egg-info/SOURCES.txt +45 -0
@@ -0,0 +1,898 @@
1
+ # wmlinksfromhell API reference
2
+
3
+ This is the reference for `wmlinksfromhell 1.0.0`. For a quick start, see [README.md](README.md).
4
+
5
+ ```python
6
+ import wmlinksfromhell
7
+ ```
8
+
9
+ The package-level functions are the easiest place to start. The lower-level classes are useful when you want to keep a resolver or metadata store around.
10
+
11
+ ## Package functions
12
+
13
+ ### `parse(text, source=None, metadata=None) -> Code`
14
+
15
+ Parse MediaWiki wikitext and return a `Code` object.
16
+
17
+ `source` can be a dbname, hostname, Wikimedia URL, `Wiki`/`WikiInfo`, Pywikibot `Site`, or Pywikibot `Page`.
18
+
19
+ ```python
20
+ code = wmlinksfromhell.parse(
21
+ '[[w:en:Apple]] and https://de.wikipedia.org/wiki/Berlin',
22
+ source='metawiki',
23
+ )
24
+ ```
25
+
26
+ ### `parse_page(page, metadata=None) -> Code`
27
+
28
+ Pywikibot convenience wrapper. It uses `page.site` as the wiki source and keeps the page itself as context.
29
+
30
+ ```python
31
+ code = wmlinksfromhell.parse_page(page)
32
+ ```
33
+
34
+ Keeping the page context matters for anchors and relative links.
35
+
36
+ ### `resolve(value, source=None, metadata=None) -> Result`
37
+
38
+ Resolve a value without first deciding whether it is a URL, interwiki target, or local link.
39
+
40
+ ```python
41
+ result = wmlinksfromhell.resolve('https://en.wikipedia.org/wiki/Apple')
42
+ ```
43
+
44
+ ### `resolve_url(value, source=None, metadata=None) -> Result`
45
+
46
+ Resolve a URL. Wikimedia article paths, `index.php`, revisions, diffs, actions, fragments, page IDs, and protocol-relative URLs are supported.
47
+
48
+ ### `resolve_interwiki(value, source=None, metadata=None) -> Result`
49
+
50
+ Resolve an interwiki or local target. A leading `:` is supported.
51
+
52
+ ```python
53
+ result = wmlinksfromhell.resolve_interwiki(
54
+ ':w:en:Apple',
55
+ source='metawiki',
56
+ )
57
+ ```
58
+
59
+ ### `to_url(destination, metadata=None) -> str`
60
+
61
+ Render a `Destination` as a URL.
62
+
63
+ ### `to_interwiki(destination, source=None, metadata=None) -> str`
64
+
65
+ Render a `Destination` as an interwiki target. Exact revision and diff targets use `Special:` forms when possible. If an operation cannot be represented exactly but the page title is known, the normal page interwiki target is used rather than returning an empty value.
66
+
67
+ ### `to_local(destination, source=None, metadata=None) -> str`
68
+
69
+ Render a destination as a local target on the source wiki. The destination must belong to that wiki.
70
+
71
+ ### `wiki(dbname, metadata=None) -> WikiInfo`
72
+
73
+ Return a wiki by database name.
74
+
75
+ ```python
76
+ wiki = wmlinksfromhell.wiki('enwiki')
77
+ print(wiki.language)
78
+ print(wiki.hostname)
79
+ ```
80
+
81
+ ### `interwiki(dbname, metadata=None, source=None) -> InterwikiInfo`
82
+
83
+ Return the canonical interwiki identity for a wiki.
84
+
85
+ ```python
86
+ info = wmlinksfromhell.interwiki('dewiktionary')
87
+ print(info.prefix) # wikt:de
88
+ print(info.project) # wikt
89
+ print(info.language) # de
90
+ ```
91
+
92
+ ### `url(dbname, title, metadata=None, source=None) -> str`
93
+
94
+ Build a page URL directly from a dbname and title.
95
+
96
+ ```python
97
+ wmlinksfromhell.url('enwiki', 'Apple')
98
+ ```
99
+
100
+ ### `link(dbname, title, label=None, metadata=None, source=None) -> str`
101
+
102
+ Build an interwiki wikilink directly.
103
+
104
+ ```python
105
+ wmlinksfromhell.link('enwiki', 'Apple')
106
+ wmlinksfromhell.link('enwiki', 'Apple', 'the fruit')
107
+ ```
108
+
109
+ ## Code
110
+
111
+ `Code` is the preferred short name for `WMCode`.
112
+
113
+ ```python
114
+ code = wmlinksfromhell.parse(text)
115
+ ```
116
+
117
+ ### `code.links`
118
+
119
+ The normal link selection, equivalent to `code.filter_links()`.
120
+
121
+ Links inside HTML comments are not included unless requested.
122
+
123
+ ### `code.filter_links(recursive=True, predicate=None, comment_links=False, **criteria) -> list[Link]`
124
+
125
+ Select links using semantic criteria.
126
+
127
+ ```python
128
+ code.filter_links()
129
+ code.filter_links(family='wikipedia')
130
+ code.filter_links(project='wikipedia')
131
+ code.filter_links(language='en')
132
+ code.filter_links(dbname='enwiki')
133
+ code.filter_links(namespace='MediaWiki')
134
+ code.filter_links(title='Xtools.js')
135
+ code.filter_links(hostname='en.wikipedia.org')
136
+ ```
137
+
138
+ Boolean filters:
139
+
140
+ ```python
141
+ code.filter_links(wikimedia_only=True)
142
+ code.filter_links(wiki_only=True)
143
+ code.filter_links(external_only=True)
144
+ code.filter_links(interwiki_only=True)
145
+ code.filter_links(local_only=True)
146
+ ```
147
+
148
+ `predicate` receives each `Link` and can be used for custom selection:
149
+
150
+ ```python
151
+ code.filter_links(
152
+ predicate=lambda link: link.is_wiki and link.namespace == 'MediaWiki'
153
+ )
154
+ ```
155
+
156
+ `comment_links=True` includes links inside HTML comments. `in_comment=True` can then be used to select comments only.
157
+
158
+ ### `code.filter_wikilinks(...) -> list[Link]`
159
+
160
+ Like `filter_links()`, but limited to wikilink nodes.
161
+
162
+ ### `code.filter_external_links(...) -> list[Link]`
163
+
164
+ Like `filter_links()`, but limited to external-link nodes.
165
+
166
+ ### `code.convert(to, recursive=True, predicate=None, comment_links=False, **criteria) -> list[Link]`
167
+
168
+ Convert selected links in place. `to` is one of `url`, `interwiki`, or `local`.
169
+
170
+ ```python
171
+ code.convert('url', family='wikipedia', language='en')
172
+ ```
173
+
174
+ Only selected links are changed. Labels and surrounding wikitext are kept.
175
+
176
+ ### `code.get_sections(*args, **kwargs) -> list[Code]`
177
+
178
+ Return section wrappers using the same resolver and source context.
179
+
180
+ ### `code.wikicode`
181
+
182
+ The underlying `mwparserfromhell.wikicode.Wikicode` object. This is mainly useful when you need something that `wmlinksfromhell` does not expose directly.
183
+
184
+ ## Link
185
+
186
+ `Link` is the preferred short name for `WMLink`.
187
+
188
+ A `Link` wraps one parsed wikilink or external link.
189
+
190
+ ### Source and syntax
191
+
192
+ ```python
193
+ link.original
194
+ link.raw
195
+ link.label
196
+ link.node
197
+ link.index
198
+ link.in_comment
199
+ link.is_wikilink
200
+ link.is_external_link
201
+ link.syntax_type
202
+ link.input_type
203
+ link.link_type
204
+ ```
205
+
206
+ `syntax_type` values:
207
+
208
+ ```text
209
+ local_wikilink
210
+ interwiki_wikilink
211
+ external_link
212
+ bare_url
213
+ ```
214
+
215
+ `link_type` distinguishes comment links too:
216
+
217
+ ```text
218
+ wikilink
219
+ external_link
220
+ comment_wikilink
221
+ comment_external_link
222
+ ```
223
+
224
+ ### Resolution
225
+
226
+ ```python
227
+ link.result
228
+ link.destination
229
+ link.status
230
+ link.reason
231
+ link.confidence
232
+ ```
233
+
234
+ ### Prefix information
235
+
236
+ ```python
237
+ link.prefix
238
+ link.canonical_prefix
239
+ link.prefix_kind
240
+ link.is_project_prefix
241
+ link.is_language_prefix
242
+ link.is_language_project
243
+ ```
244
+
245
+ ### Destination information
246
+
247
+ ```python
248
+ link.destination_type
249
+ link.is_wikimedia
250
+ link.is_self_link
251
+ link.family
252
+ link.project
253
+ link.language
254
+ link.dbname
255
+ link.wikiid
256
+ link.hostname
257
+ link.title
258
+ link.namespace
259
+ link.fragment
260
+ link.page_id
261
+ link.revision
262
+ link.diff
263
+ link.action
264
+ link.special_page
265
+ link.organization_name
266
+ link.service_name
267
+ link.is_standard_project
268
+ ```
269
+
270
+ ### Rendered forms
271
+
272
+ ```python
273
+ link.interwiki
274
+ link.url
275
+ link.canonical_interwiki
276
+ link.canonical_url
277
+ link.canonical_wikilink
278
+ ```
279
+
280
+ ### `link.matches(**criteria) -> bool`
281
+
282
+ Compare a link with semantic fields or another representation.
283
+
284
+ ```python
285
+ link.matches(dbname='enwiki')
286
+ link.matches(family='wikipedia', language='en')
287
+ link.matches(namespace='MediaWiki')
288
+ link.matches(url='https://en.wikipedia.org/wiki/Apple')
289
+ link.matches(interwiki='w:en:Apple')
290
+ ```
291
+
292
+ For a `Destination`:
293
+
294
+ ```python
295
+ link.matches(destination=destination)
296
+ link.matches(same_page_as=destination)
297
+ ```
298
+
299
+ `destination=` compares the complete destination. `same_page_as=` compares page identity without requiring fragments and other non-page state to match.
300
+
301
+ ### `link.set_url() -> None`
302
+
303
+ Replace the link with its URL form. The link must have a resolved destination.
304
+
305
+ ### `link.set_interwiki(source=None) -> None`
306
+
307
+ Replace the link with an interwiki form. Raises `ConversionError` when a safe representation is not possible.
308
+
309
+ ### `link.set_local(source=None) -> None`
310
+
311
+ Replace the link with a local form on the source wiki.
312
+
313
+ ### `link.convert(to, source=None) -> Link`
314
+
315
+ Convert one link to `url`, `interwiki`, or `local`.
316
+
317
+ ### `link.refresh() -> None`
318
+
319
+ Clear the cached result for the link so it can be resolved again.
320
+
321
+ ### `link.as_dict() -> dict`
322
+
323
+ Return a JSON-friendly dictionary.
324
+
325
+ ## Result
326
+
327
+ `Result` is the preferred short name for `ResolutionResult`.
328
+
329
+ Useful fields:
330
+
331
+ ```python
332
+ result.raw
333
+ result.syntax_type
334
+ result.input_type
335
+ result.status
336
+ result.reason
337
+ result.confidence
338
+ result.destination
339
+ result.source_wiki
340
+
341
+ result.prefix
342
+ result.canonical_prefix
343
+ result.prefix_kind
344
+
345
+ result.family
346
+ result.project
347
+ result.language
348
+ result.dbname
349
+ result.wikiid
350
+ result.hostname
351
+ result.title
352
+ result.namespace
353
+ result.fragment
354
+ result.page_id
355
+ result.revision
356
+ result.diff
357
+ result.action
358
+ result.special_page
359
+
360
+ result.interwiki
361
+ result.url
362
+ ```
363
+
364
+ There are also convenience flags such as `is_wikimedia`, `is_project_prefix`, `is_language_prefix`, `is_language_project`, `is_revision`, `is_diff`, and `is_action`.
365
+
366
+ ### `result.as_dict() -> dict`
367
+
368
+ Return a structured dictionary. The resolved `Destination` is included as nested data.
369
+
370
+ ## Destination
371
+
372
+ `Destination` is the representation used after parsing a link. It does not depend on how the link was written.
373
+
374
+ ```python
375
+ result = wmlinksfromhell.resolve('w:en:Apple')
376
+ destination = result.destination
377
+ ```
378
+
379
+ Fields:
380
+
381
+ ```python
382
+ destination.destination_type
383
+ destination.family
384
+ destination.project
385
+ destination.language
386
+ destination.dbname
387
+ destination.wikiid
388
+ destination.hostname
389
+ destination.title
390
+ destination.namespace
391
+ destination.fragment
392
+ destination.page_id
393
+ destination.revision
394
+ destination.diff
395
+ destination.action
396
+ destination.special_page
397
+ destination.organization_name
398
+ destination.service_name
399
+ destination.query_parameters
400
+ destination.canonical_url
401
+ destination.metadata_source
402
+ destination.is_multilingual
403
+ destination.is_standard_project
404
+ destination.is_wikimedia_project
405
+ ```
406
+
407
+ Methods:
408
+
409
+ ```python
410
+ destination.full_title
411
+ destination.is_revision
412
+ destination.is_diff
413
+ destination.is_action
414
+ destination.page_identity_key()
415
+ destination.identity_key()
416
+ destination.same_page_as(other)
417
+ destination.as_dict()
418
+ destination.as_json()
419
+ ```
420
+
421
+ `page_identity_key()` leaves out fragments and other state that is not part of page identity. `identity_key()` includes the full semantic state.
422
+
423
+ ## Metadata
424
+
425
+ `Metadata` is the preferred short name for `MetadataStore`.
426
+
427
+ ```python
428
+ metadata = wmlinksfromhell.Metadata()
429
+ ```
430
+
431
+ The store starts with a small bootstrap data set. Network refreshes are explicit.
432
+
433
+ ### Lookups
434
+
435
+ ```python
436
+ metadata.wiki(dbname)
437
+ metadata.wiki_by_dbname(dbname)
438
+ metadata.wiki_by_hostname(hostname)
439
+ metadata.wiki_for_family_language(family, language)
440
+ metadata.known_wiki_hosts()
441
+ metadata.resolve_wikimedia_hostname(hostname)
442
+ metadata.resolve_source(source)
443
+ ```
444
+
445
+ Language helpers:
446
+
447
+ ```python
448
+ metadata.language_name(code)
449
+ metadata.is_known_language(code)
450
+ ```
451
+
452
+ Namespace helpers:
453
+
454
+ ```python
455
+ metadata.namespace_names(dbname)
456
+ metadata.namespace_aliases(dbname)
457
+ metadata.is_namespace(text, source)
458
+ ```
459
+
460
+ Interwiki helpers:
461
+
462
+ ```python
463
+ metadata.interwiki(dbname, source=None)
464
+ metadata.interwiki_map(source=None)
465
+ metadata.global_interwiki_entry(prefix)
466
+ metadata.interwiki_language_for_wiki(wiki)
467
+ metadata.interwiki_prefix_for_destination(destination, source=None)
468
+ ```
469
+
470
+ ### Load already-fetched data
471
+
472
+ ```python
473
+ metadata.load_sitematrix(data)
474
+ metadata.load_interwiki_config(text)
475
+ ```
476
+
477
+ These methods do not fetch anything themselves.
478
+
479
+ ### Refresh data
480
+
481
+ ```python
482
+ metadata.update()
483
+ metadata.refresh()
484
+ metadata.update_sitematrix()
485
+ metadata.update_interwiki_config()
486
+ metadata.update_wiki_metadata('enwiki')
487
+ ```
488
+
489
+ `update()` refreshes the metadata normally needed by the resolver and stores it in the local cache. The other methods let you refresh individual pieces.
490
+
491
+ `update_from_network()` and `refresh_all()` are older names for compatibility.
492
+
493
+ ### `MetadataCache`
494
+
495
+ `MetadataCache` stores the JSON metadata cache.
496
+
497
+ ```python
498
+ cache = wmlinksfromhell.MetadataCache()
499
+ ```
500
+
501
+ Useful members:
502
+
503
+ ```python
504
+ cache.path
505
+ cache.default_path()
506
+ cache.load()
507
+ cache.save(data)
508
+ cache.is_expired(max_age_seconds)
509
+ cache.clear()
510
+ cache.empty()
511
+ ```
512
+
513
+ A corrupt or incompatible cache is discarded rather than being used as if it were valid metadata.
514
+
515
+ ## Wiki
516
+
517
+ `Wiki` is the preferred short name for `WikiInfo`.
518
+
519
+ Fields:
520
+
521
+ ```python
522
+ wiki.dbname
523
+ wiki.family
524
+ wiki.project
525
+ wiki.language
526
+ wiki.language_name
527
+ wiki.hostname
528
+ wiki.article_path
529
+ wiki.script_path
530
+ wiki.wikiid
531
+ wiki.sitename
532
+ wiki.api_url
533
+ wiki.metadata_source
534
+ wiki.site_code
535
+ wiki.is_multilingual
536
+ wiki.is_standard_project
537
+ ```
538
+
539
+ Helpers:
540
+
541
+ ```python
542
+ wiki.url
543
+ wiki.page_url(title)
544
+ wiki.interwiki(title)
545
+ wiki.link(title)
546
+ wiki.main_page_url()
547
+ wiki.interwiki_info(metadata=None, source=None)
548
+ wiki.as_dict()
549
+ ```
550
+
551
+ ## InterwikiInfo
552
+
553
+ `InterwikiInfo` describes how a wiki is represented in an interwiki target.
554
+
555
+ Common fields:
556
+
557
+ ```python
558
+ info.prefix
559
+ info.project
560
+ info.language
561
+ info.dbname
562
+ info.wiki
563
+ ```
564
+
565
+ Helpers:
566
+
567
+ ```python
568
+ info.target(title)
569
+ info.url(title, metadata=None, source=None)
570
+ info.link(title, metadata=None, source=None)
571
+ info.as_dict()
572
+ ```
573
+
574
+ For example:
575
+
576
+ ```python
577
+ info = wmlinksfromhell.interwiki('dewiktionary')
578
+ info.target('Haus')
579
+ # wikt:de:Haus
580
+ ```
581
+
582
+ ## InterwikiMap
583
+
584
+ `InterwikiMap` contains the interwiki prefixes available to a source wiki.
585
+
586
+ ```python
587
+ imap = metadata.interwiki_map('metawiki')
588
+ ```
589
+
590
+ Members include:
591
+
592
+ ```python
593
+ imap.entries
594
+ imap.family_prefixes
595
+ imap.single_wiki_prefixes
596
+ imap.service_prefixes
597
+ imap.chapter_prefixes
598
+ imap.organization_entries
599
+ ```
600
+
601
+ Lookup:
602
+
603
+ ```python
604
+ imap.get(prefix)
605
+ prefix in imap
606
+ ```
607
+
608
+ ## InterwikiEntry
609
+
610
+ An `InterwikiEntry` is one record from the interwiki configuration.
611
+
612
+ ```python
613
+ entry.prefix
614
+ entry.url
615
+ entry.local
616
+ entry.trans
617
+ entry.language
618
+ entry.localinterwiki
619
+ entry.extralanglink
620
+ entry.linktext
621
+ entry.sitename
622
+ entry.wikiid
623
+ entry.api
624
+ entry.destination_dbname
625
+ entry.destination_type
626
+ entry.as_dict()
627
+ ```
628
+
629
+ ## Resolver
630
+
631
+ `Resolver` is the lower-level interface used by the package functions.
632
+
633
+ ```python
634
+ resolver = wmlinksfromhell.Resolver()
635
+ ```
636
+
637
+ A shared metadata store can be supplied:
638
+
639
+ ```python
640
+ metadata = wmlinksfromhell.Metadata()
641
+ resolver = wmlinksfromhell.Resolver(metadata)
642
+ ```
643
+
644
+ Methods:
645
+
646
+ ```python
647
+ resolver.resolve(value, source=None, strict=False)
648
+ resolver.resolve_url(value, source=None, strict=False)
649
+ resolver.resolve_interwiki(value, source=None, strict=False)
650
+ resolver.to_url(destination)
651
+ resolver.to_interwiki(destination, source=None)
652
+ resolver.to_local(destination, source=None)
653
+ ```
654
+
655
+ With `strict=True`, unresolved input raises `ResolutionError` instead of returning a result with an unresolved status.
656
+
657
+ ## Enums
658
+
659
+ ### `DestinationType`
660
+
661
+ ```text
662
+ WIKI
663
+ ORGANIZATION
664
+ SERVICE
665
+ TOOL
666
+ SPECIAL
667
+ EXTERNAL
668
+ UNKNOWN
669
+ ```
670
+
671
+ ### `ResolutionStatus`
672
+
673
+ ```text
674
+ RESOLVED
675
+ AMBIGUOUS
676
+ CONTEXT_REQUIRED
677
+ UNKNOWN_PREFIX
678
+ UNKNOWN_DESTINATION
679
+ METADATA_MISSING
680
+ EXTERNAL
681
+ MALFORMED
682
+ UNSUPPORTED
683
+ ```
684
+
685
+ ### `SyntaxType`
686
+
687
+ ```text
688
+ LOCAL_WIKILINK
689
+ INTERWIKI_WIKILINK
690
+ EXTERNAL_LINK
691
+ BARE_URL
692
+ ```
693
+
694
+ ## Wikimedia prefix behaviour
695
+
696
+ The resolver understands the usual language and project shortcuts.
697
+
698
+ Examples:
699
+
700
+ ```text
701
+ w:en:Apple
702
+ wikt:simple:uppercase
703
+ b:de:Main Page
704
+ q:fr:Main Page
705
+ c:File:Example.jpg
706
+ d:Q42
707
+ m:Main Page
708
+ ```
709
+
710
+ Short project shortcuts can inherit the source language when switching between language-based project families. On a multilingual project, or when the shortcut names the source project family, English is used. Long project names such as `wikipedia:` and `wikibooks:` are normal interwiki entries and normally point to the English project.
711
+
712
+ Prefixes can be chained from left to right. Examples include:
713
+
714
+ ```text
715
+ :de:q:Hauptseite
716
+ :m:en:About
717
+ :pl:w:2006
718
+ :w:it:b:Wiskunde
719
+ :ja:ja:2006
720
+ ```
721
+
722
+ A leading colon also changes ordinary MediaWiki link parsing. For example, on `dewiki`:
723
+
724
+ ```text
725
+ [[:en:Apple]]
726
+ ```
727
+
728
+ is a local link whose title is `en:Apple`, while:
729
+
730
+ ```text
731
+ [[en:Apple]]
732
+ ```
733
+
734
+ is an interlanguage link to English Wikipedia.
735
+
736
+ ## URL behaviour
737
+
738
+ The URL resolver handles article URLs, `index.php` forms, encoded titles, fragments, revisions, diffs, actions, page IDs, and protocol-relative URLs.
739
+
740
+ Examples:
741
+
742
+ ```text
743
+ https://en.wikipedia.org/wiki/Apple
744
+ https://en.wikipedia.org/wiki/Foo%20bar
745
+ https://en.wikipedia.org/wiki/Apple#History
746
+ https://en.wikipedia.org/w/index.php?title=Apple
747
+ https://en.wikipedia.org/w/index.php?title=Apple&oldid=123456
748
+ https://en.wikipedia.org/w/index.php?title=Apple&diff=123456
749
+ https://en.wikipedia.org/w/index.php?title=Apple&action=edit
750
+ https://en.wikipedia.org/w/index.php?curid=9906
751
+ //en.wikipedia.org/wiki/Apple
752
+ ```
753
+
754
+ Fragments are kept separately from titles. `curid` is kept as a page ID. Revision and diff information is kept in the destination rather than thrown away.
755
+
756
+ ## Revisions and diffs
757
+
758
+ Where an interwiki representation exists, the resolver uses special-page targets:
759
+
760
+ ```text
761
+ oldid=123456
762
+ → w:en:Special:PermanentLink/123456
763
+
764
+ diff=123456
765
+ → w:en:Special:Diff/123456
766
+
767
+ title=Apple&oldid=123&diff=124
768
+ → w:en:Special:Diff/123/124
769
+
770
+ action=edit&title=Apple
771
+ → w:en:Special:Edit/Apple
772
+ ```
773
+
774
+ Symbolic diffs such as `prev`, `next`, and `cur` stay attached to the destination. If an operation or query parameter cannot be represented by an interwiki target but the page title is known, `to_interwiki()` uses the normal page target rather than silently losing the page.
775
+
776
+ ## Local and relative links
777
+
778
+ These forms depend on source context:
779
+
780
+ ```text
781
+ #History
782
+ /example
783
+ ../example2
784
+ ```
785
+
786
+ Without enough source information, resolution returns `CONTEXT_REQUIRED` instead of guessing.
787
+
788
+ A Pywikibot `Page` is useful here because it supplies both the wiki and page title.
789
+
790
+ ## Comments
791
+
792
+ Links inside HTML comments are ignored by default:
793
+
794
+ ```python
795
+ code.filter_links()
796
+ ```
797
+
798
+ Include them with:
799
+
800
+ ```python
801
+ code.filter_links(comment_links=True)
802
+ ```
803
+
804
+ Select only comment links with:
805
+
806
+ ```python
807
+ code.filter_links(
808
+ comment_links=True,
809
+ in_comment=True,
810
+ )
811
+ ```
812
+
813
+ Comment links use the same `Link` and conversion APIs as other links.
814
+
815
+ ## Conversion safety
816
+
817
+ Conversions do not intentionally throw away meaningful destination information.
818
+
819
+ For example, a URL can contain a revision, diff, action, fragment, or extra query parameters that have no exact interwiki spelling. When the page title is known, `to_interwiki()` can fall back to the normal page target; when even that would lose the destination itself, conversion raises `ConversionError`.
820
+
821
+ Category and file links are also treated carefully. A normal `[[Category:...]]` or `[[File:...]]` has MediaWiki behaviour beyond a clickable link, so it is not silently rewritten as a URL. A leading-colon form such as `[[:Category:Help]]` is an ordinary clickable link and can be converted.
822
+
823
+ ## Exceptions
824
+
825
+ ```python
826
+ wmlinksfromhell.ConversionError
827
+ wmlinksfromhell.MetadataMissingError
828
+ wmlinksfromhell.SourceResolutionError
829
+ wmlinksfromhell.ResolutionError
830
+ wmlinksfromhell.CacheError
831
+ wmlinksfromhell.WMLinksFromHellError
832
+ ```
833
+
834
+ `WMLinkFromHellError` and `Error` remain as compatibility aliases for `WMLinksFromHellError`.
835
+
836
+ Normal non-strict resolution returns a `Result` with a status. Use strict mode when you want unresolved input to raise:
837
+
838
+ ```python
839
+ result = wmlinksfromhell.resolve(value, strict=True)
840
+ ```
841
+
842
+ ## Compatibility names
843
+
844
+ The shorter names are preferred for new code:
845
+
846
+ ```python
847
+ Code
848
+ Link
849
+ Wiki
850
+ Metadata
851
+ Result
852
+ ```
853
+
854
+ The older/public names remain available:
855
+
856
+ ```python
857
+ WMCode
858
+ WMLink
859
+ WikiInfo
860
+ MetadataStore
861
+ ResolutionResult
862
+ ```
863
+
864
+ ## Pywikibot
865
+
866
+ Pywikibot is optional.
867
+
868
+ ```python
869
+ import pywikibot
870
+ import wmlinksfromhell
871
+
872
+ site = pywikibot.Site('meta', 'meta')
873
+ page = pywikibot.Page(site, 'Global sysops/Requests')
874
+
875
+ code = wmlinksfromhell.parse_page(page)
876
+ ```
877
+
878
+ You can also pass the page to `parse()`:
879
+
880
+ ```python
881
+ code = wmlinksfromhell.parse(
882
+ page.text,
883
+ source=page,
884
+ )
885
+ ```
886
+
887
+ ## Examples
888
+
889
+ The `examples/` directory contains small scripts for:
890
+
891
+ - the simple API
892
+ - selecting links
893
+ - converting links
894
+ - comment links
895
+ - SiteMatrix metadata
896
+ - Pywikibot
897
+
898
+ For installation and a quick introduction, see [README.md](README.md).