11import datetime
2- from typing import TYPE_CHECKING , Any , Self , overload
2+ from inspect import isclass
3+ from typing import (
4+ TYPE_CHECKING ,
5+ Annotated ,
6+ Any ,
7+ ClassVar ,
8+ Self ,
9+ TypeVar ,
10+ dataclass_transform ,
11+ get_args ,
12+ get_origin ,
13+ get_type_hints ,
14+ overload ,
15+ )
16+
17+ from tppt .types import FilePath
318
419if TYPE_CHECKING :
520 from tppt ._pptx .slide import SlideBuilder
621
7-
8- class TpptSlideLayout :
9- """Base class for all slide layouts."""
22+ _AnyType = TypeVar ("_AnyType" )
23+
24+
25+ class _Placeholder :
26+ def __init__ (self , description : str = "" ):
27+ self .description = description
28+ self .value = None
29+
30+ def __repr__ (self ) -> str :
31+ return f"Placeholder({ self .description !r} )"
32+
33+ @classmethod
34+ def __class_getitem__ (cls , item : Any ) -> Any :
35+ return Annotated [item , cls ()]
36+
37+
38+ Placeholder = Annotated [_AnyType , _Placeholder ]
39+
40+
41+ class TpptSlideLayoutMeta (type ):
42+ """TpptSlideLayoutのメタクラス
43+
44+ プレースホルダーとして注釈されたフィールドを追跡します。
45+ """
46+
47+ def __new__ (
48+ mcs , name : str , bases : tuple [type , ...], namespace : dict [str , Any ]
49+ ) -> type :
50+ cls = super ().__new__ (mcs , name , bases , namespace )
51+
52+ # プレースホルダーフィールドを収集
53+ annotations = get_type_hints (cls , include_extras = True )
54+ placeholders = {}
55+
56+ for field_name , field_type in annotations .items ():
57+ # Annotatedフィールドを検索
58+ if get_origin (field_type ) is Annotated :
59+ args = get_args (field_type )
60+ # メタデータのチェック
61+ metadata_args = args [1 :]
62+
63+ # Placeholderとして直接マークされたフィールドを検索
64+ if any (
65+ arg is _Placeholder
66+ or (isclass (arg ) and arg .__name__ == "_Placeholder" )
67+ for arg in metadata_args
68+ ):
69+ placeholders [field_name ] = args [0 ] # 実際の型
70+ continue
71+
72+ # ネストされたAnnotatedのチェック (Placeholder[T]パターン)
73+ # Placeholder[T] = Annotated[T, _Placeholder]のパターンを検出
74+ base_type = args [0 ]
75+ if get_origin (base_type ) is Annotated :
76+ nested_args = get_args (base_type )
77+ if any (
78+ arg is _Placeholder
79+ or (isclass (arg ) and arg .__name__ == "_Placeholder" )
80+ for arg in nested_args [1 :]
81+ ):
82+ placeholders [field_name ] = nested_args [0 ] # 実際の型
83+ continue
84+
85+ # プレースホルダー情報をクラスに保存
86+ setattr (cls , "__placeholders__" , placeholders )
87+ return cls
88+
89+
90+ @dataclass_transform (
91+ eq_default = True ,
92+ order_default = False ,
93+ field_specifiers = (),
94+ )
95+ class TpptSlideLayout (metaclass = TpptSlideLayoutMeta ):
96+ """スライドレイアウトのベースクラス"""
97+
98+ __placeholders__ : ClassVar [dict [str , Any ]] = {}
99+
100+ def __init__ (self , ** kwargs ) -> None :
101+ # すべてのフィールドに値を設定
102+ for field_name , field_value in kwargs .items ():
103+ if field_name in self .__class__ .__placeholders__ :
104+ # プレースホルダーフィールドの場合
105+ setattr (self , field_name , field_value )
106+ elif field_name in self .__class__ .__annotations__ :
107+ setattr (self , field_name , field_value )
108+ else :
109+ raise TypeError (
110+ f"'{ self .__class__ .__name__ } ' got an unexpected keyword argument '{ field_name } '"
111+ )
10112
11113 @overload
12114 def __get__ (self , instance : None , objtype : type [Any ]) -> type [Self ]: ...
@@ -30,228 +132,107 @@ def builder(self) -> "SlideBuilder":
30132class DefaultMasterSlide (TpptSlideLayout ):
31133 """Default master slide layout."""
32134
33- def __init__ (
34- self ,
35- * ,
36- title : str ,
37- text : str ,
38- date : datetime .date | None = None ,
39- footer : str | None = None ,
40- slide_number : bool = True ,
41- ) -> None :
42- self .title = title
43- self .text = text
44- self .date = date
45- self .footer = footer
46- self .slide_number = slide_number
135+ title : Placeholder [str ]
136+ text : Placeholder [str ]
137+ date : Placeholder [datetime .date | None ] = None
138+ footer : Placeholder [str | None ] = None
47139
48140
49141class DefaultTitleSlide (TpptSlideLayout ):
50142 """Title slide layout."""
51143
52- def __init__ (
53- self ,
54- * ,
55- title : str ,
56- subtitle : str | None = None ,
57- date : datetime .date | None = None ,
58- footer : str | None = None ,
59- slide_number : bool = True ,
60- ) -> None :
61- self .title = title
62- self .subtitle = subtitle
63- self .date = date
64- self .footer = footer
65- self .slide_number = slide_number
144+ title : Placeholder [str ]
145+ subtitle : Placeholder [str | None ] = None
146+ date : Placeholder [datetime .date | None ] = None
147+ footer : Placeholder [str | None ] = None
66148
67149
68150class DefaultTitleAndContentSlide (TpptSlideLayout ):
69151 """Title and content slide layout."""
70152
71- def __init__ (
72- self ,
73- * ,
74- title : str ,
75- content : str | None = None ,
76- date : datetime .date | None = None ,
77- footer : str | None = None ,
78- slide_number : bool = True ,
79- ) -> None :
80- self .title = title
81- self .content = content
82- self .date = date
83- self .footer = footer
84- self .slide_number = slide_number
153+ title : Placeholder [str ]
154+ content : Placeholder [str ]
155+ date : Placeholder [datetime .date | None ] = None
156+ footer : Placeholder [str | None ] = None
85157
86158
87159class DefaultSectionHeaderSlide (TpptSlideLayout ):
88160 """Section header slide layout."""
89161
90- def __init__ (
91- self ,
92- * ,
93- title : str ,
94- text : str | None = None ,
95- date : datetime .date | None = None ,
96- footer : str | None = None ,
97- slide_number : bool = True ,
98- ) -> None :
99- self .title = title
100- self .text = text
101- self .date = date
102- self .footer = footer
103- self .slide_number = slide_number
162+ title : Placeholder [str ]
163+ text : Placeholder [str ]
164+ date : Placeholder [datetime .date | None ] = None
165+ footer : Placeholder [str | None ] = None
104166
105167
106168class DefaultTwoContentSlide (TpptSlideLayout ):
107169 """Two content slide layout."""
108170
109- def __init__ (
110- self ,
111- * ,
112- title : str ,
113- left_content : str | None = None ,
114- right_content : str | None = None ,
115- date : datetime .date | None = None ,
116- footer : str | None = None ,
117- slide_number : bool = True ,
118- ) -> None :
119- self .title = title
120- self .left_content = left_content
121- self .right_content = right_content
122- self .date = date
123- self .footer = footer
124- self .slide_number = slide_number
171+ title : Placeholder [str ]
172+ left_content : Placeholder [str ]
173+ right_content : Placeholder [str ]
174+ date : Placeholder [datetime .date | None ] = None
175+ footer : Placeholder [str | None ] = None
125176
126177
127178class DefaultComparisonSlide (TpptSlideLayout ):
128179 """Comparison slide layout."""
129180
130- def __init__ (
131- self ,
132- * ,
133- title : str ,
134- left_title : str | None = None ,
135- left_content : str | None = None ,
136- right_title : str | None = None ,
137- right_content : str | None = None ,
138- date : datetime .date | None = None ,
139- footer : str | None = None ,
140- slide_number : bool = True ,
141- ) -> None :
142- self .title = title
143- self .left_title = left_title
144- self .left_content = left_content
145- self .right_title = right_title
146- self .right_content = right_content
147- self .date = date
148- self .footer = footer
149- self .slide_number = slide_number
181+ title : Placeholder [str ]
182+ left_title : Placeholder [str ]
183+ left_content : Placeholder [str ]
184+ right_title : Placeholder [str ]
185+ right_content : Placeholder [str ]
186+ date : Placeholder [datetime .date | None ] = None
187+ footer : Placeholder [str | None ] = None
150188
151189
152190class DefaultTitleOnlySlide (TpptSlideLayout ):
153191 """Title only slide layout."""
154192
155- def __init__ (
156- self ,
157- * ,
158- title : str ,
159- date : datetime .date | None = None ,
160- footer : str | None = None ,
161- slide_number : bool = True ,
162- ) -> None :
163- self .title = title
164- self .date = date
165- self .footer = footer
166- self .slide_number = slide_number
193+ title : Placeholder [str ]
194+ date : Placeholder [datetime .date | None ] = None
195+ footer : Placeholder [str | None ] = None
167196
168197
169198class DefaultBlankSlide (TpptSlideLayout ):
170199 """Blank slide layout."""
171200
172- def __init__ (
173- self ,
174- * ,
175- date : datetime .date | None = None ,
176- footer : str | None = None ,
177- slide_number : bool = True ,
178- ) -> None :
179- self .date = date
180- self .footer = footer
181- self .slide_number = slide_number
201+ date : Placeholder [datetime .date | None ] = None
202+ footer : Placeholder [str | None ] = None
182203
183204
184205class DefaultContentWithCaptionSlide (TpptSlideLayout ):
185206 """Content with caption slide layout."""
186207
187- def __init__ (
188- self ,
189- * ,
190- title : str ,
191- content : str | None = None ,
192- date : datetime .date | None = None ,
193- footer : str | None = None ,
194- slide_number : bool = True ,
195- ) -> None :
196- self .title = title
197- self .content = content
198- self .date = date
199- self .footer = footer
200- self .slide_number = slide_number
208+ title : Placeholder [str ]
209+ content : Placeholder [str ]
210+ date : Placeholder [datetime .date | None ] = None
211+ footer : Placeholder [str | None ] = None
201212
202213
203214class DefaultPictureWithCaptionSlide (TpptSlideLayout ):
204215 """Picture with caption slide layout."""
205216
206- def __init__ (
207- self ,
208- * ,
209- title : str ,
210- picture_path : str | None = None ,
211- date : datetime .date | None = None ,
212- footer : str | None = None ,
213- slide_number : bool = True ,
214- ) -> None :
215- self .title = title
216- self .picture_path = picture_path
217- self .date = date
218- self .footer = footer
219- self .slide_number = slide_number
217+ title : Placeholder [str ]
218+ picture_path : Placeholder [FilePath ]
219+ date : Placeholder [datetime .date | None ] = None
220+ footer : Placeholder [str | None ] = None
220221
221222
222223class DefaultTitleAndVerticalTextSlide (TpptSlideLayout ):
223224 """Title and vertical text slide layout."""
224225
225- def __init__ (
226- self ,
227- * ,
228- title : str ,
229- vertical_text : str | None = None ,
230- date : datetime .date | None = None ,
231- footer : str | None = None ,
232- slide_number : bool = True ,
233- ) -> None :
234- self .title = title
235- self .vertical_text = vertical_text
236- self .date = date
237- self .footer = footer
238- self .slide_number = slide_number
226+ title : Placeholder [str ]
227+ vertical_text : Placeholder [str ]
228+ date : Placeholder [datetime .date | None ] = None
229+ footer : Placeholder [str | None ] = None
239230
240231
241232class DefaultVerticalTitleAndTextSlide (TpptSlideLayout ):
242233 """Vertical title and text slide layout."""
243234
244- def __init__ (
245- self ,
246- * ,
247- vertical_title : str ,
248- text : str | None = None ,
249- date : datetime .date | None = None ,
250- footer : str | None = None ,
251- slide_number : bool = True ,
252- ) -> None :
253- self .vertical_title = vertical_title
254- self .text = text
255- self .date = date
256- self .footer = footer
257- self .slide_number = slide_number
235+ vertical_title : Placeholder [str ]
236+ text : Placeholder [str ]
237+ date : Placeholder [datetime .date | None ]
238+ footer : Placeholder [str | None ]
0 commit comments