2929
3030from base64 import b64encode
3131from collections import namedtuple
32+ from collections .abc import Iterable
3233import copy
3334import dataclasses
3435from functools import lru_cache
@@ -536,6 +537,56 @@ def afmFontProperty(fontpath, font):
536537 return FontEntry (fontpath , name , style , variant , weight , stretch , size )
537538
538539
540+ def _cleanup_fontproperties_init (init_method ):
541+ """
542+ A decorator to limit the call signature to single a positional argument
543+ or alternatively only keyword arguments.
544+
545+ We still accept but deprecate all other call signatures.
546+
547+ When the deprecation expires we can switch the signature to::
548+
549+ __init__(self, pattern=None, /, *, family=None, style=None, ...)
550+
551+ plus a runtime check that pattern is not used alongside with the
552+ keyword arguments. This results eventually in the two possible
553+ call signatures::
554+
555+ FontProperties(pattern)
556+ FontProperties(family=..., size=..., ...)
557+
558+ """
559+ @functools .wraps (init_method )
560+ def warpper (* args , ** kwargs ):
561+ # multiple args with at least some positional ones
562+ if len (args ) > 1 or len (args ) == 1 and kwargs :
563+ # Note: Both cases were previously handled as individual properties.
564+ # Therefore, we do not mention the case of font properties here.
565+ _api .warn_deprecated (
566+ "3.10" ,
567+ messsage = "Passing individual properties to FontProperties() "
568+ "positionally is deprecated. Please pass all properties "
569+ "via keyword arguments."
570+ )
571+ # single non-string arg -> clearly a family not a pattern
572+ if (len (args ) == 1 and not kwargs
573+ and isinstance (pattern , Iterable ) and not isinstance (pattern , str )):
574+ # Case font-family list passed as single argument
575+ _api .warn_deprecated (
576+ "3.10" ,
577+ messsage = "Passing family as positional argument to FontProperties() "
578+ "is deprecated. Please pass family names as keyword"
579+ "argument."
580+ )
581+ # Note on single string arg:
582+ # This has been interpreted as pattern so far. We are already raising if a
583+ # non-pattern compatible family string was given. Therefore, we do not need
584+ # to warn for this case.
585+ return init_method (* args , ** kwargs )
586+
587+ return wrapper
588+
589+
539590class FontProperties :
540591 """
541592 A class for storing and manipulating font properties.
@@ -585,9 +636,14 @@ class FontProperties:
585636 approach allows all text sizes to be made larger or smaller based
586637 on the font manager's default font size.
587638
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.
639+ This class accepts a single positional string as fontconfig_ pattern_,
640+ or alternatively individual properties as keyword arguments::
641+
642+ FontProperties(pattern)
643+ FontProperties(*, family=None, style=None, variant=None, ...)
644+
645+ This support does not depend on fontconfig; we are merely borrowing its
646+ pattern syntax for use here.
591647
592648 .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/
593649 .. _pattern:
@@ -599,6 +655,7 @@ class FontProperties:
599655 fontconfig.
600656 """
601657
658+ @_cleanup_fontproperties_init
602659 def __init__ (self , family = None , style = None , variant = None , weight = None ,
603660 stretch = None , size = None ,
604661 fname = None , # if set, it's a hardcoded filename to use
0 commit comments