6
6
import re
7
7
from datetime import timezone
8
8
from html import unescape
9
- from urllib .parse import unquote , urljoin , urlparse , urlunparse
9
+ from typing import Any , Dict , Optional , Set
10
+ from urllib .parse import ParseResult , unquote , urljoin , urlparse , urlunparse
10
11
11
12
try :
12
13
from zoneinfo import ZoneInfo
15
16
16
17
17
18
from pelican .plugins import signals
18
- from pelican .settings import DEFAULT_CONFIG
19
+ from pelican .settings import DEFAULT_CONFIG , Settings
19
20
from pelican .utils import (
20
21
deprecated_attribute ,
21
22
memoized ,
@@ -44,12 +45,20 @@ class Content:
44
45
45
46
"""
46
47
48
+ default_template = None
49
+ mandatory_properties = ()
50
+
47
51
@deprecated_attribute (old = "filename" , new = "source_path" , since = (3 , 2 , 0 ))
48
52
def filename ():
49
53
return None
50
54
51
55
def __init__ (
52
- self , content , metadata = None , settings = None , source_path = None , context = None
56
+ self ,
57
+ content : str ,
58
+ metadata : Optional [Dict [str , Any ]] = None ,
59
+ settings : Optional [Settings ] = None ,
60
+ source_path : Optional [str ] = None ,
61
+ context : Optional [Dict [Any , Any ]] = None ,
53
62
):
54
63
if metadata is None :
55
64
metadata = {}
@@ -156,10 +165,10 @@ def __init__(
156
165
157
166
signals .content_object_init .send (self )
158
167
159
- def __str__ (self ):
168
+ def __str__ (self ) -> str :
160
169
return self .source_path or repr (self )
161
170
162
- def _has_valid_mandatory_properties (self ):
171
+ def _has_valid_mandatory_properties (self ) -> bool :
163
172
"""Test mandatory properties are set."""
164
173
for prop in self .mandatory_properties :
165
174
if not hasattr (self , prop ):
@@ -169,7 +178,7 @@ def _has_valid_mandatory_properties(self):
169
178
return False
170
179
return True
171
180
172
- def _has_valid_save_as (self ):
181
+ def _has_valid_save_as (self ) -> bool :
173
182
"""Return true if save_as doesn't write outside output path, false
174
183
otherwise."""
175
184
try :
@@ -190,7 +199,7 @@ def _has_valid_save_as(self):
190
199
191
200
return True
192
201
193
- def _has_valid_status (self ):
202
+ def _has_valid_status (self ) -> bool :
194
203
if hasattr (self , "allowed_statuses" ):
195
204
if self .status not in self .allowed_statuses :
196
205
logger .error (
@@ -204,7 +213,7 @@ def _has_valid_status(self):
204
213
# if undefined we allow all
205
214
return True
206
215
207
- def is_valid (self ):
216
+ def is_valid (self ) -> bool :
208
217
"""Validate Content"""
209
218
# Use all() to not short circuit and get results of all validations
210
219
return all (
@@ -216,7 +225,7 @@ def is_valid(self):
216
225
)
217
226
218
227
@property
219
- def url_format (self ):
228
+ def url_format (self ) -> Dict [ str , Any ] :
220
229
"""Returns the URL, formatted with the proper values"""
221
230
metadata = copy .copy (self .metadata )
222
231
path = self .metadata .get ("path" , self .get_relative_source_path ())
@@ -232,19 +241,19 @@ def url_format(self):
232
241
)
233
242
return metadata
234
243
235
- def _expand_settings (self , key , klass = None ):
244
+ def _expand_settings (self , key : str , klass : Optional [ str ] = None ) -> str :
236
245
if not klass :
237
246
klass = self .__class__ .__name__
238
247
fq_key = (f"{ klass } _{ key } " ).upper ()
239
248
return str (self .settings [fq_key ]).format (** self .url_format )
240
249
241
- def get_url_setting (self , key ) :
250
+ def get_url_setting (self , key : str ) -> str :
242
251
if hasattr (self , "override_" + key ):
243
252
return getattr (self , "override_" + key )
244
253
key = key if self .in_default_lang else "lang_%s" % key
245
254
return self ._expand_settings (key )
246
255
247
- def _link_replacer (self , siteurl , m ) :
256
+ def _link_replacer (self , siteurl : str , m : re . Match ) -> str :
248
257
what = m .group ("what" )
249
258
value = urlparse (m .group ("value" ))
250
259
path = value .path
@@ -272,15 +281,15 @@ def _link_replacer(self, siteurl, m):
272
281
# XXX Put this in a different location.
273
282
if what in {"filename" , "static" , "attach" }:
274
283
275
- def _get_linked_content (key , url ) :
284
+ def _get_linked_content (key : str , url : ParseResult ) -> Optional [ Content ] :
276
285
nonlocal value
277
286
278
- def _find_path (path ) :
287
+ def _find_path (path : str ) -> Optional [ Content ] :
279
288
if path .startswith ("/" ):
280
289
path = path [1 :]
281
290
else :
282
291
# relative to the source path of this content
283
- path = self .get_relative_source_path (
292
+ path = self .get_relative_source_path ( # type: ignore
284
293
os .path .join (self .relative_dir , path )
285
294
)
286
295
return self ._context [key ].get (path , None )
@@ -324,7 +333,7 @@ def _find_path(path):
324
333
linked_content = _get_linked_content (key , value )
325
334
if linked_content :
326
335
if what == "attach" :
327
- linked_content .attach_to (self )
336
+ linked_content .attach_to (self ) # type: ignore
328
337
origin = joiner (siteurl , linked_content .url )
329
338
origin = origin .replace ("\\ " , "/" ) # for Windows paths.
330
339
else :
@@ -357,9 +366,9 @@ def _find_path(path):
357
366
parts [2 ] = origin
358
367
origin = urlunparse (parts )
359
368
360
- return "" .join ((m .group ("markup" ), m .group ("quote" ), origin , m .group ("quote" )))
369
+ return "" .join ((m .group ("markup" ), m .group ("quote" ), origin , m .group ("quote" ))) # type: ignore
361
370
362
- def _get_intrasite_link_regex (self ):
371
+ def _get_intrasite_link_regex (self ) -> re . Pattern :
363
372
intrasite_link_regex = self .settings ["INTRASITE_LINK_REGEX" ]
364
373
regex = r"""
365
374
(?P<markup><[^\>]+ # match tag with all url-value attributes
@@ -370,7 +379,7 @@ def _get_intrasite_link_regex(self):
370
379
(?P=quote)""" .format (intrasite_link_regex )
371
380
return re .compile (regex , re .X )
372
381
373
- def _update_content (self , content , siteurl ) :
382
+ def _update_content (self , content : str , siteurl : str ) -> str :
374
383
"""Update the content attribute.
375
384
376
385
Change all the relative paths of the content to relative paths
@@ -386,7 +395,7 @@ def _update_content(self, content, siteurl):
386
395
hrefs = self ._get_intrasite_link_regex ()
387
396
return hrefs .sub (lambda m : self ._link_replacer (siteurl , m ), content )
388
397
389
- def get_static_links (self ):
398
+ def get_static_links (self ) -> Set [ str ] :
390
399
static_links = set ()
391
400
hrefs = self ._get_intrasite_link_regex ()
392
401
for m in hrefs .finditer (self ._content ):
@@ -402,27 +411,27 @@ def get_static_links(self):
402
411
path = self .get_relative_source_path (
403
412
os .path .join (self .relative_dir , path )
404
413
)
405
- path = path .replace ("%20" , " " )
414
+ path = path .replace ("%20" , " " ) # type: ignore
406
415
static_links .add (path )
407
416
return static_links
408
417
409
- def get_siteurl (self ):
418
+ def get_siteurl (self ) -> str :
410
419
return self ._context .get ("localsiteurl" , "" )
411
420
412
421
@memoized
413
- def get_content (self , siteurl ) :
422
+ def get_content (self , siteurl : str ) -> str :
414
423
if hasattr (self , "_get_content" ):
415
424
content = self ._get_content ()
416
425
else :
417
426
content = self ._content
418
427
return self ._update_content (content , siteurl )
419
428
420
429
@property
421
- def content (self ):
430
+ def content (self ) -> str :
422
431
return self .get_content (self .get_siteurl ())
423
432
424
433
@memoized
425
- def get_summary (self , siteurl ) :
434
+ def get_summary (self , siteurl : str ) -> str :
426
435
"""Returns the summary of an article.
427
436
428
437
This is based on the summary metadata if set, otherwise truncate the
@@ -441,10 +450,10 @@ def get_summary(self, siteurl):
441
450
)
442
451
443
452
@property
444
- def summary (self ):
453
+ def summary (self ) -> str :
445
454
return self .get_summary (self .get_siteurl ())
446
455
447
- def _get_summary (self ):
456
+ def _get_summary (self ) -> str :
448
457
"""deprecated function to access summary"""
449
458
450
459
logger .warning (
@@ -454,25 +463,25 @@ def _get_summary(self):
454
463
return self .summary
455
464
456
465
@summary .setter
457
- def summary (self , value ):
466
+ def summary (self , value : str ):
458
467
"""Dummy function"""
459
468
pass
460
469
461
470
@property
462
- def status (self ):
471
+ def status (self ) -> str :
463
472
return self ._status
464
473
465
474
@status .setter
466
- def status (self , value ) :
475
+ def status (self , value : str ) -> None :
467
476
# TODO maybe typecheck
468
477
self ._status = value .lower ()
469
478
470
479
@property
471
- def url (self ):
480
+ def url (self ) -> str :
472
481
return self .get_url_setting ("url" )
473
482
474
483
@property
475
- def save_as (self ):
484
+ def save_as (self ) -> str :
476
485
return self .get_url_setting ("save_as" )
477
486
478
487
def _get_template (self ):
@@ -481,7 +490,9 @@ def _get_template(self):
481
490
else :
482
491
return self .default_template
483
492
484
- def get_relative_source_path (self , source_path = None ):
493
+ def get_relative_source_path (
494
+ self , source_path : Optional [str ] = None
495
+ ) -> Optional [str ]:
485
496
"""Return the relative path (from the content path) to the given
486
497
source_path.
487
498
@@ -501,7 +512,7 @@ def get_relative_source_path(self, source_path=None):
501
512
)
502
513
503
514
@property
504
- def relative_dir (self ):
515
+ def relative_dir (self ) -> str :
505
516
return posixize_path (
506
517
os .path .dirname (
507
518
os .path .relpath (
@@ -511,7 +522,7 @@ def relative_dir(self):
511
522
)
512
523
)
513
524
514
- def refresh_metadata_intersite_links (self ):
525
+ def refresh_metadata_intersite_links (self ) -> None :
515
526
for key in self .settings ["FORMATTED_FIELDS" ]:
516
527
if key in self .metadata and key != "summary" :
517
528
value = self ._update_content (self .metadata [key ], self .get_siteurl ())
@@ -534,7 +545,7 @@ class Page(Content):
534
545
default_status = "published"
535
546
default_template = "page"
536
547
537
- def _expand_settings (self , key ) :
548
+ def _expand_settings (self , key : str ) -> str :
538
549
klass = "draft_page" if self .status == "draft" else None
539
550
return super ()._expand_settings (key , klass )
540
551
@@ -561,7 +572,7 @@ def __init__(self, *args, **kwargs):
561
572
if not hasattr (self , "date" ) and self .status == "draft" :
562
573
self .date = datetime .datetime .max .replace (tzinfo = self .timezone )
563
574
564
- def _expand_settings (self , key ) :
575
+ def _expand_settings (self , key : str ) -> str :
565
576
klass = "draft" if self .status == "draft" else "article"
566
577
return super ()._expand_settings (key , klass )
567
578
@@ -571,7 +582,7 @@ class Static(Content):
571
582
default_status = "published"
572
583
default_template = None
573
584
574
- def __init__ (self , * args , ** kwargs ):
585
+ def __init__ (self , * args , ** kwargs ) -> None :
575
586
super ().__init__ (* args , ** kwargs )
576
587
self ._output_location_referenced = False
577
588
@@ -588,18 +599,18 @@ def dst():
588
599
return None
589
600
590
601
@property
591
- def url (self ):
602
+ def url (self ) -> str :
592
603
# Note when url has been referenced, so we can avoid overriding it.
593
604
self ._output_location_referenced = True
594
605
return super ().url
595
606
596
607
@property
597
- def save_as (self ):
608
+ def save_as (self ) -> str :
598
609
# Note when save_as has been referenced, so we can avoid overriding it.
599
610
self ._output_location_referenced = True
600
611
return super ().save_as
601
612
602
- def attach_to (self , content ) :
613
+ def attach_to (self , content : Content ) -> None :
603
614
"""Override our output directory with that of the given content object."""
604
615
605
616
# Determine our file's new output path relative to the linking
@@ -624,7 +635,7 @@ def attach_to(self, content):
624
635
625
636
new_url = path_to_url (new_save_as )
626
637
627
- def _log_reason (reason ) :
638
+ def _log_reason (reason : str ) -> None :
628
639
logger .warning (
629
640
"The {attach} link in %s cannot relocate "
630
641
"%s because %s. Falling back to "
0 commit comments