29
29
30
30
from base64 import b64encode
31
31
from collections import namedtuple
32
+ from collections .abc import Iterable
32
33
import copy
33
34
import dataclasses
34
35
from functools import lru_cache
36
+ import functools
35
37
from io import BytesIO
36
38
import json
37
39
import logging
@@ -536,6 +538,56 @@ def afmFontProperty(fontpath, font):
536
538
return FontEntry (fontpath , name , style , variant , weight , stretch , size )
537
539
538
540
541
+ def _cleanup_fontproperties_init (init_method ):
542
+ """
543
+ A decorator to limit the call signature to single a positional argument
544
+ or alternatively only keyword arguments.
545
+
546
+ We still accept but deprecate all other call signatures.
547
+
548
+ When the deprecation expires we can switch the signature to::
549
+
550
+ __init__(self, pattern=None, /, *, family=None, style=None, ...)
551
+
552
+ plus a runtime check that pattern is not used alongside with the
553
+ keyword arguments. This results eventually in the two possible
554
+ call signatures::
555
+
556
+ FontProperties(pattern)
557
+ FontProperties(family=..., size=..., ...)
558
+
559
+ """
560
+ @functools .wraps (init_method )
561
+ def wrapper (self , * args , ** kwargs ):
562
+ # multiple args with at least some positional ones
563
+ if len (args ) > 1 or len (args ) == 1 and kwargs :
564
+ # Note: Both cases were previously handled as individual properties.
565
+ # Therefore, we do not mention the case of font properties here.
566
+ _api .warn_deprecated (
567
+ "3.10" ,
568
+ message = "Passing individual properties to FontProperties() "
569
+ "positionally is deprecated. Please pass all properties "
570
+ "via keyword arguments."
571
+ )
572
+ # single non-string arg -> clearly a family not a pattern
573
+ if (len (args ) == 1 and not kwargs
574
+ and isinstance (args [0 ], Iterable ) and not isinstance (args [0 ], str )):
575
+ # Case font-family list passed as single argument
576
+ _api .warn_deprecated (
577
+ "3.10" ,
578
+ message = "Passing family as positional argument to FontProperties() "
579
+ "is deprecated. Please pass family names as keyword"
580
+ "argument."
581
+ )
582
+ # Note on single string arg:
583
+ # This has been interpreted as pattern so far. We are already raising if a
584
+ # non-pattern compatible family string was given. Therefore, we do not need
585
+ # to warn for this case.
586
+ return init_method (self , * args , ** kwargs )
587
+
588
+ return wrapper
589
+
590
+
539
591
class FontProperties :
540
592
"""
541
593
A class for storing and manipulating font properties.
@@ -585,9 +637,14 @@ class FontProperties:
585
637
approach allows all text sizes to be made larger or smaller based
586
638
on the font manager's default font size.
587
639
588
- This class will also accept a fontconfig_ pattern_, if it is the only
589
- argument provided. This support does not depend on fontconfig; we are
590
- merely borrowing its pattern syntax for use here.
640
+ This class accepts a single positional string as fontconfig_ pattern_,
641
+ or alternatively individual properties as keyword arguments::
642
+
643
+ FontProperties(pattern)
644
+ FontProperties(*, family=None, style=None, variant=None, ...)
645
+
646
+ This support does not depend on fontconfig; we are merely borrowing its
647
+ pattern syntax for use here.
591
648
592
649
.. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/
593
650
.. _pattern:
@@ -599,6 +656,7 @@ class FontProperties:
599
656
fontconfig.
600
657
"""
601
658
659
+ @_cleanup_fontproperties_init
602
660
def __init__ (self , family = None , style = None , variant = None , weight = None ,
603
661
stretch = None , size = None ,
604
662
fname = None , # if set, it's a hardcoded filename to use
0 commit comments